From 9b1ce711304278725b25297950f8604c63f93df3 Mon Sep 17 00:00:00 2001 From: Alexander Petrov Date: Fri, 10 Jul 2026 15:16:53 +0300 Subject: [PATCH] =?UTF-8?q?libbgi:=20=D1=83=D0=BF=D1=80=D0=BE=D1=81=D1=82?= =?UTF-8?q?=D0=B8=D1=82=D1=8C=20safe-=D0=BA=D0=BE=D0=BD=D1=82=D1=80=D0=BE?= =?UTF-8?q?=D0=BB=D1=8C=20span'=D0=BE=D0=B2=20+=20=D0=BE=D1=82=D1=81=D0=B5?= =?UTF-8?q?=D0=BA=D0=B0=D1=82=D1=8C=20len=3D=3D0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit _bgi_vspan_raw: safe-версия (без GFX_NOCHECK) больше НЕ клиппит диапазон y+len и не обрабатывает y<0 — проверяется только валидность x/y (как в _bgi_hspan_raw). Сознательный компромисс: safe ловит грубый выход за экран по координате, но не частичный отрезок; y+len<=256 — обязанность вызывающего. Оба span'а: в safe добавлена проверка len==0 → return (иначе B=0 по конвенции акселератора рисует «256»). В fast (GFX_NOCHECK) проверка вырезается — поведение прежнее (256 точек), задокументировано в шапках. Co-Authored-By: Claude Opus 4.8 --- libbgi/bgi256/_bgi_hspan_raw.c | 48 +++++++++++++++++-------- libbgi/bgi256/_bgi_vspan_raw.c | 65 ++++++++++++---------------------- 2 files changed, 56 insertions(+), 57 deletions(-) diff --git a/libbgi/bgi256/_bgi_hspan_raw.c b/libbgi/bgi256/_bgi_hspan_raw.c index f15daba..1a3ae7e 100644 --- a/libbgi/bgi256/_bgi_hspan_raw.c +++ b/libbgi/bgi256/_bgi_hspan_raw.c @@ -1,13 +1,36 @@ /* * _bgi_hspan_raw — горизонтальная линия len пикселей от (x,y), mode 0x81, - * через Fill-burst'ы акселератора (до 256 байт за выстрел). W3-naive: - * вызывающий обрамляет _bgi_begin/_bgi_end. + * через Fill-burst'ы акселератора (до 256 байт за выстрел). * - * __sdcccall(1): x→HL, y→DE(E=y), len→стек(4/5(ix)), color→стек(6(ix)). - * Значения передаются в _gfx_hfill256_segment через регистры (HL=addr, - * C=color, B=len, E=y) — без глобального скретча. len>256 (макс. ширина - * 320) → два burst'а: хвост (len-256) на addr+256, затем 256 на addr; - * усечение len до байта даёт (len-256) для 257..320 (256 -> 0 = «256»). + * Это «сырой» leaf-примитив: максимально тонкий, БЕЗ клиппинга. Всю + * защиту берёт на себя вызывающий (см. ОГРАНИЧЕНИЯ). Проверенный путь: + * _bgi_fill_span (клиппит сам) и _bgi_lineseg (ортогональ) → сюда. + * + * ---- ОГРАНИЧЕНИЯ ВЫЗОВА (ответственность вызывающего) -------------- + * - W3-скобка: вызывать ТОЛЬКО между _bgi_begin/_bgi_end (W3 замаплен + * на видеобанк; DI/EI вокруг каждого выстрела ставит сам сегмент). + * - Клиппинга НЕТ. Требуется 0 <= x и x+len <= 320 (иначе burst + * вылезет за строку и затрёт соседнюю видеопамять). Отрицательный x + * и x+len>320 НЕ обрезаются — координаты обязаны быть уже валидны. + * - Диапазон len: 1..320. len==0: safe-версия (с проверками) тихо + * вернётся; fast (GFX_NOCHECK) из-за конвенции акселератора (B=0 = + * «256») отрисует 256 точек. len<0 / len>320 — UB. + * - Встроенная проверка (если НЕ задан GFX_NOCHECK) отсекает лишь + * грубый выход за экран по одной координате: (unsigned)y>=256 и + * (unsigned)x>=320 → тихий возврат. Это НЕ клиппинг диапазона — + * частичного отрезка/правого края не режет. + * + * ---- ABI ---------------------------------------------------------- + * __sdcccall(1): x→HL, y→DE(E=y), len→стек(4/5(ix)), color→стек(6(ix)), + * __naked → callee-pops 3 байта аргументов (len + color) вручную. + * Значения уходят в _gfx_hfill256_segment через регистры (HL=addr, + * C=color, B=len, E=y) — без глобального скретча; сегмент клоббер только + * A/F, поэтому HL/E переживают выстрел (см. _gfx_hfill256.c). + * + * ---- Логика двух burst'ов (len 257..320) -------------------------- + * Сначала 256 точек на addr (B=0), затем inc h (addr+256) и хвост + * (len-256) точек. Усечение len до младшего байта само даёт (len-256) + * для 257..320; при len==256 младший байт 0 → одиночный burst «256». */ #include "../_bgi.h" @@ -29,6 +52,9 @@ void _bgi_hspan_raw(int x, int y, int len, uint8_t color) __naked ld a, h sbc a, #0x01 jr NC, hs_ret ; (unsigned)x >= 320 + ld a, 4 (ix) + or a, 5 (ix) + jr Z, hs_ret ; len == 0 (иначе B=0 → «256») #endif ld c, l ld b, h ; BC = x @@ -43,15 +69,9 @@ void _bgi_hspan_raw(int x, int y, int len, uint8_t color) __naked or a, a jr Z, hs_single ; len == 256 (B=0) ;; 257..320: два burst-a - push hl ; base addr - inc h ; addr + 256 - ld a, 4 (ix) - ld b, a ; B = len-256 (низкий байт) - call __gfx_hfill256_segment - pop hl ; base addr ld b, #0 ; 256 call __gfx_hfill256_segment - jr hs_ret + inc h ; addr + 256 hs_single: ld a, 4 (ix) ld b, a ; B = len (0 если ==256) diff --git a/libbgi/bgi256/_bgi_vspan_raw.c b/libbgi/bgi256/_bgi_vspan_raw.c index 73ce84c..9e791da 100644 --- a/libbgi/bgi256/_bgi_vspan_raw.c +++ b/libbgi/bgi256/_bgi_vspan_raw.c @@ -1,12 +1,25 @@ /* * _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». + * "Raw" leaf: тонкий, БЕЗ клиппинга диапазона. Проверка (без GFX_NOCHECK) + * отсекает лишь грубый выход за экран по одной координате — (unsigned)x>=320 + * и (unsigned)y>=256 → тихий возврат (единый стиль с _bgi_hspan_raw). + * + * ---- ОГРАНИЧЕНИЯ ВЫЗОВА (ответственность вызывающего) -------------- + * - W3-скобка: вызывать ТОЛЬКО между _bgi_begin/_bgi_end. + * - Клиппинга диапазона НЕТ: требуется 0 <= y и y+len <= 256, иначе + * burst вылезет за низ колонки и затрёт чужую видеопамять. По + * сознательному решению safe-версия проверяет валидность только x/y, + * но НЕ y+len; отрицательный y и y+len>256 НЕ обрезаются (в отличие от + * прежней версии) — координаты обязаны быть уже валидны. + * - Диапазон len: 1..256. len==0: safe-версия (с проверками) тихо + * вернётся; fast (GFX_NOCHECK) из-за конвенции акселератора (B=0 = + * «256») зальёт 256 строк. + * + * __sdcccall(1): x→HL, y→DE(E=y), len→стек(4/5(ix)), color→стек(6(ix)), + * __naked → callee-pops 3 байта (len+color). В _gfx_vfill256_segment: + * HL=addr, C=color, B=len, E=y (без глобального скретча). */ #include "../_bgi.h" @@ -25,49 +38,15 @@ void _bgi_vspan_raw(int x, int y, int len, uint8_t color) __naked 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: + jr NZ, vs_ret ; (unsigned)y >= 256 ld a, 4 (ix) or a, 5 (ix) - jr Z, vs_ret ; len == 0 - bit 7, 5 (ix) - jr NZ, vs_ret ; len < 0 + jr Z, vs_ret ; len == 0 (иначе B=0 → «256») #endif + ld c, l + ld b, h ; BC = x ld hl, (__gfx_addr_base) add hl, bc ; HL = base + x ld a, 6 (ix)