- 1 General
- 2 Hardware
- 2.1 I/O ports
- 2.2 DIP switches
- 2.3 Detecting the hardware
- 2.4 Interrupts
- 2.5 Other resources
This interface is based on the CH376 chip and allows access to USB mass storage and other USB devices on CPC.
The CH376 chip implements FAT32 in hardware, which means no filesystem driver is needed on the CPC side. It also implements the lower level aspects of USB, making it relatively easy to write drivers for other USB devices (mouse, joystick, ...).
Albireo also includes a high-speed serial port, allowing speeds past the megabit per second mark.
What's in it?
- SD and SDHC card mass storage support with fast (6MHz) SPI link with the SD card. Hardware FAT32 support for easy file access. Raw sector access also possible for homegrown filesystems and other advanced applications.
- USB host support with built in mass storage driver, also with FAT32 and direct secotr access.
- Generic USB host with direct control of USB endpoints, for connecting other kinds of USB devices.
- High-speed (1.5Mbaud) serial link with built-in USB interface for linking with other computers for fast data exchange. Includes flow control, 16 byte FIFO on CPC side, and 512 byte FIFO on remote side, allowing fast buffered and interrupt-driven operation.
- Software configurable interrupt routing to either NMI or INT, or interrutp masking. Multiplexing of several interrupt sources: USB host controller, UART, remotely triggered, and CRTC CURSOR signal interrupts are gathered and easily accessible from a single interrupt status register.
How does it work?
- There are two main devices: the CH376 handles the USB host and SD card side of things, and is accessed at FE80 (data) and FE81 (command/status). The communication side is handled by a TL16C550D, mapped at FEB0-FEB7. This is similar to the chip used on most PC hardware and some Amiga expansions like the SilverSurfer.
- There is an FT230X chip to convert the UART to USB for connecting with a modern PC (standard serial ports are not that common anymore, and they wouldn't be fast enough anyway). The FT230X also generates 12MHz and 24MHz clocks for the two other chips.
- The 16C550 "modem control" lines are connected to various things (CH376 interrupt, FT230X general IO pin for remote control) and turns them into interrupts.
What is it useful for?
Not much currently because there is no software yet, but in the future:
- Use the USB key or SD card directly from BASIC and well-behaving apps (which support extra disk ROMs and a C drive). Load games and tools from the mass storage media
- Connect to another computer like the CPC Booster, load snapshot, DSK or other files from it and write them to floppies or the mass storage media. A snapshot of 128K could be loaded in 0.86 seconds, a snapshot of 64K in less than 0.43 seconds.
- Use SLIP (serial line IP) and a properly setup gateway (Linux machine or similar) to connect the CPC to the Internet.
Albireo is largely built around two independant chips, the CH376 and the TL16C550D. Because of cost and chip count limitations in address decoding, they each have slightly separate address ranges. The decoding is clean, this means there aren't any mirror ports or undecoded address bits. Just the addresses listed below are used.
The addresses are in the I/O range, which means you access them with the OUT and IN instructions. They are not memory mapped, and to match with the CPC address decoding, the address is decoded on 16 bytes. This makes using OTIR and similar looped instructions tricky, but is required for compatibility with the CPC.
- FE80: "DATA" port (read/write)
- FE81: "COMMAND" (write) and "STATUS" (read) port
Some of the registers are sharing the same address. A register bit (DLAB) is used to switch between the two groups.
|FEB0||0||RBR/THR: RX buffer (read), TX buffer (write)|
|FEB1||0||IER: Interrupt enable|
|FEB0||1||DLL: Divisor latch LSB|
|FEB1||1||DLM: Divisor latch MSB|
|FEB2||IIR/FCR: Interrupt status (read), FIFO control (write)|
|FEB3||LCR: Line control|
|FEB4||MCR: Modem control|
|FEB5||LSR: Line status|
|FEB6||MSR: Modem status|
|FEB7||SCR: Scratch register|
The board holds 4 DIP switches for configuration. From top to bottom:
S1 - CH376 interrupt enable
- When this switch is ON, the usb controller is allowed to generate interrupts to signal the CPC when it is done performing an operation.
- When this switch is OFF, the usb controller is not allowed to generate interrupts. The CPC must then poll the CH376 STATUS register to know wether the operation is finished.
S2 - CH376 reset enable
- When this switch is ON, the CH376 will be reset at the same time as the CPC (hardware reset only).
- When this switch is OFF, the CH376 will not be reset, and the CPC must initialize it using the reset command (software reset). In this case, the CH376 internal buffer may be used to store reset resident data (but I don't know if this is of any practical use, yet).
S3 - Remote reset enable
- When this switch is ON, the DTR signal from the remote side of the serial link is connected to the CPC reset. This means that the remote side computer can trigger the CPC reset by toggling that line.
- When the switch is OFF, such reset is not allowed and the CPC is safe.
S4 - Remote interrupt enable
- When this switch is ON, the DTR signal from the remote side of the serial link is plugged to the DSR line of the UART controller. It then generates an interrupt which the CPC can process.
Detecting the hardware
Detecting the CH376
You can use the CHECK_EXIST command for this. It gets one byte as argument, and returns the negation of it.
CH376_CMD EQU 0xFE81 CH376_DATA EQU 0xFE80 CMD_CHECK_EXIST EQU 0X06 ; Send the command LD BC, CH376_CMD LD E, CMD_CHECK_EXIST OUT (C), E ; Send one data byte LD BC, CH376_DATA OUT (C), E ; Get the result IN A, (C) ; Check that the result is the same as what we sent XOR E INC A JR NC, ERROR_NOT_DETECTED ; Here, we know that the CH376 is present!
Detecting the TL16C550D
The T16C550D can be detected using its scratch register. This is a read/write register with no effect on the chip. We can check that writing a value there and reading it back works.
SCR EQU 0xFEB7 LD BC, SCRATCH LD A, 0x55 OUT (C), A IN A, (C) CP 0x55 JP NZ, ERROR_NOT_DETECTED ; Here, we know the TL16C550D is present!
All interrupts on the board are handled by the TL16C550D. They can be configured to trigger either an INT or an NMI, or nothing at all.
Individual interrupts control
The TL16C550D manages several different interrupt sources. These can be individually configured using the interrupt enable register.
- Bit 0: data receive interrupt, triggered whenever a byte is received on the serial line
- Bit 1: TX register empty, when the UART has nothing to send.
- Bit 2: receiver line status, to detect RTS/CTS handshaking
- Bit 3: modem status, used for all other interrupts (CH376, remote control, raster interrupt from CRTC cursor)
The interrupts can also be routed to NMI or INT, or none of them if you don't want your code to be interrupted. This is done using the OUT1 and OUT2 bits of the modem control register (MCR).
- OUT1 (MCR bit 2) must be set to 0 to enable INT.
- OUT2 (MCR bit 3) must be set to 0 to enable NMI.
- If both bits are set to 1, interrupts are disabled.
- If both bits are set to 0, both INT and NMI will be triggered, which is probably useless.
When an interrupt occurs, the first step in the interrupt handler is to identify where it comes from. This board provides support only for Z80 interrupt mode 1.
You get information about interrupts by first reading the IIR register. If bit 0 is set, it means Albireo is not the one which generated the interrupt. You should turn to other hardware plugged to your CPC, or, if all else fails, to the gate array interrupt.
If the bit is cleared, there is a pending interrupt from Albireo. You then look at bits 1-3 of IIR to determine which interrupt it is.
- 3: Receive line interrupt, read the LSR register for more details and to clear the interrupt.
- 2: RX data waiting, read the RX register to get the data and clear the interrupt.
- 6: RX timeout, read the RX register as soon as possible to get the data and clear the interrupt.
- 1: TX register empty, this is cleared by reading the IIR or writing more data bytes.
- 0: "modem status", means one of the external interrupts was triggered, read the MSR to learn more and clear the interrupt.
The identification of the external interrupts in the MSR is as follows:
- Bit 0: CTS, used for flow control of serial line. It is recommended to use automatic flow control, in that case this bit is unused and can be ignored.
- Bit 1: Remote control interrupt, when it is enabled using switch S4. This can be triggered from the other side of the serial link using the DTR pin.
- Bit 2: CRTC CURSOR raster interrupt. This is triggered when the CRTC CURSOR signal is activated. When not in use, it is recommended to disable the CURSOR signal using CRTC register 10 to avoid parasite interrupts coming from here.
- Bit 3: Interrupt request from the CH376. Note that this bit will be set both when the CH376 triggers an interrupt, and also when the interrupt is acknowledged on the CH376 side. This means a double-acknowledge scheme is required.
Double acknowledge of interrupts
Due to the way the interrupts are wired, the TL16C550D will trigger an interrupt both when the CH376 triggers one, but also when it is cleared by software. This means after clearing the CH376 interrupt, the interrupt flags of the TL16C550D should be checked again to make sure there isn't any other pending interrupt.
This is not a problem when using the INT pin, because everything will happen with interrupts masked by the z80. However, when using the NMI, there is no masking and this would trigger a new entry into the NMI routine, which is probably not what you want.
To avoid this problem in NMI mode, it is recommended that the NMI handler masks the NMI by using the OUT2 bit in MCR, while it processes the interrupt. This allows to perform all tasks as required. Once all interrupts have been cleared (IIR bit 0 is set again), it is safe to enable the NMI again.
The same applies for the remote control interrupt, when clearing it on the remote side a new interrupt will be triggered. A similar approach can be used: mask out the NMI, tell the remote side the interrupt is handled and let it clear the bit, acknowledge that with the TL16C550D, and finally enable the NMI again.
The CURSOR interrupt is not subject to this, because it is edge triggered. You can use it as an NMI source without such problems. The interrupt is cleared simply by reading the MSR register.
Test programs to check that the Albireo boards are working fine:
- Mass storage tests for SD and USB (get disk capacity, read root directory).
- Complement of the above (load with MERGE), adding a test for HID mouse.
The datasheets of the chips used on the board may be useful:
- CH376: USB-Host and SD card controller (part 1 readily available as "CH376DS1.PDF", and part 2, with help from Google translate.
- TL16C550D: UART for hi-speed link (also used for interrupts control)
- FT231X: UART to USB converter for hi-speed link (also used for clock generation)