Files
snark13 c71e249a4e 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>
2026-06-03 16:13:21 +03:00

176 lines
5.8 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* 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();
}
}