diff --git a/Makefile b/Makefile index a9cff11..a52fc4c 100644 --- a/Makefile +++ b/Makefile @@ -9,10 +9,13 @@ 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 0x0080 --data-loc 0x8000 +METAL_FLAGS := -mz80 --no-std-crt0 --code-loc 0xC080 --code-size 0x2F80 --data-loc 0xF000 METAL_CPPFLAGS := -I src -I $(BUILDDIR)/metal/generated --disable-warning 283 METAL_BIN_SIZE := 32768 -METAL_RAM_SIZE := 32768 +METAL_RAM_SIZE := 16384 +METAL_IMAGE_SIZE := 65536 +MONITOR_BASE := 0xC000 +MONITOR_ROM_OFFSET := 128 .DEFAULT_GOAL := metal @@ -21,24 +24,34 @@ METAL_RAM_SIZE := 32768 all: metal metal: $(BUILDDIR)/metal/$(TARGET).ihx $(BUILDDIR)/metal/$(TARGET).bin $(BUILDDIR)/metal/$(TARGET).prog.bin - @awk -v rom_capacity=$(METAL_BIN_SIZE) -v ram_capacity=$(METAL_RAM_SIZE) '\ - $$1 ~ /^_(CODE|HOME|GSINIT|GSFINAL|INITIALIZER|ITABLE[0-7]*)$$/ { rom += $$5 } \ - $$1 ~ /^_(DATA|INITIALIZED|BSEG|BSS|HEAP|STACK)$$/ { ram += $$5 } \ + @stored=$$(wc -c < $(BUILDDIR)/metal/$(TARGET).prog.bin); \ + awk -v rom_capacity=$(METAL_BIN_SIZE) -v stored=$$stored -v ram_capacity=$(METAL_RAM_SIZE) '\ + $$1 ~ /^_(CODE|HOME|GSINIT|GSFINAL|INITIALIZER)$$/ || $$1 ~ /^_ITABLE/ { code += $$5 } \ + $$1 ~ /^_(DATA|INITIALIZED|BSEG|BSS|HEAP)$$/ { data += $$5 } \ END { \ printf "\nMemory usage:\n"; \ - printf " ROM: %5u / %5u bytes (%5.1f%%), %5u bytes free\n", rom, rom_capacity, 100 * rom / rom_capacity, rom_capacity - rom; \ - printf " RAM: %5u / %5u bytes (%5.1f%%), %5u bytes free\n", ram, ram_capacity, 100 * ram / ram_capacity, ram_capacity - ram; \ + printf " ROM storage: %5u / %5u bytes (%5.1f%%)\n", stored, rom_capacity, 100 * stored / rom_capacity; \ + printf " Runtime RAM: %5u / %5u bytes (%5.1f%%), %5u bytes for growth/stack\n", code + data, ram_capacity, 100 * (code + data) / ram_capacity, ram_capacity - code - data; \ + printf " code/vector: %5u bytes; static data: %5u bytes\n", code, data; \ }' $(BUILDDIR)/metal/$(TARGET).map $(BUILDDIR)/metal/$(TARGET).ihx: $(METAL_RELFILES) @mkdir -p $(@D) $(METAL_CC) $(METAL_FLAGS) -o $@ $(METAL_RELFILES) -$(BUILDDIR)/metal/$(TARGET).bin: $(BUILDDIR)/metal/$(TARGET).ihx - $(METAL_MAKEBIN) -s $(METAL_BIN_SIZE) $< $@ +$(BUILDDIR)/metal/$(TARGET).flat.bin: $(BUILDDIR)/metal/$(TARGET).ihx + $(METAL_MAKEBIN) -s $(METAL_IMAGE_SIZE) $< $@ -$(BUILDDIR)/metal/$(TARGET).prog.bin: $(BUILDDIR)/metal/$(TARGET).ihx - $(METAL_MAKEBIN) -p $< $@ +$(BUILDDIR)/metal/$(TARGET).prog.bin: $(BUILDDIR)/metal/$(TARGET).flat.bin + @payload_end=$$(awk '$$1 == "DEF" && $$2 == "monitor_image_end" { print $$3 }' $(BUILDDIR)/metal/$(TARGET).noi); \ + payload_size=$$((payload_end - $(MONITOR_BASE))); \ + test $$payload_size -gt 0; \ + dd if=$< of=$@ bs=1 count=$(MONITOR_ROM_OFFSET) status=none; \ + dd if=$< of=$@ bs=1 skip=$$(( $(MONITOR_BASE) )) seek=$(MONITOR_ROM_OFFSET) count=$$payload_size 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 @mkdir -p $(@D) diff --git a/README.md b/README.md index 7f997c2..1a4e992 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,14 @@ # Z80 UART monitor This ROM provides a small interactive monitor on the UART/CTC card at I/O base -`20`. Connect a terminal at **2400 baud, 8 data bits, no parity, 1 stop bit**. +`20`. At reset, a small ROM bootstrap copies the monitor into the top 16 KiB of +RAM at `C000-FFFF` and starts it there. Only the occupied vector/code bytes are +stored and copied; uninitialized data is cleared directly in high RAM. The low ROM can subsequently be disabled +without taking the monitor, its data, or its interrupt table away. Runtime code +and vectors occupy the lower part of this window, globals begin at `F000`, and +the stack grows downward from `FFFF`; no RAM below `C000` is used. + +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 optional `0x` prefix is accepted. @@ -26,4 +33,12 @@ make ``` The outputs are `build/metal/monitor.bin` (32 KiB ROM image) and -`build/metal/monitor.prog.bin` (compact programmer image). +`build/metal/monitor.prog.bin` (compact programmer image). The ROM layout is: + +- `0000-007F`: reset/copy bootstrap +- `0080...`: compact vector/code image copied to RAM starting at `C000` +- Remaining bytes: unused and available for additional ROM content + +The monitor executable starts at `C080`; its IM 2 vector table is at `C040`. +`monitor.prog.bin` ends immediately after the occupied payload, while +`monitor.bin` remains a complete 32 KiB chip image. diff --git a/boot.asm b/boot.asm index dd77427..5b9b665 100644 --- a/boot.asm +++ b/boot.asm @@ -1,5 +1,7 @@ STACK_TOP = 0xFFFF DELAY_LOOPS_PER_MS = 40 +MONITOR_BASE = 0xC000 +MONITOR_ROM_SOURCE = 0x0080 .globl _main .globl _delay_ms @@ -8,33 +10,49 @@ DELAY_LOOPS_PER_MS = 40 .globl _enable_interrupts .globl _disable_interrupts .globl _jump_to + .globl monitor_image_end .globl s__DATA .globl l__DATA .globl s__INITIALIZER .globl s__INITIALIZED .globl l__INITIALIZER -.area _ITABLE (ABS) +.area _BOOT (ABS) .org 0x00 - jp init + jp boot_copy .org 0x08 +boot_copy: + di + ld sp, #STACK_TOP + ld hl, #MONITOR_ROM_SOURCE + ld de, #MONITOR_BASE + ld bc, #(monitor_image_end - MONITOR_BASE) + ldir + jp init + +.area _ITABLE (ABS) + +; The executable vector page is part of the image copied to high RAM. +.org MONITOR_BASE + jp init +.org MONITOR_BASE + 0x08 reti -.org 0x10 +.org MONITOR_BASE + 0x10 reti -.org 0x18 +.org MONITOR_BASE + 0x18 reti -.org 0x20 +.org MONITOR_BASE + 0x20 reti -.org 0x28 +.org MONITOR_BASE + 0x28 reti -.org 0x30 +.org MONITOR_BASE + 0x30 reti -.org 0x38 +.org MONITOR_BASE + 0x38 reti -; ITABLE -.org 0x40 +; IM 2 table: the CTC supplies vectors 40, 42, 44, and 46. +.org MONITOR_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 @@ -48,7 +66,7 @@ init: call gsinit ; Setup interrupt mode 2 - xor a + ld a, #0xC0 ld i, a im 2 call _main @@ -88,6 +106,7 @@ clear_loop: gsinit_done: .area _GSFINAL ret +monitor_image_end: .area _CODE