Files
Sprinter-SDCC/libbgi/_bgi.h
T
snark13 484b18d10c libc+libbgi: raw-клавиатура (held-state) + column-major блит спрайтов
libc/kbd: kbd_raw_open/close/down — сырой PS/2-канал клавиатуры с
held-state (битовая карта _kbdraw_down[512], EXT-клавиши +256).  Пока
raw открыт, кадровый IRQ-трамплин перехватывает байт SIO у DSS и
декодирует make/break (0xF0/0xE0-префиксы) сам.  FIFO вычерпывается В
ЦИКЛЕ (приёмный буфер SIO 3 байта; пачка break-кодов при одновременном
отпускании иначе теряется → залипание клавиши).  Буфер W2-трамплина
поднят 224→288 Б под выросший обработчик.

libc/conio: kbd_mod_state() — live-состояние модификаторов (ESTEX
CTRLKEY $33h), Shift/Ctrl/Alt/Lock прямо сейчас, KBD_MOD_* маска.

libbgi: gfx_blit_cols(x,y,img,flip) + _bgi_blit_cols_raw — блит
column-major спрайта вертикальным accel-проходом, бесплатный
горизонтальный флип (sstride<0), клип по экрану.  Для персонажей.

libbgi/atlas_load: восстанавливать W3 ДО записи a->count (atlas_t в
--bank памяти резолвится через W3; count оставался мусором).

tests/kbdraw — тест raw-клавиатуры; size-baseline +kbdraw.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 17:48:32 +03:00

