To understand what Libby8 is about you'll need to know at least something about the following:
The objective of Libby8 is to be able to build a hobby home computer with a minimal number of parts (4 chips or so, comparable with a ZX81) on Stripboard. We will also need some descrete components, but the chips themselves will be:
In Libby8 all the clever work for managing the glue logic will be performed by a lowly 8-bit Microcontroller. We need to show how we can boot up a Z80, handle the Flash storage; handle the peripherals (most importantly, the video and keyboard) and finally how to manage external I/O. As far as I know, no-one has yet used a Microcontroller as the glue logic for a computer - normally you would use an FPGA, so what you're looking at here is an innovation and it's in the public domain. So, in terms of Intellectual Property, this concept constitutes prior art.
Libby8 needs to be able to boot a Z80 and do all the necessary address decoding. However, we have 2 major problems: firstly, there isn't enough spare I/O pins to do any useful address decoding and manage peripherals (around 30-pins would be used for address, data and other Z80 signals if we were to do that; leaving about 4 for everything else) and secondly the AVR wouldn't be fast enough to substitute for logic in that way.
However, it turns out that we should be able to boot the Z80 if we can count the Z80 cycles and all we have access to is the Z80's data bus (D0..D7), the Z80 /RESET signal and the /M1 signal (we also need to manage the RAM's /OE signal too). The AVR shold be fast enough for that, so, the AVR can control the Z80 without having access to any address lines.
@TODO - include diagrams.
Let's assume the AVR is synchronised with the Z80 - the clock input on the Z80 is a timer output from the AVR running at 1/4 of the AVR's 16MHz clock input (i.e. 4MHz). The AVR pulls /RESET low; releases it and waits for the Z80 to start executing instructions. The AVR can monitor instruction execution by looking at the /M1 signal; it goes low at the beginning of an instruction cycle.
So, we get the AVR to boot up the Z80 by supplying instructions to the Z80 in place of ones it would read from memory and the instruction sequence will get the Z80 to load its boot up ROM.
If you know anything about the ZX81 (and its predecessor, the ZX80) you'll know that it generates its video display largely in software. For a 3.5MHz 8-bit CPU, that's pretty clever. In summary, the ZX81 does it by getting the CPU to start running from the video display memory and for every instruction fetch, the ULA reads the video byte and substitutes a NOP instruction on the CPU's data bus. The refresh cycle then supplies the memory fetch for the bit pattern for the character in question. Each Video line on a ZX81 ends with a HALT instruction which the ULA does not translate. The Z80 halts, waiting for the next NMI which transfers execution to ROM and performs the next video scan.
On Libby8 we'll use a similar scheme, except that we'll use a bitmapped display and achieve higher performance by buffering video on the AVR.
@TODO - include diagrams.
Let's assume that Libby8's video is 256x192. I think we could achieve better than that in the long run, but this spec keeps it simple for now. The AVR generates the sync signal by itself and its scan-line code assumes that there's a 256b video buffer (8 scans) available to supply video data to the dot clock output. There's something like (288-192)/2 = 48 of blank lines before the actual image and another 48 after the image.
The Sync signal is a timer output running at 15.625KHz. The dot clock output is via SPI and runs at CPU clock/3 (5.3333MHz), so 256 pixels take 48µs to clock out (leaving 16µs until the next scan). The entire 256b of video (8 scans) is equivalent to 64µs*8 = 512µs. Every block of 256b consists of two phases:
What this means is that we'll get better performance from Libby8 than a ZX81 (which has to be good!). We can calculate this. There are 625 lines on PAL, but since each image is repeated on every interlace, then there's 312.5 lines per interlace. 192 execute at 50% performance (256µs in every 512µs), the rest at pretty much full performance. That's (192*0.5 + (312.5-192))/312.5, so we'll get around 2.75MHz from a 4MHz Z80; which is passable for an early home computer. The Z80 video handling code is relatively simple and will look something like:
The AVR functions as a peripheral interface as well as the glue logic. However, since the AVR doesn't directly decode address lines we support peripherals via data packets sent between the Z80 and the AVR. We support this by using two more Z80 signals: /HALT and /INT.
I/O data packets have a simple protocol. HL is set to point to the command buffer. DE is set to point to a data output buffer, b contains the command length (>=1); c contains the output length (>=1).
Data packets are initiated via the /HALT signal, which is connected to an AVR interrupt line. When the AVR is free from generating video it responds to the interrupt; synchronises with the Z80 clock; asserts /INT and supplies an RST 8 instruction as the interrupt vector (Z80 Mode 0 interrupts). The Z80's interrupt vector executes:
The Avr monitors the data bus for ld a,(hl) and loads its command buffer accordingly. It then performs the I/O operation according to the command buffer, returning results via the ld (de),a instructions (which it monitors for). The Avr can supply interrupted results by a similar mechanism, interrupting the Z80 and supplying an RST instruction (e.g. RST 32). Here, we just return results via ld (de),a instructions (the AvrIO2 code).
This is a fairly versatile system though it's not re-entrant. The next stage is to specify the commands to access the peripherals, mostly obviously the Flash memory and keyboard.