Updated notes on an X16 CP/M card

Chat about anything CX16 related that doesn't fit elsewhere
Post Reply
BruceRMcF
Posts: 259
Joined: Sat Jan 07, 2023 10:33 pm

Updated notes on an X16 CP/M card

Post by BruceRMcF »

I've commented on the "Feature Request" for the X16 to support CP/M, but as a Feature Request, the answer is that the X16 already supports that feature, since it has a card slot specification that has everything required to support a Z80 card.

My earlier notion from now expunged forum sections was a CP/M board organized around using the ubiquitous SPI serial Flash IC's. A couple of two-bit auto-direction sensing voltage translators to convert from 3.3v to 5v ... and the 3.3v is available directly from the slot ... and you easily have the three SPI lines output to the SPI flash and the one SPI line input from it bridged between the 3.3v domain and the 5v domain. Using a virtual 512KB drive (32 128-byte sector per 4KB "track -- Flash RAM block -- and 128 tracks per drive), there can be four virtual disks in a 16Mbit (2MBx8) SPI Flash RAM.

CP/M systems traditionally worked based on a terminal for user interface -- the "luggable" CP/M systems effectively bundled the CP/M system and the terminal together in the same box -- so for communication with the X16, the Z80 system can be designed with a UART, and then the same UART as is used in the Serial/Internet card can be used on the X16 side to talk to it. If the Z80 system does not have a FIFO, it could be a four wire connection between the two, including hardware flow control. The existing X16 terminal program can then be used as the terminal, for any CP/M software that supports or can be patched to support that terminal.

A Z80 compatible CPU, RAM, storage, User I/O ... there's a system that can be brought up to run CP/M.

One thing I was worrying about back in the previous incarnation of this was the PROM / FlashROM needed for the bootloader, with the issue being how much I wanted to invest in a parallel FlashROM programmer. However, that was before I stumbled across the use of a 22V10 SPLD as a 32 byte bootloader. It turns out that by specifying what bits you wanted each of the databus outputs to show for each combination of five address lines, the program to fit the VHDL specification to the SPLD could figure out what combinations of OR's AND's loopbacks and XOR's are required to give the right byte for the right combination of address lines. Since the logical combinations available to different line are not symmetric for the 22V10, sometimes the data lines have to be re-arranged to make it work, but if it works for some specific 32byte code for a 22V10, I expect it would certainly work for an ATF750, which has the same form factor but symmetric macrocells, and with internal macrocells in addition to the macrocells on the I/O pins.

Now, someone familiar with the ATF750 might wonder how that gets me over the expense of a programmer, since programmers that are able to program ATF750's are not as common as FlashROM programmers. However, the answer is the next step up the Atmel CPLD line, the 64 Macrocell ATF1504, where the smaller form factor versions have 32 I/O pins and 32 internal Macrocells and, critically, also have the lines supporting a JTAG interface, allowing it to be programmed from the USB port of a PC with a USB to JTAG interface cable.

So an ATF1504 can be used for memory select, supporting SPI interface capabilities, latching the control bits for SPI selects, SPI mode control, a heartbeat LED, toggling the PROM out of the memory map, and if a larger than 64KB SRAM is used, memory banking bits.

Also, an ATF1504 can be used for a self-addressing PROM which knows when it is selected and when it is deselected, and which half of memory it appears in, and only asserts the bootloader code when and where it is supposed to.

Whether that is one ATF1504 or two depends entirely on whether the memory / SPI controller function leaves enough pins and free Macrocells to program the bootloader.

Since a Serial Flash RAM IC can be programmed In-Service with a USB interface to a clip that hooks directly to the IC, that implies a board that can set up entirely by ISP from laptop USB ports ... or a USB hub connected to the mini-PC I used to put video on my TV that works better from a web video interface than from a Smart TV app.

The final question is the CPU. The three reasonable choices are an original Z80, the Z180, and the eZ80.

--------------
--- Z80 -----
--------------

The Z80 is no longer in production. It seems like many that are advertised as new-old stock are in fact recycled pulls, and are not always guaranteed to actually be Z80's. However, if you can get an actual Z80 pull, they are widely available in DIP form factor for easier bread-boarding.

So the focus of this Z80 system is getting a breadboard system up. The breadboard can't take advantage of the extra speed of the Z180, and finding a DIP package Z180 is harder, so the Z180 is deferred until the Surface Mount parts can be put on a printed and at least partially assembled card.

The original Z80 is, of course, just the CPU, without internal integration of commonly used components, so a UART would be required ... but if an SPI port is being provided for the SPI Flash "drives", then just one more select line allows use of an SPI->UART bridge like the Maxim3100. The ATF1504 is available in a small enough PLCC that bread-boarding sockets are available for less than an arm and a leg. And 16Mbit serial flash RAM is available on breakout boards from Adafruit for $3, including automatic +5v to +3.3v level shifting. A third SPI select allows for testing of various other SPI devices ... a RTC, a SPI-bus interface to an NES controller, or an interface to access an SD card in SPI mode.

