Retro CP/M (with the CX16 and otherwise)

Feel free to talk about any other retro stuff here including Commodore, Sinclair, Atari, Amstrad, Apple... the list goes on!
Post Reply
BruceMcF
Posts: 1336
Joined: Fri Jul 03, 2020 4:27 am

Retro CP/M (with the CX16 and otherwise)

Post by BruceMcF »


One thing that has popped up now and again on the Facebook group discussion is the topic of CP/M on the CX16.

Since this is one of the things I am interested in, I thought I would start the thread over here. For one thing, that avoids the question of whether a post is "about CP/M or about CP/M on the CX16", and for another thing, in some versions of "CP/M on the CX16" it is not really very much ON the CX16, and it's better to keep this thread together.

(1) Approach One: Use the CX16 as a terminal for a CP/M system.

Many current retro CP/M projects are oriented toward using them with a serial terminal. The retro Z80 "RC2014" prject got CP/M in 2016: https://www.smbaker.com/z80-retrocomputing-11-cpm-on-the-rc2014

The z80 in an Altoids tin http://www.sunrise-ev.com/z80.htm  allows you to build a z80 system that ... well, that can fit inside an Altoid tin, and with boase board and one of the two optional extensions, you can have a CP/M system.

The Z80-MBC2 4 IC z80 computer https://hackaday.io/project/159973-z80-mbc2-a-4-ics-homebrew-z80-computer is another example. This is one that I am going to refer back to later in the thread.

