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:
@@ -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 |
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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))`.
|
||||
@@ -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`.
|
||||
@@ -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.
|
||||
Reference in New Issue
Block a user