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
+186
View File
@@ -0,0 +1,186 @@
/*
* gfx_raw_16.c — 16-colour (mode 0x82) raw primitives.
*
* Pixel format (verified empirically 2026-05-31): each byte at
* 0xC000+xb holds two horizontal pixels —
* high nibble (bits 7-4) = pixel at x = 2*xb (LEFT, even)
* low nibble (bits 3-0) = pixel at x = 2*xb + 1 (RIGHT, odd)
*
* Row addressing matches mode 0x81: Port_Y (0x89) = y, 320 bytes/row,
* 320 × 2 = 640 pixels.
*
* Accelerator: byte-wise — one Fill burst paints up to 256 bytes =
* 512 pixels. For a solid colour the byte value is the nibble in
* both halves: b = c | (c<<4).
*
* RMW pattern for unaligned edges & vertical lines:
* read byte at (HL); mask out target nibble; OR in new nibble; write.
* ~10 instructions per pixel — slow but unavoidable since a byte
* spans two horizontal pixels.
*
* "Raw" = W3-naive: caller wraps a sequence with one
* _gfx_w3_video_begin / _gfx_w3_video_end pair.
*/
#include <gfx.h>
#include <stdint.h>
extern uint16_t _gfx_addr_base;
/* ---- Scratch shared across asm helpers ---------------------------- */
static uint8_t g16_y;
static uint8_t g16_byte; /* combined byte: nibble | (nibble<<4) */
static uint8_t g16_nibble; /* color in correct half (low or high) */
static uint8_t g16_mask; /* mask preserving the OTHER half */
static uint8_t g16_len; /* accel block size (0 = 256) */
static uint16_t g16_addr;
/* ---- Accel horizontal Fill burst ---------------------------------- *
*
* Caller has W3 mapped and DI active. Only the block-size byte uses
* SMC. Colour is preloaded into C and shipped via `ld a, c` (0x79).
* Inserting another `ld a, #n` between LD C,C and the firing LD (HL),A
* breaks the burst — the accel FSM re-reads the immediate as a fresh
* block size.
*/
static void g16_hfill_chunk(void) __naked
{
__asm
ld a, (_g16_len)
ld (_g16_h_len_imm), a
ld a, (_g16_byte)
ld c, a
ld a, (_g16_y)
out (#0x89), a
ld hl, (_g16_addr)
ld d, d ; 0x52 set block size
ld a, #0 ; 0x3E nn length (patched)
_g16_h_len_imm = . - 1
ld c, c ; 0x49 horizontal Fill
ld a, c ; 0x79 A = colour byte
ld (hl), a ; fires accel
ld b, b ; 0x40 disable
ret
__endasm;
}
/* ---- RMW one nibble at (g16_addr, g16_y) ------------------------- */
static void g16_rmw_pixel(void) __naked
{
__asm
ld a, (_g16_y)
out (#0x89), a
ld hl, (_g16_addr)
ld a, (_g16_mask)
ld b, a ; B = preserve mask
ld a, (_g16_nibble)
ld c, a ; C = new nibble (in correct half)
ld a, (hl)
and a, b ; clear target nibble
or a, c ; OR in new
ld (hl), a
ret
__endasm;
}
/* ---- Raw primitives (W3-naive, composable) ----------------------- */
void _gfx_putpixel16_raw(int x, int y, uint8_t color)
{
if ((unsigned)x >= GFX_WIDTH_16 || (unsigned)y >= GFX_HEIGHT_16) return;
g16_y = (uint8_t)y;
g16_addr = (uint16_t)(_gfx_addr_base + ((unsigned)x >> 1));
if (x & 1) {
g16_nibble = (uint8_t)(color & 0x0F);
g16_mask = 0xF0;
} else {
g16_nibble = (uint8_t)((color & 0x0F) << 4);
g16_mask = 0x0F;
}
g16_rmw_pixel();
}
void _gfx_hline16_raw(int x, int y, int len, uint8_t color)
{
if ((unsigned)y >= GFX_HEIGHT_16) return;
if (x < 0) { len += x; x = 0; }
if (x >= GFX_WIDTH_16) return;
if (x + len > GFX_WIDTH_16) len = GFX_WIDTH_16 - x;
if (len <= 0) return;
g16_y = (uint8_t)y;
uint8_t cnib = color & 0x0F;
g16_byte = (uint8_t)(cnib | (cnib << 4));
/* Leading unaligned pixel: x odd → RIGHT half of left-most byte. */
if (x & 1) {
g16_addr = (uint16_t)(_gfx_addr_base + ((unsigned)x >> 1));
g16_nibble = cnib;
g16_mask = 0xF0;
g16_rmw_pixel();
x++;
len--;
if (len <= 0) return;
}
/* Even x; emit (len/2) full bytes via accel hfill. */
int full = len >> 1;
g16_addr = (uint16_t)(_gfx_addr_base + ((unsigned)x >> 1));
while (full > 0) {
int chunk = full > 256 ? 256 : full;
g16_len = (chunk == 256) ? 0 : (uint8_t)chunk;
g16_hfill_chunk();
full -= chunk;
g16_addr += chunk;
}
/* Trailing odd-length pixel: LEFT half of the next byte. */
if (len & 1) {
g16_nibble = (uint8_t)(cnib << 4);
g16_mask = 0x0F;
g16_rmw_pixel();
}
}
void _gfx_vline16_raw(int x, int y, int len, uint8_t color)
{
if ((unsigned)x >= GFX_WIDTH_16) return;
if (y < 0) { len += y; y = 0; }
if (y >= GFX_HEIGHT_16) return;
if (y + len > GFX_HEIGHT_16) len = GFX_HEIGHT_16 - y;
if (len <= 0) return;
g16_addr = (uint16_t)(_gfx_addr_base + ((unsigned)x >> 1));
if (x & 1) {
g16_nibble = (uint8_t)(color & 0x0F);
g16_mask = 0xF0;
} else {
g16_nibble = (uint8_t)((color & 0x0F) << 4);
g16_mask = 0x0F;
}
for (int i = 0; i < len; i++) {
g16_y = (uint8_t)(y + i);
g16_rmw_pixel();
}
}
/* Clear the whole 640×256 area with `color`. Row-major hfill —
* 256 rows × 2 bursts each (256+64 bytes). */
void _gfx_clear16_raw(uint8_t color)
{
g16_byte = (uint8_t)((color & 0x0F) | ((color & 0x0F) << 4));
for (int y = 0; y < GFX_HEIGHT_16; y++) {
g16_y = (uint8_t)y;
g16_addr = _gfx_addr_base;
g16_len = 0;
g16_hfill_chunk();
g16_addr = (uint16_t)(_gfx_addr_base + 0x100);
g16_len = 64;
g16_hfill_chunk();
}
}