First substantive commit: the entire Sprinter C compiler tree on top of
the bare README+gitignore initial commit.
What's in here:
bin/sprinter-cc — driver script invoking SDCC + linker + mkexe
libc/ — Sprinter-specific libc layer over ESTEX/BIOS
(conio, gfx, io, mem, stdio + headers)
runtime/ — crt0 variants (default/small/banked/minimal)
+ heap + bank trampolines
toolchain/ — mkexe (SprintEXE packer, C + tests)
examples/ — 30 demo programs (gfx, file I/O, env, time, …)
lib/Makefile — builds the libc archive (sprinter.lib)
docs/ — converted Sprinter manuals + asm reference samples
third_party/ — solid-c reference compiler dump + sdcc setup script
release_docs/ — packaging / release notes
gitignore overhaul:
• Drop dangerous blanket patterns: *.asm (would hide docs/samples/*.asm)
and *.exe (case-insensitive match was hiding third_party/solid-c/*.EXE
on macOS APFS). Replaced with examples/*/*.{asm,exe,…} and lib/*.lib.
• Restore tracking of toolchain/mkexe/tests/{one,big}.bin — those are
INPUT fixtures, not build outputs.
• Collapse the duplicated SDCC/C/Sdcc sections into one section per
concern (build outputs / vendored / OS-junk).
• Add .sprinter-cc-*/, build/ (catches lib/build/ too), .claude/.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
7.1 KiB
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(®s) |
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:
#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 wrappersFILE*model — ours is unbuffered (currently); Solid-C may use buffered streams- Error code constants — Solid-C's
EZERO/EINVFNC/etc.vs ourerrnotable. Will provide compat aliases inerrno.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,_setargvstub - dir.h:
_ffirst - mouse.h:
ms_*aliases +MSSTAT/MSGSTATtypedefs - types.h: provide compat
BOOL/uint/WORD/f_pointtypes
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