Add full compiler toolchain, libc, examples and reference docs

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>
This commit is contained in:
2026-06-03 16:13:21 +03:00
parent f542608b3f
commit c71e249a4e
404 changed files with 75155 additions and 58 deletions
+14
View File
@@ -0,0 +1,14 @@
# Sprinter C Compiler — User Documentation (English)
This is the documentation that ships with the release tarball. For Russian
see `../ru/README.md`.
## Contents
| File | Topic |
|---|---|
| `getting_started.md` | Install, first build, MAME notes |
| `sprinter_cc.md` | Compiler driver — all flags explained |
| `memory_modes.md` | tiny / small / big / huge / manual |
| `headers.md` | Public headers and what each provides |
| `examples.md` | Tour of the 27 bundled examples |
+76
View File
@@ -0,0 +1,76 @@
# Examples tour
The release ships with 27 example programs in `examples/`. Every one of them
is a self-contained demo with comments — they were used as regression tests
during development.
## Build any example
```sh
cd examples/hello
make
```
That produces `hello.exe` next to `hello.c` using `examples/example.mk`.
## Categories
### Hello world / basics
* **`hello`** — stdio + conio Turbo-C-style colours
* **`argv`** — argv parsing in crt0
* **`conio`** — conio API smoke test
* **`attrprob`** — probe Sprinter text-attribute byte layout
### File I/O
* **`cat`** — read & print TEST.TXT
* **`seek`** — 32-bit lseek over a 100 KB file
* **`ls`** — directory listing via ffirst/fnext
* **`filetest`** — FILE* streams (`fopen`/`fread`/`fwrite`/`fclose`)
* **`stattest`** — `stat`/`fstat` on files and directories
* **`openenv`** — open() flags + environment variables
### Memory & banking
* **`malloc`** — heap stress test (200+ allocations)
* **`mem_test`** — page allocator + `bank_read`/`bank_write`
* **`banked`** — banked code in W3 (huge mode)
* **`bankedbg`** — banked code in W1 (big mode)
* **`banklocl`** — bank-local static data and BSS
### Mouse
* **`mouse`** — driver in text mode
* **`gfx_mous`** — mouse with custom bitmap cursor in graphics mode
### Graphics
* **`gfx_demo`** — 320×256×256: lines, rectangles, fill via accelerator
* **`gfx_d16`** — 640×256×16: same primitives in 16-color mode
* **`gfx_text`** — bitmap-font text on graphics screen
### Misc
* **`errno`** — errno / strerror / perror
* **`timedir`** — date/time + directory listing
* **`ptime`** — POSIX time API (time / localtime / mktime)
* **`strtest`** — `<string.h>` test (from SDCC's z80.lib)
* **`stdlib`** — `<stdlib.h>` test (qsort / rand / strtol / etc.)
* **`assrtest`** — assert()
* **`rt_test`** — runtime helpers (sleep, setjmp, atexit)
## Example.mk
Every example uses `examples/example.mk`. A minimal Makefile looks like:
```makefile
PROJ_ROOT := $(abspath $(CURDIR)/../..)
EXAMPLE := my_app
include $(PROJ_ROOT)/examples/example.mk
```
Optional knobs (set before `include`):
```makefile
MEMORY := huge # default tiny
STACK_SIZE := 4096 # default ~1278
EXTRA_SRCS := helper.c util.c # extra .c files in same dir
EXTRA_FLAGS := --bank 1=engine.c --debug # pass-through to sprinter-cc
```
Use this template for your own programs.
+61
View File
@@ -0,0 +1,61 @@
# Getting started
## What's in the tarball
After extracting `sprinter-c-v1.0-<host>.tar.gz` you've got:
* **`bin/sprinter-cc`** — the C → SprintEXE driver (a bash script).
* **`third_party/sdcc/`** — vendored SDCC 4.5 used for the C → Z80 step.
* **`libc/include/`** — headers your programs include.
* **`lib/sprinter.lib`** — the Sprinter target libc (prebuilt; rebuilt by `make` if you modify libc sources).
* **`runtime/`** — crt0 variants and runtime helpers (assembled per-build).
* **`toolchain/mkexe/`** — host utility that packs SDCC's `.ihx` into a SprintEXE.
* **`examples/`** — 27 ready-to-build programs.
* **`docs/{en,ru}/`** — this documentation.
## First build
```sh
cd sprinter-c-v1.0-<host>
make all # rebuild lib + every example (~30 s)
```
If `make` complains about a missing SDCC binary, fetch it once:
```sh
make sdcc # downloads SDCC 4.5 if not vendored
```
## Build your own program
```sh
cat > hello.c <<EOF
#include <stdio.h>
int main(void) {
puts("Hello, Sprinter!");
return 0;
}
EOF
bin/sprinter-cc -o hello.exe hello.c
```
`hello.exe` is now a valid SprintEXE you can run on Sprinter / MAME / any
ESTEX DSS shell.
## Running on hardware or in an emulator
The release does **not** include the MAME emulator or the Sprinter ROM /
DSS / HDD images — those are large and have their own licensing. To test:
* **MAME:** install MAME 0.283+ separately, obtain Sprinter Sp2000 images from
Peters Plus, mount a FAT12 floppy with your `.exe` files as `-flop1`.
* **Real Sprinter:** copy `.exe` to a floppy or HDD partition that DSS can
see, then `RUN HELLO` from the shell.
## Next steps
* Read `sprinter_cc.md` for compiler flags.
* Read `memory_modes.md` when you start needing more than 14 KB of code.
* Browse `examples/` — every file is a working program with comments.
* See `headers.md` for the public API surface.
+50
View File
@@ -0,0 +1,50 @@
# Headers
Everything you `#include` lives under `libc/include/`.
## Standard C
| Header | Source | Provides |
|---|---|---|
| `<stdio.h>` | our libc + SDCC | `printf`, `puts`, `putchar`, `getchar`, `sprintf`, `FILE *`, `fopen`/`fread`/.../`fclose`, plus `hex8/16/32`, `dec8/16/32`, `gets` |
| `<stdlib.h>` | SDCC z80.lib | `malloc`, `free`, `calloc`, `realloc`, `atoi`, `atof`, `atol`, `strtol`, `qsort`, `bsearch`, `rand`, `srand`, `abs`, `div`, `exit`, ... |
| `<string.h>` | SDCC z80.lib | `memcpy`, `memcmp`, `memset`, `memchr`, `memmove`, full `strXxx` family |
| `<ctype.h>` | SDCC z80.lib | `tolower`, `toupper`, `isalnum`, `isdigit`, ... |
| `<math.h>` | SDCC z80.lib | `sinf`, `cosf`, `sqrtf`, ... |
| `<errno.h>` | our libc | `errno` + error constants + `strerror` |
| `<setjmp.h>` | our libc | `setjmp` / `longjmp` |
| `<assert.h>` | SDCC | `assert` macro |
| `<unistd.h>` | our libc | `read`, `write`, `close`, `lseek`, `unlink`, `SEEK_SET`/`CUR`/`END` |
| `<fcntl.h>` | our libc | `open`, `creat`, `O_RDONLY`/`O_WRONLY`/`O_CREAT`/etc. |
| `<sys/stat.h>` | our libc | `stat`, `fstat`, `struct stat` |
| `<time.h>` | our libc | `getdatetime`, `setdatetime` + POSIX `time`/`localtime`/`gmtime`/`mktime`/`asctime`/`ctime` |
## Sprinter-specific
| Header | Provides |
|---|---|
| `<conio.h>` | `putch`, `cputs`, `cprintf`, `kbhit`, `getch`, `getche`, `clrscr`, `gotoxy`, `wherex/y`, `wrchar`, `rdchar`, `textcolor`, `textbackground`, `textattr`, `get_videomode`, `set_videomode`, `COLOR_*` enum, `KEEP_EXIST_ATTR` |
| `<gfx.h>` | Graphics for 320×256×256 and 640×256×16: `gfx_init`/`gfx_done`, `gfx_pal_load`/`gfx_pal_set`, `gfx_clear`, `gfx_putpixel`, `gfx_hline`/`gfx_vline`, `gfx_rect`/`gfx_fill_rect`, `gfx_line`, `gfx_text`/`gfx_putchar`, plus all `*16` variants for 16-color mode, font management via `gfx_load_default_font`/`gfx_set_font` |
| `<mouse.h>` | Full 14-function driver wrapper: `mouse_init`/`mouse_show`/`mouse_hide`/`mouse_read`/`mouse_goto`/`mouse_bounds_*`/`mouse_text_cursor`/`mouse_load_cursor`/`mouse_get_cursor`/`mouse_set_sensitivity`/`mouse_get_sensitivity_*`/`mouse_video_mode_changed`/`mouse_refresh`, plus `mouse_cursor_t` and `mouse_state_t` structs |
| `<dir.h>` | `chdir`, `getcwd`, `mkdir`, `rmdir`, `ffirst`, `fnext`, `ffblk` struct |
| `<sprinter.h>` | Raw port numbers, ESTEX/BIOS function-number constants, `__sfr` intrinsics for paging, `print_hex`, `getenv`, `putenv` |
| `<sprinter_exit.h>` | `exit`, `_exit`, `atexit` |
| `<sprinter_mem.h>` | `mem_alloc_pages`, `mem_free_block`, `mem_get_page`, `mem_info`, `bank_read`, `bank_write`, `bank_load_byte`, `bank_store_byte` |
| `<sprinter_compat.h>` | Solid-C compatibility shims — pulls in standard headers and adds `BOOL`/`uint`/`WORD`/`f_point` types, `setmem`/`movmem` aliases, `inp`/`outp`, `enable`/`disable`, `min`/`max`, `home()`, `seek`/`tell`/`remove`, `_ffirst`, `ms_*` mouse aliases, etc. |
## Quick lookup: I want to ...
* **Print text** → `<stdio.h>` (`printf` / `puts` — fast, no colour) or
`<conio.h>` (`cprintf` / `cputs` — applies `textcolor`).
* **Read a key** → `<conio.h>`: `getch()` (blocking, no echo), `getche()` (echo),
`kbhit()` (non-blocking poll).
* **Open / read / write files** → `<unistd.h>` + `<fcntl.h>` (POSIX) or
`<stdio.h>` (`fopen` family).
* **List a directory** → `<dir.h>`: `ffirst` / `fnext`.
* **Draw pixels** → `<gfx.h>`.
* **Allocate memory** → `<stdlib.h>`: `malloc` / `free` / `calloc` / `realloc`.
* **Get current time** → `<time.h>`: `getdatetime` or POSIX `time`/`localtime`.
* **Read mouse** → `<mouse.h>`.
* **Read an env var** → `<sprinter.h>`: `getenv` / `putenv`.
* **Set text colour** → `<conio.h>`: `textcolor(COLOR_YELLOW)`,
`textbackground(COLOR_BLUE)`, or `textattr(COLOR(fg, bg))`.
+49
View File
@@ -0,0 +1,49 @@
# Memory modes
Sprinter's CPU address space is four 16 KB windows. ESTEX DSS allocates RAM
pages by program size — programs ≤ 16 KB get only **one** page, so the
"default" Spectrum-style "code in W1, data in W2" layout silently fails for
small programs. This is why `sprinter-cc` has explicit memory modes.
## The five modes
| Mode | Code at | Data at | Banks in | Use when |
|---|---|---|---|---|
| `tiny` (default) | W2 (0x8100+) | chained after code | — | code + data ≤ 14 KB |
| `small` | W1 (0x4100+) | chained after code | — | code + data ≤ 30 KB |
| `big` | W2 (0x8100+) | chained after code | **W1** (0x4000) | tiny + extra banked modules |
| `huge` | W1 (0x4100+) | W2 (0x8000+) | **W3** (0xC000) | small + extra banked modules |
| `manual` | user-specified | user-specified | user-specified | special cases |
## Choosing
Start with **`tiny`**. If `sprinter-cc` says "_CODE too big" or the program
mysteriously fails to start, jump straight to `small`.
If you have a large code base (>32 KB total) and want to keep most of it
out of the always-resident window, use `huge` and split modules into banks
with `--bank N=mod.c`. `big` is the same idea but with banks in W1 instead
of W3 — useful when you need W3 for hardware (graphics, mapped memory).
## Stack and heap
The stack starts at `0xBFFE` (top of W2) and grows down. The heap starts
right after BSS and grows up. By default they share W2 with about 1.2 KB
reserved for the stack and the rest for the heap.
`--stack-size N` reserves more (or less) for the stack at the cost of heap.
## What's actually under the hood
Every mode picks a different `runtime/crt0_*.s`:
* `tiny`: `crt0.s` — SP = 0xBFFE, parse argv, call main.
* `small`: `crt0_small.s` — read port 0xC2 to see if DSS gave W2. If not,
allocate one page via ESTEX `$3D` and map it via `$3A SETWIN2` *before*
switching the stack — BIOS calls need the stack in W2, ESTEX calls don't.
* `big`: `crt0_banked.s` with `BANK_W1=1` prepended — banks live at
`0x{N}4000` and trampolines use port `0xA2`.
* `huge`: `crt0_banked.s` (default `BANK_W1=0`) — banks at `0x{N}C000` via
port `0xE2`. Also includes the small-mode W2 auto-allocate.
For `manual` see `sprinter-cc --memory-manual SPEC` syntax in `sprinter_cc.md`.
+82
View File
@@ -0,0 +1,82 @@
# `sprinter-cc` — compiler driver
One-line entrypoint to the entire toolchain. Takes `.c` files plus options
and emits a SprintEXE.
## Synopsis
```
sprinter-cc -o OUT.exe SRC.c [more.c ...] [options]
```
## Options
### Memory layout
| Flag | Description |
|---|---|
| `--memory MODE` | `tiny` (default), `small`, `big`, `huge`, `manual`. See `memory_modes.md`. |
| `--memory-manual SPEC` | For `--memory manual`: comma-separated `KEY=VAL` list, e.g. `CODE=W2,DATA=W2,BANKED=W3`. |
| `--stack-size N` | Bytes reserved for the stack. Default ≈ 1278. Larger value reduces the heap. |
### Code organisation
| Flag | Description |
|---|---|
| `--bank N=FILE.c` | Compile FILE.c into bank N (1..15). Repeatable. Functions in banked files need the `__banked` qualifier. |
| `--crt0=TYPE` | Override startup file: `default` / `minimal` / `banked` / `small`. Normally chosen automatically by the memory mode. |
### Diagnostics
| Flag | Description |
|---|---|
| `--debug` | Prepends `DEBUG_RT = 1` to crt0 and passes `-DDEBUG_RT` to SDCC. Exposes runtime introspection symbols like `_w2_self_allocated`. |
| `-v` | Verbose — echo every sub-command. |
| `-h` / `--help` | Built-in help. |
### Passthrough
| Flag | Description |
|---|---|
| `-I PATH` | Extra include path. |
| `-Wl FLAG` | Pass FLAG to the linker. |
| `--mkexe FLAG` | Pass FLAG to mkexe (e.g. `--mkexe -p --mkexe 0` for zero-padded banks). |
| `-L 0xADDR` | Override load address. |
| `-E 0xADDR` | Override entry address. |
| `-S 0xADDR` | Override initial stack address. Default `0xBFFE`. |
## Examples
Smallest possible build:
```sh
sprinter-cc -o hello.exe hello.c
```
Larger program (doesn't fit in 14 KB):
```sh
sprinter-cc --memory small -o big.exe big.c
```
Multi-bank game:
```sh
sprinter-cc --memory huge -o game.exe \
main.c --bank 1=engine.c --bank 2=ai.c --bank 3=audio.c
```
Custom stack size:
```sh
sprinter-cc --stack-size 4096 -o app.exe app.c
```
## Under the hood
1. Picks crt0 based on `--memory` (and `--bank` presence).
2. Assembles crt0 (with optional `DEBUG_RT` / `BANK_W1` prepended).
3. Assembles `heap_top.s` (custom value if `--stack-size`).
4. Compiles every source `.c` to `.rel` via SDCC.
5. Compiles bank sources with `--codeseg/--constseg/--dataseg BANK_n`.
6. Compiles `runtime/bank.s` trampoline (if banks are used).
7. Links everything to `.ihx`, runs `check_banks.py` to enforce 16 KB bank limits.
8. Calls `toolchain/mkexe/mkexe` to wrap the `.ihx` as SprintEXE.
Per-build artefacts go in `.sprinter-cc-<basename>/` next to the output.