libbgi: выделить графику BGI в отдельную библиотеку + тест спрайтов

Графика вынесена из libc/ в новую библиотеку libbgi/:
  - common/  — mode-agnostic математика и состояние (один исходник,
    .rel попадает в оба driver-архива);
  - bgi256/ + bgi16/ — mode-specific leaf'ы (raw-плот/чтение/спаны);
  - include/ — graphics.h + gfx.h; _bgi.h — внутренний заголовок.
Собираются lib/bgi256.lib (и bgi16.lib в Фазе 2); выбор режима
линковкой через sprinter-cc --gfx 256|16.  libc/ теперь без графики.

tests/bgi_img — тест спрайтов getimage/putimage/imagesize (5 операций
COPY/XOR/OR/AND/NOT + XOR-round-trip + self-check imagesize).
Проверен автотестом в MAME.

Примечание: make size-check пока красный (gfx_dbuf/gfx_demo выросли
после реорга) — закрыть по завершении миграции libbgi.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-09 14:18:33 +03:00
parent 3bf50f7ff7
commit 52636a5d6e
164 changed files with 1072 additions and 1440 deletions
-160
View File
@@ -1,160 +0,0 @@
/*
* gfx.h — Sprinter graphics primitives.
*
* Two main modes:
* GFX_MODE_320x256x256 (0x81) — one byte per pixel, palette of 256
* entries. API functions suffixed _256.
* GFX_MODE_640x256x16 (0x82) — 4 bits per pixel, palette of 16.
* API functions suffixed _16.
*
* Common API (no suffix) covers things that are mode-agnostic:
* gfx_init / gfx_done
* gfx_set_visible_page / gfx_set_draw_page / gfx_set_bank
* gfx_wait_vsync
* gfx_pal_load / gfx_pal_set
* gfx_load_default_font / gfx_set_font
*
* Addressing reminder:
* pixel (x, y) lives at CPU 0xC000 + (x or x/2) with Port_Y (0x89) = y;
* the gfx code maps a 16 KB VRAM page into W3 around every write.
* For double-buffering, page 1 starts 320 bytes later (0xC140).
*
* Palette: BIOS $A4 (RST 8); 4 bytes per entry — 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
/* ---- Setup / teardown -------------------------------------------- *
*
* Switch to `mode`, returning the previous mode for restore. `page`
* (0 or 1) selects the initial graphics screen — both the visible and
* the draw page are set to it, and the W3 bank is reset to 0x50 (the
* canonical visible video page). Programs that don't double-buffer
* just pass 0. */
uint8_t gfx_init(uint8_t mode, uint8_t page);
/* Restore a previously-saved video mode. */
void gfx_done(uint8_t mode);
/* ---- Page selection (double-buffering) and bank control ---------- *
*
* The Sprinter hardware holds two graphics screens. The VISIBLE page
* is what's shown on screen; the DRAW page is where the gfx_* writes
* land. Render the next frame into the hidden page and flip when ready.
*
* "Bank" is the W3 page byte (0x50..0x5F) — bits 2,3 select
* normal/temp/transparent display modes. All gfx_* primitives map
* THIS bank into W3 before touching 0xC000+. Default is 0x50. */
void gfx_set_visible_page(uint8_t page); /* 0 or 1 */
uint8_t gfx_get_visible_page(void);
void gfx_set_draw_page(uint8_t page); /* 0 or 1 */
uint8_t gfx_get_draw_page(void);
void gfx_set_bank(uint8_t bank); /* 0x50..0x5F */
uint8_t gfx_get_bank(void);
/* Block until the next frame interrupt (50 Hz on Sprinter). Uses
* `EI; HALT` — the Z80 sleeps until the next IM2 tick that DSS programs
* for keyboard / cursor handling. Typical use:
*
* gfx_set_draw_page(hidden);
* draw_frame(...);
* gfx_wait_vsync(); // wait for vretrace
* gfx_set_visible_page(hidden); // tear-free flip
*/
void gfx_wait_vsync(void);
/* ---- 320×256×256 (mode 0x81) drawing API -------------------------- *
* Colour args are palette indices 0..255. */
void gfx_clear256 (uint8_t color);
void gfx_putpixel256 (int x, int y, uint8_t color);
uint8_t gfx_getpixel256 (int x, int y);
void gfx_hline256 (int x, int y, int len, uint8_t color);
void gfx_vline256 (int x, int y, int len, uint8_t color);
void gfx_line256 (int x0, int y0, int x1, int y1, uint8_t color);
void gfx_rect256 (int x, int y, int w, int h, uint8_t color);
void gfx_fill_rect256(int x, int y, int w, int h, uint8_t color);
/* ---- 640×256×16 (mode 0x82) drawing API --------------------------- *
* Colour args are palette indices 0..15. The accelerator works
* byte-wise so vline16 falls back to per-row RMW (a byte spans two
* horizontal pixels). */
void gfx_clear16 (uint8_t color);
void gfx_putpixel16 (int x, int y, uint8_t color);
void gfx_hline16 (int x, int y, int len, uint8_t color);
void gfx_vline16 (int x, int y, int len, uint8_t color);
void gfx_line16 (int x0, int y0, int x1, int y1, uint8_t color);
void gfx_rect16 (int x, int y, int w, int h, uint8_t color);
void gfx_fill_rect16(int x, int y, int w, int h, uint8_t color);
/* ---- Bitmap-font text -------------------------------------------- *
* Font is 256 glyphs × 8 rows × 1 byte (ZX-Spectrum format), 2 KB.
* On first use the default system font is fetched via BIOS WIN_GET_ZG
* (fn 0xB8). gfx_set_font() lets you swap in a custom font (the
* pointer is held — keep the storage alive). */
void gfx_load_default_font(void);
void gfx_set_font(const uint8_t *font);
/* 320×256×256 text: one byte per pixel; advances x by 8 per char. */
void gfx_putchar256(int x, int y, char c, uint8_t fg, uint8_t bg);
void gfx_text256 (int x, int y, const char *s, uint8_t fg, uint8_t bg);
/* 640×256×16 text: 4 bits per pixel; x must be EVEN (byte-aligned). */
void gfx_putchar16 (int x, int y, char c, uint8_t fg, uint8_t bg);
void gfx_text16 (int x, int y, const char *s, uint8_t fg, uint8_t bg);
/* ---- Palette ----------------------------------------------------- *
* Each graphics page has its own palette page (page 0 → palette 0,
* page 1 → palette 1). For seamless double-buffering, load the same
* palette into both. */
/* Load a contiguous block of palette entries.
* pal_num: 0..3 (graphics palettes)
* start: first colour slot (0..255)
* count: number of slots (0 → 256)
* data: pointer to count entries, each formatted (B, G, R, 0). */
void gfx_pal_load(uint8_t pal_num, uint8_t start, uint8_t count,
const uint8_t *data);
/* Convenience: set one palette entry from RGB. Internally builds the
* BGR+pad triple and calls gfx_pal_load(pal_num, idx, 1, ...). */
void gfx_pal_set (uint8_t pal_num, uint8_t idx,
uint8_t r, uint8_t g, uint8_t b);
/* Read a contiguous block of entries back from a graphics palette. */
void gfx_pal_get (uint8_t pal_num, uint8_t start, uint8_t count,
uint8_t *data);
/* Read one entry into R, G, B pointers (any may be NULL). */
void gfx_pal_get_color(uint8_t pal_num, uint8_t idx,
uint8_t *r, uint8_t *g, uint8_t *b);
/* Restore the system default graphics palette (BIOS $A6, type=1). */
void gfx_pal_reset(void);
#endif
-193
View File
@@ -1,193 +0,0 @@
/*
* graphics.h — Turbo-C-совместимый (функционально) BGI-слой для Sprinter.
*
* Слой поверх низкоуровневого gfx.h. Состояние (текущий цвет, позиция,
* границы экрана) хранится внутри — как в оригинальном BGI, где функции
* рисуют «текущим» цветом от «текущей» позиции.
*
* РЕЖИМ фиксируется на этапе ЛИНКОВКИ выбором driver-библиотеки:
* sprinter-cc --gfx 256 → 320×256×256 (эта версия)
* (--gfx 16 → 640×256×16 будет позже; API идентичен, менять код не надо)
* Одновременно два режима использовать нельзя.
*
* Отличия от Turbo-C (функциональная, а не буквальная совместимость):
* - initgraph() без аргументов-указателей на драйвер: драйвер задан
* линковкой, грузить .bgi-файл с диска не нужно.
* - в 256-режиме initgraph() загружает EGA-совместимые цвета 0..15 в
* палитру, так что setcolor(RED) и т.п. работают как в Turbo-C;
* индексы 16..255 свободны под свои цвета (getmaxcolor() = 255).
* - fill-паттерны/стили линий/стили текста — Фаза 2 (пока bar/bar-подобное
* заливается текущим цветом, линии сплошные, текст 8×8).
*/
#ifndef GRAPHICS_H
#define GRAPHICS_H
/* ---- Стандартные EGA/VGA цвета (индексы палитры 0..15) ------------ */
enum {
BLACK = 0, BLUE, GREEN, CYAN, RED, MAGENTA, BROWN, LIGHTGRAY,
DARKGRAY, LIGHTBLUE, LIGHTGREEN, LIGHTCYAN, LIGHTRED, LIGHTMAGENTA,
YELLOW, WHITE
};
/* ---- Коды graphresult() ------------------------------------------ */
#define grOk 0
#define grNoInitGraph (-1)
#define grError (-11)
/* ---- Setup / teardown -------------------------------------------- */
/* Перейти в графический режим (для этой либы — 320×256×256), загрузить
* EGA-палитру, сбросить состояние: цвет = WHITE, фон = BLACK, текущая
* позиция = (0,0). Предыдущий видеорежим запоминается для closegraph. */
void initgraph(void);
/* Вернуться в текстовый режим, действовавший до initgraph(). */
void closegraph(void);
/* Код последней ошибки; вызов сбрасывает его в grOk (как в BGI). */
int graphresult(void);
/* Очистить экран фоновым цветом и вернуть позицию в (0,0). */
void cleardevice(void);
/* ---- Границы и цвета --------------------------------------------- */
int getmaxx(void); /* 319 */
int getmaxy(void); /* 255 */
int getmaxcolor(void); /* 255 */
void setcolor(int color); /* текущий цвет рисования */
int getcolor(void);
void setbkcolor(int color); /* фоновый цвет (для cleardevice/текста) */
int getbkcolor(void);
/* ---- Точки ------------------------------------------------------- */
void putpixel(int x, int y, int color);
unsigned getpixel(int x, int y);
/* ---- Текущая позиция и линии ------------------------------------- */
void moveto(int x, int y); /* задать текущую позицию (CP) */
void moverel(int dx, int dy); /* сдвинуть CP относительно */
int getx(void);
int gety(void);
void lineto(int x, int y); /* линия CP→(x,y), CP := (x,y) */
void linerel(int dx, int dy); /* линия CP→CP+(dx,dy), CP сдвигается */
void line(int x1, int y1, int x2, int y2); /* линия, CP не меняет */
/* ---- Фигуры ------------------------------------------------------ */
void rectangle(int left, int top, int right, int bottom); /* контур */
void bar(int left, int top, int right, int bottom); /* заливка */
void circle(int x, int y, int radius); /* окружность */
/* Дуги/эллипсы: угол в градусах, 0°=восток, против часовой стрелки.
* Полный эллипс — ellipse(x,y,0,360,xr,yr). */
void arc(int x, int y, int stangle, int endangle, int radius);
void ellipse(int x, int y, int stangle, int endangle,
int xradius, int yradius);
/* Ломаная по numpoints точкам {x0,y0,x1,y1,…}; НЕ замыкается сама. */
void drawpoly(int numpoints, const int *polypoints);
/* ---- Стиль линий ------------------------------------------------- *
* Действует на line/lineto/linerel/rectangle/drawpoly. USERBIT_LINE
* использует 16-битный upattern. thickness: NORM_WIDTH или THICK_WIDTH.
* (Окружности/дуги пока всегда сплошные 1px — упрощение.) */
enum { SOLID_LINE = 0, DOTTED_LINE, CENTER_LINE, DASHED_LINE, USERBIT_LINE };
#define NORM_WIDTH 1
#define THICK_WIDTH 3
struct linesettingstype { int linestyle; unsigned upattern; int thickness; };
void setlinestyle(int linestyle, unsigned upattern, int thickness);
void getlinesettings(struct linesettingstype *lineinfo);
/* ---- Заливки ----------------------------------------------------- *
* Стиль заливки — паттерн (8×8) + цвет; действует на bar/bar3d/
* fillpoly/fillellipse. SOLID_FILL заполняет сплошняком, EMPTY_FILL —
* фоновым цветом. USER_FILL пока трактуется как SOLID. */
enum {
EMPTY_FILL = 0, SOLID_FILL, LINE_FILL, LTSLASH_FILL, SLASH_FILL,
BKSLASH_FILL, LTBKSLASH_FILL, HATCH_FILL, XHATCH_FILL,
INTERLEAVE_FILL, WIDE_DOT_FILL, CLOSE_DOT_FILL, USER_FILL
};
struct fillsettingstype { int pattern; int color; };
void setfillstyle(int pattern, int color);
void getfillsettings(struct fillsettingstype *fillinfo);
/* bar — залитый прямоугольник (текущий стиль заливки, без рамки). */
/* bar3d — 3D-брусок: перёд залит стилем заливки, рёбра — тек. цветом;
* topflag != 0 рисует верхнюю грань. */
void bar3d(int left, int top, int right, int bottom, int depth, int topflag);
/* fillpoly — залитый многоугольник (авто-замыкается); контур тек.
* цветом, нутро — стилем заливки. Вогнутые заполняются по выпуклой
* оболочке строк (min/max X на строку) — упрощение. */
void fillpoly(int numpoints, const int *polypoints);
/* fillellipse — залитый эллипс (полуоси xradius,yradius). */
void fillellipse(int x, int y, int xradius, int yradius);
/* floodfill — заливка области, содержащей (x,y), текущим стилем до
* границы цвета border. Заливка сплошная цветом заливки (паттерн в
* floodfill пока не применяется — упрощение). */
void floodfill(int x, int y, int border);
/* pieslice — залитый сектор круга; sector — эллиптический сектор.
* Контур (дуга + два радиуса) тек. цветом, нутро — стилем заливки.
* Для секторов >180° возможен перелив в «выемку» (min/max по строке). */
void pieslice(int x, int y, int stangle, int endangle, int radius);
void sector(int x, int y, int stangle, int endangle,
int xradius, int yradius);
/* ---- Растровые образы (спрайты) ---------------------------------- *
* Формат буфера: 2×uint16 (ширина, высота в пикселях) + пиксели
* построчно (1 байт/пиксель в режиме 256). Буфер выделяет вызывающий
* размером imagesize(). */
enum { COPY_PUT = 0, XOR_PUT, OR_PUT, AND_PUT, NOT_PUT };
/* Байт под образ прямоугольника (left,top)-(right,bottom) включительно.
* ВНИМАНИЕ: результат — 16-бит unsigned; образы ≥64 КБ не поддержаны. */
unsigned imagesize(int left, int top, int right, int bottom);
/* Сохранить прямоугольник экрана в bitmap (размер = imagesize()). */
void getimage(int left, int top, int right, int bottom, void *bitmap);
/* Вывести образ левым-верхним углом в (left,top) операцией op
* (COPY/XOR/OR/AND/NOT_PUT). */
void putimage(int left, int top, const void *bitmap, int op);
/* ---- Текст ------------------------------------------------------- *
* Шрифт 8×8 (системный). Рисуется текущим цветом на фоновом. */
void outtextxy(int x, int y, const char *text); /* в (x,y), CP не меняет */
void outtext(const char *text); /* в CP; CP сдвигается вправо */
/* ---- Стиль текста ------------------------------------------------ *
* Поддержан только DEFAULT_FONT (8×8, растровый); charsize 1..10 —
* целочисленный масштаб; direction — HORIZ_DIR или VERT_DIR (поворот
* 90° против часовой). Фон текста прозрачный (рисуется только цвет). */
enum { DEFAULT_FONT = 0, TRIPLEX_FONT, SMALL_FONT, SANS_SERIF_FONT,
GOTHIC_FONT };
enum { HORIZ_DIR = 0, VERT_DIR = 1 };
struct textsettingstype {
int font;
int direction;
int charsize;
int horiz;
int vert;
};
void settextstyle(int font, int direction, int charsize);
void gettextsettings(struct textsettingstype *textinfo);
int textwidth(const char *text);
int textheight(const char *text);
#endif