Compare commits
19 Commits
c71e249a4e
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
| bd116d7361 | |||
| 858e5755ad | |||
| f87b52bb7f | |||
| 07c4004bd9 | |||
| 6992c1436e | |||
| 07c398a560 | |||
| 4bed9d3f3f | |||
| 463a058f56 | |||
| 8abc1d6c16 | |||
| 982af12710 | |||
| 394ee3a2cd | |||
| 47c9cd326a | |||
| ca5f30b332 | |||
| 0ad0559fc8 | |||
| 035d93ab51 | |||
| 0dedc4dac8 | |||
| 737c974400 | |||
| b851e22fa6 | |||
| 527d4a6a18 |
+19
-1
@@ -9,7 +9,8 @@ build/
|
|||||||
# sprinter-cc per-example intermediate directory
|
# sprinter-cc per-example intermediate directory
|
||||||
.sprinter-cc-*/
|
.sprinter-cc-*/
|
||||||
|
|
||||||
# Per-example final/intermediate outputs landing alongside the source
|
# Per-program final/intermediate outputs landing alongside the source
|
||||||
|
# (real apps under examples/ and libc feature tests under tests/).
|
||||||
examples/*/*.exe
|
examples/*/*.exe
|
||||||
examples/*/*.asm
|
examples/*/*.asm
|
||||||
examples/*/*.lst
|
examples/*/*.lst
|
||||||
@@ -23,6 +24,22 @@ examples/*/*.cdb
|
|||||||
examples/*/*.mem
|
examples/*/*.mem
|
||||||
examples/*/*.rst
|
examples/*/*.rst
|
||||||
|
|
||||||
|
# Temporary build directory for floppy disk image preparation
|
||||||
|
examples/*/.disk_tmp/
|
||||||
|
|
||||||
|
tests/*/*.exe
|
||||||
|
tests/*/*.asm
|
||||||
|
tests/*/*.lst
|
||||||
|
tests/*/*.lk
|
||||||
|
tests/*/*.ihx
|
||||||
|
tests/*/*.noi
|
||||||
|
tests/*/*.sym
|
||||||
|
tests/*/*.map
|
||||||
|
tests/*/*.rel
|
||||||
|
tests/*/*.cdb
|
||||||
|
tests/*/*.mem
|
||||||
|
tests/*/*.rst
|
||||||
|
|
||||||
# libc archive (built from libc/, see lib/Makefile)
|
# libc archive (built from libc/, see lib/Makefile)
|
||||||
lib/*.lib
|
lib/*.lib
|
||||||
|
|
||||||
@@ -68,6 +85,7 @@ mame/
|
|||||||
# IDEs
|
# IDEs
|
||||||
.vscode/
|
.vscode/
|
||||||
.idea/
|
.idea/
|
||||||
|
.kilo/
|
||||||
|
|
||||||
# Claude Code local settings (per-machine, not for the repo)
|
# Claude Code local settings (per-machine, not for the repo)
|
||||||
.claude/
|
.claude/
|
||||||
|
|||||||
@@ -1,58 +1,41 @@
|
|||||||
# Sprinter C Compiler — top-level Makefile
|
# Sprinter C Compiler — top-level Makefile
|
||||||
#
|
#
|
||||||
# make build host tools, libc archive, and all examples
|
# make build host tools, libc archive, all tests, all apps
|
||||||
# make tools build only host tools (mkexe)
|
# make tools build only host tools (mkexe)
|
||||||
# make lib build lib/sprinter.lib (libc archive used by sprinter-cc)
|
# make lib build lib/sprinter.lib (libc archive used by sprinter-cc)
|
||||||
# make examples build all examples
|
# make tests build all libc feature tests under tests/
|
||||||
# make floppy package every example + test files into mame/v306/IMG/mc.img
|
# make examples build all real applications under examples/
|
||||||
|
# make floppy package every .exe + test fixtures into mame/v306/IMG/mc.img
|
||||||
# make check run mkexe unit tests
|
# make check run mkexe unit tests
|
||||||
# make clean remove all build artefacts
|
# make clean remove all build artefacts
|
||||||
# make sdcc download/extract vendored SDCC
|
# make sdcc download/extract vendored SDCC
|
||||||
#
|
#
|
||||||
# Most heavy lifting is delegated to sub-Makefiles.
|
# Most heavy lifting is delegated to sub-Makefiles.
|
||||||
|
|
||||||
EXAMPLES := hello banked bankedbg strtest cat seek malloc mem_test argv errno rt_test openenv ls conio attrprob timedir mouse banklocl stdlib assrtest ptime stattest filetest gfx_demo gfx_d16 gfx_text gfx_mous
|
# Small libc-feature tests (one program per .c-language feature or libc API).
|
||||||
|
TESTS := hello banked bankedbg strtest cat seek malloc mem_test argv errno \
|
||||||
|
rt_test openenv ls conio attrprob timedir mouse banklocl stdlib \
|
||||||
|
assrtest ptime stattest filetest gfx_demo gfx_d16 gfx_text gfx_mous
|
||||||
|
|
||||||
|
# Larger end-user applications under examples/.
|
||||||
|
APPS := mdview
|
||||||
|
|
||||||
MAME_DIR := mame/v306
|
MAME_DIR := mame/v306
|
||||||
FLOPPY_IMG := $(MAME_DIR)/IMG/mc.img
|
FLOPPY_IMG := $(MAME_DIR)/IMG/mc.img
|
||||||
MAKE_DISK := $(MAME_DIR)/make_disk.py
|
MAKE_DISK := $(MAME_DIR)/make_disk.py
|
||||||
|
|
||||||
EXE_FILES := \
|
TEST_EXES := $(foreach t,$(TESTS),tests/$(t)/$(t).exe)
|
||||||
examples/hello/hello.exe \
|
APP_EXES := $(foreach a,$(APPS),examples/$(a)/$(a).exe)
|
||||||
examples/banked/banked.exe \
|
ALL_EXES := $(TEST_EXES) $(APP_EXES)
|
||||||
examples/bankedbg/bankedbg.exe \
|
|
||||||
examples/strtest/strtest.exe \
|
|
||||||
examples/cat/cat.exe \
|
|
||||||
examples/seek/seek.exe \
|
|
||||||
examples/malloc/malloc.exe \
|
|
||||||
examples/mem_test/mem_test.exe \
|
|
||||||
examples/argv/argv.exe \
|
|
||||||
examples/errno/errno.exe \
|
|
||||||
examples/rt_test/rt_test.exe \
|
|
||||||
examples/openenv/openenv.exe \
|
|
||||||
examples/ls/ls.exe \
|
|
||||||
examples/conio/conio.exe \
|
|
||||||
examples/attrprob/attrprob.exe \
|
|
||||||
examples/timedir/timedir.exe \
|
|
||||||
examples/mouse/mouse.exe \
|
|
||||||
examples/banklocl/banklocl.exe \
|
|
||||||
examples/stdlib/stdlib.exe \
|
|
||||||
examples/assrtest/assrtest.exe \
|
|
||||||
examples/ptime/ptime.exe \
|
|
||||||
examples/stattest/stattest.exe \
|
|
||||||
examples/filetest/filetest.exe \
|
|
||||||
examples/gfx_demo/gfx_demo.exe \
|
|
||||||
examples/gfx_d16/gfx_d16.exe \
|
|
||||||
examples/gfx_text/gfx_text.exe \
|
|
||||||
examples/gfx_mous/gfx_mous.exe
|
|
||||||
|
|
||||||
DATA_FILES := \
|
DATA_FILES := \
|
||||||
examples/cat/test.txt \
|
tests/cat/test.txt \
|
||||||
examples/seek/big.txt
|
tests/seek/big.txt \
|
||||||
|
examples/mdview/SAMPLE.MD
|
||||||
|
|
||||||
.PHONY: all tools lib examples check clean sdcc floppy $(EXAMPLES)
|
.PHONY: all tools lib tests examples check clean sdcc floppy $(TESTS) $(APPS)
|
||||||
|
|
||||||
all: tools lib examples
|
all: tools lib tests examples
|
||||||
|
|
||||||
tools:
|
tools:
|
||||||
$(MAKE) -C toolchain/mkexe
|
$(MAKE) -C toolchain/mkexe
|
||||||
@@ -63,18 +46,22 @@ lib:
|
|||||||
check: tools
|
check: tools
|
||||||
$(MAKE) -C toolchain/mkexe check
|
$(MAKE) -C toolchain/mkexe check
|
||||||
|
|
||||||
examples: $(EXAMPLES)
|
tests: $(TESTS)
|
||||||
|
examples: $(APPS)
|
||||||
|
|
||||||
$(EXAMPLES): tools lib
|
$(TESTS): tools lib
|
||||||
|
$(MAKE) -C tests/$@
|
||||||
|
|
||||||
|
$(APPS): tools lib
|
||||||
$(MAKE) -C examples/$@
|
$(MAKE) -C examples/$@
|
||||||
|
|
||||||
# Generate big.txt if missing (gen_bigfile.py creates 100 KB marker file).
|
# Generate big.txt if missing (gen_bigfile.py creates 100 KB marker file).
|
||||||
examples/seek/big.txt:
|
tests/seek/big.txt:
|
||||||
cd examples/seek && python3 gen_bigfile.py big.txt 102400
|
cd tests/seek && python3 gen_bigfile.py big.txt 102400
|
||||||
|
|
||||||
# Re-pack the MAME floppy image with every built example + needed data files.
|
# Re-pack the MAME floppy image with every built exe + needed data files.
|
||||||
floppy: examples examples/seek/big.txt
|
floppy: tests examples tests/seek/big.txt
|
||||||
python3 $(MAKE_DISK) $(FLOPPY_IMG) $(EXE_FILES) $(DATA_FILES)
|
python3 $(MAKE_DISK) $(FLOPPY_IMG) $(ALL_EXES) $(DATA_FILES)
|
||||||
@echo
|
@echo
|
||||||
@echo "Floppy ready: $(FLOPPY_IMG)"
|
@echo "Floppy ready: $(FLOPPY_IMG)"
|
||||||
@echo "Run: cd $(MAME_DIR) && ./run_mame.sh"
|
@echo "Run: cd $(MAME_DIR) && ./run_mame.sh"
|
||||||
@@ -82,7 +69,8 @@ floppy: examples examples/seek/big.txt
|
|||||||
clean:
|
clean:
|
||||||
$(MAKE) -C toolchain/mkexe clean
|
$(MAKE) -C toolchain/mkexe clean
|
||||||
$(MAKE) -C lib clean
|
$(MAKE) -C lib clean
|
||||||
@for e in $(EXAMPLES); do $(MAKE) -C examples/$$e clean; done
|
@for t in $(TESTS); do $(MAKE) -C tests/$$t clean; done
|
||||||
|
@for a in $(APPS); do $(MAKE) -C examples/$$a clean; done
|
||||||
|
|
||||||
sdcc:
|
sdcc:
|
||||||
bash third_party/setup-sdcc.sh
|
bash third_party/setup-sdcc.sh
|
||||||
|
|||||||
@@ -51,13 +51,13 @@ Sprinter's address space is four 16 KB windows (W0 / W1 / W2 / W3). DSS allocat
|
|||||||
pages by program size — small programs get only one page. Pick a memory mode based
|
pages by program size — small programs get only one page. Pick a memory mode based
|
||||||
on what your program needs:
|
on what your program needs:
|
||||||
|
|
||||||
| Mode | Code lives in | Banking | Use when |
|
| Mode | Code lives in | Banking | Use when | Note |
|
||||||
|---|---|---|---|
|
|---|---|---|---|---|
|
||||||
| `tiny` (default) | W2 (0x8100+) | no | code+data < 14 KB |
|
| `tiny` (default) | W2 (0x8100+) | no | code+data < 14 KB | |
|
||||||
| `small` | W1 (0x4100+) | no | code+data < 30 KB |
|
| `small` | W1-W2 (0x4100+) | no | code+data < 30 KB | |
|
||||||
| `big` | W2 + W1 banks | yes (W1) | tiny + extra code modules |
|
| `big` | W2 + W1 banking | yes (W1) | tiny + extra code modules | |
|
||||||
| `huge` | W1 + W3 banks | yes (W3) | small + extra code modules |
|
| `huge` | W1-W2 + W3 banking | yes (W3) | small + extra code modules | |
|
||||||
| `manual` | user-specified | optional | special layouts |
|
| `manual` | user-specified | optional | special layouts | Not implemented |
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
sprinter-cc --memory small -o big.exe bigprog.c
|
sprinter-cc --memory small -o big.exe bigprog.c
|
||||||
@@ -157,20 +157,24 @@ sprinter-cc -o foo.exe foo.c [more.c ...] [options]
|
|||||||
|
|
||||||
What works in v1.0:
|
What works in v1.0:
|
||||||
* Compile / link / pack to SprintEXE — verified on all 27 examples
|
* Compile / link / pack to SprintEXE — verified on all 27 examples
|
||||||
* All five memory modes (tiny / small / big / huge / manual)
|
* Four memory modes (tiny / small / big / huge)
|
||||||
* Graphics (both modes) with accelerator
|
* Graphics (both modes) with accelerator
|
||||||
* Mouse (text + graphics cursor)
|
* Mouse (text + graphics cursor)
|
||||||
* File I/O, directories, environment, time
|
* File I/O, directories, environment, time
|
||||||
* All headers listed above
|
* All headers listed above
|
||||||
|
|
||||||
Deferred to v2.0 (see `docs/TODO.md`):
|
Deferred to v2.0 (see `docs/TODO.md`):
|
||||||
* **IM2 interrupt handlers** — research complete (`docs/im2_isr_design.md`),
|
|
||||||
implementation scheduled for v2
|
|
||||||
* **Turbo-C-style BGI graphics API** — `initgraph` / `setcolor` / `circle` /
|
* **Turbo-C-style BGI graphics API** — `initgraph` / `setcolor` / `circle` /
|
||||||
`getimage` / `putimage` / etc. on top of our `gfx_*` primitives
|
`getimage` / `putimage` / etc. on top of our `gfx_*` primitives
|
||||||
* **Audio API** (AY-3-8910 + COVOX) — requires IM2
|
|
||||||
* **ISA-8 slot drivers** — requires IM2
|
|
||||||
* Remaining Solid-C compatibility gaps (Phase 2/3) — see `docs/solid_c_compatibility.md`
|
* Remaining Solid-C compatibility gaps (Phase 2/3) — see `docs/solid_c_compatibility.md`
|
||||||
|
* Manual memory mode
|
||||||
|
* Rewrite FILE\* stream API (current implementation is very primitive and doesn't use buffers)
|
||||||
|
|
||||||
|
Deferred to v3.0:
|
||||||
|
* **IM2 interrupt handlers** — research complete (`docs/im2_isr_design.md`),
|
||||||
|
implementation scheduled for v3
|
||||||
|
* **Audio API** (AY-3-8910 + COVOX) — requires IM2
|
||||||
|
* **ISA-8 slot drivers** — requires IM2 (???)
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
@@ -213,7 +217,6 @@ This repository contains:
|
|||||||
|
|
||||||
* **Sprinter / Peters Plus** — Иван Мак, Дмитрий Паринов and the original team
|
* **Sprinter / Peters Plus** — Иван Мак, Дмитрий Паринов and the original team
|
||||||
* **SDCC** — for the underlying Z80 compiler
|
* **SDCC** — for the underlying Z80 compiler
|
||||||
* **z88dk +pps** — Дмитрий M. for paving the way with the first Sprinter target
|
|
||||||
* **MAME** — for the Sprinter Sp2000 emulation
|
* **MAME** — for the Sprinter Sp2000 emulation
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
# example.mk — shared Makefile fragment for simple Sprinter ESTEX examples.
|
# app.mk — shared Makefile fragment for any standalone Sprinter ESTEX
|
||||||
|
# program — used both by libc feature tests under tests/ and by real
|
||||||
|
# applications under examples/.
|
||||||
#
|
#
|
||||||
# Usage in an example's Makefile:
|
# Usage in a per-program Makefile:
|
||||||
#
|
#
|
||||||
# PROJ_ROOT := $(abspath $(CURDIR)/../..)
|
# PROJ_ROOT := $(abspath $(CURDIR)/../..)
|
||||||
# EXAMPLE := my_example # base name (matches my_example.c)
|
# EXAMPLE := my_program # base name (matches my_program.c)
|
||||||
#
|
#
|
||||||
# # Optional overrides (any combination):
|
# # Optional overrides (any combination):
|
||||||
# # MEMORY := small # tiny | small | big | huge | manual
|
# # MEMORY := small # tiny | small | big | huge | manual
|
||||||
@@ -12,7 +14,7 @@
|
|||||||
# # EXTRA_FLAGS := --crt0=minimal # passed through to sprinter-cc
|
# # EXTRA_FLAGS := --crt0=minimal # passed through to sprinter-cc
|
||||||
# # EXTRA_DATA := test.txt # extra files to add to `make floppy`
|
# # EXTRA_DATA := test.txt # extra files to add to `make floppy`
|
||||||
#
|
#
|
||||||
# include $(PROJ_ROOT)/examples/example.mk
|
# include $(PROJ_ROOT)/app.mk
|
||||||
#
|
#
|
||||||
# Pipeline (all driven by sprinter-cc):
|
# Pipeline (all driven by sprinter-cc):
|
||||||
# crt0 + EXAMPLE.c + EXTRA_SRCS --sdcc--> .ihx
|
# crt0 + EXAMPLE.c + EXTRA_SRCS --sdcc--> .ihx
|
||||||
@@ -61,9 +63,9 @@ $(LIB):
|
|||||||
clean:
|
clean:
|
||||||
rm -rf .sprinter-cc-* $(EXAMPLE).exe
|
rm -rf .sprinter-cc-* $(EXAMPLE).exe
|
||||||
|
|
||||||
# `make floppy` packs ONLY this example (+ optional EXTRA_DATA files) into
|
# `make floppy` packs ONLY this program (+ optional EXTRA_DATA files) into
|
||||||
# the MAME floppy image, replacing whatever was there. Handy for trying a
|
# the MAME floppy image, replacing whatever was there. Handy for trying a
|
||||||
# single program without rebuilding all 27 examples.
|
# single program without rebuilding everything.
|
||||||
floppy: $(EXAMPLE).exe
|
floppy: $(EXAMPLE).exe
|
||||||
python3 $(MAKE_DISK) $(FLOPPY_IMG) $(EXAMPLE).exe $(EXTRA_DATA)
|
python3 $(MAKE_DISK) $(FLOPPY_IMG) $(EXAMPLE).exe $(EXTRA_DATA)
|
||||||
@echo
|
@echo
|
||||||
+26
-6
@@ -186,9 +186,15 @@ ENTRY_ADDR="${ENTRY_ADDR:-$LOAD_ADDR}"
|
|||||||
|
|
||||||
# Pick crt0 source. Memory mode drives default crt0 selection; explicit
|
# Pick crt0 source. Memory mode drives default crt0 selection; explicit
|
||||||
# --crt0=TYPE still wins (e.g. --crt0=minimal for tiny w/o argv).
|
# --crt0=TYPE still wins (e.g. --crt0=minimal for tiny w/o argv).
|
||||||
|
#
|
||||||
|
# big/huge ALWAYS use crt0_banked, even without explicit --bank flags:
|
||||||
|
# big — banks in W1 (BANK_W1=1 prepended) — code stays in W2 = tiny layout
|
||||||
|
# huge — banks in W3 — code in W1 = small layout, crt0_banked auto-detects W2
|
||||||
|
# crt0_banked also exports startup-info symbols (estex_file_handle etc.).
|
||||||
case "$MEMORY_MODE" in
|
case "$MEMORY_MODE" in
|
||||||
small|huge) DEFAULT_CRT0="small";;
|
small) DEFAULT_CRT0="small";;
|
||||||
*) DEFAULT_CRT0="default";;
|
big|huge) DEFAULT_CRT0="banked";;
|
||||||
|
*) DEFAULT_CRT0="default";; # tiny, manual
|
||||||
esac
|
esac
|
||||||
if [[ "$CRT0_TYPE" == "default" && "$DEFAULT_CRT0" != "default" ]]; then
|
if [[ "$CRT0_TYPE" == "default" && "$DEFAULT_CRT0" != "default" ]]; then
|
||||||
CRT0_TYPE="$DEFAULT_CRT0"
|
CRT0_TYPE="$DEFAULT_CRT0"
|
||||||
@@ -272,20 +278,33 @@ for src in "${SOURCES[@]}"; do
|
|||||||
USER_RELS+=("$rel")
|
USER_RELS+=("$rel")
|
||||||
done
|
done
|
||||||
|
|
||||||
# 3. bank sources + trampoline (bank.s). In BIG mode banks live in W1 at
|
# 3. bank infrastructure — required whenever crt0_banked is in play.
|
||||||
# low16 = 0x4000; in HUGE / default mode they live in W3 at low16 = 0xC000.
|
# Always assemble bank.s for _bank_pages + the bcall/bjump trampolines.
|
||||||
|
# If the user did not pass any --bank, generate a tiny stub providing
|
||||||
|
# n_banks = 0 (crt0_banked.s imports this symbol unconditionally).
|
||||||
|
# In BIG mode banks live in W1 at low16 = 0x4000; in HUGE / default
|
||||||
|
# mode they live in W3 at low16 = 0xC000.
|
||||||
BANK_RELS=()
|
BANK_RELS=()
|
||||||
BANK_LD_FLAGS=()
|
BANK_LD_FLAGS=()
|
||||||
if [[ ${#BANK_SPECS[@]} -gt 0 ]]; then
|
if [[ "$CRT0_TYPE" == "banked" ]]; then
|
||||||
if [[ $BANK_W1 -eq 1 ]]; then
|
if [[ $BANK_W1 -eq 1 ]]; then
|
||||||
BANK_LOW16=0x4000
|
BANK_LOW16=0x4000
|
||||||
else
|
else
|
||||||
BANK_LOW16=0xC000
|
BANK_LOW16=0xC000
|
||||||
fi
|
fi
|
||||||
# Trampoline — depends on BANK_W1 so we assemble it here, not from the lib.
|
# Trampoline + _bank_pages table — depend on BANK_W1, so assemble per build.
|
||||||
BANK_TRAMP_REL="$WORK/bank_trampoline.rel"
|
BANK_TRAMP_REL="$WORK/bank_trampoline.rel"
|
||||||
asm_runtime "$RUNTIME/bank.s" "$BANK_TRAMP_REL"
|
asm_runtime "$RUNTIME/bank.s" "$BANK_TRAMP_REL"
|
||||||
BANK_RELS+=("$BANK_TRAMP_REL")
|
BANK_RELS+=("$BANK_TRAMP_REL")
|
||||||
|
|
||||||
|
if [[ ${#BANK_SPECS[@]} -eq 0 ]]; then
|
||||||
|
# User has no banks — supply n_banks=0 so crt0_banked links.
|
||||||
|
STUB_C="$WORK/_n_banks_stub.c"
|
||||||
|
STUB_REL="$WORK/_n_banks_stub.rel"
|
||||||
|
echo "const unsigned char n_banks = 0;" > "$STUB_C"
|
||||||
|
run "$SDCC" "${CC_FLAGS[@]}" -c -o "$STUB_REL" "$STUB_C"
|
||||||
|
BANK_RELS+=("$STUB_REL")
|
||||||
|
else
|
||||||
for spec in "${BANK_SPECS[@]}"; do
|
for spec in "${BANK_SPECS[@]}"; do
|
||||||
bank_n="${spec%%=*}"
|
bank_n="${spec%%=*}"
|
||||||
bank_src="${spec#*=}"
|
bank_src="${spec#*=}"
|
||||||
@@ -299,6 +318,7 @@ if [[ ${#BANK_SPECS[@]} -gt 0 ]]; then
|
|||||||
BANK_LD_FLAGS+=("-Wl-b_BANK${bank_n}=${addr}")
|
BANK_LD_FLAGS+=("-Wl-b_BANK${bank_n}=${addr}")
|
||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
# 4. link → .ihx
|
# 4. link → .ihx
|
||||||
IHX="$WORK/$(basename "$OUT" .exe).ihx"
|
IHX="$WORK/$(basename "$OUT" .exe).ihx"
|
||||||
|
|||||||
@@ -255,6 +255,43 @@ User-задаваемые ISR через Z80 IM 2 mode. Нужны для:
|
|||||||
|
|
||||||
### Прочие крупные пункты для v2
|
### Прочие крупные пункты для v2
|
||||||
|
|
||||||
|
- [ ] **FILE API rewrite — buffered streams** — текущая реализация в
|
||||||
|
`libc/stdio/file.c` это provisional unbuffered shim (каждый fputc/fgetc
|
||||||
|
= один read/write syscall). Нужна полноценная buffered семантика
|
||||||
|
как в Solid-C:
|
||||||
|
|
||||||
|
```c
|
||||||
|
typedef struct {
|
||||||
|
uint flags; // +0..1 file status flags
|
||||||
|
int level; // +2..3 empty/fill level of buffer
|
||||||
|
char *curp; // +4..5 current active pointer
|
||||||
|
int fd; // +6..7 underlying low-level fd
|
||||||
|
char *buffer; // +8..9 data transfer buffer
|
||||||
|
char hold; // +10 ungetc byte if no buffer
|
||||||
|
short token; // +11..12 reserved
|
||||||
|
char dummy; // +13 reserved
|
||||||
|
} FILE;
|
||||||
|
```
|
||||||
|
|
||||||
|
stdin/stdout/stderr — fd-маркеры `0 / -1 / -2`. Отрицательные для
|
||||||
|
stdout/stderr выбраны намеренно: ESTEX OPEN может вернуть positive
|
||||||
|
small fd (1, 2, …) для обычного файла → если бы stdout=1, реальный
|
||||||
|
fd=1 сталкивался бы с идентификатором. fd=0 для stdin безопасно
|
||||||
|
(ESTEX 0 не возвращает). Сами fd не передаются в syscall'ы —
|
||||||
|
диспетчеризация по флагам `_F_CONIN/_F_CONOUT`.
|
||||||
|
|
||||||
|
Принтер-потоки (stdaux/stdprn) НЕ реализуем — Sprinter принтерной
|
||||||
|
API не имеет.
|
||||||
|
|
||||||
|
Альтернатива — взять реализацию из third_party/solid-c (sources в
|
||||||
|
`SRC/CLIB/`); там есть готовый buffered FILE + fopen/fread/fwrite/
|
||||||
|
fseek/setvbuf и т.д. Адаптировать к нашим open/read/write/lseek.
|
||||||
|
|
||||||
|
При rewrite заодно решить deferred issues stdio-review:
|
||||||
|
- `fwrite` short-write должен ставить `_F_ERROR`
|
||||||
|
- `fgets(buf, 1, fp)` — стандарт говорит "empty string", мы вернули NULL
|
||||||
|
- `mode_to_flags` — break-out на '+' (cosmetic)
|
||||||
|
|
||||||
- [ ] **Audio API** — AY-3-8910 + COVOX через прерывания (требует IM2)
|
- [ ] **Audio API** — AY-3-8910 + COVOX через прерывания (требует IM2)
|
||||||
- [ ] **ISA-8 slot support** — ZX-Bus карты (sound, network, etc.) — требует IM2 + чтения portов
|
- [ ] **ISA-8 slot support** — ZX-Bus карты (sound, network, etc.) — требует IM2 + чтения portов
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,188 @@
|
|||||||
|
# MDView: описание работы и тестовые паттерны
|
||||||
|
## Назначение
|
||||||
|
`mdview` — консольный просмотрщик Markdown для текстового режима 80x32.
|
||||||
|
Программа загружает `.MD`-файл, индексирует его на экранные сегменты и рендерит содержимое с учётом переносов, стилей и специальных блоков.
|
||||||
|
|
||||||
|
## Как работает программа
|
||||||
|
### 1) Загрузка файла
|
||||||
|
- На старте вызывается `load_file(path)`.
|
||||||
|
- Если путь не передан, используется `SAMPLE.MD`.
|
||||||
|
- При ошибках загрузки программа показывает причину и завершает работу после нажатия клавиши.
|
||||||
|
|
||||||
|
### 2) Индексация (`index_lines`)
|
||||||
|
- Файл разбирается в массив сегментов для быстрого рендера.
|
||||||
|
- Поддерживаются ключевые типы строк:
|
||||||
|
- заголовки `#..####`,
|
||||||
|
- маркированные/нумерованные списки,
|
||||||
|
- цитаты `>`,
|
||||||
|
- горизонтальные разделители,
|
||||||
|
- fenced code-блоки,
|
||||||
|
- таблицы (nowrap-режим),
|
||||||
|
- обычные параграфы.
|
||||||
|
- Для list/quote/plain используется общая логика сканирования с режимами склейки строк.
|
||||||
|
|
||||||
|
### 3) Рендер (`render_line`, `render_viewport`)
|
||||||
|
- Для каждой видимой строки рисуется префикс (маркер списка, цитаты и т.д.) и текст.
|
||||||
|
- Поддерживаются inline-стили:
|
||||||
|
- `**жирный**`,
|
||||||
|
- `*курсив*`,
|
||||||
|
- `_подчёркнутый_`,
|
||||||
|
- `~~зачёркнутый~~`,
|
||||||
|
- `` `code` ``.
|
||||||
|
- Табы расширяются до фиксированного шага.
|
||||||
|
- Для длинных nowrap-строк применяется горизонтальный сдвиг и индикаторы `<`/`>`.
|
||||||
|
|
||||||
|
### 4) Навигация
|
||||||
|
- `Up/Down` — прокрутка на 1 строку.
|
||||||
|
- `PgUp/PgDn` — прокрутка на экран.
|
||||||
|
- `Home/End` — начало/конец документа.
|
||||||
|
- `Left/Right` — горизонтальная прокрутка nowrap-контента.
|
||||||
|
- `Esc` или `F10` — выход.
|
||||||
|
|
||||||
|
## Сборка и запуск
|
||||||
|
```bash
|
||||||
|
make -C examples/mdview
|
||||||
|
```
|
||||||
|
|
||||||
|
Запуск на целевой системе:
|
||||||
|
```bash
|
||||||
|
mdview.exe <путь_к_файлу.md>
|
||||||
|
```
|
||||||
|
|
||||||
|
Если путь не указан, открывается `SAMPLE.MD`.
|
||||||
|
|
||||||
|
## Тестовые паттерны
|
||||||
|
Ниже набор паттернов для ручной регрессии.
|
||||||
|
|
||||||
|
### P01 — Базовый smoke-тест открытия
|
||||||
|
Вход:
|
||||||
|
- валидный markdown-файл среднего размера.
|
||||||
|
|
||||||
|
Ожидается:
|
||||||
|
- файл открывается без ошибок;
|
||||||
|
- статус-бар и меню отображаются корректно;
|
||||||
|
- прокрутка работает.
|
||||||
|
|
||||||
|
### P02 — Ошибка открытия файла
|
||||||
|
Вход:
|
||||||
|
- несуществующий путь к файлу.
|
||||||
|
|
||||||
|
Ожидается:
|
||||||
|
- сообщение `mdview: cannot load file`;
|
||||||
|
- корректный код причины (`open failed` и т.п.);
|
||||||
|
- ожидание клавиши перед выходом.
|
||||||
|
|
||||||
|
### P03 — Пустой файл
|
||||||
|
Вход:
|
||||||
|
- пустой `.md`.
|
||||||
|
|
||||||
|
Ожидается:
|
||||||
|
- сообщение `mdview: empty file`;
|
||||||
|
- корректное завершение после нажатия клавиши.
|
||||||
|
|
||||||
|
### P04 — Обычный параграф и переносы
|
||||||
|
Шаблон:
|
||||||
|
```md
|
||||||
|
Это длинный абзац для проверки переноса строк в обычном тексте.
|
||||||
|
Вторая строка должна мягко склеиться с первой.
|
||||||
|
```
|
||||||
|
|
||||||
|
Ожидается:
|
||||||
|
- строки склеиваются как единый параграф;
|
||||||
|
- переносы происходят по ширине экрана без потери символов.
|
||||||
|
|
||||||
|
### P05 — Hard break в параграфе
|
||||||
|
Шаблон:
|
||||||
|
```md
|
||||||
|
Первая строка с двумя пробелами в конце.
|
||||||
|
Вторая строка после hard break.
|
||||||
|
```
|
||||||
|
|
||||||
|
Ожидается:
|
||||||
|
- между строками сохраняется принудительный разрыв;
|
||||||
|
- inline-стиль не ломается.
|
||||||
|
|
||||||
|
### P06 — Многострочный список с lazy continuation
|
||||||
|
Шаблон:
|
||||||
|
```md
|
||||||
|
- Пункт списка, который продолжается
|
||||||
|
на следующей строке с отступом.
|
||||||
|
```
|
||||||
|
|
||||||
|
Ожидается:
|
||||||
|
- continuation-строка склеивается с пунктом через один пробел;
|
||||||
|
- лишние ведущие отступы continuation не попадают в итоговый текст;
|
||||||
|
- переносы не ломают маркер списка.
|
||||||
|
|
||||||
|
### P07 — Многострочная цитата
|
||||||
|
Шаблон:
|
||||||
|
```md
|
||||||
|
> Первая строка цитаты
|
||||||
|
> Вторая строка цитаты
|
||||||
|
> Третья строка цитаты
|
||||||
|
```
|
||||||
|
|
||||||
|
Ожидается:
|
||||||
|
- для продолжений корректно повторяется quote-префикс;
|
||||||
|
- сырой символ `>` из исходника не «просачивается» в середину текста.
|
||||||
|
|
||||||
|
### P08 — Пустая quoted-строка как разделитель
|
||||||
|
Шаблон:
|
||||||
|
```md
|
||||||
|
> Первый параграф цитаты
|
||||||
|
>
|
||||||
|
> Второй параграф цитаты
|
||||||
|
```
|
||||||
|
|
||||||
|
Ожидается:
|
||||||
|
- пустая quoted-строка разделяет два параграфа;
|
||||||
|
- рендер не объединяет их в один поток.
|
||||||
|
|
||||||
|
### P09 — Таблица в nowrap-режиме
|
||||||
|
Шаблон:
|
||||||
|
```md
|
||||||
|
| col1 | col2 | col3 |
|
||||||
|
|------|------|------|
|
||||||
|
| очень длинное значение | очень длинное значение | очень длинное значение |
|
||||||
|
```
|
||||||
|
|
||||||
|
Ожидается:
|
||||||
|
- строка таблицы не переносится по словам;
|
||||||
|
- работает горизонтальный сдвиг `Left/Right`;
|
||||||
|
- индикаторы `<`/`>` показывают скрытый контент.
|
||||||
|
|
||||||
|
### P10 — Fenced code-block
|
||||||
|
Шаблон:
|
||||||
|
~~~md
|
||||||
|
```text
|
||||||
|
**это не жирный**
|
||||||
|
_это не подчёркнутый_
|
||||||
|
```
|
||||||
|
~~~
|
||||||
|
|
||||||
|
Ожидается:
|
||||||
|
- внутри code-блока inline-разметка не применяется;
|
||||||
|
- текст отображается как литерал.
|
||||||
|
|
||||||
|
### P11 — Inline-стили на обычном тексте
|
||||||
|
Шаблон:
|
||||||
|
```md
|
||||||
|
Текст с **жирным**, *курсивом*, _подчёркнутым_, ~~зачёркнутым~~ и `code`.
|
||||||
|
```
|
||||||
|
|
||||||
|
Ожидается:
|
||||||
|
- все перечисленные стили рендерятся корректно;
|
||||||
|
- литеральные случаи вроде `2 * 3` не превращаются в стиль.
|
||||||
|
|
||||||
|
### P12 — Статус-бар и границы прокрутки
|
||||||
|
Вход:
|
||||||
|
- документ на несколько экранов.
|
||||||
|
|
||||||
|
Ожидается:
|
||||||
|
- корректные `L x-y / total` и `%`;
|
||||||
|
- `Home/End` приводят к ожидаемым позициям;
|
||||||
|
- при выходе за границы документа top-line корректно clamp-ится.
|
||||||
|
|
||||||
|
## Рекомендация по регрессии
|
||||||
|
После любых изменений в логике индексации/рендера прогонять минимум:
|
||||||
|
- `P04`, `P05`, `P06`, `P07`, `P09`, `P10`, `P11`, `P12`.
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
#include <stdio.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <sprinter.h>
|
|
||||||
|
|
||||||
void bank1_func(int x) __banked
|
|
||||||
{
|
|
||||||
(void)x;
|
|
||||||
puts("BANK1: hello from a banked function (W1)!");
|
|
||||||
puts("BANK1: window 1 phys page = ");
|
|
||||||
print_hex(_io_page_w1); /* should be BANK1's phys page */
|
|
||||||
putchar('\n');
|
|
||||||
putchar('1');
|
|
||||||
putchar('=');
|
|
||||||
putchar('0' + (x / 10) % 10);
|
|
||||||
putchar('0' + x % 10);
|
|
||||||
putchar('\n');
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
# Build mdview.exe — Markdown viewer for Sprinter.
|
||||||
|
#
|
||||||
|
# small memory mode: code in W1, data/stack/heap in W2 (32 KB total).
|
||||||
|
# W3 stays free for the file buffer (EMM-mapped).
|
||||||
|
|
||||||
|
PROJ_ROOT := $(abspath $(CURDIR)/../..)
|
||||||
|
EXAMPLE := mdview
|
||||||
|
MEMORY := small
|
||||||
|
|
||||||
|
include $(PROJ_ROOT)/app.mk
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# Образ дискеты: только mdview.exe + README.MD (перекодированный
|
||||||
|
# из UTF-8 в CP866 — рабочую кодировку Sprinter).
|
||||||
|
#
|
||||||
|
# README.MD хранится в репозитории в UTF-8; iconv -c конвертирует
|
||||||
|
# его в CP866, отбрасывая символы без аналога в целевой кодировке.
|
||||||
|
# Результат кладётся в .disk_tmp/README.MD, чтобы make_disk.py
|
||||||
|
# использовал правильное имя файла на диске.
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
DISK_TMP := .disk_tmp
|
||||||
|
README_DISK := $(DISK_TMP)/README.MD
|
||||||
|
|
||||||
|
$(DISK_TMP):
|
||||||
|
mkdir -p $@
|
||||||
|
|
||||||
|
$(README_DISK): README.MD | $(DISK_TMP)
|
||||||
|
iconv -c -f UTF-8 -t CP866 README.MD > $@
|
||||||
|
|
||||||
|
floppy: $(EXAMPLE).exe $(README_DISK)
|
||||||
|
python3 $(MAKE_DISK) $(FLOPPY_IMG) $(EXAMPLE).exe $(README_DISK)
|
||||||
|
@echo
|
||||||
|
@echo "Floppy ready: $(FLOPPY_IMG)"
|
||||||
|
@echo "Run: cd $(MAME_DIR) && ./run_mame.sh"
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf .sprinter-cc-* $(EXAMPLE).exe $(DISK_TMP)
|
||||||
|
|
||||||
|
.PHONY: all clean floppy run
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
# MDView — Просмотрщик Markdown для Sprinter
|
||||||
|
|
||||||
|
**MDView** — программа для просмотра документов в формате *Markdown* на компьютере
|
||||||
|
Sprinter (процессор Z80). Документ хранится в отдельном W3 окне и не занимает
|
||||||
|
основную RAM программы.
|
||||||
|
|
||||||
|
## Возможности
|
||||||
|
|
||||||
|
- Документы до **128 КБ** (8 страниц EMM по 16 КБ каждая)
|
||||||
|
- До **16 384** экранных строк в индексе
|
||||||
|
- Автоматический перенос слов по ширине экрана (80 столбцов)
|
||||||
|
- Горизонтальный сдвиг для широких строк (блоки кода, таблицы)
|
||||||
|
- Статус-бар: имя файла, диапазон строк, процент прокрутки
|
||||||
|
- Спиннер в строке состояния во время загрузки и индексации
|
||||||
|
- Поддержка «мягкого» склеивания строк в абзацах и цитатах
|
||||||
|
|
||||||
|
## Запуск
|
||||||
|
|
||||||
|
```
|
||||||
|
mdview [имя_файла.md]
|
||||||
|
```
|
||||||
|
|
||||||
|
Если имя файла не задано, загружается `README.MD`.
|
||||||
|
|
||||||
|
## Управление
|
||||||
|
|
||||||
|
```
|
||||||
|
Клавиша Действие
|
||||||
|
───────────── ────────────────────────────────────────
|
||||||
|
Up Down Прокрутка на одну строку вверх / вниз
|
||||||
|
PgUp PgDn Прокрутка на страницу (30 строк)
|
||||||
|
Home Начало документа
|
||||||
|
End Конец документа
|
||||||
|
Left Right Горизонтальный сдвиг (только nowrap-строки)
|
||||||
|
F1 Окно справки
|
||||||
|
F10 / Esc Выход из программы
|
||||||
|
```
|
||||||
|
|
||||||
|
## Синтаксис Markdown
|
||||||
|
|
||||||
|
### Заголовки
|
||||||
|
|
||||||
|
Поддерживаются уровни H1–H4. Уровни H5 и H6 отображаются как H4.
|
||||||
|
|
||||||
|
# Заголовок первого уровня
|
||||||
|
## Заголовок второго уровня
|
||||||
|
### Заголовок третьего уровня
|
||||||
|
#### Заголовок четвёртого уровня
|
||||||
|
|
||||||
|
### Текстовое форматирование
|
||||||
|
|
||||||
|
**Жирный текст** выделяется двойными звёздочками: `**текст**`
|
||||||
|
|
||||||
|
*Курсив* выделяется одиночными звёздочками `*текст*` или знаком подчёркивания `_текст_`
|
||||||
|
|
||||||
|
`Встроенный код` обозначается обратными кавычками
|
||||||
|
|
||||||
|
~~Зачёркнутый текст~~ — двойные тильды: `~~текст~~`
|
||||||
|
|
||||||
|
### Ненумерованный список
|
||||||
|
|
||||||
|
Маркеры `-`, `*` или `+`:
|
||||||
|
|
||||||
|
- Первый пункт списка
|
||||||
|
- Второй пункт списка
|
||||||
|
- Третий пункт с достаточно длинным текстом, который при необходимости
|
||||||
|
будет перенесён на следующую строку с сохранением отступа
|
||||||
|
|
||||||
|
### Нумерованный список
|
||||||
|
|
||||||
|
1. Первый элемент
|
||||||
|
2. Второй элемент
|
||||||
|
3. Третий элемент
|
||||||
|
|
||||||
|
### Цитата
|
||||||
|
|
||||||
|
> Блок цитаты начинается с символа `>`. Несколько последовательных
|
||||||
|
> строк одной цитаты склеиваются в единый абзац с автоматическим
|
||||||
|
> переносом слов.
|
||||||
|
|
||||||
|
### Блок кода (verbatim)
|
||||||
|
|
||||||
|
Блок кода заключается в тройные обратные кавычки. Внутри блока
|
||||||
|
текст отображается «как есть» без разбора Markdown:
|
||||||
|
|
||||||
|
```
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sprinter.h>
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
puts("Hello, Sprinter!");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Горизонтальная линия
|
||||||
|
|
||||||
|
Три или более символов `---`, `***` или `___` на отдельной строке:
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Технические характеристики
|
||||||
|
|
||||||
|
- **Платформа:** Sprinter, процессор Z80 @ 21 МГц
|
||||||
|
- **Кодировка:** CP866 (DOS Cyrillic)
|
||||||
|
- **Максимальный размер файла:** 128 КБ
|
||||||
|
- **Максимальное число строк в индексе:** 16 384
|
||||||
|
- **Режим памяти:** small
|
||||||
|
- Код программы, cтек, данные, куча — окнa W1-W2 (32 КБ, адреса 0x4000–0xBFFF).
|
||||||
|
- Буфер файла — страницы EMM, отображаемые в W3 (0xC000–0xFFFF)
|
||||||
|
|
||||||
|
## TODO
|
||||||
|
|
||||||
|
1. **Увеличение размера документов.** Снять лимит 128 КБ: Достаточно
|
||||||
|
разрешить работать с большим кол-вом страниц памяти, пока оттестированно
|
||||||
|
на работе с 8-мю страницами по 16Кб.
|
||||||
|
|
||||||
|
2. **Форматированные таблицы.** Разбирать строки вида `| ячейка | ячейка |`
|
||||||
|
с автоматическим выравниванием столбцов и отрисовкой разделительных
|
||||||
|
линий (строки `|---|---|`). На текущий момент таблицы отображаются
|
||||||
|
как обычные nowrap-строки без выравнивания.
|
||||||
|
|
||||||
|
3. **Поддержка кодировок CP1251 и UTF-8.** Автоопределение кодировки
|
||||||
|
по BOM, явное указание через аргумент командной строки (`--encoding cp1251`),
|
||||||
|
возможность переключения кодировки во время просмотра (`F8`).
|
||||||
|
Нужно прежде всего для документов на русском языке; CP866 кодировака уже поддерживается.
|
||||||
|
|
||||||
|
4. **Ускорение рендеринга.** Кэш строк экрана. Оптимизация цикла вывода
|
||||||
|
символов через BIOS WRCHAR (пакетный вывод, DMA).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*MDView v0.2 · (c) 2026 Петров А.Г.*
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +0,0 @@
|
|||||||
# Build open_env_test.exe — uses lib/sprinter.lib in TINY memory mode.
|
|
||||||
|
|
||||||
PROJ_ROOT := $(abspath $(CURDIR)/../..)
|
|
||||||
EXAMPLE := openenv
|
|
||||||
include $(PROJ_ROOT)/examples/example.mk
|
|
||||||
+40
-15
@@ -22,23 +22,48 @@ BUILD := $(PROJ_ROOT)/lib/build
|
|||||||
|
|
||||||
# All libc C modules.
|
# All libc C modules.
|
||||||
LIBC_C := \
|
LIBC_C := \
|
||||||
libc/io/atexit.c libc/io/conio.c libc/io/cprintf.c libc/io/dir.c \
|
libc/sys/atexit.c \
|
||||||
libc/io/videomode_raw.c \
|
libc/conio/conio.c \
|
||||||
libc/io/_errno_set.c \
|
libc/conio/cprintf.c \
|
||||||
libc/io/env.c libc/io/errno.c libc/io/fsdir.c \
|
libc/conio/text_palette.c \
|
||||||
libc/io/lseek.c libc/io/mouse.c libc/io/open.c \
|
libc/io/dir.c \
|
||||||
libc/io/read.c libc/io/sleep.c \
|
libc/video/videomode_raw.c \
|
||||||
libc/io/time.c libc/io/posix_time.c libc/io/unlink.c \
|
libc/video/palette.c \
|
||||||
|
libc/errno/_errno_set.c \
|
||||||
|
libc/env/env.c \
|
||||||
|
libc/errno/errno.c \
|
||||||
|
libc/io/fsdir.c \
|
||||||
|
libc/io/lseek.c \
|
||||||
|
libc/mouse/mouse.c \
|
||||||
|
libc/io/open.c \
|
||||||
|
libc/io/read.c \
|
||||||
|
libc/time/sleep.c \
|
||||||
|
libc/time/time.c \
|
||||||
|
libc/time/posix_time.c \
|
||||||
|
libc/io/unlink.c \
|
||||||
libc/io/stat.c \
|
libc/io/stat.c \
|
||||||
libc/mem/bank_io.c libc/mem/mem_alloc.c \
|
libc/mem/bank_io_w3.c \
|
||||||
libc/gfx/gfx_core.c libc/gfx/gfx_raw_common.c \
|
libc/mem/bank_io_w1.c \
|
||||||
libc/gfx/gfx_raw_256.c libc/gfx/gfx_raw_16.c \
|
libc/mem/mem_estex.c \
|
||||||
libc/gfx/gfx_256.c libc/gfx/gfx_16.c \
|
libc/mem/mem_bios.c \
|
||||||
libc/gfx/gfx_font.c libc/gfx/gfx_text_256.c \
|
libc/gfx/gfx_core.c \
|
||||||
|
libc/gfx/gfx_palette.c \
|
||||||
|
libc/gfx/gfx_raw_common.c \
|
||||||
|
libc/gfx/gfx_raw_256.c \
|
||||||
|
libc/gfx/gfx_raw_16.c \
|
||||||
|
libc/gfx/gfx_256.c \
|
||||||
|
libc/gfx/gfx_16.c \
|
||||||
|
libc/gfx/gfx_font.c \
|
||||||
|
libc/gfx/gfx_text_256.c \
|
||||||
libc/gfx/gfx_text_16.c \
|
libc/gfx/gfx_text_16.c \
|
||||||
libc/stdio/getchar.c libc/stdio/print_hex.c \
|
libc/stdio/getchar.c \
|
||||||
libc/stdio/putchar.c libc/stdio/puts.c libc/stdio/file.c \
|
libc/stdio/putchar.c \
|
||||||
libc/stdio/solid_helpers.c libc/io/solid_compat.c
|
libc/stdio/puts.c \
|
||||||
|
libc/file/file.c \
|
||||||
|
libc/stdio/hex_print.c \
|
||||||
|
libc/stdio/dec_print.c \
|
||||||
|
libc/string/strlwr.c \
|
||||||
|
libc/string/strupr.c
|
||||||
|
|
||||||
# Runtime modules to bundle (pulled by symbol references from libc-using code).
|
# Runtime modules to bundle (pulled by symbol references from libc-using code).
|
||||||
# NOTE: runtime/bank.s is NOT bundled — its trampoline depends on the banking
|
# NOTE: runtime/bank.s is NOT bundled — its trampoline depends on the banking
|
||||||
|
|||||||
@@ -58,6 +58,30 @@ char getche(void) __naked
|
|||||||
__endasm;
|
__endasm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* getkey — like getch() but exposes BOTH the ASCII value and the
|
||||||
|
* positional scan code, so callers can distinguish extended keys
|
||||||
|
* (arrows, F1..F12, PgUp/PgDn, Home/End, Ins/Del — all of which carry
|
||||||
|
* ASCII == 0 from ESTEX) from plain ASCII keys.
|
||||||
|
*
|
||||||
|
* return = (scan << 8) | ascii
|
||||||
|
*
|
||||||
|
* For plain keys: ascii holds the character, scan holds the positional
|
||||||
|
* code (bit 7 set when Ctrl/Alt/Shift is held).
|
||||||
|
* For extended keys: ascii == 0, scan identifies the key (see KEY_* in
|
||||||
|
* <conio.h>).
|
||||||
|
*/
|
||||||
|
uint16_t getkey(void) __naked
|
||||||
|
{
|
||||||
|
__asm
|
||||||
|
push ix
|
||||||
|
ld c, #0x30 ; ESTEX WAITKEY: A=ASCII, D=scan, E=ASCII
|
||||||
|
rst #0x10
|
||||||
|
pop ix
|
||||||
|
ld e, a ; E = ASCII (defensive: A is the canonical copy)
|
||||||
|
ret ; __sdcccall(1) returns uint16_t in DE
|
||||||
|
__endasm;
|
||||||
|
}
|
||||||
|
|
||||||
/* ---- putch / cputs: Turbo-C conio convention ---------------------- *
|
/* ---- putch / cputs: Turbo-C conio convention ---------------------- *
|
||||||
* Both APPLY the current text attribute (g_text_attr). When attr is
|
* Both APPLY the current text attribute (g_text_attr). When attr is
|
||||||
* KEEP_EXIST_ATTR (>0xFF), they short-circuit to the FAST stdio path
|
* KEEP_EXIST_ATTR (>0xFF), they short-circuit to the FAST stdio path
|
||||||
@@ -332,6 +356,11 @@ char cputs(const char *s) __naked
|
|||||||
or a, l
|
or a, l
|
||||||
ret z
|
ret z
|
||||||
|
|
||||||
|
;; IX is callee-saved under SDCC's z80 ABI. We use it as a
|
||||||
|
;; function pointer below (ld ix, #__raw_putch_raw0/1) so save
|
||||||
|
;; the caller's value up front and restore before every ret.
|
||||||
|
push ix
|
||||||
|
|
||||||
;; KEEP_EXIST_ATTR? high byte of g_text_attr != 0
|
;; KEEP_EXIST_ATTR? high byte of g_text_attr != 0
|
||||||
ld a, (_g_text_attr + 1)
|
ld a, (_g_text_attr + 1)
|
||||||
or a, a
|
or a, a
|
||||||
@@ -374,11 +403,13 @@ char cputs(const char *s) __naked
|
|||||||
_cputs_loop_end:
|
_cputs_loop_end:
|
||||||
|
|
||||||
call __set_cursor
|
call __set_cursor
|
||||||
|
pop ix ; restore caller's IX
|
||||||
xor a, a ; return 0
|
xor a, a ; return 0
|
||||||
ret
|
ret
|
||||||
|
|
||||||
_cputs_fast:
|
_cputs_fast:
|
||||||
call __cputs_pchars
|
call __cputs_pchars
|
||||||
|
pop ix ; restore caller's IX
|
||||||
xor a, a ; return 0
|
xor a, a ; return 0
|
||||||
ret
|
ret
|
||||||
__endasm;
|
__endasm;
|
||||||
@@ -462,6 +493,37 @@ uint16_t wherexy(void) __naked
|
|||||||
__endasm;
|
__endasm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* wrchar(uint8_t x, uint8_t y, char ch, uint8_t attr)
|
||||||
|
*
|
||||||
|
* SDCC __sdcccall(1): x in A, y in L (2 uint8 → A, L); ch and attr
|
||||||
|
* packed and pushed on the stack as a single 16-bit value (caller does
|
||||||
|
* `ld hl, #(attr<<8)|ch; push hl`). Layout after CALL:
|
||||||
|
* [SP+0..1] = return address
|
||||||
|
* [SP+2] = ch (low half of pushed pair)
|
||||||
|
* [SP+3] = attr (high half)
|
||||||
|
* Void return → callee-pops the 2 stack-arg bytes via `pop bc` + jp (iy).
|
||||||
|
*/
|
||||||
|
void scroll(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t direction, uint8_t clear) __naked
|
||||||
|
{
|
||||||
|
(void)x; (void)y; (void)w; (void)h; (void)direction; (void)clear;
|
||||||
|
__asm
|
||||||
|
pop iy ; return address
|
||||||
|
ld d, l ; D = row (y)
|
||||||
|
ld e, a ; E = col (x)
|
||||||
|
pop hl ; H = heigth(h), L = width(w)
|
||||||
|
pop bc ; C = direction, B = clear
|
||||||
|
ld a, b ; A = clear(B)
|
||||||
|
ld b, c ; B = direction(C)
|
||||||
|
push ix
|
||||||
|
ld c, #0x55 ; ESTEX SCROLL
|
||||||
|
rst #0x10
|
||||||
|
pop ix
|
||||||
|
jp (iy)
|
||||||
|
__endasm;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* wrchar(uint8_t x, uint8_t y, char ch, uint8_t attr)
|
/* wrchar(uint8_t x, uint8_t y, char ch, uint8_t attr)
|
||||||
*
|
*
|
||||||
Vendored
@@ -1,14 +1,40 @@
|
|||||||
/*
|
/*
|
||||||
* file.c — minimal unbuffered FILE * implementation on top of the
|
* file.c — *** PROVISIONAL *** minimal unbuffered FILE * implementation
|
||||||
* POSIX-style fd I/O (open/read/write/lseek/close).
|
* on top of POSIX-style fd I/O
|
||||||
|
* (open/read/write/lseek/close).
|
||||||
*
|
*
|
||||||
* No buffering: each fputc/fgetc maps to one read/write syscall. For
|
* ============================================================
|
||||||
* heavy-throughput code, prefer fread/fwrite with a sizable buffer or
|
* TODO (v2): replace with a proper BUFFERED implementation.
|
||||||
* the raw fd I/O directly.
|
* See docs/TODO.md / Solid-C reference layout:
|
||||||
*
|
*
|
||||||
* stdin/stdout/stderr are STATIC sentinel FILEs with fd=-1 and the
|
* typedef struct {
|
||||||
* _F_CONIN/_F_CONOUT flags set; fputc/fgetc detect them and call
|
* uint flags; // +0..1 file status flags
|
||||||
* putchar()/getchar() (which already do CR/LF mapping and ESTEX calls).
|
* int level; // +2..3 empty/fill level of buffer
|
||||||
|
* char *curp; // +4..5 current active pointer
|
||||||
|
* int fd; // +6..7 underlying low-level fd
|
||||||
|
* char *buffer; // +8..9 data transfer buffer
|
||||||
|
* char hold; // +10 ungetc byte if no buffer
|
||||||
|
* short token; // +11..12 reserved
|
||||||
|
* char dummy; // +13 reserved
|
||||||
|
* } FILE;
|
||||||
|
*
|
||||||
|
* The current implementation maps each fputc/fgetc to one read/write
|
||||||
|
* syscall — fine for correctness checks, awful for throughput. Issues
|
||||||
|
* 3/4/5 from the stdio-review (fwrite short-write flag, fgets n=1,
|
||||||
|
* mode_to_flags break) are deferred until that rewrite.
|
||||||
|
* ============================================================
|
||||||
|
*
|
||||||
|
* stdin/stdout/stderr are STATIC sentinel FILEs flagged with
|
||||||
|
* _F_CONIN/_F_CONOUT; fputc/fgetc detect them and call putchar() /
|
||||||
|
* getchar() which already handle CR/LF translation and ESTEX calls.
|
||||||
|
*
|
||||||
|
* Their fd fields are 0 / -1 / -2. Negative values were chosen because
|
||||||
|
* ESTEX OPEN can return small positive fds (1, 2, …) for ordinary
|
||||||
|
* files — if we marked stdout/stderr with fd=1/2 a real file could
|
||||||
|
* collide with their identifier. fd=0 for stdin is kept (POSIX-style)
|
||||||
|
* because ESTEX does not return 0. Even so, none of these fd fields
|
||||||
|
* is ever passed to a syscall — the _F_CONIN/_F_CONOUT flags drive
|
||||||
|
* the dispatch.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@@ -19,17 +45,13 @@
|
|||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|
||||||
/* ---- console pseudo-streams ----------------------------------------*/
|
/* ---- console pseudo-streams ----------------------------------------*/
|
||||||
static FILE _stdin = { -1, _F_READ | _F_CONIN };
|
static FILE _stdin = { 0, _F_READ | _F_CONIN };
|
||||||
static FILE _stdout = { -2, _F_WRITE | _F_CONOUT };
|
static FILE _stdout = { -1, _F_WRITE | _F_CONOUT };
|
||||||
static FILE _stderr = { -3, _F_WRITE | _F_CONOUT };
|
static FILE _stderr = { -2, _F_WRITE | _F_CONOUT };
|
||||||
static FILE _stdaux = { -4, _F_WRITE | _F_CONOUT };
|
|
||||||
static FILE _stdprn = { -5, _F_WRITE | _F_CONOUT };
|
|
||||||
|
|
||||||
FILE *const stdin = &_stdin;
|
FILE *const stdin = &_stdin;
|
||||||
FILE *const stdout = &_stdout;
|
FILE *const stdout = &_stdout;
|
||||||
FILE *const stderr = &_stderr;
|
FILE *const stderr = &_stderr;
|
||||||
FILE *const stdaux = &_stdaux;
|
|
||||||
FILE *const stdprn = &_stdprn;
|
|
||||||
|
|
||||||
/* ---- fopen / fclose -------------------------------------------------*/
|
/* ---- fopen / fclose -------------------------------------------------*/
|
||||||
|
|
||||||
@@ -121,7 +143,8 @@ int fputc(int c, FILE *fp)
|
|||||||
{
|
{
|
||||||
if (!fp) { errno = EBADF; return EOF; }
|
if (!fp) { errno = EBADF; return EOF; }
|
||||||
if (fp->flags & _F_CONOUT) {
|
if (fp->flags & _F_CONOUT) {
|
||||||
return putchar(c);
|
putchar(c);
|
||||||
|
return (int)c;
|
||||||
}
|
}
|
||||||
if (!(fp->flags & _F_WRITE)) { errno = EBADF; return EOF; }
|
if (!(fp->flags & _F_WRITE)) { errno = EBADF; return EOF; }
|
||||||
uint8_t ch = (uint8_t)c;
|
uint8_t ch = (uint8_t)c;
|
||||||
@@ -153,7 +176,7 @@ int fputs(const char *s, FILE *fp)
|
|||||||
if (!fp || !s) { errno = EBADF; return EOF; }
|
if (!fp || !s) { errno = EBADF; return EOF; }
|
||||||
if (fp->flags & _F_CONOUT) {
|
if (fp->flags & _F_CONOUT) {
|
||||||
while (*s) {
|
while (*s) {
|
||||||
if (putchar((unsigned char)*s++) == EOF) return EOF;
|
putchar((unsigned char)*s++);;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -217,7 +240,7 @@ size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *fp)
|
|||||||
const char *p = (const char *)ptr;
|
const char *p = (const char *)ptr;
|
||||||
size_t total = size * nmemb;
|
size_t total = size * nmemb;
|
||||||
for (size_t i = 0; i < total; i++) {
|
for (size_t i = 0; i < total; i++) {
|
||||||
if (putchar((unsigned char)p[i]) == EOF) return i / size;
|
putchar((unsigned char)p[i]);
|
||||||
}
|
}
|
||||||
return nmemb;
|
return nmemb;
|
||||||
}
|
}
|
||||||
+4
-41
@@ -13,7 +13,7 @@
|
|||||||
* gfx_set_draw_page / get — updates _gfx_addr_base for the new page
|
* gfx_set_draw_page / get — updates _gfx_addr_base for the new page
|
||||||
* gfx_set_bank / get — sets the W3 page byte (0x50..0x5F)
|
* gfx_set_bank / get — sets the W3 page byte (0x50..0x5F)
|
||||||
* gfx_wait_vsync — EI; HALT until next frame interrupt
|
* gfx_wait_vsync — EI; HALT until next frame interrupt
|
||||||
* gfx_pal_load / gfx_pal_set — BIOS $A4 PIC_SET_PAL wrappers
|
* (palette wrappers live in gfx_palette.c, backed by libc/video/palette.c)
|
||||||
*
|
*
|
||||||
* Shared state (extern from this file):
|
* Shared state (extern from this file):
|
||||||
* _gfx_addr_base — 0xC000 for page 0, 0xC140 for page 1. Every
|
* _gfx_addr_base — 0xC000 for page 0, 0xC140 for page 1. Every
|
||||||
@@ -136,43 +136,6 @@ void gfx_wait_vsync(void) __naked
|
|||||||
__endasm;
|
__endasm;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---- Palette (BIOS $A4 PIC_SET_PAL) ----------------------------- */
|
/* Palette wrappers (gfx_pal_load / gfx_pal_set / gfx_pal_get /
|
||||||
|
* gfx_pal_get_color / gfx_pal_reset) moved to gfx_palette.c — see also
|
||||||
static uint8_t pal_num_;
|
* libc/video/palette.c for the shared low-level $A4 / $A6 implementation. */
|
||||||
static uint8_t pal_start_;
|
|
||||||
static uint8_t pal_count_;
|
|
||||||
static uint16_t pal_data_;
|
|
||||||
|
|
||||||
void gfx_pal_load(uint8_t pal_num, uint8_t start, uint8_t count,
|
|
||||||
const uint8_t *data)
|
|
||||||
{
|
|
||||||
pal_num_ = pal_num;
|
|
||||||
pal_start_ = start;
|
|
||||||
pal_count_ = count;
|
|
||||||
pal_data_ = (uint16_t)(uintptr_t)data;
|
|
||||||
|
|
||||||
__asm
|
|
||||||
push ix
|
|
||||||
ld a, (_pal_start_)
|
|
||||||
ld e, a ; E = start
|
|
||||||
ld a, (_pal_count_)
|
|
||||||
ld d, a ; D = count (0 → 256)
|
|
||||||
ld hl, (_pal_data_) ; HL = data
|
|
||||||
ld b, #0xFF ; mask
|
|
||||||
ld a, (_pal_num_) ; A = palette number
|
|
||||||
ld c, #0xA4 ; BIOS PIC_SET_PAL
|
|
||||||
rst #0x08
|
|
||||||
pop ix
|
|
||||||
__endasm;
|
|
||||||
}
|
|
||||||
|
|
||||||
void gfx_pal_set(uint8_t pal_num, uint8_t idx,
|
|
||||||
uint8_t r, uint8_t g, uint8_t b)
|
|
||||||
{
|
|
||||||
uint8_t entry[4];
|
|
||||||
entry[0] = b;
|
|
||||||
entry[1] = g;
|
|
||||||
entry[2] = r;
|
|
||||||
entry[3] = 0;
|
|
||||||
gfx_pal_load(pal_num, idx, 1, entry);
|
|
||||||
}
|
|
||||||
|
|||||||
+86
-4
@@ -38,6 +38,42 @@
|
|||||||
char kbhit (void);
|
char kbhit (void);
|
||||||
char getch (void);
|
char getch (void);
|
||||||
char getche(void);
|
char getche(void);
|
||||||
|
|
||||||
|
/* Extended-key reader. Returns (scan << 8) | ascii. Plain ASCII keys
|
||||||
|
* have ascii in the low byte; extended keys (arrows / F1..F12 /
|
||||||
|
* PgUp/PgDn / Home / End / Ins / Del) carry ascii == 0 and the
|
||||||
|
* KEY_* code in the high byte. */
|
||||||
|
uint16_t getkey(void);
|
||||||
|
|
||||||
|
/* Scan codes returned in the high byte of getkey() when the low byte
|
||||||
|
* (ASCII) is 0. Empirically verified in MAME — the ProgrammerManual.txt
|
||||||
|
* "positional code" column is misleading; BIOS returns IBM-style codes
|
||||||
|
* for the F-keys and a "5N + numpad-position" pattern for the cursor /
|
||||||
|
* editing keys.
|
||||||
|
*
|
||||||
|
* Verified 2026-06-04 by reading raw getkey() output. */
|
||||||
|
#define KEY_F1 0x3B
|
||||||
|
#define KEY_F2 0x3C
|
||||||
|
#define KEY_F3 0x3D
|
||||||
|
#define KEY_F4 0x3E
|
||||||
|
#define KEY_F5 0x3F
|
||||||
|
#define KEY_F6 0x40
|
||||||
|
#define KEY_F7 0x41
|
||||||
|
#define KEY_F8 0x42
|
||||||
|
#define KEY_F9 0x43
|
||||||
|
#define KEY_F10 0x44
|
||||||
|
#define KEY_F11 0x45 /* not verified */
|
||||||
|
#define KEY_F12 0x46 /* not verified */
|
||||||
|
#define KEY_END 0x51
|
||||||
|
#define KEY_DOWN 0x52
|
||||||
|
#define KEY_PGDN 0x53
|
||||||
|
#define KEY_LEFT 0x54
|
||||||
|
#define KEY_RIGHT 0x56
|
||||||
|
#define KEY_HOME 0x57
|
||||||
|
#define KEY_UP 0x58
|
||||||
|
#define KEY_PGUP 0x59
|
||||||
|
#define KEY_INS 0x50 /* numpad 0; not verified */
|
||||||
|
#define KEY_DEL 0x55 /* numpad 5/.; not verified */
|
||||||
char putch (char c);
|
char putch (char c);
|
||||||
char cputs (const char *s);
|
char cputs (const char *s);
|
||||||
int cprintf(const char *fmt, ...);
|
int cprintf(const char *fmt, ...);
|
||||||
@@ -63,6 +99,8 @@ uint8_t wherex (void);
|
|||||||
uint8_t wherey (void);
|
uint8_t wherey (void);
|
||||||
uint16_t wherexy(void); // high byte = Y, low byte = X coords.
|
uint16_t wherexy(void); // high byte = Y, low byte = X coords.
|
||||||
|
|
||||||
|
void scroll(uint8_t, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t);
|
||||||
|
|
||||||
/* Direct character/attribute screen access (ESTEX $57 / $58).
|
/* Direct character/attribute screen access (ESTEX $57 / $58).
|
||||||
* wrchar — write char + attribute at (x, y); does NOT advance the cursor
|
* wrchar — write char + attribute at (x, y); does NOT advance the cursor
|
||||||
* and does NOT interpret control characters. Useful for
|
* and does NOT interpret control characters. Useful for
|
||||||
@@ -143,12 +181,56 @@ uint8_t get_putch_raw_mode(void);
|
|||||||
* Colour order is standard CGA / Borland-conio.h. Constants 0..7 are
|
* Colour order is standard CGA / Borland-conio.h. Constants 0..7 are
|
||||||
* usable for both fg and bg; 8..15 are foreground-only. */
|
* usable for both fg and bg; 8..15 are foreground-only. */
|
||||||
enum {
|
enum {
|
||||||
COLOR_BLACK = 0, COLOR_BLUE, COLOR_GREEN, COLOR_CYAN,
|
COLOR_BLACK = 0,
|
||||||
COLOR_RED, COLOR_MAGENTA, COLOR_BROWN, COLOR_LIGHTGRAY,
|
COLOR_BLUE,
|
||||||
COLOR_DARKGRAY, COLOR_LIGHTBLUE, COLOR_LIGHTGREEN, COLOR_LIGHTCYAN,
|
COLOR_GREEN,
|
||||||
COLOR_LIGHTRED, COLOR_LIGHTMAGENTA, COLOR_YELLOW, COLOR_WHITE
|
COLOR_CYAN,
|
||||||
|
COLOR_RED,
|
||||||
|
COLOR_MAGENTA,
|
||||||
|
COLOR_BROWN,
|
||||||
|
COLOR_LIGHTGRAY,
|
||||||
|
COLOR_DARKGRAY,
|
||||||
|
COLOR_LIGHTBLUE,
|
||||||
|
COLOR_LIGHTGREEN,
|
||||||
|
COLOR_LIGHTCYAN,
|
||||||
|
COLOR_LIGHTRED,
|
||||||
|
COLOR_LIGHTMAGENTA,
|
||||||
|
COLOR_YELLOW,
|
||||||
|
COLOR_WHITE
|
||||||
};
|
};
|
||||||
#define COLOR_BLINK 0x80u
|
#define COLOR_BLINK 0x80u
|
||||||
#define COLOR(fg, bg) ((uint8_t)((((bg) & 0x07) << 4) | ((fg) & 0x0F)))
|
#define COLOR(fg, bg) ((uint8_t)((((bg) & 0x07) << 4) | ((fg) & 0x0F)))
|
||||||
|
|
||||||
|
/* Text-mode palette. The 16 logical CGA colours seen by COLOR(fg, bg)
|
||||||
|
* actually live in four 256-entry hardware palette planes indexed by the
|
||||||
|
* full 8-bit attribute byte:
|
||||||
|
*
|
||||||
|
* TEXT_PAL_PAPER — background colour, non-blink phase
|
||||||
|
* TEXT_PAL_INK — foreground colour, non-blink phase
|
||||||
|
* TEXT_PAL_BLINK_PAPER — background colour during the blink half-cycle
|
||||||
|
* TEXT_PAL_BLINK_INK — foreground colour during the blink half-cycle
|
||||||
|
*
|
||||||
|
* For non-blinking attributes (bit 7 = 0) all four planes display the
|
||||||
|
* same colours, so writing to PAPER/INK is enough. For blinking attrs
|
||||||
|
* (bit 7 = 1) the renderer alternates between the non-blink and blink
|
||||||
|
* planes — that's how flash is implemented in hardware.
|
||||||
|
*
|
||||||
|
* These wrappers add 4 to the plane index and forward to the low-level
|
||||||
|
* <palette.h> API (pal_load / pal_set_color / pal_get / pal_get_color).
|
||||||
|
* Use text_pal_reset() to restore the system default CGA palette. */
|
||||||
|
#define TEXT_PAL_PAPER 0
|
||||||
|
#define TEXT_PAL_INK 1
|
||||||
|
#define TEXT_PAL_BLINK_PAPER 2
|
||||||
|
#define TEXT_PAL_BLINK_INK 3
|
||||||
|
|
||||||
|
void text_pal_load (uint8_t plane, uint8_t start, uint8_t count,
|
||||||
|
const uint8_t *bgr0);
|
||||||
|
void text_pal_set_color(uint8_t plane, uint8_t attr,
|
||||||
|
uint8_t r, uint8_t g, uint8_t b);
|
||||||
|
void text_pal_get (uint8_t plane, uint8_t start, uint8_t count,
|
||||||
|
uint8_t *bgr0);
|
||||||
|
void text_pal_get_color(uint8_t plane, uint8_t attr,
|
||||||
|
uint8_t *r, uint8_t *g, uint8_t *b);
|
||||||
|
void text_pal_reset (void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
+15
-7
@@ -16,14 +16,22 @@
|
|||||||
#ifndef FCNTL_H
|
#ifndef FCNTL_H
|
||||||
#define FCNTL_H
|
#define FCNTL_H
|
||||||
|
|
||||||
#define O_RDONLY 0
|
#ifndef _STD_SEEK_
|
||||||
#define O_WRONLY 1
|
#define _STD_SEEK_
|
||||||
#define O_RDWR 2
|
/* constants to be used as 3rd argument for "fseek" function */
|
||||||
|
#define SEEK_SET 0
|
||||||
|
#define SEEK_CUR 1
|
||||||
|
#define SEEK_END 2
|
||||||
|
#endif
|
||||||
|
|
||||||
#define O_CREAT 0x040
|
/* Definition "open flags" */
|
||||||
#define O_EXCL 0x080
|
#define O_WRONLY 0x01 /* 0 file write only */
|
||||||
#define O_TRUNC 0x200
|
#define O_RDONLY 0x02 /* 1 file read only */
|
||||||
#define O_APPEND 0x400
|
#define O_RDWR 0x03 /* 1,0 file read/write */
|
||||||
|
#define O_TRUNC 0x04 /* 2 open with truncation */
|
||||||
|
#define O_CREAT 0x08 /* 3 create and open file */
|
||||||
|
#define O_EXCL 0x10 /* 4 exclusive open */
|
||||||
|
#define O_APPEND 0x20 /* 5 to end of file */
|
||||||
|
|
||||||
int open (const char *path, int flags);
|
int open (const char *path, int flags);
|
||||||
int creat(const char *path, int mode); /* mode arg ignored on Sprinter */
|
int creat(const char *path, int mode); /* mode arg ignored on Sprinter */
|
||||||
|
|||||||
@@ -145,4 +145,15 @@ void gfx_pal_load(uint8_t pal_num, uint8_t start, uint8_t count,
|
|||||||
void gfx_pal_set (uint8_t pal_num, uint8_t idx,
|
void gfx_pal_set (uint8_t pal_num, uint8_t idx,
|
||||||
uint8_t r, uint8_t g, uint8_t b);
|
uint8_t r, uint8_t g, uint8_t b);
|
||||||
|
|
||||||
|
/* Read a contiguous block of entries back from a graphics palette. */
|
||||||
|
void gfx_pal_get (uint8_t pal_num, uint8_t start, uint8_t count,
|
||||||
|
uint8_t *data);
|
||||||
|
|
||||||
|
/* Read one entry into R, G, B pointers (any may be NULL). */
|
||||||
|
void gfx_pal_get_color(uint8_t pal_num, uint8_t idx,
|
||||||
|
uint8_t *r, uint8_t *g, uint8_t *b);
|
||||||
|
|
||||||
|
/* Restore the system default graphics palette (BIOS $A6, type=1). */
|
||||||
|
void gfx_pal_reset(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -113,8 +113,7 @@ static inline void sprinter_page_w2(uint8_t page) { _io_page_w2 = page; }
|
|||||||
static inline void sprinter_page_w3(uint8_t page) { _io_page_w3 = page; }
|
static inline void sprinter_page_w3(uint8_t page) { _io_page_w3 = page; }
|
||||||
|
|
||||||
/* ---- Sprinter-specific debug helpers ------------------------------ */
|
/* ---- Sprinter-specific debug helpers ------------------------------ */
|
||||||
/* Print one byte as two uppercase hex digits via putchar(). */
|
/* Use hex8() / hex16() / hex32() from <stdio.h> for hex debug output. */
|
||||||
void print_hex(uint8_t v);
|
|
||||||
|
|
||||||
#ifdef DEBUG_RT
|
#ifdef DEBUG_RT
|
||||||
/*
|
/*
|
||||||
|
|||||||
+105
-15
@@ -3,18 +3,35 @@
|
|||||||
*
|
*
|
||||||
* For data that doesn't fit in window 2's heap, allocate physical pages
|
* For data that doesn't fit in window 2's heap, allocate physical pages
|
||||||
* directly through ESTEX EMM and access them via bank_read / bank_write
|
* directly through ESTEX EMM and access them via bank_read / bank_write
|
||||||
* (which swap window 3 internally).
|
* (which swap a CPU window internally).
|
||||||
*
|
*
|
||||||
* mem_alloc_pages(N) — reserve N contiguous 16 KB pages, return block id.
|
* mem_alloc_pages(N) — reserve N contiguous 16 KB pages, return block id.
|
||||||
* mem_free_block(id) — release a previously-allocated block.
|
* mem_free_block(id) — release a previously-allocated block.
|
||||||
* mem_get_page(id, i) — translate (block, page-index) to physical page.
|
* mem_get_page(id, i) — translate (block, page-index) to physical page.
|
||||||
* mem_info(&total, &free) — query EMM about total/free 16 KB pages.
|
* mem_info(&total, &free) — query EMM about total/free 16 KB pages.
|
||||||
*
|
*
|
||||||
* bank_load_byte / bank_store_byte — single-byte access via W3.
|
* bank_load_byte / bank_store_byte — single-byte access through W3.
|
||||||
* bank_read / bank_write — bulk copy between far page and a near buffer.
|
* bank_read / bank_write — bulk copy through W3.
|
||||||
|
* bank_*_w1 — same family but through W1 (for
|
||||||
|
* memory modes where code lives in
|
||||||
|
* W2 and W1 is free for data).
|
||||||
*
|
*
|
||||||
* The bank_* helpers live in HOME so they are always reachable; they save
|
* Each helper saves the previous window-mapping, swaps to the target
|
||||||
* the previous W3 page, swap to the target, do the work, restore W3.
|
* physical page, does the access, restores the saved page.
|
||||||
|
*
|
||||||
|
* SAFETY by memory mode (-memory FLAG to sprinter-cc):
|
||||||
|
*
|
||||||
|
* `tiny` : both bank_* and bank_*_w1 are safe.
|
||||||
|
* `small`/`huge`: only bank_* (W3) is safe; bank_*_w1 CRASHES
|
||||||
|
* because code lives in W1.
|
||||||
|
* `big` : only bank_* (W3) is safe; bank_*_w1 would clobber
|
||||||
|
* the banked-code segment in W1.
|
||||||
|
* `huge` (data path through W3 banked code): bank_* (W3) is unsafe;
|
||||||
|
* avoid banking via W3 in huge programs.
|
||||||
|
*
|
||||||
|
* In short: `_w1` accessors are a `tiny`-mode optimisation, useful when
|
||||||
|
* the program is small enough to fit in W2 and wants both W1 and W3 as
|
||||||
|
* independent banked-data windows.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef SPRINTER_MEM_H
|
#ifndef SPRINTER_MEM_H
|
||||||
@@ -22,16 +39,89 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
/* ESTEX EMM allocator wrappers. */
|
/* ===================================================================
|
||||||
uint8_t mem_alloc_pages(uint8_t n); /* 0 = failure */
|
* ESTEX EMM allocator wrappers
|
||||||
void mem_free_block (uint8_t blk_id);
|
* =================================================================== */
|
||||||
uint8_t mem_get_page (uint8_t blk_id, uint8_t idx);
|
|
||||||
void mem_info (uint16_t *total, uint16_t *free_pages);
|
|
||||||
|
|
||||||
/* Far-page accessors (always-mapped HOME, swap W3 internally). */
|
/* Allocate `n` contiguous 16-KB physical pages from the EMM pool.
|
||||||
uint8_t bank_load_byte (uint8_t phys_page, uint16_t off_in_window);
|
* n : 1..255
|
||||||
void bank_store_byte(uint8_t phys_page, uint16_t off_in_window, uint8_t v);
|
* ret : blk_id (1..255) on success; 0 on failure with errno set.
|
||||||
void bank_read (uint8_t phys_page, uint16_t off, void *dst, uint16_t n);
|
* The id is opaque — pass it to mem_get_page() and mem_free_block(). */
|
||||||
void bank_write(uint8_t phys_page, uint16_t off, const void *src, uint16_t n);
|
uint8_t mem_alloc_pages_estex(uint8_t n);
|
||||||
|
uint8_t mem_alloc_pages_bios(uint8_t n);
|
||||||
|
|
||||||
|
/* Release a block previously returned by mem_alloc_pages().
|
||||||
|
* On error errno is set (e.g. EINVAL for unknown id). Double-free is
|
||||||
|
* NOT idempotent: the second call sets errno. */
|
||||||
|
void mem_free_block_estex(uint8_t blk_id);
|
||||||
|
void mem_free_block_bios(uint8_t blk_id);
|
||||||
|
|
||||||
|
/* Translate (block, page-index) into a physical page number suitable
|
||||||
|
* for sprinter_page_w1/w2/w3() or the bank_*() helpers below.
|
||||||
|
* blk_id: from mem_alloc_pages()
|
||||||
|
* idx : 0..(n-1)
|
||||||
|
* ret : physical page (1..255) on success; 0 on failure (errno set). */
|
||||||
|
uint8_t mem_get_page_bios(uint8_t blk_id, uint8_t idx);
|
||||||
|
|
||||||
|
/* Query the EMM allocator state. Both pointers must be non-NULL.
|
||||||
|
* Cannot fail (no error path). */
|
||||||
|
void mem_info_estex(uint16_t *total, uint16_t *free_pages);
|
||||||
|
void mem_info_bios(uint16_t *total, uint16_t *free_pages);
|
||||||
|
|
||||||
|
#define MEM_MANAGE_MODE_BIOS
|
||||||
|
|
||||||
|
#ifdef MEM_MANAGE_MODE_ESTEX
|
||||||
|
|
||||||
|
#define mem_alloc_pages mem_alloc_pages_estex
|
||||||
|
#define mem_free_block mem_free_block_estex
|
||||||
|
#define mem_info mem_info_estex
|
||||||
|
|
||||||
|
#define mem_get_page mem_get_page_bios
|
||||||
|
|
||||||
|
#elif defined MEM_MANAGE_MODE_BIOS
|
||||||
|
|
||||||
|
#define mem_alloc_pages mem_alloc_pages_bios
|
||||||
|
#define mem_free_block mem_free_block_bios
|
||||||
|
#define mem_info mem_info_bios
|
||||||
|
|
||||||
|
#define mem_get_page mem_get_page_bios
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* ===================================================================
|
||||||
|
* Far-page accessors via window 3 (base 0xC000, port 0xE2)
|
||||||
|
*
|
||||||
|
* Each call saves the current W3 mapping, sets W3 to `phys_page`, does
|
||||||
|
* the access at (base + off_in_window), then restores the saved page.
|
||||||
|
* Safe in `tiny`, `small`, `big` memory modes (W3 is free for data).
|
||||||
|
* NOT safe in `huge` — banked code lives in W3 there.
|
||||||
|
* =================================================================== */
|
||||||
|
|
||||||
|
/* Read one byte from phys_page at offset off_in_window (0..0x3FFF). */
|
||||||
|
uint8_t bank_load_byte(uint8_t phys_page, uint16_t off_in_window);
|
||||||
|
|
||||||
|
/* Write byte `v` into phys_page at offset off_in_window. */
|
||||||
|
void bank_store_byte(uint8_t phys_page, uint16_t off_in_window, uint8_t v);
|
||||||
|
|
||||||
|
/* Copy `n` bytes from phys_page[off..off+n-1] into the near buffer `dst`. */
|
||||||
|
void bank_read(uint8_t phys_page, uint16_t off, void *dst, uint16_t n);
|
||||||
|
|
||||||
|
/* Copy `n` bytes from near buffer `src` into phys_page[off..off+n-1]. */
|
||||||
|
void bank_write(uint8_t phys_page, uint16_t off, const void *src, uint16_t n);
|
||||||
|
|
||||||
|
/* ===================================================================
|
||||||
|
* Far-page accessors via window 1 (base 0x4000, port 0xA2)
|
||||||
|
*
|
||||||
|
* Same semantics as the W3 family but swap W1 instead. SAFE ONLY in
|
||||||
|
* `--memory tiny` builds — in any other mode code lives in (or uses) W1
|
||||||
|
* and swapping it mid-call will crash. See header notes above for the
|
||||||
|
* full safety matrix.
|
||||||
|
* =================================================================== */
|
||||||
|
|
||||||
|
uint8_t bank_load_byte_w1(uint8_t phys_page, uint16_t off_in_window);
|
||||||
|
void bank_store_byte_w1(uint8_t phys_page, uint16_t off_in_window, uint8_t v);
|
||||||
|
void bank_read_w1(uint8_t phys_page, uint16_t off, void *dst, uint16_t n);
|
||||||
|
void bank_write_w1(uint8_t phys_page, uint16_t off, const void *src, uint16_t n);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ int vprintf(const char *, va_list);
|
|||||||
int vsprintf(char *, const char *, va_list);
|
int vsprintf(char *, const char *, va_list);
|
||||||
|
|
||||||
/* puts / putchar / getchar — overridden by our libc to use ESTEX. */
|
/* puts / putchar / getchar — overridden by our libc to use ESTEX. */
|
||||||
int puts (const char *);
|
char puts (const char *);
|
||||||
int putchar(int);
|
int putchar(int);
|
||||||
int getchar(void);
|
int getchar(void);
|
||||||
|
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
/*
|
|
||||||
* solid_compat.c — Solid-C compatibility helpers that need real code
|
|
||||||
* (rather than just header macros).
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <sprinter_compat.h>
|
|
||||||
#include <ctype.h>
|
|
||||||
|
|
||||||
char *strlwr(char *s)
|
|
||||||
{
|
|
||||||
char *p = s;
|
|
||||||
while (*p) {
|
|
||||||
if (*p >= 'A' && *p <= 'Z') *p += 'a' - 'A';
|
|
||||||
p++;
|
|
||||||
}
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *strupr(char *s)
|
|
||||||
{
|
|
||||||
char *p = s;
|
|
||||||
while (*p) {
|
|
||||||
if (*p >= 'a' && *p <= 'z') *p -= 'a' - 'A';
|
|
||||||
p++;
|
|
||||||
}
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* div() comes from SDCC's z80.lib. */
|
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* bank_io_w1.c — far-page accessors that swap window 1 (port 0xA2,
|
||||||
|
* base 0x4000). Mirror of bank_io.c but through W1.
|
||||||
|
*
|
||||||
|
* SAFE ONLY in `--memory tiny` builds. In tiny mode all code and data
|
||||||
|
* fit in W2, so both W1 and W3 are free for arbitrary banked data.
|
||||||
|
*
|
||||||
|
* small / huge: code lives in W1 (HOME) → swapping W1 unmaps the
|
||||||
|
* function mid-call and crashes.
|
||||||
|
* big : banked code segment lives in W1 → swap clobbers it.
|
||||||
|
*
|
||||||
|
* Split into its own .rel so that programs using only the W3 variants
|
||||||
|
* do NOT pull these functions in (and vice versa).
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sprinter.h>
|
||||||
|
#include <sprinter_mem.h>
|
||||||
|
|
||||||
|
uint8_t bank_load_byte_w1(uint8_t phys_page, uint16_t off_in_window)
|
||||||
|
{
|
||||||
|
uint8_t saved = _io_page_w1;
|
||||||
|
sprinter_page_w1(phys_page);
|
||||||
|
uint8_t v = *((volatile uint8_t *)(0x4000u + off_in_window));
|
||||||
|
sprinter_page_w1(saved);
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bank_store_byte_w1(uint8_t phys_page, uint16_t off_in_window, uint8_t v)
|
||||||
|
{
|
||||||
|
uint8_t saved = _io_page_w1;
|
||||||
|
sprinter_page_w1(phys_page);
|
||||||
|
*((volatile uint8_t *)(0x4000u + off_in_window)) = v;
|
||||||
|
sprinter_page_w1(saved);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bank_read_w1(uint8_t phys_page, uint16_t off, void *dst, uint16_t n)
|
||||||
|
{
|
||||||
|
uint8_t saved = _io_page_w1;
|
||||||
|
sprinter_page_w1(phys_page);
|
||||||
|
memcpy(dst, (const void *)(0x4000u + off), n);
|
||||||
|
sprinter_page_w1(saved);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bank_write_w1(uint8_t phys_page, uint16_t off, const void *src, uint16_t n)
|
||||||
|
{
|
||||||
|
uint8_t saved = _io_page_w1;
|
||||||
|
sprinter_page_w1(phys_page);
|
||||||
|
memcpy((void *)(0x4000u + off), src, n);
|
||||||
|
sprinter_page_w1(saved);
|
||||||
|
}
|
||||||
@@ -1,15 +1,16 @@
|
|||||||
/*
|
/*
|
||||||
* bank_io — HOME-resident helpers to read/write a "far" page that lives
|
* bank_io_w3.c — far-page accessors that swap window 3 (port 0xE2,
|
||||||
* in some physical RAM page outside the currently-mapped W3 bank.
|
* base 0xC000). Save the current W3 page, set the target, do the
|
||||||
|
* byte/memcpy access, restore the saved page.
|
||||||
*
|
*
|
||||||
* - We always run from HOME (window 1, always mapped), so we are free
|
* Safe in `tiny`, `small`, `big` memory modes (W3 is free for data).
|
||||||
* to swap W3 (port 0xE2) between the caller's bank and the data
|
* NOT safe in `huge` — there banked code lives in W3.
|
||||||
* page, then restore it before returning.
|
*
|
||||||
* - The caller must not rely on W3 contents during the call — the swap
|
* No DI/EI around the swap: ISRs that don't touch W3 are unaffected;
|
||||||
* is transparent to instruction-fetch (we execute from W1), and only
|
* concurrent W3 use from an ISR is unsafe.
|
||||||
* this function touches W3.
|
*
|
||||||
* - DI/EI is NOT applied around the swap; ISRs in HOME are unaffected,
|
* The `_w1` family (bank_io_w1.c) is the mirror through W1 and is
|
||||||
* and banked-call ISRs are not expected in our current design.
|
* `tiny`-only. See sprinter_mem.h for the full safety matrix.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
@@ -1,99 +0,0 @@
|
|||||||
/*
|
|
||||||
* mem_alloc_pages / mem_free_block / mem_get_page / mem_info — ESTEX EMM
|
|
||||||
* wrappers for explicit 16 KB-page allocation.
|
|
||||||
*
|
|
||||||
* ESTEX $3C INFOMEM → HL=total pages, BC=free pages
|
|
||||||
* ESTEX $3D GETMEM B=npages → A=block id, CF=err
|
|
||||||
* ESTEX $3E FREEMEM A=block id → CF=err
|
|
||||||
* BIOS $C4 EMM_GETPAGE A=blk, B=idx → A=physical page CF=err
|
|
||||||
*
|
|
||||||
* Pattern: every RST 10h / RST 8 is bracketed with push/pop IX because
|
|
||||||
* ESTEX/BIOS clobber it and the C caller uses it as a frame pointer.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <sprinter_mem.h>
|
|
||||||
|
|
||||||
uint8_t mem_alloc_pages(uint8_t n) __naked
|
|
||||||
{
|
|
||||||
(void)n;
|
|
||||||
__asm
|
|
||||||
;; SDCC single-uint8 arg → A on entry.
|
|
||||||
push ix
|
|
||||||
ld b, a
|
|
||||||
ld c, #0x3D
|
|
||||||
rst #0x10
|
|
||||||
pop ix
|
|
||||||
jr c, _alloc_fail
|
|
||||||
ret
|
|
||||||
_alloc_fail:
|
|
||||||
call __errno_set
|
|
||||||
xor a, a ; 0 = failure
|
|
||||||
ret
|
|
||||||
__endasm;
|
|
||||||
}
|
|
||||||
|
|
||||||
void mem_free_block(uint8_t blk_id) __naked
|
|
||||||
{
|
|
||||||
(void)blk_id;
|
|
||||||
__asm
|
|
||||||
;; SDCC single-uint8 arg → A on entry.
|
|
||||||
push ix
|
|
||||||
ld c, #0x3E
|
|
||||||
rst #0x10
|
|
||||||
pop ix
|
|
||||||
ret
|
|
||||||
__endasm;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t mem_get_page(uint8_t blk_id, uint8_t idx) __naked
|
|
||||||
{
|
|
||||||
(void)blk_id; (void)idx;
|
|
||||||
__asm
|
|
||||||
;; 2-arg uint8/uint8: blk_id → A, idx → L.
|
|
||||||
push ix
|
|
||||||
ld b, l ; BIOS wants idx in B
|
|
||||||
;; A still has blk_id
|
|
||||||
ld c, #0xC4 ; BIOS EMM_GETPAGE
|
|
||||||
rst #0x08
|
|
||||||
pop ix
|
|
||||||
;; A = physical page number. Return as uint8 → A.
|
|
||||||
ret
|
|
||||||
__endasm;
|
|
||||||
}
|
|
||||||
|
|
||||||
void mem_info(uint16_t *total, uint16_t *free_pages) __naked
|
|
||||||
{
|
|
||||||
(void)total; (void)free_pages;
|
|
||||||
__asm
|
|
||||||
;; HL = total, DE = free_pages on entry.
|
|
||||||
;; ESTEX INFOMEM clobbers everything; stash both pointers on stack.
|
|
||||||
push ix
|
|
||||||
push hl ; [SP+0..1] = total ptr
|
|
||||||
push de ; [SP+2..3] = free_pages ptr (wait wrong order)
|
|
||||||
|
|
||||||
;; Actually after two pushes: SP+0 = free_pages_ptr, SP+2 = total_ptr.
|
|
||||||
;; That's the layout we'll use below.
|
|
||||||
|
|
||||||
ld c, #0x3C ; ESTEX INFOMEM → HL=total, BC=free
|
|
||||||
rst #0x10
|
|
||||||
;; HL = total value, BC = free value.
|
|
||||||
|
|
||||||
pop de ; DE = free_pages ptr
|
|
||||||
ld a, c
|
|
||||||
ld (de), a
|
|
||||||
inc de
|
|
||||||
ld a, b
|
|
||||||
ld (de), a
|
|
||||||
|
|
||||||
pop de ; DE = total ptr
|
|
||||||
ld a, l
|
|
||||||
ld (de), a
|
|
||||||
inc de
|
|
||||||
ld a, h
|
|
||||||
ld (de), a
|
|
||||||
|
|
||||||
pop ix
|
|
||||||
ret
|
|
||||||
__endasm;
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,184 @@
|
|||||||
|
/*
|
||||||
|
* dec_print.c — compact decimal print primitives ported from solid-c
|
||||||
|
* (third_party/solid-c/SRC/CLIB/STDLIB.ASM, modules dec8/dec16/dec32).
|
||||||
|
*
|
||||||
|
* void dec8 (uint8_t v) — no-leading-zero "0" .. "255"
|
||||||
|
* void dec16(uint16_t v) — no-leading-zero "0" .. "65535"
|
||||||
|
* void dec32(uint32_t v) — no-leading-zero "0" .. "4294967295"
|
||||||
|
*
|
||||||
|
* Algorithm: subtract-power-of-10 with running counter, then emit the
|
||||||
|
* digit unless we are still on leading zeros. 32-bit values use a
|
||||||
|
* BC/DE/HL pair plus the shadow set (exx) for the high half.
|
||||||
|
*
|
||||||
|
* Layout: dec32 is the master routine; dec8/dec16 are tiny wrappers
|
||||||
|
* that prepare state and jump to internal entry points (__dec_entry3
|
||||||
|
* for "last 3 digits", __dec_entry5 for "last 5") — same idea as
|
||||||
|
* solid-c, sharing most of the per-digit code.
|
||||||
|
*
|
||||||
|
* ESTEX PUTCHAR ($5B) preserves IX (empirically verified) so no
|
||||||
|
* push/pop ix around the RST.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/* Leading-zero suppression flag — 0 means "still skipping zeros",
|
||||||
|
* non-zero means "first non-zero digit seen, print everything from
|
||||||
|
* here on (including subsequent zeros)". */
|
||||||
|
static uint8_t dec_flag = 0;
|
||||||
|
|
||||||
|
void dec8(uint8_t v) __naked
|
||||||
|
{
|
||||||
|
(void)v;
|
||||||
|
__asm
|
||||||
|
;; A = v.
|
||||||
|
ld l, a
|
||||||
|
ld h, #0 ; HL = value
|
||||||
|
xor a, a
|
||||||
|
ld (_dec_flag), a ; reset suppress-leading-zero flag
|
||||||
|
jp __dec_entry3
|
||||||
|
__endasm;
|
||||||
|
}
|
||||||
|
|
||||||
|
void dec16(uint16_t v) __naked
|
||||||
|
{
|
||||||
|
(void)v;
|
||||||
|
__asm
|
||||||
|
;; HL = v.
|
||||||
|
exx
|
||||||
|
ld hl, #0 ; HL alt = 0 (high 16 of composite)
|
||||||
|
exx
|
||||||
|
xor a, a
|
||||||
|
ld (_dec_flag), a
|
||||||
|
jp __dec_entry5
|
||||||
|
__endasm;
|
||||||
|
}
|
||||||
|
|
||||||
|
void dec32(uint32_t v) __naked
|
||||||
|
{
|
||||||
|
(void)v;
|
||||||
|
__asm
|
||||||
|
;; HL = high16, DE = low16 on entry (SDCC HLDE).
|
||||||
|
;; Move high16 into HL alt (shadow set), low16 into HL.
|
||||||
|
push hl
|
||||||
|
exx
|
||||||
|
pop hl ; HL alt = high16
|
||||||
|
exx
|
||||||
|
ex de, hl ; HL = low16
|
||||||
|
xor a, a
|
||||||
|
ld (_dec_flag), a
|
||||||
|
|
||||||
|
;; ---- 5 most-significant decades (1e9..1e5) ----
|
||||||
|
ld de, #0xCA00
|
||||||
|
exx
|
||||||
|
ld de, #0x3B9A ; 0x3B9ACA00 = 1,000,000,000
|
||||||
|
exx
|
||||||
|
call _dec_get_d32
|
||||||
|
|
||||||
|
ld de, #0xE100
|
||||||
|
exx
|
||||||
|
ld de, #0x05F5 ; 0x05F5E100 = 100,000,000
|
||||||
|
exx
|
||||||
|
call _dec_get_d32
|
||||||
|
|
||||||
|
ld de, #0x9680
|
||||||
|
exx
|
||||||
|
ld de, #0x0098 ; 0x00989680 = 10,000,000
|
||||||
|
exx
|
||||||
|
call _dec_get_d32
|
||||||
|
|
||||||
|
ld de, #0x4240
|
||||||
|
exx
|
||||||
|
ld de, #0x000F ; 0x000F4240 = 1,000,000
|
||||||
|
exx
|
||||||
|
call _dec_get_d32
|
||||||
|
|
||||||
|
ld de, #0x86A0
|
||||||
|
exx
|
||||||
|
ld de, #0x0001 ; 0x000186A0 = 100,000
|
||||||
|
exx
|
||||||
|
call _dec_get_d32
|
||||||
|
|
||||||
|
__dec_entry5:: ; entered from dec16
|
||||||
|
ld de, #10000
|
||||||
|
exx
|
||||||
|
ld de, #0
|
||||||
|
exx
|
||||||
|
call _dec_get_d32
|
||||||
|
|
||||||
|
ld de, #1000
|
||||||
|
call _dec_get_d16
|
||||||
|
|
||||||
|
__dec_entry3:: ; entered from dec8
|
||||||
|
ld de, #100
|
||||||
|
call _dec_get_d16
|
||||||
|
ld de, #10
|
||||||
|
call _dec_get_d16
|
||||||
|
|
||||||
|
;; Units digit — always emitted (so dec*(0) prints "0").
|
||||||
|
ld a, l
|
||||||
|
add a, #0x30
|
||||||
|
ld c, #0x5B
|
||||||
|
rst #0x10
|
||||||
|
ret
|
||||||
|
|
||||||
|
;; ---- 32-bit: how many times DE+DE_alt fits in HL+HL_alt ----
|
||||||
|
_dec_get_d32:
|
||||||
|
ld a, #0x2F ; 0x2F = '0' minus 1 (pre-decrement)
|
||||||
|
and a, a ; CF = 0
|
||||||
|
_dec_get_d32_loop:
|
||||||
|
inc a
|
||||||
|
sbc hl, de ; low half
|
||||||
|
exx
|
||||||
|
sbc hl, de ; high half (with chained borrow)
|
||||||
|
exx
|
||||||
|
jp nc, _dec_get_d32_loop
|
||||||
|
;; Overshot — restore the last good value.
|
||||||
|
add hl, de
|
||||||
|
exx
|
||||||
|
adc hl, de
|
||||||
|
exx
|
||||||
|
jr _dec_emit_or_skip
|
||||||
|
|
||||||
|
;; ---- 16-bit: how many times DE fits in HL ----
|
||||||
|
_dec_get_d16:
|
||||||
|
ld a, #0x2F
|
||||||
|
and a, a
|
||||||
|
_dec_get_d16_loop:
|
||||||
|
inc a
|
||||||
|
sbc hl, de
|
||||||
|
jp nc, _dec_get_d16_loop
|
||||||
|
add hl, de
|
||||||
|
;; Fall through to emit/skip.
|
||||||
|
|
||||||
|
_dec_emit_or_skip:
|
||||||
|
;; A = digit char in 0x30..0x39. If non-'0', latch the flag.
|
||||||
|
;; Print only if flag is non-zero.
|
||||||
|
ld b, a ; save digit across the flag test
|
||||||
|
cp a, #0x30
|
||||||
|
jr z, _dec_check_flag
|
||||||
|
ld (_dec_flag), a ; non-zero digit seen
|
||||||
|
_dec_check_flag:
|
||||||
|
ld a, (_dec_flag)
|
||||||
|
or a, a
|
||||||
|
ld a, b ; restore digit (ld does not touch flags)
|
||||||
|
ret z ; leading zero — skip print
|
||||||
|
|
||||||
|
;; ESTEX PUTCHAR ($5B) preserves the main register set (BC, DE,
|
||||||
|
;; HL, IX) but CLOBBERS the shadow set (BC alt, DE alt, HL alt).
|
||||||
|
;; The 32-bit subtract-power-of-10 loop in _dec_get_d32 keeps
|
||||||
|
;; the high 16 bits of the running remainder in HL alt, so we
|
||||||
|
;; save/restore HL alt around the RST. Main HL (= low 16 of
|
||||||
|
;; remainder) survives the call untouched, no save needed.
|
||||||
|
;; See memory/estex_putchar_abi.md.
|
||||||
|
exx
|
||||||
|
push hl
|
||||||
|
exx
|
||||||
|
ld c, #0x5B
|
||||||
|
rst #0x10
|
||||||
|
exx
|
||||||
|
pop hl
|
||||||
|
exx
|
||||||
|
ret
|
||||||
|
__endasm;
|
||||||
|
}
|
||||||
@@ -17,13 +17,7 @@ int getchar(void) __naked
|
|||||||
ld c, #0x30 ; ESTEX WAITKEY
|
ld c, #0x30 ; ESTEX WAITKEY
|
||||||
rst #0x10
|
rst #0x10
|
||||||
pop ix
|
pop ix
|
||||||
ld a, e ; E = ASCII (already the low byte of our return DE)
|
|
||||||
or a, a
|
|
||||||
jr Z, no_ascii
|
|
||||||
ld d, #0
|
ld d, #0
|
||||||
ret
|
ret
|
||||||
no_ascii:
|
|
||||||
ld de, #-1
|
|
||||||
ret
|
|
||||||
__endasm;
|
__endasm;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
* hex_print.c — compact hex print primitives ported from solid-c
|
||||||
|
* (third_party/solid-c/SRC/CLIB/STDLIB.ASM, modules hex8/hex16/hex32).
|
||||||
|
*
|
||||||
|
* void hex8 (uint8_t v) — always-2-digit "00" .. "FF"
|
||||||
|
* void hex16(uint16_t v) — always-4-digit "0000" .. "FFFF"
|
||||||
|
* void hex32(uint32_t v) — always-8-digit "00000000" .. "FFFFFFFF"
|
||||||
|
*
|
||||||
|
* Each nibble is emitted via the classic Z80 `cp 10 / sbc 0x69 / daa`
|
||||||
|
* trick — 5 bytes per nibble. hex8 self-calls for the high nibble
|
||||||
|
* then falls through for the low nibble. hex16/hex32 split into two
|
||||||
|
* hex8/hex16 calls.
|
||||||
|
*
|
||||||
|
* ESTEX PUTCHAR ($5B) preserves IX (empirically verified) so we skip
|
||||||
|
* the usual push/pop ix around the RST.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
void hex8(uint8_t v) __naked
|
||||||
|
{
|
||||||
|
(void)v;
|
||||||
|
__asm
|
||||||
|
;; A = v on entry.
|
||||||
|
push af
|
||||||
|
rra
|
||||||
|
rra
|
||||||
|
rra
|
||||||
|
rra
|
||||||
|
call _hex8_digit
|
||||||
|
pop af
|
||||||
|
_hex8_digit:
|
||||||
|
and a, #0x0F
|
||||||
|
cp a, #10
|
||||||
|
sbc a, #0x69
|
||||||
|
daa
|
||||||
|
ld c, #0x5B
|
||||||
|
rst #0x10
|
||||||
|
ret
|
||||||
|
__endasm;
|
||||||
|
}
|
||||||
|
|
||||||
|
void hex16(uint16_t v) __naked
|
||||||
|
{
|
||||||
|
(void)v;
|
||||||
|
__asm
|
||||||
|
;; HL = v on entry.
|
||||||
|
ld a, h
|
||||||
|
push hl
|
||||||
|
call _hex8
|
||||||
|
pop hl
|
||||||
|
ld a, l
|
||||||
|
jp _hex8 ; tail-call
|
||||||
|
__endasm;
|
||||||
|
}
|
||||||
|
|
||||||
|
void hex32(uint32_t v) __naked
|
||||||
|
{
|
||||||
|
(void)v;
|
||||||
|
__asm
|
||||||
|
;; HL = high16, DE = low16 on entry (SDCC HLDE).
|
||||||
|
push de
|
||||||
|
call _hex16
|
||||||
|
pop hl
|
||||||
|
jp _hex16 ; tail-call
|
||||||
|
__endasm;
|
||||||
|
}
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
/*
|
|
||||||
* print_hex — print a single byte as two uppercase hex digits.
|
|
||||||
*
|
|
||||||
* No printf yet; this is what bare-metal debug looks like in stage 3.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <sprinter.h>
|
|
||||||
|
|
||||||
void print_hex(uint8_t v)
|
|
||||||
{
|
|
||||||
static const char digits[] = "0123456789ABCDEF";
|
|
||||||
putchar(digits[(v >> 4) & 0x0F]);
|
|
||||||
putchar(digits[v & 0x0F]);
|
|
||||||
}
|
|
||||||
+6
-14
@@ -19,23 +19,15 @@ int putchar(int c) __naked
|
|||||||
{
|
{
|
||||||
(void)c;
|
(void)c;
|
||||||
__asm
|
__asm
|
||||||
ld a, l ; SDCC __sdcccall(1) int → HL
|
ld a, l
|
||||||
push ix
|
|
||||||
cp #0x0A
|
cp #0x0A
|
||||||
jr nz, _pc_emit
|
jr nz, cputc
|
||||||
ld a, #0x0D ; CR before LF
|
call cputc
|
||||||
push af
|
ld a, #0x0D
|
||||||
|
cputc:
|
||||||
ld c, #0x5B
|
ld c, #0x5B
|
||||||
rst #0x10
|
rst #0x10
|
||||||
pop af
|
ld e, l
|
||||||
ld a, #0x0A
|
|
||||||
_pc_emit:
|
|
||||||
push af
|
|
||||||
ld c, #0x5B
|
|
||||||
rst #0x10
|
|
||||||
pop af
|
|
||||||
pop ix
|
|
||||||
ld e, a
|
|
||||||
ld d, #0
|
ld d, #0
|
||||||
ret
|
ret
|
||||||
__endasm;
|
__endasm;
|
||||||
|
|||||||
+21
-34
@@ -14,17 +14,11 @@
|
|||||||
* - Avoid trailing PUTCHAR after PCHARS — empirically that sometimes
|
* - Avoid trailing PUTCHAR after PCHARS — empirically that sometimes
|
||||||
* drops the next char. Embed the line ending inside the PCHARS
|
* drops the next char. Embed the line ending inside the PCHARS
|
||||||
* buffer instead.
|
* buffer instead.
|
||||||
* - Strings longer than the buffer fall back to per-char putchar so
|
|
||||||
* we never silently truncate.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#define PUTS_BUF_SIZE 256 /* body bytes before CR expansion */
|
|
||||||
|
|
||||||
static char puts_buf[PUTS_BUF_SIZE + 3]; /* +3 for trailing CR LF NUL */
|
|
||||||
|
|
||||||
static void pchars(const char *s) __naked
|
static void pchars(const char *s) __naked
|
||||||
{
|
{
|
||||||
(void)s;
|
(void)s;
|
||||||
@@ -37,33 +31,26 @@ static void pchars(const char *s) __naked
|
|||||||
__endasm;
|
__endasm;
|
||||||
}
|
}
|
||||||
|
|
||||||
int puts(const char *s)
|
char puts(const char *s) __naked
|
||||||
{
|
{
|
||||||
uint16_t n = 0;
|
(void)s;
|
||||||
uint16_t i = 0;
|
__asm
|
||||||
|
puts_:
|
||||||
while (s[i] && n < PUTS_BUF_SIZE - 1) {
|
ld a, (hl)
|
||||||
char c = s[i++];
|
or a
|
||||||
if (c == '\n') {
|
jr z, fin_
|
||||||
puts_buf[n++] = '\r';
|
push hl
|
||||||
puts_buf[n++] = '\n';
|
ld l, a
|
||||||
} else {
|
ld h, #0
|
||||||
puts_buf[n++] = c;
|
call _putchar
|
||||||
}
|
pop hl
|
||||||
}
|
inc hl
|
||||||
|
jp puts_
|
||||||
if (s[i]) {
|
;
|
||||||
/* Overflow — char-by-char fallback so we never truncate. */
|
fin_:
|
||||||
for (uint16_t k = 0; s[k]; k++)
|
ld l, #0x0A
|
||||||
putchar((unsigned char)s[k]);
|
ld h, #0
|
||||||
putchar('\n');
|
call _putchar
|
||||||
return 0;
|
ret
|
||||||
}
|
__endasm;
|
||||||
|
|
||||||
puts_buf[n++] = '\r';
|
|
||||||
puts_buf[n++] = '\n';
|
|
||||||
puts_buf[n] = 0;
|
|
||||||
|
|
||||||
pchars(puts_buf);
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,61 +0,0 @@
|
|||||||
/*
|
|
||||||
* solid_helpers.c — small Solid-C compatibility helpers.
|
|
||||||
*
|
|
||||||
* Each function maps to the standard printf/sprintf machinery already
|
|
||||||
* available from SDCC's z80.lib + our overrides. No new syscalls.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
/* ---- gets — dangerous but Solid-C provides it ---------------------- */
|
|
||||||
char *gets(char *buf)
|
|
||||||
{
|
|
||||||
int i = 0;
|
|
||||||
int c;
|
|
||||||
for (;;) {
|
|
||||||
c = getchar();
|
|
||||||
if (c == EOF) {
|
|
||||||
if (i == 0) return 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (c == '\n' || c == '\r') break;
|
|
||||||
buf[i++] = (char)c;
|
|
||||||
}
|
|
||||||
buf[i] = 0;
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ---- decimal output: use printf %u ---------------------------------- */
|
|
||||||
|
|
||||||
void dec8(uint8_t v)
|
|
||||||
{
|
|
||||||
printf("%u", (unsigned)v);
|
|
||||||
}
|
|
||||||
|
|
||||||
void dec16(uint16_t v)
|
|
||||||
{
|
|
||||||
printf("%u", (unsigned)v);
|
|
||||||
}
|
|
||||||
|
|
||||||
void dec32(uint32_t v)
|
|
||||||
{
|
|
||||||
printf("%lu", (unsigned long)v);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ---- hex output: zero-padded ---------------------------------------- */
|
|
||||||
|
|
||||||
void hex8(uint8_t v)
|
|
||||||
{
|
|
||||||
printf("%02X", (unsigned)v);
|
|
||||||
}
|
|
||||||
|
|
||||||
void hex16(uint16_t v)
|
|
||||||
{
|
|
||||||
printf("%04X", (unsigned)v);
|
|
||||||
}
|
|
||||||
|
|
||||||
void hex32(uint32_t v)
|
|
||||||
{
|
|
||||||
printf("%08lX", (unsigned long)v);
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* solid_compat.c — Solid-C compatibility helpers that need real code
|
||||||
|
* (rather than just header macros).
|
||||||
|
*
|
||||||
|
* CP866 Cyrillic support: strlwr/strupr handle uppercase/lowercase
|
||||||
|
* conversion for both Latin and Cyrillic characters in CP866 code page
|
||||||
|
* (bytes 0x80–0xFF).
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sprinter_compat.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
char *strlwr(char *s)
|
||||||
|
{
|
||||||
|
char *p = s;
|
||||||
|
while (*p) {
|
||||||
|
if ((*p >= 'A' && *p <= 'Z') || (*p >= 0x80 && *p <= 0x8F)) *p += 'a' - 'A';
|
||||||
|
else if ((*p >= 0x90 && *p <= 0x9F)) *p += 0x50;
|
||||||
|
else if ((*p == 0xF0)) *p = 0xF1;
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* solid_compat.c — Solid-C compatibility helpers that need real code
|
||||||
|
* (rather than just header macros).
|
||||||
|
*
|
||||||
|
* CP866 Cyrillic support: strlwr/strupr handle uppercase/lowercase
|
||||||
|
* conversion for both Latin and Cyrillic characters in CP866 code page
|
||||||
|
* (bytes 0x80–0xFF).
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sprinter_compat.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
char *strupr(char *s)
|
||||||
|
{
|
||||||
|
char *p = s;
|
||||||
|
while (*p) {
|
||||||
|
if ((*p >= 'a' && *p <= 'z') || (*p >= 0xA0 && *p <= 0xAF)) *p -= 'a' - 'A';
|
||||||
|
else if ((*p >= 0xE0 && *p <= 0xEF)) *p -= 0x50;
|
||||||
|
else if ((*p == 0xF1)) *p = 0xF0;
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
@@ -2,4 +2,4 @@
|
|||||||
|
|
||||||
PROJ_ROOT := $(abspath $(CURDIR)/../..)
|
PROJ_ROOT := $(abspath $(CURDIR)/../..)
|
||||||
EXAMPLE := argv
|
EXAMPLE := argv
|
||||||
include $(PROJ_ROOT)/examples/example.mk
|
include $(PROJ_ROOT)/app.mk
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
PROJ_ROOT := $(abspath $(CURDIR)/../..)
|
PROJ_ROOT := $(abspath $(CURDIR)/../..)
|
||||||
EXAMPLE := assrtest
|
EXAMPLE := assrtest
|
||||||
include $(PROJ_ROOT)/examples/example.mk
|
include $(PROJ_ROOT)/app.mk
|
||||||
@@ -2,4 +2,4 @@
|
|||||||
|
|
||||||
PROJ_ROOT := $(abspath $(CURDIR)/../..)
|
PROJ_ROOT := $(abspath $(CURDIR)/../..)
|
||||||
EXAMPLE := attrprob
|
EXAMPLE := attrprob
|
||||||
include $(PROJ_ROOT)/examples/example.mk
|
include $(PROJ_ROOT)/app.mk
|
||||||
@@ -8,4 +8,4 @@ PROJ_ROOT := $(abspath $(CURDIR)/../..)
|
|||||||
EXAMPLE := banked
|
EXAMPLE := banked
|
||||||
MEMORY := huge
|
MEMORY := huge
|
||||||
EXTRA_FLAGS := --bank 1=bank1.c --bank 2=bank2.c
|
EXTRA_FLAGS := --bank 1=bank1.c --bank 2=bank2.c
|
||||||
include $(PROJ_ROOT)/examples/example.mk
|
include $(PROJ_ROOT)/app.mk
|
||||||
@@ -7,7 +7,7 @@ void bank1_func(int x) __banked
|
|||||||
(void)x;
|
(void)x;
|
||||||
puts("BANK1: hello from a banked function!");
|
puts("BANK1: hello from a banked function!");
|
||||||
puts("BANK1: window 3 phys page = ");
|
puts("BANK1: window 3 phys page = ");
|
||||||
print_hex(_io_page_w3); /* should be BANK1's phys page */
|
hex8(_io_page_w3); /* should be BANK1's phys page */
|
||||||
putchar('\n');
|
putchar('\n');
|
||||||
putchar('1');
|
putchar('1');
|
||||||
putchar('=');
|
putchar('=');
|
||||||
@@ -7,7 +7,7 @@ void bank2_func(int x) __banked
|
|||||||
(void)x;
|
(void)x;
|
||||||
puts("BANK2: hello from the second bank!");
|
puts("BANK2: hello from the second bank!");
|
||||||
puts("BANK2: window 3 phys page = ");
|
puts("BANK2: window 3 phys page = ");
|
||||||
print_hex(_io_page_w3); /* should be BANK2's phys page */
|
hex8(_io_page_w3); /* should be BANK2's phys page */
|
||||||
putchar('\n');
|
putchar('\n');
|
||||||
putchar('2');
|
putchar('2');
|
||||||
putchar('=');
|
putchar('=');
|
||||||
@@ -13,7 +13,7 @@ extern uint8_t bank_pages[]; /* filled by crt0_banked.s */
|
|||||||
static void show_w3(const char *who)
|
static void show_w3(const char *who)
|
||||||
{
|
{
|
||||||
puts(who);
|
puts(who);
|
||||||
print_hex(_io_page_w3); /* current physical page mapped in window 3 */
|
hex8(_io_page_w3); /* current physical page mapped in window 3 */
|
||||||
putchar('\n');
|
putchar('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,10 +26,10 @@ int main(void)
|
|||||||
show_w3("HOME: window 3 phys page = ");
|
show_w3("HOME: window 3 phys page = ");
|
||||||
|
|
||||||
puts("HOME: bank_pages[1] (BANK1 phys) = ");
|
puts("HOME: bank_pages[1] (BANK1 phys) = ");
|
||||||
print_hex(bank_pages[1]);
|
hex8(bank_pages[1]);
|
||||||
putchar('\n');
|
putchar('\n');
|
||||||
puts("HOME: bank_pages[2] (BANK2 phys) = ");
|
puts("HOME: bank_pages[2] (BANK2 phys) = ");
|
||||||
print_hex(bank_pages[2]);
|
hex8(bank_pages[2]);
|
||||||
putchar('\n');
|
putchar('\n');
|
||||||
|
|
||||||
puts("HOME: calling bank1_func(42)...");
|
puts("HOME: calling bank1_func(42)...");
|
||||||
@@ -9,4 +9,4 @@ PROJ_ROOT := $(abspath $(CURDIR)/../..)
|
|||||||
EXAMPLE := bankedbg
|
EXAMPLE := bankedbg
|
||||||
MEMORY := big
|
MEMORY := big
|
||||||
EXTRA_FLAGS := --bank 1=bank1.c --bank 2=bank2.c
|
EXTRA_FLAGS := --bank 1=bank1.c --bank 2=bank2.c
|
||||||
include $(PROJ_ROOT)/examples/example.mk
|
include $(PROJ_ROOT)/app.mk
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <sprinter.h>
|
||||||
|
|
||||||
|
void bank1_func(int x) __banked
|
||||||
|
{
|
||||||
|
(void)x;
|
||||||
|
puts("BANK1: hello from a banked function (W1)!");
|
||||||
|
puts("BANK1: window 1 phys page = ");
|
||||||
|
hex8(_io_page_w1); /* should be BANK1's phys page */
|
||||||
|
putchar('\n');
|
||||||
|
putchar('1');
|
||||||
|
putchar('=');
|
||||||
|
putchar('0' + (x / 10) % 10);
|
||||||
|
putchar('0' + x % 10);
|
||||||
|
putchar('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
void bank1_func2(int x) __banked
|
||||||
|
{
|
||||||
|
(void)x;
|
||||||
|
puts("BANK1-2: hello from a banked function (W1)!");
|
||||||
|
puts("BANK1-2: window 1 phys page = ");
|
||||||
|
hex8(_io_page_w1); /* should be BANK1's phys page */
|
||||||
|
putchar('\n');
|
||||||
|
putchar('1');
|
||||||
|
putchar('=');
|
||||||
|
putchar('0' + (x / 10) % 10);
|
||||||
|
putchar('0' + x % 10);
|
||||||
|
putchar('\n');
|
||||||
|
}
|
||||||
@@ -2,12 +2,14 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <sprinter.h>
|
#include <sprinter.h>
|
||||||
|
|
||||||
|
void bank1_func2(int x) __banked;
|
||||||
|
|
||||||
void bank2_func(int x) __banked
|
void bank2_func(int x) __banked
|
||||||
{
|
{
|
||||||
(void)x;
|
(void)x;
|
||||||
puts("BANK2: hello from the second bank (W1)!");
|
puts("BANK2: hello from the second bank (W1)!");
|
||||||
puts("BANK2: window 1 phys page = ");
|
puts("BANK2: window 1 phys page = ");
|
||||||
print_hex(_io_page_w1); /* should be BANK2's phys page */
|
hex8(_io_page_w1); /* should be BANK2's phys page */
|
||||||
putchar('\n');
|
putchar('\n');
|
||||||
putchar('2');
|
putchar('2');
|
||||||
putchar('=');
|
putchar('=');
|
||||||
@@ -15,4 +17,6 @@ void bank2_func(int x) __banked
|
|||||||
putchar('0' + (x / 10) % 10);
|
putchar('0' + (x / 10) % 10);
|
||||||
putchar('0' + x % 10);
|
putchar('0' + x % 10);
|
||||||
putchar('\n');
|
putchar('\n');
|
||||||
|
|
||||||
|
bank1_func2(10);
|
||||||
}
|
}
|
||||||
@@ -24,7 +24,7 @@ extern uint8_t bank_pages[]; /* filled by crt0_banked.s */
|
|||||||
static void show_w1(const char *who)
|
static void show_w1(const char *who)
|
||||||
{
|
{
|
||||||
puts(who);
|
puts(who);
|
||||||
print_hex(_io_page_w1); /* current physical page mapped in window 1 */
|
hex8(_io_page_w1); /* current physical page mapped in window 1 */
|
||||||
putchar('\n');
|
putchar('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,10 +37,10 @@ int main(void)
|
|||||||
show_w1("HOME: window 1 phys page = ");
|
show_w1("HOME: window 1 phys page = ");
|
||||||
|
|
||||||
puts("HOME: bank_pages[1] (BANK1 phys) = ");
|
puts("HOME: bank_pages[1] (BANK1 phys) = ");
|
||||||
print_hex(bank_pages[1]);
|
hex8(bank_pages[1]);
|
||||||
putchar('\n');
|
putchar('\n');
|
||||||
puts("HOME: bank_pages[2] (BANK2 phys) = ");
|
puts("HOME: bank_pages[2] (BANK2 phys) = ");
|
||||||
print_hex(bank_pages[2]);
|
hex8(bank_pages[2]);
|
||||||
putchar('\n');
|
putchar('\n');
|
||||||
|
|
||||||
puts("HOME: calling bank1_func(42)...");
|
puts("HOME: calling bank1_func(42)...");
|
||||||
@@ -9,4 +9,4 @@ PROJ_ROOT := $(abspath $(CURDIR)/../..)
|
|||||||
EXAMPLE := banklocl
|
EXAMPLE := banklocl
|
||||||
MEMORY := huge
|
MEMORY := huge
|
||||||
EXTRA_FLAGS := --bank 1=bank1.c --mkexe -p --mkexe 0
|
EXTRA_FLAGS := --bank 1=bank1.c --mkexe -p --mkexe 0
|
||||||
include $(PROJ_ROOT)/examples/example.mk
|
include $(PROJ_ROOT)/app.mk
|
||||||
@@ -3,4 +3,4 @@
|
|||||||
PROJ_ROOT := $(abspath $(CURDIR)/../..)
|
PROJ_ROOT := $(abspath $(CURDIR)/../..)
|
||||||
EXAMPLE := cat
|
EXAMPLE := cat
|
||||||
EXTRA_DATA := test.txt
|
EXTRA_DATA := test.txt
|
||||||
include $(PROJ_ROOT)/examples/example.mk
|
include $(PROJ_ROOT)/app.mk
|
||||||
@@ -2,4 +2,4 @@
|
|||||||
|
|
||||||
PROJ_ROOT := $(abspath $(CURDIR)/../..)
|
PROJ_ROOT := $(abspath $(CURDIR)/../..)
|
||||||
EXAMPLE := conio
|
EXAMPLE := conio
|
||||||
include $(PROJ_ROOT)/examples/example.mk
|
include $(PROJ_ROOT)/app.mk
|
||||||
@@ -2,4 +2,4 @@
|
|||||||
|
|
||||||
PROJ_ROOT := $(abspath $(CURDIR)/../..)
|
PROJ_ROOT := $(abspath $(CURDIR)/../..)
|
||||||
EXAMPLE := conio2
|
EXAMPLE := conio2
|
||||||
include $(PROJ_ROOT)/examples/example.mk
|
include $(PROJ_ROOT)/app.mk
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
PROJ_ROOT := $(abspath $(CURDIR)/../..)
|
||||||
|
EXAMPLE := dec_test
|
||||||
|
include $(PROJ_ROOT)/app.mk
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
extern void dec8 (unsigned char);
|
||||||
|
extern void dec16(unsigned int);
|
||||||
|
extern void dec32(unsigned long);
|
||||||
|
extern void hex8 (unsigned char);
|
||||||
|
extern void hex16(unsigned int);
|
||||||
|
extern void hex32(unsigned long);
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
puts("hex8:");
|
||||||
|
hex8(0x00); puts("");
|
||||||
|
hex8(0x5A); puts("");
|
||||||
|
hex8(0xFF); puts("");
|
||||||
|
puts("hex16:");
|
||||||
|
hex16(0x0000); puts("");
|
||||||
|
hex16(0xCAFE); puts("");
|
||||||
|
hex16(0xFFFF); puts("");
|
||||||
|
puts("hex32:");
|
||||||
|
hex32(0x00000000UL); puts("");
|
||||||
|
hex32(0xDEADBEEFUL); puts("");
|
||||||
|
hex32(0xFFFFFFFFUL); puts("");
|
||||||
|
puts("dec8:");
|
||||||
|
dec8(0); puts("");
|
||||||
|
dec8(42); puts("");
|
||||||
|
dec8(255); puts("");
|
||||||
|
puts("dec16:");
|
||||||
|
dec16(0); puts("");
|
||||||
|
dec16(1234); puts("");
|
||||||
|
dec16(65535); puts("");
|
||||||
|
puts("dec32:");
|
||||||
|
dec32(0UL); puts("");
|
||||||
|
dec32(98765UL); puts("");
|
||||||
|
dec32(123456UL); puts("");
|
||||||
|
dec32(123456789UL); puts("");
|
||||||
|
dec32(4294967295UL); puts("");
|
||||||
|
puts("done — any key");
|
||||||
|
(void)getchar();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -2,4 +2,4 @@
|
|||||||
|
|
||||||
PROJ_ROOT := $(abspath $(CURDIR)/../..)
|
PROJ_ROOT := $(abspath $(CURDIR)/../..)
|
||||||
EXAMPLE := errno
|
EXAMPLE := errno
|
||||||
include $(PROJ_ROOT)/examples/example.mk
|
include $(PROJ_ROOT)/app.mk
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
PROJ_ROOT := $(abspath $(CURDIR)/../..)
|
PROJ_ROOT := $(abspath $(CURDIR)/../..)
|
||||||
EXAMPLE := filetest
|
EXAMPLE := filetest
|
||||||
include $(PROJ_ROOT)/examples/example.mk
|
include $(PROJ_ROOT)/app.mk
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# Build cat.exe — uses lib/sprinter.lib in TINY memory mode.
|
||||||
|
|
||||||
|
PROJ_ROOT := $(abspath $(CURDIR)/../..)
|
||||||
|
EXAMPLE := gets
|
||||||
|
include $(PROJ_ROOT)/app.mk
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
char buff [256];
|
||||||
|
|
||||||
|
puts("--- gets test ---");
|
||||||
|
|
||||||
|
gets(buff);
|
||||||
|
|
||||||
|
puts("");
|
||||||
|
puts("loaded string:");
|
||||||
|
puts(buff);
|
||||||
|
puts("done");
|
||||||
|
(void)getchar();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -2,4 +2,4 @@
|
|||||||
|
|
||||||
PROJ_ROOT := $(abspath $(CURDIR)/../..)
|
PROJ_ROOT := $(abspath $(CURDIR)/../..)
|
||||||
EXAMPLE := gfx_d16
|
EXAMPLE := gfx_d16
|
||||||
include $(PROJ_ROOT)/examples/example.mk
|
include $(PROJ_ROOT)/app.mk
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
PROJ_ROOT := $(abspath $(CURDIR)/../..)
|
PROJ_ROOT := $(abspath $(CURDIR)/../..)
|
||||||
EXAMPLE := gfx_dbuf
|
EXAMPLE := gfx_dbuf
|
||||||
include $(PROJ_ROOT)/examples/example.mk
|
include $(PROJ_ROOT)/app.mk
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
PROJ_ROOT := $(abspath $(CURDIR)/../..)
|
PROJ_ROOT := $(abspath $(CURDIR)/../..)
|
||||||
EXAMPLE := gfx_demo
|
EXAMPLE := gfx_demo
|
||||||
include $(PROJ_ROOT)/examples/example.mk
|
include $(PROJ_ROOT)/app.mk
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
PROJ_ROOT := $(abspath $(CURDIR)/../..)
|
PROJ_ROOT := $(abspath $(CURDIR)/../..)
|
||||||
EXAMPLE := gfx_mous
|
EXAMPLE := gfx_mous
|
||||||
include $(PROJ_ROOT)/examples/example.mk
|
include $(PROJ_ROOT)/app.mk
|
||||||
@@ -2,4 +2,4 @@
|
|||||||
|
|
||||||
PROJ_ROOT := $(abspath $(CURDIR)/../..)
|
PROJ_ROOT := $(abspath $(CURDIR)/../..)
|
||||||
EXAMPLE := gfx_text
|
EXAMPLE := gfx_text
|
||||||
include $(PROJ_ROOT)/examples/example.mk
|
include $(PROJ_ROOT)/app.mk
|
||||||
@@ -2,4 +2,4 @@
|
|||||||
|
|
||||||
PROJ_ROOT := $(abspath $(CURDIR)/../..)
|
PROJ_ROOT := $(abspath $(CURDIR)/../..)
|
||||||
EXAMPLE := hello
|
EXAMPLE := hello
|
||||||
include $(PROJ_ROOT)/examples/example.mk
|
include $(PROJ_ROOT)/app.mk
|
||||||
@@ -52,7 +52,7 @@ int main(void)
|
|||||||
|
|
||||||
/* Back to a normal attribute so the goodbye reads cleanly. */
|
/* Back to a normal attribute so the goodbye reads cleanly. */
|
||||||
textattr(COLOR(COLOR_LIGHTGRAY, COLOR_BLACK));
|
textattr(COLOR(COLOR_LIGHTGRAY, COLOR_BLACK));
|
||||||
printf("a0=%d a1=%d now=%d errno=%d\n",
|
cprintf("a0=%d a1=%d now=%d errno=%d\n\r",
|
||||||
a0, a1, get_text_attr(), errno);
|
a0, a1, get_text_attr(), errno);
|
||||||
#ifdef DEBUG_RT
|
#ifdef DEBUG_RT
|
||||||
printf("w2_self_allocated = %u\n", w2_self_allocated);
|
printf("w2_self_allocated = %u\n", w2_self_allocated);
|
||||||
@@ -2,4 +2,4 @@
|
|||||||
|
|
||||||
PROJ_ROOT := $(abspath $(CURDIR)/../..)
|
PROJ_ROOT := $(abspath $(CURDIR)/../..)
|
||||||
EXAMPLE := ls
|
EXAMPLE := ls
|
||||||
include $(PROJ_ROOT)/examples/example.mk
|
include $(PROJ_ROOT)/app.mk
|
||||||
@@ -2,4 +2,4 @@
|
|||||||
|
|
||||||
PROJ_ROOT := $(abspath $(CURDIR)/../..)
|
PROJ_ROOT := $(abspath $(CURDIR)/../..)
|
||||||
EXAMPLE := malloc
|
EXAMPLE := malloc
|
||||||
include $(PROJ_ROOT)/examples/example.mk
|
include $(PROJ_ROOT)/app.mk
|
||||||
@@ -2,4 +2,4 @@
|
|||||||
|
|
||||||
PROJ_ROOT := $(abspath $(CURDIR)/../..)
|
PROJ_ROOT := $(abspath $(CURDIR)/../..)
|
||||||
EXAMPLE := mem_test
|
EXAMPLE := mem_test
|
||||||
include $(PROJ_ROOT)/examples/example.mk
|
include $(PROJ_ROOT)/app.mk
|
||||||
@@ -16,6 +16,8 @@
|
|||||||
* 5. Free the block, show the free-page count again.
|
* 5. Free the block, show the free-page count again.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
uint32_t buff[256];
|
||||||
|
|
||||||
static void show_mem(const char *label)
|
static void show_mem(const char *label)
|
||||||
{
|
{
|
||||||
uint16_t total, free_pages;
|
uint16_t total, free_pages;
|
||||||
@@ -29,6 +31,13 @@ int main(void)
|
|||||||
puts("Sprinter page allocator demo");
|
puts("Sprinter page allocator demo");
|
||||||
puts("");
|
puts("");
|
||||||
|
|
||||||
|
memset(buff, 0, sizeof(buff));
|
||||||
|
// printf("mem_io_ports_test() = 0x%02X\n", mem_io_ports_test());
|
||||||
|
printf("buff size (sizeof) = %u\n", sizeof(buff));
|
||||||
|
printf("buff size one element (sizeof) = %u\n", sizeof(buff[0]));
|
||||||
|
printf("buff first element = %u\n", buff[0]);
|
||||||
|
printf("buff last element = %u\n", buff[(sizeof(buff) / sizeof(buff[0])) - 1]);
|
||||||
|
|
||||||
show_mem("before:");
|
show_mem("before:");
|
||||||
|
|
||||||
uint8_t blk = mem_alloc_pages(3);
|
uint8_t blk = mem_alloc_pages(3);
|
||||||
@@ -2,4 +2,4 @@
|
|||||||
|
|
||||||
PROJ_ROOT := $(abspath $(CURDIR)/../..)
|
PROJ_ROOT := $(abspath $(CURDIR)/../..)
|
||||||
EXAMPLE := mouse
|
EXAMPLE := mouse
|
||||||
include $(PROJ_ROOT)/examples/example.mk
|
include $(PROJ_ROOT)/app.mk
|
||||||
@@ -10,6 +10,8 @@
|
|||||||
#include <conio.h>
|
#include <conio.h>
|
||||||
#include <mouse.h>
|
#include <mouse.h>
|
||||||
|
|
||||||
|
mouse_state_t st;
|
||||||
|
|
||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
textattr(COLOR(COLOR_LIGHTGRAY, COLOR_BLACK));
|
textattr(COLOR(COLOR_LIGHTGRAY, COLOR_BLACK));
|
||||||
@@ -29,7 +31,6 @@ int main(void)
|
|||||||
mouse_bounds_y(0, 255);
|
mouse_bounds_y(0, 255);
|
||||||
mouse_show();
|
mouse_show();
|
||||||
|
|
||||||
mouse_state_t st;
|
|
||||||
int last_x = -1, last_y = -1;
|
int last_x = -1, last_y = -1;
|
||||||
uint8_t last_btn = 0xFF;
|
uint8_t last_btn = 0xFF;
|
||||||
/* Sensitivity is a "raw steps per cursor pixel" divider: smaller =
|
/* Sensitivity is a "raw steps per cursor pixel" divider: smaller =
|
||||||
@@ -41,11 +42,13 @@ int main(void)
|
|||||||
mouse_set_sensitivity(sens_x, sens_y);
|
mouse_set_sensitivity(sens_x, sens_y);
|
||||||
int sens_dirty = 1;
|
int sens_dirty = 1;
|
||||||
|
|
||||||
|
st.x = st.y = 0; st.buttons = 0;
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
mouse_read(&st);
|
mouse_read(&st);
|
||||||
if (st.x != last_x || st.y != last_y || st.buttons != last_btn) {
|
if (st.x != last_x || st.y != last_y || st.buttons != last_btn) {
|
||||||
gotoxy(0, 6);
|
gotoxy(0, 6);
|
||||||
cprintf("x=%4u y=%4u text(%2u,%2u) buttons=0x%02X L%c R%c ",
|
printf("x=%4u y=%4u text(%2u,%2u) buttons=0x%02X L%c R%c ",
|
||||||
st.x, st.y,
|
st.x, st.y,
|
||||||
st.x / 8, st.y / 8,
|
st.x / 8, st.y / 8,
|
||||||
st.buttons,
|
st.buttons,
|
||||||
@@ -57,7 +60,7 @@ int main(void)
|
|||||||
}
|
}
|
||||||
if (sens_dirty) {
|
if (sens_dirty) {
|
||||||
gotoxy(0, 8);
|
gotoxy(0, 8);
|
||||||
cprintf("sensitivity horz=%3u vert=%3u ", sens_x, sens_y);
|
printf("sensitivity horz=%3u vert=%3u ", sens_x, sens_y);
|
||||||
sens_dirty = 0;
|
sens_dirty = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
# Build open_env_test.exe — uses lib/sprinter.lib in TINY memory mode.
|
||||||
|
|
||||||
|
PROJ_ROOT := $(abspath $(CURDIR)/../..)
|
||||||
|
EXAMPLE := openenv
|
||||||
|
MEMORY := big
|
||||||
|
EXTRA_FLAGS :=
|
||||||
|
include $(PROJ_ROOT)/app.mk
|
||||||
@@ -12,6 +12,9 @@
|
|||||||
* verifies O_CREAT / O_EXCL / O_TRUNC / O_APPEND behaviour, then deletes.
|
* verifies O_CREAT / O_EXCL / O_TRUNC / O_APPEND behaviour, then deletes.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
extern uint8_t estex_file_handle;
|
||||||
|
|
||||||
|
|
||||||
static void show_errno(const char *label)
|
static void show_errno(const char *label)
|
||||||
{
|
{
|
||||||
printf(" %s: errno=%d \"%s\"\n", label, errno, strerror(errno));
|
printf(" %s: errno=%d \"%s\"\n", label, errno, strerror(errno));
|
||||||
@@ -22,6 +25,8 @@ int main(void)
|
|||||||
puts("Sprinter open() + env API test");
|
puts("Sprinter open() + env API test");
|
||||||
puts("");
|
puts("");
|
||||||
|
|
||||||
|
printf("estex_file_handle: fd=%u\n", estex_file_handle);
|
||||||
|
|
||||||
/* --- open() state machine ------------------------------------- */
|
/* --- open() state machine ------------------------------------- */
|
||||||
|
|
||||||
/* 1. O_CREAT|O_EXCL: must succeed first time, must fail on retry. */
|
/* 1. O_CREAT|O_EXCL: must succeed first time, must fail on retry. */
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user