I have already looked at an output control latch, Memory controller, and simple SPI interface for the Z80, and it can just fit into a 32 I/O pin, 64 Macrocell ATF1504 CPLD. Given the use of an SPLD 22V10 as a 16byte PROM, it seems likely that the ATF1504 can integrate the PROM function alongside the SRAM select, control bit latches and SPI support circuitry.

[Edited: I noted on revisiting the Z180 CPLD that giving up Bank bits I previously had allocated for the Z180 to use a 128KB allowed the same CPLD to work for both CPU's. To me, using a single 32KB SRAM seems a reasonable choice for the Z80, as it is a a bread board system intended as a "proof of concept" before starting work on the board itself.]

To keep the CPLD SRAM select consistent between the Z80 breadboard system and the Z180 on a card, the breadboard version might use a 32KB RAM, which will offer a large enough TPA to support many (though not all) CP/M applications. This is more proof of concept than a finished board, but with very few changes the same CP/M BIOS that would work for this breadboard computer would work for the next one as well. And as a breadboard computer, budget techniques can be borrowed from Ben Eater's builds, such as a slow clock from a 555 timer, allowing the breadboard computer to be run at the clock frequency that a Pi Pico and a smartphone app can handle as a simple "$5 oscilloscope".

--------------
--- Z180 ----
--------------

Now, hypothetically, if the Z80 breadboard could be made to work, I wouldn't target a Z80 based CP/M board, because except for being rare in a DIP form factor, there is a much better Zilog CPU for this particular use case.

The Z180 is no longer in production, but several versions are still readily available as as parts in stock. The readily available Z180 parts are Surface Mount form factor, including both 10MHz and 20MHz parts, so with a surface mount ATF1504 and a surface mount SPI Flash "drive", it would make sense to add a surface mount SRAM and a surface mount version of the X16 side UART and get the board built complete ... with a JTAG connector wired to the ATF1504 JTAG lines, so that the built version of the board is not functional until the ATF1504 is programmed.

The Z180 integrates a memory controller, so a 128KB SRAM can be directly connected to the CPU address bus, to step up from a 32KB SRAM to a 128KB SRAM with the same SRAM chip select circuit as the Z80 system. The Z180 includes an integrated UART, increasing the efficiency of terminal access to the X16. It also includes a one-way serial clocked port, which might be used with on Gen1 systems to speed up transferring files between the host system and the CP/M card.

Since an SPI bus is already built in to access the SPI Flash NVRAM, and the UART is integrated rather than access over the SPI bus, the Z180 card could offer two "EXT" SPI servant selects, with block headers providing simple "SPI I/O daughter-board slots" ... with the pin layout an extension of the layout of the SPI serial Flash breakout board which is being used to provide the four virtual 512KB "disk".

My initial target would be for a CP/M 2.2 BIOS, which only handles 64KB directly, so in the initial BIOS, the high 64KB would act as a small RAM disk. However, the Z180 MMU can also support CP/M Plus style banked memory.

--------------
--- eZ80 ----
--------------

Finally, the 16bit eZ80 microcontroller has Z80 instruction set backward compatibility built in. It comes with integrated UART with 16-byte FIFOs and an integrated SPI bus interface, and versions include integrated 64KB (or larger) Flash ROM ... programmable from a PC USB port over a JTAG connector.

The eZ80 also runs faster than the Z80 at the same clock speed. The Z80 requires multiple clock cycles for each step of its fetch-process-execute cycle. It could pipeline the execution phase with the following first instruction byte fetch, for register-to-register operations, so some instructions were only 4 cycles long ... but many Z80 instructions are multi-byte instructions, and each data read or write cycle required three clocks. The eZ80 can execute on a single-cycle-per-byte basis, like the 6502, making it over twice as fast as the Z80.

In addition, the eZ80 has a pipeline which in the best case allows one instruction to be fetched as the previous one is being processed and the one before that to be executed, so in that best case an instruction adds a single clock to the execution time of a routine. Not all code will get this much speed-up, since the "three deep" execution requires that the execution of the earliest opcode in the queue did not require a memory access, leaving the data bus free for fetching the third byte in the pipeline ... but with the broad collection of register to register operations in the Z80 instruction set, that is not uncommon. So while the pipeline will only be two deep if the earlier opcode operates on memory, and must be flushed for every branch, jump or call, combined with the single clock per memory access it still offer s a dramatic speed-up over the conventional Z80 fetch-process-execute module.

So if there was any interest in the Z180 CP/M card, I assume that someone else would implement an eZ80 card on a similar basis, and after that point, the demand for the Z180 CP/M card would then be limited to those who wanted a "real 8bit" CPU on their CP/M card.
Last edited by BruceRMcF on Sun May 11, 2025 8:10 pm, edited 4 times in total.
BruceRMcF
Posts: 259
Joined: Sat Jan 07, 2023 10:33 pm

Re: Updated notes on an X16 CP/M card

Post by BruceRMcF »

Latching of control bits

In the Z80 version, the I/O port address in Z80 IN and OUT instructions is basically ignored: every output is putting a MOSI bit onto the SPI bus and every input is reading a MISO bit that has been latched from the SPI bus.

So how do SPI device selects, a hearbeat LED, and bits toggling the "self-addressing PROM" (aka saP) out of the address space get out of the Z80?

One of the features of the Z80 is that it generates a running 7bit count that is put out onto the low 7 bit address lines during the two clock cycles of an instruction cycle that the Z80 is processing the instruction. This simplified the DRAM refresh support system. During a "Refresh" cycle, the R register contains the value placed on the low 8 lines of the address bus.

The specific circuit generating the running R count-up is not a generic counter, but rather works for some specific data widths, with the 7bit wide version being the one implemented in the Z80. So the high bit of the R register is not affected by the running refresh counter. This is used for the SPI serial Flash /Select. On Reset, the value of the top bit is set to 0, so that on Reset, the SPI serial Flash is select, and when the top bit is set to 1:

Code: Select all

        LD A,FFh
        LD R,A
... the SPI serial Flash will be de-selected.

R(7) =: A(7) =: /SPI0

There are three Interrupt Modes in the Z80: an 8080 style interrupt where the interrupting device supplies an instruction to be executed, an interrupt similar to the 6502 interrupt, where a vector at 0038h is executed, and an interrupt where an interrupting device supplies the low byte of the address of an interrupt vector and the I register contains the high address ... so up to 128 interrupt vectors can be supports so long as the interrupting device knows where it's vector is supposed to be in the vector table.

The CP/M card might support the X16 interrupting the Z80, but I would not use Mode2 for this, so the I register is free. And it just so happens that during the refresh cycle, "I" is placed on the high eight lines of the Z80 address bus. So the I register is used to contain setting bits. For /Select0, the SPI control circuit will latch it directly, so that on /Reset the SPI serial Flash will be automatically selected (saving valuable bytes in the Boot-loader routine). For the two other SPI selects, the SPI control circuit will invert the select, so that when I is zeroed at restart, only /Select0 will be selected at the outset.

The "SPIMODE" control signal enables the conversion of the system Mode3 SPI bus to a Mode0 SPI bus (as required for SD cards in SPI mode), using a simple circuit based on a quad NOR gate. It can also be used to integrate serial shift registers directly onto an SPI bus with a quad OR gate used to qualify SCLK and SPIMODE with the Select line, so that SPIMODE can be pulled low for control of a selected serial shift register. Although the Mode0 conversion requires SPIMODE to be pulled low prior to selecting the Mode0 SPI bus device, there is no conflict with integrating a serial shift register as a Mode3 device, since there is no harm with "priming" the Mode0 device multiple times while de-selected.

The /PROM bit controls whether the PROM or the SRAM is selected for addresses below 8000h.

And the state of OR(/MREQ,/REFRESH) is latched as an output to an I/O pin to drive an inverting transparent latch, which is connected to lines A8-A13.

The layout of the Interrupt register might be:
  • I(0) =: A(8) =: EXT#(0) -- SPI /Select
  • I(0) =: A(9) =: EXT#(1) -- SPI /Select
  • I(0) =: A(10) =: EXT#(2) -- SPI /Select
  • I(0) =: A(11) =: EXT#(3) -- SPI /Select
  • I(0) =: A(12) =: EXT#(4) -- SPI /Select
  • I(0) =: A(13) =: HeartLED
  • I(6) =: A(14) =: SCK0_RESET
  • I(7) =: A(15) =: PROM#
Last edited by BruceRMcF on Mon May 12, 2025 1:58 pm, edited 4 times in total.
BruceRMcF
Posts: 259
Joined: Sat Jan 07, 2023 10:33 pm

Re: Updated notes on an X16 CP/M card

Post by BruceRMcF »

An SPI bus for a minimalist Z80 system

Now, with an FPGA or a big enough CPLD, it would be possible to implement an SPI bus driver that just had the CPU effectively write the output byte into a register, which starts the SPI bus driver in the process of outputting the high bit to the "Master Out, Servant In" line, called MOSI, latching the "Master In, Servant Out" line called MISO, then shifting the output byte one bit to the left, writing the input bit into the space left behind at the bottom, with an internal counter starting at 8 that stops the process when the counter hits 0.

That definitely allows the fastest SPI buses. Indeed, 6502.org contributor Daryl has implemented a complete SPI bus master for 65xx bus systems based on the ATF1504 ... though it uses substantially more of the internal resources of the ATF1504 than what is done below. If it turns out that the "self-addressing PROM" doesn't fit alongside the minimal essentials for the SPI selects, HeartLED, SRAM /CS and the simpler SPI bus support circuitry below, then this might all be tossed in the bit-bin in favor of one ATF1504 as SPI bus master and the control bit latches, SRAM Select and self-addressing PROM in another ATF1504.

One could also have a loop that somehow generated an SCLK and just latch the high bit while the Z80 is successively outputting the value in A, and then doing an ASLA. The input bit could be captured by a serial shift register that can be read in one IN instruction at the end. That pair of operations is 12 t-cycles, so in a fully unrolled loop, logic based on the various timings of /Refresh, /Memreq, /IOReg, /WR and /RD transitions in the Z80 bus cycle could be sorted out to generate an SCLK that is roughly 6 cycles down, 6 cycles up (a little asymmetry with one SCLK phase 5 t-clocks long and the other 7 t-clocks long would be OK).

But this is supposed to be a minimalist system, under a specific meaning of minimalist, which is based on the smallest In-Service-Programmable CPLD I have stumbled across, the ATF1504. There are smaller CPLDs, and of course SPLD's, but the ATF1504 can be programmed with a USB to JTAG interface cable. So if it can all be fit into a single ATF1504, that's going to be counted as "the minimum".

One shifter that can be done in an ATF1504 is a 1-shift left dual-latch shifter -- that is, a simplified barrel shifter. The "minimalist" trick here is that the Z80 A register is used as one of the two latches.

To shift D0 to D6 left, while outputting D7 and inputting a new D0, write D0-D6 to an internal 7 bits, while outputting MOSI from the current D7, then read those internal 7 bits back as D1-D7, along with the MISO bit that has been latched coming back on D0.

This means that a single SPI cycle is:

Code: Select all

        OUT (C),A
        IN A,(C)
Looking ahead, in the Z180, the internal I/O addresses 00h-03Fh are used by the integrated equipment, and A7 is already being brought into the ATF1504 to set the HeartLED bit from the high bit of the R register, so in the Z180, any I/O read or write anywhere in the top half of the 256 byte I/O address space will be treated as a read or write to the single de-facto SPI register. Now, no addressing is needed in the Z80 system, which has no I/O aside from the SPI bus, but to avoid having to re-do and re-test the VHDL circuit specification, the Z80 system will be set up with SPI writes/reads qualified on A7=1.

An ATF1504 macrocell can be set up as a JK flip-flop, and that serves the needs of the SCLK nicely:
  • When /IOREQ=0, /WR=0, A7=0, SCLK should go low. So K:= NOR(/IOREQ,A7,/WR)
  • When /IOREQ=0, /RD=0, A7-0, SCLK should go high. So J:=NOR(/IOREQ,A7,/RD)
  • The system should come up with SCLK high. So either CLR:=/RESET or /PR:=/RESET.
There are dedicated clock inputs in the ATF1504 in addition to the 32 I/O pins, so having this be a clocked JK circuit doesn't cost additional I/O pins.

Finally, the latching of MISO should be done after SCLK has gone low, and before the shifted SPI byte is read back. This should be done as late as is convenient, since the number of t-cycles between SCLK going low and the MISO being latched is what determines how fast the SPI IC has to be. When an SPI IC is rated as being capable of working with a 4MHz SPI SCLK, this is assuming that MISO is latched on one SPI clock transition, given the amount of time since the previous SPI clock transition took place. So an actual SCLK operating at 1/8th the frequency of the system clock does not generate the required rated speed, if MISO is not latched at that time. Instead, if MOSI is latched 2 t-cycles after SCLK goes low, the required rated speed is 1/2 of the system speed.

For the two instruction sequence (to be repeated 8 times in a fully unrolled loop), there is a lot happening with various Z80 bus signals (the Z80 bus is a lot "busier" than the 6502 bus):
  • The OUT (C),A instruction is fetched over two t-cycles, with /MEMREQ=0, /M1=0 and /RD=0
  • The OUT (C),A instruction is processed over two t-cycles, with /MEMREQ=0 and /REFRESH=0
  • The Z80 is getting set up to do the I/O write over one t-cycle
  • The Z80 is performing the I/O write over three t-cycles, with /IOREQ=0, /WR=0
  • The IN A,(C) instruction is fetched over two t-cycles, with /MEMREQ=0, /M1=0 and /RD=0
  • The IN A,(C) instruction is processed over two t-cycles, with /MEMREQ=0 and /REFRESH=0
  • The Z80 is getting set up to do the I/O read over one t-cycle
  • The Z80 is performing the I/O read over three t-cycles, with /IOREQ=0, /RD=0
Clearly, the last thing that happens before the MISO bit needs to be output is the refresh cycle of the "IN A,(C)" instruction ... so the signal for latching MISO is "/MEMREQ or /REFRESH" -- which, conveniently, was also the signal for latching the contents of the I register and the top bit of the R register from the address lines. This could be qualified by SCLK being low, but it does not need to be, since redundant latches of the bit on MISO causes no issue for this SPI bus access.

So in this approach to performing an SPI byte Write/Read -- or, in short, transceive -- the SCLK frequency is 1/16th of the system clock, but the maximum SCLK frequency stated for an SPI IC should be 1/12th of the system clock.

So this is the SPI component of the Controller Latch / SRAM Select / SPI support CPLD:
  • SCLK is a clocked JK flip-flop, with both J & K driven by /IOREAD=0 and A7=0, K driven by /WR=0, and J driven by /RD=0
  • When K goes low, D7 is latched through to the MOSI pin, while D0-D6 are latched through to internal SPI0-SPI6 latches
  • In the Refresh cycle, MISO is latched at its input pin
  • When J goes low, latched MISO is output on D0, while internal SP0-SP6 are output on D1-D7
This consumes 3 I/O pins for MISO, MISO and SCLK, 8 I/O pins for D0-D7, and at least 7 internal macrocells for internal latches for the Write/Read shift process. With the simplicity of the logic and the SCLK process generated on the output-pin macrocell itself, it surely will be fitted with no more than a single additional internal macrocell, to be conservative, I will treat it as consuming 8 internal macrocells.
BruceRMcF
Posts: 259
Joined: Sat Jan 07, 2023 10:33 pm

Re: Updated notes on an X16 CP/M card

Post by BruceRMcF »

Smuggling a PROM into a CPLD

Really, the start of this brief return to my previous CP/M Card for the X16 idea, and the reason why this iteration is not littered with 74xx glue logic, was when I saw someone on 6502.org reporting on using a 22V10 as a PROM for a <32 byte bit bang UART boot-loader.

I am no Z80 assembly language expert, and my first draft of boot-loader didn't fit into 32 bytes, but I did sort out what I think is a complete 4KB "Track0" boot-loader. It led me to reconsider my Z80 system memory map, so that 8000h-FFFFh is a mirror of 0000h-7FFFh, which means the same boot-loader can be used for the 32Kb Z80 system and the 64KB+ Z180 system.

In any event, if a 22V10 can simulate a 32byte PROM, I believe that the resources left on the CPLD with the bit settings Latch, SRAM /CS and SPI support circuitry will be able to simulate a 64byte PROM ... especially one with as many repeated bytes as in the code here.

Code: Select all

; 24 bytes, including padding to RST 18h.
; SPI0 starts selected at /Reset
; Set up target:
	LD HL,BOOTCODE	; 3 bytes

; Send Read command
	LD A,30h	; 2 bytes
	CALL TXSPIBOOT	; 3 bytes
	RST 18h		; 1 byte ; $00....
	RST 18h		; 1 byte ; $..00..
	RST 18h		; 1 byte ; $....00
PAGELD:
	RST 18h		; 1 byte
	LD (HL),A	; 1 byte
	INC L		; 1 byte
	JR NZ,PAGELD	; 2 bytes
	INC H		; 1 byte
	JR NZ,PAGELD	; 2 bytes
	JP BOOTCODE	; 3 bytes
	NOP
	NOP
	NOP
This is a full bootloader, and my "virtual" disk drive model has the 4KB sectors of the SPI serial Flash NVRAM as tracks, so the first thing to do is to load "1 track" into the top of RAM, which is 7000h-7FFFh in the Z80 system and F000h-FFFFh in the Z180 system. However, the Z80 system will have A15 not connected to anything except the latching of the I register, and selecting between PROM and SRAM when the /PROM bit is 0 (since I is zeroed at reset). So F000h-FFFFh works for both.

This also means that I don't need a separate loop index for reading the track ... when H and L overflow to 00, the loop is done. Note that H and L need to be incremented separately, since the "INC HL" operation does not set the Z flag.

An SPI loop routine takes 11 bytes, which would overflow 32 bytes. This means that the bootloader routine has to be addressed over 64 bytes, so the loop can be unrolled. As repetitive code, with the prefix byte happening with A0=0 and the OUT and IN instructions happening with A1 alternating between 0 and 1, it seems likely that the unrolled loop will be parsimonious of CPLD resources.

Code: Select all

; RST18:
	LD A,0		; 2 bytes
TXSPIBOOT:
	; SPI byte to output in A
	LD C,80h	; 2 bytes
SPIBOOTLP:
	OUT (C),A	; 2 bytes
	IN A,(C)	; 2 bytes
	OUT (C),A	; 2 bytes
	IN A,(C)	; 2 bytes
	OUT (C),A	; 2 bytes
	IN A,(C)	; 2 bytes
	OUT (C),A	; 2 bytes
	IN A,(C)	; 2 bytes
	OUT (C),A	; 2 bytes
	IN A,(C)	; 2 bytes
	OUT (C),A	; 2 bytes
	IN A,(C)	; 2 bytes
	OUT (C),A	; 2 bytes
	IN A,(C)	; 2 bytes
	OUT (C),A	; 2 bytes
	IN A,(C)	; 2 bytes
	RET		; 1 byte
	NOP		; 1 byte
	NOP		; 1 byte
	NOP		; 1 byte 
Last edited by BruceRMcF on Mon May 12, 2025 2:00 pm, edited 2 times in total.
TomXP411
Posts: 1854
Joined: Tue May 19, 2020 8:49 pm

Re: Updated notes on an X16 CP/M card

Post by TomXP411 »

Maybe I missed something, but this sounds like you're restricting the Z80 system to 32KB. Why? If it's to gate the CPLD, that's not a very CP/M-like method.

The typical CP/M boot sequence actually overlays a boot ROM over the first page of RAM. The system boots to address 0, and the boot ROM loads the operating system's boot loader from track 0 on the floppy drive. Once the boot loader has been loaded up, the CPU writes to port 255, which turns off the boot ROM, and the first page of RAM is now visible. (This is where the interrupt table lives, which is the heart of CP/M's control interface.)
BruceRMcF
Posts: 259
Joined: Sat Jan 07, 2023 10:33 pm

