Jump to content
BruceMcF

Retro CP/M (with the CX16 and otherwise)

Recommended Posts

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.

  • Like 1

Share this post


Link to post
Share on other sites
Posted (edited)

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.
 

Edited by TomXP411
  • Like 1

Share this post


Link to post
Share on other sites
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?

Share this post


Link to post
Share on other sites

... 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)".
 

Share this post


Link to post
Share on other sites
Posted (edited)
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.

Edited by BruceMcF

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites
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. 

 

  • Like 1

Share this post


Link to post
Share on other sites

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.

 

 

Share this post


Link to post
Share on other sites
Posted (edited)
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.

Edited by BruceMcF

Share this post


Link to post
Share on other sites
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. 

 

  • Like 1

Share this post


Link to post
Share on other sites
5 hours ago, TomXP411 said:

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. 

 

No reason not to. Regarding the "Approach 3", the point of that approach is typically that you are using both modes, and an expansion card is cheaper than a whole new system ... which would certainly be the case.

It's nothing I would be in the market for, but it would be beneficial for the "Approach 3" type expansion board, since there would then be more Z80 code targeting Vera.

 

Share this post


Link to post
Share on other sites
Posted (edited)

Why CP/M?

When CP/M has perioidically come up in the Facebook group, there is an argument about WHETHER one should be interested in CP/M on the CX16. That's the kind of argument I have no interest in engaging with. Do if you want to, don't if you don't want to, people who do want to "explaining" to people who don't why "they are wrong" or people who don't want to "explaining" to people why "they are wrong" is not a conversation I have the slightest interest in. I have been talking on the internet for nearly 40 years now (since before html had pictures), and I have seen hundreds of versions of the same conversation without yet seeing a point to it.

However, there is the other version of the question, which is curiosity. "But why are YOU interested in CP/M?"

Some may be interested in CP/M primarily for nostalgia. They used CP/M heavily "back in the day" and it doesn't really feel "retro" to them without a bit of PIP and SAVE action.

Now, I have some nostalgia for my little CP/M tablet, the Epson Geneva, with it's cute little built in answering-machine-tape drive and the micro printer I used to write letters home when I was in Grenada in the peace corps. But that is more nostalgia for the Geneva than nostalgia for CP/M ... I spent very little time IN the OS, and most of my time in Wordstar. A Wordstar-alike word processor is about as far as a CX16 system could go toward hitting that nostalgia unless someone made LCD tablet style case (with flip up LCD) for a CX16e board.

Rather, what I would find fun about CP/M are all of the retro programming languages it supports "out of the box", which no porting required (though sometimes with some tinkering with the terminal settings).

Consider Hjalfi's little demo of his Amstrad NC200 running CP/M off the 3.5" floppy disk: https://youtu.be/FGWshrMZcCc

With a good CP/M 2.2 with a reasonable sized Transient Program Area, you get multiple interpreted and multiple compiled Basics, m-code, p-code and native Pascals, Small C, BDS-C, Mix-C, fig Forth, DX Forth, Camel Forth (among others), Cowgol, J (APL for ASCII keyboards), Fortran, Cobol ... the list literally goes on. You might not have C++ or TCL or Perl or Python or Lua, but you have a whole bucketload of 8-bit era languages that could be hosted in a disk-oriented 8bit 64K address space system.

So if that kind of playground for old 8bit era languages sounds like fun to you, you can empathize with why I find the idea of CP/M interesting even though I am not particularly excited by the idea of doing a whole bunch of Z80 assembly language programming.

And if not ... well, I am not saying that the majority of audience for the CX16 "ought" to be interested in it. Just like those who are interested in the CX16 for whatever reason and would ALSO have a nostalgia itch scratched by a CP/M extension is a niche within a niche ... a subniche ... this interest area is ALSO a subniche, and that's fine with me.

This is why if a good cheap CP/M 2.2 expansion card is developed, I am not going to much worry about which approach it takes, I'll just buy it and stop scribbling down design notes for a dirt-cheap CP/M internal userport board.

