Wow, I thought I knew the C128 pretty well. I still needed to search the internet for several hours to answer this.
Why does the Commodore C128 perform poorly when running CP/M?
- The Z80A was sort of an after-thought in the C128 design. Before release it had been touted as "fully C64 compatible" (which the earlier C= Plus/4 was not). However, the C64 had a Z80 cartridge allowing it to run CP/M. For whatever reason the cartridge could not work on the C128, so they added the Z80 directly to the motherboard. (see reference #3 below)
- I/O was doubly indirect. Actions such as reading from the keyboard and writing to the screen first went thru the CP/M BIOS layer. Then it had to switch CPUs! From the Commodore 128 Programmer's Reference Guide (PRG), page 500:
The 8502 is responsible for most of the low-level I/O functions. The request for these functions is made through a set of mailboxes. Once the mailboxes are set up, the Z80 shuts down and the 8502 starts up (BIOS85). The 8502 looks at the command in the mailbox and performs the required task, sets the command status and shuts down. The Z80 is re-enabled; it then looks at the command status and takes the appropriate actions. 3. Updates to the screen were s-l-o-w. I believe this was due to the impact #2 had on interacting with the 8563 video controller. Although a block mode character transfer was possible, apparently the complexity of the dual-BIOS layers led to only one character being written to the screen per BIOS call. To write a character, two 16-bit 8563 registers needed to be updated, which were the hardware pipeline to the 80-column video memory. That all amounts to a heckuva lot of overhead per character. 4. Some users only had the classic C64 model 1541 disk drive. This was already known for being very slow (to be fair, "faster than cassette"). The newer 1571 drive, released with the C128, was three to six times faster, had double the capacity, and supported several CP/M formats used by other manufacturers.
Z80A only runs at 4MHz half the time. Why should this be the case?
From page 575 of the PRG:
SYSTEM DESCRIPTION
The Z80A, a 4MHz version of Zilog's standard Z80 processor, is included as an alternate processor in the C128 system. This allows the C128 to run the CPM 3.0 operating system at an effective speed of 2 MHz. The Z80 is interfaced to the 8502 bus interface and can access all the devices that the Z80 can access. The bus interface for the Z80 (the most complex part of the Z80 implementation) is described in this section, along with Z80's operation as a coprocessor in the C128 system.
BUS INTERFACE
Because a Z80 bus cycle is much different than a 65xx family bus cycle, a certain amount of interfacing is required for a Z80 to control a 65xx-type bus. Since the Z80 has built-in bus arbitration control lines, it is possible to isolate the Z80 by tri-stating its address lines. Thus, both the Z80 and the 8502 share common address lines.
The interfacing of the data lines is more complex. Because of the shared nature of the bus during Z80 mode, the Z80 must be isolated from the bus during AEC low. Thus, a tri-statable buffer must drive the processor bus during Z80 data writes. The reverse situation occurs during a Z80 read—the Z80 must not read things that are going on during AEC low; it must latch the data that was present during AEC high. Thus, a transparent latch drives the data input to the Z80. It is gated by the Z80 read-enable output, and latched when the 1 MHz clock is low. It will be seen that the Z80 actually runs during AEC low, but that the data bus interfaces with it only during AEC high.
The AEC is a signal pin of the C128's memory management unit. It is defined on pages 584 and 585 of the PRG:
AEC: Address Enable Control. Indicates whether the 8502 processor or the VIC has access to the shared bus. When low, VIC or an external DMA has the bus and VA16 have the processor bus, and no pointer or BIOS translation takes place. This signal occupies pin 16.
I.e., "It's complicated." 🤖
Performance benchmarks?
I was not able to find actual benchmarks. There were several accounts of users lamenting the C128's CP/M speed, and especially its screen updating. IMO the perception of this mode being slow was much more to do with the I/O bottleneck than the effective 2 MHz clock rate of the Z80A. There had been lots of actual 2 MHz CP/M machines using the original Z80; there were few complaints about their speed. But imagine using a CP/M spreadsheet and actually observing o n e c h a r a c t e r a t a t i m e being written to the screen. It'd be kind of annoying.
(Replies to BrianH's comments to this answer)
Is the Z80A slowdown due to something like "wait states"?
"Something like", yes. Wait states are to allow a CPU running faster than memory to still operate, especially in an era before on board CPU caches alleviated the need for the CPU to access memory every (other) cycle. The problem on the C128 is that a 6502 CPU expects different things from its bus than a Z80 does. In the modern era, you can think of different motherboards being needed for different (yet current) '86 family CPUs. They can't just run the motherboards at slower and faster speeds and expect everything to work. Bil Herd and his team had to work with one motherboard and two entirely different CPUs.
Couldn't they have just optimized the CP/M BIOS?
Yes, there was nothing in the hardware which would have prevented that. However, the engineers were on a tight schedule and implementing something that wasn't even part of the original C128 design. They would have had to rewrite the 6502 BIOS code in Z80 assembly language, and they were primarily hardware guys. I'm sure Commodore didn't want to spend extra money to optimize a feature they hadn't even asked for. So the simplest, most reliable route was to make calls to the already working and well-tested 6502 BIOS.
Cool References
- THE REAL STORY OF HACKING TOGETHER THE C128 by Bil Herd, lead designer
- The C128 Story, aggregated CompuServe postings by Bil Herd
- HEY EVERYONE! IT'S BIL HERD!!! :) 1998 usenet post to comp.sys.cbm
Bil describes why, how, and problems in adding a Z80 to the C128 - CP/M-cartridge for the C64 a README by Ruud Baltissen
detailed explanation of interfacing a Z80 to a 6502 bus - Platform Wiki: Commodore 128