Скелет (L3-SKEL, ассеты + механика): - pop_pack_guard.py получил параметр набора (GUARD/SKEL): атлас скелета poc/res/skel/g0..g3.atl (28 кадров), палитра — из его res750.pal (на ур. 3 curr_guard_color = 0, оригинал палитру не подменяет); - pop_guard_load выбирает набор по tbl_guard_type и перезагружается ПРИ СМЕНЕ УРОВНЯ (load_lev_spr, seg000:1092) — без этого скелет рисовался атласом стража и был невидим; - load_frame: charid_4_skeleton идёт по таблице стража (seg006:529), тень — только в кадрах 150..189. Пока ветка была одна (charid_2_guard), скелет получал image из таблицы Кида (180 при 28 спрайтах) и не рисовался; - check_skel (seg002:1042), ветка charid_4 в enter_guard, возрождение в комнате 3 при падении (seg002:252), autocontrol_skeleton; - leveldoor_open (seg007:456) — новый флаг, сбрасывается стартом уровня. Цвета стражей (BUG-GUARD-COLOR-1, закрыт): - все 7 палитр res10.bin -> pop_guard_pal.h, заливка 16 слотов по guards_color комнаты перед отрисовкой (set_chtab_palette, seg003:257). Проверено в MAME: ур. 2 комн. 11 = цвет 1, комн. 7 = цвет 3, полоса HP меняется вместе со стражем. Грабля: gfx_pal_load отдаёт указатель в BIOS, а тот читает только #4000-#BFFF — таблицу из банка копируем в стек. Кэш соседних комнат (BUG-SWORD-GHOST-1, закрыт): - pop_map кэширует fg соседей слева/справа ЦЕЛИКОМ и резолвит col -10..19. Было -2..11, дальше мнимая стена: луч видимости упирался в неё (страж после follow_guard в col 12), Кид прятал меч посреди боя и не мог достать обратно. +48 байт W2. Окклюзия соперника: - pop_fore_over_char получил проход other_overlay_tile (порядок midtable, seg008:1B06) и расширение перебора объединённым прямоугольником «персонаж + клинок + брызги» — падающий скелет больше не рисуется поверх кладки и верхней грани пола; - клип полем 192 строк (reset_obj_clip, seg006:0507) для спрайта, клинка (общий pop_sword_draw) и брызг — спрайт не залезает на полосу HP; - ROOMNAV после смерти Кида делает честный pop_start_level: телепорт «оживлял» мёртвого мимо старта уровня, оставляя живого скелета рядом с вернувшейся кучей костей. Ассеты чомпера (под L3-CHOMP): весь набор кадров в атласе явным списком (101-105 низ, 111-113 верх, 106-110 фронт, 114-123 кровь mono-силуэтом) — render_room анимированные тайлы пропускает, и в атласе не было ни одного. Число EMM-страниц не изменилось. Тесты: tests-host все 5 наборов зелёные, t_char вырос до 65 проверок (резолв колонок за краем комнаты, возрождение скелета); в testkit добавлен гард «код наехал на данные» (DATA_LOC). Доски: TASKS.md разнесён на TASKS_OPEN/TASKS_CLOSED, закрытые баги с разбором корней — в bug_closed.md; заведены DRAW-CHAR (отрисовка одна на всех Char, как физика после GUARD-PHYS) и L3-COLOR (зелёная кладка уровня 3: level_var_palettes = ресурс 20, есть в MSDOS/PRINCE.DAT). В roomtest.c временно оставлен автостоп на падении соперника (отладка падений скелета) — помечен ВРЕМЕННО. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sprinter C Compiler — v1.0
C toolchain for Sprinter — the Z80-based home computer by Peters Plus, running
ESTEX DSS. Host: macOS / Linux. Target: .EXE files in SprintEXE format.
Built on top of SDCC 4.5 (vendored in third_party/sdcc/). This repository adds
everything Sprinter-specific: crt0, linker integration, libc wrappers over ESTEX,
banked-call trampolines, graphics & accelerator API, mouse driver wrappers, and the
mkexe utility for producing SprintEXE images.
What you get
bin/sprinter-cc— one-line driver:sprinter-cc -o foo.exe foo.c- Memory modes:
tiny,small,big,huge,manual— see below. - stdio + conio: printf, puts, putchar, getchar, fopen/fread/..., cprintf, cputs, putch, textcolor/textbackground/textattr, gotoxy, kbhit/getch.
- Graphics: 320×256×256 and 640×256×16 modes, accelerator-backed primitives (hline / vline / rect / fill_rect / line via Bresenham, plus clear), bitmap-font text in both modes via BIOS character generator.
- File I/O: POSIX (
open/read/write/close/lseek/unlink/creat), FILE* streams (fopen/fgets/fwrite/...), directory listing (ffirst/fnext),chdir/getcwd/mkdir/rmdir,stat/fstat. - Memory: 32 KB heap (W2-resident), banking-aware page allocator (
mem_alloc_pages/bank_read/bank_write), explicit memory modes for sub-16 KB programs. - Mouse: full Sprinter driver wrapper (14 functions including custom cursor bitmaps).
- Environment:
getenv/putenv/sysenvover ESTEX$46. - Time:
getdatetime/setdatetime+ POSIXtime/localtime/mktime/asctime/ctime. - Misc:
errno/strerror/perror,atexit,setjmp/longjmp,sleep, full argv parsing in crt0.
Quick start
git clone <this repo> sprinter-c
cd sprinter-c
make sdcc # one-time: fetch SDCC 4.5 binary (~25 MB)
make all # build mkexe + libsprinter.lib + 27 examples
make floppy # pack everything into mame/v306/IMG/mc.img
cd mame/v306 && ./run_mame.sh # boot Sprinter in MAME
Compile a single program:
cat > hello.c <<EOF
#include <stdio.h>
int main(void) { puts("Hello, Sprinter!"); return 0; }
EOF
bin/sprinter-cc -o hello.exe hello.c
That's it — hello.exe is now a valid SprintEXE you can RUN HELLO from the ESTEX shell.
Memory modes
Sprinter's address space is four 16 KB windows (W0 / W1 / W2 / W3). DSS allocates pages by program size — small programs get only one page. Pick a memory mode based on what your program needs:
| Mode | Code lives in | Banking | Use when | Note |
|---|---|---|---|---|
tiny (default) |
W2 (0x8100+) | no | code+data < 14 KB | |
small |
W1-W2 (0x4100+) | no | code+data < 30 KB | |
big |
W2 + W1 banking | yes (W1) | tiny + extra code modules | |
huge |
W1-W2 + W3 banking | yes (W3) | small + extra code modules | |
manual |
user-specified | optional | special layouts | Not implemented |
sprinter-cc --memory small -o big.exe bigprog.c
sprinter-cc --memory huge -o app.exe main.c --bank 1=engine.c --bank 2=ai.c
Banked functions are declared with __banked:
void engine_tick(int dt) __banked; // lives in BANK1, automatically swapped
Examples (27 total)
| Example | What it demonstrates |
|---|---|
hello |
Hello world with stdio + conio Turbo-C-style colors |
argv |
argv parsing in crt0 |
cat |
File I/O — read & print TEST.TXT |
seek |
32-bit lseek over a 100 KB file |
ls |
Directory listing via ffirst/fnext |
filetest |
FILE* streams (fopen/fread/...) |
stattest |
stat/fstat on files and directories |
errno |
errno / strerror / perror |
mem_test |
Page allocator + bank_read/bank_write |
malloc |
Heap stress test (200+ allocations) |
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 |
gfx_demo |
320×256×256 lines / rects / accelerator |
gfx_d16 |
640×256×16 same primitives |
gfx_text |
Bitmap-font text on graphics screen |
timedir |
Date/time + directory listing |
ptime |
POSIX time API |
openenv |
open() flags + environment vars |
conio |
conio API smoke test |
attrprob |
Probe Sprinter text attribute byte layout |
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) |
Headers
Standard:
<stdio.h>— puts / printf / FILE* + Sprinter-specific dec/hex helpers<stdlib.h>— atoi / atof / malloc / qsort / ... (from SDCC z80.lib)<string.h>/<ctype.h>/<math.h>— from SDCC z80.lib<unistd.h>— read / write / close / lseek / unlink<fcntl.h>— open / creat + O_RDONLY / O_CREAT / ...<errno.h>— errno + error names + strerror<sys/stat.h>— stat / fstat<setjmp.h>/<assert.h>— from SDCC
Sprinter-specific:
<conio.h>— putch / cputs / cprintf, textcolor / textbackground / textattr, kbhit / getch, clrscr, gotoxy, wherex/y<gfx.h>— gfx_init/done, palette, putpixel, hline/vline/rect/fill_rect/line, text — for both 320 and 640 modes (gfx_*16 variants)<mouse.h>— full 14-function driver wrapper + mouse_cursor_t with bitmap support<dir.h>— chdir / getcwd / mkdir / rmdir / ffirst / fnext / ffblk<time.h>— getdatetime / setdatetime + POSIX time / localtime / etc.<sprinter.h>— raw ports, ESTEX/BIOS function numbers, env API<sprinter_exit.h>— exit / _exit / atexit<sprinter_mem.h>— mem_alloc_pages / mem_free_block / bank_read / bank_write<sprinter_compat.h>— Solid-C compatibility layer (aliases + BOOL/WORD/uint types)
Toolchain commands
make all # build mkexe + lib + every example
make floppy # repack mame/v306/IMG/mc.img with all .exe files
make check # 17 mkexe unit-tests
make clean # remove all build artefacts
make sdcc # one-time: fetch SDCC 4.5 binary
sprinter-cc options
sprinter-cc -o foo.exe foo.c [more.c ...] [options]
--memory MODE tiny | small | big | huge | manual (default: tiny)
--memory-manual SPEC explicit placement (CODE=W1|W2,DATA=W1|W2|SAME,BANKED=W1|W3)
--stack-size N bytes reserved for the stack (default ~1278)
--crt0=TYPE default | minimal | banked | small
--bank N=FILE.c compile FILE.c into bank N (repeatable, max 15)
--debug enable runtime diagnostics (defines DEBUG_RT)
-I PATH extra include path
-L 0xADDR / -E / -S override load / entry / stack addresses
-Wl FLAG pass FLAG to sdldz80
--mkexe FLAG pass FLAG to mkexe (e.g. --mkexe -p --mkexe 0 for bank padding)
-v verbose
Status
What works in v1.0:
- Compile / link / pack to SprintEXE — verified on all 27 examples
- Four memory modes (tiny / small / big / huge)
- Graphics (both modes) with accelerator
- Mouse (text + graphics cursor)
- File I/O, directories, environment, time
- All headers listed above
Deferred to v2.0 (see docs/TODO.md):
- Turbo-C-style BGI graphics API —
initgraph/setcolor/circle/getimage/putimage/ etc. on top of ourgfx_*primitives - Remaining Solid-C compatibility gaps (Phase 2/3) — see
docs/solid_c_compatibility.md - Manual memory mode
- Rewrite FILE* stream API (current implementation is very primitive and doesn't use buffers)
Deferred to v3.0:
- IM2 interrupt handlers — research complete (
docs/im2_isr_design.md), implementation scheduled for v3 - Audio API (AY-3-8910 + COVOX) — requires IM2
- ISA-8 slot drivers — requires IM2 (???)
Documentation
docs/TODO.md— roadmap and open work itemsdocs/solid_c_compatibility.md— gap analysis vs Solid-C 2004docs/im2_isr_design.md— interrupt handler design (v2)docs/converted/— source documentation (ESTEX, BIOS, architecture) converted to plain text forgrepdocs/reference/,docs/samples/,docs/memory management/— original Russian docs and code samples from Peters Plus
Repository layout
bin/sprinter-cc one-line compiler driver (bash)
toolchain/mkexe/ host-side tool: .ihx -> .exe SprintEXE
toolchain/check_banks.py post-link bank size enforcer
runtime/ crt0 variants (default, minimal, small, banked)
bank trampolines, heap, heap_top
libc/include/ headers
libc/io|stdio|mem|gfx/ C and asm sources for libsprinter.lib
lib/ Makefile that archives libsprinter.lib via sdar
examples/ 27 example programs
mame/v306/ MAME binary + Sprinter ROM/HDD images + floppy script
third_party/sdcc/ vendored SDCC 4.5 (fetched via `make sdcc`)
third_party/solid-c/ reference: original Sprinter native C (for compat target)
docs/ documentation
License
This repository contains:
- Original code in
bin/,toolchain/,runtime/,libc/,lib/,examples/— MIT-licensed. third_party/sdcc/— SDCC 4.5 under GPLv2 with linking exception (seethird_party/sdcc/COPYING.txt)third_party/solid-c/— original Sprinter Solid C, used only as a reference
Credits
- Sprinter / Peters Plus — Иван Мак, Дмитрий Паринов and the original team
- SDCC — for the underlying Z80 compiler
- MAME — for the Sprinter Sp2000 emulation
For questions / patches: see CONTRIBUTING.md (TBD) or open an issue.