Edited by BruceMcF
  • Like 1

Share this post


Link to post
Share on other sites

I think the thing that perplexes me about it is why people want to do it with the X16 when there are other retro homebrew computers out there that primarily use a Z80. None of the legacy CP/M software is going to take advantage of the VERA, they are just going to be text mode, which can be accomplished with anything. This isn't the old days when you really only have one computer, like a C128 or Apple II, when it made sense to have an add on system to support non-native software.

  • Like 1

Share this post


Link to post
Share on other sites

Even on the TI-83+, or even the Game Boy, which both have a Z80 or similar, don't think anyone thought of porting CP/M on these things. But eh, if you find a way, nothing's gonna keep you from doing this.

Share this post


Link to post
Share on other sites

I'm a CP/M fan. I used it heavily back in the day on our Amstrad PCW. I have a PCW now as you probably have seen.

I also installed CP/M on the ZX Spectrum Next, along with some PCW software.

And if it's an option I'll do the same on the X16.

Why? Because if it's possible to run a language I used in 1986 on 3 machines in 2020, two of them brand new, and one not even a clone but a completely new brand, then that is incredible to me.

(And also of course, nostalgia.)

  • Like 2

Share this post


Link to post
Share on other sites
On 7/12/2020 at 10:00 PM, SlithyMatt said:

I think the thing that perplexes me about it is why people want to do it with the X16 when there are other retro homebrew computers out there that primarily use a Z80. None of the legacy CP/M software is going to take advantage of the VERA, they are just going to be text mode, which can be accomplished with anything. This isn't the old days when you really only have one computer, like a C128 or Apple II, when it made sense to have an add on system to support non-native software.

Though other than my C64's in storage at home (fingers crossed at least one of them still works), I really am going to have the one retro computer. Working on one continent with (under normal circumstances) most of my summer vacation time in another doesn't really lend itself to hauling around multiple retro computers in my carry-on luggage.

As far as "taking advantage of Vera", I doubt that I am going to be doing much sophisticated graphical programming of the CX16 in any event, so no big difference there.

Share this post


Link to post
Share on other sites
On 7/12/2020 at 2:24 AM, BruceMcF said:

However, there is the other version of the question, which is curiosity. "But why are YOU interested in CP/M?"

 

On 7/13/2020 at 12:53 AM, Perifractic said:

Why? Because if it's possible to run a language I used in 1986 on 3 machines in 2020, two of them brand new, and one not even a clone but a completely new brand, then that is incredible to me.

I've often wondered about CP/M; I've never used it, so I guess I never knew what I was missing, and it was really nice to hear people who like it describe what it is they like about it.  Thank you for taking the time to share your thoughts about it, even if it might never end up on the X16. In particular, I had no idea about all those supported languages; neat.

  • Like 2

Share this post


Link to post
Share on other sites
Posted (edited)

Another thing to tag into here is where to get CP/M itself.

As old as it is, CP/M itself has NOT been released under what would be considered a true open source license. Rather, specific permission to distribute free of charge has been granted to The Unofficial CP/M Web Site [UCWS]: http://www.gaby.de/cpm/ ... see especially the Digital Research binaries and source code pages: http://www.gaby.de/cpm/binary.html ... and http://www.gaby.de/cpm/source.html

What this means is that while people can GO TO the UCWS to get the original CP/M and then implement their own BIOS to port it to their own system ... the resulting operating system cannot be DISTRIBUTED without violating the license terms. As you may imagine, some emulators get around this by the age old "ignore the license terms" technique, but for those who respect the terms of software licenses, this can lead to cumbersome work-arounds.

However, due to the work of David Given of Zurich, Switzerland, in both aggregating open source alternatives and writing essential workalikes that had no open source alternatives, there is a working "CP/M 2.2 alike" operating system available, called "CPMish", which may be distributed under the GPLv2. The ZSDOS clone of the Basic Disk Operating System (BDOS) is under the GPLv2 ... most of the rest appears to be under a range of less restrictive licenses (public domain, BDS 2 clause, zlib, expat, etc.) for which complying with the GPLv2 for ZSDOS complies with all the rest as well.