Re: Updated notes on an X16 CP/M card

Post by BruceRMcF »

TomXP411 wrote: Fri May 09, 2025 5:49 am Maybe I missed something, but this sounds like you're restricting the Z80 system to 32KB. Why? If it's to gate the CPLD, that's not a very CP/M-like method.
Because the Z80 version is a breadboard system to get a system along these lines up and running. If it does get up and running, there are plenty of CP/M applications that run on a 32KB system to confirm that everything is going smoothly.

The target for the board is the Z180 with its built in memory manager, UART, and the CSIO for a half-duplex side channel to talk to a 6522 SR.

Since the only thing the CPLD has to do for SRAM in the Z180 system is to assert Chip Select for any /MEMREQ that is not supposed to go to its internal PROM code, I stripped out the two Bank bits from the I register of the Z80 version. Since A15 is not connected to SRAM in the bread board Z80 system, the 1st stage bootloader routine doesn't need to be modified between the Z80 and Z180 systems. That's also why the SPI support circuitry for the the Z80 is echoed to I/O addresses $80-$FF, when the Z80 system could just as well talk to the SPI circuitry for any /IOREQ ... so the CPLD programming doesn't need to be modified between the Z80 and Z180 systems.

The idea is that if the CPLD part works correctly when programmed with the JTAG connector on the Z80 bread board system, the same circuitry installation can be done with the JTAG on the Z180 board. The differences between the two systems are supposed to be handled by the BIOS code for each one.

