libc: квирки DSS — возврат WRITE и лимит манипуляторов; тесты fdmax/fbench

- ESTEX WRITE ($14) на успехе возвращает DE=0, а НЕ счётчик записанного
  (вопреки докам; solid-c в своём fflush тоже отключил сравнение по
  счётчику) — write() теперь судит по CF/A: CF=0&A=0 → n,
  CF=0&A!=0 → ENOSPC/-1
- DSS выдаёт 8 манипуляторов (fd 2..9; fd 1 держит шелл под запущенный
  exe), а 9-й OPEN не возвращает 06h — ВЕШАЕТ систему; предохранитель
  _fd_guard: счётчик в open()/close(), отказ EMFILE без захода в DSS
- tests/fdmax — эмпирика лимита (8 хендлов, затем EMFILE=6);
  tests/fbench — бенчмарк буферизации (floor 512-байтными read,
  оценка небуферизованного по 1-байтным, fgetc/fgets/fputc)
- filetest расширен: raw-probe возврата write, сценарий r+
  (чтение-запись-чтение с инвалидацией буфера), ungetc, fprintf

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-06 16:09:29 +03:00
parent 48d552bf3a
commit 057dd615ba
11 changed files with 361 additions and 58 deletions
+53
View File
@@ -0,0 +1,53 @@
/*
* write — блочная запись в файловый хендл ESTEX.
*
* ESTEX WRITE ($14): A=handle, HL=буфер, DE=число байт
* → CF=err с кодом в A.
*
* КВИРК (подтверждён в MAME, DSS 1.71.57, 2026-07-06): вопреки докам
* («DE = реальное количество записанных байт») на успехе DE = 0.
* Достоверный признак полной записи — A = 0 при CF = 0 (как у READ,
* где A=0 «всё», A=FFh «меньше»); solid-c в своём fflush так же
* закомментировал сравнение по DE и проверяет A. Поэтому:
* CF=1 → errno из A, возврат -1;
* CF=0, A=0 → всё записано, возврат n (запрошенного);
* CF=0, A!=0 → частичная запись (диск полон), реальный счётчик
* неизвестен → errno=ENOSPC, возврат -1.
*
* ABI-паттерн идентичен read() — см. read.c.
*/
#include <unistd.h>
int write(int fd, const void *buf, size_t n) __naked
{
(void)fd; (void)buf; (void)n;
__asm
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 bc ; n нужен после RST (всё клобберится)
push ix
push iy ; сохранить адрес возврата через RST
ld c, #0x14 ; ESTEX WRITE
rst #0x10
pop iy
pop ix
pop bc ; BC = n
jr c, _write_err
or a, a ; A=0 всё записано (DE недостоверен)
jr nz, _write_short
ld d, b
ld e, c ; вернуть запрошенное n
jp (iy)
_write_short:
ld a, #10 ; ENOSPC (DSS 0Ah No free space)
_write_err:
call __errno_set
ld de, #-1
jp (iy)
__endasm;
}