This commit is contained in:
Joppe Blondel
2026-10-01 12:44:11 +02:00
parent d19ffc70a1
commit 47fffc2e71
9 changed files with 467 additions and 19 deletions
+62 -8
View File
@@ -4,10 +4,12 @@ This project provides a resident BIOS, an interactive monitor, and ROM-resident
applications for a Z80 system with a UART/CTC card at I/O base `20`.
At reset, a small ROM bootstrap copies only the BIOS and monitor into the top
16 KiB of RAM at `C000`. The BIOS initializes interrupt mode 2 and the UART,
then 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`.
16 KiB at `C000`. Before copying, it writes `06` to banking port `00`: ROM
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`.
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
@@ -17,6 +19,7 @@ optional `0x` prefix is accepted.
| --- | --- |
| `D 8000` | Dump 40 bytes starting at address 8000 |
| `D 8000 20` | Dump 20 bytes starting at address 8000 |
| `C 8000 BFFF` | Compute the 16-bit additive checksum of an inclusive range |
| `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 |
@@ -29,12 +32,41 @@ optional `0x` prefix is accepted.
`RET` returns to the monitor. Code that changes the stack pointer, interrupt
mode, or UART/CTC setup must restore it before returning.
`C` adds every byte from the start through the end address, inclusive, and
prints the low 16 bits of the sum as four hexadecimal digits. The end address
must be greater than or equal to the start address.
Build the ROM and programmer-format images with:
```sh
make
```
## Uploading Intel HEX files
`program_ihex.py` can write an Intel HEX file to memory through the running
monitor. It sends data in small blocks with `W`, checks every block with `C`,
and retries a failed write or checksum before giving up. It requires Python 3
and [pySerial](https://pyserial.readthedocs.io/).
```sh
python3 -m pip install pyserial
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:
```sh
python3 program_ihex.py /dev/ttyUSB0 program.hex --retries 5 --block-size 24
```
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
file are written; gaps are left unchanged. The HEX addresses must refer to
writable memory and must not overwrite the running monitor at `C000-FFFF`.
The outputs are:
- `build/metal/monitor.bin`: complete 32 KiB ROM image
@@ -44,8 +76,8 @@ The outputs are:
The current compact image is 6,157 bytes. Its ROM layout is:
- `0000-007F`: reset/copy bootstrap
- `0080-0F9D`: packed BIOS+monitor image copied to RAM at `C000-CF1D`
- `0F9E-0FFF`: unused space reserved for the resident image to grow
- `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
@@ -58,13 +90,20 @@ 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-CF1D`: resident BIOS, vectors, services, and monitor
- `CF1E-EFFF`: free space for resident-image growth
- `C000-CF6A`: resident BIOS, vectors, services, and monitor
- `CF6B-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
applications naturally require ROM to remain mapped while they execute.
The banking latch bits used by the bootstrap are bit 0 `ROM_DISABLE`, bit 1
`BANK_SELECT`, and bit 2 `BANK_ENABLE`. Its initial value `06` leaves ROM on and
selects the lower physical 48 KiB plus the upper physical 16 KiB. Writing `07`
later disables ROM without changing that split mapping. Writing `05` selects
the complete upper physical 64 KiB; the monitor remains available because its
`C000-FFFF` image was placed in that upper bank during boot.
## BIOS interface
Assembly applications include `include/bios.inc` and call these fixed entries:
@@ -78,10 +117,25 @@ Assembly applications include `include/bios.inc` and call these fixed entries:
| `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 |
| `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 |
The service wrappers preserve working registers except for documented return
values. The monitor uses the corresponding C API declared in `bios.h`.
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.
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.
Project organization:
- `boot.asm`: ROM bootstrap, high vectors, BIOS jump table, and assembly glue