Here, the "CX16" is entirely a fashion statement. It will require writing a serial terminal for the CX16, hooking up a serial port (the bit-banged one should be fine ... if runs at 8 times the speed as the bit banged port in the C64, that is 19,200, and that is a fine speed for a serial terminal. You COULD use a PC laptop with a USB to serial interface and running a terminal application just as well.

But doing it with the CX16 running a faster version of a processor that might in fact have been found inside a hardware terminal back in the day ... that's just so much more authentic. And the 80 column text mode of the CX16 makes it so much more comfortable than trying to do it with a C64 with a 4x8 80 column soft font on the bitmap graphical display.

So that's level 1 ... next post, level 3, the "CP/M card" like the Apple II card or the not always functional C64 CP/M cartridge.

TomXP411
Posts: 1760
Joined: Tue May 19, 2020 8:49 pm

Retro CP/M (with the CX16 and otherwise)

Post by TomXP411 »


I'm already planning a serial terminal for the CX16. I'm just waiting to see how the hardware situation shakes out. Since we now know that the on-board serial port will be software serial, I'm less enthusiastic - but I'll probably still build something that uses the KERNAL for serial I/O. 

A CP/M cartridge will be slower than the Commander - by about 2.4x, unless you put RAM on the card and just pass video data out to VERA  - but it's certainly possible to do. I suggested an RC2014/CX16 bridge on the last FB thread where someone talked about a Z80 card. 

Here's what I'd probably do if I was going to build a CP/M card: rather than just have the Z80 CPU freeze the 6502 and take over as bus master.

I'd build a bridge that uses a chunk of the 32 bytes set aside for the expansion slot I/O and basically emulate a terminal, disk drive, and memory access:

So it might work like this....

0: CPU control: bit 1 Stop, 2 Reset

1: Terminal status register. Bit 0: byte waiting to be read by Z80. 1: byte waiting to be read by 6502

2: transmit byte (Z80 writes)

3: receive byte (6502 writes)

4-5: set memory address - this is the 16-bit address used to read or write in the Z80 RAM. 

6:  step value - how many bytes to increment when memory is read or written (use the same values as VERA)

7: write to set memory in Z80 RAM. read to read from Z80 RAM. Doing so will stop the Z80 for the duration of the read or write operation 

8: disk command, written by Z80 (ie: seek to track, seek to sector, sector read, sector write, open file, close file, file read, file write, file seek, etc....)

9: transmit parameter/data (Z80 writes) 

10: Response status (6502 writes)

11: receive parameter/data (6502 writes)

12-13: CPU address bus 

14: CPU data bus

15: Application Status Register (ASR). This is a shared value that can be set by the Z80 and 6502 and read by both. 



16-31: 16 bytes of buffer space. This could be used by disk command parameters or for application data exchange. 



Disk I/O would basically need to be written from scratch, since you really want file-level access, and CP/M  is a block oriented system by nature. The terminal I/O could be designed to work just like the 88-SIO, so CP/M for the Altair 8800 could be used as a starting point. 

This design would also allow the Z80 to be used as a fully independent co-processor. You could stop the CPU, load up some data in RAM, and reset the CPU. It would then run the program. Once the program is done, the Z80 could set a value in offset 15, and the 6502 could go in and read the results. The Z80 should also be able to trigger the IRQ line to get the 6502's attention.

 

peapod
Posts: 15
Joined: Sun Jul 05, 2020 9:33 pm

Retro CP/M (with the CX16 and otherwise)

Post by peapod »



8 minutes ago, TomXP411 said:




we now know that the on-board serial port will be software serial



Hi Tom, what was the reasoning behind going software, wasn't there at one stage a hardware port on the board?

BruceMcF
Posts: 1336
Joined: Fri Jul 03, 2020 4:27 am

Retro CP/M (with the CX16 and otherwise)

Post by BruceMcF »


... OK, like TomXP411 mentioned, then there is the opposite extreme to just using the CX16 as a serial terminal (and yes, the CX16 User Port is not even getting the two free serial shift registers that the C64 has, so if it follows the best of the C64 serial ports that bit banged the GPIO ports, it should be around 8 times the ~2400bps that the C64 could achieve without serial shift registers, which is 19.2kbps. 9600bps if it turns out that the timing of the software serial port cannot be turned that tight.

So, (3) Approach 3, a Conventional CP/M expansion card/cartridge.

These would use the RAM of the system. However, using the RAM of the system is awkward. For the C64, the $0000 and $0001 ram locations simply went to an internal GPIO register on the 6510, so the $0000 and $0001 RAM locations were normal RAM. Turn off the Basic ROM, turn off the Kernal ROM, turn off the IO access, you have a clean 64KB. You needed to put the text screen up somewhere, so you wouldn't have a FULL 64KB, but it could be tucked up toward the top of RAM and you could have a pretty big Transient Program Area.

With the CX16, there is no turning off the I/O page, there is no turning off the High RAM window, there is no turning off the 16KB ROM access (at least, so far as we know), and $0000 and $0001 are hardware detected to set the RAM and ROM banks. So if you use Low RAM, at a minimum you would need a 32K SRAM on the card and use 32K of Low Ram. The logical 00000h-00FFFh (when discussing addresses from the Z80 side, I will use Intel 0xxxxh notation) actually select $8000-$8FFF. Logical 01000h-07FFFh select $1000-$7FFF Low Ram. And Logical 08000h-0FFFFh select to expansion board 32KB SRAM. The I/O page selects $9F00-$9FFF.



What this set-up gives is really easy boot. You load $8000-$8FFF with your loader, in Z80 assembly language, you put the CCP, BDOS and BIOS as payloads wherever is convenient for the loader to access them from the Low RAM segment, and stuff them into the on-board RAM. The board control itself has a bit that you set to start running the board, the board executes a reset, uses the 65c02 RDY and bus request lines to take over the system bus, and away you go.

What is also gives is the need to generate the wait states to allow the memory to be accessed. With a ~16MHz clock from a phase lock loop, a Z80 M-state can either begin at 6502 phi2=1 or phi2=0. It will then have to add either one or three wait states to access the Low RAM.

So while that is a real easy boot-up, it's not ideal for operation. Better to use a 128KB RAM. To set it up to be CPM-Plus ready, the low 32KB can always be 08000h-0FFFFh, and the low 32KB can be mode 00, from CX16 Low RAM as above, mode 01, the second 32KB in the SRAM, mode 02, the third 32KB in the SRAM, and mode 11, the fourth 32KB in the SRAM. The wait state circuitry would then only be used for mode 00 and for I/O page accesses.

OR ... alternatively, for just a Z80 processor and a CPLD to manage things, you could have the Z80 card use HIGH RAM, by freezing the Z80 clock when moving from one 8KB segment to another, write the correct High RAM segment, and make ALL RAM accesses to the $A000-$BFFF segment, except for I/O page accesses to $9Fxx.

OR ... well, there are, I am sure, dozens of workable ways of doing it.

The one thing that all of them have in common is that they take over the system bus preemptively, whether some, most or all of the time, and they basically just put the 6502 to sleep while the CP/M system is running.

Someone with a lot of familiarity with bringing up CP/M systems, who has nostalgia for the process of writing a BIOS for a new system, and especially someone who is happy to be coding in Z80 assembler, this is an approach for them. They can wallow in nostalgia in getting the Z80 BIOS written to use Vera, to use the User Port serial port, to read the keyboard ... and to access the SD card.

Which is all of way of saying, if someone DOES something like this, I might consider GETTING it, but there is NO WAY I am going to be DOING anything like this. Never mind the hardware challenges for someone who would be doing this in breadboard modules and then working out how to get on the train down to Shenzhen to find someone to make the board ... that software coding is for someone decidedly OTHER than me.

Which leads to the intermediate approach between the "talk to a CP/M board" approach and the "CP/M takes over the CX16" approach. In between those two extremes, are the co-processor approaches. Since they are in between the two extremes, this is the approach I am calling "Approach (2)".

 

BruceMcF
Posts: 1336
Joined: Fri Jul 03, 2020 4:27 am

Retro CP/M (with the CX16 and otherwise)

Post by BruceMcF »



3 hours ago, peapod said:




Hi Tom, what was the reasoning behind going software, wasn't there at one stage a hardware port on the board?



They started out wanting to use the ACIA, that is, the WDC65c51. It has two serial ports, with FIFO buffers for each. But the transmit is buggy ... the status bit that says whether the byte has been sent doesn't work.

So then they shifted to a single serial port in Vera, with no FIFO transmit buffer but a FIFO receive buffer.

But when they expanded the Vera register addressing to allow more direct access to key Vera RAM locations effectively acting as control registers, they ran out of pins to use for the serial port.

So now they've shifted to bit banging the User port, similar to the way the C64 bit banged the User Port for its serial interface.

peapod
Posts: 15
Joined: Sun Jul 05, 2020 9:33 pm

Retro CP/M (with the CX16 and otherwise)

Post by peapod »


Thanks for the reply. My next question would have been why not implement it in the FPGA on the Vera board, but you've answered that question as well.

Thank you

TomXP411
Posts: 1760
Joined: Tue May 19, 2020 8:49 pm

Retro CP/M (with the CX16 and otherwise)

Post by TomXP411 »



7 hours ago, BruceMcF said:




So, (3) Approach 3, a Conventional CP/M expansion card/cartridge.



These would use the RAM of the system. However, using the RAM of the system is awkward. For the C64, the $0000 and $0001 ram locations simply went to an internal GPIO register on the 6510, so the $0000 and $0001 RAM locations were normal RAM. Turn off the Basic ROM, turn off the Kernal ROM, turn off the IO access, you have a clean 64KB. You needed to put the text screen up somewhere, so you wouldn't have a FULL 64KB, but it could be tucked up toward the top of RAM and you could have a pretty big Transient Program Area.



Yeah, I kind of took it for granted that something an Apple Z80 card wouldn't work, because we've only really got 40K or so of RAM available without additional help. So to get a 60+K environment, you'd need to either add RAM on the Z80 board or play tricks with the banked memory on the Commander. However, if you have to add 32K of RAM to the Z80 board, it makes more sense just to go with the full 64K and not even try to use the Commander's RAM for the Z80. That thought evolved into the thing I proposed. 

@BruceMcF have you done anything with CPLDs? Maybe the solution to both our issues is to implement something like 4 16550 UARTs, running two of them to a Z80 board, one to a rear panel DB25, and one to an ESP32. Then we could work out the design to an all-in-one board that acts as a CP/M computer, a WiFi adapter, and a serial interface. 

 

BruceMcF
Posts: 1336
Joined: Fri Jul 03, 2020 4:27 am

Retro CP/M (with the CX16 and otherwise)

Post by BruceMcF »


As far as working with CPLD's, not a thing, I'm a software guy ~ hence the breadboarding. If I could work out a circuit that only needs 8v16 or 10v22 SPLD's, I would prefer it.

Which is a segue into:

(2) Approach Two: A Genuine Co-Coprocessor

The reason that the CP/M cards/cartridges in the 80s took over the system bus was because RAM was expensive. But above, I just casually cranked RAM from 32KB to 128KB. Why not 64KB? Because the 128KBx8 is more readily available.

Looking at mouser, I see 128KB (1Mbit in an byte wide layout) in the $3-$3.25 range, Q1. If you have a two IC board, plus glue logic and SPLDs, and the (up to 20MHz) processor costs about $11 (new, you might be able to get pulls substantially cheaper, if there's delays in stocking the Z80), why would you kick at spending $3.25 for 128KB RAM? For not much more you can add a button battery carrier with integrated trickle supply circuit to make the data in the RAM persistent.

So having the Z80 and either (CP/M Plus style) 1 common 32KB area and 3 32KB banks, or simply (for CP/M 2.2) two distinct 64KB memory spaces, why have the Z80 take over the CX16 system bus? Which not run it as a co-processor system, with the Z80 acting as a client and a support services program running on the CX16?

I was looking at the Z80-MBC2  https://hackaday.io/project/159973-z80-mbc2-a-4-ics-homebrew-z80-computer  and the way they get a freestanding Z80 in 4 ICs is that one of the ICs is an AtMega32A which acts as EPROM and universal I/O emulator. The 4th IC is glue logic, a quad NAND gate, which handles the swapping the virtual-EPROM and the SRAM into the Z80 memory map.

But if you have a CX16, there is no need for the Arduino ... with the right system, the CX16 can act as the universal I/O emulator, and the terminal, and can be given the ability to inject data directly into the Z80 RAM making an EPROM unnecessary.

Which brings me to the CX16 User Port, which uses free VIA GPIO and Port A handshaking lines to offer a standard Parallel Port interface. But then an EPP interface can be implemented, and it doesn't even need as many lines: https://pinout.net/browse.php?conid=156

EPP is very simple. There are 8 data lines. There are Reset, R/W, /Data and /Address lines out, a reset line n interrupt line in, /ACK and the BUSY line in that in EPP acts as a handshake line that signals availability to transfer data when low, and completion of transfer when high.

The current CX16 User Port puts a VIA Port A handshake line on the standard parallel port STROBE ... since the standard parallel port is write-only ... so it might be necessary to swap out the R/W line with one of the unused paralell port inputs, making it a "almost but not quite" standard EPP port. 

In EPP, you set R/W for the operation you want and pull down either /Data or /Address, then the device pulls down the BUSY line when it is ready, then you read or write the data lines, and then repeat the process. It is designed with an IDE interface in mind, so the /Address refers to the IDE register address. So you can use it to read and write IDE registers and when they are set up the way you want them, you can read and write Data.

For the Z80 as a pure slave co-processor with it's own RAM, this is all that is really needed.

The idea is to have an auto-increment data read or write that works in batches of 128 bytes. This is a standard CP/M sector. The "Reset" line resets the auto-increment index to 0.

Then there is a latch that holds an eight byte memory address segment value that the auto-increment index extends. That can cover 32KB. A register /Address write to register 0-3 sets the memory address segment, with the two bits of the /Address setting which 32KB is targeted.

So to boot up for the first time, you write what you need to write in the first 128 bytes of memory and then the CCP (or substitute), BDOS (or substitute) and BIOS in high RAM and away you go.

The last four register addresses are for the Z80 reset and for setting the Z80 memory bank, 04h pulls the /RESET line low and 05h pulls the /RESET line high for the 64K Memory Bank 0, and 06h and 07h does the same for the 64K Memory Bank 1.

Finally, a Z80 access to I/O address 0FFh pulls the /ACK line high, to signal to the CX16 that there is a BIOS function request (for the simplest version of the system that could be any I/O access, but one way to use the Z80 box is as an I/O expansion handler, in which case you would want to split off the I/O page into zones with, say, a 3 to 8 decoder, and invert the /Dev7 select as the /ACK line, allowing 7 devices plus the BIOS signal. Setting the trigger access at 0FFh means the BIOS won't have to be rewritten for that kind of I/O box.

So the BIOS writes whatever information is needed by the CX16 to perform the BIOS function (including a leading byte to identify which BIOS function it is) at 0FF80, and then writes to I/O 0FFh, the CX16 gets the interrupt (or polls the state of the /ACK bit), sets the data address to $FF for the register "address" 1 or 3, reads the data, performs the function, and goes back to write a $FF at the Z80 address 0FF80h. The Z80 BIOS busy loops on 0FF80 until it is 0FFh, and then completes the BIOS call.

The glue logic needed to be added to the Z80 and 128KB RAM is fairly simple. /Data is connected to Z80 /BusRequest, and combined with the R/W line turns on an 8bit line I/O transceiver in the correct direction. BUSY is connected to the AND of /BUSACK and the /RESET line, so memory can be written in normal mode when the Z80 is running as well as when the Z80 is idle with /RESET low. AND(/Address,D2) is the /Select for the segment address latch and the memory configuration latch of D0 and D1. AND(/Address,NOT(D2)) is the /Select for the latch that holds the /RESET state and the active memory bank.

The segment memory address and extension memory segment latches are tri-state latches, and their output is selected by /ACK, which will only be low when either reset is low or /BusAck is low, when the Z80 address lines are in high impedance. The /RESET, Memory Bank latch is a blind latch that is always on, hooked directly to the RESET line and to A16 of the 128KB RAM. There is also an 8bit counter for the low 7bits of the address during /DATA mode, with its reset connected to /RESET.

The /RESET latch will be in 0h state on power up, so the box will not have a free running Z80 when it powers up. If the boot has already been written and is preserved by the button battery, the CX16 server only has to write 05h or 07h to the address and it is ready to go. On first power up, or if cleaning up after a program crash, the CX16 writes the Boot up data to the correct location in the desired RAM locations, and then releases the reset in the desired RAM bank.

In pursuing a project like this, the BIOS would be a very small shim, which just writes down what it wants to happen in the communication segment and then waits until it happens. So this means the maximum amount of re-use of facilities already written into the CX16 Kernel ... and also means that the bulk of the programming is programming the server program.

So this would get a really responsive Z80 CP/M system (or CP/Mish system), to run Pascal compilers or APL, or Small-C, or Mix C, or (etc.) ... with a very modest amount of very straightforward Z80 assembly language and all of the harder support programming on the CX16. So this would be more my kind of project if nobody else does a CP/M board in the ultra-cheap space.

 

 

BruceMcF
Posts: 1336
Joined: Fri Jul 03, 2020 4:27 am

Retro CP/M (with the CX16 and otherwise)

Post by BruceMcF »



6 hours ago, TomXP411 said:




 



@BruceMcF have you done anything with CPLDs? Maybe the solution to both our issues is to implement something like 4 16550 UARTs, running two of them to a Z80 board, one to a rear panel DB25, and one to an ESP32. Then we could work out the design to an all-in-one board that acts as a CP/M computer, a WiFi adapter, and a serial interface. 



 



Going in that direction, it seems like it would make sense to use the ez80, which already has networking support worked out. If the simplest process for putting it into it's SPI-slave mode could be worked out, Andre Fachat's SPI for a 65c02 bus would allow using it's SPI interface and allow taking advantage of it's internal UART, I2C and GPIO.

Putting it into Z80 instruction mode and using it to run CP/M is pretty much a solved problem (https://www.tindie.com/products/lutherjohnson/makerlisp-ez80-lisp-cpm-computer/), so the challenge would be integrating the use of the networking code and the CP/M mode ... and of course, that would be CP/M at up to 50MHz with single cycle opcodes, so basically as fast as the RAM allows it to run.

TomXP411
Posts: 1760
Joined: Tue May 19, 2020 8:49 pm

Retro CP/M (with the CX16 and otherwise)

Post by TomXP411 »



13 hours ago, BruceMcF said:




Going in that direction, it seems like it would make sense to use the ez80, which already has networking support worked out. If the simplest process for putting it into it's SPI-slave mode could be worked out, Andre Fachat's SPI for a 65c02 bus would allow using it's SPI interface and allow taking advantage of it's internal UART, I2C and GPIO.



Putting it into Z80 instruction mode and using it to run CP/M is pretty much a solved problem (https://www.tindie.com/products/lutherjohnson/makerlisp-ez80-lisp-cpm-computer/), so the challenge would be integrating the use of the networking code and the CP/M mode ... and of course, that would be CP/M at up to 50MHz with single cycle opcodes, so basically as fast as the RAM allows it to run.



Now the big question.... why not just put VERA directly on a Z80 board?  A VERA daughterboard for RC2014 should be simple to build, and then we could have a stand-alone "Commander ZX80", which could run at 20Mhz and not need any fancy interface logic to work with the CX16. 

 

Post Reply