This also means that the bread board system can be used to format a SP serial Flash for the board, set up on the EXT1 select. I guess those would be drives E, F, G and H.
The typical CP/M boot sequence actually overlays a boot ROM over the first page of RAM. The system boots to address 0, and the boot ROM loads the operating system's boot loader from track 0 on the floppy drive. Once the boot loader has been loaded up, the CPU writes to port 255, which turns off the boot ROM, and the first page of RAM is now visible. (This is where the interrupt table lives, which is the heart of CP/M's control interface.)
Yes, this is a similar process to that ... but to save I/O pins, the PROM is not fully addressed, it is simply mirrored in the bottom half of the logical memory map, since a PROM byte is put out on the D0-D7 pins when /PROM bit in the I register is 0, A15 is 0, and the correct address shows up in A0-A5. So above where the PROM code is executing in 0000h-003Fh, it is effectively mirrored on up to 7FFFh, until the /PROM bit is set.

So the 1st stage bootloader loads the 4KB "Track 0 of DIsk 0" to F000h-FFFFh. The top of that 4KB contains the BIOS, and the bottom just has a jump to BOOT and lots of empty space. The jump to BOOT in the Z80 system also serves to get the PC back in line with the physical address of BOOT, which the BIOS has been assembled for.

If the cold boot routine is only ever called at Reset, one could conceivably store routines only used by BOOT in the second stage bootloader space to see if a page or more could be saved in the BIOS to push up the end of the TPA, but I wouldn't worry about getting versions of BDOS and CCP assembled to a higher address than the common E400h BDOS and DC00h CCP for the Z180 system (and corresponding 6400h and 5C00h versions for the Z80 system) until much further down the track.
BruceRMcF
Posts: 259
Joined: Sat Jan 07, 2023 10:33 pm

