The ZX Spectrum Reverse Engineering and Clone Desgin Blog

Harlequin

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

< 44 of 68 >

ULA Tricks

Jul 8, 2007

The ULA handles its IO port separately to the video, however it has a single data bus and so cannot perform both tasks at the same time.

An analysis of the floating bus values returned whilst reading port 0x40FF reveals that the second attribute byte fetch only is returned - the timing of which are altered due to the applied contention.

The know facts are:

  1. Ports with A0 low are contended at the 2nd T-state
  2. Ports with A0 high are uncontended
  3. Ports with a high byte between 0x40 and 0x7F comply with 1 and 2 above, but are additionally contended:
    1. If A0 is low, the 1st T-state is also contended, 3rd and 4th are not.
    2. If A0 is high, all T-states are contended
  4. Reading the floating bus with a port address high byte between 0x40 and 0x7F returns the second attribute byte of fetched pair (the last byte fetched), or 0xFF.

If the contention applied at (3) is the same as that applied during memory contention, then the IO read of the floating bus will be contended during the video memory fetch. The IO read can therefore only execute during the two uncontended cycles following the video byte fetch. This suggests that the last video byte fetched from memory might 'hang about' on the video/ULA bus during these uncontended cycles.

The difference between 3.i and 3.ii above implies that the contention that would be applied at T-states 1 to 4 (3.ii) due to the port high byte being in the range 0x40 - 0x7F, is cancelled after the 2nd T-state according to the rules of 3.i. Almost as if the appearance of a ULA IO port request at T2 stops the ULA being confused by what it thinks is a contended memory access at T1.

Reverse Engineering

A look at the Z80 timing for an IO read/write is important at this stage:


T1  T2  TW  T3 
CLK                
 
IORQ                
 
D0-7             X  
 
RD/WR                
 

Data is read and written during state T3 as the WR or RD line goes high. We're interested in the read, which occurs some time before the falling edge of the clock during T3 (X above). The data sheet specifies this is a minimum of 50ns.

To produce a single T-state low pulse that starts and ends at T2 (3.i above), we can use CLK to delay IORQ by one T state through a D-type flip-flop, and combine the real and the delayed-inverted IORQ with an OR:

Where ULAIO = IORQ + A0.

This single T-state ULA port Wait-Request signal would be NOR'ed with the active low WAIT signal and the OR'ed with the CLK. The Wait-Request going low at the same time as WAIT is low would force the CLK output high, thus 'pausing' the Z80.

The ULA has very few CPU signals fed to it from which to perform timing tricks, so it is highly likely that the above method of generating an active low signal during T2 is used.

The fact that T2 alone is contended for ULA port access means that T3 will execute at a predictable cycle, and can be shown to be either cycle 1 or cycle 2 depending on whether T2 was delayed until cycle 7 or naturally landed at cycle 8 (the two uncontended ULA cycles). See the IO state diagram.

This again highlights the problem that the ULA cannot accept the IO T3 during cycle 1 or 2 as it is currently reading video data from memory. Furthermore, for contended floating bus access (4 above) to be satisfied, IO T-state T3 must align with the ULA's second attribute byte fetch (cycle 5), unless the data 'lingers' on the bus - which I highly doubt.

These conflicts lead me to speculate that the contention timing used during IO is not the same as that used during memory access, because if it were, it would never allow T3 to execute at the correct ULA cycles.

Port high-byte contention (0x40 to 0x7F) may be introduced by combining A14 + A15 with the ULAIO select (active during T2 TW and T3) which would apply contention across all four IO instruction T-states (the address bus is stable across all four). If this 'memory contention' signal is combined with ULAIO before it is combined with ULAIO-T2 then the assertion of ULAIO during T2 will cause all contention to be cancelled at TW and T3!

This extremely simple circuit satisfies the contention requirements of each T-state depending on the combination of A14 + A15 and ULAIO.

The timing of the IO contention being potentially different to that of the memory contention still needs to be addressed if we're to get near to a working model of the ULA operation.