I've been trying to think about how we could extend the OISC design (maybe better described as OIC for one instruction computer -- it's not just that the "instruction set" is something that there's only one of, it's that the instruction set contains only one instruction). I experimented with character I/O in addition to just numeric I/O and made a little memory debugger shell. But what about segmentation and/or paging with an MMU? We could even use a OIC as our MMU running special code for it. We can extend it to use wider addressing, but what would a sensible memory map look like?
If we had the ability to give an offset to memory accesses with an MMU, we could create quasi-PIC with a memory map. EG:
segment 0-999 "local" code segment, always mapped to the physical address of our program (with a base-address register on the MMU), and this is where PIC would always assume to be loaded
segment 1000 special (I/O, MMU access)
segment 1000-999999 physical segments in RAM
The situation improves with a layer of paging, if we create a similar system where there are special "local" pages (set by a flag in the page table?) which can treat themselves as always located at the same address. If we have more fine-grained MMU control via paging with special pages, our quasi-PIC actually becomes somewhat reusable, and then we can have things like loadable libraries for our code (as long as you load at a page start, and set up your page table properly, with the special pages flagged). You can even remove some of the need for the special "local" pages by using a relocation table for library code to rewrite addresses when the library is loaded (although then shared libraries become slightly less useful, because every program using a shared library would have its own separate copy loaded).
Add paging and you have a full virtual memory system, but then you'd need a kernel which could be invoked by the MMU in order to handle a page fault... but more on adding interrupts in a minute.
We can also implement a permissions system on segments, but that gets way more complicated, because a lot of code has to be self-modifying (but, there might be workarounds for that with an MMU and memory-accessible program counter?) so we have to allow code to overwrite itself. And, in order to have something like interrupt-driven I/O or entrypoints/gates to kernel mode, we'd need to be really careful about how we did that. We might need an I/O controller which is able to read and write the processor state in order to make that happen (and the I/O controller could just be another OIC running special code...)
Multiprocessing on a single CPU is feasible if the kernel can use the MMU or I/O controller to read/set the processor state, but if we have multiple CPUs, the self-modifying code becomes a problem, because then we are filled with non-reentrant stuff. If we don't mind using more RAM (it's cheap nowadays), we could use Copy-on-Write for multi-CPU, at the expense of a more complicated multi-CPU MMU. But there might be another way to create true reentrant PIC.
I am wondering if all self-modifying code could be replaced with more complex code which references the program counter in very direct ways, based on the ".+N" system used in the OISC emulator's built-in compiler. The downside is that this either needs added to the CPU (which effectively makes it have more than one instruction, because you'd have a SUBTRACT-absolute-addressing and one or more SUBTRACT-relative-addressing instructions, although you could just call them "modes" of a single instruction and maybe get away with still calling it a single instruction), or the MMU (which creates a lot of complex interplay between the MMU and the exact state of the CPU, but we'd already need that anyway for the ability to do multiprocessing). This would work to create fully PIC, but I'm not entirely sure about being able to replace all self-modifying code this way. There may be special functions which could be implemented (by yet another coprocessor, as a OIC running its own special code perhaps) which would otherwise need to be done with self-modifying code... but having an extra coprocessor isn't such a bad thing, because we could get a memory-mapped FPU out of it, too, and speed up math operations greatly.
Writing an OS to handle all of that sounds rather complicated, and I'm not sure how security would work; but if you can make code which is fully PIC and fully reentrant, plus eliminate the need for self-modifying code, that's already half the battle.