From 5897e550942d0d844aa87727340a5b2f8f901618 Mon Sep 17 00:00:00 2001 From: Joppe Blondel Date: Sat, 3 Oct 2026 16:29:41 +0200 Subject: [PATCH] start of tape loading. hw works and have a generation script --- .vscode/settings.json | 4 + Makefile | 96 ++++++++++---- README.md | 82 ++++++++---- apps/README.md | 28 ++-- apps/casette/CUPL.PLD | 199 +++++++++++++++++++++++++++++ apps/casette/casette.jed | 54 ++++++++ apps/casette/encode.py | 189 +++++++++++++++++++++++++++ apps/casette/tape_loader.asm | 50 ++++++++ apps/{ => tinybasic}/tinybasic.asm | 4 +- bios.h | 1 + boot.asm | 48 ++++--- include/bios.inc | 23 ++-- monitor.c | 50 +++++++- program_ihex.py | 47 +++++-- rom_layout.mk | 15 +++ test.wav | Bin 0 -> 5710124 bytes 16 files changed, 784 insertions(+), 106 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 apps/casette/CUPL.PLD create mode 100644 apps/casette/casette.jed create mode 100644 apps/casette/encode.py create mode 100644 apps/casette/tape_loader.asm rename apps/{ => tinybasic}/tinybasic.asm (99%) create mode 100644 rom_layout.mk create mode 100644 test.wav diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..44901e7 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "vs-cupl.PathOpenOcd": "/usr/sbin/openocd", + "vs-cupl.PathOpenOcdDl": "/usr/sbin/openocd" +} \ No newline at end of file diff --git a/Makefile b/Makefile index 3c862f2..e9f4be9 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,11 @@ TARGET := monitor BUILDDIR := build +LAYOUT_CONFIG := rom_layout.mk +LAYOUT_BUILD := $(BUILDDIR)/generated +LAYOUT_HEADER := $(LAYOUT_BUILD)/rom_layout.h +LAYOUT_INCLUDE := $(LAYOUT_BUILD)/rom_layout.inc + +include $(LAYOUT_CONFIG) METAL_C_SRCFILES := $(wildcard *.c) METAL_ASM_SRCFILES := boot.asm @@ -9,18 +15,16 @@ METAL_RELFILES := $(METAL_ASM_SRCFILES:%.asm=$(BUILDDIR)/metal/%.rel) \ METAL_CC := sdcc METAL_AS := sdasz80 METAL_MAKEBIN := makebin -METAL_FLAGS := -mz80 --no-std-crt0 --code-loc 0xC080 --code-size 0x0F80 --data-loc 0xF000 --opt-code-size -METAL_CPPFLAGS := -I src -I $(BUILDDIR)/metal/generated --disable-warning 283 -METAL_BIN_SIZE := 32768 +METAL_FLAGS := -mz80 --no-std-crt0 --code-loc $(RESIDENT_CODE_BASE) --code-size $(RESIDENT_CODE_SIZE) --data-loc $(RESIDENT_DATA_BASE) --opt-code-size +METAL_CPPFLAGS := -I src -I $(LAYOUT_BUILD) --disable-warning 283 +METAL_BIN_SIZE := $(ROM_SIZE) METAL_RAM_SIZE := 16384 METAL_IMAGE_SIZE := 65536 -RESIDENT_BASE := 0xC000 -RESIDENT_ROM_OFFSET := 128 -TINYBASIC_BASE := 0x1000 -TINYBASIC_ROM_OFFSET := 4096 -TINYBASIC_SRC := apps/tinybasic.asm +TINYBASIC_SRC := apps/tinybasic/tinybasic.asm TINYBASIC_BUILD := $(BUILDDIR)/tinybasic +TAPE_LOADER_SRC := apps/casette/tape_loader.asm +TAPE_LOADER_BUILD := $(BUILDDIR)/casette .DEFAULT_GOAL := metal @@ -38,7 +42,12 @@ metal: $(BUILDDIR)/metal/$(TARGET).ihx $(BUILDDIR)/metal/$(TARGET).bin $(BUILDDI printf " ROM storage: %5u / %5u bytes (%5.1f%%)\n", stored, rom_capacity, 100 * stored / rom_capacity; \ printf " Resident BIOS+monitor: %5u bytes (%5u code/vector + %5u static data)\n", code + data, code, data; \ }' $(BUILDDIR)/metal/$(TARGET).map - @printf " Tiny BASIC: %5u ROM bytes at 1000\n" $$(wc -c < $(TINYBASIC_BUILD)/tinybasic.bin) + @printf " Tiny BASIC: %5u ROM bytes at %04X\n" \ + $$(wc -c < $(TINYBASIC_BUILD)/tinybasic.bin) \ + $$(( $(TINYBASIC_ROM_ADDRESS) )) + @printf " Tape loader: %5u ROM bytes at %04X\n" \ + $$(wc -c < $(TAPE_LOADER_BUILD)/tape_loader.bin) \ + $$(( $(TAPE_LOADER_ROM_ADDRESS) )) $(BUILDDIR)/metal/$(TARGET).ihx: $(METAL_RELFILES) @mkdir -p $(@D) @@ -47,31 +56,48 @@ $(BUILDDIR)/metal/$(TARGET).ihx: $(METAL_RELFILES) $(BUILDDIR)/metal/$(TARGET).flat.bin: $(BUILDDIR)/metal/$(TARGET).ihx $(METAL_MAKEBIN) -s $(METAL_IMAGE_SIZE) $< $@ -$(BUILDDIR)/metal/$(TARGET).prog.bin: $(BUILDDIR)/metal/$(TARGET).flat.bin $(TINYBASIC_BUILD)/tinybasic.bin +$(BUILDDIR)/metal/$(TARGET).prog.bin: $(BUILDDIR)/metal/$(TARGET).flat.bin $(TINYBASIC_BUILD)/tinybasic.bin $(TAPE_LOADER_BUILD)/tape_loader.bin @set -e; \ payload_end=$$(awk '$$1 == "DEF" && $$2 == "resident_image_end" { print $$3 }' $(BUILDDIR)/metal/$(TARGET).noi); \ - payload_size=$$((payload_end - $(RESIDENT_BASE))); \ - test $$payload_size -gt 0; \ - test $$payload_size -le $$(( $(TINYBASIC_ROM_OFFSET) - $(RESIDENT_ROM_OFFSET) )); \ - dd if=$< of=$@ bs=1 count=$(TINYBASIC_ROM_OFFSET) status=none; \ - dd if=$< of=$@ bs=1 skip=$$(( $(RESIDENT_BASE) )) seek=$(RESIDENT_ROM_OFFSET) count=$$payload_size conv=notrunc status=none; \ - dd if=$(TINYBASIC_BUILD)/tinybasic.bin of=$@ bs=1 seek=$(TINYBASIC_ROM_OFFSET) conv=notrunc status=none + payload_size=$$((payload_end - $(RESIDENT_RAM_BASE))); \ + payload_rom_end=$$(( $(RESIDENT_ROM_OFFSET) + payload_size )); \ + tiny_size=$$(wc -c < $(TINYBASIC_BUILD)/tinybasic.bin); \ + tape_size=$$(wc -c < $(TAPE_LOADER_BUILD)/tape_loader.bin); \ + if test $$payload_size -le 0; then \ + echo "error: resident image is empty or has an invalid end symbol" >&2; exit 1; \ + fi; \ + if test $$payload_rom_end -gt $$(( $(TINYBASIC_ROM_ADDRESS) )); then \ + printf "error: resident ROM image ends at %04X, beyond Tiny BASIC origin %04X\n" \ + $$payload_rom_end $$(( $(TINYBASIC_ROM_ADDRESS) )) >&2; exit 1; \ + fi; \ + if test $$(( $(TINYBASIC_ROM_ADDRESS) + tiny_size )) -gt $$(( $(TAPE_LOADER_ROM_ADDRESS) )); then \ + printf "error: Tiny BASIC ends at %04X, beyond tape-loader origin %04X\n" \ + $$(( $(TINYBASIC_ROM_ADDRESS) + tiny_size )) $$(( $(TAPE_LOADER_ROM_ADDRESS) )) >&2; exit 1; \ + fi; \ + if test $$(( $(TAPE_LOADER_ROM_ADDRESS) + tape_size )) -gt $$(( $(ROM_SIZE) )); then \ + printf "error: tape loader ends at %04X, beyond ROM size %04X\n" \ + $$(( $(TAPE_LOADER_ROM_ADDRESS) + tape_size )) $$(( $(ROM_SIZE) )) >&2; exit 1; \ + fi; \ + dd if=$< of=$@ bs=1 count=$$(( $(TAPE_LOADER_ROM_ADDRESS) )) status=none; \ + dd if=$< of=$@ bs=1 skip=$$(( $(RESIDENT_RAM_BASE) )) seek=$$(( $(RESIDENT_ROM_OFFSET) )) count=$$payload_size conv=notrunc status=none; \ + dd if=$(TINYBASIC_BUILD)/tinybasic.bin of=$@ bs=1 seek=$$(( $(TINYBASIC_ROM_ADDRESS) )) conv=notrunc status=none; \ + dd if=$(TAPE_LOADER_BUILD)/tape_loader.bin of=$@ bs=1 seek=$$(( $(TAPE_LOADER_ROM_ADDRESS) )) conv=notrunc status=none $(BUILDDIR)/metal/$(TARGET).bin: $(BUILDDIR)/metal/$(TARGET).flat.bin $(BUILDDIR)/metal/$(TARGET).prog.bin dd if=$< of=$@ bs=$(METAL_BIN_SIZE) count=1 status=none dd if=$(BUILDDIR)/metal/$(TARGET).prog.bin of=$@ conv=notrunc status=none -$(BUILDDIR)/metal/%.rel: %.c +$(BUILDDIR)/metal/%.rel: %.c $(LAYOUT_HEADER) @mkdir -p $(@D) $(METAL_CC) $(METAL_FLAGS) $(METAL_CPPFLAGS) -o $@ -c $< -$(BUILDDIR)/metal/%.rel: %.asm +$(BUILDDIR)/metal/%.rel: %.asm $(LAYOUT_INCLUDE) @mkdir -p $(@D) - $(METAL_AS) -Iinclude -I$(BUILDDIR)/metal/generated -plosgff -o $@ $< + $(METAL_AS) -Iinclude -I$(LAYOUT_BUILD) -plosgff -o $@ $< -$(TINYBASIC_BUILD)/tinybasic.rel: $(TINYBASIC_SRC) +$(TINYBASIC_BUILD)/tinybasic.rel: $(TINYBASIC_SRC) $(LAYOUT_INCLUDE) @mkdir -p $(@D) - $(METAL_AS) -Iinclude -plosgff -o $@ $< + $(METAL_AS) -Iinclude -I$(LAYOUT_BUILD) -plosgff -o $@ $< $(TINYBASIC_BUILD)/tinybasic.ihx: $(TINYBASIC_BUILD)/tinybasic.rel sdldz80 -n -i -m -j $(basename $@) $< @@ -82,7 +108,33 @@ $(TINYBASIC_BUILD)/tinybasic.flat.bin: $(TINYBASIC_BUILD)/tinybasic.ihx $(TINYBASIC_BUILD)/tinybasic.bin: $(TINYBASIC_BUILD)/tinybasic.flat.bin @tiny_size=$$(awk '$$1 == "TINYBASIC0" { print $$5 + 0 }' $(TINYBASIC_BUILD)/tinybasic.map); \ test $$tiny_size -gt 0; \ - dd if=$< of=$@ bs=1 skip=$$(( $(TINYBASIC_BASE) )) count=$$tiny_size status=none + dd if=$< of=$@ bs=1 skip=$$(( $(TINYBASIC_ROM_ADDRESS) )) count=$$tiny_size status=none + +$(TAPE_LOADER_BUILD)/tape_loader.rel: $(TAPE_LOADER_SRC) $(LAYOUT_INCLUDE) + @mkdir -p $(@D) + $(METAL_AS) -Iinclude -I$(LAYOUT_BUILD) -plosgff -o $@ $< + +$(TAPE_LOADER_BUILD)/tape_loader.ihx: $(TAPE_LOADER_BUILD)/tape_loader.rel + sdldz80 -n -i -m -j $(basename $@) $< + +$(TAPE_LOADER_BUILD)/tape_loader.flat.bin: $(TAPE_LOADER_BUILD)/tape_loader.ihx + $(METAL_MAKEBIN) -s $(METAL_IMAGE_SIZE) $< $@ + +$(TAPE_LOADER_BUILD)/tape_loader.bin: $(TAPE_LOADER_BUILD)/tape_loader.flat.bin + @loader_size=$$(awk '$$1 == "TAPELOAD0" { print $$5 + 0 }' $(TAPE_LOADER_BUILD)/tape_loader.map); \ + test $$loader_size -gt 0; \ + dd if=$< of=$@ bs=1 skip=$$(( $(TAPE_LOADER_ROM_ADDRESS) )) count=$$loader_size status=none + +$(LAYOUT_HEADER): $(LAYOUT_CONFIG) + @mkdir -p $(@D) + @awk 'BEGIN { print "#ifndef ROM_LAYOUT_H"; print "#define ROM_LAYOUT_H"; print "" } \ + /^[A-Z][A-Z0-9_]*[[:space:]]*:=[[:space:]]*/ { print "#define " $$1 " " $$3 } \ + END { print ""; print "#endif" }' $< > $@ + +$(LAYOUT_INCLUDE): $(LAYOUT_CONFIG) + @mkdir -p $(@D) + @awk 'BEGIN { print "; Generated from rom_layout.mk; do not edit." } \ + /^[A-Z][A-Z0-9_]*[[:space:]]*:=[[:space:]]*/ { print $$1 " = " $$3 }' $< > $@ clean: rm -rf $(BUILDDIR) diff --git a/README.md b/README.md index 8af579e..897df65 100644 --- a/README.md +++ b/README.md @@ -8,8 +8,8 @@ At reset, a small ROM bootstrap copies only the BIOS and monitor into the top remains enabled, banking is enabled, and `C000-FFFF` maps to the upper physical 16 KiB. The BIOS then initializes interrupt mode 2 and the UART and starts the monitor. Applications remain in ROM and execute in place while calling the -stable BIOS jump table in high RAM. Tiny BASIC is the first such application, -linked at ROM address `1000`. +stable BIOS jump table in high RAM. Tiny BASIC is linked at ROM address `1400`, +and the tape loader is linked at `3000`. Connect a terminal at **2400 baud, 8 data bits, no parity, 1 stop bit**. Commands are case-insensitive and all numeric arguments are hexadecimal; an @@ -23,9 +23,14 @@ optional `0x` prefix is accepted. | `W 8000 3E 41 D3 25 C9` | Write bytes starting at address 8000 | | `I 20` | Read I/O port 20 | | `O 20 57` | Write 57 to I/O port 20 | +| `K 0` or `BANK 0` | Select lower physical 48K plus upper physical 16K | +| `K 1` or `BANK 1` | Select the complete upper physical 64K | +| `R ON` or `ROM ON` | Enable ROM while preserving the RAM bank selection | +| `R OFF` or `ROM OFF` | Disable ROM while preserving the RAM bank selection | | `G 8000` | Execute code at address 8000 | -| `G 1000` | Start the ROM-resident Z80 Tiny BASIC interpreter | -| `B` or `BASIC` | Shortcut for `G 1000` | +| `G 1400` | Start the ROM-resident Z80 Tiny BASIC interpreter | +| `B` or `BASIC` | Shortcut for `G 1400` | +| `T` or `TAPE` | Start the ROM-resident tape loader at `3000` | | `HELP` | Show the command summary | `G` transfers control with a return address on the stack, so code ending in @@ -55,12 +60,17 @@ python3 program_ihex.py /dev/ttyUSB0 program.hex ``` The default connection is 2400 baud, the default block size is 16 bytes, and -three retries are allowed after the initial attempt. For example: +three retries are allowed after the initial attempt. Characters are paced with +a 5 ms delay because the monitor UART can lose bytes from an uninterrupted USB +serial burst. For example: ```sh python3 program_ihex.py /dev/ttyUSB0 program.hex --retries 5 --block-size 24 ``` +Use `--char-delay 0.01` if commands still arrive with missing characters. A +smaller value uploads faster; `--char-delay 0` disables host-side pacing. + Use a Windows port such as `COM3` in place of `/dev/ttyUSB0`. Run `python3 program_ihex.py --help` for all options. The monitor must be sitting at its `>` prompt before starting the upload. Only data records present in the HEX @@ -72,14 +82,17 @@ The outputs are: - `build/metal/monitor.bin`: complete 32 KiB ROM image - `build/metal/monitor.prog.bin`: compact image ending at the last occupied byte - `build/tinybasic/tinybasic.bin`: standalone relocated Tiny BASIC payload +- `build/casette/tape_loader.bin`: standalone relocated tape-loader payload -The current compact image is 6,157 bytes. Its ROM layout is: +The current compact image is 12,318 bytes. Its ROM layout is: - `0000-007F`: reset/copy bootstrap -- `0080-0FEA`: packed BIOS+monitor image copied to RAM at `C000-CF6A` -- `0FEB-0FFF`: unused space reserved for the resident image to grow -- `1000-180C`: Tiny BASIC, executed directly from ROM -- `180D-7FFF`: unused space available for additional ROM content +- `0080-1201`: packed BIOS+monitor image copied to RAM at `C000-D181` +- `1202-13FF`: unused space reserved for the resident image to grow +- `1400-1C0C`: Tiny BASIC, executed directly from ROM +- `1C0D-2FFF`: unused ROM +- `3000-301D`: tape loader, executed directly from ROM +- `301E-7FFF`: unused space available for additional ROM content The resident image starts at `C000`, its IM 2 vector table is at `C040`, and linked code starts at `C080`. The bootstrap obtains the resident copy length @@ -90,8 +103,8 @@ The resulting RAM layout is: - `8000-AFFF`: Tiny BASIC workspace and BASIC program text - `B000-B077`: Tiny BASIC variables and input buffer - `B078-BFFF`: Tiny BASIC stack space -- `C000-CF6A`: resident BIOS, vectors, services, and monitor -- `CF6B-EFFF`: free space for resident-image growth +- `C000-D181`: resident BIOS, vectors, services, and monitor +- `D182-EFFF`: free space for resident-image growth - `F000-FFFF`: BIOS globals, UART buffers, and monitor stack The BIOS and monitor continue to work if low ROM is disabled. ROM-resident @@ -119,6 +132,7 @@ Assembly applications include `include/bios.inc` and call these fixed entries: | `C062` | `BIOS_MONITOR` | Warm-start the monitor; does not return | | `C065` | `BIOS_BANK` | `A=0`: split lower 48K/upper 16K; otherwise select upper 64K | | `C068` | `BIOS_ROM_DISABLE` | Disable ROM while retaining the selected RAM mapping | +| `C06B` | `BIOS_ROM_ENABLE` | Enable ROM while retaining the selected RAM mapping | The service wrappers preserve working registers except for documented return values. The monitor uses the corresponding C API declared in `bios.h`. @@ -127,38 +141,47 @@ The banking latch at port `00` is write-only, so the BIOS keeps a persistent shadow byte initialized to `06` by the copied resident image. `BIOS_BANK` always leaves banking enabled and changes only the bank selection while retaining the shadowed ROM-disable bit and auxiliary-output bits. -`BIOS_ROM_DISABLE` sets the ROM-disable bit and is idempotent. Code should not -write port `00` through `BIOS_IO_OUT` or the monitor's `O` command after using -these services, because such writes bypass the BIOS shadow state. +`BIOS_ROM_DISABLE` sets the ROM-disable bit and `BIOS_ROM_ENABLE` clears it; +both are idempotent. Code should not write port `00` through `BIOS_IO_OUT` or +the monitor's `O` command after using these services, because such writes +bypass the BIOS shadow state. Use the monitor's `BANK` and `ROM` commands +instead. The equivalent C API is `bios_bank(uint8_t bank)` and -`bios_rom_disable(void)`. A ROM-resident caller cannot return normally after -selecting the complete upper 64 KiB or disabling ROM, because its return -address is no longer mapped; it must arrange its continuation in RAM. +`bios_rom_disable(void)` / `bios_rom_enable(void)`. A ROM-resident caller cannot +return normally after selecting the complete upper 64 KiB or disabling ROM, +because its return address is no longer mapped; it must arrange its +continuation in RAM. Project organization: - `boot.asm`: ROM bootstrap, high vectors, BIOS jump table, and assembly glue - `bios.c` / `bios.h`: UART, buffers, interrupts, and resident BIOS API - `monitor.c`: command interpreter implemented on the BIOS API -- `apps/tinybasic.asm`: first ROM-resident application +- `rom_layout.mk`: authoritative ROM and resident-memory addresses +- `apps/tinybasic/tinybasic.asm`: Tiny BASIC ROM application +- `apps/casette/tape_loader.asm`: tape-loader ROM application - `apps/README.md`: ROM application memory and integration contract - `include/bios.inc`: stable assembly interface for ROM applications +The build generates `rom_layout.h` for C and `rom_layout.inc` for assembly from +`rom_layout.mk`. Change application origins and resident-memory addresses only +in `rom_layout.mk`; the generated files are removed by `make clean`. + ## Tiny BASIC -Tiny BASIC is built from `apps/tinybasic.asm`. The port replaces fixed low-page -`RST` calls with relocatable calls and uses a marker-based command table so it -can be linked at a nonzero ROM address. +Tiny BASIC is built from `apps/tinybasic/tinybasic.asm`. The port replaces fixed +low-page `RST` calls with relocatable calls and uses a marker-based command +table so it can be linked at a nonzero ROM address. -Tiny BASIC is linked at and runs directly from ROM address `1000`. Start it from +Tiny BASIC is linked at and runs directly from ROM address `1400`. Start it from the monitor with: ```text B ``` -`BASIC` and `G 1000` are equivalent alternatives. +`BASIC` and `G 1400` are equivalent alternatives. Tiny BASIC calls the BIOS at `C050` for output and `C053` for non-blocking input. Tiny BASIC keeps interrupts enabled so it can share the BIOS-configured @@ -174,5 +197,12 @@ returns to the Tiny BASIC prompt. Tiny BASIC resets the stack pointer when it starts, so it does not currently return through the monitor's `G` call. Reset the machine to return to the -monitor. The first currently unused ROM byte is `180D`; `1900` is a convenient -aligned starting point for another ROM payload. +monitor. The tape loader occupies the later ROM slot at `3000-301D`; the first +unused byte after it is `301E`. + +## Tape loader + +The tape-loader image is built from `apps/casette/tape_loader.asm`, linked at +`3000`, and included in both ROM images. Start it with `T`, `TAPE`, or `G 3000`. +The current image polls the comparator input and prints its state through the +resident BIOS console service. diff --git a/apps/README.md b/apps/README.md index 1c2fa9f..910576f 100644 --- a/apps/README.md +++ b/apps/README.md @@ -7,25 +7,29 @@ directory instead of accessing the UART hardware directly. The current allocation is: -- `1000-180C`: Tiny BASIC -- `180D-7FFF`: available for more applications (`1900` is the next convenient +- `1400-1C0C`: Tiny BASIC +- `1C0D-2FFF`: available +- `3000-301D`: tape loader +- `301E-7FFF`: available for more applications (`3100` is the next convenient aligned origin) The resident BIOS and monitor own high RAM at `C000-FFFF`. Tiny BASIC owns -`8000-BFFF` for its workspace and stack. A future application may reuse Tiny -BASIC's lower-RAM allocation because ROM applications run one at a time, but it -must leave `C000-FFFF` untouched. +`8000-BFFF` for its workspace and stack. Applications may reuse this lower-RAM +allocation because they run one at a time, but they must leave `C000-FFFF` +untouched. Applications using the interrupt-driven console must leave interrupt mode 2, the `I` register, and the BIOS UART/CTC configuration intact. They may return normally if they preserve the caller's stack, or jump to `BIOS_MONITOR` for a non-returning monitor warm start after replacing the stack pointer. -Use `BIOS_BANK` and `BIOS_ROM_DISABLE` for the write-only banking latch rather -than writing port `00` directly. The BIOS keeps the ROM-disable state when the -RAM bank selection changes. A ROM application must not expect a normal `RET` -after it maps out the ROM containing its return address. +Use `BIOS_BANK`, `BIOS_ROM_DISABLE`, and `BIOS_ROM_ENABLE` for the write-only +banking latch rather than writing port `00` directly. The BIOS keeps the +ROM-disable state when the RAM bank selection changes. A ROM application must +not expect a normal `RET` after it maps out the ROM containing its return +address. -To add an application, add its assembler target and ROM offset to the Makefile, -overlay its binary in the `monitor.prog.bin` recipe, and add a monitor command -or use `G address` to launch it. +Application origins and the resident-memory layout are defined centrally in +`rom_layout.mk`. The build turns that file into C and assembly includes. To add +an application, add its address there, add its build and overlay rules to the +Makefile, and add a monitor command or use `G address` to launch it. diff --git a/apps/casette/CUPL.PLD b/apps/casette/CUPL.PLD new file mode 100644 index 0000000..8ef1851 --- /dev/null +++ b/apps/casette/CUPL.PLD @@ -0,0 +1,199 @@ +Name casette; +PartNo 00; +Date 2026-10-03; +Revision 01; +Designer joppe; +Company unknown; +Assembly None; +Location unknown; +Device g22v10; + + +/* ------------------------------------------------------------ + Constants + ------------------------------------------------------------ */ + +ar = 'b'0; +sp = 'b'0; + + +/* ------------------------------------------------------------ + Pin assignments + + Physical signals IOSEL, RD and WR are active-low. + Their polarity is expressed in the equations with !. + ------------------------------------------------------------ */ + +/* inputs */ + +PIN 1 = CLK; + +PIN 2 = A1; +PIN 3 = A2; + +PIN 4 = IOSEL; +PIN 5 = RD; +PIN 6 = A0; + +PIN 7 = COMP; +PIN 8 = WR; + + +/* data bus */ + +PIN 14 = D0; +PIN 15 = D1; +PIN 16 = D2; +PIN 17 = D3; +PIN 18 = D4; +PIN 19 = D5; +PIN 20 = D6; +PIN 21 = D7; + + +/* registered output */ + +PIN 22 = LED; + + +/* ------------------------------------------------------------ + Read register at offset 0 + + Conditions: + + /IOSEL = 0 + /RD = 0 + /WR = 1 + A2:A0 = 000 + + Result: + + D0 = COMP + D7..D1 = 0 + ------------------------------------------------------------ */ + +D0 = COMP; + +D1 = 'b'0; +D2 = 'b'0; +D3 = 'b'0; +D4 = 'b'0; +D5 = 'b'0; +D6 = 'b'0; +D7 = 'b'0; + + +D0.OE = + !IOSEL + & !RD + & WR + & !A2 + & !A1 + & !A0; + +D1.OE = + !IOSEL + & !RD + & WR + & !A2 + & !A1 + & !A0; + +D2.OE = + !IOSEL + & !RD + & WR + & !A2 + & !A1 + & !A0; + +D3.OE = + !IOSEL + & !RD + & WR + & !A2 + & !A1 + & !A0; + +D4.OE = + !IOSEL + & !RD + & WR + & !A2 + & !A1 + & !A0; + +D5.OE = + !IOSEL + & !RD + & WR + & !A2 + & !A1 + & !A0; + +D6.OE = + !IOSEL + & !RD + & WR + & !A2 + & !A1 + & !A0; + +D7.OE = + !IOSEL + & !RD + & WR + & !A2 + & !A1 + & !A0; + + +/* ------------------------------------------------------------ + Writable LED register + + Write conditions: + + /IOSEL = 0 + /RD = 1 + /WR = 0 + A2:A0 = 000 + + During such a write: + + LED.D = D0 + + Otherwise: + + LED.D = LED + + Pin 1 (CLK) clocks the registered macrocell. + ------------------------------------------------------------ */ + +LED.D = + ( + !IOSEL + & RD + & !WR + & !A2 + & !A1 + & !A0 + & D0 + ) + + # + + ( + ( + IOSEL + # !RD + # WR + # A2 + # A1 + # A0 + ) + & LED + ); + + +LED.ar = ar; +LED.sp = sp; \ No newline at end of file diff --git a/apps/casette/casette.jed b/apps/casette/casette.jed new file mode 100644 index 0000000..4ae757a --- /dev/null +++ b/apps/casette/casette.jed @@ -0,0 +1,54 @@ + +CUPL(WM) 5.0a Serial# MW-10400000 +Device g22v10 Library DLIB-h-40-1 +Created Sat Oct 03 16:22:02 2026 +Name casette +Partno 00 +Revision 01 +Date 2026-10-03 +Designer joppe +Company unknown +Assembly None +Location unknown +*QP24 +*QF5892 +*G1 +*F0 +*L00416 00000000000000000000000011111111 +*L00448 11111111111111111111111111111111 +*L00480 11111111101110111011011110111111 +*L00512 10111111110111111111111011110111 +*L00544 11111111111111111111111111111111 +*L00576 11101111111110111111111111111111 +*L00608 11111111111111101111111111111111 +*L00640 11110111111111111111111111100111 +*L00672 11111111111111111111111111111111 +*L00704 11110110111111111111111111111111 +*L00736 11111111111111111110111111111111 +*L00768 01111111111111111111111100000000 +*L00896 00000000000000000000000000001111 +*L00928 10111011101110111011111101111111 +*L00960 11111111000000000000000000000000 +*L01472 00000000000000000000000011111011 +*L01504 10111011101110111111011111111111 +*L01536 11110000000000000000000000000000 +*L02144 00000000000011111011101110111011 +*L02176 10111111011111111111111100000000 +*L02880 00000000000000000000000011111011 +*L02912 10111011101110111111011111111111 +*L02944 11110000000000000000000000000000 +*L03648 00001111101110111011101110111111 +*L03680 01111111111111110000000000000000 +*L04288 00000000000000000000000011111011 +*L04320 10111011101110111111011111111111 +*L04352 11110000000000000000000000000000 +*L04864 00000000000000000000111110111011 +*L04896 10111011101111110111111111111111 +*L05344 00000000000000000000000011111011 +*L05376 10111011101110111111011111111111 +*L05408 11111111111111111111111111110111 +*L05440 11111111111111110000000000000000 +*L05792 00000000000000000010111111111111 +*L05824 11110011000000110000000000000000 +*C5BE4 +*6A87 \ No newline at end of file diff --git a/apps/casette/encode.py b/apps/casette/encode.py new file mode 100644 index 0000000..b29bc8d --- /dev/null +++ b/apps/casette/encode.py @@ -0,0 +1,189 @@ +#!/usr/bin/env python3 +""" +z80_tape_encode.py + +Encode a binary file as a mono PCM WAV using robust two-tone Manchester FSK. + +Defaults: +- 48 kHz sample rate +- 600 bit/s +- 2 kHz / 4 kHz tones +- Manchester FSK: + 0 -> low tone for half a bit, then high tone + 1 -> high tone for half a bit, then low tone +- 1 second 4 kHz leader +- 64 bytes of 0x55 preamble +- 256-byte CRC-protected blocks +- CRC-16/CCITT-FALSE +""" + +from __future__ import annotations + +import argparse +import math +import struct +import wave +from pathlib import Path + +SYNC = b"\xD3\x91" +PREAMBLE_BYTE = 0x55 + + +def crc16_ccitt_false(data: bytes) -> int: + crc = 0xFFFF + for byte in data: + crc ^= byte << 8 + for _ in range(8): + crc = ((crc << 1) ^ 0x1021) & 0xFFFF if (crc & 0x8000) else (crc << 1) & 0xFFFF + return crc + + +class FSKWriter: + def __init__(self, sample_rate: int, baud: int, f_low: float, f_high: float, amplitude: float): + if sample_rate % (2 * baud) != 0: + raise ValueError("sample_rate must be divisible by 2*baud") + self.sample_rate = sample_rate + self.baud = baud + self.f_low = f_low + self.f_high = f_high + self.amplitude = amplitude + self.samples_per_half_bit = sample_rate // (2 * baud) + self.phase = 0.0 + self.samples = bytearray() + + def _tone(self, frequency: float, sample_count: int) -> None: + phase_step = 2.0 * math.pi * frequency / self.sample_rate + scale = int(32767 * self.amplitude) + phase = self.phase + out = self.samples + + for _ in range(sample_count): + sample = int(round(scale * math.sin(phase))) + out.extend(struct.pack("= 2.0 * math.pi: + phase -= 2.0 * math.pi + + self.phase = phase + + def leader(self, seconds: float) -> None: + self._tone(self.f_high, round(seconds * self.sample_rate)) + + def bit(self, value: int) -> None: + # Manchester-FSK: + # 0 = low -> high + # 1 = high -> low + if value: + first, second = self.f_high, self.f_low + else: + first, second = self.f_low, self.f_high + + self._tone(first, self.samples_per_half_bit) + self._tone(second, self.samples_per_half_bit) + + def byte(self, value: int) -> None: + # MSB first + for bit_no in range(7, -1, -1): + self.bit((value >> bit_no) & 1) + + def bytes(self, data: bytes) -> None: + for value in data: + self.byte(value) + + +def make_block(sequence: int, payload: bytes) -> bytes: + header = struct.pack(" bytes: + header = struct.pack(" None: + data = input_path.read_bytes() + + if not 0 < amplitude <= 1.0: + raise ValueError("amplitude must be > 0 and <= 1") + if not 1 <= block_size <= 0xFFFF: + raise ValueError("block_size must be in 1..65535") + + writer = FSKWriter(sample_rate, baud, f_low, f_high, amplitude) + + writer.leader(leader_seconds) + writer.bytes(bytes([PREAMBLE_BYTE]) * preamble_bytes) + + sequence = 0 + for start in range(0, len(data), block_size): + payload = data[start:start + block_size] + writer.bytes(make_block(sequence, payload)) + sequence = (sequence + 1) & 0xFFFF + + writer.bytes(make_eof_block(sequence)) + + with wave.open(str(output_path), "wb") as wav: + wav.setnchannels(1) + wav.setsampwidth(2) + wav.setframerate(sample_rate) + wav.writeframes(writer.samples) + + duration = len(writer.samples) / 2 / sample_rate + + print(f"Input: {input_path}") + print(f"Output: {output_path}") + print(f"Input bytes: {len(data)}") + print(f"Bit rate: {baud} bit/s") + print(f"Tones: {f_low:g} Hz / {f_high:g} Hz") + print(f"Sample rate: {sample_rate} Hz") + print(f"Half-bit: {writer.samples_per_half_bit} samples") + print(f"Block size: {block_size} bytes") + print(f"WAV duration: {duration:.2f} s") + + +def main() -> None: + parser = argparse.ArgumentParser(description="Encode a binary file as Manchester two-tone FSK WAV.") + parser.add_argument("input", type=Path, help="binary input file") + parser.add_argument("output", type=Path, nargs="?", help="output WAV; default: INPUT.wav") + parser.add_argument("--baud", type=int, default=600) + parser.add_argument("--low", type=float, default=2000.0, help="low tone in Hz") + parser.add_argument("--high", type=float, default=4000.0, help="high tone in Hz") + parser.add_argument("--sample-rate", type=int, default=48000) + parser.add_argument("--leader", type=float, default=1.0, help="leader seconds") + parser.add_argument("--preamble", type=int, default=64, help="number of 0x55 preamble bytes") + parser.add_argument("--block-size", type=int, default=64) + parser.add_argument("--amplitude", type=float, default=0.75) + + args = parser.parse_args() + output = args.output or args.input.with_name(args.input.name + ".wav") + + encode( + args.input, + output, + sample_rate=args.sample_rate, + baud=args.baud, + f_low=args.low, + f_high=args.high, + leader_seconds=args.leader, + preamble_bytes=args.preamble, + block_size=args.block_size, + amplitude=args.amplitude, + ) + + +if __name__ == "__main__": + main() diff --git a/apps/casette/tape_loader.asm b/apps/casette/tape_loader.asm new file mode 100644 index 0000000..328a438 --- /dev/null +++ b/apps/casette/tape_loader.asm @@ -0,0 +1,50 @@ +.include "bios.inc" + +.area TAPELOAD (ABS) +.org TAPE_LOADER_ROM_ADDRESS + +COMP_PORT = 0x18 + +WORK_BASE = 0x0B000 +LOAD_START = 0x08000 +LOAD_LIMIT_HI = 0x0B0 +STACK_TOP = 0x0BFFF + +OLD_SP = WORK_BASE+0x00 + +start: + ld (OLD_SP), sp + ld sp, #STACK_TOP + + di + +poll_loop: + + ; Read from the hardware port COMP_PORT + in a, (COMP_PORT) + out (COMP_PORT), a + + ; Save the raw port status in register B + ld b, a + + ; Print a carriage return '\r' (ASCII 0x0D) + ld a, #0x0D + call BIOS_PUTC + + ; Restore the port status and isolate the relevant bit + ; (Assuming bit 0 determines 0 or 1; adjust 'and' mask if it is a different bit) + ld a, b + and #0x01 + + ; Convert the binary 0 or 1 into ASCII '0' (0x30) or '1' (0x31) + add a, #0x30 + + ; Print the '0' or '1' character + call BIOS_PUTC + + ; Infinite loop: poll continuously + jr poll_loop + + ld sp, (OLD_SP) + ei + ret diff --git a/apps/tinybasic.asm b/apps/tinybasic/tinybasic.asm similarity index 99% rename from apps/tinybasic.asm rename to apps/tinybasic/tinybasic.asm index eb632af..a123fe0 100644 --- a/apps/tinybasic.asm +++ b/apps/tinybasic/tinybasic.asm @@ -42,7 +42,7 @@ ; Adapted to work with GNU z80asm ; Added interrupt handling ; Notes for this monitor port: -; Converted to sdasz80 syntax and linked as a ROM application at 1000. +; Converted to sdasz80 syntax and linked as a ROM application at 2200. ; Fixed-page RST calls were converted to local CALLs. ; UART access uses the resident BIOS ABI at C050/C053. @@ -102,7 +102,7 @@ STKLMT = 0xB078 ;LOWEST ALLOWED STACK ADDRESS ;************************************************************* .area TINYBASIC (ABS) - .org 0x1000 + .org TINYBASIC_ROM_ADDRESS main: START: LD SP,#STACK ;*** COLD START *** diff --git a/bios.h b/bios.h index fc8c72b..6968a0e 100644 --- a/bios.h +++ b/bios.h @@ -14,6 +14,7 @@ void bios_flush(void); /* Write-only banking latch services. */ void bios_bank(uint8_t bank); void bios_rom_disable(void); +void bios_rom_enable(void); /* Low-level routines implemented by the resident assembly BIOS. */ void io_out(uint8_t port, uint8_t value); diff --git a/boot.asm b/boot.asm index 76b7af3..1434ffd 100644 --- a/boot.asm +++ b/boot.asm @@ -1,7 +1,5 @@ STACK_TOP = 0xFFFF DELAY_LOOPS_PER_MS = 40 -RESIDENT_BASE = 0xC000 -RESIDENT_ROM_SOURCE = 0x0080 BANK_CONTROL_PORT = 0x00 BOOT_BANK_MAPPING = 0x06 ; ROM on, banking on, upper 16K at C000-FFFF ROM_DISABLE_BIT = 0x01 @@ -24,6 +22,7 @@ BANK_MODE_CLEAR_MASK = 0xF9 ; preserve ROM-disable and auxiliary outputs .globl _bios_init .globl _bios_bank .globl _bios_rom_disable + .globl _bios_rom_enable .globl _bank_control_shadow .globl resident_image_end .globl s__DATA @@ -44,34 +43,34 @@ boot_copy: ld a, #BOOT_BANK_MAPPING out (BANK_CONTROL_PORT), a ld sp, #STACK_TOP - ld hl, #RESIDENT_ROM_SOURCE - ld de, #RESIDENT_BASE - ld bc, #(resident_image_end - RESIDENT_BASE) + ld hl, #RESIDENT_ROM_OFFSET + ld de, #RESIDENT_RAM_BASE + ld bc, #(resident_image_end - RESIDENT_RAM_BASE) ldir jp init .area _ITABLE (ABS) ; The executable vector page is part of the image copied to high RAM. -.org RESIDENT_BASE +.org RESIDENT_RAM_BASE jp init -.org RESIDENT_BASE + 0x08 +.org RESIDENT_RAM_BASE + 0x08 reti -.org RESIDENT_BASE + 0x10 +.org RESIDENT_RAM_BASE + 0x10 reti -.org RESIDENT_BASE + 0x18 +.org RESIDENT_RAM_BASE + 0x18 reti -.org RESIDENT_BASE + 0x20 +.org RESIDENT_RAM_BASE + 0x20 reti -.org RESIDENT_BASE + 0x28 +.org RESIDENT_RAM_BASE + 0x28 reti -.org RESIDENT_BASE + 0x30 +.org RESIDENT_RAM_BASE + 0x30 reti -.org RESIDENT_BASE + 0x38 +.org RESIDENT_RAM_BASE + 0x38 reti ; IM 2 table: the CTC supplies vectors 40, 42, 44, and 46. -.org RESIDENT_BASE + 0x40 +.org RESIDENT_RAM_BASE + 0x40 .dw ctc_isr ; CTC channel 0: vector 0x40 .dw ctc_isr ; CTC channel 1: vector 0x42 .dw ctc_isr ; CTC channel 2: vector 0x44 @@ -80,7 +79,7 @@ boot_copy: ; Stable BIOS jump table for ROM-resident applications. ; C050 putc(A), C053 try_getc(), C056 getc(), C059 io_out(A,L), ; C05C io_in(A), C05F delay_ms(HL), C062 monitor warm start, -; C065 bank(A), C068 disable ROM. +; C065 bank(A), C068 disable ROM, C06B enable ROM. .org BIOS_PUTC jp bios_service_putc .org BIOS_TRY_GETC @@ -99,11 +98,13 @@ boot_copy: jp _bios_bank .org BIOS_ROM_DISABLE jp _bios_rom_disable +.org BIOS_ROM_ENABLE + jp _bios_rom_enable ; Port 00 is write-only. Keep its last BIOS-managed value outside the C data ; areas so a monitor warm start does not reset it. The bootstrap establishes ; this same value in the hardware before the resident image is copied. -.org RESIDENT_BASE + 0x70 +.org RESIDENT_RAM_BASE + 0x70 _bank_control_shadow:: .db BOOT_BANK_MAPPING @@ -184,16 +185,21 @@ bios_bank_update: pop af ret -; Permanently hide ROM until reset, preserving the selected RAM mapping. +; Hide ROM while preserving the selected RAM mapping. ; A and flags are preserved. _bios_rom_disable:: push af ld a, (_bank_control_shadow) or #ROM_DISABLE_BIT - ld (_bank_control_shadow), a - out (BANK_CONTROL_PORT), a - pop af - ret + jr bios_bank_update + +; Make ROM visible again while preserving the selected RAM mapping. +; A and flags are preserved. +_bios_rom_enable:: + push af + ld a, (_bank_control_shadow) + and #0xFE + jr bios_bank_update ; BIOS ABI services preserve registers except documented return values. bios_service_putc: diff --git a/include/bios.inc b/include/bios.inc index 7de3f54..f651be8 100644 --- a/include/bios.inc +++ b/include/bios.inc @@ -1,10 +1,13 @@ -; Stable assembly ABI exported by the resident BIOS at C000. -BIOS_PUTC = 0xC050 ; A = character; preserves all registers -BIOS_TRY_GETC = 0xC053 ; returns A = character or 0; preserves BC..IY -BIOS_GETC = 0xC056 ; blocking; returns character in A -BIOS_IO_OUT = 0xC059 ; A = port, L = value; preserves all registers -BIOS_IO_IN = 0xC05C ; A = port; returns value in A -BIOS_DELAY_MS = 0xC05F ; HL = milliseconds; preserves all registers -BIOS_MONITOR = 0xC062 ; warm-start the monitor; does not return -BIOS_BANK = 0xC065 ; A=0 split 48K/16K, otherwise upper 64K -BIOS_ROM_DISABLE = 0xC068 ; disable ROM while preserving the bank selection + .include "rom_layout.inc" + +; Stable assembly ABI exported by the resident BIOS. +BIOS_PUTC = RESIDENT_RAM_BASE + 0x50 ; A = character; preserves all registers +BIOS_TRY_GETC = RESIDENT_RAM_BASE + 0x53 ; returns A = character or 0; preserves BC..IY +BIOS_GETC = RESIDENT_RAM_BASE + 0x56 ; blocking; returns character in A +BIOS_IO_OUT = RESIDENT_RAM_BASE + 0x59 ; A = port, L = value; preserves all registers +BIOS_IO_IN = RESIDENT_RAM_BASE + 0x5C ; A = port; returns value in A +BIOS_DELAY_MS = RESIDENT_RAM_BASE + 0x5F ; HL = milliseconds; preserves all registers +BIOS_MONITOR = RESIDENT_RAM_BASE + 0x62 ; warm-start the monitor; does not return +BIOS_BANK = RESIDENT_RAM_BASE + 0x65 ; A=0 split 48K/16K, otherwise upper 64K +BIOS_ROM_DISABLE = RESIDENT_RAM_BASE + 0x68 ; disable ROM while preserving the bank selection +BIOS_ROM_ENABLE = RESIDENT_RAM_BASE + 0x6B ; enable ROM while preserving the bank selection diff --git a/monitor.c b/monitor.c index d919ec6..019d2d8 100644 --- a/monitor.c +++ b/monitor.c @@ -1,5 +1,6 @@ #include #include "bios.h" +#include "rom_layout.h" #define LINE_SIZE 80 #define MAX_ARGS 40 @@ -97,8 +98,11 @@ static void print_help(void) " W addr byte... Write memory\r\n" " I port Read I/O\r\n" " O port byte Write I/O\r\n" + " K bank Bank: 0=split, 1=upper 64K\r\n" + " R ON|OFF Enable or disable ROM\r\n" " G addr Execute\r\n" " B Tiny BASIC\r\n" + " T Tape loader\r\n" " H Help\r\n"); } @@ -204,6 +208,35 @@ static void command_out(uint8_t argc, char **argv) bios_puts("OK\r\n"); } +static void command_bank(uint8_t argc, char **argv) +{ + uint16_t bank; + + if (argc != 2 || !parse_hex(argv[1], &bank) || bank > 1) { + bios_puts("Usage: BANK 0|1\r\n"); + return; + } + bios_bank((uint8_t)bank); + bios_puts("OK\r\n"); +} + +static void command_rom(uint8_t argc, char **argv) +{ + if (argc != 2) { + bios_puts("Usage: ROM ON|OFF\r\n"); + return; + } + if (strings_equal(argv[1], "on")) { + bios_rom_enable(); + } else if (strings_equal(argv[1], "off")) { + bios_rom_disable(); + } else { + bios_puts("Usage: ROM ON|OFF\r\n"); + return; + } + bios_puts("OK\r\n"); +} + static void execute_address(uint16_t address) { bios_puts("Executing at "); @@ -231,7 +264,16 @@ static void command_basic(uint8_t argc) bios_puts("Usage: B\r\n"); return; } - execute_address(0x1000); + execute_address(TINYBASIC_ROM_ADDRESS); +} + +static void command_tape(uint8_t argc) +{ + if (argc != 1) { + bios_puts("Usage: T\r\n"); + return; + } + execute_address(TAPE_LOADER_ROM_ADDRESS); } static void execute_line(char *line) @@ -253,10 +295,16 @@ static void execute_line(char *line) command_in(argc, argv); } else if (strings_equal(argv[0], "o") || strings_equal(argv[0], "out")) { command_out(argc, argv); + } else if (strings_equal(argv[0], "k") || strings_equal(argv[0], "bank")) { + command_bank(argc, argv); + } else if (strings_equal(argv[0], "r") || strings_equal(argv[0], "rom")) { + command_rom(argc, argv); } else if (strings_equal(argv[0], "g") || strings_equal(argv[0], "go")) { command_go(argc, argv); } else if (strings_equal(argv[0], "b") || strings_equal(argv[0], "basic")) { command_basic(argc); + } else if (strings_equal(argv[0], "t") || strings_equal(argv[0], "tape")) { + command_tape(argc); } else { bios_puts("Unknown command; type HELP\r\n"); } diff --git a/program_ihex.py b/program_ihex.py index 0cdf799..95372e0 100755 --- a/program_ihex.py +++ b/program_ihex.py @@ -6,6 +6,7 @@ from __future__ import annotations import argparse import re import sys +import time from pathlib import Path try: @@ -132,7 +133,8 @@ def make_blocks(memory: dict[int, int], block_size: int) -> list[tuple[int, byte class Monitor: - def __init__(self, port: str, baud: int, timeout: float): + def __init__(self, port: str, baud: int, timeout: float, char_delay: float): + self.char_delay = char_delay try: self.serial = serial.Serial( port=port, @@ -151,18 +153,32 @@ class Monitor: def command(self, command: str) -> str: try: - self.serial.write(command.encode("ascii") + b"\r") - self.serial.flush() + for value in command.encode("ascii") + b"\r": + self.serial.write(bytes((value,))) + self.serial.flush() + if self.char_delay: + time.sleep(self.char_delay) response = self.serial.read_until(b"> ") except serial.SerialException as exc: raise ProgramError(f"serial communication failed: {exc}") from exc if not response.endswith(b"> "): - raise ProgramError(f"timeout waiting for monitor after '{command}'") + text = response.decode("ascii", errors="replace") + raise ProgramError( + f"timeout waiting for monitor after '{command}'; partial={text!r}" + ) return response.decode("ascii", errors="replace") def synchronize(self) -> None: try: self.serial.reset_input_buffer() + # reset_input_buffer() cannot discard bytes which are still queued + # in the computer or USB adapter. Read until the line has stayed + # quiet before generating a fresh, unambiguous prompt. + normal_timeout = self.serial.timeout + self.serial.timeout = min(normal_timeout, 0.2) + while self.serial.read(256): + pass + self.serial.timeout = normal_timeout except serial.SerialException as exc: raise ProgramError(f"serial communication failed: {exc}") from exc response = self.command("") @@ -187,14 +203,14 @@ def write_and_verify(monitor: Monitor, start: int, data: bytes) -> tuple[bool, s values = " ".join(f"{value:02X}" for value in data) response = monitor.command(f"W {start:04X} {values}") if not response_has_ok(response): - return False, "monitor did not acknowledge the write" + return False, f"monitor did not acknowledge the write; response={response!r}" end = start + len(data) - 1 expected = sum(data) & 0xFFFF response = monitor.command(f"C {start:04X} {end:04X}") actual = response_checksum(response) if actual is None: - return False, "monitor did not return a checksum" + return False, f"monitor did not return a checksum; response={response!r}" if actual != expected: return False, f"checksum mismatch (expected {expected:04X}, received {actual:04X})" return True, "" @@ -225,6 +241,12 @@ def parse_args() -> argparse.Namespace: default=5.0, help="serial response timeout in seconds (default: 5)", ) + parser.add_argument( + "--char-delay", + type=float, + default=0.005, + help="delay after each transmitted character in seconds (default: 0.005)", + ) args = parser.parse_args() if not 1 <= args.block_size <= MAX_BLOCK_SIZE: parser.error(f"--block-size must be between 1 and {MAX_BLOCK_SIZE}") @@ -234,6 +256,8 @@ def parse_args() -> argparse.Namespace: parser.error("--baud must be positive") if args.timeout <= 0: parser.error("--timeout must be positive") + if args.char_delay < 0: + parser.error("--char-delay cannot be negative") return args @@ -249,7 +273,7 @@ def main() -> int: f"({min(memory):04X}-{max(memory):04X})" ) - monitor = Monitor(args.port, args.baud, args.timeout) + monitor = Monitor(args.port, args.baud, args.timeout, args.char_delay) monitor.synchronize() for number, (start, data) in enumerate(blocks, 1): @@ -263,9 +287,8 @@ def main() -> int: success, reason = False, str(exc) if success: print( - f"\rBlock {number}/{len(blocks)} {start:04X}-{end:04X} verified", - end="", - flush=True, + f"Block {number}/{len(blocks)} " + f"{start:04X}-{end:04X} verified" ) break if attempt == args.retries: @@ -274,12 +297,12 @@ def main() -> int: f"{args.retries + 1} attempts: {reason}" ) print( - f"\nRetrying {start:04X}-{end:04X} " + f"Retrying {start:04X}-{end:04X} " f"({attempt + 1}/{args.retries}): {reason}", file=sys.stderr, ) - print(f"\nProgrammed and verified {len(memory)} bytes") + print(f"Programmed and verified {len(memory)} bytes") return 0 except ProgramError as exc: print(f"error: {exc}", file=sys.stderr) diff --git a/rom_layout.mk b/rom_layout.mk new file mode 100644 index 0000000..424f48b --- /dev/null +++ b/rom_layout.mk @@ -0,0 +1,15 @@ +# Authoritative ROM and resident-memory layout. +# +# Keep assignments in the simple "NAME := value" form: the build generates +# equivalent C and sdasz80 include files from these definitions. + +ROM_SIZE := 32768 + +RESIDENT_RAM_BASE := 0xC000 +RESIDENT_ROM_OFFSET := 0x0080 +RESIDENT_CODE_BASE := 0xC080 +RESIDENT_CODE_SIZE := 0x0F80 +RESIDENT_DATA_BASE := 0xF000 + +TINYBASIC_ROM_ADDRESS := 0x1400 +TAPE_LOADER_ROM_ADDRESS := 0x3000 diff --git a/test.wav b/test.wav new file mode 100644 index 0000000000000000000000000000000000000000..2624c08aa1c34187b58ff5780f7f4e4346a4455e GIT binary patch literal 5710124 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 F