Z80 Serial IO (SIO) Emulation - Worked Example

A project log for 3-Chip Z80 Design

Combining a Z80 retro design with a modern PSoC CPU. 09/23/2019 at 17:300 Comments

Serial I/O Emulation

Normally, serial I/O using the 6850, SIO or DART consists of two I/O mapped locations per serial port. One I/O address is for data read/write. The other I/O address is for status/control. In the case of the 6850 UART this reduces down to just two bits of status (Transmitter full and Receiver empty) and the 8-bit data read/write register.

Polled Serial I/O

Serial output from Z80 works by the Z80 polling the Transmit not full register waiting for transmit not full and then writing the data to transmit in the Transmit Output buffer. This action is emulated by the PSoC. The Z80 requests the status register value which the PSoC presents. The PSoC determines the value that should be sent to the status register by looking at it's own UART interface. If the PSoC UART transmitter is not full then it returns that status to the Z80. The Z80 sees that the transmitter is not full and then sends out the transmit byte. The PSoC picks up the transmit byte and puts it out to the UART.

Read operations are similar. The Z80 reads the UART status register which is emulated by the PSoC and waits for receive data to be present. It then requests the data by performing a read which causes the PSoC to read the UART and return the value to the Z80.

From the PSoC Perspective

From the PSoC operations consist of either waiting for an interrupt or polling the IO_Stat_Reg and waiting for IORQ* to go active.

When IORQ* goes active the PSoC then takes the values in the IO_Stat_Reg status register to determine the operation. It also takes the address from the AdrLowIn status register to determine the peripheral address that is being accessed by the Z80.

Interrupt Driven I/O

Interrupt driven I/O is only a bit more complicated. The PSoC generates the INT* line to the Z80 indicating that an interrupt is present. This causes the Z80 to issue the following timing:

In response, the PSoC places the interrupt vector onto the data bus to the Z80. This operation by the Z80 is similar to an I/O read with the exception that the M1* line is active (low) during the read of the interrupt vector. The PSoC sees the M1* line low and knows that this an I/O operation reading an interrupt vector.

This behavior can be emulated by the PSoC by monitoring the M1* line.

The PSoC sees the M1* line low and knows that this an I/O operation reading an interrupt vector. This seems to be a common source of confusion for many designers who do not include M1* in their logic. If it's a standard Z80 peripheral chip it probably knows to not send out normal data, but send out the interrupt vector.

The interrupt vector could be implemented with some additional logic in the PSoC but would come at the cost of 8 of 2:1 multiplexers on the data bus out of the PSoC to the Z80. With this emulation method there's no real advantage to adding extra logic for this function. If M1* is high then the read is a "normal" I/O read. If the M1* is low then the read is an interrupt vector read. In either case, the PSoC provides the requested value.

The reading of the interrupt vector by the Z80 is the interrupt acknowledge and will remove the interrupt.

The PSoC hardware to implement the external UART is relatively straightforward.

The  Configuration for the UART is:

This configuration allows the UART to be software controlled (for baud rate and other configuration values). It also provides hardware handshake lines. The PSoC UART can be set up to interrupt on various conditions.

These can be used to mimic the interrupt characteristics of the Serial I/O being emulated.

Sequence of Operations - Serial In - Polled

The steps to receive data from the serial port are:

Sequence of Operations - Serial Out - Polled

This is very similar to the above example for reads. The difference is the transmit bits are used.

This all seems complicated but it's really not all that complicated. Basically, the function of the I/O interface of any Z80 peripheral can be emulated by the PSoC. This consists of emulating the internal registers of the peripheral and connecting them to the peripheral functions in the PSoC. The UART example above is familiar. Others may be less familiar. Some, like timers may require additional sofware/hardware in the PSoC to emulate the function of the chip.

The beauty of this design method is that the peripherals can be switched in/out of the design by creating a bit of code to emulate their control/status interface and the PSoC can do all of the work.