Gordon's thread about zero page usage reminded me of something I meant to try, so I've sketched out a circuit for it here. It was originally just a quirky idea, but in the end it seems to be simple enough that I think it might actually be practically useful. One day I ought to actually gather all the quirky ideas together and make a coherent system that uses them all at once!
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:
Attachment:
supermodedecoding.png [ 38.94 KiB | Viewed 6511 times ]
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:
$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
So the OS has ROM at the top and RAM at the bottom, as usual, and in the middle it has the I/O devices and a window to access user RAM. It can choose which part of user RAM to access using the bank register - any write to $B000-$BFFF will set the bank register to the bottom four bits of the data bus. The bottom three bits of the bank register form A15, A14, and A13, for the user RAM. This allows it to access any data required by OS system calls made by the user code.
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.