Here's where we really need to dig deep into the data sheet. Again, I suggest um0081 for the nitty gritty on how the PIO works.
The data says there are multiple modes for the part and that the part is largely used in interrupt driven applications. Of course this complicates the design for reasons previously mentioned. But to start with, is there a polled mode that can be implemented? Something that lets us blink a light or read a switch?
One of the first things in the data sheet is what happens during power on initialization of the PIO. These registers are initialized as follows.
These actions can be done by creating a function that gets called before the Z80 gets pulled out of reset. I didn't explicitly create an initialization function for the SIO since the BIOS (defined as the Basic Input Output or monitor) initializes the registers it needs. It is a good practice to have a function to initialize the values. In the case of the PIO, the initialization code should also initialize the MCP23017. For instance, #2 above says that the GPIO pins should be set to high-impedance. For the MCP23017 parts this means setting them to inputs.
Perhaps an obvious question
Astute readers will note that the Front Panel uses the same MCP23017 parts. So we already have initialization code for the MCP23017. But there are important differences. One is that the MCP23017 is optional. Well, so is the Front Panel for that matter. The Front Panel has 8 bits as input (for the switches) and 8 bits of output (for the LEDs). This limits the drive current from the part. If all of the pins on a part were outputs then it would stress the drive current to drive 16 LEDs. So some values are "hard-coded" for the Front panels but need to be programmable for the MCP23017 that is used on the base board.
Conditional Inclusion of Optional Hardware
There are a lot of ways to deal with optional hardware. In the case of the external MCP23017 it's controlled by the I2C interface in the PSoC. Perhaps the PSoC could probe to see if the MCP23017 part is present? Something like an I2C port scan. That would allow the part to be installed or not installed.
Another way would be with #defines and conditional compilation.
A third way might be to create a new version of the PSoC project. Create Workspace Bundle under File is the way to do that. But that's painful since we'd end up with a lot of builds.
For now, I'll just install a part and deal with this later.