Protected mode 65C02

A project log for PZ1 6502 laptop

I am building a laptop with a W65C02, lots of memory, SID-sound, decent graphics and a filesystem.

adamklotblixtadam.klotblixt 02/09/2024 at 09:280 Comments

I've spent quite some time exploring the possibilities of extending the 65C02 to deal with supervisor/user mode. Several HW designs and code variants have been tested to reach something that actually works the way I want.

The PZ1 has a fixed (ROM) top page, those 256 bytes of code are key to getting everything working.
The kernel scheduler is run via IRQ and a timer.

User processes have to use osCalls to get stuff in/out of the system. This adds overhead, but surprisingly little. I think it is worth it.

Some implementation details:
- Whenever VPB fires (IRQ/BRK/RESET/NMI) the ioAccess bit 0 is set.
- Op code read at $FE00 sets ioAccess bit 1. This is used as the osCall entry point.
- Writing to PORT_IO_ACCESS can clear ioAccess, run as the last instruction before returning to a user process from an osCall.
- When accessing the $FExx area, and ioAccess is not 0, the io ports are visible. Otherwise a banked memory operation takes place. Nice to have those extra 256 bytes for user processes :)
- The kernel scheduler saves the ioAccess state for each user process. A process can be in the middle of an osCall, and needs to resume operation in the same state. This is the reason to have 2 bits in ioAccess. Very few osCalls so far are non-interruptible.
- In order to deal with dangerous op-codes in user mode (SEI/WAI/STP) there is a watch dog timer (WDT). If the user code takes too long because of a WAI or STP, the WDT issues a reset. The WDT prevents any user process from hogging all the cpu cycles.
- A user process can't access the memory of another process, since the memory banking system is done via io ports, managed by the kernel.

Other enhancements I've explored:
- RESET can now differentiate between cold boot, reset button, and WDT reset.
- BRK can now be used to insert breakpoints for deugging.
- NMI can now be used to enter a very rudimentary debugger.

Irritatingly, the 65C02 use the same vector for BRK and IRQ, so extra code is needed to separate those tasks. This makes the IRQ-handler a bit slower than needed, such a shame.

I've done the implementation for all this in the Pico/Teensy io processor, but have had a good look at how to do it in real HW as well. The HW needed for this is surprisingly simple: some address comparators and a few latches. Definitely possible to do with electronics available in the early 80's, just the way I like it