The ZX Spectrum Reverse Engineering and Clone Desgin Blog

Harlequin

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

< 31 of 68 >

Wait Too Late

May 28, 2007

To help track down the timing problem, it would be useful if the Z80s control lines changed in a regular and predictable way. When booting from ROM they do not, and might as well be random.

To do this, I wrote two small snippets of assembler and loaded them into ERPOM at address 0000 (rst) and 0066 (NMI). The code at 0000 repeatedly writes 0AAh to address 16384, the NMI code at 0066 repeatedly reads from address 16384.

; Cyclic memory read/write exerciser
; (c) 2007 netFluid Technology Limited - Chris Smith
; 
        ORG   0000

contention_start:
        DI

        LD    HL,16384
load_byte:
        LD    (HL),0AAh
        JR    load_byte

        DEFS  0066h - $

read_byte_nmi:
        DI

        LD    HL,16384
read_byte:
        LD    A,(HL)
        JR    read_byte
            

Booting from this EPROM puts the Z80 into a repeated write loop (except of course the two instruction fetches within the load_byte loop). This gives a predictable MREQ and WR sequence that we can monitor with the oscilloscope. The other signals of interest are WAIT and VMVen.

WAIT and VMVen as they stand are not very regular as they change when the electron beam is in the border or not. To avoid this I held the output of HC8 + VBorder low so that contention always occurs.

I already suspected that the switch from video access read mode to CPU access write mode was causing a problem, so I concentrated on the RAM CE, WR and RD lines.

Resetting the Z80 put the CPU into my write test. It was clear that if the WR line went low before the video demanded access, it was held low for an extra 6 T-states by the WAIT whilst the video circuit read two bytes.

In the last 2 T-states of the WAIT the video circuit hands control of the lower 16K RAM back to the CPU. There is ample time during this held write state for the address and data bus to stabilise as far as the RAM is concerned, and so I doubt the problem is here.

This got me thinking about the start of the wait period. The WAIT line is asserted at the same time the video circuit takes control of the buses and presents it's own address to the lower 16K RAM. It took me quite a while to realise that if the Z80 does not acknowledge this wait request immediately (which is doubtful) then write operations will be lost, since we take the RAM away from it before it's finished the write it was doing.

During a read or write operation, the Z80 samples the WAIT line during T-State T2 of the instruction. If low, it inserts a wait state and samples agin. During state T3, the data bus is read or written. To avoid problems I should allow one T-state after asserting the WAIT line before taking control of the RAM to give the Z80 sufficent time to sample WAIT, or to complete the memory write cycle T3 (if that was what it was doing) and the write to succeed.

It sounds quite straight forward and obvious now, but it took me a while to see the light!


The Floating Bus

During this analysis I noticed that the floating bus behaviour would not work properly. If the CPU is to see both bytes of the display and attribute pair on the bus, I need to make sure that a byte is read by the video circuit during a single CPU T-state, so that if Z80 I/O fetch cycle T3 occurs during this time, it will fetch the byte being read by the video circuit.

In redesigning the video fetch, the byte fetches are 1 T-state wide.

The revised timing diagram is as follows:


T1  T2  T3  T4  T5  T6  T7  T8 
HC0                                
 
HC1                                
 
HC2                                
 
HC3                                
 
CLK3.5                                
 
AL1                                
 
AL2                                
 
OL                                
 
VMVen                                
 
WAIT                                
 

The important point is that the first byte fetch (AL1) does not occur for one T-state after WAIT has been asserted, and WAIT is removed one T-state after the video memory has finished access. OL is now much later, so HC8 needs to be delayed a little longer.

Designing the logic to implement AL1, AL2 etc is quite complicated and is much easier to derive from a 3 to 8 line decoder driven from HC1 .. 3. I would rather have used standard gates as I was trying to implement as much as possible using basic gates to make conversion to an FPGA easier.... I'll worry about that later!

Anyway, implementing the above timing diagram was quite quick, replacing and rewiring three logic chips. However things were not quite perfect when switching on - there were occasionally stray pixels on the screen, and the odd reset.

There was obviously still a write problem. Analysing the WR line at the RAM chip, I could see that there was a very short and unexpected pulse when the address bus buffers switched between video and CPU access. Since WR is also buffered, the pulse was clearly due to the buffers propagation delay. A stray WR pulse is destructive, so I instead buffered RD and CS instead of RD and WR, choosing to force WR high whenever the video system has access to the memory with a simple OR gate.

Turning on immediately gave the welcome screen, and the sporadic problems went away!!!

Now before anything useful can be done, I need a keyboard interface and an interrupt generator....

The revisions to the CPU and memory interface timings are shown in schematics version 1.7.