Files
Sprinter-SDCC/libbgi/include/gfx.h
T
snark13 ee1ca00c6f libbgi: funcptr-диспетч clip/noclip спрайтовых ядер + снятие src[0]-фикса
Диспетчеризация clip/noclip через указатели _gfx_blit_fn/_gfx_heal_fn
(common/_gfx_sprite_fns.c, дефолт clip): gfx_sprite_clip() — теперь
модуль, переключает указатели один раз; sprite_update/putsprite/
movesprite зовут через указатель — ветка if(clip) из горячего цикла
убрана (съедала половину выигрыша noclip). Программа без вызова
gfx_sprite_clip() noclip-ядра не линкует.

Замер dev-MAME (16 шаров, uncapped): clip 50 → noclip 60-61 fps
(+20-22%). Регресс tests/sprites (A/B PASS), size-check OK
(balls −237 Б, sprites −3177 Б — отвязались лишние ядра).

Фикс CPU-байта write-триггера (preread + EX AF,AF') снят: точная
dev-MAME эмулирует ПЛМ, подавляющую CPU-байт при активном burst'е —
подтверждено по байтам VRAM (tests/blitw col0 = GREEN через
read_vram MCP-моста). Для heal фикс был избыточен всегда (банк 0x50
перезаписывает dst[0]). Строки фикса оставлены закомментированными
в трёх leaf'ах на случай отличий реального железа; шапки и §9а/§9б
дизайна обновлены. НА ЖЕЛЕЗЕ ПЕРЕПРОВЕРИТЬ.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 11:37:54 +03:00

130 lines
7.8 KiB
C
Raw 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.h — Sprinter graphics: mode-agnostic API (BGI_GFX).
*
* Рисование (putpixel/line/bar/circle/…) вынесено в BGI — см.
* <graphics.h> и driver-библиотеки bgi256.lib / bgi16.lib (выбор режима
* линковкой: sprinter-cc --gfx 256 | --gfx 16). Здесь остались только
* функции БЕЗ BGI-аналога: вход/выход в режим, страницы, банк, vsync,
* палитра, шрифт. Они mode-agnostic и живут в libbgi/common/ (один
* исходник, .rel в обеих driver-библиотеках).
*
* Адресация (для не-BGI программ, использующих эти примитивы напрямую:
* пиксель (x,y) — CPU 0xC000 + (x или x/2) с Port_Y (0x89) = y;
* gfx-код мапит 16 КБ VRAM-страницу в W3 при записи.
* Для double-buffering страница 1 начинается на 320 байт дальше (0xC140).
*
* Палитра: BIOS $A4 (RST 8); 4 байта на запись — B, G, R, pad.
*/
#ifndef GFX_H
#define GFX_H
#include <stdint.h>
/* ESTEX SETVMOD codes — same values as the SETVMOD `A` register. */
#define GFX_MODE_TEXT_40x32 0x02
#define GFX_MODE_TEXT_80x32 0x03
#define GFX_MODE_320x256x256 0x81
#define GFX_MODE_640x256x16 0x82
/* Pixel dimensions of mode 0x81 (320×256, 256 colours). */
#define GFX_WIDTH 320
#define GFX_HEIGHT 256
/* Pixel dimensions of mode 0x82 (640×256, 16 colours). Each byte at
* 0xC000+x_byte holds two pixels: high nibble = LEFT (even-x), low
* nibble = RIGHT (odd-x) — see memory/sprinter_graphics.md. */
#define GFX_WIDTH_16 640
#define GFX_HEIGHT_16 256
#define GFX_COLORS_16 16
/* ---- Page selection (double-buffering) and bank control ---------- */
/* Внутреннее состояние (хранилище: libbgi/common/_gfx_state.c) — НЕ
* трогать напрямую, только через функции ниже. */
extern uint8_t _gfx_visible_page, _gfx_draw_page, _gfx_bank;
void gfx_set_visible_page(uint8_t page); /* 0 or 1 */
inline uint8_t gfx_get_visible_page(void) { return _gfx_visible_page; }
void gfx_set_draw_page(uint8_t page); /* 0 or 1 */
inline uint8_t gfx_get_draw_page(void) { return _gfx_draw_page; }
/* Видеобанк W3 (0x50..0x5F): биты 2/3 номера страницы — аппаратные
* подрежимы ЗАПИСИ (Иван Мак §4.3; docs/sprite-api-design.md §1),
* действуют на ВСЕ примитивы рисования до следующего gfx_set_bank.
* Биты 0/1 всегда 0 (резерв конфигураций). */
#define GFX_BANK_NORMAL 0x50 /* запись в видео-ОЗУ + ОЗУ-копию */
#define GFX_BANK_NOSHADOW 0x54 /* только видео-ОЗУ (врем. вывод) */
#define GFX_BANK_TRANSPARENT 0x58 /* байт 0xFF не записывается */
#define GFX_BANK_SPRITE 0x5C /* NOSHADOW + TRANSPARENT */
/* Прозрачный цвет аппаратной прозрачности (256-режим: пиксель;
* 16-режим: ПАРА пикселей цвета 15). Цвет 255 нельзя нарисовать
* через банк с битом 3 — он и есть прозрачный. */
#define GFX_TRANSPARENT 0xFF
inline void gfx_set_bank(uint8_t bank) { _gfx_bank = bank; } /* GFX_BANK_* */
inline uint8_t gfx_get_bank(void) { return _gfx_bank; }
/* Клип спрайтов по экрану (putsprite/movesprite/спрайтовый движок).
* on=1 (дефолт) — клипить (спрайты могут уходить за края); on=0 —
* ОТКЛЮЧИТЬ (приложение ГАРАНТИРУЕТ, что спрайты целиком на экране —
* иначе запись за край портит соседнюю память, см. tests/spriteclip).
* Выигрыш ~+19% на анимации: движок зовёт noclip-ядра без клип-кода
* (не просто скип проверки — вся клип-математика физически отсутствует,
* SDCC тесней раскладывает регистры). Переключение — перенаправлением
* внутренних указателей на ядра (один раз здесь, не ветка в горячем
* цикле движка). НЕ влияет на putimage/gfx_blit (у них клип всегда). */
extern uint8_t _gfx_sprite_clip;
void gfx_sprite_clip(uint8_t on);
inline uint8_t gfx_get_sprite_clip(void) { return _gfx_sprite_clip; }
/* ---- Блиттинг (ядро спрайтов; Фаза B sprite-api-design) ----------- *
* Формат картинки — getimage: uint16 w, uint16 h, пиксели построчно
* (1 байт/пиксель в 256-режиме). Буфер обязан лежать ВНЕ W3
* (< 0xC000) — на время операции W3 замаплен на видеобанк.
* Блит через accel block-copy (до 256 байт/burst), клиппинг по экрану
* есть всегда; рисует ТЕКУЩИМ банком (gfx_set_bank). */
/* Блит целой картинки левым-верхним углом в (x,y). */
void gfx_blit(int x, int y, const void *img);
/* Блит под-прямоугольника (sx,sy,w,h) картинки (атлас кадров:
* кадр N ленты — sx = N*FRAME_W). Под-прямоугольник обязан лежать
* внутри img; по экрану — клиппится. */
void gfx_blit_part(int x, int y, const void *img,
int sx, int sy, int w, int h);
/* Восстановить прямоугольник экрана из ОЗУ-копии (стирание спрайта/
* оверлея, нарисованного банком с битом 2: 0x54/0x5C). Всегда
* работает банком GFX_BANK_NORMAL независимо от gfx_set_bank;
* клиппится по экрану. Буфер сохранения не нужен: ОЗУ-копия и есть
* бэкап фона (чтение из #50..#5F всегда возвращает её). */
void gfx_heal(int x, int y, int w, int h);
/* Block until the next frame (50 Hz). Типичный double-buffer:
* gfx_set_draw_page(hidden); draw_frame(); gfx_wait_vsync();
* gfx_set_visible_page(hidden); // tear-free flip */
void gfx_wait_vsync(void);
/* ---- Bitmap-font text -------------------------------------------- *
* Шрифт 256 глифов × 8 рядов × 1 байт (ZX-Spectrum формат), 2 КБ.
* Грузится лениво при первом использовании BGI-текста; gfx_set_font
* подменяет указатель (storage должен жить, пока шрифт активен). */
void gfx_load_default_font(void);
void gfx_set_font(const uint8_t *font);
/* ---- Palette ----------------------------------------------------- *
* Каждая графическая страница имеет свою палитру (page 0 → palette 0,
* page 1 → palette 1). Для seamless double-buffering грузить одну
* палитру в обе. */
void gfx_pal_load(uint8_t pal_num, uint8_t start, uint8_t count,
const uint8_t *data);
void gfx_pal_set (uint8_t pal_num, uint8_t idx,
uint8_t r, uint8_t g, uint8_t b);
void gfx_pal_get (uint8_t pal_num, uint8_t start, uint8_t count,
uint8_t *data);
void gfx_pal_get_color(uint8_t pal_num, uint8_t idx,
uint8_t *r, uint8_t *g, uint8_t *b);
void gfx_pal_reset(void);
#endif