All of this may be found in source code at github at: https://github.com/davidgiven/cpmish

So with CPMish, a complete CP/M 2.2 clone can be put together in the classic style, by implementing a CP/M BIOS ... and then the aggregate can be distributed in any way that complies with the GPLv2 (or later, as the GPLv2 has a "later version at your option" clause).

{Final note: raising the issues of licensing will attract different people, some of whom have very strong views regarding the differences of different open source licenses ... so I will also note that if someone wanted a distribution less encumbered than the GPL, they could combine the parts of CPMish under less restrictive licenses with instructions to download the Digital Research original sources from the Unofficial CP/M web site.}

Edited by BruceMcF
  • Like 1

Share this post


Link to post
Share on other sites
Posted (edited)

I'd be down for a Commander zx80 project! Or should it be Commander Z16?

MP/M would be my preferred option built on an eZ80 based system.  But right now I am thinking GSX for VERA would be pretty cool. 

 

Edited by Raongodz
  • Like 2

Share this post


Link to post
Share on other sites
On 7/21/2020 at 3:59 AM, BruceMcF said:

As old as it is, CP/M itself has NOT been released under what would be considered a true open source license.

The ZINC projet also seem to be rewriting bits of CPM to make them GNU (although not 100% sure of current project state or activity).

  • Like 1

Share this post


Link to post
Share on other sites
20 hours ago, Raongodz said:

I'd be down for a Commander zx80 project! Or should it be Commander Z16?

MP/M would be my preferred option built on an eZ80 based system.  But right now I am thinking GSX for VERA would be pretty cool. 

 

Yes, an ez80 expansion slot card with I/O writes going out to $9Fxx would be able to write to Vera basically as fast ad it can be written to ... and it seems like it could be set up to serve as a fast serial card when not running its own applications.

Share this post


Link to post
Share on other sites

Was looking at AVR tiny's (which have very convenient ISProgrammng in many of the iterations of the family), and realized something: if there are tristate latches for the address that the CX16 is talking to in the Z80 RAM, the microcontroller does not have to manage the CX16 user port's access to those latches, but only when the the BUSREQ to the Z80 is acknowledged so they can be asserted to the UserBox RAM. So two tristate latches as address, to buffer the userbox dates port from the Z80 data bus.

And that means there might be enough available pins to do it with just logic and latches (L&L).

A2x4 decoder on DATA_STROBE (bit0) and ADDR_STROBE (bit 1) can go,:

/Sel0 =: Address_high latch write select (

/Sel1 = Address_low latch write select

/Sel2 = Z80 /BUSREQ and to controller (1 pin)

/Sel3 = NC

Now with address_strobe datastrobe  PB0, address_strobe PB1:

1] Retrieve PB, AND #$FC, store, set up PB_DIR states, PB_RW states

2] Store high address in latch using PA

3] INC PB, high address latched, also asserted to low address latch, but BUSREQ is high, it's OR'd with the controller address assert so it's high impedance on the address bus.

3] Store low address in latch using PA

4] Take PB state, ORA #3, STA PB

W5] Store data in PA

W6] Store W_LO in PB | R5] Store R_LO in PB

W6/R5] Wait until /BUSACK on PB#>1

W7/R6] Set /R /W flags to output, then store RW_HI to PB. This latches the data in the correct data bus tristate latch.

R7] Read data from PA

8] Set /R /W flag to inputs

9] DEC PB {*1}

10] Increment register with low address, store in low address latch

11] Return to 4 if more data.

So there is only one acknowledge in the process, and it is generated by the Z80 automatically.

The trick here is the target page Index register can be kept aligned with a (zp),Y by correct choice of zero page vector ... so Y is used to store the User Box low address value AND to index LDA (OUT),Y or STA (IN),Y. The state of the PB OR #3 (hopefully) only has to be generated once, so it can reside in X, for storing in PB in step 4]. And the PB_DIR register values can be generated and put in zp. The inner loop is around 80-90 clocks, so about 90-100 clocks/byte, so about 80-90KB/s,

