From 029607971f4f866f1f747fe6cb723fe9643bac13 Mon Sep 17 00:00:00 2001 From: Alexander Petrov Date: Fri, 10 Jul 2026 14:32:21 +0300 Subject: [PATCH] =?UTF-8?q?libbgi:=20bgi256=20fill-=D0=BF=D1=80=D0=B8?= =?UTF-8?q?=D0=BC=D0=B8=D1=82=D0=B8=D0=B2=D1=8B=20=D0=BD=D0=B0=20register-?= =?UTF-8?q?ABI=20+=20typedef=20color=5Ft?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - удалён глобальный скретч акселератора (_gfx_acc256.c); fill-сегменты (_gfx_hfill256/_gfx_vfill256) принимают аргументы в регистрах HL/C/B/E, вызываются только из asm raw-примитивов - новый _gfx_rectfill256: заливка прямоугольника через Horizontal_Size - raw-примитивы (plot/read/hspan/vspan/clear) переписаны под новый ABI - graphics.h: typedef color_t (uint8_t) для всех public color-функций - sprinter-cc: флаг --safe (линковка *_safe.lib при наличии) - CLAUDE.md/mame_interactive: авто-прогон тестов в MAME - gfx_demo: демонстрация rectfill Co-Authored-By: Claude Opus 4.8 --- .clangd | 13 ++++ CLAUDE.md | 10 ++- bin/sprinter-cc | 15 +++- examples/mdview2/README.md | 2 +- libbgi/Makefile | 114 +++++++++++++++++------------- libbgi/_bgi.h | 32 +++++---- libbgi/bgi256/_bgi_clear_raw.c | 46 ++++++++---- libbgi/bgi256/_bgi_hspan_raw.c | 80 +++++++++++++++------ libbgi/bgi256/_bgi_plot_raw.c | 55 ++++++++++----- libbgi/bgi256/_bgi_read_raw.c | 48 ++++++++----- libbgi/bgi256/_bgi_vspan_raw.c | 117 ++++++++++++++++++++----------- libbgi/bgi256/_gfx_acc256.c | 12 ---- libbgi/bgi256/_gfx_hfill256.c | 58 ++++++++------- libbgi/bgi256/_gfx_rectfill256.c | 91 ++++++++++++++++++++++++ libbgi/bgi256/_gfx_vfill256.c | 36 ++++++++++ libbgi/bgi256/getpixel.c | 42 ++++++----- libbgi/bgi256/putpixel.c | 44 +++++++----- libbgi/common/_bgi_begin.c | 3 +- libbgi/common/_bgi_end.c | 3 +- libbgi/common/bar.c | 12 +++- libbgi/common/getbkcolor.c | 2 +- libbgi/common/getcolor.c | 2 +- libbgi/common/getmaxcolor.c | 2 +- libbgi/common/setbkcolor.c | 2 +- libbgi/common/setcolor.c | 2 +- libbgi/common/setfillstyle.c | 2 +- libbgi/include/graphics.h | 24 ++++--- tests/bgi_img/Makefile | 2 +- tests/bgitest/Makefile | 2 +- tests/gfx_demo/gfx_demo.c | 15 ++-- toolchain/mame_interactive.py | 13 +++- 31 files changed, 618 insertions(+), 283 deletions(-) create mode 100644 .clangd delete mode 100644 libbgi/bgi256/_gfx_acc256.c create mode 100644 libbgi/bgi256/_gfx_rectfill256.c create mode 100644 libbgi/bgi256/_gfx_vfill256.c diff --git a/.clangd b/.clangd new file mode 100644 index 0000000..5abaee1 --- /dev/null +++ b/.clangd @@ -0,0 +1,13 @@ +CompileFlags: + Add: + - -Ilibbgi/include + - -Ilibc/include + - -I. + - --target=z80-unknown-unknown + - -mz80 + - -std=c99 + - -D__SDCC # если нужно гасить SDCC-специфику через #ifdef + Remove: + - --target=z80-unknown-unknown +Diagnostics: + UnusedIncludes: None # опционально, чтобы не ругался на "лишние" инклюды под другой таргет diff --git a/CLAUDE.md b/CLAUDE.md index baec71d..ae6d77b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -18,9 +18,13 @@ make size-baseline # принять текущие размеры эталон использующая graphics.h, собирается с `--gfx 256` (или `--gfx 16` в Фазе 2): sprinter-cc подлинкует lib/bgi256.lib и добавит -I libbgi/include. -Одиночный тест: `cd tests/<имя> && make run` (пакует ТОЛЬКО этот exe -+ EXTRA_DATA на дискету и запускает MAME). Тесты в MAME гоняет -пользователь — готовь дискету и проси прогнать. +Одиночный тест: `cd tests/<имя> && make run` (пакует exe + EXTRA_DATA на +дискету и запускает MAME автоматически через `toolchain/mame_interactive.py`, +снимает скриншоты, выводит пути). Для сложных сценариев (диалог, несколько +шагов ввода) — прямой вызов: +`python3 toolchain/mame_interactive.py tests/<имя>/<имя>.exe --snap T1,T2 --timeout T`. +Скриншоты лежат в `mame/v306/snap_auto/sprinter/` (читаются инструментом Read). +Подробности: `docs/mame-autotest.md`. После правок libc/libbgi: пересборка от чистого листа (`make -C libc clean` / `make -C libbgi clean`) не обязательна — stale .rel чистятся diff --git a/bin/sprinter-cc b/bin/sprinter-cc index 51e102f..9721515 100755 --- a/bin/sprinter-cc +++ b/bin/sprinter-cc @@ -37,10 +37,12 @@ # --max-allocs N SDCC --max-allocs-per-node (default: SDCC's 3000). # Higher values give smaller/faster code at the cost of # compile time; 10000..100000 is a reasonable range. -# --debug enable runtime diagnostics — defines DEBUG_RT for both +# --debug enable runtime diagnostics — defines DEBUG_RT for both # sdcc (-DDEBUG_RT) and the crt0 assembly (prepended # `DEBUG_RT = 1`). Exposes runtime introspection symbols # such as `_w2_self_allocated` (see ). +# --safe link safe versions of libraries (e.g. lib/bgi256_safe.lib) +# if they exist; otherwise fall back to standard library. # -v verbose (echo every command) # -h, --help show this help # @@ -88,6 +90,7 @@ 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 +SAFE_LIBS=0 # if 1, prefer *_safe.lib variants (fallback to standard) # ------- Parse args ---------------------------------------------------------- usage() { @@ -114,6 +117,7 @@ while [[ $# -gt 0 ]]; do --max-allocs) MAX_ALLOCS="$2"; shift 2;; --gfx) GFX_MODE="$2"; shift 2;; --debug) DEBUG_RT=1; shift;; + --safe) SAFE_LIBS=1; shift;; -v) VERBOSE=1; shift;; -h|--help) usage 0;; -*) echo "sprinter-cc: unknown option: $1" >&2; usage 1;; @@ -130,7 +134,14 @@ done GFX_LD=() case "$GFX_MODE" in "") ;; # graphics.h не используется - 256) GFX_LD=("-lbgi256");; # libbgi/bgi256 → lib/bgi256.lib + 256) + if [[ "$SAFE_LIBS" -eq 1 ]] && [[ -f "$LIB_DIR/bgi256_safe.lib" ]]; then + GFX_LD=("-lbgi256_safe") + [[ $VERBOSE -eq 1 ]] && echo " gfx: safe variant (lib/bgi256_safe.lib)" + else + GFX_LD=("-lbgi256") + fi + ;; 16) echo "sprinter-cc: --gfx 16 ещё не реализован (пока только 256)" >&2; exit 1;; *) echo "sprinter-cc: --gfx: ожидается 256 (или 16), дано: $GFX_MODE" >&2; exit 1;; esac diff --git a/examples/mdview2/README.md b/examples/mdview2/README.md index 1fde9d7..0cc3c6a 100644 --- a/examples/mdview2/README.md +++ b/examples/mdview2/README.md @@ -121,7 +121,7 @@ MDVIEW2.EXE <файл.md> ``` make # собрать mdview2.exe make floppy # собрать и упаковать дискету для MAME (mc.img) -make run # floppy + запуск MAME +make run # floppy + автоматический прогон в MAME (скриншоты) ``` На дискету кладутся: `MDVIEW2.EXE` и три документа, каждый в своей кодировке diff --git a/libbgi/Makefile b/libbgi/Makefile index 8513c83..9c8d4eb 100644 --- a/libbgi/Makefile +++ b/libbgi/Makefile @@ -1,79 +1,99 @@ -# Build lib/bgi256.lib — Sprinter BGI graphics driver (320×256×256). +# Build lib/bgi256.lib + lib/bgi256_safe.lib — Sprinter BGI graphics driver (320×256×256). # # libbgi/ — самостоятельная графическая библиотека, отделённая от libc. # Две driver-библиотеки собираются из одной исходной базы: -# bgi256.lib = common/*.rel + bgi256/*.rel (эта — Фаза 1) -# bgi16.lib = common/*.rel + bgi16/*.rel (Фаза 2; пока не собирается) -# common/ — mode-agnostic: .rel компилируется ОДИН раз и кладётся в ОБА -# архива (один исходник для функций, общих 256- и 16-цветной версиям). -# bgi256/ — mode-specific leaf'ы (реальные реализации, без обёрток). +# bgi256.lib = common/*.rel + bgi256/*.rel (fast, без bounds-check) +# bgi256_safe.lib = common/*.rel + bgi256/*.rel (safe, с bounds-check) +# common/ — mode-agnostic: .rel компилируется в ОБЕ версии параллельно. +# bgi256/ — mode-specific leaf'ы. # # Каждый .c — отдельный .rel в архиве (1 публичная функция = 1 модуль); -# линкер тянет только нужные .rel → DCE на уровне файлов. См. план -# «Рефакторинг GFX → BGI» и memory/bgi_two_lib_design. +# линкер тянет только нужные .rel → DCE на уровне файлов. # -# make build lib/bgi256.lib -# make clean remove build/ + lib/bgi256.lib +# make build lib/bgi256.lib + lib/bgi256_safe.lib (обе) +# make fast build только lib/bgi256.lib +# make safe build только lib/bgi256_safe.lib +# make BUILD_SAFE=1 build только lib/bgi256_safe.lib (legacy) +# make clean remove build/ + обе библиотеки # -# (Фаза 2 добавит bgi16.lib = common + bgi16; common .rel переиспользуются.) PROJ_ROOT := $(abspath $(CURDIR)/..) SDCC_BIN := $(PROJ_ROOT)/third_party/sdcc/bin SDCC := $(SDCC_BIN)/sdcc SDAR := $(SDCC_BIN)/sdar -# -I libbgi/include (gfx.h, graphics.h) + -I libc/include (palette.h, -# sprinter_exit.h — публичные заголовки libc; libbgi всегда линкуется с -# libc). SDCC stdint и пр. — из собственного include-пути SDCC. INC := -I$(CURDIR)/include -I$(PROJ_ROOT)/libc/include -CC_FLAGS := -mz80 --no-std-crt0 --std-c99 --opt-code-size $(INC) BUILD := $(CURDIR)/build +COMMON_C := $(wildcard $(CURDIR)/common/*.c) +BGI256_C := $(wildcard $(CURDIR)/bgi256/*.c) -COMMON_C := $(wildcard $(CURDIR)/common/*.c) -BGI256_C := $(wildcard $(CURDIR)/bgi256/*.c) -# BGI16_C := $(wildcard $(CURDIR)/bgi16/*.c) # Фаза 2 - -COMMON_RELS := $(patsubst $(CURDIR)/common/%.c,$(BUILD)/common/%.rel,$(COMMON_C)) -BGI256_RELS := $(patsubst $(CURDIR)/bgi256/%.c,$(BUILD)/bgi256/%.rel,$(BGI256_C)) -KNOWN_RELS := $(COMMON_RELS) $(BGI256_RELS) +COMMON_FAST_RELS := $(patsubst $(CURDIR)/common/%.c,$(BUILD)/fast/common/%.rel,$(COMMON_C)) +BGI256_FAST_RELS := $(patsubst $(CURDIR)/bgi256/%.c,$(BUILD)/fast/bgi256/%.rel,$(BGI256_C)) +COMMON_SAFE_RELS := $(patsubst $(CURDIR)/common/%.c,$(BUILD)/safe/common/%.rel,$(COMMON_C)) +BGI256_SAFE_RELS := $(patsubst $(CURDIR)/bgi256/%.c,$(BUILD)/safe/bgi256/%.rel,$(BGI256_C)) LIB_DIR := $(PROJ_ROOT)/lib -LIB256 := $(LIB_DIR)/bgi256.lib +LIB_FAST := $(LIB_DIR)/bgi256.lib +LIB_SAFE := $(LIB_DIR)/bgi256_safe.lib -# Штамп списка модулей — иначе удаление/переименование исходника не -# триггерит перелинковку и stale .rel остаётся в архиве. На exFAT -# грубая mtime — сравниваем по списку, не по времени. -MODLIST := $(BUILD)/.modules -ifneq ($(shell cat $(MODLIST) 2>/dev/null),$(KNOWN_RELS)) -_ := $(shell mkdir -p $(BUILD); echo "$(KNOWN_RELS)" > $(MODLIST); rm -f $(LIB256)) -endif +all: fast safe -all: $(LIB256) +fast: $(LIB_FAST) -# Pattern rule для common/ и bgi256/ (один уровень под build/). -$(BUILD)/common/%.rel: $(CURDIR)/common/%.c +safe: $(LIB_SAFE) + +# --- Fast version: -DGFX_NOCHECK (guard'и выключены) --- + +$(BUILD)/fast/common/%.rel: $(CURDIR)/common/%.c @mkdir -p $(dir $@) - $(SDCC) $(CC_FLAGS) -c -o $@ $< + $(SDCC) -mz80 --no-std-crt0 --std-c99 --opt-code-size -DGFX_NOCHECK $(INC) -c -o $@ $< -$(BUILD)/bgi256/%.rel: $(CURDIR)/bgi256/%.c +$(BUILD)/fast/bgi256/%.rel: $(CURDIR)/bgi256/%.c @mkdir -p $(dir $@) - $(SDCC) $(CC_FLAGS) -c -o $@ $< + $(SDCC) -mz80 --no-std-crt0 --std-c99 --opt-code-size -DGFX_NOCHECK $(INC) -c -o $@ $< -# Archive — sdar rcs = replace/create/symtab. Перед упаковкой удаляем -# stale .rel (wildcard не знает об удалённых исходниках). -$(LIB256): $(COMMON_RELS) $(BGI256_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"; \ +# --- Safe version: guard'и включены (по умолчанию) --- + +$(BUILD)/safe/common/%.rel: $(CURDIR)/common/%.c + @mkdir -p $(dir $@) + $(SDCC) -mz80 --no-std-crt0 --std-c99 --opt-code-size $(INC) -c -o $@ $< + +$(BUILD)/safe/bgi256/%.rel: $(CURDIR)/bgi256/%.c + @mkdir -p $(dir $@) + $(SDCC) -mz80 --no-std-crt0 --std-c99 --opt-code-size $(INC) -c -o $@ $< + +# --- Archive: fast --- + +$(BUILD)/fast/.have: $(COMMON_FAST_RELS) $(BGI256_FAST_RELS) + @find $(BUILD)/fast -name '*.rel' -print | sort > $@ 2>/dev/null || true + @printf '%s\n' $(COMMON_FAST_RELS) $(BGI256_FAST_RELS) | sort > $(BUILD)/fast/.want + @comm -23 $(BUILD)/fast/.have $(BUILD)/fast/.want | while read f; do \ + echo " stale: $$f — rm"; \ rm -f "$$f" "$${f%.rel}.asm" "$${f%.rel}.lst" "$${f%.rel}.sym"; \ done + +$(LIB_FAST): $(COMMON_FAST_RELS) $(BGI256_FAST_RELS) $(BUILD)/fast/.have rm -f $@ - $(SDAR) -rcs $@ $(COMMON_RELS) $(BGI256_RELS) - @echo " Built $@ with $(words $(COMMON_RELS)) common + $(words $(BGI256_RELS)) bgi256 modules." + $(SDAR) -rcs $@ $(COMMON_FAST_RELS) $(BGI256_FAST_RELS) + @echo " Built $@ with $$(( $$(echo "$(words $(COMMON_FAST_RELS))") + $$(echo "$(words $(BGI256_FAST_RELS))") )) modules." + +# --- Archive: safe --- + +$(BUILD)/safe/.have: $(COMMON_SAFE_RELS) $(BGI256_SAFE_RELS) + @find $(BUILD)/safe -name '*.rel' -print | sort > $@ 2>/dev/null || true + @printf '%s\n' $(COMMON_SAFE_RELS) $(BGI256_SAFE_RELS) | sort > $(BUILD)/safe/.want + @comm -23 $(BUILD)/safe/.have $(BUILD)/safe/.want | while read f; do \ + echo " stale: $$f — rm"; \ + rm -f "$$f" "$${f%.rel}.asm" "$${f%.rel}.lst" "$${f%.rel}.sym"; \ + done + +$(LIB_SAFE): $(COMMON_SAFE_RELS) $(BGI256_SAFE_RELS) $(BUILD)/safe/.have + rm -f $@ + $(SDAR) -rcs $@ $(COMMON_SAFE_RELS) $(BGI256_SAFE_RELS) + @echo " Built $@ with $$(( $$(echo "$(words $(COMMON_SAFE_RELS))") + $$(echo "$(words $(BGI256_SAFE_RELS))") )) modules." clean: - rm -rf $(BUILD) $(LIB256) + rm -rf $(BUILD) $(LIB_FAST) $(LIB_SAFE) -.PHONY: all clean +.PHONY: all fast safe clean diff --git a/libbgi/_bgi.h b/libbgi/_bgi.h index 554bbb5..176065e 100644 --- a/libbgi/_bgi.h +++ b/libbgi/_bgi.h @@ -62,8 +62,8 @@ int _videomode_raw_set(uint8_t mode); extern uint8_t _gfx_saved_w3; -void _bgi_begin(void); -void _bgi_end(void); +void _bgi_begin(void) __preserves_regs(b,c,d,e,h,l); +void _bgi_end(void) __preserves_regs(b,c,d,e,h,l); /* ---- LEAF-операции драйвера (bgi256/ или bgi16/) ----------------- * * Каждая self-contained: raw-варианты НЕ ставят свою W3-скобку @@ -86,17 +86,25 @@ unsigned _bgi_read (int x, int y); void _bgi_hspan (int x, int y, int len, uint8_t color); void _bgi_clearall(uint8_t color); -/* ---- Скретч акселератора 256 (bgi256/_gfx_acc256.c) -------------- * - * Общий для hspan/vspan/clear; однопоточно, IRQ выключены между - * _bgi_begin/_bgi_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'ы акселератора 256 (register-ABI, asm-only) ------ * + * Вызываются ТОЛЬКО из asm raw-примитивов; аргументы в регистрах, без + * глобального скретча в _DATA: + * HL = CPU-адрес (base + x), C = color, + * B = длина (0 = 256), E = y (строка для Port_Y). + * Клоббер A/F; сохраняют BC/DE/HL/IX/IY. Pre: W3 замаплен, IRQ гасятся + * внутри вокруг одного выстрела. Прототип void(void) — только чтобы + * линкер видел символ (реальный ABI регистровый, см. bgi256/_gfx_*fill256.c). */ -/* Горизонтальный Fill-burst: _gfx_acc_len байт с _gfx_acc_addr на - * строке _gfx_acc_y (W3 замаплен, DI активен). */ -void _gfx_hfill256_chunk(void); +/* Горизонтальный Fill-burst (bgi256/_gfx_hfill256.c). */ +void _gfx_hfill256_segment(void); + +/* Вертикальный Fill-burst (bgi256/_gfx_vfill256.c). */ +void _gfx_vfill256_segment(void); + +/* Заливка прямоугольника цветом: x, y, w, h, color. + * Механизм: один раз задаётся Horizontal_Size, затем в цикле по Y + * акселератор копирует байт цвета на ширину строки (docs/part2/accelerator_doc.txt). */ +void _gfx_rectfill256(int x, int y, int w, int h, uint8_t color); /* ---- Скретч 16-цветных примитивов (bgi16/_gfx_g16_state.c) ------- */ extern uint8_t _gfx_g16_y; diff --git a/libbgi/bgi256/_bgi_clear_raw.c b/libbgi/bgi256/_bgi_clear_raw.c index 5d557a7..2f89870 100644 --- a/libbgi/bgi256/_bgi_clear_raw.c +++ b/libbgi/bgi256/_bgi_clear_raw.c @@ -1,21 +1,41 @@ /* * _bgi_clear_raw — залить всё поле 320×256 цветом color, mode 0x81. - * Построчный hfill: 256 строк × 2 burst'а (256 + 64 байта) = 512. - * W3-naive: вызывающий обрамляет begin/end. + * 320 вертикальных Fill-burst'ов (по колонке 256 строк), Port_Y=0. + * Register-only asm, без глобального скретча. W3-naive: вызывающий + * обрамляет _bgi_begin/_bgi_end. color → A (__sdcccall(1)). */ #include "../_bgi.h" -void _bgi_clear_raw(uint8_t color) +void _bgi_clear_raw(uint8_t color) __naked { - _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(); - } + __asm + ld c, a ; color -> c + xor a, a + out (#0x89), a ; Port_Y = 0 + + ld hl, (__gfx_addr_base) ; HL = база draw-страницы (0xC000/0xC140) + ld de, #0x0000 + + _clear_cyc0: + ld a, e ; if DE >= 320 - return + sub a, #0x40 + ld a, d + sbc a, #0x01 + ret NC + + ;; --- ПОСЛЕДОВАТЕЛЬНОСТЬ АКСЕЛЕРАТОРА --- + di + ld d, d ; 0x52 — режим размера блока + ld a, #0 ; 0x3E 0 — размер (256 точек по высоте) + ld e, e ; 0x5B — вертикальный Fill + ld a, c ; 0x79 — A = цвет (НЕ ещё один ld a,#n) + ld (hl), a ; выстрел; заливает _gfx_acc_len байт + ld b, b ; 0x40 — выключить + ei + + inc hl + inc de + jr _clear_cyc0 + __endasm; } diff --git a/libbgi/bgi256/_bgi_hspan_raw.c b/libbgi/bgi256/_bgi_hspan_raw.c index 8b702ac..f15daba 100644 --- a/libbgi/bgi256/_bgi_hspan_raw.c +++ b/libbgi/bgi256/_bgi_hspan_raw.c @@ -1,28 +1,66 @@ /* - * _bgi_hspan_raw — горизонтальная линия len пикселей от (x,y), - * mode 0x81, через Fill-burst'ы акселератора (до 256 байт за выстрел). - * W3-naive: вызывающий обрамляет begin/end. + * _bgi_hspan_raw — горизонтальная линия len пикселей от (x,y), mode 0x81, + * через Fill-burst'ы акселератора (до 256 байт за выстрел). W3-naive: + * вызывающий обрамляет _bgi_begin/_bgi_end. + * + * __sdcccall(1): x→HL, y→DE(E=y), len→стек(4/5(ix)), color→стек(6(ix)). + * Значения передаются в _gfx_hfill256_segment через регистры (HL=addr, + * C=color, B=len, E=y) — без глобального скретча. len>256 (макс. ширина + * 320) → два burst'а: хвост (len-256) на addr+256, затем 256 на addr; + * усечение len до байта даёт (len-256) для 257..320 (256 -> 0 = «256»). */ #include "../_bgi.h" -void _bgi_hspan_raw(int x, int y, int len, uint8_t color) +void _bgi_hspan_raw(int x, int y, int len, uint8_t color) __naked { - 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; - } + __asm + .globl __gfx_addr_base + .globl __gfx_hfill256_segment + push ix + ld ix, #0 + add ix, sp +#ifndef GFX_NOCHECK + ld a, d + or a, a + jr NZ, hs_ret ; (unsigned)y >= 256 + ld a, l + sub a, #0x40 + ld a, h + sbc a, #0x01 + jr NC, hs_ret ; (unsigned)x >= 320 +#endif + ld c, l + ld b, h ; BC = x + ld hl, (__gfx_addr_base) + add hl, bc ; HL = base + x + ld a, 6 (ix) + ld c, a ; C = color + ld a, 5 (ix) + or a, a + jr Z, hs_single ; len < 256 + ld a, 4 (ix) + or a, a + jr Z, hs_single ; len == 256 (B=0) + ;; 257..320: два burst-a + push hl ; base addr + inc h ; addr + 256 + ld a, 4 (ix) + ld b, a ; B = len-256 (низкий байт) + call __gfx_hfill256_segment + pop hl ; base addr + ld b, #0 ; 256 + call __gfx_hfill256_segment + jr hs_ret + hs_single: + ld a, 4 (ix) + ld b, a ; B = len (0 если ==256) + call __gfx_hfill256_segment + hs_ret: + pop ix + pop hl + pop af + inc sp + jp (hl) + __endasm; } diff --git a/libbgi/bgi256/_bgi_plot_raw.c b/libbgi/bgi256/_bgi_plot_raw.c index 83ee5fe..7b6c4aa 100644 --- a/libbgi/bgi256/_bgi_plot_raw.c +++ b/libbgi/bgi256/_bgi_plot_raw.c @@ -1,30 +1,47 @@ /* - * _bgi_plot_raw — пиксель (x,y) цветом color, mode 0x81. + * _bgi_plot_raw — пиксель (x,y) цветом color, mode 0x81 (register-only asm). * - * "Raw" = W3-naive: вызывающий уже сделал _gfx_w3_video_begin (W3 на - * видеобанке, DI). Адресация: CPU _gfx_addr_base + x, Port_Y (0x89) = y. + * "Raw" = W3-naive: вызывающий уже сделал _bgi_begin (W3 на видеобанке, DI). + * Адресация: CPU _gfx_addr_base + x, Port_Y (0x89) = y. * - * Скретч отдельный от _gfx_acc_* — чтобы putpixel внутри цикла - * Брезенхэма не топтал внешнее состояние акселератора. + * __sdcccall(1): x→HL, y→DE (E=low), color→стек (1 байт над ret-адресом). + * Всё считается в регистрах — ни статик-скретча, ни round-trip через _DATA + * (самый горячий примитив: Брезенхэм в line/circle/fillpoly/draw_scaled). */ #include "../_bgi.h" -static uint8_t _gfx_pp_y; -static uint16_t _gfx_pp_addr; -static uint8_t _gfx_pp_color; - -void _bgi_plot_raw(int x, int y, uint8_t color) +void _bgi_plot_raw(int x, int y, uint8_t color) __naked { - 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 + .globl __gfx_addr_base +#ifndef GFX_NOCHECK + ld a, l + sub a, #0x40 + ld a, h + sbc a, #0x01 + jr NC, pp_done ; (unsigned)x >= 320 + ld a, d + or a, a + jr NZ, pp_done ; (unsigned)y >= 256 +#endif + ld a, e + out (#0x89), a ; Port_Y = y + ld bc, (__gfx_addr_base) + add hl, bc ; HL = base + x + ld c, l + ld b, h ; BC = dest + ld hl, #2 + add hl, sp + ld a, (hl) ; A = color (со стека, SP+2) + ld h, b + ld l, c ; HL = dest + ld (hl), a ; выстрел +#ifndef GFX_NOCHECK + pp_done: +#endif + pop hl ; ret-адрес + inc sp ; callee-pop байта color + jp (hl) __endasm; } diff --git a/libbgi/bgi256/_bgi_read_raw.c b/libbgi/bgi256/_bgi_read_raw.c index bffc51a..c9bc33e 100644 --- a/libbgi/bgi256/_bgi_read_raw.c +++ b/libbgi/bgi256/_bgi_read_raw.c @@ -1,27 +1,41 @@ /* - * _bgi_read_raw — прочитать пиксель (x,y) БЕЗ своей W3-скобки - * (вызывающий уже сделал _gfx_w3_video_begin). Для блиттинга image: - * getimage/putimage читают тысячи пикселей в одной скобке. + * _bgi_read_raw — прочитать пиксель (x,y), mode 0x81 (register-only asm). + * + * "Raw" = W3-naive: вызывающий уже сделал _bgi_begin (W3 на видеобанке, DI). + * Для блиттинга: getimage/putimage читают тысячи пикселей в одной скобке. * За пределами экрана возвращает 0. + * + * __sdcccall(1): x→HL, y→DE (E=low); возврат uint8_t в A. Всё в регистрах, + * без статик-скретча (единый стиль с _bgi_plot_raw). */ #include "../_bgi.h" -static uint8_t _gfx_gpr_y; -static uint16_t _gfx_gpr_addr; -static uint8_t _gfx_gpr_color; - -uint8_t _bgi_read_raw(int x, int y) +uint8_t _bgi_read_raw(int x, int y) __naked { - 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); + (void)x; (void)y; __asm - ld a, (__gfx_gpr_y) - out (#0x89), a - ld hl, (__gfx_gpr_addr) - ld a, (hl) - ld (__gfx_gpr_color), a + .globl __gfx_addr_base +#ifndef GFX_NOCHECK + ld a, l + sub a, #0x40 + ld a, h + sbc a, #0x01 + jr NC, rr_zero ; (unsigned)x >= 320 + ld a, d + or a, a + jr NZ, rr_zero ; (unsigned)y >= 256 +#endif + ld a, e + out (#0x89), a ; Port_Y = y + ld bc, (__gfx_addr_base) + add hl, bc ; HL = base + x + ld a, (hl) ; A = пиксель (возврат) + ret +#ifndef GFX_NOCHECK + rr_zero: + xor a, a + ret +#endif __endasm; - return _gfx_gpr_color; } diff --git a/libbgi/bgi256/_bgi_vspan_raw.c b/libbgi/bgi256/_bgi_vspan_raw.c index ed5a64e..73ce84c 100644 --- a/libbgi/bgi256/_bgi_vspan_raw.c +++ b/libbgi/bgi256/_bgi_vspan_raw.c @@ -1,50 +1,85 @@ /* - * _bgi_vspan_raw — вертикальная линия len пикселей от (x,y), - * mode 0x81. Вертикальный Fill акселератора сам инкрементирует Port_Y, - * так что вся колонка (высота 256) — один burst. W3-naive. + * _bgi_vspan_raw — вертикальная линия len пикселей от (x,y), mode 0x81, + * через Vertical Fill акселератора (один burst = колонка до 256 строк). + * W3-naive: вызывающий обрамляет _bgi_begin/_bgi_end. + * + * __sdcccall(1): x→HL, y→DE(E=y), len→стек(4/5(ix)), color→стек(6(ix)). + * В _gfx_vfill256_segment передаём HL=addr, C=color, B=len, E=y (без + * глобального скретча). x держим в BC на время клипа (clip портит HL/DE), + * адрес считаем в конце. len усекается до байта: 256 -> 0 = «256». */ #include "../_bgi.h" -/* Вертикальный Fill: _gfx_acc_len пикселей в колонке _gfx_acc_addr, - * верхняя строка _gfx_acc_y. Про SMC и правила FSM акселератора — - * см. _gfx_hfill256.c. */ -static void vfill_chunk(void) __naked +void _bgi_vspan_raw(int x, int y, int len, uint8_t color) __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 + .globl __gfx_addr_base + .globl __gfx_vfill256_segment + push ix + ld ix, #0 + add ix, sp +#ifndef GFX_NOCHECK + ld a, l + sub a, #0x40 + ld a, h + sbc a, #0x01 + jr NC, vs_ret ; (unsigned)x >= 320 +#endif + ld c, l + ld b, h ; BC = x (пережить клип) +#ifndef GFX_NOCHECK + bit 7, d + jr Z, vs_ypos + ;; y < 0: len += y ; y = 0 + ld l, 4 (ix) + ld h, 5 (ix) + add hl, de ; len + y (y отрицателен) + ld 4 (ix), l + ld 5 (ix), h + ld de, #0 + vs_ypos: + ld a, d + or a, a + jr NZ, vs_ret ; y >= 256 + ;; y + len > 256 ? -> len = 256 - y + ld l, 4 (ix) + ld h, 5 (ix) + add hl, de ; y + len (D=0) + ld a, h + or a, a + jr Z, vs_lenchk ; < 256 + dec a + jr NZ, vs_clamp ; high >= 2 + ld a, l + or a, a + jr Z, vs_lenchk ; == 256 + vs_clamp: + xor a, a + sub a, e ; 0 - y (низкий байт) + ld 4 (ix), a + ld a, #1 + sbc a, #0 ; 1 - borrow => 256 - y + ld 5 (ix), a + vs_lenchk: + ld a, 4 (ix) + or a, 5 (ix) + jr Z, vs_ret ; len == 0 + bit 7, 5 (ix) + jr NZ, vs_ret ; len < 0 +#endif + ld hl, (__gfx_addr_base) + add hl, bc ; HL = base + x + ld a, 6 (ix) + ld c, a ; C = color + ld a, 4 (ix) + ld b, a ; B = len (256 -> 0) + call __gfx_vfill256_segment + vs_ret: + pop ix + pop hl + pop af + inc sp + jp (hl) __endasm; } - -void _bgi_vspan_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(); -} diff --git a/libbgi/bgi256/_gfx_acc256.c b/libbgi/bgi256/_gfx_acc256.c deleted file mode 100644 index f7a9e80..0000000 --- a/libbgi/bgi256/_gfx_acc256.c +++ /dev/null @@ -1,12 +0,0 @@ -/* - * _gfx_acc256 — скретч акселератора для 256-цветных raw-примитивов - * (модуль только с данными). Общий для hline/vline/clear; однопоточно — - * GFX работает с выключенными прерываниями между W3-begin/end. - */ - -#include "../_bgi.h" - -uint8_t _gfx_acc_color; -uint8_t _gfx_acc_y; -uint8_t _gfx_acc_len; /* 0 = 256 — конвенция акселератора */ -uint16_t _gfx_acc_addr; diff --git a/libbgi/bgi256/_gfx_hfill256.c b/libbgi/bgi256/_gfx_hfill256.c index d8a108b..3a55f44 100644 --- a/libbgi/bgi256/_gfx_hfill256.c +++ b/libbgi/bgi256/_gfx_hfill256.c @@ -1,45 +1,43 @@ /* - * _gfx_hfill256_chunk — горизонтальный Fill-burst акселератора: - * _gfx_acc_len байт цвета _gfx_acc_color с адреса _gfx_acc_addr на - * строке _gfx_acc_y. Pre: W3 замаплен на видеобанк, DI активен. + * _gfx_hfill256_segment — горизонтальный Fill-burst акселератора (register-ABI). * - * Опкоды акселератора (docs/converted/accel_r.txt): + * ВХОД (регистры; вызывается ТОЛЬКО из asm — raw-примитивами): + * HL = CPU-адрес начала строки (base + x) + * C = цвет + * B = длина (0 = 256 — конвенция акселератора) + * E = y (строка для Port_Y) + * Клоббер: A, F. Сохраняет BC/DE/HL/IX/IY — все опкоды акселератора это + * `ld r,r`, т.е. no-op'ы по регистрам, поэтому color(C)/len(B)/y(E) переживают + * accel-последовательность (это и есть смысл register-ABI — без скретча в _DATA). + * Pre: W3 замаплен на видеобанк (вызывающий сделал _bgi_begin). + * + * Опкоды (docs/converted/accel_r.txt): * LD D,D (0x52) режим «задать размер блока»; СЛЕДУЮЩИЙ байт — длина, - * ОБЯЗАН быть immediate-операндом LD A,n (0x3E) + * ОБЯЗАН быть immediate-операндом LD A,n (0x3E) → SMC-патч. * 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 - * как новый размер блока. + * Другой `ld a,#n` между LD C,C и стреляющим LD (HL),A вставлять нельзя — + * FSM акселератора перечитает immediate как новый размер блока. */ #include "../_bgi.h" -void _gfx_hfill256_chunk(void) __naked +void _gfx_hfill256_segment(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 патчится выше) + ld a, b + ld (_hfill256_len_imm), a ; SMC: размер блока <- B + ld a, e + out (#0x89), a ; Port_Y <- E (y) + di + ld d, d ; 0x52 — режим размера блока + ld a, #0 _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 — выключить + ld c, c ; 0x49 — горизонтальный Fill (C=color, no-op) + ld a, c ; 0x79 — A = цвет + ld (hl), a ; выстрел; заливает len байт + ld b, b ; 0x40 — выключить (B=len, no-op) + ei ret __endasm; } diff --git a/libbgi/bgi256/_gfx_rectfill256.c b/libbgi/bgi256/_gfx_rectfill256.c new file mode 100644 index 0000000..f396b74 --- /dev/null +++ b/libbgi/bgi256/_gfx_rectfill256.c @@ -0,0 +1,91 @@ +/* + * _gfx_rectfill256 — заливка прямоугольника (x,y,w,h) цветом color, mode 0x81. + * + * Выбор ориентации burst'а: если w>h и w<=256 — гоним h горизонтальных + * Fill'ов шириной w (y++ на каждой строке); иначе w вертикальных Fill'ов + * высотой h (addr++ на каждой колонке). Инварианты (color=C, len=B) и + * бегущий addr=HL/y=E живут в регистрах через весь цикл — _gfx_*_segment + * их сохраняет; счётчик другой размерности крутится в его стек-слоте. + * + * __sdcccall(1): x→HL, y→DE(E=y); w→4/5(ix), h→6/7(ix), color→8(ix). + * Pre: W3 замаплен, DI активен вокруг выстрелов внутри segment'ов. + * Вызывающий управляет _bgi_begin/_bgi_end. w,h предполагаются >= 0. + */ + +#include "../_bgi.h" + +void _gfx_rectfill256(int x, int y, int w, int h, uint8_t color) __naked +{ + __asm + .globl __gfx_addr_base + .globl __gfx_hfill256_segment + .globl __gfx_vfill256_segment + push ix + ld ix, #0 + add ix, sp + ld bc, (__gfx_addr_base) + add hl, bc ; HL = base + x (бегущий addr) + ld a, 8 (ix) + ld c, a ; C = color + + ;; --- ветка: w>h && w<=256 ? (unsigned, w/h >= 0) --- + ld a, 4 (ix) + sub a, 6 (ix) + ld b, a ; (w-h) низкий + ld a, 5 (ix) + sbc a, 7 (ix) ; CF=1 если w не (w>h) + ld a, 5 (ix) ; w high + cp a, #2 + jr NC, rf_vbranch ; w >= 512 + dec a + jr NZ, rf_hbranch ; high==0 -> w<256 + ld a, 4 (ix) + or a, a + jr NZ, rf_vbranch ; high==1,low!=0 -> w>256 + + rf_hbranch: + ld a, 4 (ix) + ld b, a ; B = len = w (256 -> 0) + rf_hloop: + ld a, 6 (ix) + or a, 7 (ix) + jr Z, rf_done ; h == 0 + call __gfx_hfill256_segment + inc e ; y++ + ld a, 6 (ix) + sub a, #1 + ld 6 (ix), a + ld a, 7 (ix) + sbc a, #0 + ld 7 (ix), a + jr rf_hloop + + rf_vbranch: + ld a, 6 (ix) + ld b, a ; B = len = h (256 -> 0) + rf_vloop: + ld a, 4 (ix) + or a, 5 (ix) + jr Z, rf_done ; w == 0 + call __gfx_vfill256_segment + inc hl ; addr++ + ld a, 4 (ix) + sub a, #1 + ld 4 (ix), a + ld a, 5 (ix) + sbc a, #0 + ld 5 (ix), a + jr rf_vloop + + rf_done: + pop ix + pop hl + pop af + pop af + inc sp + jp (hl) + __endasm; +} diff --git a/libbgi/bgi256/_gfx_vfill256.c b/libbgi/bgi256/_gfx_vfill256.c new file mode 100644 index 0000000..6c96971 --- /dev/null +++ b/libbgi/bgi256/_gfx_vfill256.c @@ -0,0 +1,36 @@ +/* + * _gfx_vfill256_segment — вертикальный Fill-burst акселератора (register-ABI). + * + * ВХОД (регистры; вызывается ТОЛЬКО из asm — raw-примитивами): + * HL = CPU-адрес колонки (base + x) + * C = цвет + * B = длина (0 = 256 — конвенция акселератора) + * E = y (стартовая строка для Port_Y) + * Клоббер: A, F. Сохраняет BC/DE/HL/IX/IY (все accel-опкоды — no-op'ы по + * регистрам; см. _gfx_hfill256.c). Pre: W3 замаплен (вызывающий сделал _bgi_begin). + * + * Опкоды: LD D,D (0x52) размер блока (SMC-immediate), LD E,E (0x5B) + * вертикальный Fill, LD B,B (0x40) выключить. + */ + +#include "../_bgi.h" + +void _gfx_vfill256_segment(void) __naked +{ + __asm + ld a, b + ld (_vfill256_len_imm), a ; SMC: размер блока <- B + ld a, e + out (#0x89), a ; Port_Y <- E (y) + di + ld d, d ; 0x52 — режим размера блока + ld a, #0 + _vfill256_len_imm = . - 1 + ld e, e ; 0x5B — вертикальный Fill (E=y, no-op) + ld a, c ; 0x79 — A = цвет + ld (hl), a ; выстрел; заливает len байт + ld b, b ; 0x40 — выключить (B=len, no-op) + ei + ret + __endasm; +} diff --git a/libbgi/bgi256/getpixel.c b/libbgi/bgi256/getpixel.c index f1c52fb..e1eddd1 100644 --- a/libbgi/bgi256/getpixel.c +++ b/libbgi/bgi256/getpixel.c @@ -9,25 +9,31 @@ static uint8_t _gp_y; static uint16_t _gp_addr; static uint8_t _gp_color; -unsigned getpixel(int x, int y) +color_t getpixel(int x, int y) { +#ifndef GFX_NOCHECK if ((unsigned)x >= GFX_WIDTH || (unsigned)y >= GFX_HEIGHT) return 0; - _gp_y = (uint8_t)y; - _gp_addr = (uint16_t)(_gfx_addr_base + (unsigned)x); - __asm - di - in a, (#0xE2) - ld (__gfx_saved_w3), a - ld a, (__gfx_bank) - out (#0xE2), a - ld a, (_gp_y) - out (#0x89), a - ld hl, (_gp_addr) - ld a, (hl) - ld (_gp_color), a - ld a, (__gfx_saved_w3) - out (#0xE2), a - ei - __endasm; +#endif + _bgi_begin(); + register uint8_t _gp_color = _bgi_read_raw(x, y); + _bgi_end(); return _gp_color; + // _gp_y = (uint8_t)y; + // _gp_addr = (uint16_t)(_gfx_addr_base + (unsigned)x); + // __asm + // di + // in a, (#0xE2) + // ld (__gfx_saved_w3), a + // ld a, (__gfx_bank) + // out (#0xE2), a + // ld a, (_gp_y) + // out (#0x89), a + // ld hl, (_gp_addr) + // ld a, (hl) + // ld (_gp_color), a + // ld a, (__gfx_saved_w3) + // out (#0xE2), a + // ei + // __endasm; + // return _gp_color; } diff --git a/libbgi/bgi256/putpixel.c b/libbgi/bgi256/putpixel.c index 414e648..200bf27 100644 --- a/libbgi/bgi256/putpixel.c +++ b/libbgi/bgi256/putpixel.c @@ -15,25 +15,31 @@ static uint8_t _pp_y; static uint16_t _pp_addr; static uint8_t _pp_color; -void putpixel(int x, int y, int color) +void putpixel(int x, int y, color_t color) { +#ifndef GFX_NOCHECK if ((unsigned)x >= GFX_WIDTH || (unsigned)y >= GFX_HEIGHT) return; - _pp_y = (uint8_t)y; - _pp_addr = (uint16_t)(_gfx_addr_base + (unsigned)x); - _pp_color = (uint8_t)color; - __asm - di - in a, (#0xE2) - ld (__gfx_saved_w3), a - ld a, (__gfx_bank) - out (#0xE2), a - ld a, (_pp_y) - out (#0x89), a - ld hl, (_pp_addr) - ld a, (_pp_color) - ld (hl), a - ld a, (__gfx_saved_w3) - out (#0xE2), a - ei - __endasm; +#endif + _bgi_begin(); + _bgi_plot_raw(x, y, color); + _bgi_end(); + return; + // _pp_y = (uint8_t)y; + // _pp_addr = (uint16_t)(_gfx_addr_base + (unsigned)x); + // _pp_color = (uint8_t)color; + // __asm + // di + // in a, (#0xE2) + // ld (__gfx_saved_w3), a + // ld a, (__gfx_bank) + // out (#0xE2), a + // ld a, (_pp_y) + // out (#0x89), a + // ld hl, (_pp_addr) + // ld a, (_pp_color) + // ld (hl), a + // ld a, (__gfx_saved_w3) + // out (#0xE2), a + // ei + // __endasm; } diff --git a/libbgi/common/_bgi_begin.c b/libbgi/common/_bgi_begin.c index e138f6a..fd5a5d4 100644 --- a/libbgi/common/_bgi_begin.c +++ b/libbgi/common/_bgi_begin.c @@ -11,10 +11,9 @@ */ #include "../_bgi.h" -void _bgi_begin(void) __naked +void _bgi_begin(void) __preserves_regs(b,c,d,e,h,l) __naked { __asm - di in a, (#0xE2) ld (__gfx_saved_w3), a ld a, (__gfx_bank) diff --git a/libbgi/common/_bgi_end.c b/libbgi/common/_bgi_end.c index 25b1adb..8df9a46 100644 --- a/libbgi/common/_bgi_end.c +++ b/libbgi/common/_bgi_end.c @@ -4,12 +4,11 @@ */ #include "../_bgi.h" -void _bgi_end(void) __naked +void _bgi_end(void) __naked __preserves_regs(b,c,d,e,h,l) { __asm ld a, (__gfx_saved_w3) out (#0xE2), a - ei ret __endasm; } diff --git a/libbgi/common/bar.c b/libbgi/common/bar.c index 72595ed..f557c64 100644 --- a/libbgi/common/bar.c +++ b/libbgi/common/bar.c @@ -12,7 +12,15 @@ void bar(int left, int top, int right, int bottom) if (bottom < top) { t = top; top = bottom; bottom = t; } _bgi_begin(); - for (y = top; y <= bottom; y++) - _bgi_fill_span(left, right, y); + if (_bgi_fill_pattern == EMPTY_FILL) { + _gfx_rectfill256(left, top, right - left + 1, bottom - top + 1, _bgi_bg); return; + } + else if (_bgi_fill_pattern == SOLID_FILL || _bgi_fill_pattern == USER_FILL || _bgi_fill_pattern > CLOSE_DOT_FILL) { + _gfx_rectfill256(left, top, right - left + 1, bottom - top + 1, _bgi_fill_color); + return; + } else { + for (y = top; y <= bottom; y++) + _bgi_fill_span(left, right, y); + } _bgi_end(); } diff --git a/libbgi/common/getbkcolor.c b/libbgi/common/getbkcolor.c index 67e74cb..9ce4df6 100644 --- a/libbgi/common/getbkcolor.c +++ b/libbgi/common/getbkcolor.c @@ -1,7 +1,7 @@ /* getbkcolor — текущий фоновый цвет. */ #include "../_bgi.h" -int getbkcolor(void) +color_t getbkcolor(void) { return _bgi_bg; } diff --git a/libbgi/common/getcolor.c b/libbgi/common/getcolor.c index 65cf2c7..fc350a4 100644 --- a/libbgi/common/getcolor.c +++ b/libbgi/common/getcolor.c @@ -1,7 +1,7 @@ /* getcolor — текущий цвет рисования. */ #include "../_bgi.h" -int getcolor(void) +color_t getcolor(void) { return _bgi_fg; } diff --git a/libbgi/common/getmaxcolor.c b/libbgi/common/getmaxcolor.c index f011be4..680a016 100644 --- a/libbgi/common/getmaxcolor.c +++ b/libbgi/common/getmaxcolor.c @@ -1,7 +1,7 @@ /* getmaxcolor — максимальный индекс цвета (255 в 256-цветном режиме). */ #include "../_bgi.h" -int getmaxcolor(void) +color_t getmaxcolor(void) { return _bgi_maxcolor; } diff --git a/libbgi/common/setbkcolor.c b/libbgi/common/setbkcolor.c index 7c54f07..02141c8 100644 --- a/libbgi/common/setbkcolor.c +++ b/libbgi/common/setbkcolor.c @@ -1,7 +1,7 @@ /* setbkcolor — задать фоновый цвет (cleardevice, фон текста). */ #include "../_bgi.h" -void setbkcolor(int color) +void setbkcolor(color_t color) { _bgi_bg = (uint8_t)color; } diff --git a/libbgi/common/setcolor.c b/libbgi/common/setcolor.c index 61c3100..d63aaba 100644 --- a/libbgi/common/setcolor.c +++ b/libbgi/common/setcolor.c @@ -1,7 +1,7 @@ /* setcolor — задать текущий цвет рисования. */ #include "../_bgi.h" -void setcolor(int color) +void setcolor(color_t color) { _bgi_fg = (uint8_t)color; } diff --git a/libbgi/common/setfillstyle.c b/libbgi/common/setfillstyle.c index 3e9b1fa..3376f86 100644 --- a/libbgi/common/setfillstyle.c +++ b/libbgi/common/setfillstyle.c @@ -1,7 +1,7 @@ /* setfillstyle — задать паттерн и цвет заливки (bar/fillpoly/…). */ #include "../_bgi.h" -void setfillstyle(int pattern, int color) +void setfillstyle(int pattern, color_t color) { _bgi_fill_pattern = (uint8_t)pattern; _bgi_fill_color = (uint8_t)color; diff --git a/libbgi/include/graphics.h b/libbgi/include/graphics.h index c81397d..6833744 100644 --- a/libbgi/include/graphics.h +++ b/libbgi/include/graphics.h @@ -23,6 +23,8 @@ #ifndef GRAPHICS_H #define GRAPHICS_H +#include + /* ---- Стандартные EGA/VGA цвета (индексы палитры 0..15) ------------ */ enum { BLACK = 0, BLUE, GREEN, CYAN, RED, MAGENTA, BROWN, LIGHTGRAY, @@ -30,6 +32,10 @@ enum { YELLOW, WHITE }; +/* 8-битный цвет (индекс палитры). При смене формата достаточно поменять + * один typedef — все public-функции используют этот тип. */ +typedef uint8_t color_t; + /* ---- Коды graphresult() ------------------------------------------ */ #define grOk 0 #define grNoInitGraph (-1) @@ -55,17 +61,17 @@ void cleardevice(void); int getmaxx(void); /* 319 */ int getmaxy(void); /* 255 */ -int getmaxcolor(void); /* 255 */ +color_t getmaxcolor(void); /* 255 */ -void setcolor(int color); /* текущий цвет рисования */ -int getcolor(void); -void setbkcolor(int color); /* фоновый цвет (для cleardevice/текста) */ -int getbkcolor(void); +void setcolor(color_t color); /* текущий цвет рисования */ +color_t getcolor(void); +void setbkcolor(color_t color); /* фоновый цвет (для cleardevice/текста) */ +color_t getbkcolor(void); /* ---- Точки ------------------------------------------------------- */ -void putpixel(int x, int y, int color); -unsigned getpixel(int x, int y); +void putpixel(int x, int y, color_t color); +color_t getpixel(int x, int y); /* ---- Текущая позиция и линии ------------------------------------- */ @@ -116,9 +122,9 @@ enum { INTERLEAVE_FILL, WIDE_DOT_FILL, CLOSE_DOT_FILL, USER_FILL }; -struct fillsettingstype { int pattern; int color; }; +struct fillsettingstype { int pattern; color_t color; }; -void setfillstyle(int pattern, int color); +void setfillstyle(int pattern, color_t color); void getfillsettings(struct fillsettingstype *fillinfo); /* bar — залитый прямоугольник (текущий стиль заливки, без рамки). */ diff --git a/tests/bgi_img/Makefile b/tests/bgi_img/Makefile index bc36d30..b959f2e 100644 --- a/tests/bgi_img/Makefile +++ b/tests/bgi_img/Makefile @@ -1,4 +1,4 @@ PROJ_ROOT := $(abspath $(CURDIR)/../..) EXAMPLE := bgi_img -EXTRA_FLAGS := --gfx 256 +EXTRA_FLAGS ?= --gfx 256 include $(PROJ_ROOT)/app.mk diff --git a/tests/bgitest/Makefile b/tests/bgitest/Makefile index 4d148a8..3010db8 100644 --- a/tests/bgitest/Makefile +++ b/tests/bgitest/Makefile @@ -1,4 +1,4 @@ PROJ_ROOT := $(abspath $(CURDIR)/../..) EXAMPLE := bgitest -EXTRA_FLAGS := --gfx 256 +EXTRA_FLAGS ?= --gfx 256 include $(PROJ_ROOT)/app.mk diff --git a/tests/gfx_demo/gfx_demo.c b/tests/gfx_demo/gfx_demo.c index aa1945d..96f1286 100644 --- a/tests/gfx_demo/gfx_demo.c +++ b/tests/gfx_demo/gfx_demo.c @@ -20,6 +20,9 @@ #include /* GFX_WIDTH/HEIGHT, gfx_pal_load (mode-agnostic) */ #include + +extern void gfx_rectfill(int x, int y, int w, int h, uint8_t color); + static uint8_t palette_data[256 * 4]; static void wait_key(void) @@ -48,8 +51,11 @@ int main(void) setcolor(0xFF); line(1, 1, GFX_WIDTH - 2, 1); /* top */ line(1, GFX_HEIGHT - 2, GFX_WIDTH - 2, GFX_HEIGHT - 2); /* bottom */ - line(1, 2, 1, GFX_HEIGHT - 4); /* left */ - line(GFX_WIDTH - 2, 2, GFX_WIDTH - 2, GFX_HEIGHT - 4); /* right */ + line(1, 1, 1, GFX_HEIGHT - 2); /* left */ + line(GFX_WIDTH - 2, 1, GFX_WIDTH - 2, GFX_HEIGHT - 2); /* right */ + +// gfx_rectfill(20, 20, 40, 3, 120); +// gfx_rectfill(120, 20, 3, 40, 120); wait_key(); /* --- Stage 2: nested rectangles --------------------------------- */ @@ -57,11 +63,12 @@ int main(void) for (int i = 0; i < 16; i++) { setcolor((uint8_t)(0x40 + i * 8)); rectangle(i * 10, i * 8, - GFX_WIDTH - i * 20, - GFX_HEIGHT - i * 16); + GFX_WIDTH - i * 10, + GFX_HEIGHT - i * 8); } wait_key(); + /* --- Stage 3: filled-rect colour bars (bar = залитый прямоугольник) */ setbkcolor(0); cleardevice(); for (int i = 0; i < 16; i++) { diff --git a/toolchain/mame_interactive.py b/toolchain/mame_interactive.py index b42067d..a7446f4 100644 --- a/toolchain/mame_interactive.py +++ b/toolchain/mame_interactive.py @@ -111,6 +111,17 @@ CADENCE = 0.14 # шаг между символами HOLD = 0.06 # длительность удержания клавиши +def basename_83(name): + """Обрезать имя файла до формата 8.3, как в FAT12 корневом каталоге.""" + name = name.upper() + if '.' in name: + base, ext = name.rsplit('.', 1) + base = base[:8].ljust(8, ' ') + ext = ext[:3].ljust(3, ' ') + return f"{base}.{ext}" + return name[:8].ljust(8, ' ') + '.EXE' + + def resolve(ch): """char → (tag, mask, need_shift). Заглавные буквы = shift+строчная.""" if ch in PHYS: @@ -238,7 +249,7 @@ def main(): if not os.path.isfile(exe_path): print(f"ОШИБКА: не найден {exe_path}") sys.exit(1) - exe_name = os.path.basename(exe_path).lower() + exe_name = basename_83(os.path.basename(exe_path)).upper() steps.append((args.launch_at, f"a:\\{exe_name}\n")) if not steps: print("ОШИБКА: укажите exe для запуска или хотя бы один --step")