Александр Петров 4737ec323c MEM-BANK5: pop_ctrl.c → банк 5; чит [/] подгонки Кида по X
Три правки едут вместе намеренно: n_banks и --bank обязаны меняться
атомарно, иначе промежуточный коммит — зависание (см. ниже).

MEM-BANK5.  CODE и DATA делят одно 32-КБ пространство W1+W2, поэтому
килобайт кода, уехавший в банк, — это килобайт, доступный данным.
Кандидат выбран не по размеру, а по частоте вызова: диспетчер управления
дёргается раз в кадр на персонажа и горячих банк→банк переходов не
создаёт (в отличие от pop_level, чей pop_level_tile зовётся из банка 2 на
КАЖДЫЙ тайл).

  _CODE   26 780 -> 24 662 Б   (−2 118)
  куча       180 -> 2 298 Б
  банк 5            2 211 / 16 384 (13.5 %)

Шина control_* (8 глобалов) переехала в pop_state.c.  Сегодня она уцелела
бы и в pop_ctrl.c — банки собираются без --bank-data, их писучие данные
остаются в общем _DATA, — но это флаг сборки, а не свойство кода, а шину
трогают уже три банка: 5 пишет с клавиатуры, 1 подаёт синтетический ввод
ИИ (autocontrol_*, seg002), 3 читает через pop_ctrl_shift_held.
Заодно pop_ctrl.c наконец включает собственный заголовок — раньше
объявления жили прямо в нём.

ГРАБЛИ, на которые наступили (стоили дольше самой задачи): n_banks в
roomtest.c захардкожен, и его надо править вместе с числом --bank.  С
n_banks=4 и пятым банком crt0 выделил четыре страницы, _bank_pages[5]
остался нулём, и первый же вызов pop_ctrl_init() через трамплин
отобразил в W3 страницу 0 и прыгнул на 0xC874 в мусор — исполнение
забрело в дисковый код DSS и осталось крутить чтение секторов.  Симптом:
загрузка ресурсов проходит целиком (open=45 — все атласы), комната и Кид
успевают нарисоваться из enter_room, а HP и номер комнаты уже нет, и
kid_tick не вызывается ни разу.  Ровно предупреждение из шапки
runtime/bank.s.  Сверку n_banks с реальным максимальным индексом банка
записал в docs/TODO.md (Auto-banking) — это должно быть ошибкой сборки.

DBG-CHEATS: [ (0x54) и ] (0x5B) двигают Кида на пиксель (seg000:1828),
по фронту нажатия, под pop_cheats.  Нужны потому, что мост MAME теряет
нажатия при быстрой отправке и подогнать Кида в позу скриптом нельзя —
на это упёрлись BUG-LOOSE-2 и BUG-GATE-PASS-1.

ROOMNAV больше не зовёт pop_trob_reset: reset обнуляет room_seen, то есть
чит ОТМАТЫВАЛ МИР (открытые/закрытые ворота, нажатые кнопки).  Навигация
обязана только телепортировать.

Проверено в MAME: старт уровня 1 рисуется полностью (комната, Кид, HP,
номер), бег вправо и падение на второй ряд отрабатывают, ] даёт x+1 и
[ даёт x−1 по одному нажатию, Shift+→ — осторожный шаг (x 131 -> 142,
колонка 4 -> 5) и при удержании 90 кадров не срывается в бег (x 142 ->
151), то есть pop_ctrl_shift_held работает через границу банк 3 -> банк 5.
Наборы под ucsim: geom 39, grab 53, phys 1723 — все зелёные.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-05 13:45:15 +03:00
2026-06-10 10:41:03 +03:00

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/sysenv over ESTEX $46.
  • Time: getdatetime/setdatetime + POSIX time/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 APIinitgraph / setcolor / circle / getimage / putimage / etc. on top of our gfx_* 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 items
  • docs/solid_c_compatibility.md — gap analysis vs Solid-C 2004
  • docs/im2_isr_design.md — interrupt handler design (v2)
  • docs/converted/ — source documentation (ESTEX, BIOS, architecture) converted to plain text for grep
  • docs/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 (see third_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.

S
Description
No description provided
Readme MIT 12 MiB
Languages
C 84%
Python 8.2%
Makefile 2.8%
Assembly 2.7%
Shell 2.3%