start of tape loading. hw works and have a generation script
This commit is contained in:
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user