libc: graphics.h — Turbo-C BGI-слой, Фаза 1 (режим 256)

Функционально-совместимый с Turbo-C <graphics.h> поверх gfx.h.
Архитектура: mode-agnostic математика (libc/bgi/*.c → sprinter.lib) +
driver-leaf'ы per-режим (libc/bgi/drv256/*.c → sprinter_gfx256.lib).
Режим выбирается линковкой: sprinter-cc --gfx 256 (16 — позже, тем же
leaf-split'ом). Один код работает в любом режиме без правок.

API: initgraph/closegraph/graphresult/cleardevice, set/get color+bkcolor,
getmaxx/y/color, put/getpixel, moveto/moverel/getx/gety, line/lineto/
linerel, rectangle, bar, circle, outtext/outtextxy. initgraph грузит
EGA-палитру 0..15. Пакетные примитивы (circle) — одна W3-скобка на
примитив (иначе на порядок медленнее). Проверено в MAME (tests/bgitest).

Попутно: gfx_getpixel256 в libc/gfx. size-check без регресса, baseline
обновлён. Детали: memory/bgi_two_lib_design, docs/TODO.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-08 15:40:49 +03:00
parent 72466f7cad
commit e761d21505
49 changed files with 721 additions and 15 deletions
+51
View File
@@ -0,0 +1,51 @@
/*
* _bgi.h — внутренности BGI-слоя (НЕ публичный заголовок).
*
* Математика примитивов (circle/line/bar/…) — mode-agnostic, лежит в
* каталоге libc/bgi (файлы .c) и попадает в основную sprinter.lib.
* Различающиеся по видеорежиму LEAF-операции (`_bgi_*`) реализованы в
* отдельных driver-каталогах libc/bgi/drv256 и drv16 (каждый — свой архив, выбирается
* линковкой; см. memory/bgi_two_lib_design). Публичные символы leaf'ов
* одинаковы в обоих драйверах, поэтому math не знает режима.
*/
#ifndef _BGI_INTERNAL_H
#define _BGI_INTERNAL_H
#include <stdint.h>
#include <graphics.h>
/* ---- Состояние (_bgi_state.c) ------------------------------------ */
extern uint8_t _bgi_fg; /* текущий цвет рисования */
extern uint8_t _bgi_bg; /* фоновый цвет */
extern int _bgi_cx; /* текущая позиция X (CP) */
extern int _bgi_cy; /* текущая позиция Y */
extern int _bgi_maxx; /* правая граница (319) */
extern int _bgi_maxy; /* нижняя граница (255) */
extern int _bgi_maxcolor; /* макс. индекс цвета (255) */
extern uint8_t _bgi_prevmode; /* видеорежим до initgraph */
extern int _bgi_result; /* код graphresult */
/* ---- LEAF-операции драйвера (каталоги drv256 или drv16) ----------- *
* Каждая self-contained (сама оборачивает W3-скобку через gfx_*).
* mode_set: войти в граф.режим, загрузить палитру, заполнить
* _bgi_maxx/_bgi_maxy/_bgi_maxcolor; вернуть предыдущий видеорежим. */
uint8_t _bgi_mode_set(void);
void _bgi_mode_done(uint8_t prev_mode);
void _bgi_plot(int x, int y, uint8_t color);
unsigned _bgi_read(int x, int y);
/* Пакетное рисование: одна W3-скобка на весь примитив вместо скобки на
* каждый пиксель (для circle/ellipse/fill — тысячи точек). Между
* _bgi_begin и _bgi_end IRQ выключены; плот делать только _bgi_plot_raw
* (без своей скобки, с клипом по краю экрана). */
void _bgi_begin(void);
void _bgi_end(void);
void _bgi_plot_raw(int x, int y, uint8_t color);
void _bgi_hspan(int x, int y, int len, uint8_t color);
void _bgi_lineseg(int x0, int y0, int x1, int y1, uint8_t color);
void _bgi_fillrect(int x, int y, int w, int h, uint8_t color);
void _bgi_clearall(uint8_t color);
void _bgi_drawtext(int x, int y, const char *s, uint8_t fg, uint8_t bg);
#endif
+19
View File
@@ -0,0 +1,19 @@
/*
* _bgi_state.c — общее состояние BGI-слоя.
*
* File-scope переменные НЕ инициализируются (crt0 зануляет _DATA;
* см. memory/sdcc_static_storage_gotcha). Осмысленные значения
* выставляет initgraph().
*/
#include "_bgi.h"
uint8_t _bgi_fg;
uint8_t _bgi_bg;
int _bgi_cx;
int _bgi_cy;
int _bgi_maxx;
int _bgi_maxy;
int _bgi_maxcolor;
uint8_t _bgi_prevmode;
int _bgi_result;
+15
View File
@@ -0,0 +1,15 @@
/*
* bar — залитый прямоугольник (left,top)-(right,bottom) включительно.
*
* В Turbo-C цвет/паттерн заливки задаётся setfillstyle; пока (Фаза 1)
* заливаем текущим цветом рисования сплошняком.
*/
#include "_bgi.h"
void bar(int left, int top, int right, int bottom)
{
int w = right - left + 1;
int h = bottom - top + 1;
if (w <= 0 || h <= 0) return;
_bgi_fillrect(left, top, w, h, _bgi_fg);
}
+40
View File
@@ -0,0 +1,40 @@
/*
* circle — окружность (midpoint) с центром (cx,cy) радиуса r,
* текущим цветом. Целочисленный алгоритм, 8 октантов за шаг.
*
* Вся окружность рисуется в ОДНОЙ W3-скобке (_bgi_begin/_bgi_end) +
* raw-плот — иначе скобка на каждый пиксель делает большие окружности
* мучительно медленными (тысячи DI/EI).
*/
#include "_bgi.h"
void circle(int cx, int cy, int r)
{
int x, y, d;
uint8_t c = _bgi_fg;
if (r < 0) return;
x = 0;
y = r;
d = 1 - r;
_bgi_begin();
while (x <= y) {
_bgi_plot_raw(cx + x, cy + y, c);
_bgi_plot_raw(cx - x, cy + y, c);
_bgi_plot_raw(cx + x, cy - y, c);
_bgi_plot_raw(cx - x, cy - y, c);
_bgi_plot_raw(cx + y, cy + x, c);
_bgi_plot_raw(cx - y, cy + x, c);
_bgi_plot_raw(cx + y, cy - x, c);
_bgi_plot_raw(cx - y, cy - x, c);
if (d < 0) {
d += 2 * x + 3;
} else {
d += 2 * (x - y) + 5;
y--;
}
x++;
}
_bgi_end();
}
+9
View File
@@ -0,0 +1,9 @@
/* cleardevice — очистить экран фоновым цветом, вернуть CP в (0,0). */
#include "_bgi.h"
void cleardevice(void)
{
_bgi_clearall(_bgi_bg);
_bgi_cx = 0;
_bgi_cy = 0;
}
+7
View File
@@ -0,0 +1,7 @@
/* closegraph — вернуть текстовый режим, действовавший до initgraph. */
#include "_bgi.h"
void closegraph(void)
{
_bgi_mode_done(_bgi_prevmode);
}
+8
View File
@@ -0,0 +1,8 @@
/* _bgi_begin (drv256) — открыть W3-сессию для пакетного рисования. */
#include "../_bgi.h"
#include "../../gfx/_gfx.h"
void _bgi_begin(void)
{
_gfx_w3_video_begin();
}
+8
View File
@@ -0,0 +1,8 @@
/* _bgi_clearall (drv256) — залить весь экран цветом. */
#include "../_bgi.h"
#include <gfx.h>
void _bgi_clearall(uint8_t color)
{
gfx_clear256(color);
}
+8
View File
@@ -0,0 +1,8 @@
/* _bgi_drawtext (drv256) — строка шрифтом 8×8 цветами fg/bg в (x,y). */
#include "../_bgi.h"
#include <gfx.h>
void _bgi_drawtext(int x, int y, const char *s, uint8_t fg, uint8_t bg)
{
gfx_text256(x, y, s, fg, bg);
}
+8
View File
@@ -0,0 +1,8 @@
/* _bgi_end (drv256) — закрыть W3-сессию пакетного рисования. */
#include "../_bgi.h"
#include "../../gfx/_gfx.h"
void _bgi_end(void)
{
_gfx_w3_video_end();
}
+8
View File
@@ -0,0 +1,8 @@
/* _bgi_fillrect (drv256) — залитый прямоугольник w×h из (x,y). */
#include "../_bgi.h"
#include <gfx.h>
void _bgi_fillrect(int x, int y, int w, int h, uint8_t color)
{
gfx_fill_rect256(x, y, w, h, color);
}
+8
View File
@@ -0,0 +1,8 @@
/* _bgi_hspan (drv256) — горизонтальный отрезок len пикселей. */
#include "../_bgi.h"
#include <gfx.h>
void _bgi_hspan(int x, int y, int len, uint8_t color)
{
gfx_hline256(x, y, len, color);
}
+8
View File
@@ -0,0 +1,8 @@
/* _bgi_lineseg (drv256) — отрезок (x0,y0)->(x1,y1) (использует accel-line). */
#include "../_bgi.h"
#include <gfx.h>
void _bgi_lineseg(int x0, int y0, int x1, int y1, uint8_t color)
{
gfx_line256(x0, y0, x1, y1, color);
}
+8
View File
@@ -0,0 +1,8 @@
/* _bgi_mode_done (drv256) — вернуть видеорежим, бывший до initgraph. */
#include "../_bgi.h"
#include <gfx.h>
void _bgi_mode_done(uint8_t prev_mode)
{
gfx_done(prev_mode);
}
+40
View File
@@ -0,0 +1,40 @@
/*
* _bgi_mode_set (drv256) — вход в графический режим 320×256×256.
*
* Загружает EGA-совместимые цвета 0..15 в палитру 0, чтобы setcolor(RED)
* и прочие BGI-константы давали ожидаемый цвет. Заполняет границы
* экрана в состоянии BGI. Возвращает предыдущий видеорежим.
*/
#include "../_bgi.h"
#include <gfx.h>
/* 16 EGA-цветов, формат записи палитры (B, G, R, pad). */
static const uint8_t _bgi_ega_pal[16 * 4] = {
0, 0, 0, 0, /* 0 BLACK */
168, 0, 0, 0, /* 1 BLUE */
0, 168, 0, 0, /* 2 GREEN */
168, 168, 0, 0, /* 3 CYAN */
0, 0, 168, 0, /* 4 RED */
168, 0, 168, 0, /* 5 MAGENTA */
0, 84, 168, 0, /* 6 BROWN */
168, 168, 168, 0, /* 7 LIGHTGRAY */
84, 84, 84, 0, /* 8 DARKGRAY */
255, 84, 84, 0, /* 9 LIGHTBLUE */
84, 255, 84, 0, /* 10 LIGHTGREEN */
255, 255, 84, 0, /* 11 LIGHTCYAN */
84, 84, 255, 0, /* 12 LIGHTRED */
255, 84, 255, 0, /* 13 LIGHTMAGENTA */
84, 255, 255, 0, /* 14 YELLOW */
255, 255, 255, 0, /* 15 WHITE */
};
uint8_t _bgi_mode_set(void)
{
uint8_t prev = gfx_init(GFX_MODE_320x256x256, 0);
gfx_pal_load(0, 0, 16, _bgi_ega_pal);
_bgi_maxx = GFX_WIDTH - 1;
_bgi_maxy = GFX_HEIGHT - 1;
_bgi_maxcolor = 255;
return prev;
}
+8
View File
@@ -0,0 +1,8 @@
/* _bgi_plot (drv256) — одиночный пиксель текущим цветом. */
#include "../_bgi.h"
#include <gfx.h>
void _bgi_plot(int x, int y, uint8_t color)
{
gfx_putpixel256(x, y, color);
}
+11
View File
@@ -0,0 +1,11 @@
/*
* _bgi_plot_raw (drv256) — пиксель без своей W3-скобки (вызывать между
* _bgi_begin/_bgi_end). Клип по краю экрана делает сам raw-примитив.
*/
#include "../_bgi.h"
#include "../../gfx/_gfx.h"
void _bgi_plot_raw(int x, int y, uint8_t color)
{
_gfx_putpixel256_raw(x, y, color);
}
+8
View File
@@ -0,0 +1,8 @@
/* _bgi_read (drv256) — цвет пикселя (x,y). */
#include "../_bgi.h"
#include <gfx.h>
unsigned _bgi_read(int x, int y)
{
return gfx_getpixel256(x, y);
}
+7
View File
@@ -0,0 +1,7 @@
/* getbkcolor — текущий фоновый цвет. */
#include "_bgi.h"
int getbkcolor(void)
{
return _bgi_bg;
}
+7
View File
@@ -0,0 +1,7 @@
/* getcolor — текущий цвет рисования. */
#include "_bgi.h"
int getcolor(void)
{
return _bgi_fg;
}
+7
View File
@@ -0,0 +1,7 @@
/* getmaxcolor — максимальный индекс цвета (255 в 256-цветном режиме). */
#include "_bgi.h"
int getmaxcolor(void)
{
return _bgi_maxcolor;
}
+7
View File
@@ -0,0 +1,7 @@
/* getmaxx — максимальная координата X (ширина-1). */
#include "_bgi.h"
int getmaxx(void)
{
return _bgi_maxx;
}
+7
View File
@@ -0,0 +1,7 @@
/* getmaxy — максимальная координата Y (высота-1). */
#include "_bgi.h"
int getmaxy(void)
{
return _bgi_maxy;
}
+7
View File
@@ -0,0 +1,7 @@
/* getpixel — цвет пикселя (x,y). */
#include "_bgi.h"
unsigned getpixel(int x, int y)
{
return _bgi_read(x, y);
}
+7
View File
@@ -0,0 +1,7 @@
/* getx — текущая позиция X. */
#include "_bgi.h"
int getx(void)
{
return _bgi_cx;
}
+7
View File
@@ -0,0 +1,7 @@
/* gety — текущая позиция Y. */
#include "_bgi.h"
int gety(void)
{
return _bgi_cy;
}
+9
View File
@@ -0,0 +1,9 @@
/* graphresult — код последней ошибки; вызов сбрасывает его в grOk. */
#include "_bgi.h"
int graphresult(void)
{
int r = _bgi_result;
_bgi_result = grOk;
return r;
}
+18
View File
@@ -0,0 +1,18 @@
/*
* initgraph — перейти в графический режим и сбросить состояние BGI.
*
* Режим определяется driver-библиотекой (для sprinter_gfx256 —
* 320×256×256). По умолчанию: цвет WHITE, фон BLACK, позиция (0,0).
*/
#include "_bgi.h"
void initgraph(void)
{
_bgi_prevmode = _bgi_mode_set();
_bgi_fg = WHITE;
_bgi_bg = BLACK;
_bgi_cx = 0;
_bgi_cy = 0;
_bgi_result = grOk;
}
+7
View File
@@ -0,0 +1,7 @@
/* line — отрезок (x1,y1)->(x2,y2) текущим цветом; CP не меняется. */
#include "_bgi.h"
void line(int x1, int y1, int x2, int y2)
{
_bgi_lineseg(x1, y1, x2, y2, _bgi_fg);
}
+11
View File
@@ -0,0 +1,11 @@
/* linerel — линия от CP до CP+(dx,dy); CP сдвигается в конец. */
#include "_bgi.h"
void linerel(int dx, int dy)
{
int nx = _bgi_cx + dx;
int ny = _bgi_cy + dy;
_bgi_lineseg(_bgi_cx, _bgi_cy, nx, ny, _bgi_fg);
_bgi_cx = nx;
_bgi_cy = ny;
}
+9
View File
@@ -0,0 +1,9 @@
/* lineto — линия от CP до (x,y) текущим цветом; CP := (x,y). */
#include "_bgi.h"
void lineto(int x, int y)
{
_bgi_lineseg(_bgi_cx, _bgi_cy, x, y, _bgi_fg);
_bgi_cx = x;
_bgi_cy = y;
}
+8
View File
@@ -0,0 +1,8 @@
/* moverel — сдвинуть текущую позицию относительно. */
#include "_bgi.h"
void moverel(int dx, int dy)
{
_bgi_cx += dx;
_bgi_cy += dy;
}
+8
View File
@@ -0,0 +1,8 @@
/* moveto — задать текущую позицию (CP). */
#include "_bgi.h"
void moveto(int x, int y)
{
_bgi_cx = x;
_bgi_cy = y;
}
+12
View File
@@ -0,0 +1,12 @@
/*
* outtext — строка в текущей позиции (CP) текущим цветом; CP сдвигается
* вправо на ширину строки (шрифт 8×8, 8 пикселей на символ).
*/
#include "_bgi.h"
#include <string.h>
void outtext(const char *text)
{
_bgi_drawtext(_bgi_cx, _bgi_cy, text, _bgi_fg, _bgi_bg);
_bgi_cx += (int)strlen(text) * 8;
}
+7
View File
@@ -0,0 +1,7 @@
/* outtextxy — строка шрифтом 8×8 в (x,y) текущим цветом; CP не меняет. */
#include "_bgi.h"
void outtextxy(int x, int y, const char *text)
{
_bgi_drawtext(x, y, text, _bgi_fg, _bgi_bg);
}
+7
View File
@@ -0,0 +1,7 @@
/* putpixel — точка (x,y) заданным цветом (не трогает текущий цвет/CP). */
#include "_bgi.h"
void putpixel(int x, int y, int color)
{
_bgi_plot(x, y, (uint8_t)color);
}
+11
View File
@@ -0,0 +1,11 @@
/* rectangle — контур прямоугольника по углам (left,top)-(right,bottom). */
#include "_bgi.h"
void rectangle(int left, int top, int right, int bottom)
{
uint8_t c = _bgi_fg;
_bgi_lineseg(left, top, right, top, c); /* верх */
_bgi_lineseg(right, top, right, bottom, c); /* право */
_bgi_lineseg(right, bottom, left, bottom, c); /* низ */
_bgi_lineseg(left, bottom, left, top, c); /* лево */
}
+7
View File
@@ -0,0 +1,7 @@
/* setbkcolor — задать фоновый цвет (cleardevice, фон текста). */
#include "_bgi.h"
void setbkcolor(int color)
{
_bgi_bg = (uint8_t)color;
}
+7
View File
@@ -0,0 +1,7 @@
/* setcolor — задать текущий цвет рисования. */
#include "_bgi.h"
void setcolor(int color)
{
_bgi_fg = (uint8_t)color;
}
+30
View File
@@ -0,0 +1,30 @@
/*
* gfx_getpixel256 — прочитать цвет пикселя (x,y) в режиме 0x81.
*
* Одиночный вызов в собственной W3-скобке. Адресация та же, что у
* _gfx_putpixel256_raw: CPU _gfx_addr_base + x, Port_Y (0x89) = y.
* За пределами экрана возвращает 0.
*/
#include "_gfx.h"
static uint8_t _gfx_gp_y;
static uint16_t _gfx_gp_addr;
static uint8_t _gfx_gp_color;
uint8_t gfx_getpixel256(int x, int y)
{
if ((unsigned)x >= GFX_WIDTH || (unsigned)y >= GFX_HEIGHT) return 0;
_gfx_gp_y = (uint8_t)y;
_gfx_gp_addr = (uint16_t)(_gfx_addr_base + (unsigned)x);
_gfx_w3_video_begin();
__asm
ld a, (__gfx_gp_y)
out (#0x89), a
ld hl, (__gfx_gp_addr)
ld a, (hl)
ld (__gfx_gp_color), a
__endasm;
_gfx_w3_video_end();
return _gfx_gp_color;
}
+1
View File
@@ -91,6 +91,7 @@ void gfx_wait_vsync(void);
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);
+93
View File
@@ -0,0 +1,93 @@
/*
* 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); /* окружность */
/* ---- Текст ------------------------------------------------------- *
* Шрифт 8×8 (системный). Рисуется текущим цветом на фоновом. */
void outtextxy(int x, int y, const char *text); /* в (x,y), CP не меняет */
void outtext(const char *text); /* в CP; CP сдвигается вправо */
#endif