339 lines
22 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.
/*
* _bgi.h — внутренности графической подсистемы BGI (НЕ публичный заголовок).
*
* Слияние старых libc/bgi/_bgi.h и libc/gfx/_gfx.h. Графика вынесена из
* libc в самостоятельную библиотеку libbgi/ (см. docs + memory
* bgi_two_lib_design). Из libbgi собираются две driver-библиотеки
* (каждый .c → отдельный .rel):
* bgi256.lib ← common/ + bgi256/ (320×256×256, mode 0x81)
* bgi16.lib ← common/ + bgi16/ (640×256×16, mode 0x82)
* common/ — mode-agnostic: .rel компилируется ОДИН раз и кладётся в
* ОБА архива (один исходник для функций, общих 256- и 16-цветной
* версиям). bgi256/ и bgi16/ — mode-specific leaf'ы (реальные
* реализации, БЕЗ обёрток-делегатов). Режим выбирается линковкой
* (--gfx 256 / --gfx 16), одновременно один.
*
* Публичные leaf-символы (_bgi_plot_raw, _bgi_hspan_raw, _bgi_read_raw,
* _bgi_vspan_raw, _bgi_clear_raw, _bgi_mode_set) одинаково объявлены в
* обоих driver-каталогах, поэтому common-математика не знает режима —
* нужный .rel тянет линкер из выбранной библиотеки.
*/
#ifndef _BGI_INTERNAL_H
#define _BGI_INTERNAL_H
#include <stdint.h>
#include <graphics.h>
#include <gfx.h> /* GFX_MODE_*, размеры — для внутренних таблиц */
/* ---- Состояние BGI (_bgi_state.c) -------------------------------- *
* _bgi_fg/_bgi_bg/_bgi_cx/_bgi_cy/_bgi_maxx/_bgi_maxy/_bgi_maxcolor/
* _bgi_result объявлены в ПУБЛИЧНОМ graphics.h (нужны static inline
* аксессорам: setcolor/moveto/getmaxx/…, 2026-07-10);
* _gfx_visible_page/_gfx_draw_page/_gfx_bank — в публичном gfx.h.
* Байт _gfx_bank (0x50..0x5F): какой видеобанк мапится при записи —
* 0x50 normal, 0x54 temp, 0x58 transparent, 0x5C оба. */
extern uint8_t _bgi_prevmode; /* видеорежим до initgraph */
/* CPU-адрес колонки 0 текущей draw-страницы: 0xC000 (page 0) или
* 0xC140 (page 1); каждый примитив использует его вместо константы. */
extern uint16_t _gfx_addr_base;
/* ---- Низкоуровневый видеорежим (libc/video) ---------------------- */
uint8_t _videomode_raw_get(void);
int _videomode_raw_set(uint8_t mode);
/* ---- W3-скобки (common/_bgi_begin.c, _bgi_end.c) ----------------- *
* begin: DI + сохранить текущий W3 и замапить _gfx_bank;
* end: восстановить W3 + EI. Не реентерабельно (сейв в статике). */
extern uint8_t _gfx_saved_w3;
void _bgi_begin(void) __preserves_regs(b,c,d,e,h,l);
void _bgi_end(void) __preserves_regs(b,c,d,e,h,l);
/* ---- LEAF-операции драйвера (bgi256/ или bgi16/) ----------------- *
* Каждая self-contained: raw-варианты НЕ ставят свою W3-скобку
* (вызывающий обязан обернуть в _bgi_begin/_bgi_end); self-bracket
* (_bgi_read/_bgi_hspan/_bgi_clearall) ставят скобку сами. */
uint8_t _bgi_mode_set(void); /* войти в режим + палитра + границы */
/* __preserves_regs(d,e): проверено по asm — эти примитивы читают D/E
* (y приходит в DE), но НЕ пишут; SDCC не обязан спасать DE вокруг
* вызова (экономит push/pop в горячих циклах Брезенхэма/блита).
* При правке asm сверять клоббер-лист заново! */
void _bgi_plot_raw (int x, int y, uint8_t color) __preserves_regs(d,e); /* точка, без скобки */
uint8_t _bgi_read_raw (int x, int y) __preserves_regs(d,e); /* чтение, без скобки */
void _bgi_hspan_raw(int x, int y, int len, uint8_t color) __preserves_regs(d,e); /* H-отрезок, без скобки */
void _bgi_vspan_raw(int x, int y, int len, uint8_t color) __preserves_regs(d,e); /* V-отрезок, без скобки */
void _bgi_clear_raw(uint8_t color); /* заливка экрана, без скобки (клоббер всех) */
/* Копирование h строк по w байт через accel block-copy (LD L,L) —
* ядро блиттинга (putimage, getimage, gfx_blit, gfx_heal). Без скобки и
* клиппинга; w,h: 0 = 256; stride 0 = адрес на месте (сторона экрана,
* строку выбирает Port_Y); y0 — стартовый Port_Y. Направление задают
* указатели (буфер обязан быть вне W3). Контракт/схема — в
* bgi256/_bgi_copy_rows_raw.c. */
void _bgi_copy_rows_raw(const uint8_t *src, uint8_t *dst,
uint8_t w, uint8_t h,
int dstride, int sstride, uint8_t y0);
/* Специализация для БЛИТА: dst фиксирован (dstride 0), src двигается на
* sstride (SMC-константа); dst-цепочка выкинута (~24Т/строку). Ядро
* gfx_blit_part/putsprite/спрайтового движка. Без скобки/клиппинга; банк
* ставит вызывающий (0x5C прозрачный). bgi256/_bgi_blit_rows_raw.c. */
void _bgi_blit_rows_raw(const uint8_t *src, uint8_t *dst,
uint8_t w, uint8_t h,
int sstride, uint8_t y0);
/* КОЛОНОЧНАЯ специализация (column-major src -> экран): каждая колонка —
* вертикальный accel-burst (LD A,A, Port_Y авто-шаг); dst +1/колонку, src
* +sstride/колонку. Даёт бесплатный горизонтальный флип (sstride<0 + src
* с конца). w=колонок, h=высота(=размер блока), y0=Port_Y стартовый.
* bgi256/_bgi_blit_cols_raw.c. */
void _bgi_blit_cols_raw(const uint8_t *src, uint8_t *dst,
uint8_t w, uint8_t h,
int sstride, uint8_t y0);
/* Специализация для heal: src == dst == адрес экрана, оба страйда 0
* (строку выбирает Port_Y) — адресной арифметики в цикле нет, строка
* 77Т + burst'ы. y0 — стартовый Port_Y; w,h: 0 = 256. Без скобки и
* клиппинга; банк ставит вызывающий (gfx_heal форсит 0x50). */
void _bgi_heal_rows_raw(uint8_t *scr, int y0, uint8_t w, uint8_t h);
/* Self-bracket версии (begin + raw + end) — для разовых вызовов из
* математики, которой не нужен пакетный режим (floodfill, cleardevice).
* Разовое чтение пикселя — публичный getpixel (та же композиция;
* бывший дубликат _bgi_read удалён 2026-07-10). */
void _bgi_hspan (int x, int y, int len, uint8_t color);
void _bgi_clearall(uint8_t color);
/* Ядро блиттинга gfx_blit_part/gfx_blit/gfx_heal (Фаза B) — публичные,
* прототипы в <gfx.h>; спрайтовые обёртки putsprite/movesprite — в
* <graphics.h>. Реализации: common/gfx_blit_part.c и соседи. */
/* Bracket-free общие ядра (полная ширина) (common/_gfx_blit_full.c, _gfx_heal_full.c):
* клип по экрану + нарезка полос ≤256 + accel-leaf, БЕЗ _bgi_begin/
* _bgi_end и БЕЗ смены банка. Вызывающий обязан замапить W3 (_bgi_begin)
* и выставить нужный банк ДО вызова. Общее ядро для разовых обёрток
* (gfx_blit_part/gfx_heal) и batch-пасса спрайтового движка v2, который
* держит одну скобку + один банк на весь кадр (docs/sprite-api-design.md
* §9.1). blit: банк 0x5C для прозрачности; heal: банк 0x50. */
void _gfx_blit_full(int x, int y, const void *img,
int sx, int sy, int w, int h);
void _gfx_heal_full(int x, int y, int w, int h);
/* Лин-ядра для СПРАЙТОВ (кадр ≤ 64×64, sprite.h): как *_full, но без
* split'а >256 и с 8-битными w/h (движок/putsprite — горячий путь;
* программа только со спрайтами не тянет общие *_full со split'ом).
* Сигнатура БЕЗ img/sx/sy (2026-07-14): src — ГОТОВЫЙ адрес пикселей
* кадра, stride — шаг строки источника; считает их вызывающий один раз
* (кэш в sprite_t / putsprite из заголовка) — ни чтения заголовка, ни
* умножения в ядре. common/_gfx_blit_sprite.c, _gfx_heal_sprite.c. */
void _gfx_blit_sprite(int x, int y, const uint8_t *src,
uint16_t stride, uint8_t w, uint8_t h);
void _gfx_heal_sprite(int x, int y, uint8_t w, uint8_t h);
/* NOCLIP-ядра: клип-кода нет вообще (полный выигрыш от codegen, не только
* скип проверок). Зовутся при gfx_sprite_clip(0) — приложение
* гарантирует пределы. common/_gfx_blit_sprite_noclip.c, _gfx_heal_… */
void _gfx_blit_sprite_noclip(int x, int y, const uint8_t *src,
uint16_t stride, uint8_t w, uint8_t h);
void _gfx_heal_sprite_noclip(int x, int y, uint8_t w, uint8_t h);
/* Диспетчеризация clip/noclip через УКАЗАТЕЛИ (не ветка в горячем цикле):
* gfx_sprite_clip() перенаправляет их на clip- или noclip-ядро ОДИН раз,
* а sprite_update/putsprite/movesprite зовут через указатель — один
* call-site, без if в цикле → тесный codegen (полный выигрыш noclip,
* см. docs/sprite-api-design.md). Дефолт (clip-ядра) — статическая
* инициализация; указатели в РАЗДЕЛЬНЫХ data-модулях (_gfx_blit_fn.c,
* _gfx_heal_fn.c): putsprite-only программа не тянет heal-ядро. */
typedef void (*_gfx_blit_fp)(int, int, const uint8_t *, uint16_t,
uint8_t, uint8_t);
typedef void (*_gfx_heal_fp)(int, int, uint8_t, uint8_t);
extern _gfx_blit_fp _gfx_blit_fn;
extern _gfx_heal_fp _gfx_heal_fn;
/* ---- Y-сортировка блит-прохода (sprite.h: gfx_sprite_ysort) ------- *
* _spr_ysort ведёт ПЕРСИСТЕНТНУЮ таблицу 4-байтных записей
* {key16 = layer:8|clamp_y:8, ptr16} (вставки, стабильно; таблица и
* состояние сцены — приватные данные его модуля) и ВОЗВРАЩАЕТ её
* адрес. Указатель спрайта — в записи по смещению +2. Лимит спрайтов
* на кадр — _GFX_YSORT_MAX; больше — sprite_update рисует БЕЗ
* сортировки (фолбэк).
* Подключение — указателем _spr_ysort_fn (NULL = выкл, дефолт; ставит
* gfx_sprite_ysort, обнуляя _spr_ysort_last — форс rebuild) —
* программа без вызова сеттера не линкует ни код сортировщика, ни
* таблицу (funcptr-DCE, как _spr_tick_fn).
* common/_spr_ysort.c, _spr_ysort_fn.c, gfx_sprite_ysort.c. */
#define _GFX_YSORT_MAX 32u
uint8_t *_spr_ysort(void *arr, uint16_t count);
extern uint8_t *(*_spr_ysort_fn)(void *arr, uint16_t count);
extern void *_spr_ysort_last; /* arr прошлого кадра (сеттер сбрасывает) */
extern uint8_t _spr_ysort_kmask[2]; /* маски ключа: [0] y, [1] layer (0/FF) */
/* ---- Атласы в страницах W0 (sprite.h, §9в дизайна) ---------------- *
* _gfx_w0_dss — страница ядра DSS (снимается при первом map/load);
* _gfx_w0_cur — какая спрайт-страница должна стоять в W0 (читает
* ISR-стаб _gfx_w0_isr для restore после прерывания).
* common/_gfx_w0_state.c, _gfx_w0_isr.c. */
extern uint8_t _gfx_w0_dss;
extern uint8_t _gfx_w0_cur;
void _gfx_w0_isr(void);
/* Раскладка .atl-файла/страницы (вариант II, §3.1). */
#define _ATL_DIR_OFF 0x68u /* каталог: count × 8 Б (макс 19) */
#define _ATL_DATA_OFF 0x100u /* ленты; = резерв ISR-стаба */
/* ---- Палитровые хелперы (common/_gfx_pal_buf.c) ------------------- *
* Общий буфер чанковых операций (sync/fload/fsave): 64 записи × 4 Б;
* в W2 (BIOS $A4 требует буфер в #4000-#BFFF). */
#define _GFX_PAL_CHUNK 64u
extern uint8_t _gfx_pal_buf[_GFX_PAL_CHUNK * 4];
/* ---- Авто-анимация спрайтов (§9г) --------------------------------- *
* Тикер подключается указателем (DCE: программа без sprite_anim/
* sprite_moveto не тянет его код): дефолт NULL (_BSS), ставят
* sprite_anim()/sprite_moveto(). sprite_update зовёт в начале кадра.
* Типы из <sprite.h> здесь недоступны (подключается после) — void*. */
typedef void (*_spr_tick_fp)(void *arr, uint8_t count);
extern _spr_tick_fp _spr_tick_fn; /* common/_spr_tick_fn.c */
void _sprite_tick(void *arr, uint8_t count); /* common/_sprite_tick.c */
/* ---- Fill-burst'ы акселератора 256 (register-ABI, asm-only) ------ *
* Вызываются ТОЛЬКО из asm raw-примитивов; аргументы в регистрах, без
* глобального скретча в _DATA:
* HL = CPU-адрес (base + x), C = color,
* B = длина (0 = 256), E = y (строка для Port_Y).
* Клоббер A/F; сохраняют BC/DE/HL/IX/IY. Pre: W3 замаплен, IRQ гасятся
* внутри вокруг одного выстрела. Прототип void(void) — только чтобы
* линкер видел символ (реальный ABI регистровый, см. bgi256/_gfx_*fill256.c). */
/* Горизонтальный Fill-burst (bgi256/_gfx_hfill256.c). */
void _gfx_hfill256_segment(void);
/* Вертикальный Fill-burst (bgi256/_gfx_vfill256.c). */
void _gfx_vfill256_segment(void);
/* Заливка прямоугольника цветом: x, y, w, h, color.
* _gfx_rectfill256 — диспетчер: выбирает ориентацию с меньшим числом
* выстрелов (~160-245Т на проверку) и tail-jump'ит в leaf. Если форма
* известна заранее или |w-h| < ~4, выгоднее звать leaf напрямую:
* _gfx_recthfill256 — h горизонтальных строк, ТРЕБУЕТ 1 <= w <= 256;
* _gfx_rectvfill256 — w вертикальных колонок, ТРЕБУЕТ 1 <= h <= 256.
* Контракты/расчёт break-even — в шапках bgi256/_gfx_rect*fill256.c. */
void _gfx_rectfill256(int x, int y, int w, int h, uint8_t color);
void _gfx_recthfill256(int x, int y, int w, int h, uint8_t color);
void _gfx_rectvfill256(int x, int y, int w, int h, uint8_t color);
/* ---- Скретч 16-цветных примитивов (bgi16/_gfx_g16_state.c) ------- */
extern uint8_t _gfx_g16_y;
extern uint8_t _gfx_g16_byte; /* nibble | (nibble<<4) */
extern uint8_t _gfx_g16_nibble; /* цвет в нужной половине байта */
extern uint8_t _gfx_g16_mask; /* маска, сохраняющая ДРУГУЮ половину */
extern uint8_t _gfx_g16_len; /* размер accel-блока (0 = 256) */
extern uint16_t _gfx_g16_addr;
/* Горизонтальный Fill-burst байтами _gfx_g16_byte (bgi16/_gfx_hfill16.c). */
void _gfx_hfill16_chunk(void);
/* RMW одного нибла по (_gfx_g16_addr, _gfx_g16_y) (bgi16/_gfx_rmw16.c). */
void _gfx_rmw16_pixel(void);
/* ---- Шрифт (common/_gfx_font_state.c, _gfx_font_ensure.c) -------- *
* Формат: 256 глифов × 8 рядов, INTERLEAVED — offset = row*256 + char;
* биты MSB-first. См. memory/sprinter_font_format. */
#define _GFX_FONT_BYTES 2048
extern uint8_t _gfx_font_buf[_GFX_FONT_BYTES];
extern const uint8_t *_gfx_font_ptr;
extern uint8_t _gfx_font_loaded;
/* Ленивая инициализация: грузит системный шрифт при первом использовании. */
void _gfx_font_ensure(void);
/* 8 байт строк глифа c (грузит шрифт при первом использовании) —
* mode-agnostic, просто читает _gfx_font_ptr. common/_bgi_font_rows.c. */
void _bgi_font_rows(unsigned char c, uint8_t *rows8);
/* ---- Тригонометрия для дуг (common/_bgi_trig.c) ------------------ *
* Угол в градусах; результат sin/cos × 128 (Q7 — чтобы радиус×значение
* влезало в int без 32-бит арифметики). */
int _bgi_sin128(int deg);
int _bgi_cos128(int deg);
/* Общий рисователь дуги/контура эллипса (common/_bgi_arc_draw.c):
* полилиния по точкам от stangle до endangle с полуосями xr,yr вокруг
* (cx,cy). */
void _bgi_ellipse_arc(int cx, int cy, int stangle, int endangle,
int xr, int yr, uint8_t color);
/* Целочисленный sqrt (0..~32767) для fillellipse (common/_bgi_isqrt.c). */
int _bgi_isqrt(int n);
/* ---- Состояние линий (common/_bgi_line_state.c) ------------------ */
extern uint8_t _bgi_line_style; /* SOLID_LINE..USERBIT_LINE */
extern uint8_t _bgi_line_thick; /* 1 (NORM) или 3 (THICK) */
extern uint16_t _bgi_line_pattern; /* маска для USERBIT_LINE */
/* Отрезок текущим стилем/толщиной линии (common/_bgi_styled_line.c). */
void _bgi_styled_line(int x0, int y0, int x1, int y1, uint8_t color);
/* Отрезок (x0,y0)->(x1,y1) БЕЗ стиля: ортогонал → hspan/vspan,
* диагональ → Брезенхэм over _bgi_plot_raw, своя W3-скобка
* (common/_bgi_lineseg.c). Реальная логика, не обёртка. */
void _bgi_lineseg(int x0, int y0, int x1, int y1, uint8_t color);
/* ---- Состояние заливки (common/_bgi_fill_state.c) ---------------- */
extern uint8_t _bgi_fill_pattern; /* *_FILL */
extern uint8_t _bgi_fill_color;
/* Залить строку [x0..x1] на y ТЕКУЩИМ стилем заливки (паттерн+цвет),
* с клипом по экрану. Вызывать между _bgi_begin/_bgi_end. */
void _bgi_fill_span(int x0, int x1, int y);
/* ---- Стиль текста (common/_bgi_text_state.c) --------------------- */
extern uint8_t _bgi_text_dir; /* HORIZ_DIR / VERT_DIR */
extern uint8_t _bgi_text_size; /* масштаб 1..10 */
/* Нарисовать строку s в (x,y) текущим стилем текста (масштаб/направление,
* цвет _bgi_fg, прозрачный фон) — common/_bgi_draw_scaled.c. */
void _bgi_draw_scaled(int x, int y, const char *s);
/* ---- Скан-таблицы min/max X по строкам для fillpoly -------------- *
* (common/_bgi_poly_state.c). GFX_HEIGHT — высота экрана (256 в обоих
* режимах), см. <gfx.h>. */
extern int _bgi_span_min[GFX_HEIGHT];
extern int _bgi_span_max[GFX_HEIGHT];
/* Прогнать ребро (Брезенхэм) обновляя _bgi_span_min/max
* (common/_bgi_poly_edge.c). */
void _bgi_poly_edge(int x0, int y0, int x1, int y1);
/* ---- Cross-library: внутренние символы libc/cbl ------------------ *
* libbgi ВСЕГДА линкуется вместе с libc (sprinter.lib), поэтому
* зависимость от внутренних символов libc допустима. gfx_wait_vsync
* держит bit7 порта 0x004E через рефкаунт _cbl_port_ref/unref, чтобы
* бит 5 порта 0xFE (позиция луча) был доступен.
* ВНИМАНИЕ: это ВНУТРЕННИЕ символы libc/cbl — при изменении сигнатур
* СВЕРЯТЬ с libc/cbl/_cbl.h и синхронизировать. (По тому же паттерну,
* каким здесь живут _videomode_raw_* из libc/video.) */
extern void _cbl_port_ref(void);
extern void _cbl_port_unref(void);
/* ---- FPS-делитель (frame pacing) --------------------------------- *
* Фоновый счётчик кадров через цепочку кадровых IRQ (libc/irq). Данные
* в common/_gfx_fps_state.c; при _gfx_fps_div<=1 путь не активен и
* gfx_wait_vsync остаётся лучевым поллингом. Механику irq_chain_add
* дёргает ТОЛЬКО gfx_set_fps_div.c (DCE). */
extern uint8_t _gfx_fps_div; /* 0/1 = старый путь; >=2 — делитель */
extern volatile uint8_t _gfx_frame_tick; /* инкрементит _gfx_frame_isr */
extern uint8_t _gfx_fps_last; /* тик прошлого выхода из wait */
/* Кадровый ISR: чистый инкремент _gfx_frame_tick (правила irq.h —
* никаких ESTEX/gfx/банков). */
void _gfx_frame_isr(void);
#endif