diff --git a/bin/sprinter-cc b/bin/sprinter-cc index fe9f549..b8a46a0 100755 --- a/bin/sprinter-cc +++ b/bin/sprinter-cc @@ -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 } diff --git a/docs/TODO.md b/docs/TODO.md index 7fea377..1b2164f 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -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 ``: 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. diff --git a/docs/libc-reference.md b/docs/libc-reference.md index 4c4b536..6b16dcf 100644 --- a/docs/libc-reference.md +++ b/docs/libc-reference.md @@ -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). + +## — Turbo-C BGI (функц. совместимость), Фаза 1, режим 256 + +Слой поверх `` со «текущим» цветом/позицией. Режим задаётся +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. ## — user-ISR кадрового прерывания (IM 2) diff --git a/docs/size_baseline.tsv b/docs/size_baseline.tsv index 1db9216..36682a9 100644 --- a/docs/size_baseline.tsv +++ b/docs/size_baseline.tsv @@ -6,6 +6,7 @@ banked 1056 bankedbg 1067 banklocl 4832 banktest 3767 +bgitest 4158 bios_text 4470 cat 915 cblstream 6277 diff --git a/lib/Makefile b/lib/Makefile index a77344d..eace06d 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -23,8 +23,16 @@ BUILD := $(PROJ_ROOT)/lib/build # All libc C modules — every .c under libc// 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 +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 diff --git a/libc/bgi/_bgi.h b/libc/bgi/_bgi.h new file mode 100644 index 0000000..11289cd --- /dev/null +++ b/libc/bgi/_bgi.h @@ -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 +#include + +/* ---- Состояние (_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 diff --git a/libc/bgi/_bgi_state.c b/libc/bgi/_bgi_state.c new file mode 100644 index 0000000..a5e9ffc --- /dev/null +++ b/libc/bgi/_bgi_state.c @@ -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; diff --git a/libc/bgi/bar.c b/libc/bgi/bar.c new file mode 100644 index 0000000..d044aa7 --- /dev/null +++ b/libc/bgi/bar.c @@ -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); +} diff --git a/libc/bgi/circle.c b/libc/bgi/circle.c new file mode 100644 index 0000000..3318b16 --- /dev/null +++ b/libc/bgi/circle.c @@ -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(); +} diff --git a/libc/bgi/cleardevice.c b/libc/bgi/cleardevice.c new file mode 100644 index 0000000..4724dbf --- /dev/null +++ b/libc/bgi/cleardevice.c @@ -0,0 +1,9 @@ +/* cleardevice — очистить экран фоновым цветом, вернуть CP в (0,0). */ +#include "_bgi.h" + +void cleardevice(void) +{ + _bgi_clearall(_bgi_bg); + _bgi_cx = 0; + _bgi_cy = 0; +} diff --git a/libc/bgi/closegraph.c b/libc/bgi/closegraph.c new file mode 100644 index 0000000..7e155d4 --- /dev/null +++ b/libc/bgi/closegraph.c @@ -0,0 +1,7 @@ +/* closegraph — вернуть текстовый режим, действовавший до initgraph. */ +#include "_bgi.h" + +void closegraph(void) +{ + _bgi_mode_done(_bgi_prevmode); +} diff --git a/libc/bgi/drv256/_bgi_begin.c b/libc/bgi/drv256/_bgi_begin.c new file mode 100644 index 0000000..b4c2346 --- /dev/null +++ b/libc/bgi/drv256/_bgi_begin.c @@ -0,0 +1,8 @@ +/* _bgi_begin (drv256) — открыть W3-сессию для пакетного рисования. */ +#include "../_bgi.h" +#include "../../gfx/_gfx.h" + +void _bgi_begin(void) +{ + _gfx_w3_video_begin(); +} diff --git a/libc/bgi/drv256/_bgi_clearall.c b/libc/bgi/drv256/_bgi_clearall.c new file mode 100644 index 0000000..2aea0d9 --- /dev/null +++ b/libc/bgi/drv256/_bgi_clearall.c @@ -0,0 +1,8 @@ +/* _bgi_clearall (drv256) — залить весь экран цветом. */ +#include "../_bgi.h" +#include + +void _bgi_clearall(uint8_t color) +{ + gfx_clear256(color); +} diff --git a/libc/bgi/drv256/_bgi_drawtext.c b/libc/bgi/drv256/_bgi_drawtext.c new file mode 100644 index 0000000..1608977 --- /dev/null +++ b/libc/bgi/drv256/_bgi_drawtext.c @@ -0,0 +1,8 @@ +/* _bgi_drawtext (drv256) — строка шрифтом 8×8 цветами fg/bg в (x,y). */ +#include "../_bgi.h" +#include + +void _bgi_drawtext(int x, int y, const char *s, uint8_t fg, uint8_t bg) +{ + gfx_text256(x, y, s, fg, bg); +} diff --git a/libc/bgi/drv256/_bgi_end.c b/libc/bgi/drv256/_bgi_end.c new file mode 100644 index 0000000..eb43be2 --- /dev/null +++ b/libc/bgi/drv256/_bgi_end.c @@ -0,0 +1,8 @@ +/* _bgi_end (drv256) — закрыть W3-сессию пакетного рисования. */ +#include "../_bgi.h" +#include "../../gfx/_gfx.h" + +void _bgi_end(void) +{ + _gfx_w3_video_end(); +} diff --git a/libc/bgi/drv256/_bgi_fillrect.c b/libc/bgi/drv256/_bgi_fillrect.c new file mode 100644 index 0000000..b94f704 --- /dev/null +++ b/libc/bgi/drv256/_bgi_fillrect.c @@ -0,0 +1,8 @@ +/* _bgi_fillrect (drv256) — залитый прямоугольник w×h из (x,y). */ +#include "../_bgi.h" +#include + +void _bgi_fillrect(int x, int y, int w, int h, uint8_t color) +{ + gfx_fill_rect256(x, y, w, h, color); +} diff --git a/libc/bgi/drv256/_bgi_hspan.c b/libc/bgi/drv256/_bgi_hspan.c new file mode 100644 index 0000000..ad8bbd2 --- /dev/null +++ b/libc/bgi/drv256/_bgi_hspan.c @@ -0,0 +1,8 @@ +/* _bgi_hspan (drv256) — горизонтальный отрезок len пикселей. */ +#include "../_bgi.h" +#include + +void _bgi_hspan(int x, int y, int len, uint8_t color) +{ + gfx_hline256(x, y, len, color); +} diff --git a/libc/bgi/drv256/_bgi_lineseg.c b/libc/bgi/drv256/_bgi_lineseg.c new file mode 100644 index 0000000..02c7e17 --- /dev/null +++ b/libc/bgi/drv256/_bgi_lineseg.c @@ -0,0 +1,8 @@ +/* _bgi_lineseg (drv256) — отрезок (x0,y0)->(x1,y1) (использует accel-line). */ +#include "../_bgi.h" +#include + +void _bgi_lineseg(int x0, int y0, int x1, int y1, uint8_t color) +{ + gfx_line256(x0, y0, x1, y1, color); +} diff --git a/libc/bgi/drv256/_bgi_mode_done.c b/libc/bgi/drv256/_bgi_mode_done.c new file mode 100644 index 0000000..0b6c74b --- /dev/null +++ b/libc/bgi/drv256/_bgi_mode_done.c @@ -0,0 +1,8 @@ +/* _bgi_mode_done (drv256) — вернуть видеорежим, бывший до initgraph. */ +#include "../_bgi.h" +#include + +void _bgi_mode_done(uint8_t prev_mode) +{ + gfx_done(prev_mode); +} diff --git a/libc/bgi/drv256/_bgi_mode_set.c b/libc/bgi/drv256/_bgi_mode_set.c new file mode 100644 index 0000000..8dfd0fc --- /dev/null +++ b/libc/bgi/drv256/_bgi_mode_set.c @@ -0,0 +1,40 @@ +/* + * _bgi_mode_set (drv256) — вход в графический режим 320×256×256. + * + * Загружает EGA-совместимые цвета 0..15 в палитру 0, чтобы setcolor(RED) + * и прочие BGI-константы давали ожидаемый цвет. Заполняет границы + * экрана в состоянии BGI. Возвращает предыдущий видеорежим. + */ + +#include "../_bgi.h" +#include + +/* 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; +} diff --git a/libc/bgi/drv256/_bgi_plot.c b/libc/bgi/drv256/_bgi_plot.c new file mode 100644 index 0000000..6041ba7 --- /dev/null +++ b/libc/bgi/drv256/_bgi_plot.c @@ -0,0 +1,8 @@ +/* _bgi_plot (drv256) — одиночный пиксель текущим цветом. */ +#include "../_bgi.h" +#include + +void _bgi_plot(int x, int y, uint8_t color) +{ + gfx_putpixel256(x, y, color); +} diff --git a/libc/bgi/drv256/_bgi_plot_raw.c b/libc/bgi/drv256/_bgi_plot_raw.c new file mode 100644 index 0000000..0ce530f --- /dev/null +++ b/libc/bgi/drv256/_bgi_plot_raw.c @@ -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); +} diff --git a/libc/bgi/drv256/_bgi_read.c b/libc/bgi/drv256/_bgi_read.c new file mode 100644 index 0000000..8303807 --- /dev/null +++ b/libc/bgi/drv256/_bgi_read.c @@ -0,0 +1,8 @@ +/* _bgi_read (drv256) — цвет пикселя (x,y). */ +#include "../_bgi.h" +#include + +unsigned _bgi_read(int x, int y) +{ + return gfx_getpixel256(x, y); +} diff --git a/libc/bgi/getbkcolor.c b/libc/bgi/getbkcolor.c new file mode 100644 index 0000000..c907e85 --- /dev/null +++ b/libc/bgi/getbkcolor.c @@ -0,0 +1,7 @@ +/* getbkcolor — текущий фоновый цвет. */ +#include "_bgi.h" + +int getbkcolor(void) +{ + return _bgi_bg; +} diff --git a/libc/bgi/getcolor.c b/libc/bgi/getcolor.c new file mode 100644 index 0000000..e0c2b0d --- /dev/null +++ b/libc/bgi/getcolor.c @@ -0,0 +1,7 @@ +/* getcolor — текущий цвет рисования. */ +#include "_bgi.h" + +int getcolor(void) +{ + return _bgi_fg; +} diff --git a/libc/bgi/getmaxcolor.c b/libc/bgi/getmaxcolor.c new file mode 100644 index 0000000..d862c74 --- /dev/null +++ b/libc/bgi/getmaxcolor.c @@ -0,0 +1,7 @@ +/* getmaxcolor — максимальный индекс цвета (255 в 256-цветном режиме). */ +#include "_bgi.h" + +int getmaxcolor(void) +{ + return _bgi_maxcolor; +} diff --git a/libc/bgi/getmaxx.c b/libc/bgi/getmaxx.c new file mode 100644 index 0000000..0e326e5 --- /dev/null +++ b/libc/bgi/getmaxx.c @@ -0,0 +1,7 @@ +/* getmaxx — максимальная координата X (ширина-1). */ +#include "_bgi.h" + +int getmaxx(void) +{ + return _bgi_maxx; +} diff --git a/libc/bgi/getmaxy.c b/libc/bgi/getmaxy.c new file mode 100644 index 0000000..b7f63c0 --- /dev/null +++ b/libc/bgi/getmaxy.c @@ -0,0 +1,7 @@ +/* getmaxy — максимальная координата Y (высота-1). */ +#include "_bgi.h" + +int getmaxy(void) +{ + return _bgi_maxy; +} diff --git a/libc/bgi/getpixel.c b/libc/bgi/getpixel.c new file mode 100644 index 0000000..1a56540 --- /dev/null +++ b/libc/bgi/getpixel.c @@ -0,0 +1,7 @@ +/* getpixel — цвет пикселя (x,y). */ +#include "_bgi.h" + +unsigned getpixel(int x, int y) +{ + return _bgi_read(x, y); +} diff --git a/libc/bgi/getx.c b/libc/bgi/getx.c new file mode 100644 index 0000000..b1cf7a3 --- /dev/null +++ b/libc/bgi/getx.c @@ -0,0 +1,7 @@ +/* getx — текущая позиция X. */ +#include "_bgi.h" + +int getx(void) +{ + return _bgi_cx; +} diff --git a/libc/bgi/gety.c b/libc/bgi/gety.c new file mode 100644 index 0000000..35dc83d --- /dev/null +++ b/libc/bgi/gety.c @@ -0,0 +1,7 @@ +/* gety — текущая позиция Y. */ +#include "_bgi.h" + +int gety(void) +{ + return _bgi_cy; +} diff --git a/libc/bgi/graphresult.c b/libc/bgi/graphresult.c new file mode 100644 index 0000000..c7f4511 --- /dev/null +++ b/libc/bgi/graphresult.c @@ -0,0 +1,9 @@ +/* graphresult — код последней ошибки; вызов сбрасывает его в grOk. */ +#include "_bgi.h" + +int graphresult(void) +{ + int r = _bgi_result; + _bgi_result = grOk; + return r; +} diff --git a/libc/bgi/initgraph.c b/libc/bgi/initgraph.c new file mode 100644 index 0000000..d6224af --- /dev/null +++ b/libc/bgi/initgraph.c @@ -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; +} diff --git a/libc/bgi/line.c b/libc/bgi/line.c new file mode 100644 index 0000000..5ccb0c3 --- /dev/null +++ b/libc/bgi/line.c @@ -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); +} diff --git a/libc/bgi/linerel.c b/libc/bgi/linerel.c new file mode 100644 index 0000000..b6b715d --- /dev/null +++ b/libc/bgi/linerel.c @@ -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; +} diff --git a/libc/bgi/lineto.c b/libc/bgi/lineto.c new file mode 100644 index 0000000..1197884 --- /dev/null +++ b/libc/bgi/lineto.c @@ -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; +} diff --git a/libc/bgi/moverel.c b/libc/bgi/moverel.c new file mode 100644 index 0000000..b3dd510 --- /dev/null +++ b/libc/bgi/moverel.c @@ -0,0 +1,8 @@ +/* moverel — сдвинуть текущую позицию относительно. */ +#include "_bgi.h" + +void moverel(int dx, int dy) +{ + _bgi_cx += dx; + _bgi_cy += dy; +} diff --git a/libc/bgi/moveto.c b/libc/bgi/moveto.c new file mode 100644 index 0000000..8a3b7fa --- /dev/null +++ b/libc/bgi/moveto.c @@ -0,0 +1,8 @@ +/* moveto — задать текущую позицию (CP). */ +#include "_bgi.h" + +void moveto(int x, int y) +{ + _bgi_cx = x; + _bgi_cy = y; +} diff --git a/libc/bgi/outtext.c b/libc/bgi/outtext.c new file mode 100644 index 0000000..f573ce1 --- /dev/null +++ b/libc/bgi/outtext.c @@ -0,0 +1,12 @@ +/* + * outtext — строка в текущей позиции (CP) текущим цветом; CP сдвигается + * вправо на ширину строки (шрифт 8×8, 8 пикселей на символ). + */ +#include "_bgi.h" +#include + +void outtext(const char *text) +{ + _bgi_drawtext(_bgi_cx, _bgi_cy, text, _bgi_fg, _bgi_bg); + _bgi_cx += (int)strlen(text) * 8; +} diff --git a/libc/bgi/outtextxy.c b/libc/bgi/outtextxy.c new file mode 100644 index 0000000..c9a65f3 --- /dev/null +++ b/libc/bgi/outtextxy.c @@ -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); +} diff --git a/libc/bgi/putpixel.c b/libc/bgi/putpixel.c new file mode 100644 index 0000000..f1d6996 --- /dev/null +++ b/libc/bgi/putpixel.c @@ -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); +} diff --git a/libc/bgi/rectangle.c b/libc/bgi/rectangle.c new file mode 100644 index 0000000..09509e2 --- /dev/null +++ b/libc/bgi/rectangle.c @@ -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); /* лево */ +} diff --git a/libc/bgi/setbkcolor.c b/libc/bgi/setbkcolor.c new file mode 100644 index 0000000..05a26be --- /dev/null +++ b/libc/bgi/setbkcolor.c @@ -0,0 +1,7 @@ +/* setbkcolor — задать фоновый цвет (cleardevice, фон текста). */ +#include "_bgi.h" + +void setbkcolor(int color) +{ + _bgi_bg = (uint8_t)color; +} diff --git a/libc/bgi/setcolor.c b/libc/bgi/setcolor.c new file mode 100644 index 0000000..d416ae5 --- /dev/null +++ b/libc/bgi/setcolor.c @@ -0,0 +1,7 @@ +/* setcolor — задать текущий цвет рисования. */ +#include "_bgi.h" + +void setcolor(int color) +{ + _bgi_fg = (uint8_t)color; +} diff --git a/libc/gfx/gfx_getpixel256.c b/libc/gfx/gfx_getpixel256.c new file mode 100644 index 0000000..8ea4137 --- /dev/null +++ b/libc/gfx/gfx_getpixel256.c @@ -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; +} diff --git a/libc/include/gfx.h b/libc/include/gfx.h index f4004d0..197a004 100644 --- a/libc/include/gfx.h +++ b/libc/include/gfx.h @@ -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); diff --git a/libc/include/graphics.h b/libc/include/graphics.h new file mode 100644 index 0000000..8550db5 --- /dev/null +++ b/libc/include/graphics.h @@ -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 diff --git a/tests/bgitest/Makefile b/tests/bgitest/Makefile new file mode 100644 index 0000000..4d148a8 --- /dev/null +++ b/tests/bgitest/Makefile @@ -0,0 +1,4 @@ +PROJ_ROOT := $(abspath $(CURDIR)/../..) +EXAMPLE := bgitest +EXTRA_FLAGS := --gfx 256 +include $(PROJ_ROOT)/app.mk diff --git a/tests/bgitest/bgitest.c b/tests/bgitest/bgitest.c new file mode 100644 index 0000000..19cc2d9 --- /dev/null +++ b/tests/bgitest/bgitest.c @@ -0,0 +1,61 @@ +/* + * bgitest — демонстрация BGI-слоя graphics.h (режим 320×256×256). + * + * Собирается с `--gfx 256` (см. Makefile). Рисует рамку, заголовок, + * палитровую полосу, веер линий, вложенные окружности и залитые бары — + * визуальная проверка ядра BGI (Фаза 1). + */ + +#include + +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; +}