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
+89
View File
@@ -0,0 +1,89 @@
# Build lib/sprinter.lib — the Sprinter target libc archive.
#
# libc/ — ядро библиотеки (БЕЗ графики; графика вынесена в libbgi/).
# Каждый .c под libc/<area>/ — отдельный .rel в архиве (1 публичная
# функция = 1 модуль); линкер тянет только нужные .rel → DCE на уровне
# файлов. Плюс runtime-хелперы, которые автотянутся кодогеном SDCC
# (heap для malloc). crt0-семейство НЕ в архиве — оно всегда явно
# линкуется, не DCE-eligible. См. docs/libc-split-asm-cases.md.
#
# make build lib/sprinter.lib
# make clean remove build/ + lib/sprinter.lib
#
# Симметрично libbgi/Makefile: исходники и build/ рядом, результат
# ложится в общий каталог lib/ (откуда его линкует sprinter-cc).
PROJ_ROOT := $(abspath $(CURDIR)/..)
SDCC_BIN := $(PROJ_ROOT)/third_party/sdcc/bin
SDCC := $(SDCC_BIN)/sdcc
SDAR := $(SDCC_BIN)/sdar
SDASZ80 := $(SDCC_BIN)/sdasz80
INC := -I$(CURDIR)/include
CC_FLAGS := -mz80 --no-std-crt0 --std-c99 --opt-code-size $(INC)
BUILD := $(CURDIR)/build
# All libc C modules — every .c under libc/<area>/ becomes its own .rel.
# `libc/*/*.c` — ровно ОДИН уровень вложенности; графика (gfx/bgi) сюда
# не попадает (вынесена в libbgi/). Один PUBLIC-символ на файл.
LIBC_C := $(wildcard $(CURDIR)/*/*.c)
# Runtime modules to bundle (pulled by symbol references from libc-using
# code). bank.s НЕ bundled — его трамплин зависит от banking-окна (W1
# для BIG, W3 для HUGE), что решается per-build в sprinter-cc; он
# ассемблирует bank.s с нужным BANK_W1 флагом на каждый banked-build.
RUNTIME_S := $(PROJ_ROOT)/runtime/heap.s
LIBC_RELS := $(patsubst $(CURDIR)/%.c,$(BUILD)/%.rel,$(LIBC_C))
RUNTIME_RELS := $(patsubst $(PROJ_ROOT)/runtime/%.s,$(BUILD)/%.rel,$(RUNTIME_S))
ALL_RELS := $(LIBC_RELS) $(RUNTIME_RELS)
KNOWN_RELS := $(ALL_RELS)
# Штамп списка модулей: обновляется при любом изменении состава
# (добавление/удаление/переименование исходника) — иначе удаление
# исходника не триггерит перелинковку и stale .rel остаётся в архиве.
# Сразу сносим и архив: на exFAT (SAM8) гранулярность mtime грубая,
# и «список изменился» через сравнение времён можно проглядеть.
MODLIST := $(BUILD)/.modules
ifneq ($(shell cat $(MODLIST) 2>/dev/null),$(KNOWN_RELS))
_ := $(shell mkdir -p $(BUILD); echo "$(KNOWN_RELS)" > $(MODLIST); rm -f $(LIB))
endif
LIB_DIR := $(PROJ_ROOT)/lib
LIB := $(LIB_DIR)/sprinter.lib
all: $(LIB)
# Pattern rule для C-модулей libc/<area>/file.c → build/<area>/file.rel
# (сохраняет путь io|mem|stdio внутри архива).
$(BUILD)/%.rel: $(CURDIR)/%.c
@mkdir -p $(dir $@)
$(SDCC) $(CC_FLAGS) -c -o $@ $<
# Runtime .s → .rel (runtime/ вне libc/, но heap.s бандлится в архив).
$(BUILD)/%.rel: $(PROJ_ROOT)/runtime/%.s
@mkdir -p $(dir $@)
$(SDASZ80) -o $@ $<
# Archive — sdar rcs = replace/create/symtab. Перед упаковкой удаляем
# stale .rel: wildcard не знает об удалённых/переименованных исходниках,
# и осиротевший .rel из build/ иначе жил бы вечно.
$(LIB): $(ALL_RELS) $(MODLIST)
@find $(BUILD) -name '*.rel' -print | sort > $(BUILD)/.have
@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"; \
done
rm -f $@
$(SDAR) -rcs $@ $(ALL_RELS)
@echo
@echo " Built $@ with $(words $(ALL_RELS)) modules:"
@$(SDAR) -t $@ | sed 's/^/ /'
clean:
rm -rf $(BUILD) $(LIB)
.PHONY: all clean
-107
View File
@@ -1,107 +0,0 @@
/*
* _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>
#include <gfx.h> /* GFX_WIDTH/HEIGHT для внутренних таблиц */
/* ---- Состояние (_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);
/* ---- Тригонометрия для дуг (_bgi_trig.c) ------------------------- *
* Угол в градусах; результат sin/cos × 128 (Q7 — чтобы радиус×значение
* влезало в int без 32-бит арифметики). */
int _bgi_sin128(int deg);
int _bgi_cos128(int deg);
/* Общий рисователь дуги/контура эллипса (_bgi_arc_draw.c): полилиния
* по точкам от stangle до endangle с полуосями xr,yr вокруг (cx,cy). */
void _bgi_ellipse_arc(int cx, int cy, int stangle, int endangle,
int xr, int yr, uint8_t color);
/* Целочисленный sqrt (0..~32767) для fillellipse (_bgi_isqrt.c). */
int _bgi_isqrt(int n);
/* ---- Состояние линий (_bgi_line_state.c) ------------------------- */
extern uint8_t _bgi_line_style; /* SOLID_LINE..USERBIT_LINE */
extern uint8_t _bgi_line_thick; /* 1 (NORM) или 3 (THICK) */
extern uint16_t _bgi_line_pattern; /* маска для USERBIT_LINE */
/* Отрезок текущим стилем/толщиной линии (_bgi_styled_line.c). */
void _bgi_styled_line(int x0, int y0, int x1, int y1, uint8_t color);
/* ---- Состояние заливки (_bgi_fill_state.c) ----------------------- */
extern uint8_t _bgi_fill_pattern; /* *_FILL */
extern uint8_t _bgi_fill_color;
/* Raw horizontal span (без своей W3-скобки; клип делает сам gfx-raw). */
void _bgi_hspan_raw(int x, int y, int len, uint8_t color);
/* Raw чтение пикселя (без своей W3-скобки) — для getimage/putimage. */
unsigned _bgi_read_raw(int x, int y);
/* ---- Стиль текста (_bgi_text_state.c) ---------------------------- */
extern uint8_t _bgi_text_dir; /* HORIZ_DIR / VERT_DIR */
extern uint8_t _bgi_text_size; /* масштаб 1..10 */
/* Заполнить 8 байт строк глифа символа c (грузит шрифт при первом
* использовании) — drv256/_bgi_font_rows.c. */
void _bgi_font_rows(unsigned char c, uint8_t *rows8);
/* Нарисовать строку s в (x,y) текущим стилем текста (масштаб/направление,
* цвет _bgi_fg, прозрачный фон) — _bgi_draw_scaled.c. */
void _bgi_draw_scaled(int x, int y, const char *s);
/* Залить строку [x0..x1] на y ТЕКУЩИМ стилем заливки (паттерн+цвет),
* с клипом по экрану. Вызывать между _bgi_begin/_bgi_end. */
void _bgi_fill_span(int x0, int x1, int y);
/* Скан-таблицы min/max X по строкам для fillpoly (_bgi_poly_state.c). */
extern int _bgi_span_min[GFX_HEIGHT];
extern int _bgi_span_max[GFX_HEIGHT];
/* Прогнать ребро (Брезенхэм) обновляя _bgi_span_min/max (_bgi_poly_edge.c). */
void _bgi_poly_edge(int x0, int y0, int x1, int y1);
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
-33
View File
@@ -1,33 +0,0 @@
/*
* _bgi_ellipse_arc — контур дуги/эллипса полилинией.
*
* Точки параметризованы углом (BGI: 0°=восток, против часовой, ось Y
* вниз): px = cx + xr*cos(a), py = cy - yr*sin(a). Соседние выборки
* соединяются отрезками — контур сплошной при любом радиусе.
*
* Тригонометрия в Q7 (×128): xr*cos128 ≤ 255×128 < 32767, помещается в
* int — БЕЗ 32-битной арифметики (её SDCC/Z80 собирает криво: раньше
* эллипс через (long)…>>8 рисовался прямоугольником).
*/
#include "_bgi.h"
void _bgi_ellipse_arc(int cx, int cy, int stangle, int endangle,
int xr, int yr, uint8_t color)
{
int a, x, y, px, py, first;
if (endangle < stangle) endangle += 360; /* дуга через 0° */
px = 0;
py = 0;
first = 1;
for (a = stangle; a <= endangle; a++) {
x = cx + ((xr * _bgi_cos128(a)) >> 7);
y = cy - ((yr * _bgi_sin128(a)) >> 7);
if (!first) _bgi_lineseg(px, py, x, y, color);
px = x;
py = y;
first = 0;
}
}
-48
View File
@@ -1,48 +0,0 @@
/*
* _bgi_draw_scaled — строка растровым шрифтом 8×8 с целочисленным
* масштабом и направлением, цветом _bgi_fg, прозрачным фоном.
*
* HORIZ_DIR: пиксель глифа (c,r) → экран (x+c*size, y+r*size).
* VERT_DIR (поворот 90° против часовой): (c,r) → (x+r*size, y-c*size).
* Каждый «пиксель» глифа — блок size×size из raw-плотов, всё в одной
* W3-скобке.
*/
#include "_bgi.h"
void _bgi_draw_scaled(int x, int y, const char *s)
{
uint8_t rows[8];
uint8_t b, fg, size, dir;
int r, c, bx, by, px, py;
fg = _bgi_fg;
size = _bgi_text_size ? _bgi_text_size : 1;
dir = _bgi_text_dir;
_bgi_begin();
while (*s) {
_bgi_font_rows((unsigned char)*s, rows);
for (r = 0; r < 8; r++) {
b = rows[r];
for (c = 0; c < 8; c++) {
if (!((b >> (7 - c)) & 1)) continue;
for (by = 0; by < size; by++) {
for (bx = 0; bx < size; bx++) {
if (dir == VERT_DIR) {
px = x + r * size + by;
py = y - c * size - bx;
} else {
px = x + c * size + bx;
py = y + r * size + by;
}
_bgi_plot_raw(px, py, fg);
}
}
}
}
if (dir == VERT_DIR) y -= 8 * size;
else x += 8 * size;
s++;
}
_bgi_end();
}
-47
View File
@@ -1,47 +0,0 @@
/*
* _bgi_fill_span — залить строку [x0..x1] на y текущим стилем заливки.
*
* SOLID → быстрый raw-hline; EMPTY → hline фоновым цветом; паттерн →
* поточечно по 8×8-битовой маске (строка паттерна = pat[y&7], бит x&7).
* Клип по экрану внутри. ВЫЗЫВАТЬ между _bgi_begin/_bgi_end.
*/
#include "_bgi.h"
/* Стандартные 8×8-паттерны Borland, индексы LINE_FILL..CLOSE_DOT_FILL. */
static const uint8_t _bgi_pat[10][8] = {
{ 0xFF,0xFF,0x00,0x00,0xFF,0xFF,0x00,0x00 }, /* LINE_FILL */
{ 0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80 }, /* LTSLASH_FILL */
{ 0xE0,0xC1,0x83,0x07,0x0E,0x1C,0x38,0x70 }, /* SLASH_FILL */
{ 0xF0,0x78,0x3C,0x1E,0x0F,0x87,0xC3,0xE1 }, /* BKSLASH_FILL */
{ 0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01 }, /* LTBKSLASH_FILL */
{ 0xFF,0x88,0x88,0x88,0xFF,0x88,0x88,0x88 }, /* HATCH_FILL */
{ 0x81,0x42,0x24,0x18,0x18,0x24,0x42,0x81 }, /* XHATCH_FILL */
{ 0xCC,0x33,0xCC,0x33,0xCC,0x33,0xCC,0x33 }, /* INTERLEAVE_FILL */
{ 0x80,0x00,0x08,0x00,0x80,0x00,0x08,0x00 }, /* WIDE_DOT_FILL */
{ 0x88,0x00,0x22,0x00,0x88,0x00,0x22,0x00 }, /* CLOSE_DOT_FILL */
};
void _bgi_fill_span(int x0, int x1, int y)
{
int x;
uint8_t p, row;
if (x1 < x0) { x = x0; x0 = x1; x1 = x; }
if (y < 0 || y > _bgi_maxy) return;
if (x0 < 0) x0 = 0;
if (x1 > _bgi_maxx) x1 = _bgi_maxx;
if (x1 < x0) return;
p = _bgi_fill_pattern;
if (p == EMPTY_FILL) { _bgi_hspan_raw(x0, y, x1 - x0 + 1, _bgi_bg); return; }
if (p == SOLID_FILL || p == USER_FILL || p > CLOSE_DOT_FILL) {
_bgi_hspan_raw(x0, y, x1 - x0 + 1, _bgi_fill_color);
return;
}
/* Паттерн LINE_FILL..CLOSE_DOT_FILL. */
row = _bgi_pat[p - LINE_FILL][y & 7];
for (x = x0; x <= x1; x++)
if ((row >> (x & 7)) & 1)
_bgi_plot_raw(x, y, _bgi_fill_color);
}
-10
View File
@@ -1,10 +0,0 @@
/*
* _bgi_fill_state — текущий стиль заливки BGI (паттерн + цвет).
* Значения по умолчанию выставляет initgraph (SOLID_FILL/WHITE).
* Без инициализации (crt0 зануляет _DATA).
*/
#include "_bgi.h"
uint8_t _bgi_fill_pattern;
uint8_t _bgi_fill_color;
-15
View File
@@ -1,15 +0,0 @@
/*
* _bgi_isqrt — целочисленный floor(sqrt(n)) для n в 0..~32767.
* Линейный поиск (n≤180² → ≤180 шагов) — для fillellipse достаточно,
* без float и 32-бит.
*/
#include "_bgi.h"
int _bgi_isqrt(int n)
{
int x = 0;
if (n < 0) return 0;
while ((x + 1) * (x + 1) <= n) x++;
return x;
}
-9
View File
@@ -1,9 +0,0 @@
/*
* _bgi_line_state — текущий стиль/толщина/маска линий BGI.
* initgraph ставит SOLID_LINE / NORM / 0xFFFF. Без инициализации.
*/
#include "_bgi.h"
uint8_t _bgi_line_style;
uint8_t _bgi_line_thick;
uint16_t _bgi_line_pattern;
-30
View File
@@ -1,30 +0,0 @@
/*
* _bgi_poly_edge — прогнать ребро (x0,y0)->(x1,y1) по Брезенхэму,
* обновляя _bgi_span_min/_bgi_span_max для каждой затронутой строки.
* Только целочисленные сложения (без 32-бит). Строки вне экрана
* пропускаются. Заливка потом идёт от min до max по строке.
*/
#include "_bgi.h"
void _bgi_poly_edge(int x0, int y0, int x1, int y1)
{
int dx, dy, sx, sy, err, e2;
dx = x1 - x0; if (dx < 0) dx = -dx;
dy = y1 - y0; if (dy < 0) dy = -dy;
sx = (x0 < x1) ? 1 : -1;
sy = (y0 < y1) ? 1 : -1;
err = dx - dy;
for (;;) {
if (y0 >= 0 && y0 < GFX_HEIGHT) {
if (x0 < _bgi_span_min[y0]) _bgi_span_min[y0] = x0;
if (x0 > _bgi_span_max[y0]) _bgi_span_max[y0] = x0;
}
if (x0 == x1 && y0 == y1) break;
e2 = 2 * err;
if (e2 > -dy) { err -= dy; x0 += sx; }
if (e2 < dx) { err += dx; y0 += sy; }
}
}
-11
View File
@@ -1,11 +0,0 @@
/*
* _bgi_poly_state — скан-таблицы min/max X по строкам для fillpoly.
* По строке экрана хранит крайние левый/правый X контура — заливка
* идёт от min до max (выпуклая оболочка строки). ~1 КБ в _DATA,
* однопоточно (рисование не реентерабельно).
*/
#include "_bgi.h"
int _bgi_span_min[GFX_HEIGHT];
int _bgi_span_max[GFX_HEIGHT];
-19
View File
@@ -1,19 +0,0 @@
/*
* _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;
-58
View File
@@ -1,58 +0,0 @@
/*
* _bgi_styled_line — отрезок текущим стилем и толщиной линии.
*
* SOLID+NORM → быстрый _bgi_lineseg (accel). Иначе — Брезенхэм с
* 16-битной маской (бит на шаг; когда бит 0 — пропуск) и, для THICK,
* дублированием перпендикулярно основной оси (±1). Всё raw в одной
* W3-скобке.
*/
#include "_bgi.h"
/* Маски стандартных стилей (SOLID/DOTTED/CENTER/DASHED). */
static const uint16_t _bgi_line_pat[4] = { 0xFFFF, 0xCCCC, 0xFF18, 0xF8F8 };
void _bgi_styled_line(int x0, int y0, int x1, int y1, uint8_t color)
{
uint16_t pat;
uint8_t bit, style, thick;
int dx, dy, sx, sy, err, e2, major_x;
style = _bgi_line_style;
thick = _bgi_line_thick;
if (style == SOLID_LINE && thick <= 1) {
_bgi_lineseg(x0, y0, x1, y1, color);
return;
}
pat = (style == USERBIT_LINE) ? _bgi_line_pattern
: _bgi_line_pat[style & 3];
dx = x1 - x0; if (dx < 0) dx = -dx;
dy = y1 - y0; if (dy < 0) dy = -dy;
sx = (x0 < x1) ? 1 : -1;
sy = (y0 < y1) ? 1 : -1;
err = dx - dy;
major_x = (dx >= dy);
bit = 0;
_bgi_begin();
for (;;) {
if ((pat >> (bit & 15)) & 1) {
_bgi_plot_raw(x0, y0, color);
if (thick > 1) {
if (major_x) {
_bgi_plot_raw(x0, y0 - 1, color);
_bgi_plot_raw(x0, y0 + 1, color);
} else {
_bgi_plot_raw(x0 - 1, y0, color);
_bgi_plot_raw(x0 + 1, y0, color);
}
}
}
bit++;
if (x0 == x1 && y0 == y1) break;
e2 = 2 * err;
if (e2 > -dy) { err -= dy; x0 += sx; }
if (e2 < dx) { err += dx; y0 += sy; }
}
_bgi_end();
}
-8
View File
@@ -1,8 +0,0 @@
/*
* _bgi_text_state — направление и масштаб текста BGI.
* initgraph ставит HORIZ_DIR / size 1. Без инициализации.
*/
#include "_bgi.h"
uint8_t _bgi_text_dir;
uint8_t _bgi_text_size;
-43
View File
@@ -1,43 +0,0 @@
/*
* _bgi_trig — целочисленная тригонометрия для дуг/эллипсов BGI.
*
* Угол в градусах (Turbo-C); sin/cos масштабированы на 128 (Q7):
* _bgi_sin128(90) = 128. Масштаб 128 (а не 256) выбран так, чтобы
* произведение радиус×значение помещалось в 16-бит int для любого
* экранного радиуса (255×128 = 32640 < 32767) — и НЕ приходилось
* трогать 32-битную арифметику, которую SDCC/Z80 собирает криво
* (см. memory/avoid_32bit_arith_z80; ловили эллипс-в-прямоугольник).
*/
#include "_bgi.h"
/* sin(0°..90°) × 128. */
static const int _bgi_sintab[91] = {
0, 2, 4, 7, 9, 11, 13, 16, 18, 20,
22, 24, 27, 29, 31, 33, 35, 37, 40, 42,
44, 46, 48, 50, 52, 54, 56, 58, 60, 62,
64, 66, 68, 70, 72, 73, 75, 77, 79, 81,
82, 84, 86, 87, 89, 91, 92, 94, 95, 97,
98, 99, 101, 102, 104, 105, 106, 107, 109, 110,
111, 112, 113, 114, 115, 116, 117, 118, 119, 119,
120, 121, 122, 122, 123, 124, 124, 125, 125, 126,
126, 126, 127, 127, 127, 128, 128, 128, 128, 128,
128,
};
/* sin(угол°) × 128, угол приводится к 0..359. */
int _bgi_sin128(int deg)
{
deg %= 360;
if (deg < 0) deg += 360;
if (deg <= 90) return _bgi_sintab[deg];
if (deg <= 180) return _bgi_sintab[180 - deg];
if (deg <= 270) return -_bgi_sintab[deg - 180];
return -_bgi_sintab[360 - deg];
}
/* cos(угол°) × 128 = sin(угол+90). */
int _bgi_cos128(int deg)
{
return _bgi_sin128(deg + 90);
}
-7
View File
@@ -1,7 +0,0 @@
/* arc — дуга окружности радиуса r от stangle до endangle (градусы). */
#include "_bgi.h"
void arc(int x, int y, int stangle, int endangle, int radius)
{
_bgi_ellipse_arc(x, y, stangle, endangle, radius, radius, _bgi_fg);
}
-18
View File
@@ -1,18 +0,0 @@
/*
* bar — залитый прямоугольник (left,top)-(right,bottom) включительно
* ТЕКУЩИМ стилем заливки (setfillstyle), без рамки — как в Turbo-C.
*/
#include "_bgi.h"
void bar(int left, int top, int right, int bottom)
{
int y, t;
if (right < left) { t = left; left = right; right = t; }
if (bottom < top) { t = top; top = bottom; bottom = t; }
_bgi_begin();
for (y = top; y <= bottom; y++)
_bgi_fill_span(left, right, y);
_bgi_end();
}
-27
View File
@@ -1,27 +0,0 @@
/*
* bar3d — трёхмерный брусок: передняя грань залита текущим стилем
* заливки, рёбра — текущим цветом рисования. depth — глубина (вправо-
* вверх), topflag != 0 рисует верхнюю грань.
*/
#include "_bgi.h"
void bar3d(int left, int top, int right, int bottom, int depth, int topflag)
{
int t;
if (right < left) { t = left; left = right; right = t; }
if (bottom < top) { t = top; top = bottom; bottom = t; }
bar(left, top, right, bottom); /* передняя грань — заливка */
rectangle(left, top, right, bottom); /* рамка переда — тек. цвет */
if (depth != 0) {
line(right, top, right + depth, top - depth);
line(right, bottom, right + depth, bottom - depth);
line(right + depth, top - depth, right + depth, bottom - depth);
if (topflag) {
line(left, top, left + depth, top - depth);
line(left + depth, top - depth, right + depth, top - depth);
}
}
}
-40
View File
@@ -1,40 +0,0 @@
/*
* 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
@@ -1,9 +0,0 @@
/* cleardevice — очистить экран фоновым цветом, вернуть CP в (0,0). */
#include "_bgi.h"
void cleardevice(void)
{
_bgi_clearall(_bgi_bg);
_bgi_cx = 0;
_bgi_cy = 0;
}
-7
View File
@@ -1,7 +0,0 @@
/* closegraph — вернуть текстовый режим, действовавший до initgraph. */
#include "_bgi.h"
void closegraph(void)
{
_bgi_mode_done(_bgi_prevmode);
}
-17
View File
@@ -1,17 +0,0 @@
/*
* drawpoly — ломаная по numpoints точкам (массив x0,y0,x1,y1,…),
* текущим цветом. Как в Turbo-C, контур НЕ замыкается автоматически:
* чтобы получить замкнутый полигон, повторите первую точку последней.
*/
#include "_bgi.h"
void drawpoly(int numpoints, const int *polypoints)
{
int i;
if (numpoints < 2) return;
for (i = 0; i < numpoints - 1; i++) {
_bgi_styled_line(polypoints[2 * i], polypoints[2 * i + 1],
polypoints[2 * i + 2], polypoints[2 * i + 3],
_bgi_fg);
}
}
-8
View File
@@ -1,8 +0,0 @@
/* _bgi_begin (drv256) — открыть W3-сессию для пакетного рисования. */
#include "../_bgi.h"
#include "../../gfx/_gfx.h"
void _bgi_begin(void)
{
_gfx_w3_video_begin();
}
-8
View File
@@ -1,8 +0,0 @@
/* _bgi_clearall (drv256) — залить весь экран цветом. */
#include "../_bgi.h"
#include <gfx.h>
void _bgi_clearall(uint8_t color)
{
gfx_clear256(color);
}
-8
View File
@@ -1,8 +0,0 @@
/* _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
@@ -1,8 +0,0 @@
/* _bgi_end (drv256) — закрыть W3-сессию пакетного рисования. */
#include "../_bgi.h"
#include "../../gfx/_gfx.h"
void _bgi_end(void)
{
_gfx_w3_video_end();
}
-8
View File
@@ -1,8 +0,0 @@
/* _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);
}
-17
View File
@@ -1,17 +0,0 @@
/*
* _bgi_font_rows (drv256) — 8 байт строк глифа символа c.
* Шрифт системный, interleaved: offset = row*256 + char (MSB-first),
* см. memory/sprinter_font_format. Грузится лениво (_gfx_font_ensure).
*/
#include "../_bgi.h"
#include "../../gfx/_gfx.h"
void _bgi_font_rows(unsigned char c, uint8_t *rows8)
{
const uint8_t *f;
int r;
_gfx_font_ensure();
f = _gfx_font_ptr;
for (r = 0; r < 8; r++)
rows8[r] = f[r * 256 + c];
}
-8
View File
@@ -1,8 +0,0 @@
/* _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);
}
-11
View File
@@ -1,11 +0,0 @@
/*
* _bgi_hspan_raw (drv256) — горизонтальный отрезок без своей W3-скобки
* (вызывать между _bgi_begin/_bgi_end). Клип делает сам raw-примитив.
*/
#include "../_bgi.h"
#include "../../gfx/_gfx.h"
void _bgi_hspan_raw(int x, int y, int len, uint8_t color)
{
_gfx_hline256_raw(x, y, len, color);
}
-8
View File
@@ -1,8 +0,0 @@
/* _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
@@ -1,8 +0,0 @@
/* _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
@@ -1,40 +0,0 @@
/*
* _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
@@ -1,8 +0,0 @@
/* _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
@@ -1,11 +0,0 @@
/*
* _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
@@ -1,8 +0,0 @@
/* _bgi_read (drv256) — цвет пикселя (x,y). */
#include "../_bgi.h"
#include <gfx.h>
unsigned _bgi_read(int x, int y)
{
return gfx_getpixel256(x, y);
}
-11
View File
@@ -1,11 +0,0 @@
/*
* _bgi_read_raw (drv256) — чтение пикселя без своей W3-скобки
* (между _bgi_begin/_bgi_end). Для getimage/putimage.
*/
#include "../_bgi.h"
#include "../../gfx/_gfx.h"
unsigned _bgi_read_raw(int x, int y)
{
return _gfx_getpixel256_raw(x, y);
}
-11
View File
@@ -1,11 +0,0 @@
/*
* ellipse — контур эллипса/эллиптической дуги с полуосями xradius,
* yradius от stangle до endangle. Полный эллипс: stangle=0,endangle=360.
*/
#include "_bgi.h"
void ellipse(int x, int y, int stangle, int endangle,
int xradius, int yradius)
{
_bgi_ellipse_arc(x, y, stangle, endangle, xradius, yradius, _bgi_fg);
}
-31
View File
@@ -1,31 +0,0 @@
/*
* fillellipse — залитый эллипс с полуосями rx,ry, центр (cx,cy).
* Нутро — текущим стилем заливки, контур — текущим цветом.
*
* По строкам: полуширина w = rx*sqrt(ry²-dy²)/ry (целочисленно через
* _bgi_isqrt). ry²,dy² ≤ ~180² < 32767, rx*s ≤ rx*ry < 32767 — без
* 32-бит (см. memory/avoid_32bit_arith_z80).
*/
#include "_bgi.h"
void fillellipse(int cx, int cy, int rx, int ry)
{
int dy, s, w;
if (rx < 0 || ry < 0) return;
_bgi_begin();
if (ry == 0) {
_bgi_fill_span(cx - rx, cx + rx, cy);
} else {
for (dy = -ry; dy <= ry; dy++) {
s = _bgi_isqrt(ry * ry - dy * dy); /* 0..ry */
w = (rx * s) / ry;
_bgi_fill_span(cx - w, cx + w, cy + dy);
}
}
_bgi_end();
/* Контур текущим цветом. */
ellipse(cx, cy, 0, 360, rx, ry);
}
-54
View File
@@ -1,54 +0,0 @@
/*
* fillpoly — залитый многоугольник по numpoints точкам {x0,y0,…}.
* Нутро — текущим стилем заливки, контур — текущим цветом. Полигон
* авто-замыкается (последняя точка соединяется с первой).
*
* Заливка: для каждой строки берём крайние X контура (min/max) и
* заполняем между ними — точно для выпуклых, для вогнутых = выпуклая
* оболочка по строкам (упрощение). Крайние X даёт брезенхэмовский
* проход рёбер (_bgi_poly_edge) — без 32-бит арифметики.
*/
#include "_bgi.h"
void fillpoly(int numpoints, const int *polypoints)
{
int i, j, y, ymin, ymax, xa, ya, xb, yb;
if (numpoints < 2) return;
ymin = 32767;
ymax = -32768;
for (i = 0; i < numpoints; i++) {
y = polypoints[2 * i + 1];
if (y < ymin) ymin = y;
if (y > ymax) ymax = y;
}
if (ymin < 0) ymin = 0;
if (ymax > GFX_HEIGHT - 1) ymax = GFX_HEIGHT - 1;
if (ymax < ymin) return;
for (y = ymin; y <= ymax; y++) {
_bgi_span_min[y] = 32767;
_bgi_span_max[y] = -32768;
}
for (i = 0; i < numpoints; i++) {
j = (i + 1) % numpoints;
_bgi_poly_edge(polypoints[2 * i], polypoints[2 * i + 1],
polypoints[2 * j], polypoints[2 * j + 1]);
}
_bgi_begin();
for (y = ymin; y <= ymax; y++)
if (_bgi_span_max[y] >= _bgi_span_min[y])
_bgi_fill_span(_bgi_span_min[y], _bgi_span_max[y], y);
_bgi_end();
/* Контур текущим цветом (замкнутый). */
for (i = 0; i < numpoints; i++) {
j = (i + 1) % numpoints;
xa = polypoints[2 * i]; ya = polypoints[2 * i + 1];
xb = polypoints[2 * j]; yb = polypoints[2 * j + 1];
_bgi_lineseg(xa, ya, xb, yb, _bgi_fg);
}
}
-70
View File
@@ -1,70 +0,0 @@
/*
* floodfill — заливка связной области с (x,y) до границы цвета border.
*
* Скан-строчный (span) flood: заполняем горизонтальный отрезок текущим
* цветом заливки, затем ищем новые «затравки» в строках сверху/снизу.
* Стоп-условие пикселя: вне экрана / цвет == border / цвет уже == цвет
* заливки (иначе не сойдётся). Паттерн в floodfill не применяется —
* заливаем сплошным _bgi_fill_color (упрощение).
*
* Чтение/запись — self-bracket (_bgi_read/_bgi_hspan): чуть медленнее,
* зато не держим DI на всю (возможно большую) заливку.
*
* Стек затравок ограничен FF_STACK; при переполнении лишние затравки
* отбрасываются (заливка может остаться неполной на очень «рваных»
* фигурах) — компромисс по памяти.
*/
#include "_bgi.h"
#define FF_STACK 300
static int ff_sx[FF_STACK];
static int ff_sy[FF_STACK];
static int ff_sp;
/* 1 = пиксель нельзя заливать (стоп): вне экрана / border / уже залит. */
static int ff_stop(int x, int y, int border)
{
int c;
if (x < 0 || x > _bgi_maxx || y < 0 || y > _bgi_maxy) return 1;
c = _bgi_read(x, y);
return (c == border || c == _bgi_fill_color);
}
static void ff_push(int x, int y)
{
if (ff_sp < FF_STACK) { ff_sx[ff_sp] = x; ff_sy[ff_sp] = y; ff_sp++; }
}
void floodfill(int x, int y, int border)
{
int xl, xr, xi, yy, d;
uint8_t fc = _bgi_fill_color;
if (ff_stop(x, y, border)) return;
ff_sp = 0;
ff_push(x, y);
while (ff_sp > 0) {
ff_sp--;
x = ff_sx[ff_sp];
y = ff_sy[ff_sp];
if (ff_stop(x, y, border)) continue;
for (xl = x; !ff_stop(xl - 1, y, border); ) xl--;
for (xr = x; !ff_stop(xr + 1, y, border); ) xr++;
_bgi_hspan(xl, y, xr - xl + 1, fc);
for (d = -1; d <= 1; d += 2) {
yy = y + d;
xi = xl;
while (xi <= xr) {
while (xi <= xr && ff_stop(xi, yy, border)) xi++;
if (xi > xr) break;
while (xi <= xr && !ff_stop(xi, yy, border)) xi++;
ff_push(xi - 1, yy); /* правый край найденного пробега */
}
}
}
}
-7
View File
@@ -1,7 +0,0 @@
/* getbkcolor — текущий фоновый цвет. */
#include "_bgi.h"
int getbkcolor(void)
{
return _bgi_bg;
}
-7
View File
@@ -1,7 +0,0 @@
/* getcolor — текущий цвет рисования. */
#include "_bgi.h"
int getcolor(void)
{
return _bgi_fg;
}
-9
View File
@@ -1,9 +0,0 @@
/* getfillsettings — прочитать текущий паттерн и цвет заливки. */
#include "_bgi.h"
void getfillsettings(struct fillsettingstype *fillinfo)
{
if (!fillinfo) return;
fillinfo->pattern = _bgi_fill_pattern;
fillinfo->color = _bgi_fill_color;
}
-27
View File
@@ -1,27 +0,0 @@
/*
* getimage — сохранить прямоугольник экрана в буфер bitmap.
* Формат: uint16 width, uint16 height, затем w*h байт (по строкам).
* Чтение raw в одной W3-скобке.
*/
#include "_bgi.h"
void getimage(int left, int top, int right, int bottom, void *bitmap)
{
uint8_t *p = (uint8_t *)bitmap;
int w = right - left + 1;
int h = bottom - top + 1;
int x, y;
if (w <= 0 || h <= 0) return;
*p++ = (uint8_t)w;
*p++ = (uint8_t)(w >> 8);
*p++ = (uint8_t)h;
*p++ = (uint8_t)(h >> 8);
_bgi_begin();
for (y = 0; y < h; y++)
for (x = 0; x < w; x++)
*p++ = (uint8_t)_bgi_read_raw(left + x, top + y);
_bgi_end();
}
-10
View File
@@ -1,10 +0,0 @@
/* getlinesettings — прочитать текущий стиль/маску/толщину линий. */
#include "_bgi.h"
void getlinesettings(struct linesettingstype *lineinfo)
{
if (!lineinfo) return;
lineinfo->linestyle = _bgi_line_style;
lineinfo->upattern = _bgi_line_pattern;
lineinfo->thickness = _bgi_line_thick;
}
-7
View File
@@ -1,7 +0,0 @@
/* getmaxcolor — максимальный индекс цвета (255 в 256-цветном режиме). */
#include "_bgi.h"
int getmaxcolor(void)
{
return _bgi_maxcolor;
}
-7
View File
@@ -1,7 +0,0 @@
/* getmaxx — максимальная координата X (ширина-1). */
#include "_bgi.h"
int getmaxx(void)
{
return _bgi_maxx;
}
-7
View File
@@ -1,7 +0,0 @@
/* getmaxy — максимальная координата Y (высота-1). */
#include "_bgi.h"
int getmaxy(void)
{
return _bgi_maxy;
}
-7
View File
@@ -1,7 +0,0 @@
/* getpixel — цвет пикселя (x,y). */
#include "_bgi.h"
unsigned getpixel(int x, int y)
{
return _bgi_read(x, y);
}
-12
View File
@@ -1,12 +0,0 @@
/* gettextsettings — текущий стиль текста (justify не реализован → 0). */
#include "_bgi.h"
void gettextsettings(struct textsettingstype *textinfo)
{
if (!textinfo) return;
textinfo->font = DEFAULT_FONT;
textinfo->direction = _bgi_text_dir;
textinfo->charsize = _bgi_text_size;
textinfo->horiz = 0;
textinfo->vert = 0;
}
-7
View File
@@ -1,7 +0,0 @@
/* getx — текущая позиция X. */
#include "_bgi.h"
int getx(void)
{
return _bgi_cx;
}
-7
View File
@@ -1,7 +0,0 @@
/* gety — текущая позиция Y. */
#include "_bgi.h"
int gety(void)
{
return _bgi_cy;
}
-9
View File
@@ -1,9 +0,0 @@
/* graphresult — код последней ошибки; вызов сбрасывает его в grOk. */
#include "_bgi.h"
int graphresult(void)
{
int r = _bgi_result;
_bgi_result = grOk;
return r;
}
-17
View File
@@ -1,17 +0,0 @@
/*
* imagesize — размер буфера под образ (left,top)-(right,bottom) в байтах:
* 4 (заголовок w,h) + w*h (по байту на пиксель в режиме 256).
*
* Результат 16-бит unsigned (как в Turbo-C): образы ≥64 КБ не влезают.
* Произведение w*h считаем в unsigned — для валидных (<64 КБ) образов
* умещается точно; без 32-бит.
*/
#include "_bgi.h"
unsigned imagesize(int left, int top, int right, int bottom)
{
int w = right - left + 1;
int h = bottom - top + 1;
if (w <= 0 || h <= 0) return 0;
return (unsigned)4 + (unsigned)w * (unsigned)h;
}
-25
View File
@@ -1,25 +0,0 @@
/*
* 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;
_bgi_fill_pattern = SOLID_FILL;
_bgi_fill_color = WHITE;
_bgi_line_style = SOLID_LINE;
_bgi_line_thick = NORM_WIDTH;
_bgi_line_pattern = 0xFFFF;
_bgi_text_dir = HORIZ_DIR;
_bgi_text_size = 1;
}
-7
View File
@@ -1,7 +0,0 @@
/* line — отрезок (x1,y1)->(x2,y2) текущим цветом; CP не меняется. */
#include "_bgi.h"
void line(int x1, int y1, int x2, int y2)
{
_bgi_styled_line(x1, y1, x2, y2, _bgi_fg);
}
-11
View File
@@ -1,11 +0,0 @@
/* 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_styled_line(_bgi_cx, _bgi_cy, nx, ny, _bgi_fg);
_bgi_cx = nx;
_bgi_cy = ny;
}
-9
View File
@@ -1,9 +0,0 @@
/* lineto — линия от CP до (x,y) текущим цветом; CP := (x,y). */
#include "_bgi.h"
void lineto(int x, int y)
{
_bgi_styled_line(_bgi_cx, _bgi_cy, x, y, _bgi_fg);
_bgi_cx = x;
_bgi_cy = y;
}
-8
View File
@@ -1,8 +0,0 @@
/* moverel — сдвинуть текущую позицию относительно. */
#include "_bgi.h"
void moverel(int dx, int dy)
{
_bgi_cx += dx;
_bgi_cy += dy;
}
-8
View File
@@ -1,8 +0,0 @@
/* moveto — задать текущую позицию (CP). */
#include "_bgi.h"
void moveto(int x, int y)
{
_bgi_cx = x;
_bgi_cy = y;
}
-12
View File
@@ -1,12 +0,0 @@
/*
* outtext — строка в текущей позиции (CP) текущим стилем текста; CP
* сдвигается на textwidth (гориз.) или вверх на textheight (верт.).
*/
#include "_bgi.h"
void outtext(const char *text)
{
_bgi_draw_scaled(_bgi_cx, _bgi_cy, text);
if (_bgi_text_dir == VERT_DIR) _bgi_cy -= textheight(text);
else _bgi_cx += textwidth(text);
}
-8
View File
@@ -1,8 +0,0 @@
/* outtextxy — строка в (x,y) текущим стилем текста (масштаб/направление,
* прозрачный фон); CP не меняет. */
#include "_bgi.h"
void outtextxy(int x, int y, const char *text)
{
_bgi_draw_scaled(x, y, text);
}
-7
View File
@@ -1,7 +0,0 @@
/* pieslice — залитый сектор круга радиуса r (частный случай sector). */
#include "_bgi.h"
void pieslice(int x, int y, int stangle, int endangle, int radius)
{
sector(x, y, stangle, endangle, radius, radius);
}
-37
View File
@@ -1,37 +0,0 @@
/*
* putimage — вывести образ bitmap левым-верхним углом в (left,top)
* операцией op. Заголовок буфера: uint16 width, uint16 height.
*
* COPY — пишем src; XOR/OR/AND — комбинируем с экраном (нужно raw-
* чтение); NOT — пишем ~src. Всё в одной W3-скобке (raw-плот/чтение).
*/
#include "_bgi.h"
void putimage(int left, int top, const void *bitmap, int op)
{
const uint8_t *p = (const uint8_t *)bitmap;
int w, h, x, y;
uint8_t src, dst;
w = p[0] | (p[1] << 8);
h = p[2] | (p[3] << 8);
p += 4;
if (w <= 0 || h <= 0) return;
_bgi_begin();
for (y = 0; y < h; y++) {
for (x = 0; x < w; x++) {
src = *p++;
switch (op) {
case XOR_PUT: dst = (uint8_t)_bgi_read_raw(left + x, top + y) ^ src; break;
case OR_PUT: dst = (uint8_t)_bgi_read_raw(left + x, top + y) | src; break;
case AND_PUT: dst = (uint8_t)_bgi_read_raw(left + x, top + y) & src; break;
case NOT_PUT: dst = (uint8_t)~src; break;
case COPY_PUT:
default: dst = src; break;
}
_bgi_plot_raw(left + x, top + y, dst);
}
}
_bgi_end();
}
-7
View File
@@ -1,7 +0,0 @@
/* 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
@@ -1,11 +0,0 @@
/* rectangle — контур прямоугольника по углам (left,top)-(right,bottom). */
#include "_bgi.h"
void rectangle(int left, int top, int right, int bottom)
{
uint8_t c = _bgi_fg;
_bgi_styled_line(left, top, right, top, c); /* верх */
_bgi_styled_line(right, top, right, bottom, c); /* право */
_bgi_styled_line(right, bottom, left, bottom, c); /* низ */
_bgi_styled_line(left, bottom, left, top, c); /* лево */
}
-49
View File
@@ -1,49 +0,0 @@
/*
* sector — залитый эллиптический сектор (центр cx,cy, полуоси xr,yr,
* от stangle до endangle). Нутро — стилем заливки, контур (дуга + два
* радиуса) — текущим цветом.
*
* Заливка: границу сектора (центр → дуга → центр) прогоняем по
* _bgi_poly_edge, получая min/max X по строкам, и заливаем между ними
* (как fillpoly). Для секторов ≤180° точно; >180° возможен перелив в
* «выемку» (min/max по строке не различает 4 пересечения).
*/
#include "_bgi.h"
/* точка на эллипсе под углом a (Q7-тригонометрия). */
static int se_x(int cx, int xr, int a) { return cx + ((xr * _bgi_cos128(a)) >> 7); }
static int se_y(int cy, int yr, int a) { return cy - ((yr * _bgi_sin128(a)) >> 7); }
void sector(int cx, int cy, int stangle, int endangle, int xr, int yr)
{
int a, y, px, py, x, py2;
if (endangle < stangle) endangle += 360;
for (y = 0; y <= _bgi_maxy; y++) {
_bgi_span_min[y] = 32767;
_bgi_span_max[y] = -32768;
}
/* Граница: из центра в первую точку дуги, по дуге, обратно в центр. */
px = cx; py = cy;
for (a = stangle; a <= endangle; a++) {
x = se_x(cx, xr, a);
py2 = se_y(cy, yr, a);
_bgi_poly_edge(px, py, x, py2);
px = x; py = py2;
}
_bgi_poly_edge(px, py, cx, cy);
_bgi_begin();
for (y = 0; y <= _bgi_maxy; y++)
if (_bgi_span_max[y] >= _bgi_span_min[y])
_bgi_fill_span(_bgi_span_min[y], _bgi_span_max[y], y);
_bgi_end();
/* Контур: дуга + два радиуса. */
_bgi_ellipse_arc(cx, cy, stangle, endangle, xr, yr, _bgi_fg);
_bgi_lineseg(cx, cy, se_x(cx, xr, stangle), se_y(cy, yr, stangle), _bgi_fg);
_bgi_lineseg(cx, cy, se_x(cx, xr, endangle), se_y(cy, yr, endangle), _bgi_fg);
}
-7
View File
@@ -1,7 +0,0 @@
/* setbkcolor — задать фоновый цвет (cleardevice, фон текста). */
#include "_bgi.h"
void setbkcolor(int color)
{
_bgi_bg = (uint8_t)color;
}
-7
View File
@@ -1,7 +0,0 @@
/* setcolor — задать текущий цвет рисования. */
#include "_bgi.h"
void setcolor(int color)
{
_bgi_fg = (uint8_t)color;
}
-8
View File
@@ -1,8 +0,0 @@
/* setfillstyle — задать паттерн и цвет заливки (bar/fillpoly/…). */
#include "_bgi.h"
void setfillstyle(int pattern, int color)
{
_bgi_fill_pattern = (uint8_t)pattern;
_bgi_fill_color = (uint8_t)color;
}
-9
View File
@@ -1,9 +0,0 @@
/* setlinestyle — задать стиль, пользовательскую маску и толщину линий. */
#include "_bgi.h"
void setlinestyle(int linestyle, unsigned upattern, int thickness)
{
_bgi_line_style = (uint8_t)linestyle;
_bgi_line_pattern = (uint16_t)upattern;
_bgi_line_thick = (thickness >= THICK_WIDTH) ? THICK_WIDTH : NORM_WIDTH;
}
-12
View File
@@ -1,12 +0,0 @@
/*
* settextstyle — шрифт (поддержан только DEFAULT_FONT), направление и
* масштаб текста. font игнорируется (всегда растровый 8×8).
*/
#include "_bgi.h"
void settextstyle(int font, int direction, int charsize)
{
(void)font;
_bgi_text_dir = (direction == VERT_DIR) ? VERT_DIR : HORIZ_DIR;
_bgi_text_size = (charsize < 1) ? 1 : (charsize > 10 ? 10 : (uint8_t)charsize);
}
-9
View File
@@ -1,9 +0,0 @@
/* textheight — высота строки как она будет нарисована (масштаб/направление). */
#include "_bgi.h"
#include <string.h>
int textheight(const char *text)
{
if (_bgi_text_dir == VERT_DIR) return (int)strlen(text) * 8 * _bgi_text_size;
return 8 * _bgi_text_size;
}
-11
View File
@@ -1,11 +0,0 @@
/* textwidth — ширина строки как она будет нарисована (с учётом
* масштаба и направления). */
#include "_bgi.h"
#include <string.h>
int textwidth(const char *text)
{
int n = (int)strlen(text);
if (_bgi_text_dir == VERT_DIR) return 8 * _bgi_text_size;
return n * 8 * _bgi_text_size;
}
-110
View File
@@ -1,110 +0,0 @@
/*
* _gfx.h — внутренности графической подсистемы (НЕ публичный заголовок).
*
* Общее состояние лежит в data-модулях (_gfx_state.c, _gfx_w3_state.c,
* _gfx_acc256.c, _gfx_g16_state.c, _gfx_font_state.c), helpers — в
* отдельных internal-модулях; см. docs/libc-split-asm-cases.md.
*/
#ifndef _GFX_INTERNAL_H
#define _GFX_INTERNAL_H
#include <gfx.h>
/* ---- Общее состояние (_gfx_state.c) ------------------------------ */
/* Кэш SELPAGE: видимая и рисуемая страницы (0/1). */
extern uint8_t _gfx_visible_page;
extern uint8_t _gfx_draw_page;
/* Байт страницы W3 (0x50..0x5F) — какой видеобанк мапится при записи. */
extern uint8_t _gfx_bank;
/* CPU-адрес колонки 0 текущей draw-страницы: 0xC000 (page 0) или
* 0xC140 (page 1); каждый примитив использует его вместо константы. */
extern uint16_t _gfx_addr_base;
/* ---- Низкоуровневый видеорежим (video/videomode_raw.c) ----------- */
uint8_t _videomode_raw_get(void);
int _videomode_raw_set(uint8_t mode);
/* ---- W3-скобки (_gfx_w3_video_begin/end.c, _gfx_w3_state.c) ------ *
* begin: DI + сохранить текущий W3 и замапить _gfx_bank;
* end: восстановить W3 + EI. Не реентерабельно (сейв в статике). */
extern uint8_t _gfx_saved_w3;
void _gfx_w3_video_begin(void);
void _gfx_w3_video_end(void);
/* ---- Raw-примитивы 256 цветов (mode 0x81) ------------------------- *
* "Raw" = W3-naive: вызывающий обязан обернуть последовательность в
* одну пару _gfx_w3_video_begin/_gfx_w3_video_end. */
void _gfx_putpixel256_raw(int x, int y, uint8_t color);
uint8_t _gfx_getpixel256_raw(int x, int y);
void _gfx_hline256_raw (int x, int y, int len, uint8_t color);
void _gfx_vline256_raw (int x, int y, int len, uint8_t color);
void _gfx_clear256_raw (uint8_t color);
/* Скретч акселератора (_gfx_acc256.c) — общий для hline/vline/clear;
* однопоточно, IRQ выключены между begin/end. */
extern uint8_t _gfx_acc_color;
extern uint8_t _gfx_acc_y;
extern uint8_t _gfx_acc_len; /* 0 = 256 — конвенция акселератора */
extern uint16_t _gfx_acc_addr;
/* Горизонтальный Fill-burst: _gfx_acc_len байт с _gfx_acc_addr на
* строке _gfx_acc_y (W3 замаплен, DI активен). */
void _gfx_hfill256_chunk(void);
/* ---- Raw-примитивы 16 цветов (mode 0x82) -------------------------- */
void _gfx_putpixel16_raw(int x, int y, uint8_t color);
void _gfx_hline16_raw (int x, int y, int len, uint8_t color);
void _gfx_vline16_raw (int x, int y, int len, uint8_t color);
void _gfx_clear16_raw (uint8_t color);
/* Скретч 16-цветных примитивов (_gfx_g16_state.c). */
extern uint8_t _gfx_g16_y;
extern uint8_t _gfx_g16_byte; /* nibble | (nibble<<4) */
extern uint8_t _gfx_g16_nibble; /* цвет в нужной половине байта */
extern uint8_t _gfx_g16_mask; /* маска, сохраняющая ДРУГУЮ половину */
extern uint8_t _gfx_g16_len; /* размер accel-блока (0 = 256) */
extern uint16_t _gfx_g16_addr;
/* Горизонтальный Fill-burst байтами _gfx_g16_byte (_gfx_hfill16.c). */
void _gfx_hfill16_chunk(void);
/* RMW одного нибла по (_gfx_g16_addr, _gfx_g16_y) (_gfx_rmw16.c). */
void _gfx_rmw16_pixel(void);
/* ---- Шрифт (_gfx_font_state.c, _gfx_font_ensure.c) ---------------- *
* Формат: 256 глифов × 8 рядов, INTERLEAVED — offset = row*256 + char;
* биты MSB-first. См. memory/sprinter_font_format. */
#define _GFX_FONT_BYTES 2048
extern uint8_t _gfx_font_buf[_GFX_FONT_BYTES];
extern const uint8_t *_gfx_font_ptr;
extern uint8_t _gfx_font_loaded;
/* Ленивая инициализация: грузит системный шрифт при первом использовании. */
void _gfx_font_ensure(void);
/* ---- Текст 16 цветов: общая машинерия ----------------------------- *
* Данные в _gfx_t16_state.c, рендер ряда в _gfx_render_row16.c,
* pair-таблица в _gfx_t16_pair_build.c. */
extern uint8_t _gfx_t16_y;
extern uint8_t _gfx_t16_row; /* битовый паттерн ряда глифа */
extern uint16_t _gfx_t16_addr; /* VRAM-адрес ряда */
extern uint8_t _gfx_t16_pair[4]; /* LEFT*2+RIGHT → (LEFT<<4)|RIGHT */
/* Отрисовать один 8-пиксельный ряд по pair-таблице (4 записи байт). */
void _gfx_render_row16(void);
/* Заполнить pair-таблицу для пары цветов fg/bg. */
void _gfx_t16_pair_build(uint8_t fg, uint8_t bg);
#endif
-12
View File
@@ -1,12 +0,0 @@
/*
* _gfx_acc256 — скретч акселератора для 256-цветных raw-примитивов
* (модуль только с данными). Общий для hline/vline/clear; однопоточно —
* GFX работает с выключенными прерываниями между W3-begin/end.
*/
#include "_gfx.h"
uint8_t _gfx_acc_color;
uint8_t _gfx_acc_y;
uint8_t _gfx_acc_len; /* 0 = 256 — конвенция акселератора */
uint16_t _gfx_acc_addr;
-20
View File
@@ -1,20 +0,0 @@
/*
* _gfx_clear16_raw — залить всё поле 640×256 цветом color, mode 0x82.
* Построчный hfill: 256 строк × 2 burst'а (256 + 64 байта). W3-naive.
*/
#include "_gfx.h"
void _gfx_clear16_raw(uint8_t color)
{
_gfx_g16_byte = (uint8_t)((color & 0x0F) | ((color & 0x0F) << 4));
for (int y = 0; y < GFX_HEIGHT_16; y++) {
_gfx_g16_y = (uint8_t)y;
_gfx_g16_addr = _gfx_addr_base;
_gfx_g16_len = 0;
_gfx_hfill16_chunk();
_gfx_g16_addr = (uint16_t)(_gfx_addr_base + 0x100);
_gfx_g16_len = 64;
_gfx_hfill16_chunk();
}
}
-21
View File
@@ -1,21 +0,0 @@
/*
* _gfx_clear256_raw — залить всё поле 320×256 цветом color, mode 0x81.
* Построчный hfill: 256 строк × 2 burst'а (256 + 64 байта) = 512.
* W3-naive: вызывающий обрамляет begin/end.
*/
#include "_gfx.h"
void _gfx_clear256_raw(uint8_t color)
{
_gfx_acc_color = color;
for (int y = 0; y < GFX_HEIGHT; y++) {
_gfx_acc_y = (uint8_t)y;
_gfx_acc_addr = _gfx_addr_base; /* burst 256 байт */
_gfx_acc_len = 0;
_gfx_hfill256_chunk();
_gfx_acc_addr = (uint16_t)(_gfx_addr_base + 256); /* burst 64 байта */
_gfx_acc_len = 64;
_gfx_hfill256_chunk();
}
}
-12
View File
@@ -1,12 +0,0 @@
/*
* _gfx_font_ensure — ленивая инициализация шрифта: текстовые рендеры
* зовут её при каждом входе, системный шрифт грузится при первом
* использовании (чисто графическая программа BIOS-вызов не платит).
*/
#include "_gfx.h"
void _gfx_font_ensure(void)
{
if (!_gfx_font_loaded) gfx_load_default_font();
}
-16
View File
@@ -1,16 +0,0 @@
/*
* _gfx_font_state — буфер шрифта, указатель и флаг загрузки (данные).
*
* Формат (ZX-Spectrum-совместимый): 256 глифов × 8 рядов × 1 байт = 2 КБ,
* INTERLEAVED — offset = row*256 + char (ряд 0 всех глифов занимает
* 0x000..0x0FF и т.д.), биты MSB-first (бит 7 = левый пиксель).
*
* _gfx_font_ptr всегда указывает на валидные данные после
* _gfx_font_ensure(); программы могут подменить шрифт gfx_set_font().
*/
#include "_gfx.h"
uint8_t _gfx_font_buf[_GFX_FONT_BYTES];
const uint8_t *_gfx_font_ptr = _gfx_font_buf;
uint8_t _gfx_font_loaded;
-14
View File
@@ -1,14 +0,0 @@
/*
* _gfx_g16_state — скретч 16-цветных raw-примитивов (только данные).
* Общий для putpixel/hline/vline/clear; однопоточно, IRQ выключены
* между W3-begin/end.
*/
#include "_gfx.h"
uint8_t _gfx_g16_y;
uint8_t _gfx_g16_byte; /* nibble | (nibble<<4) — цвет в обе половины */
uint8_t _gfx_g16_nibble; /* цвет в нужной половине (low или high) */
uint8_t _gfx_g16_mask; /* маска, сохраняющая ДРУГУЮ половину */
uint8_t _gfx_g16_len; /* размер accel-блока (0 = 256) */
uint16_t _gfx_g16_addr;
-27
View File
@@ -1,27 +0,0 @@
/*
* _gfx_getpixel256_raw — прочитать пиксель (x,y) БЕЗ своей W3-скобки
* (вызывающий уже сделал _gfx_w3_video_begin). Для блиттинга image:
* getimage/putimage читают тысячи пикселей в одной скобке.
* За пределами экрана возвращает 0.
*/
#include "_gfx.h"
static uint8_t _gfx_gpr_y;
static uint16_t _gfx_gpr_addr;
static uint8_t _gfx_gpr_color;
uint8_t _gfx_getpixel256_raw(int x, int y)
{
if ((unsigned)x >= GFX_WIDTH || (unsigned)y >= GFX_HEIGHT) return 0;
_gfx_gpr_y = (uint8_t)y;
_gfx_gpr_addr = (uint16_t)(_gfx_addr_base + (unsigned)x);
__asm
ld a, (__gfx_gpr_y)
out (#0x89), a
ld hl, (__gfx_gpr_addr)
ld a, (hl)
ld (__gfx_gpr_color), a
__endasm;
return _gfx_gpr_color;
}
-38
View File
@@ -1,38 +0,0 @@
/*
* _gfx_hfill16_chunk — горизонтальный Fill-burst акселератора для
* mode 0x82: _gfx_g16_len байт значения _gfx_g16_byte с _gfx_g16_addr
* на строке _gfx_g16_y. Один burst красит до 256 байт = 512 пикселей.
* Pre: W3 замаплен, DI активен.
*
* SMC только у байта длины. Цвет предзагружается в C и уходит через
* `ld a, c` (0x79) — вставить ещё один `ld a,#n` между LD C,C и
* стреляющим LD (HL),A нельзя: FSM акселератора перечитает immediate
* как новый размер блока.
*/
#include "_gfx.h"
void _gfx_hfill16_chunk(void) __naked
{
__asm
ld a, (__gfx_g16_len)
ld (_hfill16_len_imm), a
ld a, (__gfx_g16_byte)
ld c, a
ld a, (__gfx_g16_y)
out (#0x89), a
ld hl, (__gfx_g16_addr)
ld d, d ; 0x52 режим размера блока
ld a, #0 ; 0x3E nn длина (патчится)
_hfill16_len_imm = . - 1
ld c, c ; 0x49 горизонтальный Fill
ld a, c ; 0x79 A = байт цвета
ld (hl), a ; выстрел
ld b, b ; 0x40 выключить
ret
__endasm;
}
-45
View File
@@ -1,45 +0,0 @@
/*
* _gfx_hfill256_chunk — горизонтальный Fill-burst акселератора:
* _gfx_acc_len байт цвета _gfx_acc_color с адреса _gfx_acc_addr на
* строке _gfx_acc_y. Pre: W3 замаплен на видеобанк, DI активен.
*
* Опкоды акселератора (docs/converted/accel_r.txt):
* LD D,D (0x52) режим «задать размер блока»; СЛЕДУЮЩИЙ байт — длина,
* ОБЯЗАН быть immediate-операндом LD A,n (0x3E)
* LD C,C (0x49) горизонтальный Fill (LD (HL),A заливает n байт)
* LD B,B (0x40) выключить акселератор
*
* Байт длины патчится SMC — HOME после загрузки DSS это RAM, патчить
* собственный .EXE безопасно. Вставлять другой `ld a,#n` между LD C,C
* и стреляющим LD (HL),A нельзя — FSM акселератора перечитает immediate
* как новый размер блока.
*/
#include "_gfx.h"
void _gfx_hfill256_chunk(void) __naked
{
__asm
;; Пропатчить immediate LD A,#n байтом длины.
ld a, (__gfx_acc_len)
ld (_hfill256_len_imm), a
ld a, (__gfx_acc_y)
out (#0x89), a ; Port_Y = y
;; Цвет в C и адрес в HL готовим ДО взведения акселератора.
ld a, (__gfx_acc_color)
ld c, a
ld hl, (__gfx_acc_addr)
;; --- ПОСЛЕДОВАТЕЛЬНОСТЬ АКСЕЛЕРАТОРА ---
ld d, d ; 0x52 режим размера блока
ld a, #0 ; 0x3E nn размер (nn патчится выше)
_hfill256_len_imm = . - 1
ld c, c ; 0x49 горизонтальный Fill
ld a, c ; 0x79 A = цвет (НЕ ещё один ld a,#n)
ld (hl), a ; выстрел; заливает _gfx_acc_len байт
ld b, b ; 0x40 выключить
ret
__endasm;
}
-50
View File
@@ -1,50 +0,0 @@
/*
* _gfx_hline16_raw — горизонтальная линия len пикселей от (x,y),
* mode 0x82: невыровненные крайние ниблы через RMW, середина —
* Fill-burst'ами акселератора по целым байтам. W3-naive.
*/
#include "_gfx.h"
void _gfx_hline16_raw(int x, int y, int len, uint8_t color)
{
if ((unsigned)y >= GFX_HEIGHT_16) return;
if (x < 0) { len += x; x = 0; }
if (x >= GFX_WIDTH_16) return;
if (x + len > GFX_WIDTH_16) len = GFX_WIDTH_16 - x;
if (len <= 0) return;
_gfx_g16_y = (uint8_t)y;
uint8_t cnib = color & 0x0F;
_gfx_g16_byte = (uint8_t)(cnib | (cnib << 4));
/* Ведущий невыровненный пиксель: x нечётный — ПРАВАЯ половина
* крайнего левого байта. */
if (x & 1) {
_gfx_g16_addr = (uint16_t)(_gfx_addr_base + ((unsigned)x >> 1));
_gfx_g16_nibble = cnib;
_gfx_g16_mask = 0xF0;
_gfx_rmw16_pixel();
x++;
len--;
if (len <= 0) return;
}
/* x чётный; len/2 целых байтов через accel hfill. */
int full = len >> 1;
_gfx_g16_addr = (uint16_t)(_gfx_addr_base + ((unsigned)x >> 1));
while (full > 0) {
int chunk = full > 256 ? 256 : full;
_gfx_g16_len = (chunk == 256) ? 0 : (uint8_t)chunk;
_gfx_hfill16_chunk();
full -= chunk;
_gfx_g16_addr += chunk;
}
/* Хвостовой пиксель нечётной длины: ЛЕВАЯ половина след. байта. */
if (len & 1) {
_gfx_g16_nibble = (uint8_t)(cnib << 4);
_gfx_g16_mask = 0x0F;
_gfx_rmw16_pixel();
}
}
-28
View File
@@ -1,28 +0,0 @@
/*
* _gfx_hline256_raw — горизонтальная линия len пикселей от (x,y),
* mode 0x81, через Fill-burst'ы акселератора (до 256 байт за выстрел).
* W3-naive: вызывающий обрамляет begin/end.
*/
#include "_gfx.h"
void _gfx_hline256_raw(int x, int y, int len, uint8_t color)
{
if ((unsigned)y >= GFX_HEIGHT) return;
if (x < 0) { len += x; x = 0; }
if (x >= GFX_WIDTH) return;
if (x + len > GFX_WIDTH) len = GFX_WIDTH - x;
if (len <= 0) return;
_gfx_acc_color = color;
_gfx_acc_y = (uint8_t)y;
_gfx_acc_addr = (uint16_t)(_gfx_addr_base + (unsigned)x);
while (len > 0) {
int chunk = len > 256 ? 256 : len;
_gfx_acc_len = (chunk == 256) ? 0 : (uint8_t)chunk;
_gfx_hfill256_chunk();
len -= chunk;
_gfx_acc_addr += chunk;
}
}
-22
View File
@@ -1,22 +0,0 @@
/*
* _gfx_putpixel16_raw — пиксель (x,y) цветом 0..15, mode 0x82.
* Байт по адресу _gfx_addr_base + x/2 держит два пикселя: high nibble =
* ЛЕВЫЙ (чётный x), low = ПРАВЫЙ (нечётный). W3-naive.
*/
#include "_gfx.h"
void _gfx_putpixel16_raw(int x, int y, uint8_t color)
{
if ((unsigned)x >= GFX_WIDTH_16 || (unsigned)y >= GFX_HEIGHT_16) return;
_gfx_g16_y = (uint8_t)y;
_gfx_g16_addr = (uint16_t)(_gfx_addr_base + ((unsigned)x >> 1));
if (x & 1) {
_gfx_g16_nibble = (uint8_t)(color & 0x0F);
_gfx_g16_mask = 0xF0;
} else {
_gfx_g16_nibble = (uint8_t)((color & 0x0F) << 4);
_gfx_g16_mask = 0x0F;
}
_gfx_rmw16_pixel();
}
-30
View File
@@ -1,30 +0,0 @@
/*
* _gfx_putpixel256_raw — пиксель (x,y) цветом color, mode 0x81.
*
* "Raw" = W3-naive: вызывающий уже сделал _gfx_w3_video_begin (W3 на
* видеобанке, DI). Адресация: CPU _gfx_addr_base + x, Port_Y (0x89) = y.
*
* Скретч отдельный от _gfx_acc_* — чтобы putpixel внутри цикла
* Брезенхэма не топтал внешнее состояние акселератора.
*/
#include "_gfx.h"
static uint8_t _gfx_pp_y;
static uint16_t _gfx_pp_addr;
static uint8_t _gfx_pp_color;
void _gfx_putpixel256_raw(int x, int y, uint8_t color)
{
if ((unsigned)x >= GFX_WIDTH || (unsigned)y >= GFX_HEIGHT) return;
_gfx_pp_y = (uint8_t)y;
_gfx_pp_addr = (uint16_t)(_gfx_addr_base + (unsigned)x);
_gfx_pp_color = color;
__asm
ld a, (__gfx_pp_y)
out (#0x89), a
ld hl, (__gfx_pp_addr)
ld a, (__gfx_pp_color)
ld (hl), a
__endasm;
}
-53
View File
@@ -1,53 +0,0 @@
/*
* _gfx_render_row16 — один 8-пиксельный ряд глифа в mode 0x82
* (640×256×16): 4 выходных байта, каждый пакует два соседних пикселя
* как (LEFT<<4) | RIGHT через прекомпилированную _gfx_t16_pair —
* 2-битный паттерн источника (LEFT*2 + RIGHT) сразу даёт выходной
* байт, без mask/OR на пиксель. Сам делает DI / W3 save+restore —
* вызывать можно из любого контекста без скобок.
*/
#include "_gfx.h"
void _gfx_render_row16(void) __naked
{
__asm
di
in a, (#0xE2)
push af
ld a, #0x50
out (#0xE2), a
ld a, (__gfx_t16_y)
out (#0x89), a
ld hl, (__gfx_t16_addr)
ld a, (__gfx_t16_row)
ld c, a ; C = сдвигаемые биты источника
ld b, #4 ; 4 выходных байта
rr16_loop:
;; Два верхних бита C в индекс A = LEFT*2 + RIGHT.
sla c ; CY = бит LEFT; C <<= 1
ld a, #0
adc a, a ; A = LEFT
sla c ; CY = бит RIGHT; C <<= 1
adc a, a ; A = LEFT*2 + RIGHT (0..3)
;; A = _gfx_t16_pair[A]; HL сохраняем через стек.
push hl
ld e, a
ld d, #0
ld hl, #__gfx_t16_pair
add hl, de
ld a, (hl)
pop hl
ld (hl), a
inc hl
djnz rr16_loop
pop af
out (#0xE2), a
ei
ret
__endasm;
}
-28
View File
@@ -1,28 +0,0 @@
/*
* _gfx_rmw16_pixel — RMW одного нибла по (_gfx_g16_addr, _gfx_g16_y):
* прочитать байт, стереть целевой нибл маской _gfx_g16_mask, вписать
* _gfx_g16_nibble. ~10 инструкций на пиксель — медленно, но байт
* покрывает два горизонтальных пикселя, иначе никак.
* Pre: W3 замаплен, DI активен.
*/
#include "_gfx.h"
void _gfx_rmw16_pixel(void) __naked
{
__asm
ld a, (__gfx_g16_y)
out (#0x89), a
ld hl, (__gfx_g16_addr)
ld a, (__gfx_g16_mask)
ld b, a ; B = маска сохранения
ld a, (__gfx_g16_nibble)
ld c, a ; C = новый нибл (в нужной половине)
ld a, (hl)
and a, b ; стереть целевой нибл
or a, c ; вписать новый
ld (hl), a
ret
__endasm;
}
-20
View File
@@ -1,20 +0,0 @@
/*
* _gfx_state — общее состояние графики (модуль только с данными).
*
* Пишут gfx_init / gfx_set_visible_page / gfx_set_draw_page /
* gfx_set_bank; читают все mode-специфичные примитивы.
*/
#include "_gfx.h"
uint8_t _gfx_visible_page;
uint8_t _gfx_draw_page;
/* См. memory/sprinter_vram_transparency.md про биты 0x50..0x5F
* (0x50 normal, 0x54 temp, 0x58 transparent, 0x5C оба). */
uint8_t _gfx_bank = 0x50;
/* Каждая VRAM-строка — 1024 байта: page 0 занимает байты 0..319
* (CPU 0xC000+), page 1 — 320..639 (CPU 0xC140+), остаток — дескрипторы
* режима/палитра, их не трогаем. */
uint16_t _gfx_addr_base = 0xC000;
-17
View File
@@ -1,17 +0,0 @@
/*
* _gfx_t16_pair_build — заполнить pair-таблицу текстового рендера
* mode 0x82 для пары цветов fg/bg.
*/
#include "_gfx.h"
void _gfx_t16_pair_build(uint8_t fg, uint8_t bg)
{
uint8_t f = fg & 0x0F;
uint8_t b = bg & 0x0F;
/* HIGH nibble = LEFT, LOW = RIGHT (конвенция sprinter_graphics). */
_gfx_t16_pair[0] = (uint8_t)((b << 4) | b); /* 00 BG BG */
_gfx_t16_pair[1] = (uint8_t)((b << 4) | f); /* 01 BG FG */
_gfx_t16_pair[2] = (uint8_t)((f << 4) | b); /* 10 FG BG */
_gfx_t16_pair[3] = (uint8_t)((f << 4) | f); /* 11 FG FG */
}
-14
View File
@@ -1,14 +0,0 @@
/*
* _gfx_t16_state — скретч текстового рендера mode 0x82 (только данные).
* Пишут gfx_putchar16/gfx_text16 и _gfx_t16_pair_build, читает
* _gfx_render_row16.
*/
#include "_gfx.h"
uint8_t _gfx_t16_y;
uint8_t _gfx_t16_row;
uint16_t _gfx_t16_addr;
/* index = LEFT*2 + RIGHT, value = (LEFT<<4)|RIGHT. */
uint8_t _gfx_t16_pair[4];
-30
View File
@@ -1,30 +0,0 @@
/*
* _gfx_vline16_raw — вертикальная линия len пикселей от (x,y),
* mode 0x82. Вертикального accel-пути нет: байт покрывает два
* горизонтальных пикселя, Fill затёр бы соседний нибл — поэтому
* RMW на каждую строку. W3-naive.
*/
#include "_gfx.h"
void _gfx_vline16_raw(int x, int y, int len, uint8_t color)
{
if ((unsigned)x >= GFX_WIDTH_16) return;
if (y < 0) { len += y; y = 0; }
if (y >= GFX_HEIGHT_16) return;
if (y + len > GFX_HEIGHT_16) len = GFX_HEIGHT_16 - y;
if (len <= 0) return;
_gfx_g16_addr = (uint16_t)(_gfx_addr_base + ((unsigned)x >> 1));
if (x & 1) {
_gfx_g16_nibble = (uint8_t)(color & 0x0F);
_gfx_g16_mask = 0xF0;
} else {
_gfx_g16_nibble = (uint8_t)((color & 0x0F) << 4);
_gfx_g16_mask = 0x0F;
}
for (int i = 0; i < len; i++) {
_gfx_g16_y = (uint8_t)(y + i);
_gfx_rmw16_pixel();
}
}
-50
View File
@@ -1,50 +0,0 @@
/*
* _gfx_vline256_raw — вертикальная линия len пикселей от (x,y),
* mode 0x81. Вертикальный Fill акселератора сам инкрементирует Port_Y,
* так что вся колонка (высота 256) — один burst. W3-naive.
*/
#include "_gfx.h"
/* Вертикальный Fill: _gfx_acc_len пикселей в колонке _gfx_acc_addr,
* верхняя строка _gfx_acc_y. Про SMC и правила FSM акселератора —
* см. _gfx_hfill256.c. */
static void vfill_chunk(void) __naked
{
__asm
ld a, (__gfx_acc_len)
ld (_vfill256_len_imm), a
ld a, (__gfx_acc_y)
out (#0x89), a ; стартовый Y
ld a, (__gfx_acc_color)
ld c, a
ld hl, (__gfx_acc_addr)
ld d, d ; 0x52 режим размера блока
ld a, #0 ; immediate-длина (патчится)
_vfill256_len_imm = . - 1
ld e, e ; 0x5B вертикальный Fill
ld a, c ; A = цвет
ld (hl), a ; выстрел
ld b, b ; 0x40 выключить
ret
__endasm;
}
void _gfx_vline256_raw(int x, int y, int len, uint8_t color)
{
if ((unsigned)x >= GFX_WIDTH) return;
if (y < 0) { len += y; y = 0; }
if (y >= GFX_HEIGHT) return;
if (y + len > GFX_HEIGHT) len = GFX_HEIGHT - y;
if (len <= 0) return;
/* GFX_HEIGHT = 256, полная колонка — один accel-burst. */
_gfx_acc_color = color;
_gfx_acc_y = (uint8_t)y;
_gfx_acc_addr = (uint16_t)(_gfx_addr_base + (unsigned)x);
_gfx_acc_len = (len == 256) ? 0 : (uint8_t)len;
vfill_chunk();
}
-10
View File
@@ -1,10 +0,0 @@
/*
* _gfx_w3_state — сохранённый байт W3 между begin и end (данные).
*
* НЕ реентерабельно — безопасно только потому, что GFX держит
* прерывания выключенными между _gfx_w3_video_begin и _end.
*/
#include "_gfx.h"
uint8_t _gfx_saved_w3;
-20
View File
@@ -1,20 +0,0 @@
/*
* _gfx_w3_video_begin — DI, сохранить текущую страницу W3 и замапить
* _gfx_bank (видеобанк 0x50..0x5F). Композитные примитивы обрамляют
* одной парой begin/end весь внутренний цикл — W3-танец оплачивается
* один раз на операцию, а не на пиксель.
*/
#include "_gfx.h"
void _gfx_w3_video_begin(void) __naked
{
__asm
di
in a, (#0xE2)
ld (__gfx_saved_w3), a
ld a, (__gfx_bank)
out (#0xE2), a
ret
__endasm;
}
-15
View File
@@ -1,15 +0,0 @@
/*
* _gfx_w3_video_end — восстановить сохранённую страницу W3 и EI.
*/
#include "_gfx.h"
void _gfx_w3_video_end(void) __naked
{
__asm
ld a, (__gfx_saved_w3)
out (#0xE2), a
ei
ret
__endasm;
}
-12
View File
@@ -1,12 +0,0 @@
/*
* gfx_clear16 — залить экран цветом (mode 0x82); одна W3-скобка.
*/
#include "_gfx.h"
void gfx_clear16(uint8_t color)
{
_gfx_w3_video_begin();
_gfx_clear16_raw(color);
_gfx_w3_video_end();
}
-12
View File
@@ -1,12 +0,0 @@
/*
* gfx_clear256 — залить экран цветом (mode 0x81); одна W3-скобка.
*/
#include "_gfx.h"
void gfx_clear256(uint8_t color)
{
_gfx_w3_video_begin();
_gfx_clear256_raw(color);
_gfx_w3_video_end();
}

Some files were not shown because too many files have changed in this diff Show More