That aside, I'm quite interested in kernels (as indicated by QOS) so it makes sense that I'll be attempting to write a simple one for it. Ideally, my vision is to interface with a terminal (with UART through a SIO) and create a binary loader (through a DMA). From there, new software can be serialised into SRAM and ran immediately, without having to write to an EEPROM.
As frontend to this operating system, I'm going to attempt to recreate Wozmon (Apple I software for the 6502) on the Z80.
Part list
I'm using the 6 MHz specs of all the Z84Cxx components, except for the CTC (which is an NMOS spec):
- Z84C00 (CMOS, 6 MHz) 'Z80 CPU'
- Z84C20 (CMOS, 6 MHz) 'PIO' (Parallel Input/Output)
- Z84C30 (NMOS, 6 MHz) 'CTC' (Counter/Timer Circuit)
- Z84C40 (CMOS, 6 MHz) 'SIO/0' (Serial Input/Output)
- W27C020 (256 KiB) EEPROM
- AS6C1008 (128 KiB) SRAM
- SN74LS32N ORs
- SN74HC14N Hex Inverters
- SN74HC238N 3-to-8 Line Decoder
Now, I'm running this system on a 4 MHz crystal oscillator. I also have a 1.8432 MHz crystal oscillator tied to the trigger inputs of the CTC for the first three channels. The fourth channel trigger input is tied to the trigger output of the third channel. The trigger inputs are used whilst in counter mode, and allow me to use them for flexible baudrate generation. Channels 0 and 1 of the CTC are tied to the clocks of SIO channels A and B, respectively.
Memory map
For those who are unfamiliar with the Z80 address space, and unlike
most other CPUs around that time, the Z80 has separate memory and I/O
spaces. This is indicated by the MREQ and
IOREQ control lines from the CPU.
In the memory range, I set up the memory map so writable RAM takes up the majority of space. This is for the reason I stated before, as I'd like to upload programs through a loader into RAM. So:
0000 - 3FFF :EEPROM (16k)4000 - FFFF :SRAM (48k)
Conversely, the I/O space is only 8 bit, rather than the full memory
address range of 16 bits. The Z80 implemented it like this essentially
due to the OTIR-like instructions, which use register
B for byte countdown, and register C for the
I/O address. In any case, the upper 3 bits of the address are decoded:
0b101xxxxx :PIO0b110xxxxx :SIO0b111xxxxx :CTC
Wozmon is the original software that came with the Apple I. I ran it on my 6502 breadboard project. Now, one with a few brain cells might realise that the Apple I isn't made with the Z80 CPU. A short superficial research shows that few have recreated it, but it seemed fun to do so, too.
My Wozmon, or ZMon as I'd like to call it, uses at the time of writing 253 bytes of EEPROM, without initialisation of I/O. That last bit costs another 40-ish bytes because the SIO needs quite a lot of bytes in sequence for it to be set up.
This hex monitor allows you to now write programs in hexadecimal and
jump to them. For example, 3E is the opcode for ld
a, n, D3 for out (n), a, and
C3 for the absolute jump instruction.
My goal of this Z80 System on a Breadboard (SoB) is to allow you to upload a binary file and immediately write it to RAM using a loader. This way, you can program it without rewriting the EEPROM, just through the UART connection with the SIO.
Today, I was able to collect the Z80 DMA that I had ordered:
I've registered the DMA peripheral at the following I/O address:
0b100xxxxx :DMA
I had to add another breadboard in order to fit the DMA. In the first picture, the DMA can be seen at the top. It connects to the control wires from the Z80 CPU, as well as bus request and bus acknowledge, and the address and data buses.
To test the DMA, I wrote a trivial program at address 1000
(in EEPROM) which copies 256 bytes from 5000 to
6000 (in RAM) and jumps back to ZMon. In the second
picture, the addresses are first assessed before calling
1000R (jumping to the DMA copy routine), after which the
addresses are printed out again, which shows that the DMA transferred
the source range to the destination range. I also wrote a program
(which is not depicted) that receives 16 bytes from an I/O port, which
tests the SIO's ready line to the DMA. This allows the SIO to trigger
DMA transfers from the I/O peripheral and sync it to memory
automatically, without CPU intervention.
Maybe I'll add another DMA for SIO Channel A, in order to use it for other purposes like disk transfers (if I'll ever create that). In any case, this direct transfer method allows me to (not easily, but more neatly) create a file loader. In contrast, I can now transfer entire ASCII files to the SIO without having to stall the CPU due to SIO lag (transmission delay).
Like I mentioned in the beginning of this thread, my goal is to create an operating system for the Z80 breadboard project. Particularly, a time-sharing kernel. The ZOS project is my attempt at it.
It's not much, currently, but I do have a simple shell to which I'm
able to jump to different programs such as ZMon,
memcpy, and the binary loader. A stdlib with map_find,
streq, and I/O operations such as echo,
echo_str, and echo_format_str are available.
In the future, I'll be adding pre-emptive multitasking and a kernel
process interface to this project.



