BigEd wrote:
Just to be clear, the JBANK stack is not a stack of values within the MMU, I think, rather the JBANK value points to a page-one mapping which replaces the 6502 stack. (Or indeed, both a page zero and a page one, because that's what you've chosen to build.)
JBANK represents the memory bank/block that you are goint to JMP to (when you set the next entry with the MMJ instruction).
To make the thought process easier, lets focus on the 64K-mode. Thus, every bank is 64KB in size and the JBANK equals the 4 most significant bits (MSB) of the 20-bit memory address. Now, you don't utilize the stack during the JMP instructions so its not so interesting in that regard, but if you jump with an JSR, the 65C02 pushes its 16-bit address onto its internal stack in the normal way. But the address is 20-bit here, so what do you do with the upper 4 bits?
To answer that, the upper 4 bits represents the bank number and they are pushed onto the JBANK stack which is a separate internal stack of the MMU. It is handled the same way during an RTS: The MMU will pull the last entry from the JBANK stack and use that as the 4 MSB of the 20-bit address when it returns to the location of the JSR. So you get back to the correct 20-bit address as the 65C02 CPU pulls its 16-bit address from the normal stack, and everyone gets happy (=you return to the same 20-bit address from which you initiated the JSR instruction).
fachat wrote:
How does the CPU know, on return, that it jumps into supervisor mode?
Is it because the return address is flagged as supervisor? How do you prevent the user from manipulating the stack and this way 'jump' anywhere into supervisor memory?
The CPU doesn't know about supervisor or user mode, it's fully handled by the MMU. It is also only available in the 64K-mode for a number of reasons (which I won't discuss now). E.g. with 64KB memory blocks.
Lets define supervisor mode as the "normal mode" of the 65C02 CPU. Supervisor memory is all the memory in the system as it can all be read in supervisor mode. If you have for example 1MB, that is 16 memory blocks/banks of 64KB each. Through normal 65C02 CPU instructions and MMU instructions you can read (and run programs) within all of the 1MB of memory.
"User mode" on the other hand can only read "user memory". The MMU prevents any memory access to supervisor memory in the user mode. If one 64KB memory block/bank is user memory, programs running in that bank will not see the rest of the memory (even through MMU instructions).
In 64K-mode the MMB instruction gets another function once the PROTECTED_MEMORY flag is turned on. One can then set an "access key" for each memory block. The key is only possible to set in supervisor mode, and it can be set to a value between 0 and 255. If set to a number>0, the memory block will be user memory. If, on the other hand it is reset back to zero, that memory block/bank is going to be supervisor memory.
The user mode is triggered once a JSR happens into a memory block with access key>0 (e.g. the PC points to user memory). A program in this memory block won't be able to read other memory blocks unless the access key of that memory block is the same as for the current memory. So there is no way to read supervisor memory or supervisor stack from user mode, and no way to "jump" to supervisor memory either. The program running in user mode simply doesn't see supervisor memory or other user memory blocks (with different access keys).
The only way to exit "user mode" is through a RTS instruction or an interrupt. Once a RTS instruction is coutered, the MMU pulls the return bank from the JBANK stack and the CPU pulls the return address from the 65C02 stack in supervisor memory. If an interrupt occurs, the MMU pushes current user memory bank into the JBANK stack (while the 65C02 pushes the current address into its stack) before it pulls either IBANK (IRQ) or NBANK (NMI). It then uses that as the memory bank before it pulls the interrupt address vector and jumps to that. IRQ and NMI interrupt routines can reside at different memory banks.
At RTI, the MMU will pull the JBANK stack again and return to the address that the interrupt occured at (by pulling the 65C02 stack at in that bank).
So its all pretty secure. There is simply no way for a program in user memory to access data in supervisor memory. You can't set access keys in the user mode either, so its totally isolated.
I have alot of macros to handle the 20-bit addressing. I will add those to the next version of the manual since I think it makes it easier to use (you won't have to think about MMU instructions in that way).