Why do this?
I mean, now you're making a 65X02.
It's fair to argue that your code isn't going to be portable to some other system "anyway", so what difference does it make. You can macro wrap everything. But, the I/O instructions are kind of going against the flavor of the CPU family.
On my simulator, I have this:
Code:
static final int CHAR_READY = 0xc000;
static final int CHAR_DATA_IN = 0xc001;
static final int CHAR_DATA_OUT = 0xc002;
static final int DISK_SECTOR_LOW = 0xc003;
static final int DISK_SECTOR_HIGH = 0xc004;
static final int DISK_SECTOR_COUNT = 0xc005;
static final int DISK_OP_ADDR_LOW = 0xc006;
static final int DISK_OP_ADDR_HIGH = 0xc007;
static final int DISK_OP_RESULT = 0xc008;
static final int DISK_MODE = 0xc009;
static final int DISK_CTL = 0xc00a;
static final int SECTOR_SIZE = 256;
CHAR_READY has a 1 when a character is available from the keyboard, you can then read it from CHAR_DATA_IN. If you write to CHAR_DATA_OUT, it shows up on the screen.
Similarly, you populate the DISK_SECTOR count and the 16b DISK_SECTOR that you want, and the address. set DISK_MODE to read or write and then store a 1 to DISK_CTL, and "magic happens". DMA, yea, that's the ticket, a DMA Disk controller. High tech. So, it supports a single "disk" with 65K 256 byte sectors, 16MB. And you can read/write up to 256 sectors at a time.
More than enough for the FIG-Forth I was playing with at the time.
The simulator looks for read/write activity at those addresses and Does The Right Thing.
It's not as far as simulation of a VIA chip or a UART, but it does offer a wee taste of the whole memory mapped I/O experience, and doesn't need a new assembler or a bunch of macros.