Libby8‎ > ‎

Concept

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:
  1. The CPU (in this case a Z80, though it might be possible to use a 6502 though I haven't investigated this).
  2. Some RAM. A real home computer runs all its code from RAM (a Microcontroller is dominated by Flash and usually has little RAM).
  3. Some non-volatile storage. In an 80's home computer, they all had a built-in ROM which provided the bootup code; the computer's 'OS' and language (almost always BASIC). We will be using a serial Flash chip (4Mbit, 512Kb) for all of this.
  4. Some glue logic to manage address decoding and peripherals. We will be using a DIL-based Atmel AVR Microcontroller.

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.

Booting

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.

 Step InstructionComments
 0 ld de,00 /OE is kept low and the AVR supplies <ld de,nn> <0> <0> on 3 successive Z80 opcode fetches.
 1 ld a,n /OE is kept low and the AVR supplies <ld a,n> <Rom[addr++]> on 2 successive Z80 opcode fetches.
 2 ld (de),a The AVR supplies <ld (de),a> on the opcode fetch and tri-states its Z80 Data port during the Z80's memory write cycle, so the Z80 transfers the loaded Rom byte to RAM.
 3 inc de The AVR supplies <inc de> on the opcode fetch and waits an extra 2 cycles. It then repeats from step 1 until the ROM is fully transferred.

 3*RomSize+1

jp 0The ROM is now loaded and AVR supplies <jp> <0> <0> on the opcode fetch to begin Z80 execution from RAM (which contains the ROM), /OE is released.

Video

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:

  1. We force the Z80 to start running from video RAM and fetch 256b of video, substituting nop instructions on the Z80 data bus after each video byte fetch. This phase will take 256µs, since at 4MHz each NOP will take 1µs. During this time we'll output video via SPI whilst reading in from the Z80, but since the Z80 is executing video memory at twice the rate on average, then we'll store the excess in the video buffer.
  2. We let the Z80 carry on executing normal code and generate video from the remaining 128b of video from the video buffer.

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:


NMI:
   push af
   push hl
   ld a,(VidBnk) ;starts at 0xe800, RAM is duplicated  at addresses 0x8000 to 0xffff.
   ld l,0 ;video always begins on 256b boundary.
   ld h,a
   call NMI0
   inc a
   jr nz NMI1
   ld a,0xe8 ;restart video.
NMI1:
   ld (VidBnk),a
   pop hl
   pop af
   retn
NMI0:
   jp (hl) ;jump to video, executes nops and avr returns the z80 to the inc a.

Peripherals

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:


ORG 8 ;AVR I/O interface.
AvrIO:
    ld a,(hl)
    inc hl
    djnz AvrIO
    ld c,b
    jr AvrIO2

ORG 32
AvrIO2:
    ld (de),a
    inc de
    djnz AvrIO2
    reti

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.

Comments