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!
BruceMcF
Posts: 1336
Joined: Fri Jul 03, 2020 4:27 am

Retro CP/M (with the CX16 and otherwise)

Post by BruceMcF »



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.

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

Retro CP/M (with the CX16 and otherwise)

Post by BruceMcF »


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.

 

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

Retro CP/M (with the CX16 and otherwise)

Post by BruceMcF »


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.

 

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

Retro CP/M (with the CX16 and otherwise)

Post by BruceMcF »



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.

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

Retro CP/M (with the CX16 and otherwise)

Post by BruceMcF »


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

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

Retro CP/M (with the CX16 and otherwise)

Post by BruceMcF »


It should be obvious I am not a hardware hand ... where I say HA & LA "latches" above, if they are D latches, or bus transceivers with D latch functions, they will float with the new data on PA of the User Port when the mode moves from /HALE to /LALE to /W. They need to be flip-flops, to latch the data on the falling or rising edge of /HALE | /LALE, hold it until the next /HALE or /LALE,  and put it on the SRAM address lines whenever /BUSREQ is low.

OTOH whether the flip flops latch their data on the rising edge or falling edge latch might be down to price / availability of through hole parts for bread boarding. The only difference is write the address byte into PA THEN assert the mode with falling edge, assert the mode then write the address byte into PA with rising edge, so whichever one it is will determine the byte writing protocol. But either way, writing a sector will be a "DEC PB / INC PB" process for generating the mode.

Also, the Z80 /BUSACK obviously cannot be connected to /SELRAM, it has to go through a driver (74x373 are a good general purpose bus transceiver / latch), since when the /BUSREQ cycle is over it will be driven high by the Z80, and direct connect would contend with the /MREQ that normally provides /SELRAM. So make it 3 Main IC's, 2 flipflops, 1 LD & 1 dual 2-4 decoder (eg. 74x139), or a 7IC CP/M board.

An advantage of the Z80 multiple clock cycle memory access process is that it looks like 55ns should be fine for a 16MHz Z80. Unlike the 65xx memory cycle, both read and write states are asserted for MORE than one full clock cycle ... & +5v through hole 16MHz clock modules are inexpensive and still readily available. Though I would probably start breadboarding at 8MHz and then try faster clock modules at 2MHz intervals until I find out which one is too fast.

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

Retro CP/M (with the CX16 and otherwise)

Post by BruceMcF »


Of course, the ultimate goal includes direct C64 User Port access.

But the C64 User Port doesn't have as much GPIO. We've got Port B, PA2, /FLAG2, /PC2, and the two serial ports and their counters.



Given a CP/M card that plugs directly into the CX16 User Port block header, the cleanest is to create Port A on the User Port card using the serial ports and serial shift registers ... one Parallel In, Serial Out, one Serial In, parallel Out. That takes 16 clock cycles to transfer a byte, but:

1] For Z80->UP, the system can be set up to start a single shot byte load as soon as the /ALERT is generated, and we have two additional PortB lines, so if the server is doing something, when it polls the /ALERT line and then checks if the byte is loaded, quite often it would be, and if not it would only need one or two busy loops to retrieve it.

2] For UP->Z80 when /BUSREQ is asserted, in a loop:

- DEC PortB : INY : STY SerialOut : LDA (SRC),Y : NOP : NOP : NOP : INC PortB : STA SerialOut : NOP : NOP : NOP : CPY N : BNE -

... would be about enough spacing to handle it, which is still a lot faster than the fastest UART transfer a C64 can handle without a serial cart.

A CX16 expansion card version to avoid hogging the User Port GPIO might be two VIA's on a card, the first VIA driving a User Port compatible Parallel Port, the second VIA controlling the CP/M card as a block header daughterboard, and the two serial shift registers plus PB6&PB7 as four additional GPIO providing a UART or RS-232C serial port.

Mind, that expansion card would not be dedicated to hosting a CP/M card, it would be a second User Port for essentially any use of the first User Port.

martinot
Posts: 115
Joined: Fri Aug 21, 2020 3:32 pm

Retro CP/M (with the CX16 and otherwise)

Post by martinot »


---

How do I delete posts (made double entry)?

martinot
Posts: 115
Joined: Fri Aug 21, 2020 3:32 pm

Retro CP/M (with the CX16 and otherwise)

Post by martinot »



On 7/9/2020 at 12:15 PM, BruceMcF said:




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.



Love the idea of making it possible to run CP/M on the X16!

Used to have a Microsoft Z80 SoftCard for my Apple computer to be able to run CP/M and software not native to 6502/DOS3x/ProDOS.

Classic:

332e0d606926d8b2fca0505fdf4220a8.jpg

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

Retro CP/M (with the CX16 and otherwise)

Post by BruceMcF »


I am also much more enthusiastic about having it than making it ... but I am enthusiastic ENOUGH about having it that I may make a trip to Shenzhen in the gap between the two week Chinese New Year holiday and the start of semester to see about getting it made if nobody else is game.

Post Reply