Close

Turbo CPU

A project log for MSX COMPATIBLE BOARDS FOR RC2014

Create a series of boards designed for the RC2014 bus to achieve MSX/MSX2 compatiblity

dean-nethertonDean Netherton 02/19/2022 at 23:160 Comments

Another module I been developing in the MSX for RC2014 series is the Turbo CPU.  This is a module that run the Z80 at 20Mhz, instead of the stock 3.5Mhz speed.  

This module is not designed to a particular MSX standard - but I wanted to make my machine go a little faster, yet still be compatible (for the most part) with existing software/games.

The compatibility issue is not a simple one, when you increase the CPU speed, to 20Mhz, many other chips in the system will not be able to keep up.  And in particular the V9958 will have issues.  A lot of software written for the MSX, assumed its running on a Z80 at 3.5Mhz, and will have issues, typically with video, if this is not the case.

MSX did come out with a 'faster' machine - the Turbo MSX.  They solve the compatibility issue by having 2 CPUs.   A stock Z80 running at the 3.5Mhz for compatibility and a R800 (basically a custom Z80, similar to the Z180/280 etc, that ran much faster).  Unlike the Z80, the R800 is not available today - at least not as a recently manufactured brand new item 

The fastest Z80 this is available today, in DIP40 configuration is rated at 20Mhz (and can probably go faster).  A MSX running at 20Mhz - sounds super fast to this old-timer!

The typical way for the Z80 to manage access to devices with slower timing requirements, is by triggering hardware wait-states.  So when the Z80 requests an I/O operation - we need to hold Z80's wait line active for a small number of clock cycles - allowing the communication with other devices to be 'slowed'. 

The number of clocks cycles that need to be 'waited' is dependant on the speed of your Z80 and the timing requirements of the other chips.  For a generic designed circuit, suitable for a most situations, we need to wait for the slowest component in the system.

But we also need to consider more than just the timing requirements for IO communications.  The Video (V9958) is a processor also - and when it receives commands from the Z80 - it will take its own time to process those commands.  The chip has its own clock - and as such will work through it logic, at its own independant pace.  

So any software that assumes its running on a Z80 at 3.5Mhz, but is actually running at 20Mhz, will probably have severe video corruptions as it issues commands to the V9958 at a rate far too high.

We could solve this issue by waiting (pausing) the Z80 every time it communicates with the V9958 VDP, but we would need a fairly large counter to ensure good operation.  

Another approach to solving the timing requirements, is instead of using hardware wait states, we just simply 'slow' down the Z80's clock when it initiates any I/O operations.  That is, we switch the Z80 to the standard 3.5Mhz when it starts an I/O operation, and maintain that speed for a number of cycles, before resuming to full speed.  So from the view of the running software, its timing to the V9958 will be unchanged. When the Z80 goes on to do other operations - it will resume at full speed.  Well least that's the theory - there is only one sure way to find out if this works. 

 (The GR8BIT has published a similar mod for their system: http://kb.gr8bit.ru/KB0009/GR8BIT-KB0009-Accelerating-your-GR8BIT.html, so that gave me hope this would work)

Of course, a Z80, doesn't just communicate with I/O devices - it also accesses RAM and ROM, across the RC2014 bus.  Will the memory chips keep up?  Will the RC2014 bus operate at this speed ok?  On the surface, the timing for the ROM and RAM chips will not work at 20Mhz - but if we note that although the Z80 is running at 20Mhz, it will not access memory at this speed.  Again, only way to truly know, is to try it out.

So I came up with a design that has the following characteristics:

1. use a 3 way slider to 'switch' the operation mode into 1 of 3 modes:

2. When operating in Turbo Mode, the Z80 CPU and CLK1 and CLK2 bus lines are using independent clocks

3. Use PLDs to allow me to configure the number of wait states and clock slow down periods - so I can optimised the timing

4. I calculated that I would need to hold the clock slow for about 32 (@3.5Mhz) cycles upon any I/O operations to ensure that existing software will not overload the V9958

5. Use a socket for the 'Turbo' oscillator, so I can replace it with alternative values to allow for testing and experimentation

6. Have jumpers so I can select speeds for CLK1 and CLK2 bus lines as well as whether Turbo mode uses the onboard oscillator (20Mhz) or the 7Mhz clock

7. The RC2014 CLK1 and CLK2 are configurable as per the stock RC2104 dual clock module

8. Using higher speed CMOS chips were required to operate at the 20Mhz speed

9. Integrate a clock synchronising circuit, so that when we switch from 20Mhz to 3.5Mhz, that the phase differences are handled ensuring the Z80 always has a clean clock.  I stole the design from (http://www.6502.org/mini-projects/clock-switching/clock-switching.html)

Results:

Here is a video of the module in operation, in an MSX configured RC2014. 

One gotcha

I discovered there is one issue with running in Turbo CPU mode.  In particular the SIO/2 module would not work correctly and I could not get serial communication to work.

The Zilog SIO chip (and other Zilog chips), have a requirements that their supplied clock is the same as the CPU Clock.  When operating in Turbo Mode, the Z80 clock and the SIO/2 (driven by CLK1) are not in sync as per the SIO/2 requirement.

The SIO/2 needs the clocks in sync, to enable a feature where the SIO/2 chip will release it interrupt state automatically.  It does this, by inspecting, via the data bus, what instructions the Z80 is executing.  It's looking for when the Z80 has executed the RETI (Return from Interrupt) instruction.  When it see this, it can then release its interrupt state/latching flags.

If it doesn't see the RETI, then the interrupt triggering will not work correctly.  In the case of the SIO, you get one interrupt when data comes in, but there after, no more interrupts will be raised.

By running the Z80 clock asynchronously to the SIO/2, the SIO/2 is not able to inspect the Z80 operation.  The SIO/2 will not know the Z80 is running at a different clock, and so might even see what it thinks is a RETI - when in fact the Z80 did not finish its interrupt handling*.  

To get serial communication working, in Turbo mode, I found 2 solutions that can be used:

1. Prior to executing the RETI, we perform an I/O instruction, thus dropping the Z80 down to 3.5Mhz in sync with the SIO/2.

2. Explicitly tell the SIO to unlatch it interrupt flag.

For MSX, I had to go with the 2nd option in my serial driver.  Option 1 would be difficult to implement, as it requires changing the interrupt handling code in the system BIOS.   As the MSX standard does not use any Zilog I/O I think this is fine.

For RomWBW, I will need to submit a patch to Wayne Warthen, that integrates the changes as per option 1.

* My experiments to date, using option 1 and 2, have produced very reliable and fast serial communications - so I gather that this is not really a problem.

I will add this module to my Tindie store soon.  The GitHub site for it is on my dev branch at: https://github.com/vipoo/yellow-msx-series-for-rc2014/tree/dev/turbo-cpu

Discussions