Which is plenty fast enough for moving 128byte sectors around at a good pace.

Finally, there is a SINGLE BYTE that the Z80 can send out, which is over it's I/O line. Z80 /IOREQ goes to the UserPort as Peripheral Interrupt. It is also ORed with the BUSACK on the z80 databus output to usserport latch. So the /IOREQ latches data, and the CX16 has to read it. The /IO_ACK line is ORred with the /R line that bring the latch output from tristate to output, the byte is read, and the /IO_ACK line brought high again.

Then the CX16 works on handling the request while the CP/M BIOS has the Z80 in a busy loop watching until the magic byte appears that says the data has been read or written. It resets the magic byte and finishes the BIOS routine.

So in this approach, the logic is a 2x4 decoder and two OR gates. But really, a 2x1 quad OR is best. Up in the sequence at step 9], there is a {*1}. When that decrement happens, the /BUSREQ is released. But the /BUSACK won't be released instantly. The address and write to Z80 data bus latches should go to active out when /BUSACK goes low, but tristate when /BUSREQ goes high, so they are fed into the third OR to drive the tri-state / output status of those three latches.

Note that the decrement is deselecting data mode and selecting low address byte write mode "at the same time". So the low byte latch may generate a spurious address as it is selected to float with the current state of PA before it is tristated. To reduce that issue, the low address input /select line can be fed into both sides of the remaining OR to give it a similar gate delay.

 

  • Like 1

Share this post


Link to post
Share on other sites

Now, that's too complex, but taking the CPLD/Microcontroller out of the picture is a start.

But one of the things I saw when looking for cheap programmers of CPLD's is that cheap EPROM and EEPROM programmers ARE available. And in the right circumstances, a ROM can make a state machine.

Another thing that came up in another discussion is dual ported RAM, which I've considered before for this. However, in looking at PRICES of different types of dual ported RAM, I came across PICbus 256 byte dual ported RAM. The PIC bus architecture saves pins and bus lines by overlaying the address and data port. For 16K PIC bus RAM, you need to have TWO different ADDRESS write modes, and a read and a write DATA byte mode. But for 256 bytes or less, you only need an ADDRESS write, a DATA write, and a DATA read: two pins.  So a PIC bus port for 256 byte RAM is 8 address/data lines, and three access lines: /CE (chip enable), /WE (write enable) and /OE (output enable): 11 lines, rather than 19 for a 6502 architecture 256 byte RAM: d0-d7, a0-a7, /SEL, R/W, CLK is replaced by a|d0-a|d7, /CE, /WE, /OE.

That means that the CX16 User Port can write directly to one port of a 256 PICbus dual port ram without little additional circuitry ... if it is a +5v part, like the (obsolete) Maxim DS1609, it might be nothing other than correct termination.

So put the dual port RAM on the Z80 bus in place of the Zero Page, and we are almost done. Put a User Port GPIO on the Z80 /RESET line, the CX16 writes the packet containing the first 128 packet of information into the start of the zero page including the code to store it in the correct spot and then signal for the next packet and wait, the CX16 writes the next packet, and so on until the BIOS and BDOS are loaded, and then have it call the routine that takes the dual port out of RAM, it sets up the normal zero page in the normal RAM, and it's good to go.

Now when there is a call to the BIOS, it puts the dual port RAM back into the address space, puts in the information that the CX16 needs, and signals to the CX16 that it requires a BIOS call performed. A page is set-aside in the BIOS (which RAM, not ROM, after all) to copy any data that might be needed from the actual CP/M zero page. The CX16 simply reads the information it needs, writes the information it needs to write, and then acts to end the busy loop. If a file sector is being returned, it is returned in the bottom half of the zero page RAM.

