libc: сплит «1 функция = 1 модуль» — вся библиотека, wildcard-сборка

- bios/conio/env/errno/gfx/io/mem/mouse/stdio/stdlib/string/sys/time/
  video разложены по модулям: общие статики и helpers — в internal
  _-модулях (_conio.h/_mouse.h/_gfx.h/_palette.h/_atexit.h/_time.h)
- lib/Makefile: LIBC_C = wildcard libc/*/*.c — гранулярность файлов
  = гранулярность DCE линкера
- эффект _CODE: gfx_text 6986→2568 Б, gfx_mous −1745, gfx_demo/d16
  −542; ранее timedir −3270, ls −3098, stattest −2995
- комментарии оставшихся модулей переведены на русский; puts: убран
  мёртвый pchars; videomode_raw разложен на get/set
- docs/libc-split-asm-cases.md — правила asm-связок между модулями;
  docs/libc-roadmap.md — план этапа
- восстановлен examples/mdview/SAMPLE.MD (нужен make floppy)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-06 16:08:58 +03:00
parent 46553f4e07
commit 4a081501d8
237 changed files with 4830 additions and 3933 deletions
+24
View File
@@ -0,0 +1,24 @@
/*
* chdir — сменить текущий каталог. ESTEX CHDIR ($1D), HL = путь.
* Возвращает 0 или -1 + errno.
*/
#include <unistd.h>
int chdir(const char *path) __naked
{
(void)path;
__asm
push ix
ld c, #0x1D ; ESTEX CHDIR; HL уже = путь
rst #0x10
pop ix
jr c, _cd_err
ld de, #0
ret
_cd_err:
call __errno_set
ld de, #-1
ret
__endasm;
}
+14
View File
@@ -0,0 +1,14 @@
/*
* creat — создать/усечь файл на запись: open(path, O_WRONLY|O_CREAT|
* O_TRUNC). Параметр mode игнорируется — на Sprinter нет per-file
* прав доступа.
*/
#include <fcntl.h>
#include <unistd.h>
int creat(const char *path, int mode)
{
(void)mode; /* на Sprinter нет битов прав доступа */
return open(path, O_WRONLY | O_CREAT | O_TRUNC);
}
-68
View File
@@ -1,68 +0,0 @@
/*
* ffirst / fnext — directory iteration via ESTEX $19 / $1A.
*
* ESTEX F_FIRST ($19):
* HL = pattern, DE = buffer, A = attribute mask, B = format (0/1)
* CF = err / A = error code
* ESTEX F_NEXT ($1A):
* DE = same buffer
* CF = err / A = error code
*
* We always use format B=1 — 256-byte buffer with NUL-terminated DOS
* "name.ext" name at offset 33.
*
* ABI note: ffirst takes uint8_t as its 3rd arg. SDCC pushes a *single*
* byte for that (via `push af; inc sp`), not two — so the callee must
* pop ret-addr (2 bytes) AND consume the attr byte (`inc sp`) on the way
* out. Naively `pop bc` would over-eat into the caller's frame.
*/
#include <dir.h>
int ffirst(const char *pattern, ffblk_t *buf, uint8_t attrib) __naked
{
(void)pattern; (void)buf; (void)attrib;
__asm
;; On entry: HL = pattern, DE = buf, [SP+0..1] = ret, [SP+2] = attr.
ld iy, #2
add iy, sp
ld a, 0 (iy) ; A = attr (read without disturbing SP)
push ix
ld bc, #0x0119 ; ESTEX F_FIRST; format: 1 = DOS "name.ext" layout
rst #0x10
pop ix
pop hl ; HL = return address
inc sp ; consume the 1-byte attr
jr c, _ff_err
ld de, #0
jp (hl)
_ff_err:
call __errno_set
ld de, #-1
jp (hl)
__endasm;
}
int fnext(ffblk_t *buf) __naked
{
(void)buf;
__asm
;; HL = buf on entry; ESTEX F_NEXT wants buf in DE.
push ix
ex de, hl
ld c, #0x1A ; ESTEX F_NEXT
rst #0x10
pop ix
jr c, _fnext_err
ld de, #0
ret
_fnext_err:
call __errno_set
ld de, #-1
ret
__endasm;
}
+43
View File
@@ -0,0 +1,43 @@
/*
* ffirst — начать перебор каталога по шаблону через ESTEX F_FIRST ($19):
* HL = шаблон, DE = буфер, A = маска атрибутов, B = формат (0/1).
* CF = err / A = код ошибки.
*
* Всегда используем формат B=1 — 256-байтный буфер с ASCIIZ DOS-именем
* "name.ext" по смещению 33. Продолжение перебора — fnext() с тем же
* буфером. Возвращает 0 или -1 + errno.
*
* ABI: третий аргумент uint8_t SDCC пушит ОДНИМ байтом (push af; inc
* sp), поэтому callee снимает адрес возврата (2 байта) И съедает байт
* атрибута через `inc sp` — наивный `pop bc` переел бы кадр вызывающего.
*/
#include <dir.h>
int ffirst(const char *pattern, ffblk_t *buf, uint8_t attrib) __naked
{
(void)pattern; (void)buf; (void)attrib;
__asm
;; На входе: HL = шаблон, DE = буфер, [SP+0..1] = ret, [SP+2] = attr.
ld iy, #2
add iy, sp
ld a, 0 (iy) ; A = attr (читаем, не трогая SP)
push ix
ld bc, #0x0119 ; ESTEX F_FIRST; формат 1 = DOS "name.ext"
rst #0x10
pop ix
pop hl ; HL = адрес возврата
inc sp ; съесть 1-байтовый attr
jr c, _ff_err
ld de, #0
jp (hl)
_ff_err:
call __errno_set
ld de, #-1
jp (hl)
__endasm;
}
+27
View File
@@ -0,0 +1,27 @@
/*
* fnext — следующая запись перебора каталога, начатого ffirst().
* ESTEX F_NEXT ($1A): DE = тот же буфер; CF = err / A = код ошибки.
* Возвращает 0 или -1 + errno (ENOENT-класс в конце перебора).
*/
#include <dir.h>
int fnext(ffblk_t *buf) __naked
{
(void)buf;
__asm
;; HL = buf на входе; ESTEX F_NEXT хочет буфер в DE.
push ix
ex de, hl
ld c, #0x1A ; ESTEX F_NEXT
rst #0x10
pop ix
jr c, _fnext_err
ld de, #0
ret
_fnext_err:
call __errno_set
ld de, #-1
ret
__endasm;
}
-88
View File
@@ -1,88 +0,0 @@
/*
* fsdir.c — directory operations via ESTEX:
* $1B MKDIR — create directory (HL = path)
* $1C RMDIR — remove empty directory (HL = path)
* $1D CHDIR — change current directory (HL = path)
* $1E CURDIR — read current directory path (HL = 256-byte buffer)
*
* All four return CF=1 + A=error on failure. We surface that as errno
* with a -1 (or NULL for getcwd) return value, matching POSIX.
*/
#include <unistd.h>
#include <errno.h>
int mkdir(const char *path) __naked
{
(void)path;
__asm
push ix
ld c, #0x1B ; ESTEX MKDIR; HL already = path
rst #0x10
pop ix
jr c, _mk_err
ld de, #0
ret
_mk_err:
call __errno_set
ld de, #-1
ret
__endasm;
}
int rmdir(const char *path) __naked
{
(void)path;
__asm
push ix
ld c, #0x1C ; ESTEX RMDIR; HL already = path
rst #0x10
pop ix
jr c, _rm_err
ld de, #0
ret
_rm_err:
call __errno_set
ld de, #-1
ret
__endasm;
}
int chdir(const char *path) __naked
{
(void)path;
__asm
push ix
ld c, #0x1D ; ESTEX CHDIR; HL already = path
rst #0x10
pop ix
jr c, _cd_err
ld de, #0
ret
_cd_err:
call __errno_set
ld de, #-1
ret
__endasm;
}
char *getcwd(char *buf, size_t size) __naked
{
(void)buf; (void)size;
__asm
;; HL = buf, DE = size (ignored ESTEX always wants 256 bytes).
push ix
push hl ; preserve buf across RST
ld c, #0x1E ; ESTEX CURDIR
rst #0x10
pop hl ; restore buf
pop ix
jr c, _gc_err
ex de, hl ; return buf via DE (SDCC ptr return)
ret
_gc_err:
call __errno_set
ld de, #0 ; NULL on error
ret
__endasm;
}
+91
View File
@@ -0,0 +1,91 @@
/*
* fstat — POSIX fstat() поверх метаданных ESTEX.
*
* fstat(fd, &st) -> ESTEX GET_D_T ($17) для mtime + lseek/SEEK_END
* для размера.
*
* Sprinter / DSS не хранит POSIX owner/group/inode, поэтому mode
* синтезируется минимальный (S_IFREG | rw для пользователя).
*/
#include <sys/stat.h>
#include <unistd.h>
#include <time.h>
#include <errno.h>
#include <stdint.h>
/* Дата/время файла по хендлу. ESTEX GET_D_T: A=fd, C=$17 → D=day,
* E=month, IX=year, H=hour, L=min, B=sec. CF=1 + A=errcode при ошибке.
*
* Пишет прямо в *out через `ex (sp), hl` (обмен сохранённого out с
* HL=час:мин после RST) — статический скретч не нужен.
* `out->dow` не трогается (GET_D_T его не возвращает). */
static int get_dt_for_handle(int fd, datetime_t *out) __naked
{
(void)fd; (void)out;
__asm
;; __sdcccall(1): fd in HL (low byte), out in DE.
push ix ; save caller IX
push de ; stash out pointer
ld a, l ; A = fd
ld c, #0x17 ; ESTEX GET_D_T
rst #0x10
jr c, _gdt_err
;; D=day E=month IX=year H=hour L=min B=sec
ex (sp), hl ; TOS<->HL: HL=out, TOS=hour:min
ld (hl), d ; +0 day
inc hl
ld (hl), e ; +1 month
inc hl
push ix ; year onto stack
pop de ; DE = year
ld (hl), e ; +2 year low
inc hl
ld (hl), d ; +3 year high
inc hl
pop de ; D=hour E=min (from earlier ex (sp))
ld (hl), d ; +4 hour
inc hl
ld (hl), e ; +5 min
inc hl
ld (hl), b ; +6 sec (+7 dow left untouched)
pop ix ; restore caller IX
ld de, #0
ret
_gdt_err:
pop hl ; discard stashed out pointer
pop ix ; restore caller IX
call __errno_set
ld de, #-1
ret
__endasm;
}
int fstat(int fd, struct stat *buf)
{
/* Размер — через lseek-трюк. */
long cur = lseek(fd, 0L, SEEK_CUR);
if (cur < 0) return -1;
long end = lseek(fd, 0L, SEEK_END);
if (end < 0) return -1;
(void)lseek(fd, cur, SEEK_SET);
buf->st_size = (uint32_t)end;
/* Дата/время — через ESTEX. */
datetime_t ft;
if (get_dt_for_handle(fd, &ft) < 0) return -1;
{
struct tm tm;
tm.tm_sec = ft.second;
tm.tm_min = ft.minute;
tm.tm_hour = ft.hour;
tm.tm_mday = ft.day;
tm.tm_mon = (unsigned char)(ft.month - 1);
tm.tm_year = (int)ft.year - 1900;
tm.tm_isdst = 0;
tm.tm_hundredth = 0;
buf->st_mtime = mktime(&tm);
}
buf->st_mode = S_IFREG | S_IRUSR | S_IWUSR;
return 0;
}
+28
View File
@@ -0,0 +1,28 @@
/*
* getcwd — путь текущего каталога в buf. ESTEX CURDIR ($1E) пишет в
* буфер (ESTEX всегда рассчитывает на 256 байт — параметр size
* игнорируется). Возвращает buf или NULL + errno.
*/
#include <unistd.h>
char *getcwd(char *buf, size_t size) __naked
{
(void)buf; (void)size;
__asm
;; HL = buf, DE = size (игнорируется ESTEX хочет 256 байт).
push ix
push hl ; сохранить buf через RST
ld c, #0x1E ; ESTEX CURDIR
rst #0x10
pop hl ; вернуть buf
pop ix
jr c, _gc_err
ex de, hl ; вернуть buf в DE (SDCC ptr return)
ret
_gc_err:
call __errno_set
ld de, #0 ; NULL при ошибке
ret
__endasm;
}
+25 -24
View File
@@ -1,18 +1,18 @@
/*
* lseek — 32-bit file position via ESTEX MOVE_FP ($15).
* lseek — 32-битная позиция файла через ESTEX MOVE_FP ($15).
*
* ESTEX MOVE_FP: A=handle, B=whence (0=SET, 1=CUR, 2=END),
* HL=offset high16, IX=offset low16
* HL:IX = new absolute position, CF=err
* возврат: HL:IX = новая абсолютная позиция, CF=err
*
* SDCC __sdcccall(1) on z80 for `long lseek(int fd, long offset, int whence)`:
* - fd HL (1st 16-bit arg in register)
* - offset → stack as two 16-bit words (low first, then high)
* - whence → stack (top after offset)
* - 32-bit return: DE = low16, HL = high16
* - caller-pops (caller-side `pop af; pop af; pop af` after the call)
* SDCC __sdcccall(1) на z80 для `long lseek(int fd, long offset, int whence)`:
* - fd HL (первый 16-битный аргумент в регистре)
* - offset — на стеке двумя 16-битными словами (сначала low, потом high)
* - whence — на стеке (выше offset)
* - 32-битный возврат: DE = low16, HL = high16
* - стек чистит вызывающий (`pop af; pop af; pop af` после call)
*
* IX is saved (caller frame pointer).
* IX сохраняется (frame pointer вызывающего).
*/
#include <unistd.h>
@@ -21,21 +21,21 @@ long lseek(int fd, long offset, int whence) __naked
{
(void)fd; (void)offset; (void)whence;
__asm
push ix ; save caller-side IX
;; Layout after push:
;; SP+0..1 = saved IX
;; SP+2..3 = ret addr
push ix ; сохранить IX вызывающего
;; Раскладка после push:
;; SP+0..1 = сохранённый IX
;; SP+2..3 = адрес возврата
;; SP+4..5 = offset low16
;; SP+6..7 = offset high16
;; SP+8..9 = whence
ld a, l ; A = fd low byte (was in HL)
ld a, l ; A = младший байт fd (был в HL)
;; Walk through 5 consecutive stack bytes via HL cheaper than
;; IY-indexed because `ld r,(hl); inc hl` (2B/13T per byte) beats
;; `ld r, n(iy)` (3B/19T) for sequential reads.
;; 5 последовательных байтов стека читаем через HL дешевле,
;; чем IY-индексация: `ld r,(hl); inc hl` (2Б/13Т на байт)
;; против `ld r, n(iy)` (3Б/19Т) при последовательном чтении.
ld hl, #4
add hl, sp ; HL offset_low
add hl, sp ; HL указывает на offset_low
ld e, (hl)
inc hl
@@ -47,18 +47,19 @@ long lseek(int fd, long offset, int whence) __naked
inc hl
ld d, (hl) ; DE = offset_high
inc hl
ld b, (hl) ; B = whence (low byte)
ex de, hl ; HL = offset_high (ESTEX wants it here)
ld b, (hl) ; B = whence (младший байт)
ex de, hl ; HL = offset_high (ESTEX ждёт его тут)
ld c, #0x15 ; ESTEX MOVE_FP
rst #0x10
jr c, _lseek_err
;; Returns HL:IX = new position. Convert to SDCC long return (DE:HL).
;; Вернулось HL:IX = новая позиция. Приводим к long-возврату
;; SDCC (DE:HL).
push ix
pop de ; DE = low16 (was IX)
;; HL already has high16
pop ix ; restore caller-side IX
pop de ; DE = low16 (был IX)
;; HL уже содержит high16
pop ix ; восстановить IX вызывающего
ret
_lseek_err:
+24
View File
@@ -0,0 +1,24 @@
/*
* mkdir — создать каталог. ESTEX MKDIR ($1B), HL = путь.
* Возвращает 0 или -1 + errno.
*/
#include <unistd.h>
int mkdir(const char *path) __naked
{
(void)path;
__asm
push ix
ld c, #0x1B ; ESTEX MKDIR; HL уже = путь
rst #0x10
pop ix
jr c, _mk_err
ld de, #0
ret
_mk_err:
call __errno_set
ld de, #-1
ret
__endasm;
}
+10 -38
View File
@@ -1,15 +1,12 @@
/*
* read / write — bulk transfer through ESTEX file handles.
* read — блочное чтение из файлового хендла ESTEX.
*
* ESTEX READ ($13) / WRITE ($14):
* A = handle, HL = buffer, DE = byte count
* → DE = bytes actually transferred, CF = err with code in A.
* ESTEX READ ($13): A=handle, HL=буфер, DE=число байт
* → DE = фактически прочитано, CF=err с кодом в A.
*
* SDCC __sdcccall(1) for 3-arg int functions uses callee-pops for the
* stack-passed argument; this implementation mirrors the pattern used
* by SDCC's own z80.lib _memset.
*
* On error: sets errno, returns -1.
* SDCC __sdcccall(1) для 3-аргументных int-функций: третий аргумент на
* стеке снимает callee (паттерн как в _memset из SDCC z80.lib).
* При ошибке: errno установлен, возврат -1.
*/
#include <unistd.h>
@@ -18,21 +15,21 @@ int read(int fd, void *buf, size_t n) __naked
{
(void)fd; (void)buf; (void)n;
__asm
pop iy ; IY = return address
pop bc ; BC = n (stack arg)
pop iy ; IY = адрес возврата
pop bc ; BC = n (стековый аргумент)
ld a, l ; A = handle
ex de, hl ; HL = buf
ld d, b
ld e, c ; DE = n
push ix
push iy ; preserve return addr across RST
push iy ; сохранить адрес возврата через RST
ld c, #0x13 ; ESTEX READ
rst #0x10
pop iy
pop ix
jr c, _read_err
;; DE already holds count read.
;; DE уже держит прочитанное количество.
jp (iy)
_read_err:
call __errno_set
@@ -40,28 +37,3 @@ int read(int fd, void *buf, size_t n) __naked
jp (iy)
__endasm;
}
int write(int fd, const void *buf, size_t n) __naked
{
(void)fd; (void)buf; (void)n;
__asm
pop iy
pop bc
ld a, l
ex de, hl
ld d, b
ld e, c
push ix
push iy
ld c, #0x14
rst #0x10
pop iy
pop ix
jr c, _write_err
jp (iy)
_write_err:
call __errno_set
ld de, #-1
jp (iy)
__endasm;
}
+24
View File
@@ -0,0 +1,24 @@
/*
* rmdir — удалить ПУСТОЙ каталог. ESTEX RMDIR ($1C), HL = путь.
* Возвращает 0 или -1 + errno.
*/
#include <unistd.h>
int rmdir(const char *path) __naked
{
(void)path;
__asm
push ix
ld c, #0x1C ; ESTEX RMDIR; HL уже = путь
rst #0x10
pop ix
jr c, _rm_err
ld de, #0
ret
_rm_err:
call __errno_set
ld de, #-1
ret
__endasm;
}
+14 -96
View File
@@ -1,15 +1,9 @@
/*
* stat.c — POSIX stat() and fstat() over ESTEX metadata.
* stat — POSIX stat() поверх метаданных ESTEX.
*
* fstat(fd, &st) -> ESTEX GET_D_T ($17) for mtime + lseek/SEEK_END
* for size.
* stat(path, &st) -> open(O_RDONLY) + fstat() + close. This works
* for any regular file; directory paths fail at
* open() — F_FIRST-based stat had unreliable
* semantics for exact filenames.
*
* Sprinter / DSS doesn't track POSIX owner/group/inode, so we synth a
* minimal mode (S_IFREG | rw user perm).
* stat(path, &st) -> open(O_RDONLY) + fstat() + close. Работает для
* любого обычного файла; для директорий — fallback
* через F_FIRST (и перебор "*.*" для "."/"..").
*/
#include <sys/stat.h>
@@ -21,84 +15,8 @@
#include <string.h>
#include <dir.h>
/* ESTEX GET_D_T: A=fd, C=$17 → D=day, E=month, IX=year, H=hour, L=min,
* B=sec. CF=1 + A=errcode on failure.
*
* Writes directly into *out using `ex (sp), hl` to swap the saved out
* pointer with HL=hour:min after RST — no static scratch needed.
* `out->dow` is left untouched (GET_D_T doesn't return it). */
static int get_dt_for_handle(int fd, datetime_t *out) __naked
{
(void)fd; (void)out;
__asm
;; __sdcccall(1): fd in HL (low byte), out in DE.
push ix ; save caller IX
push de ; stash out pointer
ld a, l ; A = fd
ld c, #0x17 ; ESTEX GET_D_T
rst #0x10
jr c, _gdt_err
;; D=day E=month IX=year H=hour L=min B=sec
ex (sp), hl ; TOS<->HL: HL=out, TOS=hour:min
ld (hl), d ; +0 day
inc hl
ld (hl), e ; +1 month
inc hl
push ix ; year onto stack
pop de ; DE = year
ld (hl), e ; +2 year low
inc hl
ld (hl), d ; +3 year high
inc hl
pop de ; D=hour E=min (from earlier ex (sp))
ld (hl), d ; +4 hour
inc hl
ld (hl), e ; +5 min
inc hl
ld (hl), b ; +6 sec (+7 dow left untouched)
pop ix ; restore caller IX
ld de, #0
ret
_gdt_err:
pop hl ; discard stashed out pointer
pop ix ; restore caller IX
call __errno_set
ld de, #-1
ret
__endasm;
}
int fstat(int fd, struct stat *buf)
{
/* Size via lseek trick. */
long cur = lseek(fd, 0L, SEEK_CUR);
if (cur < 0) return -1;
long end = lseek(fd, 0L, SEEK_END);
if (end < 0) return -1;
(void)lseek(fd, cur, SEEK_SET);
buf->st_size = (uint32_t)end;
/* Date/time via ESTEX. */
datetime_t ft;
if (get_dt_for_handle(fd, &ft) < 0) return -1;
{
struct tm tm;
tm.tm_sec = ft.second;
tm.tm_min = ft.minute;
tm.tm_hour = ft.hour;
tm.tm_mday = ft.day;
tm.tm_mon = (unsigned char)(ft.month - 1);
tm.tm_year = (int)ft.year - 1900;
tm.tm_isdst = 0;
tm.tm_hundredth = 0;
buf->st_mtime = mktime(&tm);
}
buf->st_mode = S_IFREG | S_IRUSR | S_IWUSR;
return 0;
}
/* Convert ESTEX DOS-style date+time to time_t epoch (used by stat()
* when going through F_FIRST for directory entries). */
/* Конвертация DOS-формата даты+времени ESTEX в time_t (используется
* на пути через F_FIRST для записей директорий). */
static time_t dos_to_epoch(uint16_t date, uint16_t dtime)
{
struct tm tm;
@@ -113,8 +31,8 @@ static time_t dos_to_epoch(uint16_t date, uint16_t dtime)
return mktime(&tm);
}
/* Returns 1 if path is "." or "..", else 0. Reads at most 3 bytes;
* NULL-safe. ~28 bytes / 34117 T-states depending on input. */
/* Возвращает 1, если path "." или "..", иначе 0. Читает максимум
* 3 байта; NULL-безопасно. ~28 байт / 34117 тактов от входа. */
static char is_dot_or_dotdot(const char *path) __naked
{
(void)path;
@@ -146,7 +64,7 @@ static char is_dot_or_dotdot(const char *path) __naked
int stat(const char *path, struct stat *buf)
{
/* Regular file: open + fstat. */
/* Обычный файл: open + fstat. */
int fd = open(path, O_RDONLY);
if (fd >= 0) {
int r = fstat(fd, buf);
@@ -155,7 +73,7 @@ int stat(const char *path, struct stat *buf)
}
int saved = errno;
/* Try ffirst directly — works for ordinary subdirectories. */
/* Пробуем ffirst напрямую — работает для обычных подпапок. */
ffblk_t ffb;
if (ffirst(path, &ffb, FA_DIREC) == 0 && (ffb.found_attr & FA_DIREC)) {
buf->st_size = ffb.size;
@@ -164,9 +82,9 @@ int stat(const char *path, struct stat *buf)
return 0;
}
/* Verified 2026-05-29: ESTEX F_FIRST rejects bare "." and ".." with
* EINAME (16), same as open(). But they DO appear in the "*.*"
* directory listing with FA_DIREC. Iterate to find them. */
/* Проверено 2026-05-29: ESTEX F_FIRST отвергает голые "." и ".."
* с EINAME (16), как и open(). Но в листинге "*.*" они ЕСТЬ с
* FA_DIREC — ищем перебором. */
if (is_dot_or_dotdot(path)) {
if (ffirst("*.*", &ffb, FA_DIREC) == 0) {
do {
@@ -178,7 +96,7 @@ int stat(const char *path, struct stat *buf)
}
} while (fnext(&ffb) == 0);
}
/* Last-resort synthetic entry — FS variant didn't expose them. */
/* Крайний случай — синтетическая запись: ФС их не отдала. */
buf->st_mode = S_IFDIR | S_IRWXU;
buf->st_size = 0;
buf->st_mtime = 0;
+3 -3
View File
@@ -1,6 +1,6 @@
/*
* unlink — remove a file via ESTEX DELETE ($0E).
* HL = ASCIIZ path; CF=err with code in A. Sets errno on failure.
* unlink — удалить файл через ESTEX DELETE ($0E).
* HL = ASCIIZ-путь; CF=err с кодом в A. При ошибке ставит errno.
*/
#include <unistd.h>
@@ -10,7 +10,7 @@ int unlink(const char *path) __naked
(void)path;
__asm
push ix
ld c, #0x0E ; ESTEX DELETE (HL = file name, A = attribure file)
ld c, #0x0E ; ESTEX DELETE (HL = имя файла, A = атрибут)
rst #0x10
pop ix
jr c, _unlink_err