libbgi: bgi256 fill-примитивы на register-ABI + typedef color_t

- удалён глобальный скретч акселератора (_gfx_acc256.c); fill-сегменты
  (_gfx_hfill256/_gfx_vfill256) принимают аргументы в регистрах HL/C/B/E,
  вызываются только из asm raw-примитивов
- новый _gfx_rectfill256: заливка прямоугольника через Horizontal_Size
- raw-примитивы (plot/read/hspan/vspan/clear) переписаны под новый ABI
- graphics.h: typedef color_t (uint8_t) для всех public color-функций
- sprinter-cc: флаг --safe (линковка *_safe.lib при наличии)
- CLAUDE.md/mame_interactive: авто-прогон тестов в MAME
- gfx_demo: демонстрация rectfill

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-10 14:32:21 +03:00
parent 52636a5d6e
commit 029607971f
31 changed files with 618 additions and 283 deletions
+76 -41
View File
@@ -1,50 +1,85 @@
/*
* _bgi_vspan_raw — вертикальная линия len пикселей от (x,y),
* mode 0x81. Вертикальный Fill акселератора сам инкрементирует Port_Y,
* так что вся колонка (высота 256) — один burst. W3-naive.
* _bgi_vspan_raw — вертикальная линия len пикселей от (x,y), mode 0x81,
* через Vertical Fill акселератора (один burst = колонка до 256 строк).
* W3-naive: вызывающий обрамляет _bgi_begin/_bgi_end.
*
* __sdcccall(1): x→HL, y→DE(E=y), len→стек(4/5(ix)), color→стек(6(ix)).
* В _gfx_vfill256_segment передаём HL=addr, C=color, B=len, E=y (без
* глобального скретча). x держим в BC на время клипа (clip портит HL/DE),
* адрес считаем в конце. len усекается до байта: 256 -> 0 = «256».
*/
#include "../_bgi.h"
/* Вертикальный Fill: _gfx_acc_len пикселей в колонке _gfx_acc_addr,
* верхняя строка _gfx_acc_y. Про SMC и правила FSM акселератора —
* см. _gfx_hfill256.c. */
static void vfill_chunk(void) __naked
void _bgi_vspan_raw(int x, int y, int len, uint8_t color) __naked
{
__asm
ld a, (__gfx_acc_len)
ld (_vfill256_len_imm), a
ld a, (__gfx_acc_y)
out (#0x89), a ; стартовый Y
ld a, (__gfx_acc_color)
ld c, a
ld hl, (__gfx_acc_addr)
ld d, d ; 0x52 режим размера блока
ld a, #0 ; immediate-длина (патчится)
_vfill256_len_imm = . - 1
ld e, e ; 0x5B вертикальный Fill
ld a, c ; A = цвет
ld (hl), a ; выстрел
ld b, b ; 0x40 выключить
ret
.globl __gfx_addr_base
.globl __gfx_vfill256_segment
push ix
ld ix, #0
add ix, sp
#ifndef GFX_NOCHECK
ld a, l
sub a, #0x40
ld a, h
sbc a, #0x01
jr NC, vs_ret ; (unsigned)x >= 320
#endif
ld c, l
ld b, h ; BC = x (пережить клип)
#ifndef GFX_NOCHECK
bit 7, d
jr Z, vs_ypos
;; y < 0: len += y ; y = 0
ld l, 4 (ix)
ld h, 5 (ix)
add hl, de ; len + y (y отрицателен)
ld 4 (ix), l
ld 5 (ix), h
ld de, #0
vs_ypos:
ld a, d
or a, a
jr NZ, vs_ret ; y >= 256
;; y + len > 256 ? -> len = 256 - y
ld l, 4 (ix)
ld h, 5 (ix)
add hl, de ; y + len (D=0)
ld a, h
or a, a
jr Z, vs_lenchk ; < 256
dec a
jr NZ, vs_clamp ; high >= 2
ld a, l
or a, a
jr Z, vs_lenchk ; == 256
vs_clamp:
xor a, a
sub a, e ; 0 - y (низкий байт)
ld 4 (ix), a
ld a, #1
sbc a, #0 ; 1 - borrow => 256 - y
ld 5 (ix), a
vs_lenchk:
ld a, 4 (ix)
or a, 5 (ix)
jr Z, vs_ret ; len == 0
bit 7, 5 (ix)
jr NZ, vs_ret ; len < 0
#endif
ld hl, (__gfx_addr_base)
add hl, bc ; HL = base + x
ld a, 6 (ix)
ld c, a ; C = color
ld a, 4 (ix)
ld b, a ; B = len (256 -> 0)
call __gfx_vfill256_segment
vs_ret:
pop ix
pop hl
pop af
inc sp
jp (hl)
__endasm;
}
void _bgi_vspan_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, полная колонка — один accel-burst. */
_gfx_acc_color = color;
_gfx_acc_y = (uint8_t)y;
_gfx_acc_addr = (uint16_t)(_gfx_addr_base + (unsigned)x);
_gfx_acc_len = (len == 256) ? 0 : (uint8_t)len;
vfill_chunk();
}