|The expansion port (viewed from the rear of the computer)|
Using the expansion port is a bit more awkward than using the printer or joystick ports. This is because hardware on the expansion port needs a 16-bit numerical address, just like a memory address. This means that your hardware must include logic circuitry to decode the address lines (A0 to A15) so that it only acts on instructions that are intended for it. A lot of the possible addresses are already being used by the CPC's internal hardware, but there are ranges that are reserved for user peripherals, namely:
The addresses &F8FF, &F9FF, &FAFF, and &FBFF are used to reset peripherals on the expansion bus.
The CPC firmware guide (SOFT 968) has the full list of I/O addresses and the hardware that uses them.
The BASIC command for reading a byte from an I/O address is:
where &nnnn is the 16-bit I/O address to read from.
To send a byte to an I/O address, use:
where &nnnn is the 16-bit I/O address, and &xx is the byte to send.
There's also the WAIT command, which will wait until the specified I/O address returns a particular value:
&nnnn is a 16-bit I/O address. mask and inversion (optional parameter) are 8 bit values.
This command reads a byte from the I/O port specified as &nnnn, XORs the byte with inversion then ANDs it with mask. It loops until this operation returns a non-zero result.
The following opcodes are available for reading and writing to and from I/O ports:IN A, (C)
Despite the (C) in the opcode, these operations actually use the full 16-bit value of the BC register.
The Z80 processor has block-copy instructions for I/O operations, equivalent to the LDIR instructions for copying memory. However, the design of the CPC's hardware means that these instructions don't work on the CPC.
The Z80 also has two undocumented I/O instructions. Most assemblers won't recognise the opcodes, so you'd probably have to insert them as hexadecimal values if you wanted to use them.
|IN F, (C)||ED 70|
|OUT (C), 0||ED 71|
When you issue an IN/INP or OUT command (from BASIC or machine code), the following things happen:
So, to give an example, if you made a device that would ignore all IN or OUT commands except those to address &F8F0, you could send a byte to it by doing something like OUT &F8F0, &FF (or the machine code equivalent). The IORQ and WR lines would go low, the value &F8F0 would be put on the address bus, and the value &FF would be put on the data bus. The address decoding logic on your expansion device would recognise that this I/O operation was directed at it, and the device would then do whatever it was supposed to with the data on the data bus.