The ZX Spectrum Reverse Engineering and Clone Desgin Blog


A site dedicated to the reverse engineering of the ZX Spectrum and related projects.

< 29 of 68 >

Hooking up the Z80 and ROM

May 18, 2007

Hooking up the Z80 and ROM/RAM should be relatively straightforward. I need to be careful to decouple the Z80 address bus from the VideoAddress bus when not accessing it, and to make sure that the Z80 waits before accessing the video memory when the video circuit is.

The only Z80 control signals that will be required, other than MREQ, IOREQ, RD and WR for memory and IO access will be CLK, RST and INT. The other active low input signals to the processor should be tied high.

At present there is no interrupt signal to deliver to INT, so that will be tied high for the moment. The CPU must be reset on power up to achieve proper operation, and so RST must be held low for at least three T-states. A simple way of achieving this is to pull RST high with a resistor, but tie it to ground with a capacitor. With a sensible choice of capacitor and resistor, the capacitor will hold RST low as is charges through the resistor. The capacitors effective resistance will drop during this period until it has charged, at which point it's resistance will be negligible and RST will be pulled high by the resistor.

The ZX Spectrum operates at a CPU clock of 3.5MHz. This is exactly half the pixel clock frequency of 7MHz, and accounts for the fact that two pixels are displayed in one T-state. The pixel clock is generated by dividing the output of a 14MHz crystal oscillator with a D-Type flip-flop, which results in a cleaner signal. Dividing it again will give the CPU clock.

Only one address can be presented to the lower 16K memory chip containing the video buffer at any one time. The 74HC275 chip used to multiplex the video display byte and attribute addresses has an enable line allowing it's output to be decoupled. Using a 74HC245 octal buffer on the lower address bits allows the whole bus to be decoupled.

Equally using octal buffers on the CPU address lines allows the same decoupling to take place. I just select the video address buffers and deselect the CPU address buffers when appropriate.

When the video address is selected (VMVen active), the lower 16K RAM CE and RD lines are always active. WR must be high.

When the CPU bus is selected (VMVen inactive), the lower 16K RAM lines CE, RD and WR must be the true CPU signals.

The ROM is selected whenever A14 and A15 are low. The lower 16K RAM is selected (RAM16) when A14 is high and A15 low.

The ZX Spectrum demonstrates a particular Z80 wait period. This must be faithfully reproduced if all games are to run successfully. The Spectrum ULA stops clocking the CPU with CLK held high whenever it needs access to the video memory at the same time as the CPU. This is a very simple and clever way of pausing the CPU, and Sinclair presumably implemented it like this to avoid dedicating a valuable ULA pin to the job of controlling the CPU WAIT signal. With some pointers from Philip Kendall I've sorted out the video byte fetch to match that of the Spectrum, and should therefore match the Spectrum's memory contention behaviour. This contention is also described in the comp.sys.sinclair FAQ. I will generate a proper CPU WAIT when there is contention for the lower 16K of memory instead of interrupting the CPU clock.

The comp.sys.sinclair FAQ describes a Z80 wait stage that lasts for six out of eight T-states whilst the ULA is accessing the video memory. Theoretically the Harlequin only requires the Z80 to wait for four T-states in every eight, but as we're trying to be true to the ZX Spectrum behaviour, a six T-State wait it needs to be!

The following diagram pulls together the various signals used by the video generation memory access, taking into account the details described in comp.sys.sinclair FAQ, plus the required VMVen and WAIT.

T1  T2  T3  T4  T5  T6  T7  T8 

Memory contention between the video and the Z80 only occurs whilst the main display rectangle is being drawn, i.e. when HC8 is low and VBorder is low. If the CPU is at this time accessing the lower 16K of memory (RAM16 low) which also contains the video buffer, then we must halt the CPU.

From the diagram above we can see that when the CPU is accessing the lower 16K (RAM16 low) and the display needs updating (HC8 and VBorder low) then:

WAIT = RAM16 + HC8 + VBorder + (HC2 • HC3)

The combination of HC2 • HC3 used to create the expected 6 T-state long WAIT signal. HC8 + VBorder + HC2 is currently produced as part of OutLatch and HC8 + VBorder + HC3 is produced as VMVen.

Rearranging OutLatch as OutLatch = VLE + VLS, where VLE = HC8 + VBorder + HC2, and VLS = (HC0 • HC1) + CLK7.

This gives us VLE, which is active during the display rectangle when HC2 is low. Therefore WAIT = RAM16 + (VLEVMVen)

The Z80 Data Bus is decoupled from the video data bus in the ZX Spectrum through resistors. This is an extremely simple idea, very effective and cheap. It also leads to the floating bus behaviour seen in a true Spectrum, something that would be difficult to replicate without knowing whether an address has been decoded by and I/O device or not. The resistor takes up the potential difference between both sides of the bus if they are both under load so that the true respective values are seen. If one side of the bus is not under load, then it will track the voltage on the other side. Which is why when you read from an un-decoded port, the data bus goes into read mode, nothing loads the CPU side of the bus, and so it 'sees' whatever is on the video side of the bus.

The schematic modification to implement the CPU interface are shown in schematics version 1.6.