Now, does that mean two more pins, /IOREQ and /IOACK? No, it means one more pin, /IOREQ. The bus loop is initialized by writing a Return from Subroutine instruction into 80h, and then it sets the carry, calls 80h (in the Z80 address space in the Z80Box), and branches back to the call is the carry is set. The CX16 writes the response as a routine that loads a byte into the Z80 A accumulator, clears the carry, and returns. The last byte it writes is the one that replaces the return from subroutine instruction, so the next call will return with carry clear and the response code in A. No /IOACK line required, so only one more PB location. With port A as A|D0-7, we have ACLCK, AMODE1, AMODE2, /ZRESET, and /IOREQ, for four GPIO and one User Port interrupt line.

SO ... how does the Z80 side of this work?

The core design is straightforward. Three line drivers are connected between Z80 pins and the Dual Port B address/data lines. The inputs of the address driver are Z80 a0-a7 and the outputs are a/d0-a/07. The inputs of the data write driver are Z80 d0-d7 and the outputs are a/d0-a/d7. The inputs of the data-read driver are a/d0-a/d7, and the outputs are d0-d7. None of these latches are active when /MEMREQ=0 or /IOREQ=0. So we have the Z80 as IC1, 128K of SRAM as IC2, and three tri-state output latches as IC3, IC4 and IC5. These can be line drivers instead of tri-state output latches because they are the only connection of Dual Port B to the data or address buses of the Z80 and only one will ever be selected at the same time, and the Z80 or the Dual Port RAM serve to provide the data when required.

The state machine is built by working out what logic values are needed for the distinct state machines, allocating all input information to address lines of the ROM, and allocating all output information to data lines of the ROM. If there are 8 or fewer output levels, we are in business. If more, we might be able to squeeze it down with additional logic. However many input lines we need decides the size of the ROM, and how many output lines decides the data width.

