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
+15 -2
View File
@@ -86,6 +86,7 @@ LD_EXTRA=()
MKEXE_EXTRA=()
BANK_SPECS=() # entries like "1=engine.c"
MAX_ALLOCS="" # if set, passed to sdcc as --max-allocs-per-node
GFX_MODE="" # "256" → link BGI graphics.h driver sprinter_gfx256.lib
# ------- Parse args ----------------------------------------------------------
usage() {
@@ -110,6 +111,7 @@ while [[ $# -gt 0 ]]; do
--bank) BANK_SPECS+=("$2"); shift 2;;
--mkexe) MKEXE_EXTRA+=("$2"); shift 2;;
--max-allocs) MAX_ALLOCS="$2"; shift 2;;
--gfx) GFX_MODE="$2"; shift 2;;
--debug) DEBUG_RT=1; shift;;
-v) VERBOSE=1; shift;;
-h|--help) usage 0;;
@@ -121,6 +123,17 @@ done
[[ -z "$OUT" ]] && { echo "sprinter-cc: -o NAME is required" >&2; exit 1; }
[[ ${#SOURCES[@]} -eq 0 ]] && { echo "sprinter-cc: no input files" >&2; exit 1; }
# ------- BGI graphics driver selection (--gfx) -------------------------------
# graphics.h — mode-agnostic слой; конкретный видеорежим задаёт driver-
# архив. Одновременно только один. drv16 пока не реализован.
GFX_LD=()
case "$GFX_MODE" in
"") ;; # graphics.h не используется
256) GFX_LD=("-lsprinter_gfx256");;
16) echo "sprinter-cc: --gfx 16 ещё не реализован (пока только 256)" >&2; exit 1;;
*) echo "sprinter-cc: --gfx: ожидается 256 (или 16), дано: $GFX_MODE" >&2; exit 1;;
esac
# ------- Resolve memory mode → CODE_LOC / DATA_LOC ---------------------------
# tiny : CODE in W2 (0x8100), DATA auto after code (= W2)
# small : CODE in W1 (0x4100), DATA in W2 (0x8000) — crt0 must alloc W2
@@ -342,12 +355,12 @@ for f in "${LD_EXTRA[@]}"; do LINK_FLAGS+=("$f"); done
if [[ $VERBOSE -eq 1 ]]; then
run "$SDCC" "${LINK_FLAGS[@]}" -o "$IHX" \
"$CRT0_REL" "$HEAP_TOP_REL" "${USER_RELS[@]}" "${BANK_RELS[@]}" \
"-L$LIB_DIR" "-lsprinter"
"-L$LIB_DIR" "${GFX_LD[@]}" "-lsprinter"
else
# Drop the warning line + its two follow-up "Library:" lines.
run "$SDCC" "${LINK_FLAGS[@]}" -o "$IHX" \
"$CRT0_REL" "$HEAP_TOP_REL" "${USER_RELS[@]}" "${BANK_RELS[@]}" \
"-L$LIB_DIR" "-lsprinter" 2>&1 \
"-L$LIB_DIR" "${GFX_LD[@]}" "-lsprinter" 2>&1 \
| awk '
/^\?ASlink-Warning-Definition of public symbol/ { skip = 3 }
skip > 0 { skip--; next }
+20 -7
View File
@@ -24,14 +24,27 @@ first-fit-decreasing, уважение `#pragma codeseg BANKn`, перелинк
## ОБЯЗАТЕЛЬНОЕ ДЛЯ V2
### Turbo-C-style graphics API (BGI-like)
### Turbo-C-style graphics API (BGI-like) — Фаза 1 ГОТОВА (256, 2026-07-08)
Расширить `gfx_*` до уровня Turbo-C `<graphics.h>`: initgraph/
closegraph/cleardevice-алиасы, current point (moveto/lineto/linerel),
setcolor/setbkcolor, getpixel, circle/arc/ellipse/pieslice, drawpoly/
fillpoly, floodfill, bar/bar3d, outtext[xy]/settextstyle/textwidth,
imagesize/getimage/putimage (COPY/XOR/AND/OR/NOT_PUT), setviewport/
клиппинг, setactivepage/setvisualpage (2 страницы есть), setlinestyle.
Архитектура: `graphics.h` — mode-agnostic слой (libc/bgi/*.c в
sprinter.lib), режим задаёт driver-архив; выбор линковкой через
`sprinter-cc --gfx 256` (16 — позже, тем же leaf-split'ом; см.
memory/bgi_two_lib_design).
**Фаза 1 (256, реализовано и проверено в MAME — tests/bgitest):**
initgraph/closegraph/graphresult/cleardevice, setcolor/getcolor/
setbkcolor/getbkcolor, getmaxx/getmaxy/getmaxcolor, putpixel/getpixel,
moveto/moverel/getx/gety, line/lineto/linerel, rectangle, bar, circle,
outtext/outtextxy. initgraph грузит EGA-палитру 0..15. Пакетные
примитивы (circle) — одна W3-скобка на весь примитив (raw-плот), иначе
на порядок медленнее.
**Фаза 2 (осталось):** ellipse/arc/pieslice, drawpoly/fillpoly,
floodfill, setfillstyle/паттерны, bar3d, settextstyle/textwidth/
textheight (размеры/направление), imagesize/getimage/putimage
(COPY/XOR/AND/OR/NOT_PUT), setviewport/клиппинг, setactivepage/
setvisualpage (2 страницы есть), setlinestyle, setaspectratio
(пиксели 320×256 неквадратные — окружности визуально эллиптичны).
Acceptance: типичная BGI-программа переносится без существенных
правок. Референс: Turbo C 2.x BGIDEMO.
+21
View File
@@ -206,6 +206,27 @@ rect fill_rect` — через акселератор (hline/vline burst до 25
чётный), `gfx_load_default_font`, `gfx_set_font(ptr)` (interleaved
font[row*256+char]).
Палитра: `gfx_pal_load/set/get/get_color/reset` (страницы 0..3).
`gfx_getpixel256(x,y)→color` — чтение пикселя (0x81).
## <graphics.h> — Turbo-C BGI (функц. совместимость), Фаза 1, режим 256
Слой поверх `<gfx.h>` со «текущим» цветом/позицией. Режим задаётся
driver-либой на линковке: `sprinter-cc --gfx 256` (→ 320×256×256; 16 —
позже). API mode-agnostic: код не меняется при смене режима.
Setup: `initgraph()` (без аргументов — режим фиксирован либой; грузит
EGA-палитру 0..15, цвет=WHITE, фон=BLACK, CP=(0,0)),
`closegraph()`, `graphresult()`, `cleardevice()`.
Границы/цвет: `getmaxx/getmaxy` (319/255), `getmaxcolor` (255),
`setcolor/getcolor`, `setbkcolor/getbkcolor`. Константы BLACK..WHITE.
Точки: `putpixel(x,y,c)`, `getpixel(x,y)`.
Позиция/линии: `moveto/moverel/getx/gety`, `lineto/linerel` (двигают
CP), `line(x1,y1,x2,y2)` (не двигает).
Фигуры: `rectangle` (контур), `bar` (заливка тек. цветом — паттерны в
Ф2), `circle`. Текст 8×8: `outtextxy(x,y,s)`, `outtext(s)` (двигает CP).
Реализация: libc/bgi/*.c (math) + libc/bgi/drv256/*.c (leaf →
sprinter_gfx256.lib). Пакетные примитивы — одна W3-скобка на примитив.
Ф2: ellipse/arc/fillpoly/floodfill/setfillstyle/getimage/putimage/
viewport/settextstyle — см. docs/TODO.md.
## <irq.h> — user-ISR кадрового прерывания (IM 2)
+1
View File
@@ -6,6 +6,7 @@ banked 1056
bankedbg 1067
banklocl 4832
banktest 3767
bgitest 4158
bios_text 4470
cat 915
cblstream 6277
1 # Эталон размеров _CODE (байт); обновление: python3 toolchain/size_check.py --update
6 bankedbg
7 banklocl
8 banktest
9 bgitest
10 bios_text
11 cat
12 cblstream
+25 -5
View File
@@ -23,8 +23,16 @@ BUILD := $(PROJ_ROOT)/lib/build
# All libc C modules — every .c under libc/<area>/ becomes its own .rel.
# One PUBLIC function per file (see docs/libc-split-asm-cases.md): the
# linker pulls whole .rel modules, so file granularity == DCE granularity.
# Внимание: `libc/*/*.c` — ровно ОДИН уровень вложенности, поэтому
# ловит mode-agnostic BGI-математику libc/bgi/*.c (идёт в sprinter.lib),
# но НЕ ловит driver-leaf'ы libc/bgi/drv256/*.c (два уровня) — они
# собираются в ОТДЕЛЬНЫЙ архив sprinter_gfx256.lib (выбор режима
# линковкой; см. memory/bgi_two_lib_design).
LIBC_C := $(patsubst $(PROJ_ROOT)/%,%,$(wildcard $(PROJ_ROOT)/libc/*/*.c))
# Driver-leaf'ы 256-цветного BGI → sprinter_gfx256.lib.
GFX256_C := $(patsubst $(PROJ_ROOT)/%,%,$(wildcard $(PROJ_ROOT)/libc/bgi/drv256/*.c))
# Runtime modules to bundle (pulled by symbol references from libc-using code).
# NOTE: runtime/bank.s is NOT bundled — its trampoline depends on the banking
# window (W1 for BIG, W3 for HUGE) which is decided per-build by sprinter-cc.
@@ -32,9 +40,14 @@ LIBC_C := $(patsubst $(PROJ_ROOT)/%,%,$(wildcard $(PROJ_ROOT)/libc/*/*.c))
RUNTIME_S := runtime/heap.s
LIBC_RELS := $(patsubst libc/%.c,$(BUILD)/%.rel,$(LIBC_C))
GFX256_RELS := $(patsubst libc/%.c,$(BUILD)/%.rel,$(GFX256_C))
RUNTIME_RELS := $(patsubst runtime/%.s,$(BUILD)/%.rel,$(RUNTIME_S))
# Члены основной sprinter.lib.
ALL_RELS := $(LIBC_RELS) $(RUNTIME_RELS)
# Всё, что вообще собираем (для штампа и чистки stale — иначе .rel'ы
# драйвера сочтутся «осиротевшими» и будут удалены).
KNOWN_RELS := $(ALL_RELS) $(GFX256_RELS)
# Штамп списка модулей: обновляется при любом изменении состава
# (добавление/удаление/переименование исходника) — иначе удаление
@@ -42,13 +55,14 @@ ALL_RELS := $(LIBC_RELS) $(RUNTIME_RELS)
# Сразу сносим и архив: на exFAT (SAM8) гранулярность mtime грубая,
# и «список изменился» через сравнение времён можно проглядеть.
MODLIST := $(BUILD)/.modules
ifneq ($(shell cat $(MODLIST) 2>/dev/null),$(ALL_RELS))
_ := $(shell mkdir -p $(BUILD); echo "$(ALL_RELS)" > $(MODLIST); rm -f sprinter.lib)
ifneq ($(shell cat $(MODLIST) 2>/dev/null),$(KNOWN_RELS))
_ := $(shell mkdir -p $(BUILD); echo "$(KNOWN_RELS)" > $(MODLIST); rm -f sprinter.lib sprinter_gfx256.lib)
endif
LIB := sprinter.lib
GFX256_LIB := sprinter_gfx256.lib
all: $(LIB)
all: $(LIB) $(GFX256_LIB)
# Pattern rule for C modules — preserves the libc/io|mem|stdio path
# so .rel members keep their natural names inside the archive.
@@ -67,7 +81,7 @@ $(BUILD)/%.rel: $(PROJ_ROOT)/runtime/%.s
# вечно (уже кусало: старый file.rel после сплита).
$(LIB): $(ALL_RELS) $(MODLIST)
@find $(BUILD) -name '*.rel' -print | sort > $(BUILD)/.have
@printf '%s\n' $(ALL_RELS) | sort > $(BUILD)/.want
@printf '%s\n' $(KNOWN_RELS) | sort > $(BUILD)/.want
@comm -23 $(BUILD)/.have $(BUILD)/.want | while read f; do \
echo " stale: $$f (исходник удалён) — rm"; \
rm -f "$$f" "$${f%.rel}.asm" "$${f%.rel}.lst" "$${f%.rel}.sym"; \
@@ -78,7 +92,13 @@ $(LIB): $(ALL_RELS) $(MODLIST)
@echo " Built $@ with $(words $(ALL_RELS)) modules:"
@$(SDAR) -t $@ | sed 's/^/ /'
# Driver-архив 256-цветного BGI: только leaf'ы drv256/.
$(GFX256_LIB): $(GFX256_RELS) $(MODLIST)
rm -f $@
$(SDAR) -rcs $@ $(GFX256_RELS)
@echo " Built $@ with $(words $(GFX256_RELS)) modules."
clean:
rm -rf $(BUILD) $(LIB)
rm -rf $(BUILD) $(LIB) $(GFX256_LIB)
.PHONY: all clean
+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
+4
View File
@@ -0,0 +1,4 @@
PROJ_ROOT := $(abspath $(CURDIR)/../..)
EXAMPLE := bgitest
EXTRA_FLAGS := --gfx 256
include $(PROJ_ROOT)/app.mk
+61
View File
@@ -0,0 +1,61 @@
/*
* bgitest — демонстрация BGI-слоя graphics.h (режим 320×256×256).
*
* Собирается с `--gfx 256` (см. Makefile). Рисует рамку, заголовок,
* палитровую полосу, веер линий, вложенные окружности и залитые бары —
* визуальная проверка ядра BGI (Фаза 1).
*/
#include <graphics.h>
int main(void)
{
int i, mx, my, cx, cy;
initgraph();
mx = getmaxx(); /* 319 */
my = getmaxy(); /* 255 */
cx = mx / 2;
cy = my / 2;
cleardevice();
/* Рамка по краю экрана. */
setcolor(WHITE);
rectangle(0, 0, mx, my);
/* Заголовок. */
setcolor(YELLOW);
outtextxy(88, 6, "SPRINTER BGI 256");
/* Палитровая полоса: 15 залитых баров цветами 1..15. */
for (i = 1; i <= 15; i++) {
int x0 = 8 + (i - 1) * 20;
setcolor(i);
bar(x0, 24, x0 + 16, 44);
}
/* Веер линий из центра к верхней кромке. */
for (i = 0; i <= mx; i += 16) {
setcolor(1 + (i / 16) % 15);
line(cx, cy, i, 52);
}
/* Вложенные окружности. */
for (i = 8; i < 90; i += 8) {
setcolor(9 + (i / 8) % 7);
circle(cx, cy, i);
}
/* Точечная диагональ (putpixel). */
for (i = 0; i < 60; i++)
putpixel(10 + i, 200 + i, LIGHTGREEN);
/* Подпись внизу текущей позицией (outtext). */
setcolor(LIGHTCYAN);
moveto(8, 240);
outtext("Phase 1: lines rects circles text");
for (;;) { } /* держим картинку на экране */
return 0;
}