CP/M 3.0 for the PC
What is it?
In short it is an ISA card with a small Z80 based system that should be capable of running CP/M 3.0 and is fitted inside a PC that has at least one free ISA slot.State: Schematics, board and PCB.
Background
Although I don't run it very often, I have a soft spot for CP/M. I have seen various Z80 cards that enable a Commodore 64 to run CP/M. I even re-engineered the original "CP/M Cartridge" for the Commodore 64. And now I decided to create one for the PC.IMHO an emulator can run CP/M at least hundred times faster than a real CP/M system so why am I doing it? Mainly just for the fun that I can do it at all.
The hardware: the ISA bus
Why ISA and not PCI (-Express)? The answer is very simple: I have no idea how to do it for the PCI bus. It will most probably need a FPGA or another fancy IC and these are out of my scope. So ISA it is.The hardware: the interface towards the PC
The Z80 has to communicate with the PC in one or another way. I had no doubt about how to do that: two 8255s, one for the Z80 part (IC3), the other for the PC (IC2).Why the 8255? It has two 8-bits ports that can be used for exchanging data, one for incoming and one for outgoing data. And it has two 4-bits ports for control lines. The 8255 does not have an INT/IRQ output and I don't know if this could be a disadvantage. Just in case I provided one my self, see later.
Selecting the 8255 for the PC is done by using a 74LS688, an 8-bits comparator. Dip switch block SW1 enables the user to select an address ranging from 000h to 3FCh in steps of four bytes. AEN, IOWR and IORD make sure that the 8255 is only selected when I/O is addressed.
Remark: it is up to the user to select an address range not occupied by any other hardware. A possible advise: 320h.
The hardware: test facility and interrupt
I needed something to test the card and debug the software in some way. So I added four LEDs and their drivers to port C: two for the lower port and two for the upper port. So once the direction of each half port is set, I always have two LEDs at the disposal of both the PC and Z80 system.When writing this page I thought about providing an interrupt of my own to both systems. Having four control lines, which is rather much, why not using one for each of the interrupts?
I cannot use the output of one of the pins directly directly towards the PC because after a reset it acts as an input. Because an open input is seen by another input as (H) and the INT inputs are triggered by an (H), I decided to tap the signal from the output of one of the inverters. Jumper J1 enables the user to disable this feature.
Another pin of port C provides a mean to trigger the INT input of the Z80. J2 can disable this feature.
The hardware: the Z80 part
What do we need? A Z80, ROM, RAM, the mentioned 8255, glue logic and.... whatever. This should become a CP/M system which means that the system will start up from ROM but this ROM must be replaced by RAM in time. But we go a step further: this is going to be a "banked CP/M 3.0" capable system and that means that, except replacing ROM with RAM, a banked system can also swap a part of the lower memory for other memory.There must always be a common part present and that part must always be at the top of the memory. The size of the common part must be at least 8 KB so the swappable lower part of the memory can be as big as 56 KB. The "System Guide" for CP/M Plus even mentions 4 KB but that depends on the part of the BIOS that MUST be kept in the common part. Each swappable part is called a bank. CM/M 3.0 can support up to 16 banks.
To make a very long story short, I created four versions and then Doug Miller gave me a hint to simplify the last version. The first version was able to use banks of 32 KB, just like my Bondwell 14. Version two used a GAL instead of various TTL ICs.
Douglas also told me, before I read the System Guide, that the more elaborate systems have only a 8 KB common area and this gave me the idea to use the 74LS612 Memory Management Unit (MMU) resulting in version 3.
The MMU enables us to address up to 16 MB of memory. But that is if we make use of all the twelve bit of its internal registers. Unfortunately the Z80 is an 8-bitter. Unless we throw in an extra circuit, we can only address 8 bits of these internal registers. This means that we can "only" address up to 1 MB of memory but IMHO that is more than enough.
More elaborate machines are capable of reading data from one bank and writing it to anther bank in one move. I solved this by adding another MMU. Then Doug pointed me to the fact that I could use a so far unused input of the MMU to perform the same functionality with just one MMU. Many thanks, Doug!
Version 1
The schematic:
IC7, a 74LS174 6-bit latch, is the heart of it all. A reset of the system will clear it as well and all of its outputs will become (L). The moment the Z80 outputs an address from 0000h to 7FFFh, the output of IC11A, a 74LS32 2-input OR gate becomes (L). An inverter, IC9C, a 74LS04N, will pull input 9 of IC11C (H) which causes its output and thus the CS input of the RAM, IC8, to become (H) as well. In other words, from 0000h to 7FFFh only the ROM will be accessed at this moment.
IC11C and IC11D also make sure that either the ROM or the RAM are enable during an active MEMR = MEMory Request.
The RAM, IC8, is 512 KB big. That makes 15 banks 32 KB plus 32 KB of common RAM. Four bits of the 174 latch generate the address lines A15..18, called MA15..18 in the schematic. We also want to be sure we have a common memory. That means, whatever bank has been selected, the moment the Z80 addresses the range 8000h to 0FFFFh, one specific bank of 32 KB has to be selected. This is done by placing four AND gates between the outputs of the 174 latch and the inputs A15..18 of the RAM. One input of each AND gate has been connected to the inverted address line A15 of the Z80, using inverter IC9B. The moment address line A15 becomes (H), the outputs of the AND gates and therefore the address lines MA15..18 become (L).
OK, that means That bank 0 becomes the common RAM. But then what about the range 0000h..7FFFh? In fact that doesn't matter, we can take every other bank. The moment we disable the ROM, that bank has to be selected as well. If we don't, we will not only end up with bank 0 in the range 8000h..0FFFFh, but also in the range 0000h..7FFFh. There is nothing wrong with using RAM bank 1 in the range 0000h..7FFFh as the CP/M starting bank, RAM bank 2 as CP/M bank 1, etc., etc. In other words, it is just a matter of software.
The 174 latch is, like the 8255, addressed using I/O calls. Because we need two address lines to address the registers of the 8255, we will use address line A2 to select between the 8255 and the 174 latch. Not using the other addresses means we will have a lot of mirrors but that shouldn't be a problem IMHO.
We'll use a 74LS139 2-to-4 demultiplexer to select the I/O parts. IORQ is used to enable the 139, A2 to activate either output Y3 for the 8255 or output Y0 for the 174 latch. The second half of the 139 is used as a kind of OR gate to combine Yo of the first half with the WR = WRite signal. So the 174 will only be activated when it is written. An accidental read then won't do any harm.
The board
The first idea was NOT to create a PCB of this schematic but then I changed my mind: I wanted to proof myself that this also could work only using basic ICs, i.e. not using a GAL. If things work out fine, the next step can be building version 2 or better.
Version 2
This is, more or less, the GAL version of version 1. "More or less" because some parts have been added, deleted or moved and to optimize the way the lines go, for example the order of how the data bus bits have been connected to the 174 latch, has been changed:
And the board:
Version 3
This is the first version with the 74LS612 MMU. From this version the GAL version has been derived. But the schematic you see:
is in fact the IC version of the GAL version; it explains how the GAL will be programmed.
How does it work
The reset signal of the ISA bus is active (H), just what the 8255 needs. The Z80 OTOH needs an active (L) signal and we create that by inverting it by using IC12A.The heart of the system is the 74LS612. The MMU has two working modes: the "pass mode" and the "mapping mode". In the "pass mode" outputs MO8..11 reflect the input levels of MA0..3. In "mapping mode" the outputs MO0..11 reflect the status of 16 internal registers. But after a power-up the state of these registers is completely unknown so we first have to make sure that after a reset the MMU is in "pass mode". The level of input MM (Map Mode) determines in what mode the 612 is put. (H) means "pass mode", (L) means "mapping mode". Unfortunately the 74LS612 does not have something like an internal reset and that means that some external hardware has to set the level of MM to "pass mode" after a reset.
This external hardware is a flip-flop made out of two 3-input NAND gates, IC9A and IC9C. What at this moment is important to know is that the flip-flop is resetted by feeding the inverted reset signal of the ISA bus that is mentioned above, to one of the inputs of IC9C. The output of IC9C, (H) after a reset, is fed to the MM input and so the MMU is put into "pass mode".
In "pass mode" the outputs MO8..11 just output the signals that are presented at the inputs MA0..3. In this case these inputs are address lines A12..15 but in reverse order. The other MO lines output a (L) in "pass mode". This means that in "pass mode" the Z80 will address 64 KB of ROM.
Within the 1 MB range, the first 512 KB are occupied by ROM and the other 512 KB by RAM. But the BIOS has to be written to RAM so how can we do that? There are two options:
- Set up the mapping system as soon as possible.
- "Write through", that is: reading from an address where the ROM is situated will read the value of that address from the ROM. But writing to an address where the ROM is situated will write the value to the RAM.
First: IC4B, a 74LS139, is used for selecting either the ROM or the RAM. It is enabled by the MREQ signal. An AND of WR and RD makes sure that outputs Y0 and Y1 are only activated during a read or write. An OR gate, IC13A, combines MA19 and an inverted WR. When the ROM is read, WR is (H) and thus input 1 of the OR gate will be (L). The output will then follow MA19 and thus the ROM will be read indeed. But during a WR, input 1 will be (H) and this means that the 139 will select the RAM.
Note: why combining RD and WR? This is due to some differences in timing I found in the data sheets of the Z80. Checking the schematics of the Bondwell, I found that that was done here as well.
But I haven't explained the I/O area yet. IC4A, the 74LS139 2-to-4 demultiplexer, creates four mini ranges of sixteen bytes each. The first mini range is used to program the internal bank registers of the MMU.
Addressing the second range enables the "mapping mode" of the MMU and the addressing the third range disables it i.e. puts the MMU in "pass mode" again.
The fourth range is for handling the 8255.
I think it should be clear by now: all this glue-logic is put into one 16V8 GAL.
How to program the MMU
The 74LS612 has 16 registers of 12 bits each where each of these bits represents a MOxx output. We will use all 16 registers but, as said before, only 8 bits of each register: that's why inputs D0..D3 are tied to +5V. The resistor is needed because these pins can also serve as outputs. What should we know first:- A register is selected by the address line A0..3 and data is written into it by an I/O write operation.
- When programming the registers, the content of each bit determines the state of one of the MOxx outputs: data bit D0 does this for MA12, D1 for MA13 etc., etc. up to D7 for MA19.
- When in "mapping mode", the inputs MA0..3 determine what 12-bit register is chosen. In this case the address lines A12..15 determine what bank is chosen.
After the ROM has done its job, CP/M likes to see only RAM in the whole range and that means that in "mapping mode" MA19 should output a (H) all the time. So what ever else we are going to write to the registers, bit D7 should be "1".
But before we select any RAM at all we have to decide how much common RAM we want because that number also decides the size of each bank. A common bank of 16 KB means we can have 10 banks of 48 KB and we have a left-over of 16 KB that can be used as a temporary storage or scratch pad. A common bank of 8 KB means we can have 9 banks of 56 KB but no left-over. For the moment we choose for this configuration.
The 8 KB of common RAM starts at $E000 and we must be sure that it stays here what ever bank we choose. The range 0E000h..0FFFFh is covered by the registers 7 and 14. Again, what we write to bit D0 will be outputted to MA12, bit D1 is for MA13, etc., etc. up to bit 7 for MA19. There is nothing against selecting the very first 8 KB of the RAM for our common RAM. This means register 14 will become $00 and register 15 will be come $01. And simply by not writing to these two registers anymore, the common RAM will stay here as long as the computer is working. By filling register 0 up to 13 with the values $02..$0F the next 56 KB of RAM is chosen as Bank 0 of the system. For Bank 1 we fill the register 0 up to 13 with the values $10..1D. For Bank 2 with $1E..2B, etc., etc., etc.
When choosing for 16/48 system, we fill the register 12..15 with the values $00..$03 and the registers 0..11 with the values $04..$0F. For Bank 1 we fill the register 0 up to 13 with the values $10..1B. For Bank 2 with $1C..29, etc., etc., etc.
Version 4
This is the GAL version:
And its board:
The software
You can email me here.