One part of the zero page detect, which is a line driver on Z80 A8-A15, with all outputs wired together, so it is a 1 for "not zero page" and a 0 for "zero page". So that is IC7 of the 8IC (or more if the state machine outputs don't fit into a byte of output). The eighth is the loop back line driver for the output lines of the state machine that loop back into the input lines (in other words, five of the eight ICs are line drivers ... building enough of these boards might get into quantity discounts on line drivers before it hits any other price break!).

OK, what other data is needed?

For the EPROM to function, the clock needs to be connected to the ROM clock, and the ROM is never "Not selected" by the system, to /SEL is tied to ground.

How will it set the Z80 access to the ZP?

I am going to give away half of the IO-page and let it set Zero Page state by writing to ANY address in the top half of the IO page. Only the bottom bit will matter, so you could actually just increment to toggle states if the Z80 instruction set makes that appealing.

So that is /IOREQ, /W,  /A7, /D0,  /DPSEL-in ("Dual Port B Select) needed ... 6 lines. Output is /DPBSEL-out. ... 1 line. For any EEPROM location where /IOSEL=/W=0 and A7=1, the value for /DPBSEL out is /D0. For any other combination of those 6 inputs, /DPBSEL-out := /DPBSEL-in. of course those are looped back through the loop back line driver.

Now we add /MEMREQ and the output of the ZP address detector, /ZP. /MEMREQ from the Z80 is ONLY connected to the state machine ROM ... /MEMREQ on the system bus is taken from the state machine output. So that is 2 more inputs and one more output: /ZP-in, /MEMREQ-in, and /MEMREQ-out.

When /MEMREQ-in=/ZP=/DPBSEL-in=0, /MEMREQ-out =1. In all other circumstances, /MREQ-out := /MREQ-in.

This is why setting the dual port memory state is in the IO space ... /MREQ is never low when /IOREQ is low. And the /IOWAIT line can be ignored because the registering of the change of ZP state can always take place within an IO M-cycle.

So, to tally up, 8 inputs, 2 outputs.

Now we are at the ZP memory read/write state machine. We need to add the /R line for this, so that is /R-in. We also need the /WAIT state, which is an OUPUT to the Z80, so /WAIT-out is an output line. That is 9 inputs, 3 outputs.

We also need to cycle through the state machine, so we need both input and output lines for the states of the state machine. We can do the state machine DIRECTLY with the /CE /WE and /RE, and /WE and /RE can directly drive the data-write and data-read line drivers, but the address line driver needs its own driver, /PBADDR-out so that is three more inputs and four outputs. Finally, to avoid timing issues, there is a /PBWAIT toggle to stretch the timing of each Dual Port B cycle to two Z80 system bus clock cycles.

Final tally: 13 inputs, 8 outputs, so the ROM (EPROM, EEPROM, Flash ROM) is 8Kx8. A very familiar size to those familiar with the 8bit era.
 

Share this post


Link to post
Share on other sites
Just now, BruceMcF said:

... Final tally: 13 inputs, 8 outputs, so the ROM (EPROM, EEPROM, Flash ROM) is 8Kx8. A very familiar size to those familiar with the 8bit era.

 

When /ZP=1, /CE-out=/WE-out=/OE-out=/WAIT-out=/PBWAIT-out :=1 (though if it was necessary to add wait states to the SRAM access, this design makes it straightforward to do).

All of the following is only true if /DBSEL-in = /MREQ-in = /ZP = 0, and in that case, all of the states include /MREQ-out :=1, /DBSEL-out := 0.

When /CE-in=/PBWAIT-in=1, this is the start of the read or write cycle. The start is the same either way.

  /WE-out :=/OE-out := 1 => /CE-out := /PB-ADDR-out := /WAIT-out := /PBWAIT-out :=0

Next is the first Port B Wait state, /PBADDR-out = /PBWAIT-in = /CE-in = 0 => /WAIT-out := /CE-out := /PBADDR-out := 0, /WE-out := /OE-out := /PBWAIT-out := 1,

Next is the data transition state: /PB-addr-in = /CE-out = 0, /PBWAIT-out = 1 =>/WAIT-out := /CE-out := /PBWAIT := 0, /PBADDR-out := 1, /CE-out := /W-in, /OE-out := /R-in

Next is the second Port B wait state, /CE-in = /PBWAIT-in = 0, /PBADDR=1 => /WAIT-out := /CE-out := 0, /PBWAIT := /PBADDR := 1, /WE-out := /W-in, /OE-out := /R-in

Finally is the end of the Z80 Wait State, /CE-in = 0, /PBWAIT-in =/PBADDR=1 => /PBWAIT-out = 0, /WAIT-out := /CE-out := /PBADDR-out := /WE-out := /OE-out = 1
(Note that /PBWAIT-out = 0 to avoid restarting the state and re-asserting /WAIT before the Z80 has a chance to advance out of the /ZP=0 state!)

So rather than a "4 IC Z80" system, this is an "8 IC Z80 system", but none of the IC's are microcontrollers.

The challenge is getting dual-ported 512Kbit Dual Port bytewide Address/Data overlay ... because the Maxim DS1609 is out of stock at Digikey, and listed as OBSOLETE DO NOT USE IN NEW DESIGNS at Maxim. So this whole approach is hinging on the availability of the key part. I presume I can find some down in Shenzhen somewhere, but it might not be so easy to find over Stateside.

However, this approach to hardware design is like a second cousin of programming, so it might be an easier way for an old software hand to get into tinkering with hardware than starting out with a CPLD based approach.

Share this post


Link to post
Share on other sites

Now, two problems with that:
1] It's too complicated
2] You can't get those 8085 /ALE /WE /OE or PIC /CE /WE /OE ad0-ad7 multiplexed bus chips anymore. Those parts existed in the 80's and 90's, and were good for reducing chip count and board space, but latches and standard SRAM were always cheaper, and with the surface mount revolution, a surface mount SRAM and a surface mount latch or pair of latches for addressing occupies very little board space for adding external RAM to multiplexed ad0-ad7 bus microcontrollers.

However, some things start to emerge:
1] The more the existing facilities of the Z80 are used, the less complex the design
2] With both side of the communication channel part of the same system, with a server running on the CX16 and the BIOS running on the Z80 designed to work with each other, pretty much anything can work as a communication channel.

All that is really requires is a way to write the SRAM, and a way for the Z80 to pass information to the CX16. If the CX16 can write the SRAM, that can be used to BOTH install the operating system AND pass information to the Z80.

