snark13 2349481b86 SprPoP: звуковые эффекты без перелинковки — раскладка уехала на диск
Часть II плана music_runtime_index_plan.md (SI0..SI4).  gen/pop_sound_tbl.h
БОЛЬШЕ НЕ ГЕНЕРИРУЕТСЯ: раскладка набора читается из SND/snd.idx (формат
PSI1, писатель и разборщик — tools/pop_idx.py, 22 теста в make test-tools).
Один и тот же sprpop.exe работает с набором SDLPoP (9 страниц) и MSDOS
(10) — sha256 бинарника при смене набора не меняется.

Заодно умолчание источника эффектов переведено на SDLPoP (SND_SRC=sdlpop):
сборка обязана работать без оригинального дистрибутива DOS.  У кого он
есть, включает лучший набор явно — make SND_SRC=msdos (там полнее
оцифровка: в SDLPoP звук 48 spiked пустой).

Устройство: pop_snd_tbl/pop_snd_page/pop_snd_pages — резидентные данные
(pop_snd_data.c), тип и инварианты — рукописный pop_snd_tbl.h.  Записи
читаются ОДНИМ read прямо в таблицу, поэтому sizeof(pop_snd_ent_t) == 5
стало частью дискового контракта: проверяется статически и полем размера
записи в заголовке.  POP_SND_PAGES как compile-time размер набора исчез —
вместо него POP_SND_MAX_PAGES (вместимость, 16) и runtime pop_snd_pages.

Цена: таблица переехала из _CODE в _DATA, суммарный резидент почти не
изменился (куча 239 -> 229 Б); банк 8 +601 Б на чтение и валидацию.

Валидация не доверяет файлу: заголовок целиком плюс каждая запись
(страница, смещение, кратность блоку, непересечение с блоком тишины,
выход за последнюю страницу).  Последнее считается В БЛОКАХ — байтовый
адрес конца не влезает в uint16, а 32-битная арифметика на Z80 дорога.

НЕТ ИНДЕКСА — ЭФФЕКТОВ НЕТ, НО МУЗЫКА ИГРАЕТ.  Первая версия просто
возвращала ошибку, и игра становилась непроходимой: тишину льёт первый
блок набора, без набора CBL не открывался, а с ним вставала музыка (её
блоки считает тот же насос) — заставка ждала конца трека вечно.  Теперь
поднимается пустой набор с блоком тишины.  Заливается ровно 128 байт и
под DI: gfx_w0_page_prepare ставит в страницу IRQ-стабы, и заливка всей
страницы затирала их — первое же прерывание давало чёрный экран.

Грабли сборки: смена SND_SRC тихо давала неверный результат
(sdlpop -> msdos -> sdlpop оставлял чужой набор в assets/packed).  Причина
не в логике, а в секундной гранулярности mtime.  Лечение убирает время из
решения: смена варианта сносит stamp'ы своего семейства, а упаковка,
сборка архива и копия индекса делаются одним рецептом.  То же получила и
музыка (MUSIC_FMT).

Проверено в MAME: таблица в памяти совпадает с файлом из образа побайтово;
один EXE поднимает оба набора; отладочный --order reverse (30 из 31
записей отличаются от штатных) звучит правильно; битый индекс выключает
эффекты, не роняя игру; без индекса PV-сцена проходит с музыкой; Ctrl+S
работает в обоих режимах.  Разбор — docs/sound_plan.md §10.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011MsUsEFAQfsjjQpJ7RtKVY
2026-08-31 16:14:53 +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, consecutive 1..15;
                          crt0 bank count is generated automatically)
  --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 36 MiB
Languages
C 65.4%
Python 24.8%
Shell 3.1%
Assembly 3%
Makefile 2.7%
Other 1%