/* * _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 #include #include /* 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); /* Специализация для 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) — публичные, * прототипы в ; спрайтовые обёртки putsprite/movesprite — в * . Реализации: 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'ом). * common/_gfx_blit_sprite.c, _gfx_heal_sprite.c. */ void _gfx_blit_sprite(int x, int y, const void *img, int sx, int sy, 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 void *img, int sx, int sy, 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 void *, int, int, 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; /* ---- Атласы в страницах 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-стаба */ /* ---- 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 в обоих * режимах), см. . */ 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); #endif