// SwatResident.bcpl - Companion to OS module SwatResident.asm // Copyright Xerox Corporation 1979, 1982 // Last modified April 11, 1982 4:11 PM by Boggs get "Swat.decl" get "AltoDefs.d" external [ // outgoing procedures InitResident Go; Call; UserScreen ResidentSwapOut; ResidentSwapIn ResidentSysOut; ResidentSysIn StoreVec; PrintSwateeString; DisplayState // incoming procedures MapStack; PrintFrame; IsBreak; CheckAllBreaks AddrToSym; BuildSI; TeleSwatServer VMStore; VMFetch; VMSwap; VMPrint UserPrintError; ParityError; SymPrint ReportFail; Fail; Confirm Allocate; Free; Zero; Usc Endofs; Gets; Puts; Resets ReadBlock; WriteBlock Ws; Wss; PutTemplate Enqueue; Dequeue; Unqueue // outgoing statics stackDubious // incoming statics sysZone dsp; state vm ] static [ rsQ // -> queue of RS blocks swateeDubious // true if Swatee not runable stackDubious // true if Stack unsuitable for ^c scm // -> SCM in Swatee cvPtr // free words remaining in codeVec ] compileif lenSI ne 6 then [ Error("Change size of block for SI in Go() and Call()") ] //---------------------------------------------------------------------------- structure RS: // Resident's State //---------------------------------------------------------------------------- [ link word // must be first state word lenState // 700-707 trapPC word // trapPC is disturbed by Swat mechanism scm word lenSCM // the whole SCM ] manifest lenRS = size RS/16 //---------------------------------------------------------------------------- let InitResident() be [ rsQ = Allocate(sysZone, 2); rsQ!0 = 0 ] //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- and Go(addr) = valof //---------------------------------------------------------------------------- // Returns a vector of code which resumes execution of the user program, // with the first instruction executed being that at addr. // If addr is broken, we simulate the missing instruction and resume // execution at its next address, otherwise we simulate a nop at // addr-1 and resume execution at addr. [ let cv = table [ 9 //number of words that follow 4407b //jsr .+7 0 //space for SI -- *** known to be 6 words long *** 0 0 0 0 0 171000b //mov 3 2 64767b //jsrii .-11 (SimIns) ] test IsBreak(addr) % (VMFetch(addr) & 177400b) eq 77400b ifso [ BuildSI(cv+2, VMFetch(addr)) //simulate broken instr VMStore(userPC, addr) ] ifnot [ BuildSI(cv+2, 101000b) //yes. 101000b = Mov 0 0 VMStore(userPC, addr-1) //simMA increments userPC ] resultis cv ] //---------------------------------------------------------------------------- and UserScreen() = valof //---------------------------------------------------------------------------- // Display the user screen [ SaveResidentState() resultis table [ 7 //0 number of words that follow 24406B //1 lda 1 .+6 22404B //2 lda 0 @.+4 107414B //3 and# 0 1 szr 776B //4 jmp .-2 screenTrap //5 swat 177037B //6 kbdAd+3 4 //7 swat key mask ] ] //---------------------------------------------------------------------------- and Call(numArgs, argVec) = valof //---------------------------------------------------------------------------- // Note that the following only works for BCPL procedures. // Compose a call by filling AC0, AC1, perhaps putting entries in AC2!3. // This is a little dangerous because a poor soul might get a trap before // GetFrame had fully finished the frame, and given another procedure call. // The documentation says "expect occasional anomolies after ^C". [ if stackDubious then unless Confirm("The stack is dubious. Do you still want to call? ") do Fail() SaveResidentState() let ac2 = VMFetch(userAC2) let procArgs = numArgs-1 //number of args to pass switchon procArgs into [ default: [ let r = StoreVec(argVec+3, procArgs-2) argVec!3 = r-ac2-3 ] case 3: VMStore(ac2+3, argVec!3) case 2: VMStore(userAC1, argVec!2) case 1: VMStore(userAC0, argVec!1) case 0: endcase ] let cv = table [ 13 //number of words that follow 4407b //jsr .+7 0 //space for SI -- *** known to be 6 words long *** 0 0 0 0 0 171000b //mov 3 2 64767b //jsrii .-11 (SimIns) 6403b //jsr @.+3 0 //numArgs callReturnTrap 0 //procedure address ] BuildSI(cv+2, 101000b) //simulate a Mov 0 0 VMStore(userPC, scm+codeVectorOffset+9-1) //simMA increments it cv!11 = procArgs cv!13 = argVec!0 resultis cv ] //---------------------------------------------------------------------------- and ResidentSwapIn() = valof //---------------------------------------------------------------------------- // Returns true iff ok to go on reading from command file [ let ok = true swateeDubious = false //assume Swatee is ok Ws("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*n") test vm>>VM.type eq vmTypeDisk ifso if VMFetch(dumperFlg) then [ Ws("The Swatee was created by a Dumper-boot.*n") Ws("Page 0 is invalid and the display is off.*n") swateeDubious = true ok = false ] ifnot swateeDubious = true scm = VMFetch(567B) //Swat Communication table base address if VMFetch(scm+versionOffset) ls residentVersion then [ Ws("Swat's resident is lost or incompatible.*n") swateeDubious = true ok = false ] let pc = VMFetch(userPC) //where to resume user let trapAddr = VMFetch(trapPC)-1 //address of trapping instruction let trapInstr = VMFetch(trapAddr) //trapping instruction CheckAllBreaks(trapAddr) switchon trapInstr into [ default: case generalTrap: //general "Get me to swat" instruction [ unless IsBreak(trapAddr) do //CheckAllBreaks already did it [ PutTemplate(dsp, "Trap instruction $UO at $P*N", trapInstr, AddrToSym, pc) ok = false ] endcase ] case parityTrap: //a parity error happened [ ParityError(pc, VMFetch) endcase ] case callSwatTrap: //user called CallSwat() case teleSwatTrap: [ ok = false PutTemplate(dsp, "CallSwat from $P*N", AddrToSym, VMFetch(userAC3)-1) let numArgs = VMFetch(VMFetch(userAC3)) if numArgs gr 0 then if PrintSwateeString(VMFetch(userAC0)) then Puts(dsp, $*N) if numArgs gr 1 then if PrintSwateeString(VMFetch(userAC1)) then Puts(dsp, $*N) if trapInstr eq teleSwatTrap then TeleSwatServer() endcase ] case printErrorTrap: //print an error message from a file [ UserPrintError() endcase ] case screenTrap: //return from UserScreen ^U [ RestoreResidentState() endcase ] case callReturnTrap: //return from proc called by Swat [ let value = VMFetch(userAC0) PutTemplate(dsp, "Call returns $UOb = $D.*n", value, value) RestoreResidentState() endcase ] case kbdTrap: // case abortTrap: // (should never be seen in Swat) [ PutTemplate(dsp, "Keyboard trap at $P*N", AddrToSym, pc) ok = false endcase ] ] cvPtr = lenCodeVector-1 //the code vector is empty DisplayState() resultis ok ] //---------------------------------------------------------------------------- and ResidentSwapOut(sm) be //---------------------------------------------------------------------------- [ if swateeDubious then unless Confirm("The Swatee is dubious. Do you still want to swap? ") do Fail() // signal Swat resident to jump into the code vector VMStore(scm+resumeOffset, -1) // put the code in the code vector if sm!0 gr cvPtr then ReportFail("Code vector overflow") for i = 1 to sm!0 do VMStore(scm+codeVectorOffset+i-1, sm!i) ] //---------------------------------------------------------------------------- and DisplayState() be //---------------------------------------------------------------------------- // Display state of machine from user's viewpoint [ Resets(state) PutTemplate(state, "AC0:$UO AC1:$UO AC2:$UO AC3:$UO CRY:$UO", VMFetch(userAC0), VMFetch(userAC1), VMFetch(userAC2), VMFetch(userAC3), VMFetch(userCry)? 1, 0) PutTemplate(state, " PC:$P", AddrToSym, VMFetch(userPC)) PutTemplate(state, " INT:$S", VMFetch(userInt)? "ON", "OFF") PutTemplate(state, "*N*N$P $P*N*N", VMPrint, false, SymPrint, false) stackDubious = true //assume the worst MapStack(2, PrintFrame, state, VMFetch(userAC2)) ] //---------------------------------------------------------------------------- and PrintSwateeString(addr, Fetch; numargs na) = valof //---------------------------------------------------------------------------- // See if addr could be a pointer to a string in Swatee and print it if so. // return true if anything was printed [ if na ls 2 then Fetch = VMFetch if Usc(addr, 176777B) gr 0 resultis false //can't be in IO area let string = vec 127 string!0 = Fetch(addr) for i = 1 to string>>String.length rshift 1 do string!i = Fetch(addr+i) for i = 1 to string>>String.length do if string>>String.char^i ge 200b resultis false Ws(string) resultis true ] //---------------------------------------------------------------------------- and StoreVec(vector, length) = valof //---------------------------------------------------------------------------- // Store vector in the code vector, and return the address of its first word // Vectors are packed in from the end working toward the front. // Code is packed in from the front working toward the end. // cvPtr is the index of the first free word, working back from the end. [ if swateeDubious then ReportFail("Can't store vector -- swatee is dubious.") test cvPtr-length ls 0 ifso ReportFail("Code vector overflow") ifnot cvPtr = cvPtr-length let cv = scm + codeVectorOffset + cvPtr +1 for i = 0 to length-1 do VMStore(cv+i, vector!i) resultis cv ] //---------------------------------------------------------------------------- and ResidentSysOut(sysOut) be //---------------------------------------------------------------------------- [ // count up the number of state frames let count, rs = 0, rsQ!0 while rs ne 0 do [ count = count +1 rs = rs>>RS.link ] Puts(sysOut, count) // write them out rs = rsQ!0; while rs ne 0 do [ WriteBlock(sysOut, rs, lenRS) rs = rs>>RS.link ] ] //---------------------------------------------------------------------------- and ResidentSysIn(sysIn) be //---------------------------------------------------------------------------- [ swateeDubious = true // reset resident state to empty while rsQ!0 ne 0 do Free(sysZone, Dequeue(rsQ)) for i = 1 to (Endofs(sysIn)? 0, Gets(sysIn)) do [ let rs = Allocate(sysZone, lenRS) ReadBlock(sysIn, rs, lenRS) Enqueue(rsQ, rs) ] ] //---------------------------------------------------------------------------- and SaveResidentState() be //---------------------------------------------------------------------------- // note that we manage this queue LIFO rather than FIFO [ let rs = Allocate(sysZone, lenRS) let state = lv rs>>RS.state for i = 0 to lenState-1 do state!i = VMFetch(userPC+i) rs>>RS.trapPC = VMFetch(trapPC) let scm = lv rs>>RS.scm for i = 0 to lenSCM-1 do scm!i = VMFetch(scm+i) Enqueue(rsQ, rs) //add to tail of queue ] //---------------------------------------------------------------------------- and RestoreResidentState() be //---------------------------------------------------------------------------- // note that we manage this queue LIFO rather than FIFO [ if rsQ!0 eq 0 then ReportFail("Registers lost") let rs = rsQ!1; Unqueue(rsQ, rs) //remove most recently queued item (TAIL) let state = lv rs>>RS.state for i = 0 to lenState-1 do VMStore(userPC+i, state!i) VMStore(trapPC, rs>>RS.trapPC) let scm = lv rs>>RS.scm for i = 0 to lenSCM-1 do VMStore(scm+i, scm!i) Free(sysZone, rs) ]