How can the passing of information from the Z80 to the CX16 be as simple as possible?
1] The CX16 is waiting for it, with the data port set as input, so that when the information is presented, the CX16 can collect it.
2] The alerting of the CX16 is an automatic result of the Z80 writing to an IO page location.

OK, if not wanting to to take over the whole IO space for the Z80>CX16 channel, it may depend on /IOREQ & A7. There is no need to include /R and /W in the logic if the BIOS is programmed to write data to IO register FFh, and not to try to read data from that location: then any /IOREQ.with A7=1 is a write, and data is being put on the data bus by the Z80.

So the /IOREQ selects a 2>4 pull down decoder with A7 and the CX16 IOACK/BUSY line. In general, the IOACK/BUSY is kept busy except just after reading the data being written, to release the Z80 to continue working (possibly on delivering another byte). The A7=1, ACK/BUSY=0 line is connected to the Z80 /IOWAIT, and the CX16 /ALERT. So the Z80 is suspended in write mode, asserting data, which is connected to Port A, which in a following input mode, and the /Alert flag is pulled low. The CX16 reads PortA, , release the /IOREQ lock by raising IOACK/BUSY, and then restores it /BUSY to be prepared for the next data input.

To write to RAM, the simplest solution is to make a /BUSREQ and write when receiving a /BUSACK. However, with a cooperative system, things can be simplified. The /BUSREQ selects the other side of the dual 2>4 decoder with two mode inputs on PB0 and PB1, connected to /HALE (High Address Latch Enable), /LALE (Low Address ...), and /W, starting in /HALE mode. The /BUSACK line selects the SRAM /CE, /HAOE (High Address Output Enable), and /LAOE (Low Address ...).

Now the Data Port A output sets the High Address, then the Low Address, then sets /W mode and writes the data. Decrement back down to Low Address Latch, write the next low address, increment back up to write data mode, and write the data, until the process is finished. The VIA already buffers the CX16 data bus from the Z80 data bus ... this protocol means that Port A can be bused directly to the Z80 and SRAM data bus, since when the Z80 is not suspended, Port A is in input mode.

So this would be a 5IC CP/M box: Z80, SRAM, 2 latches and one decoder. There would be pullups on the /HALE, /LALE and /IOWAIT:=/Alert, but by working with the Z80 and the 6522, it gains substantial simplicity.

There are two different settings for /BUSREQ. In the first, the SRAM is being loaded with the system (which is only required at initialization or after a bad system crash), so after the loading of the RAM is finished, the RESET may be pulled load before /BUSACK is release, and then /RESET released to execute the BIOS call at 0000h.

In the second, the Z80 IO request requires the CX16 to place information in the Z80 memory, so that the /BUSREQ is placed low and then the IOACK/BUSY is released high, so that the IO-space write instruction can be completed before the /BUSACK is generated. The process of switching Port A to output under Mode3, then setting the Mode to 0, writing the high address, incrementing the Mode, and writing the low address, gives more than ample time for the Z80 to have generated its /BUSACK which sets the conditions for the Mode0 data write. Then, typically, increment the low address in an index register, decrement to Low Address Latch mode, store the address, increment to Data Write mode, fetch the data and store it, index the low address in the index register, in a loop.

With a cooperative system, the Low Byte Address can be setup to the same as the index offset that is being used to access the data being written.

This occupied all of Port A, PB0 and PB1 as Mode0 and Mode1, the port A handshake line as an /IRQ line, PB2 as IOACK/RDY, PB3 as /BUSREQ, and PB4 as /RESET. So PB5 can be tied to the SRAM a16, selecting between the two CP/M address spaces.

So there's a Z80, a 128K SRAM, two 74x373's and a 74x139. Less than a $20 chip order (with Clock Module and Battery Module, plus a few pull-ups, the BOM might be mode like under $25, under $20 at quantity price breaks).

Edited by BruceMcF

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • Create New...

Important Information

Please review our Terms of Use