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.
+15
View File
@@ -0,0 +1,15 @@
# Sprinter C Compiler — Документация пользователя (Русский)
Это документация, поставляемая в составе релизного tarball. Английская
версия — `../en/README.md`.
## Содержание
| Файл | Тема |
|---|---|
| `getting_started.md` | Установка, первая сборка, заметки про MAME |
| `sprinter_cc.md` | Драйвер компилятора — все флаги |
| `memory_modes.md` | tiny / small / big / huge / manual |
| `headers.md` | Публичные заголовки и что в них |
| `examples.md` | Обзор 27 встроенных примеров |
| `platform_reference.md` | Особенности платформы Sprinter и нюансы разработки (на основе наших dev-заметок) |
+75
View File
@@ -0,0 +1,75 @@
# Обзор примеров
Релиз содержит 27 примеров в каталоге `examples/`. Каждый — самодостаточная
демо-программа с комментариями (использовались как regression-тесты при разработке).
## Сборка примера
```sh
cd examples/hello
make
```
Результат — `hello.exe` рядом с `hello.c` через `examples/example.mk`.
## Категории
### Hello world / основы
* **`hello`** — stdio + conio Turbo-C-стиль цвета
* **`argv`** — парсинг argv в crt0
* **`conio`** — smoke test conio API
* **`attrprob`** — пробинг байта атрибутов Sprinter
### Файловый ввод-вывод
* **`cat`** — читает и печатает TEST.TXT
* **`seek`** — 32-битный lseek по файлу в 100 КБ
* **`ls`** — листинг каталога через ffirst/fnext
* **`filetest`** — FILE* стримы (`fopen`/`fread`/`fwrite`/`fclose`)
* **`stattest`** — `stat`/`fstat` для файлов и каталогов
* **`openenv`** — флаги open() + environment variables
### Память и банки
* **`malloc`** — стресс-тест heap (200+ allocations)
* **`mem_test`** — page allocator + `bank_read`/`bank_write`
* **`banked`** — banked-код в W3 (huge mode)
* **`bankedbg`** — banked-код в W1 (big mode)
* **`banklocl`** — bank-local статические данные и BSS
### Мышь
* **`mouse`** — драйвер в текстовом режиме
* **`gfx_mous`** — мышь с пользовательским bitmap-курсором в графическом режиме
### Графика
* **`gfx_demo`** — 320×256×256: линии, прямоугольники, fill через accelerator
* **`gfx_d16`** — 640×256×16: те же примитивы в 16-цветном режиме
* **`gfx_text`** — bitmap-текст на графическом экране
### Прочее
* **`errno`** — errno / strerror / perror
* **`timedir`** — дата/время + листинг каталога
* **`ptime`** — POSIX time API (time / localtime / mktime)
* **`strtest`** — `<string.h>` тест (из SDCC's z80.lib)
* **`stdlib`** — `<stdlib.h>` тест (qsort / rand / strtol / etc.)
* **`assrtest`** — assert()
* **`rt_test`** — runtime helpers (sleep, setjmp, atexit)
## Example.mk
Каждый пример использует `examples/example.mk`. Минимальный Makefile:
```makefile
PROJ_ROOT := $(abspath $(CURDIR)/../..)
EXAMPLE := my_app
include $(PROJ_ROOT)/examples/example.mk
```
Опциональные параметры (задаются до `include`):
```makefile
MEMORY := huge # по умолчанию tiny
STACK_SIZE := 4096 # по умолчанию ~1278
EXTRA_SRCS := helper.c util.c # дополнительные .c в той же папке
EXTRA_FLAGS := --bank 1=engine.c --debug # pass-through в sprinter-cc
```
Используйте этот template для своих программ.
+62
View File
@@ -0,0 +1,62 @@
# Начало работы
## Что в tarball
После распаковки `sprinter-c-v1.0-<host>.tar.gz` вы получаете:
* **`bin/sprinter-cc`** — драйвер C → SprintEXE (bash-скрипт).
* **`third_party/sdcc/`** — vendored SDCC 4.5 для шага C → Z80.
* **`libc/include/`** — заголовки для подключения в ваших программах.
* **`lib/sprinter.lib`** — целевая libc для Sprinter (предсобрана; пересобирается через `make`, если изменили исходники libc).
* **`runtime/`** — варианты crt0 и runtime-помощники (ассемблируются per-build).
* **`toolchain/mkexe/`** — host-утилита, упаковывающая `.ihx` SDCC в SprintEXE.
* **`examples/`** — 27 готовых программ.
* **`docs/{en,ru}/`** — эта документация.
## Первая сборка
```sh
cd sprinter-c-v1.0-<host>
make all # пересобрать lib + все примеры (~30 с)
```
Если `make` жалуется на отсутствующий бинарник SDCC — загрузите его один раз:
```sh
make sdcc # скачивает SDCC 4.5 если не vendored
```
## Сборка своей программы
```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` теперь — корректный SprintEXE, который можно запустить на
Sprinter / MAME / в любом ESTEX DSS шелле.
## Запуск на железе или в эмуляторе
Релиз **не содержит** эмулятор MAME или образы ROM / DSS / HDD Sprinter —
они большие и имеют свои лицензии. Для тестирования:
* **MAME:** установите MAME 0.283+ отдельно, получите образы Sprinter Sp2000
у Peters Plus, смонтируйте FAT12-флоп с вашими `.exe` файлами как `-flop1`.
* **Реальный Sprinter:** скопируйте `.exe` на флоп или раздел HDD, видимый
для DSS, затем `RUN HELLO` в шелле.
## Что дальше
* Прочитайте `sprinter_cc.md` про флаги компилятора.
* Прочитайте `memory_modes.md` когда не хватит 14 КБ кода.
* Просмотрите `examples/` — каждый файл это рабочая программа с комментариями.
* `headers.md` — список публичных API.
* `platform_reference.md` — глубокие нюансы платформы и компилятора (от граблей до подводных камней).
+50
View File
@@ -0,0 +1,50 @@
# Заголовки
Все `#include` живут в `libc/include/`.
## Стандартный C
| Заголовок | Источник | Что предоставляет |
|---|---|---|
| `<stdio.h>` | наша libc + SDCC | `printf`, `puts`, `putchar`, `getchar`, `sprintf`, `FILE *`, `fopen`/`fread`/.../`fclose`, плюс `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`, вся семья `strXxx` |
| `<ctype.h>` | SDCC z80.lib | `tolower`, `toupper`, `isalnum`, `isdigit`, ... |
| `<math.h>` | SDCC z80.lib | `sinf`, `cosf`, `sqrtf`, ... |
| `<errno.h>` | наша libc | `errno` + константы ошибок + `strerror` |
| `<setjmp.h>` | наша libc | `setjmp` / `longjmp` |
| `<assert.h>` | SDCC | макрос `assert` |
| `<unistd.h>` | наша libc | `read`, `write`, `close`, `lseek`, `unlink`, `SEEK_SET`/`CUR`/`END` |
| `<fcntl.h>` | наша libc | `open`, `creat`, `O_RDONLY`/`O_WRONLY`/`O_CREAT`/... |
| `<sys/stat.h>` | наша libc | `stat`, `fstat`, `struct stat` |
| `<time.h>` | наша libc | `getdatetime`, `setdatetime` + POSIX `time`/`localtime`/`gmtime`/`mktime`/`asctime`/`ctime` |
## Sprinter-специфичные
| Заголовок | Что предоставляет |
|---|---|
| `<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>` | Графика для 320×256×256 и 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`, все варианты `*16` для 16-color режима, управление шрифтом через `gfx_load_default_font`/`gfx_set_font` |
| `<mouse.h>` | Полная обёртка из 14 функций драйвера: `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`, плюс структуры `mouse_cursor_t` и `mouse_state_t` |
| `<dir.h>` | `chdir`, `getcwd`, `mkdir`, `rmdir`, `ffirst`, `fnext`, структура `ffblk` |
| `<sprinter.h>` | Сырые номера портов, константы ESTEX/BIOS function numbers, `__sfr` intrinsics для 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 — подтягивает стандартные заголовки и добавляет типы `BOOL`/`uint`/`WORD`/`f_point`, алиасы `setmem`/`movmem`, `inp`/`outp`, `enable`/`disable`, `min`/`max`, `home()`, `seek`/`tell`/`remove`, `_ffirst`, mouse-алиасы `ms_*` и т.д. |
## Быстрая навигация: я хочу...
* **Вывести текст** → `<stdio.h>` (`printf` / `puts` — быстро, без цвета) или
`<conio.h>` (`cprintf` / `cputs` — применяет `textcolor`).
* **Прочитать клавишу** → `<conio.h>`: `getch()` (blocking, без эхо),
`getche()` (с эхо), `kbhit()` (non-blocking poll).
* **Открыть / прочитать / записать файл** → `<unistd.h>` + `<fcntl.h>` (POSIX)
или `<stdio.h>` (семья `fopen`).
* **Прочитать каталог** → `<dir.h>`: `ffirst` / `fnext`.
* **Нарисовать пиксели** → `<gfx.h>`.
* **Выделить память** → `<stdlib.h>`: `malloc` / `free` / `calloc` / `realloc`.
* **Получить текущее время** → `<time.h>`: `getdatetime` или POSIX `time`/`localtime`.
* **Прочитать мышь** → `<mouse.h>`.
* **Прочитать env var** → `<sprinter.h>`: `getenv` / `putenv`.
* **Задать цвет текста** → `<conio.h>`: `textcolor(COLOR_YELLOW)`,
`textbackground(COLOR_BLUE)`, или `textattr(COLOR(fg, bg))`.
+51
View File
@@ -0,0 +1,51 @@
# Режимы памяти
Адресное пространство CPU Sprinter — четыре окна по 16 КБ. ESTEX DSS
выделяет страницы RAM по размеру программы — программам ≤16 КБ
выделяется **одна** страница. Из-за этого "очевидная" Spectrum-style
раскладка "код в W1, данные в W2" для маленьких программ молча ломается.
Именно поэтому `sprinter-cc` имеет явные memory modes.
## Пять режимов
| Mode | Код в | Данные в | Банки в | Когда использовать |
|---|---|---|---|---|
| `tiny` (default) | W2 (0x8100+) | сразу после кода | — | код + данные ≤ 14 КБ |
| `small` | W1 (0x4100+) | сразу после кода | — | код + данные ≤ 30 КБ |
| `big` | W2 (0x8100+) | сразу после кода | **W1** (0x4000) | tiny + дополнительные banked-модули |
| `huge` | W1 (0x4100+) | W2 (0x8000+) | **W3** (0xC000) | small + дополнительные banked-модули |
| `manual` | задаётся вручную | вручную | вручную | специальные случаи |
## Как выбирать
Начните с **`tiny`**. Если `sprinter-cc` ругается "_CODE too big" или
программа таинственно не запускается — сразу переходите на `small`.
Если кодовая база большая (>32 КБ) и нужно держать большую часть кода
вне always-resident окна — используйте `huge` и разбивайте модули на
банки через `--bank N=mod.c`. `big` — та же идея, но банки в W1 вместо
W3 — полезно когда W3 нужен для железа (графика, mapped-memory).
## Стек и heap
Стек начинается с `0xBFFE` (верх W2) и растёт вниз. Heap начинается
сразу после BSS и растёт вверх. По умолчанию они делят W2, с ~1.2 КБ
зарезервированными под стек и остальным под heap.
`--stack-size N` резервирует больше (или меньше) под стек ценой heap.
## Что внутри
Каждый режим выбирает свой `runtime/crt0_*.s`:
* `tiny`: `crt0.s` — SP = 0xBFFE, парсит argv, вызывает main.
* `small`: `crt0_small.s` — читает порт 0xC2 чтобы проверить дала ли
DSS уже W2. Если нет — выделяет страницу через ESTEX `$3D` и маппит
через `$3A SETWIN2` **до** переключения стека (BIOS-вызовы требуют
стек в W2, ESTEX — нет).
* `big`: `crt0_banked.s` с `BANK_W1=1` — банки живут по `0x{N}4000` и
trampoline'ы используют порт `0xA2`.
* `huge`: `crt0_banked.s` (default `BANK_W1=0`) — банки по `0x{N}C000`
через порт `0xE2`. Также включает small-mode W2 auto-allocation.
Для `manual` см. синтаксис `sprinter-cc --memory-manual SPEC` в `sprinter_cc.md`.
+374
View File
@@ -0,0 +1,374 @@
# Platform Reference — особенности разработки под Sprinter
Документ собирает все нетривиальные нюансы платформы Sprinter и нашего
C-toolchain'а, накопленные в процессе разработки. Если вы пишете
программу и что-то "молча не работает" — скорее всего ответ здесь.
---
## 1. Архитектура Sprinter (cheat-sheet)
* **CPU:** Z84C15 (Z80 совместимый), 21 МГц / 3.5 МГц.
* **Адресное пространство:** 4 окна по 16 КБ:
* **W0** (0x0000..0x3FFF) — ESTEX DSS система
* **W1** (0x4000..0x7FFF) — обычно HOME-программа
* **W2** (0x8000..0xBFFF) — обычно данные / стек
* **W3** (0xC000..0xFFFF) — обычно banked / видео
* **Порты page-select:** 0x82 / 0xA2 / 0xC2 / 0xE2 для W0..W3 соответственно.
Запись номера страницы переключает окно. **Чтение** — возвращает текущий
номер страницы (полезно для детектирования).
* **Системные вызовы:**
* `RST 10h` — ESTEX DSS, номер функции в C
* `RST 8` — BIOS, номер функции в C
* `RST 30h` — Mouse driver, номер функции в C
* **Видеорежимы:** 320×256×256, 640×256×16, 80×32 text, 40×32 text.
* **Формат EXE:** SprintEXE (512-байтный header + образ HOME + опциональные банки).
---
## 2. Подводные камни вызовов системы
### IX **обязательно** сохранять
ESTEX и BIOS клобберят IX без предупреждения. SDCC использует IX как
frame pointer. Каждая обёртка над `RST 10h` / `RST 8` / `RST 30h`
ДОЛЖНА оборачивать вызов в `push ix` / `pop ix`:
```asm
push ix
ld c, #0x47 ; ESTEX APPINFO
rst #0x10
pop ix
```
Забудешь — frame pointer уедет, локальные переменные станут мусором,
debug будет долгим.
### BIOS требует стек в W2
BIOS-вызовы (`RST 8` / `CALL 3D13h`) требуют SP в диапазоне 0x8000..0xBFFF
(W2). ESTEX-вызовы — нет (они используют свой стек).
Практическое следствие: в `crt0_small.s` нельзя выделять W2 через
`BIOS $C4 EMM_GETPAGE + OUT (0xC2)`, потому что на этот момент стек
ещё в W1. Делается через **ESTEX `$3A SETWIN2`** — он маппит страницу
сразу из ESTEX без BIOS.
### ESTEX `$46 ENV` — баг в документации
`DiskSyscalls.txt v1.6` пишет: `A=0 — FOUND, A=1 — NOT FOUND`. Реально
наоборот: **A=0 — NOT FOUND**. Все наши getenv/putenv учитывают это.
### ESTEX `$21 SYSTIME` — день недели 1-based
`dow` (day-of-week) возвращается как `1..7` где `1 = Sunday`, `7 = Saturday`.
Если код ждёт `0..6` или `1..7` начиная с Monday — будет смещение.
### ESTEX `$19 F_FIRST` и каталоги "." / ".."
Запись с именем `"."` или `".."` для родительского каталога **не возвращается**
функцией F_FIRST при поиске `"*.*"` в подкаталоге. Если они нужны —
итерироваться через явные `"."` и `".."` запросы.
### `puts()` после `PCHARS` иногда теряет следующий байт
ESTEX `$5C PCHARS` после себя оставляет cursor state в котором
**следующий** `$5B PUTCHAR` может быть проигнорирован. Решение:
если нужна новая строка после PCHARS — встроить `\r\n` ВНУТРИ строки
для PCHARS, не вызывать отдельный PUTCHAR. Наш `puts()` так и делает.
---
## 3. SDCC ABI — нетривиальные моменты
### `__sdcccall(1)` — смешанная схема передачи
* **1-й 16-битный аргумент** → HL
* **2-й 16-битный аргумент** → DE
* **3-й и далее** → стек
* **1-й uint8_t / char** → A (не L!)
* **Long-аргументы на стеке** → caller pops
* **Int-аргументы на стеке** → callee pops
### Возврат значений
* `int` / `uint16_t` / `pointer`**DE** (НЕ HL как в старых SDCC!)
* `char` / `uint8_t` → A (low byte of DE)
* `long` / `uint32_t` → DE:HL (DE=low word, HL=high word)
* `float` → DE:HL по тому же layout
Самая частая ошибка — `ld a, l` в обёртках для char-возврата. Нужно
`ld a, e` (потому что char идёт в low byte регистра возврата = E).
### `__asm` блок клобберит DE, но SDCC об этом не знает
SDCC иногда сохраняет указатель аргумента в DE между C-кодом и
inline asm. Если внутри `__asm` написать `ld a, d` или `ld a, e`
(например для извлечения возврата из RST), DE будет клобберн, и
post-asm код типа `c->field = ...` запишет в случайный адрес.
**Решение:** парковать указатель в static BSS перед `__asm`, после
загружать заново:
```c
static mouse_cursor_t *dest = 0;
void mouse_get_cursor(mouse_cursor_t *c) {
dest = c;
__asm
; ... clobbers DE ...
__endasm;
mouse_cursor_t *p = dest; // SDCC fetches fresh from BSS
p->width = mc_width; // writes to correct address
}
```
### "Static без инициализатора" грабли BSS
Несколько подряд `static uint8_t x;` БЕЗ `=0` могут сколлапсировать в
**один и тот же адрес** — записи в одну стомпают другие.
```c
static uint8_t a; // адрес 0x9100
static uint8_t b; // ТОЖЕ адрес 0x9100!
static uint8_t c; // ТОЖЕ 0x9100!
a = 0xAA; b = 0xBB; c = 0xCC;
// a == b == c == 0xCC
```
**Решение:** всегда инициализировать: `static uint8_t a = 0;` — SDCC
гарантированно резервирует разные адреса.
### z80.lib почти полная — НЕ переписывать
SDCC z80.lib содержит работающие реализации:
- `atoi / atol / atof / strtol / strtoul`
- `malloc / free / calloc / realloc` (мы только переопределили heap location)
- `qsort / bsearch / rand / srand / abs / div`
- Полный `<string.h>` (memcpy/memset/strlen/strcmp/strcpy/strchr/strstr/strtok/etc.)
- `<ctype.h>` (toupper/tolower/isalpha/isdigit/etc.)
- `<math.h>` (sinf/cosf/sqrtf/etc.)
Линкер автоматически тянет нужное из z80.lib когда есть unresolved
symbol. Не переписывать ради переписывания.
---
## 4. Banking — нюансы
### ABI banked-вызовов
SDCC эмитит для `void f(int x) __banked`:
- символ `b_f = N` (bank id = число из `--codeseg BANKn`)
- символ `_f` = адрес внутри банка (с `bank_id` в верхнем 8-битном байте)
Вызов:
```asm
ld hl, #arg_value
push hl
ld e, #b_f ; E = bank id
ld hl, #_f ; HL = target addr (low 16 bits)
call ___sdcc_bcall_ehl
pop af ; caller cleans up arg
```
### Стековый "spacer" в trampoline
Между ret-адресом callee'я и аргументами трамплин ОБЯЗАН вставить
**ровно 3 байта** (1 сохранённая страница + 2 байта внутреннего
bcall return). SDCC компилирует доступ к аргументам с offset'ом +5
от стека. Любая разница ломает все banked-вызовы.
### CRITICAL: `pop af; out (n), a` клобберит A
Старый trampoline восстанавливал W3-страницу через `pop af`
**клоббит A**, а SDCC возвращает uint8_t/char именно в A. Все
banked-функции с char-возвратом тихо теряли результат.
Текущая версия использует `pop bc; ld c, #port; out (c), b` — порт
через C, значение через B, A сохраняется нетронутым.
### Bank-local статические данные
Для модуля целиком в банке:
```sh
sdcc --codeseg BANK1 --constseg BANK1 --dataseg BANK1 -c bank1.c
```
Всё (код + const + BSS) живёт в банке. `mkexe -p 0` нужен чтобы BSS
загружался обнулённым (иначе будет FF из padding).
Heap через `malloc()` из banked-функции работает прозрачно — heap в
W2 (HOME), W2 trampoline никогда не свопит, указатель валиден из
любого контекста.
---
## 5. Видео-режимы и графика
### Mode 0x81 (320×256×256)
* Адресация: pixel `(x, y)` → CPU-адрес `0xC000 + x` с **Port_Y (0x89) = y**
* 320 байт на видимую строку
* Палитра: 256 цветов из 4 палитр (BIOS `$A4 PIC_SET_PAL`)
* Cleanup: 0x300..0x39F = mode-descriptors, 0x3E0..0x3FF = palette данные — НЕ трогать в обычной отрисовке
### Mode 0x82 (640×256×16)
* Та же row-addressing что и 320 mode (320 байт на строку)
* НО каждый байт = 2 пикселя по 4 бита
* **HIGH nibble (биты 7-4) = LEFT пиксель** (even x)
* **LOW nibble (биты 3-0) = RIGHT пиксель** (odd x)
* Доки пишут "первыми младшие 4 бита" — это про **временной** порядок
в FPGA-сериализаторе, **не пространственный** на экране
* Палитра: 16 нижних цветов из любой из 4 палитр
### Графический Accelerator
CPU-опкоды используются как control-сигналы (NOP-tricks):
- `LD D,D` (0x52) — "ждать LD A, imm для размера блока"
- `LD C,C` (0x49) — Fill mode (горизонталь): `LD (HL),A` заполняет N байт
- `LD E,E` (0x5B) — Fill mode (вертикаль): `LD (HL),A` заполняет N pix вертикально (авто-Y инкремент)
- `LD L,L` (0x6D) — Copy (horizontal)
- `LD A,A` (0x7F) — Copy (vertical)
- `LD B,B` (0x40) — выключить accel
**КРИТИЧНО:** размер блока должен быть **immediate операндом** `LD A, n` (опкод 0x3E nn) сразу после `LD D,D`. Accel snoop'ит этот байт. `LD A, (mem)` (опкод 0x3A) — другой 2-й байт, accel захватит мусор.
**КРИТИЧНО 2:** между `LD C,C` (Fill mode) и `LD (HL),A` (fire) **нельзя** ставить второй `LD A, #imm` — accel re-interpret'ит его как новый block-size. Color через `C`/`B` регистр + `LD A, C` (опкод 0x79, 1 байт).
**Скорость:** ~7 µs/byte vs ~14-20 µs/byte ручного цикла → ~2-3× быстрее.
**Прерывания:** DI/EI обязательны вокруг accel — он подменяет систему команд CPU, ISR в это время крашит.
Реализация в `libc/gfx/gfx_lines.c``gfx_hline`/`gfx_vline`/`gfx_rect`/`gfx_fill_rect`. `gfx_clear` использует accel-burst column-major (320 vfill'ов × 256 пикселей за burst, ~4× быстрее ручного цикла).
### Формат шрифта BIOS
Шрифт 2 КБ = 256 chars × 8 rows × 1 byte/row, **interleaved row-major**:
`offset = row * 256 + char_code`.
То есть row 0 всех 256 chars лежит в 0x000..0x0FF, row 1 в 0x100..0x1FF, ...
Не "char 0 в 0x000..0x007, char 1 в 0x008..0x00F"! Наивное `font[char*8+row]` даст нечитаемую кашу.
Bit order внутри byte: **MSB-first**. Bit 7 = крайний левый пиксель.
Получить системный шрифт: BIOS `$B8 WIN_GET_ZG`, DE = destination, читает 2 КБ.
---
## 6. Memory modes
DSS выделяет страницы RAM по размеру программы. **Программа ≤16 КБ получает только 1 страницу.** В остальные окна подключается "страница `0xFF`" (read = 0xFF, write игнорируется).
Из-за этого классическая раскладка "код в W1 (0x4100+), данные в W2 (0x8000+)" для маленькой программы **молча не работает** — write в W2 уходит в никуда.
Решение — наши 5 режимов:
| Mode | Layout | Trick |
|---|---|---|
| tiny | CODE + DATA в W2 | DSS гарантирует W2 (загружает в неё образ) |
| small | CODE в W1, DATA chained | crt0_small читает порт 0xC2 — если 0xFF, выделяет W2 через ESTEX `$3D`/`$3A` |
| big | tiny + банки в W1 | crt0_banked с BANK_W1=1, trampoline свопит порт 0xA2 |
| huge | small + банки в W3 | crt0_banked с default BANK_W1=0, trampoline на port 0xE2 |
### Детектирование W2 — порт 0xC2
`IN A, (0xC2)` возвращает текущую страницу в W2. `0xFF` = "не выделена". Используется в crt0_small для auto-detect.
**Грабли:** в первой версии стоял `IN A, (0xA2)` — это **W1**, не W2! Для small mode там всегда code-page (не 0xFF) → auto-detect не срабатывал, W2 не выделялась, программа крашилась.
---
## 7. Mouse driver
* Всё через `RST 30h`, номер функции в `C`.
* Координаты в **пикселях**. Для text mode 03h (80×32) делить x/8 и y/8.
* **Sensitivity = divider** (не коэффициент): меньше = быстрее курсор.
Документация ProgrammerManual пишет наоборот — это ошибка.
* MAME `$0E GET_SENSITIVE` возвращает 0 (stub). Workaround: всегда
ставить значение через `mouse_set_sensitivity()` при старте.
* MAME `$0B RETURN_CURSOR` пишет битмап в IX-буфер, но не обновляет H/L/D/E.
`mouse_get_cursor()` вернёт width/height/hot_x/hot_y как 0 — это известное ограничение эмулятора.
* **Cursor bitmap format**: 1 byte per pixel, row-major; `0xFF = transparent`.
Cursor живёт в отдельном видео-банке, не в 0x50 page.
### `$81 CHANGE VIDEO MODE`
При смене видеорежима — звать `mouse_video_mode_changed(new_mode)`.
**Важно:** аргумент `A = режим экрана` обязателен, в документации
указан но легко пропустить. Без него драйвер не пересинхронизирует
координаты, и в graphics mode может остаться text-mode XOR-курсор.
---
## 8. Linker warnings — что есть и почему
`sdldz80` пишет `?ASlink-Warning-Definition of public symbol '_X' found more than once` когда наша `sprinter.lib` override'ит функцию из SDCC's `z80.lib`.
Текущие overrides:
- `_puts` — наш через PCHARS+\r\n vs SDCC стандартный
- `___sdcc_heap` — наш heap в W2 vs стандартный
- `_asctime`, `_localtime` — наш `posix_time.c` vs SDCC's `time.rel` (требует _RtcRead)
Линкер берёт **первое найденное** определение — это наши. Warning только шум.
`sprinter-cc` отфильтровывает эти warning-блоки из вывода `sdcc` (3 строки: warning + 2 follow-up `Library:` строк). Через `-v` всё видно.
---
## 9. Текстовый вывод — Turbo-C convention
В Sprinter нет ESTEX-функции "set persistent attribute" — только WRCHAR пишет char+attr единоразово. Поэтому два набора функций:
| Группа | Header | Скорость | Цвет |
|---|---|---|---|
| stdio | `<stdio.h>` | Fast (~5 µs/char через PCHARS / PUTCHAR) | НЕТ — ambient |
| conio | `<conio.h>` | Slow (~50 µs/char через WRCHAR) | ДА — `g_text_attr` |
`puts` / `printf` / `putchar` — быстрые без цвета. Цвет = whatever shell оставил. Программа должна `clrscr_attr(attr)` если нужен конкретный default.
`cputs` / `cprintf` / `putch` — медленные с цветом. Применяют `g_text_attr` (`textcolor`/`textbackground`/`textattr`). При `g_text_attr == KEEP_EXIST_ATTR` (0xFFFF) — fallback на fast path.
**Cputs/putch НЕ делают `\n` → CR LF translation** (как в Turbo C). Caller должен явно писать `"\r\n"`. `puts` делает.
---
## 10. Прочее
### `dec/hex8/16/32` — мини-форматтеры
Solid-C-style минимальный вывод чисел без формата:
```c
hex8(0xAB); // печатает "AB"
hex16(0xCAFE); // "CAFE"
dec16(50000); // "50000"
```
Использовать когда не хочется тащить полный printf.
### `<sprinter_compat.h>`
Единый header который подтягивает все стандартные + добавляет Solid-C
shims: `BOOL`/`uint`/`WORD`/`f_point` types, `setmem`/`movmem`/`min`/`max`
макросы, `inp`/`outp`, `enable`/`disable`, `ms_*` mouse aliases.
Программы из Solid-C 2004 портируются с минимальными правками.
### `--debug` runtime flag
```sh
sprinter-cc --debug -o foo.exe foo.c
```
Prepend'ит `DEBUG_RT = 1` в crt0 + передаёт `-DDEBUG_RT` в SDCC. Открывает symbol `_w2_self_allocated` (uint8_t) — runtime diagnostic кто аллоцировал W2 (0 = DSS, 1 = crt0 сам). Полезно для troubleshooting'а в small mode.
### MAME testing workflow
```sh
make floppy # пакует все .exe + data в mame/v306/IMG/mc.img
cd mame/v306 && ./run_mame.sh
```
Имена файлов на флопе должны быть 8.3 (FAT12). Все примеры названы соответственно — `banked_big → bankedbg`, `seek_demo → seek`, `time_dir_test → timedir`, etc.
---
## История изменений
- 2026-06-01 — первый релиз v1.0
+82
View File
@@ -0,0 +1,82 @@
# `sprinter-cc` — драйвер компилятора
Однострочный вход во всю цепочку инструментов. Принимает `.c` файлы и
опции, выдаёт SprintEXE.
## Синопсис
```
sprinter-cc -o OUT.exe SRC.c [more.c ...] [options]
```
## Опции
### Раскладка памяти
| Флаг | Описание |
|---|---|
| `--memory MODE` | `tiny` (по умолчанию), `small`, `big`, `huge`, `manual`. См. `memory_modes.md`. |
| `--memory-manual SPEC` | Для `--memory manual`: список `KEY=VAL` через запятую, например `CODE=W2,DATA=W2,BANKED=W3`. |
| `--stack-size N` | Сколько байт зарезервировать под стек. По умолчанию ≈1278. Большее значение уменьшает heap. |
### Организация кода
| Флаг | Описание |
|---|---|
| `--bank N=FILE.c` | Компилировать FILE.c в банк N (1..15). Повторяемый. Функции в banked-файлах нужны с квалификатором `__banked`. |
| `--crt0=TYPE` | Переопределение startup-файла: `default` / `minimal` / `banked` / `small`. Обычно выбирается автоматически по memory mode. |
### Диагностика
| Флаг | Описание |
|---|---|
| `--debug` | Подмешивает `DEBUG_RT = 1` в crt0 + передаёт `-DDEBUG_RT` в SDCC. Открывает runtime-symbols типа `_w2_self_allocated`. |
| `-v` | Verbose — печать каждой подкоманды. |
| `-h` / `--help` | Встроенная справка. |
### Pass-through
| Флаг | Описание |
|---|---|
| `-I PATH` | Дополнительный include-путь. |
| `-Wl FLAG` | Передать FLAG линкеру. |
| `--mkexe FLAG` | Передать FLAG в mkexe (например `--mkexe -p --mkexe 0` для zero-padded банков). |
| `-L 0xADDR` | Переопределить load-адрес. |
| `-E 0xADDR` | Переопределить entry-адрес. |
| `-S 0xADDR` | Переопределить стартовый стек. По умолчанию `0xBFFE`. |
## Примеры
Минимальная сборка:
```sh
sprinter-cc -o hello.exe hello.c
```
Программа побольше (не помещается в 14 КБ):
```sh
sprinter-cc --memory small -o big.exe big.c
```
Многобанковая игра:
```sh
sprinter-cc --memory huge -o game.exe \
main.c --bank 1=engine.c --bank 2=ai.c --bank 3=audio.c
```
Свой размер стека:
```sh
sprinter-cc --stack-size 4096 -o app.exe app.c
```
## Что происходит внутри
1. Выбирает crt0 на основе `--memory` (и наличия `--bank`).
2. Ассемблирует crt0 (с опциональным `DEBUG_RT` / `BANK_W1`).
3. Ассемблирует `heap_top.s` (custom значение если `--stack-size`).
4. Каждый `.c``.rel` через SDCC.
5. Bank-исходники компилируются с `--codeseg/--constseg/--dataseg BANK_n`.
6. Компилирует trampoline `runtime/bank.s` (если есть банки).
7. Линкует всё в `.ihx`, запускает `check_banks.py` для проверки 16 КБ лимита.
8. Вызывает `toolchain/mkexe/mkexe` для упаковки `.ihx` в SprintEXE.
Промежуточные файлы лежат в `.sprinter-cc-<basename>/` рядом с выходным.