Virtual CPU for DOS




Outline:


Make a CPU Emulator for the DOS platform. Use HIMEM.SYS to access MBs of ram. Emulator will have multiple screens. One for the program output, and one which shows the CPU's innards (debugger). Eventually, a C-Compiler will be written for this Virtual CPU, and It will have a C-style debugger incorporated into the Emulator (another screen). The Emulator wil support Multiple-CPUs, but first I need to get a single one working! Emulator will have screens for: High-level code, machine code, a register view for each processor, and of course the ability to view all sections of ram.


About:


The 1st challenge is Designing the CPU's architecture. I have finally decided on a design. My initial endeavor is chronicled here, it has desing philosophy and two models. The present model is a mix of the previous two.


Vision:


This CPU is multi-task oriented. Since the registers are right there to see along with the code, this emulator will be great for students designing Operating Systems. Debugging and watching the code in action becomes easy! This is extremely attractive because of the planned multiple-cpu capability.



MODEL

Specs:

32-bit Registers:
D0 - D7 -- 8 Data Registers
A0 - A7 -- 8 Address Registers
PC0 -- Program Counter 0, used for Interrupts
PC1 -- Program Counter 1, used for Core OS
PC2 -- Program Counter 2, used for Tasks
        (the following registers modify memory access when PC2 is executing)
CSB -- Program Segment Base
CSS -- Program Segment Size
DSB -- Data Segment Base
DSS -- Data Segment Size



Design Theory:

PC0 and PC1 are Privileged executors since they have unrestricted access to ram. PC2's execution range is defined between the values of PSB and PCB+PSS.
While PC2 is executing, data access is confined to a range between the values in DSB and DSB+DSS.

The multiple Program Counters allow task-switching with minimal CPU complexity. Though the OS does a little more work, it might be advantageous.
We will find out!
Intel's segment based task-switching allows code to call various tasks and code segmetns (keep Privilege levels in mind). My model prevents this. There are no segments. The OS (privileged code executed by PC0 or PC1) can access anywhere in a ram range using true Effective Addresses. Application/system-module code (executed by PC2) can only access the memory block defined by 4 segment control registers. Those registers are termed 'Segment Control' because they create a usabe segment of ram for tasked-code, not because they contain a segment-selector (like intel's x86's). Before a task is run, the v-cpu must set the 4 Segment Control Registers to the appropiate values to make-available that task's code and data location in ram. Since this memory allocation (that pseudo segment which the application lives is) is not formaly defined in a segment-descriptor, it can be easily changed. I hope this to be great for threading.






Using the 3 Program Counters, any task-switch can occur without losing the execution-point at the time of the task-switch; whether the task-switch be an INT, or a normally initiated task-switch.
A forced task-switch (INT) only affects PC0 (and interrupts cannot occur during an interrupt-service). OS code executed by PC1 initiates a task-switch to PC2. Code executed by PC2 can INT, thus causing a task-switch to PC0. The handler code must decide whether to return exeution (task-switch BACK) to PC0 or PC1. When a Switch to PC2 occurs, the Timer is activated and when the Timer has run-out, control returns to the previously active Program Counter. If PC2 is INTerrupted, the timer is suspened and resumes when control Switches back to PC2. The most important thing is: PC1, the OS executor, will not lose it's place due to a Task-Switch or Interrupt. Using flags, privileged code can determine which PC was last active, but task coordination can be reliabliy handled through the OS task scheduler. Inteupt Handler code is excepted to schedule it's services with the OS. If the Handler code itself knows the scheduled task in NOT critcal, it can return control to whatever PC was last executing. But if the Handler code knows the scheduled task IS critical, it can switch striaght to PC1, where the Task Manager code will save the registers of the previous task (so the handler could better push and pop those registers if it interrupted a PC2 task) and then run the next most-critical task (determined by the Task Scheduler, which the Handler code previously 'called').












Timer Register -- not a counter
Task flag ??
Int flag -- set when interrupt occurs, only unset by Iret or ClearInt
I flag -- disablies interrupts sti/cli

There is a set of flags that aid in Multi-tasking errors. On return to OS (original task) a single instruction can check for ANY error.
Then a procedure can run to find the exact problem like:
-Memory out of bounds


some Instructions
--------------------
Switch (starts processing at pc0 or pc2) Switch Back
Save State (saves all registers)
Store / Load state (all registers to/from RAM)

Let's say:
pc2 is executing (Task flag is set), interrupt occurs, auto-switch to pc0 (Int flag is set).
If pc0 decides to return straight to pc1 (handler to OS), what should be done about the flag and regiser state of pc2's task?
is a task flag necesarry to let the OS know the task never finished?
Or should the handler let the OS-scheduler know the pc2's task SHOULD NOT be resumed (and niether should the handler).

Save State uses a CPU memory space. Is this wise, since a saved state can be easily over-written by another save state.
As in the above situation: pc0 saves state of pc2, then dicides to switch staight to pc1. pc1 will transfer that saved state into a task-state-area... but it better not become corrupted.
Save state is destructive to nest-tasks.

This processor does NOT support nested-tasks.