Re: Updated notes on an X16 CP/M card

Post by BruceRMcF »

The SRAM chip select select.

This is one of the simplest components. The CPLD has the /PROM bit latched at an internal macrocell (it doesn't require an I/O pin if the /PROM function is internal to the CPLD). /MREQ and /RD are brought in for the /PROM circuit, and /A15 is brought in to latch the /PROM bit.

In order for the PROM to be active, both A15 and /PROM must be low, so making the PROM select active high only when both A15 and /PROM are low is a nor function:

PROM_Select := A15 nor PROM#

Then SRAM would be needed for any /MREQ other than the refresh cycle or when PROM_select is high, so:

SRAM_CS# <= PROM_Select or MREQ# or (not REFRESH#)
BruceRMcF
Posts: 259
Joined: Sat Jan 07, 2023 10:33 pm

Re: Updated notes on an X16 CP/M card

Post by BruceRMcF »

Two SPI Points

I've mentioned the Z80 system using an SPI UART. However, the SPI circuitry described above is a "Mode3" SPI serial clock.

Since the SPI interface is an ad-hoc interface rather than operating under a rigorous industry standard, it turns out that there are SPI devices made to work with "every possible way" of running a serial clock:
  • Polarity is the resting state of the serial clock, so that the "0" phase is when it changes away from the resting state, and the "1" phase is when it returns to the resting state. Polarity of 0 means it starts a 0, cycles up to 1, then back down to 0, while polarity of 1 means it starts at 1, cycles down to 0, then back up to 1. The SPI clock described above is Polarity of 1.
  • Phase means when MOSI data is latched by the servant device ... in the transition to Phase 0, or the transition to Phase 1. The SPI clock described above is Phase 1.
The "Mode" of an SPI interface is Phase+(Polarityx2), so the internal SPI bus for the circuit described is Phase=1 + 2x(Polarity=1), or Phase 3. However, the polarity of an SD card in SPI mode is 0, and MOSI is latched at the beginning of phase 0, so an SD card in SPI operating mode requires and SPI Mode 0 bus.

Most SPI ICs/Devices I have seen are Mode0, Mode3, combined Mode0/3, or combined Mode0/2. So the focus here is on supporting Mode0 and Mode3 devices. This is done with a circuit to use a SR latch reset input to pull the latch output low, and the Mode3 SCLK ("SCK3") as the SR latch set input to pull the latch output high. Then the Mode0 SCLK is the AND of that latch output with SCK3. SCK0 will be pulled low by the latch after reset, and will be released to follow SCK3 after the set ... at which point SCK3 will be low, so SCK0 doesn't do its first transition until the second transition of SCK3.

For repeated bytes, the first eight SCK3 clock cycles generate seven and a half SCK0 clock cycles, and the first SCK3 phase of the first bit in the following byte acts as the final SCK0 phase of the previous byte, so the SCK0 reset doesn't have to be touched again until just prior to de-selecting the Mode0 device.

This approach to generate Mode3 and Mode0 serial clocks means that a clock line can be provided on the SPI bus for each, and a given IC is connected to whichever serial clock is appropriate. If a dedicated Mode1 or Mode2 IC/device is encountered, an inverter would convert Mode3 to Mode 1 or Mode0 to Mode2.

The combinatorial logic for the circuit is:

Mode0_Line1 := SCK0_RESET nand Mode0_Line2
Mode0_Line2 := SCK3 nand Mode0_Line1
SCK0 <= SCK3 and Mode0_Line2

For the eZ80, the SPI port would be set up as a Mode3 port, and a quad NAND gate can be used to implement the circuit, since two NAND gates can provide an SR Latch, and two NAND gates can an AND gate by using one to invert the output of the other.

Code: Select all

VCC A4  B4  Y4  A3  B3  Y3
p14 p13 p12 p11 p10 p09 p08
p01 p02 p03 p04 p05 p06 p07
A1  B1  Y1  A2  B2  Y2  GND 
pin01_A1 := SCK3
pin02_B1 := pin11_Y4
pin03_Y1 =: pin12_B4
pin04_A2 := pin08_Y3
pin05_B2 := pin08_Y3
pin06_Y2 =: SCK0
pin07_GND :=: SYS_GND
pin08_A3 := pin_Y4
pin09_B3 := SCK3
pin10_Y3 =: pin10_A2 =: pin09_B2
pin11_A4 := SCK3
pin12_B4 := pin03_Y1
pin13_Y4 =: pin_02B1 =: pin10_A3
pin14_VCC := SYS_VCC
______________________

An eZ80 SPI Expansion Bus

Maintaining the SPI select pins on the CPLD is not an efficient use of the 32 I/O pins, since each one requires one I/O pin connected to the address line, and one I/O pin as a latched output. It is more efficient to generate a control signal output for use by a transparent octal latch to capture the state of the A7-A13 lines when both /MEMREQ and /REFRESH are low. In order for all SPI selects other than the SPI serial flash to reset to de-selected, an inverted latch may be used -- which then implies that the SPI serial flash should not be on the latch, since having the system reset to the SPI serial Flash selected helps keep the bootloader routine within 64 bytes with a fully unrolled SPI byte transfer.

However, given one GPIO dedicated to the external SCK0 circuit, the eZ80 SPI can only support up to three /Select pins. The eZ80 hardware SPI interface is on Port B, alongside four Timer outputs, with any of the pins able to be set up as GPIO instead.

PB0: Timer1 / GPIO
PB1: Timer2 / GPIO
PB2: /SS / GPIO
PB3: SCLK / GPIO
PB4: Timer3 / GPIO
PB5: Timer4 / GPIO
PB6: MISO / GPIO
PB7: MOSI / GPIO

The eZ80 SPI interface can be used in master or servant mode. In servant mode, the /SS is the servant select line asserted low by the SPI bus master. In bus master mode, the /SS must be held high, or else there will be an SPI bus fault.

So there are four GPIO that are available if the Timer pin functions are not used, and for the "mode-agnostic" dual SCK0/SCK3 approach used by the Z80 & Z180 systems, one of those GPIO are required for SCK0_Reset. So under this approach, and the block pin header layout to support it, the eZ80 will have 3 SPI device selects available.

PB0 =: /EXT0
PB1 =: /EXT1
PB2 := pull-up resister to VCC
PB3 =: SCK3
PB4 =: SCK0_Reset
PB5 =: /FLASH
PB6 := MISO
PB7 =: MOSI

To accommodate more than three SPI devices on the eZ80 SPI bus, a 74x595 serial shift register may be used as a selection port extender. The SCLK and Serial inputs of the SSR are connected to SCK3 and MOSI. The 8 output lines terminated by a network resister connected to VCC, so the select lines are all de-selected when the tri-state output is high.

The /OE line of the SSR is connected to /EXT0, so that the contents of the parallel output latch is asserted when /EXT0 is asserted.

Now it is necessary to load the parallel output latch from the serial shift latch when, and only when, the serial shift register contains the desired device selection byte. So the parallel clock is connected to /EXT1

The contents of the serial shift register are promiscuous ... they reflect the most recent 8 bits sent over the SPI bus, in whatever state. When the SPI serial flash is in use, the /EXT0 and /EXT1 will both be high, so none of the devices on the bus are selected and the previous state of the parallel output bus is retained.

To set up the bus for a device on the SPI expansion bus, a byte is transmitted on the SPI bus with all three selects high. Then the /EXT1 line is pulled down and up again to load the parallel latch on the SSR -- a process with doesn't affect the SPI serial flash IC. Finally the /EXT0 flag is pulled low to assert the desired selection flag.

This process means that devices which require the /Select line to be pulled low to start an operation and pulled high to complete it only have to go through the external selection latch setup once for a series of operations. It also means that if selection toggles between one external bus device and the flash storage, the serial shift selection register doesn't have to be set up again.
Last edited by BruceRMcF on Mon May 12, 2025 3:58 pm, edited 4 times in total.
BruceRMcF
Posts: 259
Joined: Sat Jan 07, 2023 10:33 pm

Re: Updated notes on an X16 CP/M card

Post by BruceRMcF »

CPLD Pin inventory:

The ATF1504 has four input pins, which are set up as Global Clear, Global Clock, Output Enable, and Global Clock2 / Output Enable2. PHI2 might be brought in on one clock input, and /MREQ and /Refresh on the output enable lines.

Additional bus signal inputs required are /IOREQ, /WR and /RD, for 3 I/0 pins as inputs.

With external latching of A8-A13 under CPLD control, address line inputs are required for A0-A5, A7, A14 & A15, for 9 I/O pins as address line inputs

All eight databus lines are required as I/O pins.

/SRAM_CS requires one I/O as output.

SPI_MISO requires one I/O as input.
SPI_MOSI, SPI_SCK3, SPI_SCK0, /SPI0 and /SELECT_LATCH requires five I/O as output.

This makes for 2 inputs on dedicated input pins, 13 I/O pins as inputs, 6 I/O pins as outputs, and 8 I/O pins as databus I/O. That is 27 I/O pins, allowing the 4 JTAG I/O pins to be reserved for in-service programming.

That only leaves one I/O pin as a "bodge wire" input for another Z80 bus signal if required, but only one I/O pin as leeway is better than none.
Post Reply