Part A: Scan the PDP-8 manual pages on registers, memory management and interrupts in order to find all components of the CPU and MMU state that would need to be saved on behalf of one process while another is running. Document this state information, item by item.
The information that needs to be saved includes:* PC the program counter
* AC the accumulator
* MQ the accumulator
* L the link bit
* IF the current instruction field
* DF the current data field
* GT the greater-than flag (possibly useless)
* U the user-mode (privilege) bitThere are several other bits of CPU state such as IE (interrupt enable) but these seem not to be part of process states. For example, all bits involved with enabling interrupts will always indicate interrupts enabled for ready processes, since a process only disables interrupts durring an interval when it cannot be preempted and must not relinquish.
Part B: Re-scan the first 5 sections of the manual, looking for the tools you would need to use to save and restore the components of the CPU state.
We'll use DCA to store the AC as the first instruction of interrupt service routines, and CLA/TAD to restore it just before return from interrupt. AC will usually not be saved across kernel calls that block a user process; instead, it will be used for parameter passing.The interrupt or kernel call will save the PC, but then we'll have to use the sequence CLA/TAD/DCA to move the saved PC to an appropriate location in the process description record.
We'll use the sequences CLA/MQA/DCA and CLA/TAD/MQL to save and restore the MQ register.
We'll use the sequences GTF/DCA and CLA/TAD/RTF to save and restore the L, GT, U, IF and DF registers.
Part C: Suggest instruction sequences to be used for saving the process state on entry to the interrupt handler, and for restoring the process state on exit from the interrupt handler.
First, we assume that the interrupt handler has reserved locations in field zero to point to each component of the current process description record. Each component is prefixed with SAV, where the suffix names the component. The skeleton interrupt service routine would be:Location Value 0000 -0- /the return address 0001 JMP INTRPT /goto interrupt service routine INTRPT, DCA I SAVAC /save AC GTF DCA I SAVF /save flags (IF,DF,L etc) MQA DCA I SAVMQ /save MQ TAD 0 DCA I SAVPC /save PC --- body of interrupt service routine --- CLA TAD I SAVPC /get PC (but don't restore) DCA 0 TAD I SAVMQ /get MQ MQL TAD I SAVAC /get AC (but don't restore) DCA TMP TAD I SAVF /get flags RTF CLA TAD TMP /restore AC ION /enable interrputs JMP I 0 /restores PCThis code is a bit tricky! Some instructions clear AC, some don't, and the restore of the flags doesn't change the real IF register; rather, that change only occurs when the JMP is executed. Similarly, the ION instruction doesn't enable interrupts, but rather, this takes place after the immediately following instruction, the JMP.
quicksort (int a, int b) /* given a and b, bounds of the region of array to be sorted */ { if (a >= b) return; c = partition( a, b ); cobegin quicksort( a, c ); quicksort( c, b ); coend; }
Part B: Write a less naive version of Quicksort that counts the number of processes running and only creates new processes when there are CPUs to spare. When the set of CPUs is exhausted, it should run sequentially.
int processes = 1; quicksort (int a, int b) /* given a and b, bounds of the region of array to be sorted */ { if (a >= b) return; c = partition( a, b ); if (processes++ < maxprocesses) { -- atomic! cobegin quicksort( a, c ); quicksort( c, b ); coend; processes--; -- atomic! } else { quicksort( a, c ); quicksort( c, b ); } }In the above, we assumed that the ++ and -- operators are atomic, so that competing processes attempting to increment and decrement processes will not cause unexpected results.
The Problem: Design a generalized waitfor(ID) routine that awaits the termination of a particular child.
waitfor( int id ) { if (is_set_member( id, terminated )) { remove_from_set( id, terminated ); return; } for (;;) { int id1 = wait(); if (id == id1) return; add_to_set( id1, terminated ); } }In the above, we assume that we have a good implementation of the set abstraction for arbitrarily sized sets of process IDs. The size of the set terminated will never exceed the number of child processes of this process, but it is illegal to wait for any particular child more than once.