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
+175
View File
@@ -0,0 +1,175 @@
/*
* gfx_raw_256.c — 256-colour (mode 0x81) raw primitives.
*
* "Raw" = W3-naive: caller must have W3 mapped to the video bank and
* interrupts disabled (typically via _gfx_w3_video_begin/end from
* gfx_raw_common.c). Composite primitives in gfx_256.c wrap one
* begin/end around many raw calls so the W3 dance is amortised across
* the whole drawing operation.
*
* Addressing (mode 0x81): pixel (x, y) lives at CPU 0xC000 + x with
* Port_Y (0x89) = y. _gfx_addr_base is 0xC000 for draw page 0 and
* 0xC140 for draw page 1 — see gfx_core.c.
*
* Accelerator opcodes (docs/converted/accel_r.txt):
* LD D,D (0x52) enter "set block size" mode; the NEXT byte is the
* length operand and MUST follow LD A immediate (0x3E).
* LD C,C (0x49) horizontal Fill mode (LD (HL),A fills n bytes)
* LD E,E (0x5B) vertical Fill mode (auto-increments Port_Y)
* LD B,B (0x40) disable accelerator
*
* The block-length byte uses SMC — we patch the immediate at runtime,
* then run the accel sequence. HOME is RAM after DSS loads us, so
* SMC inside our own .EXE is safe.
*/
#include <gfx.h>
#include <stdint.h>
extern uint16_t _gfx_addr_base;
/* ---- Scratch shared across asm helpers --------------------------- *
* Single-threaded — no reentrancy. GFX runs with interrupts off
* between _gfx_w3_video_begin and _gfx_w3_video_end. */
static uint8_t acc_color;
static uint8_t acc_y;
static uint8_t acc_len; /* 0 means 256 — the accel convention */
static uint16_t acc_addr;
/* Putpixel scratch — separate from acc_* so a putpixel inside a
* Bresenham loop doesn't trample on outer accel state. */
static uint8_t _gfx_pp_y;
static uint16_t _gfx_pp_addr;
static uint8_t _gfx_pp_color;
/* ---- inner accel bursts ------------------------------------------ *
*
* Pre: W3 mapped to the video bank, DI active. Caller wraps a sequence
* of bursts in a single begin/end pair. */
/* Horizontal fill: acc_len bytes at acc_addr on row acc_y. */
static void hfill_chunk(void) __naked
{
__asm
;; Patch the LD A,#n immediate (operand byte) with acc_len.
ld a, (_acc_len)
ld (_hfill_len_imm), a
ld a, (_acc_y)
out (#0x89), a ; Port_Y = y
;; Pre-load colour into C and dest into HL before arming accel.
ld a, (_acc_color)
ld c, a
ld hl, (_acc_addr)
;; --- ACCEL SEQUENCE ---
ld d, d ; 0x52 set block size mode
ld a, #0 ; 0x3E nn block size (nn patched above)
_hfill_len_imm = . - 1
ld c, c ; 0x49 horizontal Fill
ld a, c ; 0x79 A = colour (NOT another ld a,#n)
ld (hl), a ; fires accel; fills acc_len bytes
ld b, b ; 0x40 disable
ret
__endasm;
}
/* Vertical fill: acc_len pixels at column acc_addr, top row acc_y.
* The accel auto-increments Port_Y as it paints down the column. */
static void vfill_chunk(void) __naked
{
__asm
ld a, (_acc_len)
ld (_vfill_len_imm), a
ld a, (_acc_y)
out (#0x89), a ; starting Y
ld a, (_acc_color)
ld c, a
ld hl, (_acc_addr)
ld d, d ; 0x52 set block size
ld a, #0 ; immediate length (patched)
_vfill_len_imm = . - 1
ld e, e ; 0x5B vertical Fill
ld a, c ; A = colour
ld (hl), a ; fires accel
ld b, b ; 0x40 disable
ret
__endasm;
}
/* ---- Raw primitives (W3-naive, composable) ----------------------- *
*
* These do NO DI/W3 setup — caller wraps a sequence with one
* _gfx_w3_video_begin / _gfx_w3_video_end pair. */
void _gfx_putpixel256_raw(int x, int y, uint8_t color)
{
if ((unsigned)x >= GFX_WIDTH || (unsigned)y >= GFX_HEIGHT) return;
_gfx_pp_y = (uint8_t)y;
_gfx_pp_addr = (uint16_t)(_gfx_addr_base + (unsigned)x);
_gfx_pp_color = color;
__asm
ld a, (__gfx_pp_y)
out (#0x89), a
ld hl, (__gfx_pp_addr)
ld a, (__gfx_pp_color)
ld (hl), a
__endasm;
}
void _gfx_hline256_raw(int x, int y, int len, uint8_t color)
{
if ((unsigned)y >= GFX_HEIGHT) return;
if (x < 0) { len += x; x = 0; }
if (x >= GFX_WIDTH) return;
if (x + len > GFX_WIDTH) len = GFX_WIDTH - x;
if (len <= 0) return;
acc_color = color;
acc_y = (uint8_t)y;
acc_addr = (uint16_t)(_gfx_addr_base + (unsigned)x);
while (len > 0) {
int chunk = len > 256 ? 256 : len;
acc_len = (chunk == 256) ? 0 : (uint8_t)chunk;
hfill_chunk();
len -= chunk;
acc_addr += chunk;
}
}
void _gfx_vline256_raw(int x, int y, int len, uint8_t color)
{
if ((unsigned)x >= GFX_WIDTH) return;
if (y < 0) { len += y; y = 0; }
if (y >= GFX_HEIGHT) return;
if (y + len > GFX_HEIGHT) len = GFX_HEIGHT - y;
if (len <= 0) return;
/* GFX_HEIGHT = 256 so a full column is a single accel burst. */
acc_color = color;
acc_y = (uint8_t)y;
acc_addr = (uint16_t)(_gfx_addr_base + (unsigned)x);
acc_len = (len == 256) ? 0 : (uint8_t)len;
vfill_chunk();
}
/* Clear the whole 320×256 area with `color`. Row-major hfill:
* 256 rows × 2 bursts each (256-byte + 64-byte) = 512 bursts. */
void _gfx_clear256_raw(uint8_t color)
{
acc_color = color;
for (int y = 0; y < GFX_HEIGHT; y++) {
acc_y = (uint8_t)y;
acc_addr = _gfx_addr_base; /* 256-byte burst */
acc_len = 0;
hfill_chunk();
acc_addr = (uint16_t)(_gfx_addr_base + 256); /* 64-byte burst */
acc_len = 64;
hfill_chunk();
}
}