new memory and project layout
This commit is contained in:
@@ -2,7 +2,7 @@ TARGET := monitor
|
|||||||
BUILDDIR := build
|
BUILDDIR := build
|
||||||
|
|
||||||
METAL_C_SRCFILES := $(wildcard *.c)
|
METAL_C_SRCFILES := $(wildcard *.c)
|
||||||
METAL_ASM_SRCFILES := $(wildcard *.asm)
|
METAL_ASM_SRCFILES := boot.asm
|
||||||
METAL_RELFILES := $(METAL_ASM_SRCFILES:%.asm=$(BUILDDIR)/metal/%.rel) \
|
METAL_RELFILES := $(METAL_ASM_SRCFILES:%.asm=$(BUILDDIR)/metal/%.rel) \
|
||||||
$(METAL_C_SRCFILES:%.c=$(BUILDDIR)/metal/%.rel)
|
$(METAL_C_SRCFILES:%.c=$(BUILDDIR)/metal/%.rel)
|
||||||
|
|
||||||
@@ -14,12 +14,12 @@ METAL_CPPFLAGS := -I src -I $(BUILDDIR)/metal/generated --disable-warning 2
|
|||||||
METAL_BIN_SIZE := 32768
|
METAL_BIN_SIZE := 32768
|
||||||
METAL_RAM_SIZE := 16384
|
METAL_RAM_SIZE := 16384
|
||||||
METAL_IMAGE_SIZE := 65536
|
METAL_IMAGE_SIZE := 65536
|
||||||
MONITOR_BASE := 0xC000
|
RESIDENT_BASE := 0xC000
|
||||||
MONITOR_ROM_OFFSET := 128
|
RESIDENT_ROM_OFFSET := 128
|
||||||
|
|
||||||
TINYBASIC_BASE := 0xD000
|
TINYBASIC_BASE := 0x1000
|
||||||
TINYBASIC_ROM_OFFSET := 4096
|
TINYBASIC_ROM_OFFSET := 4096
|
||||||
TINYBASIC_SRC := tinybasic_sdas.asm
|
TINYBASIC_SRC := apps/tinybasic.asm
|
||||||
TINYBASIC_BUILD := $(BUILDDIR)/tinybasic
|
TINYBASIC_BUILD := $(BUILDDIR)/tinybasic
|
||||||
|
|
||||||
.DEFAULT_GOAL := metal
|
.DEFAULT_GOAL := metal
|
||||||
@@ -36,9 +36,9 @@ metal: $(BUILDDIR)/metal/$(TARGET).ihx $(BUILDDIR)/metal/$(TARGET).bin $(BUILDDI
|
|||||||
END { \
|
END { \
|
||||||
printf "\nMemory usage:\n"; \
|
printf "\nMemory usage:\n"; \
|
||||||
printf " ROM storage: %5u / %5u bytes (%5.1f%%)\n", stored, rom_capacity, 100 * stored / rom_capacity; \
|
printf " ROM storage: %5u / %5u bytes (%5.1f%%)\n", stored, rom_capacity, 100 * stored / rom_capacity; \
|
||||||
printf " Monitor linked: %5u bytes (%5u code/vector + %5u static data)\n", code + data, code, data; \
|
printf " Resident BIOS+monitor: %5u bytes (%5u code/vector + %5u static data)\n", code + data, code, data; \
|
||||||
}' $(BUILDDIR)/metal/$(TARGET).map
|
}' $(BUILDDIR)/metal/$(TARGET).map
|
||||||
@printf " Tiny BASIC: %5u bytes at D000\n" $$(wc -c < $(TINYBASIC_BUILD)/tinybasic.bin)
|
@printf " Tiny BASIC: %5u ROM bytes at 1000\n" $$(wc -c < $(TINYBASIC_BUILD)/tinybasic.bin)
|
||||||
|
|
||||||
$(BUILDDIR)/metal/$(TARGET).ihx: $(METAL_RELFILES)
|
$(BUILDDIR)/metal/$(TARGET).ihx: $(METAL_RELFILES)
|
||||||
@mkdir -p $(@D)
|
@mkdir -p $(@D)
|
||||||
@@ -48,11 +48,12 @@ $(BUILDDIR)/metal/$(TARGET).flat.bin: $(BUILDDIR)/metal/$(TARGET).ihx
|
|||||||
$(METAL_MAKEBIN) -s $(METAL_IMAGE_SIZE) $< $@
|
$(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
|
||||||
@payload_end=$$(awk '$$1 == "DEF" && $$2 == "monitor_image_end" { print $$3 }' $(BUILDDIR)/metal/$(TARGET).noi); \
|
@payload_end=$$(awk '$$1 == "DEF" && $$2 == "resident_image_end" { print $$3 }' $(BUILDDIR)/metal/$(TARGET).noi); \
|
||||||
payload_size=$$((payload_end - $(MONITOR_BASE))); \
|
payload_size=$$((payload_end - $(RESIDENT_BASE))); \
|
||||||
test $$payload_size -gt 0; \
|
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 count=$(TINYBASIC_ROM_OFFSET) status=none; \
|
||||||
dd if=$< of=$@ bs=1 skip=$$(( $(MONITOR_BASE) )) seek=$(MONITOR_ROM_OFFSET) count=$$payload_size conv=notrunc 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
|
dd if=$(TINYBASIC_BUILD)/tinybasic.bin of=$@ bs=1 seek=$(TINYBASIC_ROM_OFFSET) conv=notrunc status=none
|
||||||
|
|
||||||
$(BUILDDIR)/metal/$(TARGET).bin: $(BUILDDIR)/metal/$(TARGET).flat.bin $(BUILDDIR)/metal/$(TARGET).prog.bin
|
$(BUILDDIR)/metal/$(TARGET).bin: $(BUILDDIR)/metal/$(TARGET).flat.bin $(BUILDDIR)/metal/$(TARGET).prog.bin
|
||||||
@@ -65,13 +66,11 @@ $(BUILDDIR)/metal/%.rel: %.c
|
|||||||
|
|
||||||
$(BUILDDIR)/metal/%.rel: %.asm
|
$(BUILDDIR)/metal/%.rel: %.asm
|
||||||
@mkdir -p $(@D)
|
@mkdir -p $(@D)
|
||||||
$(METAL_AS) -I$(BUILDDIR)/metal/generated -plosgff -o $@ $<
|
$(METAL_AS) -Iinclude -I$(BUILDDIR)/metal/generated -plosgff -o $@ $<
|
||||||
|
|
||||||
$(BUILDDIR)/metal/boot.rel: $(BUILDDIR)/metal/generated/tinybasic.inc
|
|
||||||
|
|
||||||
$(TINYBASIC_BUILD)/tinybasic.rel: $(TINYBASIC_SRC)
|
$(TINYBASIC_BUILD)/tinybasic.rel: $(TINYBASIC_SRC)
|
||||||
@mkdir -p $(@D)
|
@mkdir -p $(@D)
|
||||||
$(METAL_AS) -plosgff -o $@ $<
|
$(METAL_AS) -Iinclude -plosgff -o $@ $<
|
||||||
|
|
||||||
$(TINYBASIC_BUILD)/tinybasic.ihx: $(TINYBASIC_BUILD)/tinybasic.rel
|
$(TINYBASIC_BUILD)/tinybasic.ihx: $(TINYBASIC_BUILD)/tinybasic.rel
|
||||||
sdldz80 -n -i -m -j $(basename $@) $<
|
sdldz80 -n -i -m -j $(basename $@) $<
|
||||||
@@ -84,10 +83,6 @@ $(TINYBASIC_BUILD)/tinybasic.bin: $(TINYBASIC_BUILD)/tinybasic.flat.bin
|
|||||||
test $$tiny_size -gt 0; \
|
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_BASE) )) count=$$tiny_size status=none
|
||||||
|
|
||||||
$(BUILDDIR)/metal/generated/tinybasic.inc: $(TINYBASIC_BUILD)/tinybasic.ihx
|
|
||||||
@mkdir -p $(@D)
|
|
||||||
@awk '$$1 == "TINYBASIC0" { printf "TINYBASIC_SIZE = 0x%04X\n", $$5 }' $(TINYBASIC_BUILD)/tinybasic.map > $@
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf $(BUILDDIR)
|
rm -rf $(BUILDDIR)
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
# Z80 UART monitor
|
# Z80 UART monitor
|
||||||
|
|
||||||
This ROM provides a small interactive monitor and a bundled Z80 Tiny BASIC
|
This project provides a resident BIOS, an interactive monitor, and ROM-resident
|
||||||
interpreter on the UART/CTC card at I/O base `20`. At reset, a small ROM
|
applications for a Z80 system with a UART/CTC card at I/O base `20`.
|
||||||
bootstrap copies both programs into the top 16 KiB of RAM and starts the
|
|
||||||
monitor. Only occupied code/vector bytes are stored and copied; uninitialized
|
At reset, a small ROM bootstrap copies only the BIOS and monitor into the top
|
||||||
data is cleared directly in high RAM. The low ROM can subsequently be disabled
|
16 KiB of RAM at `C000`. The BIOS initializes interrupt mode 2 and the UART,
|
||||||
without taking away the monitor, Tiny BASIC, monitor data, or interrupt table.
|
then starts the monitor. Applications remain in ROM and execute in place while
|
||||||
No runtime RAM below `C000` is used.
|
calling the stable BIOS jump table in high RAM. Tiny BASIC is the first such
|
||||||
|
application, linked at ROM address `1000`.
|
||||||
|
|
||||||
Connect a terminal at **2400 baud, 8 data bits, no parity, 1 stop bit**.
|
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
|
Commands are case-insensitive and all numeric arguments are hexadecimal; an
|
||||||
@@ -20,8 +21,8 @@ optional `0x` prefix is accepted.
|
|||||||
| `I 20` | Read I/O port 20 |
|
| `I 20` | Read I/O port 20 |
|
||||||
| `O 20 57` | Write 57 to I/O port 20 |
|
| `O 20 57` | Write 57 to I/O port 20 |
|
||||||
| `G 8000` | Execute code at address 8000 |
|
| `G 8000` | Execute code at address 8000 |
|
||||||
| `G D000` | Start the bundled Z80 Tiny BASIC interpreter |
|
| `G 1000` | Start the ROM-resident Z80 Tiny BASIC interpreter |
|
||||||
| `B` or `BASIC` | Shortcut for `G D000` |
|
| `B` or `BASIC` | Shortcut for `G 1000` |
|
||||||
| `HELP` | Show the command summary |
|
| `HELP` | Show the command summary |
|
||||||
|
|
||||||
`G` transfers control with a return address on the stack, so code ending in
|
`G` transfers control with a return address on the stack, so code ending in
|
||||||
@@ -43,45 +44,70 @@ The outputs are:
|
|||||||
The current compact image is 6,157 bytes. Its ROM layout is:
|
The current compact image is 6,157 bytes. Its ROM layout is:
|
||||||
|
|
||||||
- `0000-007F`: reset/copy bootstrap
|
- `0000-007F`: reset/copy bootstrap
|
||||||
- `0080-0F6E`: monitor image copied to RAM at `C000-CEEE`
|
- `0080-0F9D`: packed BIOS+monitor image copied to RAM at `C000-CF1D`
|
||||||
- `0F6F-0FFF`: unused space reserved for monitor growth
|
- `0F9E-0FFF`: unused space reserved for the resident image to grow
|
||||||
- `1000-180C`: Tiny BASIC image copied to RAM at `D000-D80C`
|
- `1000-180C`: Tiny BASIC, executed directly from ROM
|
||||||
- `180D-7FFF`: unused space available for additional ROM content
|
- `180D-7FFF`: unused space available for additional ROM content
|
||||||
|
|
||||||
The monitor executable starts at `C080`; its IM 2 vector table is at `C040`.
|
The resident image starts at `C000`, its IM 2 vector table is at `C040`, and
|
||||||
The bootstrap obtains both copy lengths from generated/linker symbols, so the
|
linked code starts at `C080`. The bootstrap obtains the resident copy length
|
||||||
copied sizes track changes to the programs automatically.
|
from a linker symbol, so it tracks BIOS and monitor changes automatically.
|
||||||
|
|
||||||
The resulting high-RAM layout is:
|
The resulting RAM layout is:
|
||||||
|
|
||||||
- `C000-CEEE`: monitor vectors, services, and code
|
- `8000-AFFF`: Tiny BASIC workspace and BASIC program text
|
||||||
- `CEEF-CFFF`: free
|
- `B000-B077`: Tiny BASIC variables and input buffer
|
||||||
- `D000-D80C`: Tiny BASIC code
|
- `B078-BFFF`: Tiny BASIC stack space
|
||||||
- `D80D-DFFF`: free
|
- `C000-CF1D`: resident BIOS, vectors, services, and monitor
|
||||||
- `E000-E7FF`: Tiny BASIC workspace and BASIC program text
|
- `CF1E-EFFF`: free space for resident-image growth
|
||||||
- `E800-E877`: Tiny BASIC variables and input buffer
|
- `F000-FFFF`: BIOS globals, UART buffers, and monitor stack
|
||||||
- `E878-EFFF`: Tiny BASIC stack space
|
|
||||||
- `F000-FFFF`: monitor globals and monitor stack
|
The BIOS and monitor continue to work if low ROM is disabled. ROM-resident
|
||||||
|
applications naturally require ROM to remain mapped while they execute.
|
||||||
|
|
||||||
|
## BIOS interface
|
||||||
|
|
||||||
|
Assembly applications include `include/bios.inc` and call these fixed entries:
|
||||||
|
|
||||||
|
| Address | Service | Interface |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `C050` | `BIOS_PUTC` | Write character in `A` |
|
||||||
|
| `C053` | `BIOS_TRY_GETC` | Return character in `A`, or zero |
|
||||||
|
| `C056` | `BIOS_GETC` | Wait for and return a character in `A` |
|
||||||
|
| `C059` | `BIOS_IO_OUT` | Write `L` to port `A` |
|
||||||
|
| `C05C` | `BIOS_IO_IN` | Read port `A`, returning the value in `A` |
|
||||||
|
| `C05F` | `BIOS_DELAY_MS` | Delay approximately `HL` milliseconds |
|
||||||
|
| `C062` | `BIOS_MONITOR` | Warm-start the monitor; does not return |
|
||||||
|
|
||||||
|
The service wrappers preserve working registers except for documented return
|
||||||
|
values. The monitor uses the corresponding C API declared in `bios.h`.
|
||||||
|
|
||||||
|
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
|
||||||
|
- `apps/README.md`: ROM application memory and integration contract
|
||||||
|
- `include/bios.inc`: stable assembly interface for ROM applications
|
||||||
|
|
||||||
## Tiny BASIC
|
## Tiny BASIC
|
||||||
|
|
||||||
The original GNU-z80asm listing is retained as `tinybasic/tinybasic.asm`. Its
|
Tiny BASIC is built from `apps/tinybasic.asm`. The port replaces fixed low-page
|
||||||
`sdasz80` port is `tinybasic/tinybasic_sdas.asm` and is built automatically.
|
`RST` calls with relocatable calls and uses a marker-based command table so it
|
||||||
The port replaces fixed low-page `RST` calls with relocatable calls and uses a
|
can be linked at a nonzero ROM address.
|
||||||
marker-based command table so addresses in the `D000` page work correctly.
|
|
||||||
|
|
||||||
At reset the bootstrap copies Tiny BASIC from ROM offset `1000` to `D000`.
|
Tiny BASIC is linked at and runs directly from ROM address `1000`. Start it from
|
||||||
Start it from the monitor with:
|
the monitor with:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
B
|
B
|
||||||
```
|
```
|
||||||
|
|
||||||
`BASIC` and `G D000` are equivalent alternatives.
|
`BASIC` and `G 1000` are equivalent alternatives.
|
||||||
|
|
||||||
Tiny BASIC calls the monitor ABI at `C050` for output and `C053` for
|
Tiny BASIC calls the BIOS at `C050` for output and `C053` for non-blocking
|
||||||
non-blocking input. The assembly ABI wrappers preserve the working registers,
|
input. Tiny BASIC keeps interrupts enabled so it can share the BIOS-configured
|
||||||
and Tiny BASIC keeps interrupts enabled so it can share the monitor's configured
|
|
||||||
interrupt-driven UART.
|
interrupt-driven UART.
|
||||||
|
|
||||||
Line editing accepts either `BS` (`08`) or `DEL` (`7F`) for backspace and emits
|
Line editing accepts either `BS` (`08`) or `DEL` (`7F`) for backspace and emits
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
# ROM applications
|
||||||
|
|
||||||
|
Applications in this directory execute directly from ROM; the reset bootstrap
|
||||||
|
does not copy them. Each application must have its own non-overlapping `ORG`
|
||||||
|
below `8000` and should include `bios.inc` from the project's `include`
|
||||||
|
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
|
||||||
|
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.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
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.
|
||||||
@@ -42,13 +42,11 @@
|
|||||||
; Adapted to work with GNU z80asm
|
; Adapted to work with GNU z80asm
|
||||||
; Added interrupt handling
|
; Added interrupt handling
|
||||||
; Notes for this monitor port:
|
; Notes for this monitor port:
|
||||||
; Converted to sdasz80 syntax and relocated to D000.
|
; Converted to sdasz80 syntax and linked as a ROM application at 1000.
|
||||||
; Fixed-page RST calls were converted to local CALLs.
|
; Fixed-page RST calls were converted to local CALLs.
|
||||||
; UART access uses the monitor ABI at C050/C053.
|
; UART access uses the resident BIOS ABI at C050/C053.
|
||||||
|
|
||||||
; Monitor ABI jump table. These entry points remain stable across monitor builds.
|
.include "bios.inc"
|
||||||
MONITOR_PUTC = 0xC050
|
|
||||||
MONITOR_GETC = 0xC053
|
|
||||||
|
|
||||||
|
|
||||||
SPACE = 0x020 ; Space
|
SPACE = 0x020 ; Space
|
||||||
@@ -67,9 +65,10 @@ CTRLU = 0x015 ; Control "U"
|
|||||||
ESC = 0x01B ; Escape
|
ESC = 0x01B ; Escape
|
||||||
DEL = 0x07F ; Delete
|
DEL = 0x07F ; Delete
|
||||||
|
|
||||||
; Adjust to fit RAM mapping
|
; Tiny BASIC owns lower RAM 8000-BFFF. The resident BIOS and monitor own
|
||||||
STACK = 0xEFFF ; Tiny BASIC stack grows down below monitor data
|
; C000-FFFF, so BASIC cannot overwrite them even when its stack is deep.
|
||||||
OCSW = 0xE000 ;SWITCH FOR OUTPUT
|
STACK = 0xBFFF ; Tiny BASIC stack grows down through B078-BFFF
|
||||||
|
OCSW = 0x8000 ;SWITCH FOR OUTPUT
|
||||||
CURRNT = OCSW+1 ;POINTS FOR OUTPUT
|
CURRNT = OCSW+1 ;POINTS FOR OUTPUT
|
||||||
STKGOS = OCSW+3 ;SAVES SP IN 'GOSUB'
|
STKGOS = OCSW+3 ;SAVES SP IN 'GOSUB'
|
||||||
VARNXT = OCSW+5 ;TEMP STORAGE
|
VARNXT = OCSW+5 ;TEMP STORAGE
|
||||||
@@ -83,11 +82,11 @@ RANPNT = OCSW+19 ;RANDOM NUMBER POINTER
|
|||||||
TXTUNF = OCSW+21 ;->UNFILLED TEXT AREA
|
TXTUNF = OCSW+21 ;->UNFILLED TEXT AREA
|
||||||
TXTBGN = OCSW+23 ;TEXT SAVE AREA BEGINS
|
TXTBGN = OCSW+23 ;TEXT SAVE AREA BEGINS
|
||||||
|
|
||||||
TXTEND = 0xE800 ;TEXT SAVE AREA ENDS
|
TXTEND = 0xB000 ;TEXT SAVE AREA ENDS
|
||||||
VARBGN = 0xE800 ;VARIABLE @(0)
|
VARBGN = 0xB000 ;VARIABLE @(0)
|
||||||
BUFFER = 0xE837 ;55-byte variable area ends here
|
BUFFER = 0xB037 ;55-byte variable area ends here
|
||||||
BUFEND = 0xE877 ;64-byte input buffer ends here
|
BUFEND = 0xB077 ;64-byte input buffer ends here
|
||||||
STKLMT = 0xE878 ;LOWEST ALLOWED STACK ADDRESS
|
STKLMT = 0xB078 ;LOWEST ALLOWED STACK ADDRESS
|
||||||
|
|
||||||
|
|
||||||
;*************************************************************
|
;*************************************************************
|
||||||
@@ -103,7 +102,7 @@ STKLMT = 0xE878 ;LOWEST ALLOWED STACK ADDRESS
|
|||||||
;*************************************************************
|
;*************************************************************
|
||||||
|
|
||||||
.area TINYBASIC (ABS)
|
.area TINYBASIC (ABS)
|
||||||
.org 0xD000
|
.org 0x1000
|
||||||
main:
|
main:
|
||||||
START:
|
START:
|
||||||
LD SP,#STACK ;*** COLD START ***
|
LD SP,#STACK ;*** COLD START ***
|
||||||
@@ -1601,7 +1600,7 @@ OUTC:
|
|||||||
OUTC2:
|
OUTC2:
|
||||||
CALL TX_RDY ;SEE IF TRANSMIT IS AVAILABLE
|
CALL TX_RDY ;SEE IF TRANSMIT IS AVAILABLE
|
||||||
POP AF ;RESTORE THE REGISTER
|
POP AF ;RESTORE THE REGISTER
|
||||||
CALL MONITOR_PUTC ;SEND THE BYTE THROUGH MONITOR UART
|
CALL BIOS_PUTC ;SEND THE BYTE THROUGH BIOS UART
|
||||||
CP #CR
|
CP #CR
|
||||||
RET NZ
|
RET NZ
|
||||||
LD A,#LF
|
LD A,#LF
|
||||||
@@ -1826,7 +1825,7 @@ RX_RDY:
|
|||||||
; However, if a character is not available, it returns with the
|
; However, if a character is not available, it returns with the
|
||||||
; Z-flag set.
|
; Z-flag set.
|
||||||
|
|
||||||
CALL MONITOR_GETC
|
CALL BIOS_TRY_GETC
|
||||||
OR A
|
OR A
|
||||||
RET
|
RET
|
||||||
;-------------------------------------------------------------------------------
|
;-------------------------------------------------------------------------------
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
#include <stdint.h>
|
||||||
|
#include "bios.h"
|
||||||
|
#include "circ_buf.h"
|
||||||
|
|
||||||
|
#define UART_CTC_CARD_BASE 0x20
|
||||||
|
#define CTC_BASE (UART_CTC_CARD_BASE + 0x00)
|
||||||
|
#define CTC_CHANNEL_0 (CTC_BASE + 0)
|
||||||
|
#define CTC_CHANNEL_3 (CTC_BASE + 3)
|
||||||
|
#define UART_BASE (UART_CTC_CARD_BASE + 0x04)
|
||||||
|
#define UART_CONTROL (UART_BASE + 0)
|
||||||
|
#define UART_DATA (UART_BASE + 1)
|
||||||
|
|
||||||
|
static volatile uint8_t uart_tx_busy;
|
||||||
|
static volatile circular_buffer_t rx_buf;
|
||||||
|
static volatile circular_buffer_t tx_buf;
|
||||||
|
|
||||||
|
static void uart_kick(void)
|
||||||
|
{
|
||||||
|
char c;
|
||||||
|
|
||||||
|
disable_interrupts();
|
||||||
|
if (!uart_tx_busy && buffer_dequeue(&tx_buf, &c)) {
|
||||||
|
uart_tx_busy = 1;
|
||||||
|
io_out(UART_DATA, c);
|
||||||
|
io_out(UART_CONTROL, 0b10110101);
|
||||||
|
}
|
||||||
|
enable_interrupts();
|
||||||
|
}
|
||||||
|
|
||||||
|
void bios_putc(char c)
|
||||||
|
{
|
||||||
|
uint8_t queued;
|
||||||
|
|
||||||
|
do {
|
||||||
|
disable_interrupts();
|
||||||
|
queued = buffer_enqueue(&tx_buf, c);
|
||||||
|
enable_interrupts();
|
||||||
|
if (!queued) uart_kick();
|
||||||
|
} while (!queued);
|
||||||
|
uart_kick();
|
||||||
|
}
|
||||||
|
|
||||||
|
void bios_puts(const char *s)
|
||||||
|
{
|
||||||
|
while (*s) bios_putc(*s++);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t bios_try_getc(void)
|
||||||
|
{
|
||||||
|
char c;
|
||||||
|
uint8_t received;
|
||||||
|
|
||||||
|
disable_interrupts();
|
||||||
|
received = buffer_dequeue(&rx_buf, &c);
|
||||||
|
enable_interrupts();
|
||||||
|
return received ? (uint8_t)c : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
char bios_getc(void)
|
||||||
|
{
|
||||||
|
uint8_t c;
|
||||||
|
|
||||||
|
do {
|
||||||
|
c = bios_try_getc();
|
||||||
|
} while (!c);
|
||||||
|
return (char)c;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bios_flush(void)
|
||||||
|
{
|
||||||
|
while (uart_tx_busy || !buffer_is_empty(&tx_buf)) uart_kick();
|
||||||
|
}
|
||||||
|
|
||||||
|
void bios_init(void)
|
||||||
|
{
|
||||||
|
buffer_init(&rx_buf);
|
||||||
|
buffer_init(&tx_buf);
|
||||||
|
|
||||||
|
io_out(CTC_CHANNEL_0, 0x57);
|
||||||
|
io_out(CTC_CHANNEL_0, 13);
|
||||||
|
io_out(CTC_CHANNEL_3, 0b11001111);
|
||||||
|
io_out(CTC_CHANNEL_3, 1);
|
||||||
|
io_out(CTC_CHANNEL_0, 0x00);
|
||||||
|
io_out(CTC_CHANNEL_0, 0x40);
|
||||||
|
|
||||||
|
uart_tx_busy = 0;
|
||||||
|
io_out(UART_CONTROL, 0b00000011);
|
||||||
|
io_out(UART_CONTROL, 0b10010101);
|
||||||
|
enable_interrupts();
|
||||||
|
}
|
||||||
|
|
||||||
|
void uart_interrupt(void)
|
||||||
|
{
|
||||||
|
uint8_t status = io_in(UART_CONTROL);
|
||||||
|
|
||||||
|
if ((status & 0x21) != 0) {
|
||||||
|
char c = io_in(UART_DATA);
|
||||||
|
if ((status & 0x01) != 0) buffer_enqueue(&rx_buf, c);
|
||||||
|
}
|
||||||
|
if ((status & 0x02) != 0 && uart_tx_busy) {
|
||||||
|
char c;
|
||||||
|
if (buffer_dequeue(&tx_buf, &c)) {
|
||||||
|
io_out(UART_DATA, c);
|
||||||
|
} else {
|
||||||
|
io_out(UART_CONTROL, 0b10010101);
|
||||||
|
uart_tx_busy = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ctc_interrupt(void)
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
#ifndef BIOS_H
|
||||||
|
#define BIOS_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/* Resident BIOS API used by the monitor. */
|
||||||
|
void bios_init(void);
|
||||||
|
void bios_putc(char c);
|
||||||
|
void bios_puts(const char *s);
|
||||||
|
char bios_getc(void);
|
||||||
|
uint8_t bios_try_getc(void);
|
||||||
|
void bios_flush(void);
|
||||||
|
|
||||||
|
/* Low-level routines implemented by the resident assembly BIOS. */
|
||||||
|
void io_out(uint8_t port, uint8_t value);
|
||||||
|
uint8_t io_in(uint8_t port);
|
||||||
|
void delay_ms(uint16_t milliseconds);
|
||||||
|
void enable_interrupts(void);
|
||||||
|
void disable_interrupts(void);
|
||||||
|
void jump_to(uint16_t address);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -1,11 +1,9 @@
|
|||||||
STACK_TOP = 0xFFFF
|
STACK_TOP = 0xFFFF
|
||||||
DELAY_LOOPS_PER_MS = 40
|
DELAY_LOOPS_PER_MS = 40
|
||||||
MONITOR_BASE = 0xC000
|
RESIDENT_BASE = 0xC000
|
||||||
MONITOR_ROM_SOURCE = 0x0080
|
RESIDENT_ROM_SOURCE = 0x0080
|
||||||
TINYBASIC_BASE = 0xD000
|
|
||||||
TINYBASIC_ROM_SOURCE = 0x1000
|
|
||||||
|
|
||||||
.include "tinybasic.inc"
|
.include "bios.inc"
|
||||||
|
|
||||||
.globl _main
|
.globl _main
|
||||||
.globl _delay_ms
|
.globl _delay_ms
|
||||||
@@ -14,9 +12,11 @@ TINYBASIC_ROM_SOURCE = 0x1000
|
|||||||
.globl _enable_interrupts
|
.globl _enable_interrupts
|
||||||
.globl _disable_interrupts
|
.globl _disable_interrupts
|
||||||
.globl _jump_to
|
.globl _jump_to
|
||||||
.globl _monitor_uart_putc
|
.globl _bios_putc
|
||||||
.globl _monitor_uart_getc
|
.globl _bios_try_getc
|
||||||
.globl monitor_image_end
|
.globl _bios_getc
|
||||||
|
.globl _bios_init
|
||||||
|
.globl resident_image_end
|
||||||
.globl s__DATA
|
.globl s__DATA
|
||||||
.globl l__DATA
|
.globl l__DATA
|
||||||
.globl s__INITIALIZER
|
.globl s__INITIALIZER
|
||||||
@@ -31,50 +31,56 @@ TINYBASIC_ROM_SOURCE = 0x1000
|
|||||||
boot_copy:
|
boot_copy:
|
||||||
di
|
di
|
||||||
ld sp, #STACK_TOP
|
ld sp, #STACK_TOP
|
||||||
ld hl, #MONITOR_ROM_SOURCE
|
ld hl, #RESIDENT_ROM_SOURCE
|
||||||
ld de, #MONITOR_BASE
|
ld de, #RESIDENT_BASE
|
||||||
ld bc, #(monitor_image_end - MONITOR_BASE)
|
ld bc, #(resident_image_end - RESIDENT_BASE)
|
||||||
ldir
|
|
||||||
|
|
||||||
ld hl, #TINYBASIC_ROM_SOURCE
|
|
||||||
ld de, #TINYBASIC_BASE
|
|
||||||
ld bc, #TINYBASIC_SIZE
|
|
||||||
ldir
|
ldir
|
||||||
jp init
|
jp init
|
||||||
|
|
||||||
.area _ITABLE (ABS)
|
.area _ITABLE (ABS)
|
||||||
|
|
||||||
; The executable vector page is part of the image copied to high RAM.
|
; The executable vector page is part of the image copied to high RAM.
|
||||||
.org MONITOR_BASE
|
.org RESIDENT_BASE
|
||||||
jp init
|
jp init
|
||||||
.org MONITOR_BASE + 0x08
|
.org RESIDENT_BASE + 0x08
|
||||||
reti
|
reti
|
||||||
.org MONITOR_BASE + 0x10
|
.org RESIDENT_BASE + 0x10
|
||||||
reti
|
reti
|
||||||
.org MONITOR_BASE + 0x18
|
.org RESIDENT_BASE + 0x18
|
||||||
reti
|
reti
|
||||||
.org MONITOR_BASE + 0x20
|
.org RESIDENT_BASE + 0x20
|
||||||
reti
|
reti
|
||||||
.org MONITOR_BASE + 0x28
|
.org RESIDENT_BASE + 0x28
|
||||||
reti
|
reti
|
||||||
.org MONITOR_BASE + 0x30
|
.org RESIDENT_BASE + 0x30
|
||||||
reti
|
reti
|
||||||
.org MONITOR_BASE + 0x38
|
.org RESIDENT_BASE + 0x38
|
||||||
reti
|
reti
|
||||||
|
|
||||||
; IM 2 table: the CTC supplies vectors 40, 42, 44, and 46.
|
; IM 2 table: the CTC supplies vectors 40, 42, 44, and 46.
|
||||||
.org MONITOR_BASE + 0x40
|
.org RESIDENT_BASE + 0x40
|
||||||
.dw ctc_isr ; CTC channel 0: vector 0x40
|
.dw ctc_isr ; CTC channel 0: vector 0x40
|
||||||
.dw ctc_isr ; CTC channel 1: vector 0x42
|
.dw ctc_isr ; CTC channel 1: vector 0x42
|
||||||
.dw ctc_isr ; CTC channel 2: vector 0x44
|
.dw ctc_isr ; CTC channel 2: vector 0x44
|
||||||
.dw uart_isr ; CTC channel 3: vector 0x46
|
.dw uart_isr ; CTC channel 3: vector 0x46
|
||||||
|
|
||||||
; Stable entry points for programs living alongside the monitor.
|
; Stable BIOS jump table for ROM-resident applications.
|
||||||
; C050: write the character in A. C053: return a character in A, or 0.
|
; C050 putc(A), C053 try_getc(), C056 getc(), C059 io_out(A,L),
|
||||||
.org MONITOR_BASE + 0x50
|
; C05C io_in(A), C05F delay_ms(HL), C062 monitor warm start.
|
||||||
jp monitor_service_putc
|
.org BIOS_PUTC
|
||||||
.org MONITOR_BASE + 0x53
|
jp bios_service_putc
|
||||||
jp monitor_service_getc
|
.org BIOS_TRY_GETC
|
||||||
|
jp bios_service_try_getc
|
||||||
|
.org BIOS_GETC
|
||||||
|
jp bios_service_getc
|
||||||
|
.org BIOS_IO_OUT
|
||||||
|
jp bios_service_io_out
|
||||||
|
.org BIOS_IO_IN
|
||||||
|
jp bios_service_io_in
|
||||||
|
.org BIOS_DELAY_MS
|
||||||
|
jp bios_service_delay_ms
|
||||||
|
.org BIOS_MONITOR
|
||||||
|
jp init
|
||||||
|
|
||||||
.area _HOME
|
.area _HOME
|
||||||
init:
|
init:
|
||||||
@@ -87,6 +93,7 @@ init:
|
|||||||
ld a, #0xC0
|
ld a, #0xC0
|
||||||
ld i, a
|
ld i, a
|
||||||
im 2
|
im 2
|
||||||
|
call _bios_init
|
||||||
call _main
|
call _main
|
||||||
|
|
||||||
hang:
|
hang:
|
||||||
@@ -124,20 +131,19 @@ clear_loop:
|
|||||||
gsinit_done:
|
gsinit_done:
|
||||||
.area _GSFINAL
|
.area _GSFINAL
|
||||||
ret
|
ret
|
||||||
monitor_image_end:
|
resident_image_end:
|
||||||
|
|
||||||
.area _CODE
|
.area _CODE
|
||||||
|
|
||||||
; Monitor ABI services preserve all working registers. PUTC also preserves AF;
|
; BIOS ABI services preserve registers except documented return values.
|
||||||
; GETC returns the received byte (or zero) in A.
|
bios_service_putc:
|
||||||
monitor_service_putc:
|
|
||||||
push af
|
push af
|
||||||
push bc
|
push bc
|
||||||
push de
|
push de
|
||||||
push hl
|
push hl
|
||||||
push ix
|
push ix
|
||||||
push iy
|
push iy
|
||||||
call _monitor_uart_putc
|
call _bios_putc
|
||||||
pop iy
|
pop iy
|
||||||
pop ix
|
pop ix
|
||||||
pop hl
|
pop hl
|
||||||
@@ -146,13 +152,13 @@ monitor_service_putc:
|
|||||||
pop af
|
pop af
|
||||||
ret
|
ret
|
||||||
|
|
||||||
monitor_service_getc:
|
bios_service_try_getc:
|
||||||
push bc
|
push bc
|
||||||
push de
|
push de
|
||||||
push hl
|
push hl
|
||||||
push ix
|
push ix
|
||||||
push iy
|
push iy
|
||||||
call _monitor_uart_getc
|
call _bios_try_getc
|
||||||
pop iy
|
pop iy
|
||||||
pop ix
|
pop ix
|
||||||
pop hl
|
pop hl
|
||||||
@@ -160,6 +166,66 @@ monitor_service_getc:
|
|||||||
pop bc
|
pop bc
|
||||||
ret
|
ret
|
||||||
|
|
||||||
|
bios_service_getc:
|
||||||
|
push bc
|
||||||
|
push de
|
||||||
|
push hl
|
||||||
|
push ix
|
||||||
|
push iy
|
||||||
|
call _bios_getc
|
||||||
|
pop iy
|
||||||
|
pop ix
|
||||||
|
pop hl
|
||||||
|
pop de
|
||||||
|
pop bc
|
||||||
|
ret
|
||||||
|
|
||||||
|
bios_service_io_out:
|
||||||
|
push af
|
||||||
|
push bc
|
||||||
|
push de
|
||||||
|
push hl
|
||||||
|
push ix
|
||||||
|
push iy
|
||||||
|
call _io_out
|
||||||
|
pop iy
|
||||||
|
pop ix
|
||||||
|
pop hl
|
||||||
|
pop de
|
||||||
|
pop bc
|
||||||
|
pop af
|
||||||
|
ret
|
||||||
|
|
||||||
|
bios_service_io_in:
|
||||||
|
push bc
|
||||||
|
push de
|
||||||
|
push hl
|
||||||
|
push ix
|
||||||
|
push iy
|
||||||
|
call _io_in
|
||||||
|
pop iy
|
||||||
|
pop ix
|
||||||
|
pop hl
|
||||||
|
pop de
|
||||||
|
pop bc
|
||||||
|
ret
|
||||||
|
|
||||||
|
bios_service_delay_ms:
|
||||||
|
push af
|
||||||
|
push bc
|
||||||
|
push de
|
||||||
|
push hl
|
||||||
|
push ix
|
||||||
|
push iy
|
||||||
|
call _delay_ms
|
||||||
|
pop iy
|
||||||
|
pop ix
|
||||||
|
pop hl
|
||||||
|
pop de
|
||||||
|
pop bc
|
||||||
|
pop af
|
||||||
|
ret
|
||||||
|
|
||||||
.macro MAKE_ISR entry, handler
|
.macro MAKE_ISR entry, handler
|
||||||
.globl handler
|
.globl handler
|
||||||
entry:
|
entry:
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
#ifndef __H_BOOT
|
|
||||||
#define __H_BOOT
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
void io_out(uint8_t a, uint8_t d);
|
|
||||||
uint8_t io_in(uint8_t d);
|
|
||||||
void delay_ms(uint16_t t);
|
|
||||||
void enable_interrupts(void);
|
|
||||||
void disable_interrupts(void);
|
|
||||||
void jump_to(uint16_t address);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
; 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
|
||||||
+36
-153
@@ -1,113 +1,11 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include "boot.h"
|
#include "bios.h"
|
||||||
#include "circ_buf.h"
|
|
||||||
|
|
||||||
#define UART_CTC_CARD_BASE 0x20
|
|
||||||
#define CTC_BASE (UART_CTC_CARD_BASE + 0x00)
|
|
||||||
#define CTC_CHANNEL_0 (CTC_BASE + 0)
|
|
||||||
#define CTC_CHANNEL_3 (CTC_BASE + 3)
|
|
||||||
#define UART_BASE (UART_CTC_CARD_BASE + 0x04)
|
|
||||||
#define UART_CONTROL (UART_BASE + 0)
|
|
||||||
#define UART_DATA (UART_BASE + 1)
|
|
||||||
|
|
||||||
#define LINE_SIZE 80
|
#define LINE_SIZE 80
|
||||||
#define MAX_ARGS 40
|
#define MAX_ARGS 40
|
||||||
#define DEFAULT_DUMP_LENGTH 0x40
|
#define DEFAULT_DUMP_LENGTH 0x40
|
||||||
#define MAX_DUMP_LENGTH 0x100
|
#define MAX_DUMP_LENGTH 0x100
|
||||||
|
|
||||||
volatile uint8_t uart_tx_busy;
|
|
||||||
volatile circular_buffer_t rx_buf;
|
|
||||||
volatile circular_buffer_t tx_buf;
|
|
||||||
|
|
||||||
static void uart_kick(void)
|
|
||||||
{
|
|
||||||
char c;
|
|
||||||
|
|
||||||
disable_interrupts();
|
|
||||||
if (!uart_tx_busy && buffer_dequeue(&tx_buf, &c)) {
|
|
||||||
uart_tx_busy = 1;
|
|
||||||
io_out(UART_DATA, c);
|
|
||||||
io_out(UART_CONTROL, 0b10110101);
|
|
||||||
}
|
|
||||||
enable_interrupts();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void uart_putc(char c)
|
|
||||||
{
|
|
||||||
uint8_t queued;
|
|
||||||
|
|
||||||
do {
|
|
||||||
disable_interrupts();
|
|
||||||
queued = buffer_enqueue(&tx_buf, c);
|
|
||||||
enable_interrupts();
|
|
||||||
if (!queued) uart_kick();
|
|
||||||
} while (!queued);
|
|
||||||
uart_kick();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Stable monitor ABI implementations, reached through the jump table at C050. */
|
|
||||||
void monitor_uart_putc(char c)
|
|
||||||
{
|
|
||||||
uart_putc(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t monitor_uart_getc(void)
|
|
||||||
{
|
|
||||||
char c;
|
|
||||||
uint8_t received;
|
|
||||||
|
|
||||||
disable_interrupts();
|
|
||||||
received = buffer_dequeue(&rx_buf, &c);
|
|
||||||
enable_interrupts();
|
|
||||||
return received ? (uint8_t)c : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void uart_puts(const char *s)
|
|
||||||
{
|
|
||||||
while (*s) uart_putc(*s++);
|
|
||||||
}
|
|
||||||
|
|
||||||
static char uart_getc(void)
|
|
||||||
{
|
|
||||||
char c;
|
|
||||||
uint8_t received;
|
|
||||||
|
|
||||||
do {
|
|
||||||
disable_interrupts();
|
|
||||||
received = buffer_dequeue(&rx_buf, &c);
|
|
||||||
enable_interrupts();
|
|
||||||
} while (!received);
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void uart_flush(void)
|
|
||||||
{
|
|
||||||
while (uart_tx_busy || !buffer_is_empty(&tx_buf)) uart_kick();
|
|
||||||
}
|
|
||||||
|
|
||||||
void uart_interrupt(void)
|
|
||||||
{
|
|
||||||
uint8_t status = io_in(UART_CONTROL);
|
|
||||||
|
|
||||||
if ((status & 0x21) != 0) {
|
|
||||||
char c = io_in(UART_DATA);
|
|
||||||
if ((status & 0x01) != 0) buffer_enqueue(&rx_buf, c);
|
|
||||||
}
|
|
||||||
if ((status & 0x02) != 0 && uart_tx_busy) {
|
|
||||||
char c;
|
|
||||||
if (buffer_dequeue(&tx_buf, &c)) {
|
|
||||||
io_out(UART_DATA, c);
|
|
||||||
} else {
|
|
||||||
io_out(UART_CONTROL, 0b10010101);
|
|
||||||
uart_tx_busy = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ctc_interrupt(void)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
static char hex_digit(uint8_t value)
|
static char hex_digit(uint8_t value)
|
||||||
{
|
{
|
||||||
value &= 0x0f;
|
value &= 0x0f;
|
||||||
@@ -116,8 +14,8 @@ static char hex_digit(uint8_t value)
|
|||||||
|
|
||||||
static void print_hex8(uint8_t value)
|
static void print_hex8(uint8_t value)
|
||||||
{
|
{
|
||||||
uart_putc(hex_digit(value >> 4));
|
bios_putc(hex_digit(value >> 4));
|
||||||
uart_putc(hex_digit(value));
|
bios_putc(hex_digit(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void print_hex16(uint16_t value)
|
static void print_hex16(uint16_t value)
|
||||||
@@ -192,14 +90,14 @@ static uint8_t split_line(char *line, char **argv)
|
|||||||
|
|
||||||
static void print_help(void)
|
static void print_help(void)
|
||||||
{
|
{
|
||||||
uart_puts(
|
bios_puts(
|
||||||
"Commands (hexadecimal numbers):\r\n"
|
"Commands (hexadecimal numbers):\r\n"
|
||||||
" D|DUMP address [length] Dump memory (default 0040, max 0100)\r\n"
|
" D|DUMP address [length] Dump memory (default 0040, max 0100)\r\n"
|
||||||
" W|WRITE address byte ... Write one or more memory bytes\r\n"
|
" W|WRITE address byte ... Write one or more memory bytes\r\n"
|
||||||
" I|IN port Read an I/O port\r\n"
|
" I|IN port Read an I/O port\r\n"
|
||||||
" O|OUT port byte Write an I/O port\r\n"
|
" O|OUT port byte Write an I/O port\r\n"
|
||||||
" G|GO address Execute code; RET returns here\r\n"
|
" G|GO address Execute code; RET returns here\r\n"
|
||||||
" B|BASIC Start Tiny BASIC at D000\r\n"
|
" B|BASIC Start ROM Tiny BASIC at 1000\r\n"
|
||||||
" H|HELP|? Show this help\r\n");
|
" H|HELP|? Show this help\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -211,11 +109,11 @@ static void command_dump(uint8_t argc, char **argv)
|
|||||||
|
|
||||||
if (argc < 2 || argc > 3 || !parse_hex(argv[1], &address) ||
|
if (argc < 2 || argc > 3 || !parse_hex(argv[1], &address) ||
|
||||||
(argc == 3 && !parse_hex(argv[2], &length))) {
|
(argc == 3 && !parse_hex(argv[2], &length))) {
|
||||||
uart_puts("Usage: D address [length]\r\n");
|
bios_puts("Usage: D address [length]\r\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (length > MAX_DUMP_LENGTH) {
|
if (length > MAX_DUMP_LENGTH) {
|
||||||
uart_puts("Length must be 0100 or less\r\n");
|
bios_puts("Length must be 0100 or less\r\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
while (offset < length) {
|
while (offset < length) {
|
||||||
@@ -223,12 +121,12 @@ static void command_dump(uint8_t argc, char **argv)
|
|||||||
uint8_t row_length = (uint8_t)((length - offset) > 16 ? 16 : (length - offset));
|
uint8_t row_length = (uint8_t)((length - offset) > 16 ? 16 : (length - offset));
|
||||||
|
|
||||||
print_hex16((uint16_t)(address + offset));
|
print_hex16((uint16_t)(address + offset));
|
||||||
uart_puts(": ");
|
bios_puts(": ");
|
||||||
for (column = 0; column < row_length; ++column) {
|
for (column = 0; column < row_length; ++column) {
|
||||||
print_hex8(*(volatile uint8_t *)(address + offset + column));
|
print_hex8(*(volatile uint8_t *)(address + offset + column));
|
||||||
uart_putc(' ');
|
bios_putc(' ');
|
||||||
}
|
}
|
||||||
uart_puts("\r\n");
|
bios_puts("\r\n");
|
||||||
offset += row_length;
|
offset += row_length;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -240,19 +138,19 @@ static void command_write(uint8_t argc, char **argv)
|
|||||||
uint8_t i;
|
uint8_t i;
|
||||||
|
|
||||||
if (argc < 3 || !parse_hex(argv[1], &address)) {
|
if (argc < 3 || !parse_hex(argv[1], &address)) {
|
||||||
uart_puts("Usage: W address byte [byte ...]\r\n");
|
bios_puts("Usage: W address byte [byte ...]\r\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (i = 2; i < argc; ++i) {
|
for (i = 2; i < argc; ++i) {
|
||||||
if (!parse_hex(argv[i], &value) || value > 0xff) {
|
if (!parse_hex(argv[i], &value) || value > 0xff) {
|
||||||
uart_puts("Bad byte: ");
|
bios_puts("Bad byte: ");
|
||||||
uart_puts(argv[i]);
|
bios_puts(argv[i]);
|
||||||
uart_puts("\r\n");
|
bios_puts("\r\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
*(volatile uint8_t *)address++ = (uint8_t)value;
|
*(volatile uint8_t *)address++ = (uint8_t)value;
|
||||||
}
|
}
|
||||||
uart_puts("OK\r\n");
|
bios_puts("OK\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void command_in(uint8_t argc, char **argv)
|
static void command_in(uint8_t argc, char **argv)
|
||||||
@@ -260,13 +158,13 @@ static void command_in(uint8_t argc, char **argv)
|
|||||||
uint16_t port;
|
uint16_t port;
|
||||||
|
|
||||||
if (argc != 2 || !parse_hex(argv[1], &port) || port > 0xff) {
|
if (argc != 2 || !parse_hex(argv[1], &port) || port > 0xff) {
|
||||||
uart_puts("Usage: I port\r\n");
|
bios_puts("Usage: I port\r\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
print_hex8((uint8_t)port);
|
print_hex8((uint8_t)port);
|
||||||
uart_puts(": ");
|
bios_puts(": ");
|
||||||
print_hex8(io_in((uint8_t)port));
|
print_hex8(io_in((uint8_t)port));
|
||||||
uart_puts("\r\n");
|
bios_puts("\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void command_out(uint8_t argc, char **argv)
|
static void command_out(uint8_t argc, char **argv)
|
||||||
@@ -276,21 +174,21 @@ static void command_out(uint8_t argc, char **argv)
|
|||||||
|
|
||||||
if (argc != 3 || !parse_hex(argv[1], &port) || port > 0xff ||
|
if (argc != 3 || !parse_hex(argv[1], &port) || port > 0xff ||
|
||||||
!parse_hex(argv[2], &value) || value > 0xff) {
|
!parse_hex(argv[2], &value) || value > 0xff) {
|
||||||
uart_puts("Usage: O port byte\r\n");
|
bios_puts("Usage: O port byte\r\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
io_out((uint8_t)port, (uint8_t)value);
|
io_out((uint8_t)port, (uint8_t)value);
|
||||||
uart_puts("OK\r\n");
|
bios_puts("OK\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void execute_address(uint16_t address)
|
static void execute_address(uint16_t address)
|
||||||
{
|
{
|
||||||
uart_puts("Executing at ");
|
bios_puts("Executing at ");
|
||||||
print_hex16(address);
|
print_hex16(address);
|
||||||
uart_puts("...\r\n");
|
bios_puts("...\r\n");
|
||||||
uart_flush();
|
bios_flush();
|
||||||
jump_to(address);
|
jump_to(address);
|
||||||
uart_puts("Returned to monitor\r\n");
|
bios_puts("Returned to monitor\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void command_go(uint8_t argc, char **argv)
|
static void command_go(uint8_t argc, char **argv)
|
||||||
@@ -298,7 +196,7 @@ static void command_go(uint8_t argc, char **argv)
|
|||||||
uint16_t address;
|
uint16_t address;
|
||||||
|
|
||||||
if (argc != 2 || !parse_hex(argv[1], &address)) {
|
if (argc != 2 || !parse_hex(argv[1], &address)) {
|
||||||
uart_puts("Usage: G address\r\n");
|
bios_puts("Usage: G address\r\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
execute_address(address);
|
execute_address(address);
|
||||||
@@ -307,10 +205,10 @@ static void command_go(uint8_t argc, char **argv)
|
|||||||
static void command_basic(uint8_t argc)
|
static void command_basic(uint8_t argc)
|
||||||
{
|
{
|
||||||
if (argc != 1) {
|
if (argc != 1) {
|
||||||
uart_puts("Usage: B\r\n");
|
bios_puts("Usage: B\r\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
execute_address(0xd000);
|
execute_address(0x1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void execute_line(char *line)
|
static void execute_line(char *line)
|
||||||
@@ -335,7 +233,7 @@ static void execute_line(char *line)
|
|||||||
} else if (strings_equal(argv[0], "b") || strings_equal(argv[0], "basic")) {
|
} else if (strings_equal(argv[0], "b") || strings_equal(argv[0], "basic")) {
|
||||||
command_basic(argc);
|
command_basic(argc);
|
||||||
} else {
|
} else {
|
||||||
uart_puts("Unknown command; type HELP\r\n");
|
bios_puts("Unknown command; type HELP\r\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -345,25 +243,10 @@ void main(void)
|
|||||||
uint8_t length = 0;
|
uint8_t length = 0;
|
||||||
uint8_t ignore_lf = 0;
|
uint8_t ignore_lf = 0;
|
||||||
|
|
||||||
buffer_init(&rx_buf);
|
bios_puts("\r\nZ80 monitor - v1\r\nType HELP for commands.\r\n> ");
|
||||||
buffer_init(&tx_buf);
|
|
||||||
|
|
||||||
io_out(CTC_CHANNEL_0, 0x57);
|
|
||||||
io_out(CTC_CHANNEL_0, 13);
|
|
||||||
io_out(CTC_CHANNEL_3, 0b11001111);
|
|
||||||
io_out(CTC_CHANNEL_3, 1);
|
|
||||||
io_out(CTC_CHANNEL_0, 0x00);
|
|
||||||
io_out(CTC_CHANNEL_0, 0x40);
|
|
||||||
|
|
||||||
uart_tx_busy = 0;
|
|
||||||
io_out(UART_CONTROL, 0b00000011);
|
|
||||||
io_out(UART_CONTROL, 0b10010101);
|
|
||||||
enable_interrupts();
|
|
||||||
|
|
||||||
uart_puts("\r\nZ80 monitor - v1\r\nType HELP for commands.\r\n> ");
|
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
char c = uart_getc();
|
char c = bios_getc();
|
||||||
|
|
||||||
if (c == '\n' && ignore_lf) {
|
if (c == '\n' && ignore_lf) {
|
||||||
ignore_lf = 0;
|
ignore_lf = 0;
|
||||||
@@ -373,25 +256,25 @@ void main(void)
|
|||||||
if (c == '\r' || c == '\n') {
|
if (c == '\r' || c == '\n') {
|
||||||
ignore_lf = (c == '\r');
|
ignore_lf = (c == '\r');
|
||||||
line[length] = 0;
|
line[length] = 0;
|
||||||
uart_puts("\r\n");
|
bios_puts("\r\n");
|
||||||
execute_line(line);
|
execute_line(line);
|
||||||
length = 0;
|
length = 0;
|
||||||
uart_puts("> ");
|
bios_puts("> ");
|
||||||
} else if (c == '\b' || c == 0x7f) {
|
} else if (c == '\b' || c == 0x7f) {
|
||||||
if (length) {
|
if (length) {
|
||||||
--length;
|
--length;
|
||||||
uart_puts("\b \b");
|
bios_puts("\b \b");
|
||||||
}
|
}
|
||||||
} else if (c == 0x15) {
|
} else if (c == 0x15) {
|
||||||
while (length) {
|
while (length) {
|
||||||
--length;
|
--length;
|
||||||
uart_puts("\b \b");
|
bios_puts("\b \b");
|
||||||
}
|
}
|
||||||
} else if (c >= ' ' && c <= '~' && length < LINE_SIZE - 1) {
|
} else if (c >= ' ' && c <= '~' && length < LINE_SIZE - 1) {
|
||||||
line[length++] = c;
|
line[length++] = c;
|
||||||
uart_putc(c);
|
bios_putc(c);
|
||||||
} else if (c >= ' ' && c <= '~') {
|
} else if (c >= ' ' && c <= '~') {
|
||||||
uart_putc('\a');
|
bios_putc('\a');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user