I decided to return to this idea and try implementing simple preemptive multitasking by having paged RAM.
It would be very easy to do with a VIA (and I'm still planning to use VIA's timer for context switching), but I want to have a separate 8-bit "register" to hold active bank, and also to be able to read its value. I'm going to use AS6C4008, so I'll get 16x 32KB banks.
Here's what I came up with as a "cheap" read-write register:
Attachment:
rw_banking.jpg [ 195.38 KiB | Viewed 6308 times ]
I couldn't think of a way to do this with less than 2 chips (+ logic gates), but I may be wrong. Maybe there is a better way. Finally, I'm not sure if the above contraption is going to work (although it seems to when simulated). Any thoughts are welcome.
EDIT: Okay, turns out all chip selects can be handled by a single NAND.
So now I'm down to 3 chips total - '273, '244, and '00.
Attachment:
rw_banking2.jpg [ 188.93 KiB | Viewed 6304 times ]
EDIT 2: André - as you mentioned about zeropage etc, I gave it some thought and my new plan is to bank entire visible RAM and make first bank a "kernel" page (zerobank, if you will), and banks 1 to 15 will be for individual "processes".
So each process (as well as the kernel itself) will have their own zeropage, stack, and RAM.
When a system call is performed (basically, any call of kernel's public routines), kernel will then decide whether it needs to bank to its own bank or stay in program bank.
E. g. for `open`, kernel will use program's bank to read file name, but then use its own bank to store file descriptor information.
For `exec`, it will use program's bank to read file name and then use a new bank to store cmdline, initialize stack, etc.
For `memcpy`, it can stay inside program's bank.
Basically, I'm treating bank register as something like CS register in x86.
Finally, system calls will use a lot of sei/cli instructions to ensure that context switching does not happen during critical kernel operation. And most importantly, all system calls that switch from program bank to kernel bank will reactivate program bank before return to ensure that we're returning to the right PC in stack.
Another benefit is that with banking I won't need to write relocatable code.
Of course, it's all just a theory for now. And my `rom.cfg` is probably going to look like a mess with lots of overlapping segments.
I'll be happy to hear your thoughts, especially considering your impressive work on GeckOS!
EDIT 3: I think the biggest bottleneck in my case is that I'll need to waste some cycles or even an entire register to keep track of "previous" bank number in cases when a syscall needs to, say, copy some data between banks and not get lost in multiple stacks. I'm starting to think that it might be worth sharing a small segment of RAM (say, 256 bytes at the end of RAM region - $7F00-$7FFF), i. e. making this little page non-bankable. Or I could make it 1K or more and call it a "shared memory" so that it can be used for more powerful primitives like pipes or mutexes.
(My dream is to use all of this to build a POSIX-like shell for 6502 with job control, piping, and subshells.)