So a few years ago I was investigating preemptive multiprocessing with an MMU, inspired by Andre's past work. A difference in my system was that all of the I/O space was hidden from user programs - so only the supervisor could see it. This meant that the address space for user programs was very simple - they had up to 64K of RAM, and that was it. The OS on the other hand didn't have much use for large amounts of its own RAM, but needed a lot of ROM and some I/O in the address space, and the ability to read and write user RAM.
I think some elements of this scheme can be useful in non-multiprocess systems, without a proper MMU. The advantage of using a two-mode system is that the complex address decoding needed for I/O, vectors, ROM and RAM, only apply for the OS code, and as it is not very demanding of overall address space, we can be quite lazy about how we do that, without incurring any limitations on the amount of RAM visible to our "user" mode code. And in fact the hardware required to implement these two modes is pretty basic:
This diagram shows a 74HC175 quad D flipflop register, a 74HC157 quad 2-to-1 multiplexer, and a 74HC139 dual 2-to-4 decoder. The 74HC175 could be replaced by any flipflop-style register (273, 374, 574, etc).
This is enough to allow user mode code to access its own 64K of RAM, and supervisor code to access 16K of ROM, 16K of RAM, and three I/O devices, as well as being able to access any part of the user-mode RAM through a configurable window.
On startup, and on any interrupt, the bank/mode register gets reset to zeros, putting the system in supervisor mode. In this mode, the memory map is as follows:
Code: Select all
$C000-$FFFF OS ROM
$B000-$BFFF Bank register interface
$A000-$AFFF Floppy controller
$9000-$9FFF ACIA
$8000-$8FFF VIA
$4000-$7FFF User RAM access window
$0000-$3FFF OS RAM
If the supervisor code writes the bank register with bit D3 set, then the system goes into user mode. At that point the address space changes completely, and the whole address space is just user RAM. The OS RAM is not visible in this mode, and nor is any I/O. There would need to be a convention for execution to continue - e.g. the OS will leave PC at a specific address, which - in user RAM - should contain $40 (RTI), so that this is then executed after the switch to user mode.
Last time I did this I had a lot of problems with returning to user mode without having at least some points where a badly-timed NMI could cause errors, but I don't think that would be a problem here, as the return to user mode is atomic enough.
The elephant in the room is the /BANK signal in my schematic, which I think ought to be qualified by PHI2, but that's a fairly minor change in principle - I think adding one more 74HC139 would allow us to do that and also generate the OE/WE signals for the RAM, and it's possible we could instead use U30B for this, and add a separate 74HC138 decoder to provide access to more different I/O devices if that's useful.