# 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.

## History

- 2026-06-01 — initial gap analysis vs Solid-C v2004
