// 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: //<Left-Shift><Control><Swat>
case abortTrap: //<Left-Shift><Swat> (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)
]