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
+178
View File
@@ -0,0 +1,178 @@
/*
* gfx_core.c — Sprinter graphics: common state + setup/teardown API.
*
* This module owns the variables that select which graphics page is
* visible vs drawn into, which W3 bank is mapped during gfx writes,
* and the row-base CPU address. All mode-specific primitives (256-
* color in gfx_raw_256.c / gfx_256.c, 16-color in gfx_raw_16.c /
* gfx_16.c, text in gfx_text_*.c) read this state via extern.
*
* Public API:
* gfx_init / gfx_done — switch video mode, restore previous
* gfx_set_visible_page / get — ESTEX $54 SELPAGE wrapper + cached value
* gfx_set_draw_page / get — updates _gfx_addr_base for the new page
* gfx_set_bank / get — sets the W3 page byte (0x50..0x5F)
* gfx_wait_vsync — EI; HALT until next frame interrupt
* gfx_pal_load / gfx_pal_set — BIOS $A4 PIC_SET_PAL wrappers
*
* Shared state (extern from this file):
* _gfx_addr_base — 0xC000 for page 0, 0xC140 for page 1. Every
* mode-specific primitive uses this instead of a
* hard-coded 0xC000 so the same code targets the
* currently-selected draw page.
* _gfx_bank — read by _gfx_w3_video_begin in gfx_raw_common.c
* to map the right W3 page.
*/
#include <gfx.h>
#include <stdint.h>
/* From conio's videomode_raw.c — bypasses set_videotextmode's text-only
* validation so gfx_init can move INTO graphics modes. */
extern uint8_t _videomode_raw_get(void);
extern int _videomode_raw_set(uint8_t mode);
/* ---- Shared graphics state --------------------------------------- */
/* Cached values of the SELPAGE state. Page numbers are 0 or 1. */
static uint8_t _gfx_visible_page = 0;
static uint8_t _gfx_draw_page = 0;
/* The W3 page byte (0x50..0x5F) — see memory/sprinter_vram_transparency.md
* for the bit-meanings (0x50 normal, 0x54 temp, 0x58 transparent, 0x5C
* both). Read by _gfx_w3_video_begin in gfx_raw_common.c. */
uint8_t _gfx_bank = 0x50;
/* CPU address of column 0 in the current draw page. Each VRAM row is
* 1024 bytes wide — page 0 occupies bytes 0..319 (CPU 0xC000+), page 1
* occupies 320..639 (CPU 0xC140+), and the remaining 384 bytes hold
* mode descriptors / palette data we don't touch. Updated by
* gfx_set_draw_page; read by every primitive's raw helper. */
uint16_t _gfx_addr_base = 0xC000;
/* ---- gfx_init / gfx_done ----------------------------------------- */
uint8_t gfx_init(uint8_t mode, uint8_t page)
{
uint8_t prev = _videomode_raw_get();
_videomode_raw_set(mode);
_gfx_bank = 0x50;
gfx_set_visible_page(page);
gfx_set_draw_page(page);
return prev;
}
void gfx_done(uint8_t mode)
{
_videomode_raw_set(mode);
}
/* ---- Visible page (ESTEX $54 SELPAGE) ---------------------------- *
*
* Direct OUT to port 0xC9 bit 0 toggles the "screen mode page" register
* but doesn't notify DSS's display bookkeeping, leaving the screen in
* an inconsistent state (one of the swaps shows as black). Going
* through the syscall keeps DSS happy.
*/
void gfx_set_visible_page(uint8_t page)
{
_gfx_visible_page = page & 1;
__asm
push ix
ld a, (__gfx_visible_page)
ld b, a ; B = page 0/1
ld c, #0x54 ; ESTEX SELPAGE
rst #0x10
pop ix
__endasm;
}
uint8_t gfx_get_visible_page(void)
{
return _gfx_visible_page;
}
/* ---- Draw page --------------------------------------------------- */
void gfx_set_draw_page(uint8_t page)
{
_gfx_draw_page = page & 1;
/* Direct constants beat (0xC000 + (cond ? 0x140 : 0)) by 3 Z80
* instructions — SDCC doesn't fold the constant addition. */
_gfx_addr_base = _gfx_draw_page ? 0xC140 : 0xC000;
}
uint8_t gfx_get_draw_page(void)
{
return _gfx_draw_page;
}
/* ---- W3 bank (0x50..0x5F) ---------------------------------------- */
void gfx_set_bank(uint8_t bank)
{
_gfx_bank = bank;
}
uint8_t gfx_get_bank(void)
{
return _gfx_bank;
}
/* ---- Frame sync -------------------------------------------------- *
*
* Block until the next IM2 frame interrupt (50 Hz, programmed by DSS
* for keyboard / cursor maintenance). The Z80's HALT instruction
* sleeps the CPU until the next IRQ, which DSS handles and returns
* to the instruction after HALT — that's the start of the vertical
* retrace window, ideal for a tear-free page swap.
*/
void gfx_wait_vsync(void) __naked
{
__asm
ei
halt
ret
__endasm;
}
/* ---- Palette (BIOS $A4 PIC_SET_PAL) ----------------------------- */
static uint8_t pal_num_;
static uint8_t pal_start_;
static uint8_t pal_count_;
static uint16_t pal_data_;
void gfx_pal_load(uint8_t pal_num, uint8_t start, uint8_t count,
const uint8_t *data)
{
pal_num_ = pal_num;
pal_start_ = start;
pal_count_ = count;
pal_data_ = (uint16_t)(uintptr_t)data;
__asm
push ix
ld a, (_pal_start_)
ld e, a ; E = start
ld a, (_pal_count_)
ld d, a ; D = count (0 256)
ld hl, (_pal_data_) ; HL = data
ld b, #0xFF ; mask
ld a, (_pal_num_) ; A = palette number
ld c, #0xA4 ; BIOS PIC_SET_PAL
rst #0x08
pop ix
__endasm;
}
void gfx_pal_set(uint8_t pal_num, uint8_t idx,
uint8_t r, uint8_t g, uint8_t b)
{
uint8_t entry[4];
entry[0] = b;
entry[1] = g;
entry[2] = r;
entry[3] = 0;
gfx_pal_load(pal_num, idx, 1, entry);
}