Files
Sprinter-SDCC/docs/solid_c_compatibility.md
T
snark13 60373930fb libc: Solid-C совместимость (П3) + rename/isatty (П4); scanf-семейство
- <dos.h>: getdate/gettime/setdate/settime (структуры Turbo-C, обёртки
  над getdatetime), getdisk/setdisk (ESTEX $02/$01), absread/abswrite
  (BIOS $55/$56, rst 8 — номера найдены в solid-c DOS.ASM; сектор 0 =
  boot логического диска)
- scanf/fscanf/sscanf: своё C-ядро _scanf_core (%d %u %x %o %c %s,
  модификатор l, ширина, подавление '*', %%); в SDCC z80 scanf нет,
  asm solid-c не портируем из-за чужого ABI; 22 хост-теста ядра
- хвост П2: fdopen/freopen/fclosall/fgetpos/fsetpos поверх таблицы
  FILE; парсер режима и выдача слота вынесены в _file_mode/_file_slot
- rename() — ESTEX RENAME $10; isatty(fd) = fd <= 0 (tty только
  псевдо-fd 0/-1/-2: из CLI DSS манипуляторы идут с 1 — verified,
  fd 1 не резерв, под Flex Navigator его держит навигатор)
- errno.h: Solid-C имена ошибок (EZERO/EINVFNC/ENOFILE/...) как алиасы
- <sprinter_solid.h> — зонтичный заголовок для портирования;
  ltell/_setargv в sprinter_compat.h; div/ldiv — из SDCC (проверено)
- tests/solidt — smoke всех П3/П4 API, зелёный в MAME (вкл. absread
  boot-сектора с сигнатурой 55AA)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 16:35:58 +03:00

