I've been investigating the contended port issue discovered during the Sidewize test over the last few days, trying to match up the Z80 timing diagrams and the documented contention behaviour in the comp.sys.sinclair FAQ. So far I can't quite make them fit together.
I realised that using the Z80 WAIT signal to control memory and port contention is inappropriate as you can only introduce a single wait stage, and thus a single contended T-state per instruction. The FAQ clearly states that under certain circumstances all T-states may be contended. This is a direct consequence of Sinclair instead holding the CPU clock high to pause the Z80. If I am to have any chance of replicating the contention demonstrated by a ZX Spectrum the same method of pausing the Z80 must be used.
This is very easy to achieve by OR'ing CLK3.5 and an inverted WAIT and using that to drive the Z80 CLK. The CPU
The FAQ states that any ULA contention for an IO instruction occurs during T2, and if the port high-byte is 0x40 to 0x7F then contention occurs at T1 and T2. In addition, if the port is undecoded (0xFF for instance) then there is no contention unless the port high-byte is between 0x40 and 0x7F, in which case contention occurs at T1, T2, T3 and T4.
These Z80 IO T-states are labled T1, T2, TW and T3 so to avoid confusion I'll refer to them as 1st, 2nd, 3rd and 4th.
The contention timing puzzles me as the actual IO read (for instance) takes place during the 4th T-state, so why is it the 2nd that is documented as being contended? If the 2nd T-state is contended it will be delayed until fist uncontended cycle, followed by the 3rd and then the all important 4th T-state (T3). This 4th T-state would find itself back at a contended cycle (see diagram below showing the two possible execution cycles for T-state T3). To have the 4th T-state execute during ULA cycle 7 or 8 (which are uncontended) it would have to be contended.
Things at present do not add up.
If we consider ULA port access (A0 low, A8 - 15 outside 0x40 - 0x7F) the current Harlequin design applies contention to the whole active IO period of the IO instruction, namely across the 2nd to 4th T-states. According to the FAQ this is too much.
Furthermore, reading the floating bus with a port address high byte between 0x40 and 0x7F does not work with the Harlequin. You consistently read 0xFF, and since A8 - 15 have been added to the IO decoding, every T-state becomes contended whether or not we're reading the ULA port or floating bus. Again at odds with the FAQ.
So there are two problems that need to be addressed:
- Port contention is currently aggressive and excessive
- The floating bus does not work with a high byte between 0x40 and 0x7F
I need to think about how the ULA divides it's time between accessing the video memory and handling it's IO port, and have a look at ZX Spectrum's floating bus behaviour with a high byte between 0x40 and 0x7F.