/* * 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 #include 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(); } }