161 lines
8.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Solid-C compatibility — gap analysis
**Goal:** if a function exists in Solid-C, it must exist in our libc (with the same name). If we deliberately use a different name, document the difference here.
Solid-C source (`third_party/solid-c/INCLUDE/*.H`) provides these functions. SDCC's z80.lib auto-includes most of the standard C library. This document tracks the remaining gaps.
## Already covered (no work needed)
**From our libc directly:**
atexit, chdir, clearerr, close, clrscr, cprintf, cputs, creat, ctime, asctime,
exit, _exit, fclose, feof, ferror, fflush, fgetc, fgets, ffirst, fnext,
fopen, fputc, fputs, fread, fseek, fstat, ftell, fwrite, getch, getchar,
getche, getcwd, getenv, gotoxy, kbhit, longjmp, lseek, mkdir, open, perror,
printf, putch, putchar, putenv, puts, read, rewind, rmdir, setjmp, sleep,
sprintf, stat, textattr, textbackground, textcolor, unlink, vprintf,
vsprintf, write, wherex, wherey, set_videomode, get_videomode,
mouse_{init,show,hide,read,goto,bounds_x,bounds_y,refresh,text_cursor,
load_cursor,get_cursor,get_sensitivity_x,get_sensitivity_y,set_sensitivity,
video_mode_changed}.
**Free from SDCC's z80.lib (auto-linked):**
abs, atof, atoi, atol, bsearch, calloc, free, isalpha, isdigit, islower,
isupper, isspace, iscntrl, isgraph, isprint, ispunct, isalnum, isxdigit,
malloc, memchr, memcmp, memcpy, memmove, memset, qsort, rand, realloc,
srand, strcat, strchr, strcmp, strcpy, strcspn, strlen, strncat, strncmp,
strncpy, strpbrk, strrchr, strspn, strstr, strtok, strtol, strtoul,
tolower, toupper.
## REAL gaps — to implement
### Category A: trivial (one-liner aliases or small wrappers)
| Solid-C | Status | Notes |
|---|---|---|
| `getc(fd)` | macro alias | `#define getc(fp) fgetc(fp)` |
| `putc(c, fp)` | macro alias | `#define putc(c,fp) fputc(c,fp)` |
| `remove(name)` | alias | `#define remove(n) unlink(n)` (POSIX semantic match) |
| `seek(fd, off)` | alias | `int seek(fd, off) { return lseek(fd, off, SEEK_SET); }` |
| `tell(fd)` | alias | `int tell(fd) { return lseek(fd, 0, SEEK_CUR); }` |
| `ltell(fd, &lpos)` | wrapper | wrap our `lseek` (32-bit) |
| `home()` | macro alias | `#define home() gotoxy(0,0)` |
| `_setargv()` | crt0 does this | provide stub or doc that crt0 covers it |
| `_ffirst()` | alias | already have `ffirst` with same semantic — alias macro |
| `setmem(p, n, b)` | alias | `#define setmem(p,n,b) memset(p,b,n)` |
| `movmem(s, d, n)` | alias | `#define movmem(s,d,n) memcpy(d,s,n)` (note arg order!) |
| `min(a,b)`, `max(a,b)` | macros | `#define min(a,b) ((a)<(b)?(a):(b))` etc. |
| `isascii(c)` | macro | `#define isascii(c) ((unsigned)(c) < 128)` |
| `abort()` | wrapper | `void abort(void) { _exit(0xFF); }` |
| `strerr(n)` | alias | rename existing `strerror` or alias |
| `sysenv()` | wrapper | already have env API, just alias |
### Category B: small new implementations
| Solid-C | Notes |
|---|---|
| `dec8/dec16/dec32` | print uint8/16/32 as decimal — use sprintf("%u") internally |
| `hex8/hex16/hex32` | print uint8/16/32 as hex — we have `print_hex`, just add hex16 and hex32 |
| `strlwr(s)` | in-place lowercase the string (returns s) |
| `strupr(s)` | in-place uppercase the string |
| `div(num, den)` | returns `div_t { quot, rem }` — trivial wrapper over `/` and `%` |
| `getc/putc/ungetc` | one-byte putback for streams; `ungetc` needs 1-char buffer in FILE* |
| `gets(buf)` | dangerous-but-Solid-C-has-it, simple fgets-wrapper |
| `cgets(buf)` | line input via conio (no echo control — wrap getche loop) |
| `inp(port)` / `outp(port,val)` | direct port I/O — Z80 IN/OUT, can be __sfr or inline asm wrappers |
| `enable()` / `disable()` | `__asm ei __endasm` / `__asm di __endasm` |
| `getdisk()` / `setdisk(d)` | ESTEX $02 CURDISK / $01 CHDISK |
### Category C: requires research / non-trivial work
| Solid-C | Notes |
|---|---|
| `fdopen(fd, mode)` / `freopen` | needs FILE* expansion — currently we have unbuffered FILE; mid-effort |
| `fclosall()` | iterate over all open FILE*'s and close them — depends on FILE table refactor |
| `fgetpos(fp, &pos)` / `fsetpos` | wrappers over ftell/fseek with `fpos_t` type |
| `bdos(a, hl)` / `bdosh(...)` / `intdos(&regs)` | Solid-C exposes raw ESTEX call interface. Our convention is typed wrappers — we may **document** the difference and skip these. |
| `absread(disk, n, sect, buf)` / `abswrite` | absolute disk sector I/O — need ESTEX READ_SECT / WRITE_SECT (need to find function numbers) |
| `_ffirst` raw vs `ffirst` cooked | Solid-C has two — raw "FilenameExt" format and cooked. Investigate if we want both. |
| `ioctl(fd, ...)` | terminal control — Solid-C scope unclear, may skip |
| `isatty(fd)` | trivial (always returns 0 — we have no tty handle), but check our FD numbers |
| `brk(addr)` / `sbrk(n)` | classical Unix heap pointer manipulation; our heap is managed by SDCC `_sbrk` already? Investigate alias. |
| `scanf` / `sscanf` / `fscanf` | parser — SDCC may have it in stdlib if requested. Check or add stub. |
| `getdate` / `gettime` / `setdate` / `settime` | we have `getdatetime`/`setdatetime`; Solid-C splits into date/time structs. Build wrappers. |
### Category D: Mouse aliases — Solid-C `ms_*` names
Solid-C uses short names: `ms_init/show/hide/stat/spos/ybnd/xbnd/scur/tcur/gcur/gsen/ssen/hard/vmod/ref` + `msgstat`/`mssgpos`. All have the same semantics as our `mouse_*` functions.
**Plan:** add aliases via header macros so both names work:
```c
#define ms_init mouse_init
#define ms_show mouse_show
/* ... etc */
```
Also Solid-C wraps state into `MSSTAT` (8-bit coords) and `MSGSTAT` (16-bit coords) structs. Our `mouse_state_t` already matches `MSGSTAT` semantics — add a typedef alias.
## Documented differences (will NOT match Solid-C exactly)
These will be in `docs/solid_c_diff.md`:
- **`bdos` / `bdosh` / `intdos`** — we don't expose raw ESTEX call interface; use typed wrappers
- **`FILE*` model** — ours is unbuffered (currently); Solid-C may use buffered streams
- **Error code constants** — Solid-C's `EZERO/EINVFNC/etc.` vs our `errno` table. Will provide compat aliases in `errno.h`.
## Implementation plan
**Phase 1 — trivial aliases/macros** (~1 hour):
- conio.h additions: `home`, `inp`, `outp`, `enable`, `disable`
- stdio.h: `getc`, `putc`, `remove`, `gets`
- string.h: `setmem`, `movmem`, `strlwr`, `strupr`, `strerr`
- stdlib.h: `min`, `max`, `div`, `abort`, `sysenv`, `dec8/16/32`, `hex8/16/32`
- ctype.h: `isascii`
- io.h: `seek`, `tell`, `ltell`, `_setargv` stub
- dir.h: `_ffirst`
- mouse.h: `ms_*` aliases + `MSSTAT`/`MSGSTAT` typedefs
- types.h: provide compat `BOOL`/`uint`/`WORD`/`f_point` types
**Phase 2 — new impls** (~2-3 hours):
- ungetc, gets, cgets
- getdisk/setdisk
- getdate/gettime/setdate/settime (split datetime struct)
**Phase 3 — investigate / decide** (research first):
- absread/abswrite (sector I/O)
- scanf family (SDCC has it?)
- brk/sbrk vs SDCC heap
## Compatibility header
To make porting easier, add a single `<sprinter_solid.h>` that includes all the standard headers (`stdio.h`, `string.h`, `conio.h`, etc.) — Solid-C programs can `#include <sprinter_solid.h>` and have most functions available.
## Status 2026-07-06 — Phase 1/2/3 ЗАКРЫТЫ
Всё из категорий A/B/C реализовано или закрыто решением:
- **A (алиасы)**: все на месте в sprinter_compat.h (+ ltell, _setargv
добавлены 2026-07-06); div/ldiv — из SDCC z80.lib (проверено).
- **B**: getdisk/setdisk (ESTEX $02/$01, libc/io), getdate/gettime/
setdate/settime (обёртки над getdatetime, структуры Turbo-C в
<dos.h>), остальное было готово ранее.
- **C**: fdopen/freopen/fclosall/fgetpos/fsetpos — реализованы поверх
таблицы FILE v2 (libc/file); ungetc — есть (FILE v2);
**absread/abswrite — BIOS $55/$56** (rst 8, A=диск, HL:IX=сектор,
DE=буфер, B=счётчик; найдено в solid-c DOS.ASM) — реализованы в
libc/io; **scanf/fscanf/sscanf — реализованы** (своё C-ядро
_scanf_core с семантикой Solid-C: %d %u %x %o %c %s, l, ширина, %*;
в SDCC z80 scanf нет); isatty — fd < 2 (см. memory/dss_fd_limit).
bdos/bdosh/intdos — НЕ экспонируем (решение: типизированные
обёртки); brk/sbrk — НЕ нужны (heap SDCC); ioctl — скип.
- **errno**: Solid-C имена (EZERO/EINVFNC/ENOFILE/…) — алиасы в errno.h.
- **Зонтичный заголовок**: <sprinter_solid.h>.
- Тест: tests/solidt (MAME).
## History
- 2026-07-06 — Phase 1/2/3 закрыты: dos.h (даты/диски/сектора),
scanf-семейство, fdopen/freopen/fclosall/fgetpos/fsetpos,
rename/isatty (П4), errno-алиасы, sprinter_solid.h, тест solidt
- 2026-06-01 — initial gap analysis vs Solid-C v2004