With this idea in mind, a premilinary thought was to 'reserve' 4k of memory as an IO space, which is further divided into 'device pages' (perhaps 256 bytes, but I haven't considered this yet).
Each IO device that is installed could then have a 2 word (4 byte) ID in its IOSpace (at a fixed location, perhaps 0-1 and 2-3) that could be used as a device ID, along the same lines as PCI's Device ID.
Apple has a system whereby the address of a device is dependent on which slot you insert it into. I do not recall the exact details, but the Apple II series came with 8 expansion slots. Each slot had the 6502's full address bus presented to it in case a memory expansion was desired; but for regular I/O devices, it had *pre-decoded* I/O selects which were decoded by the motherboard on behalf of the I/O device. Hence, you could locate the expansion card in any slot, without fear of changing addresses and the like. In slot 0, a device might occupy $E000-$E0FF, but in slot 6, it might appear at $E600-$E6FF.
NOTE: ROMs containing code (e.g., supplying the device's driver on the card itself to attain 100% plug-n-play) must be prepared to handle execution from any
address. The 6502 has a pretty static execution environment -- except for branches, all PC-affecting instructions do so with absolute or absolute indirect addresses. 99% of all zero-page and non-ZP memory accesses are done with absolute addresses. X-register-indexed is insufficient to make a truely location-independent piece of software, due to it being relatively slow and the X register being only 8-bits wide.
To work around these problems, you could take the route that the Atari ST uses to boot its own OS up: a ROM-disk. The basic idea is to store your software in ROM in a relocatable execution format, such as O65, in ROM. Recognition of the device requires "loading" the ROM-resident software just as if it were disk or tape-resident. The advantages are as follows:
* The software can be relocated anywhere
in memory at boot-time.
* The software, once loaded, can be unloaded
and its memory reclaimed for other uses if and when necessary. For example, as I'm typing this content, my floppy disk isn't doing a single thing. In fact, I rarely ever use the floppy disk. Even the harddrive itself can be said to not be doing anything useful (certainly it wouldn't in a 6502 environment, where memory protection and virtual memory doesn't exist). Hence, why take up space with a floppy or IDE device driver when it's not being actively used? I could be devoting more of my limited (<64K) RAM to the content being edited. Then, when I need to load or save some data, the OS can at that time
reload the IDE/floppy device driver on an as-needed basis. (Of course, it needs RAM to do that with; this is an advanced
application of the system; for a simple system, you wouldn't typically do this. I'm just saying this is possible
without any complicated virtual memory hardware, nor with a sophisticated I/O bus design.)
* The software isn't limited to running in a 6502 environment -- if, at a later time, you want to upgrade to a 65816-based solution, the software can still be loaded and used anywhere in the 65816's address space, including between 64K and 16MB regions.
* Heck, it's not even limited to the 65-architecture at all. If you decide to make a homebrew PC based around a simple RISC or 68K-series processor, you can easily make a 6502-to-native-machine-language translator. Yes, there's a run-time performance hit. But there's no arguing -- it'll still work
, and that's pretty much the point, isn't it?
* It is CPU-speed independent -- RAM is always as fast as the CPU, while most of the cheaper ROM chips can handle at most 4 to 8MHz depending on bus loading.
* It's simpler to hook into the system: a serial ROM typically has only 8 pins to the chip (two of which are power, two of which actually carry the data/clocking required to access the data, and the remaining four are typically address select pins if you want greater storage capacity; most serial ROMs can allow up to 8 devices to be placed on its I2C serial bus, giving the illusion of storage up to 512KB!!), and can hold up to 64K in a single unit. Hence, it can not only store the driver proper, but any default "support files" it needs too.
* If serial ROMs were anticipated by the I/O bus specification, it could support a special out-of-band serial interface which could be an intrinsic part of the motherboard registers, which can greatly simplify the interface to such hardware.
* Because the ROMs hold a filesystem image, instead of raw executable content, a filesystem standard can be created that allows device capability inquiries to be made: vendor IDs, minimum requirement lists, whether or not it's a boot-capable device, and if so, what it's default priority is in the system, etc.
* The filesystem image need not be even as complex as FAT; since it exists in ROM, there is no need for a block allocation table, etc. Directories can be implemented using very simple data structures, like a linked list, without a performance penalty, etc. Support for subdirectories are not typically needed, but it can be supported easily using only a single flag-bit in each directory entry node.
2) Devices have a limited register space for I/O registers. This is not usually a concern, but it can be if you are adding a device which requires a large amount of memory for its own use (e.g., a video card often requires a video frame buffer). For example, you're expecting to allocate 4KB to devices. If you assume a maximum of 16 slots, that makes for 256 bytes of register space per slot. 256 bytes is woefully inadequate to address a video frame buffer, even for a plain, monochrome, text-mode display. One solution to this is to use a video system whereby you access frame buffer memory through a set of ports, such as the Commodore 128's VDC chip, or the TI-99/4A's VDP. To get an idea of what I mean, you can reference the VDP's documentation online. Here's a good link that I found while googling for "9918A datasheet": http://www.msxnet.org/tech/tms9918a.txt
If the peripheral you're looking to interface doesn't support memory-access-through-registers, it's usually not too hard to implement it yourself.
Other than these two "gotchas," as I can't even call them disadvantages really, a slot-dependent-base-address technique is not only exceptionally powerful, it's simple, cheap, and for a simple bus interface like the 65816, damn fast. This is the type of bus interface I was going to use for my own devices in my own homebrew environment. My system, inspired by Garth's own design concepts, was not going to have a BIOS ROM or such, except purely for the purposes of cold-booting. Instead, it was going to draw its system software from a serial EEPROM, probably a 24C512 series chip. After loading the system software into RAM, it would write-protect that region of RAM, so that it emulated ROM as far as the rest of the software was concerned.
I cannot fathom why the PC bus interface didn't follow this approach. ISA, arguably the most useful bus interface second only to PCI, could easily have handled full plug-n-play and still retained its hacker-friendly interface. The only disadvantage to ISA would be its 8MHz speed, which could easily have been addressed by revising NOT the software specifications, but the hardware specifications alone. As it is, PCI was a world unto itself, specifying both software and hardware requirements. Fortunately, PCI got a different approach towards autoconfiguration which is equally viable and, while not hacker friendly, it has a long-life (evidence: AGP is actually a point-to-point version of the PCI bus, operating at MUCH higher speeds; it is 100% software compatible with PCI, and even appears as a PCI bus when enumerating the system hardware through the PCI configuration register space).