With a simple daughterboard, a GAL and a 74LS74 the Z80 in C128 can run at dot clock frequency (almost 8MHz) during its 1MHz phase of the system clock, effectively doubling the speed.
Basic benchmarks written in Turbo Pascal show that indeed it is about twice faster.
CP/M is visibly snappier, to the point of being usable.
This circuit works fine, during tests it was stable for several hours, busy with drawing fractals and calculating pi using Monte-Carlo method.
The CPU doesn't even get warm to the touch.
Disk I/O worked fine, as well as REU ram disk M: (emulated by UII+).
There was a message sent to cbm-hackers mailing list back in 2002, about a PCB for C128 that boosted Z80 to 8MHz
Unfortunately that device was not fully reverse-engineered and it seems that it was never mentioned again.
I haven't tried to overclock the original Z80B, I replaced it some time ago by a Z84C0020 that uses much less energy.
Following modifications can be done using bending Z80 pins and soldering to the mainboard, but having a daughterboard will keep everything tidy.
We need to isolate /WAIT
input (pin 24) from the mainboard because it is tied to /NMI
there.
We will also disconnected from CPU the onboard CLK
signal (pin 6).
We will use it as an GAL input to have an option of switching between the original clock (4MHz half of the time) and fast mode (8MHz half of the time).
The new CLK
will be generated by GAL. We will also use few other lines from Z80 as GAL inputs.
We need two more inputs not available on the Z80 chip/Z80 socket. We want DOT CLOCK
from the expansion port (pin 6) and the system clock 1MHz signal
(called CLK1MHZ
below and D1MHZ
on C128 schematic) from U12 (pin 11).
This is the normal timing of Z80 in C128. During system clock CLK1MHZ
low phase VIC generates two clock ticks for Z80.
We use DOT CLOCK
instead to pass four ticks.
Whenever Z80 wants to read or write to/from memory or I/O we have to stop it until the start of the next 1MHz clock low phase.
Latched data from 74LS74 will hold the /WAIT
line low until the end of current low phase of CLK1MHZ
(Z80 turn).
During the following high phase (VIC's turn) 74LS74 will stay in reset and release WAIT
to inactive (high) state, but during that time we will not output any clock pulses - just like in the original setup.
I had to add a case to CLKOUT
to handle memory writes. Waitstate is not enough, the CPU had to be really stopped. That's not an issue for reads because they are buffered with U12.
Here is the effect - for two clocks Z80 was running at 1MHz because of memory write, then picked up the speed:
The Kicad project is in z80-dotclock-gal-and-latch/ folder.
Schematic is also available in PDF form.
Gerber files generated from PCB project that should fit C128 and C128DCR are in z80-dotclock-gal-and-latch/gerbers/ folder.
PLD file to be used with WinCUPL to generate JED file for programming GAL is available in two versions:
- for 22V10 in z80-dotclock-gal-and-latch/gal22v10/
- for 16V8 in z80-dotclock-gal-and-latch/gal6v8/
You will find there all the files generated by WinCUPL, including JED files ready to be flased to GAL devices.
They differ only in pin numbering. I just happened to have an ATF22V10 available. The circuit is ready for both options - just align pin 1 of 16V8 with pin 1 of the socket.
Here is how it is supposed to work:
- VIC cycle phase starts, latch is in reset state, latch
D
input is constant1
, but due to resetQ
output is0
and negated/Q
is1
; Z80 is stopped as its clock is held low - Z80 cycle phase starts, latch goes out of reset,
/Q
output =WAITLATCH
stays1
, Z80 sees that as inactive/WAIT
and runs program, without bus access requestsWAITTRIGGER
stays0
- Z80 wants to access the bus,
WAITTRIGGER
will rise to1
; this will cause latch D input connected to1
be copied to the output, so/Q
becomes0
, Z80 will see that as an active/WAIT
and will stop - This state will not change until start of the next VIC cycle phase
- VIC cycle phase starts, latch is reset - will set latch
/Q
to1
, so for Z80/WAIT
now becomes inactive; but Z80 won't run because during this half-cycle the CPU clock signal is not passed at all - Z80 cycle phase starts, it can continue because after latch reset
/WAIT
is inactive; Z80 accesses the bus soWAITTRIGGER
stays1
until Z80 gets what it needs from data lines - then it goes back to0
The idea of this is that during Z80 clock phase when WAITTRIGGER
goes from
inactive to active we can latch /WAIT
line low to hold it low at least
until the start of VIC cycle.
We release it before/at the start of Z80 cycle it so /WAIT
becomes high
again - even though the latch trigger condition is still active.
The simplest way to handle CLKOUT I did was:
CLKOUT = !DOTCLK & !CLK1MHZ;
C128 started fine, but CP/M wouldn't boot. With pieces of Z80 code I was able to confirm that this works for I/O reads and writes but only memory reads.
Memory writes never happened, hence the special case with MREQ & WR
.
I tried using the 16MHz clock signal from VDC (pin 2) instead of dot clock.
The C128 will start, but CP/M won't boot. However all my simple tests passed. I suspect that memory access is still an issue and some of the memory writes fail.
A possible reason is that exact 16MHz signal is not a true double of the dot clock, which depends on PAL/NTSC.
If this is the reason, then one solution could be to double the dot clock using MAX9010. This idea was already mentioned on Reddit, but that circuit would never work. But it depends on specific values of R/C pair.
Using a part specifically designed for that purpose - ICS501 seems more reliable.
-
How can we keep it simple but run at full speed all the time and stall only on memory and I/O access? It's not clear if the device mentioned in 2002 was really working at 8MHz all the time or just during Z80's turn.
-
Since I already used ATF22V10 which has asynchronous reset for registers, it should be possible to get rid of 74LS74 with different assignment of pin 1 (GAL CLK input). I wasn't able to do it. I'm probably doing something wrong - messing up high/low vs active/inactive or my CUPL skills are lacking (they are).
-
What is the problem with 16MHz clock? Maybe it should be created by doubling the dot clock to keep things in sync? (No need to bother if (1) is possible).