; SwatResident.asm -- OS-resident part of Swat
;	Matching file in Swat is SwatResident.bcpl, which has detailed
;	 knowledge about everything in this file.
; Copyright Xerox Corporation 1979
; Last modified May 5, 1979  8:09 PM by Boggs

	.titl SwatRes

; Keyboard commands:
;	<Left-Shift><Swat>		Abort current subsystem
;	<Left-Shift><Control><Swat>	Invoke Swat debugger

; Trap instructions:
	generalTrap = 77400	; General "get me to Swat" instruction
	parityTrap = 77401	; PC -> parity error info block
	callSwatTrap = 77402	; CallSwat(string [], string[])
	errorTrap = 77403	; AC0 -> error file name
				; AC1 -> table [errcode,p1,p2,...]
	callReturnTrap = 77404	; Return from procedure called from Swat
	screenTrap = 77405	; Return from UserScreen ↑U
	breakPtTrap = 77406	; Normal breakpoint
	mpbpTrap = 77407	; Multiple proceed breakpoint
	kbdTrap = 77410		; <Left-Shift><Control><Swat>
	abortTrap = 77411	; <Left-Shift><Swat>
	teleSwatTrap = 77412	; Become a TeleSwat Server

; Outgoing procedures
.ent CallSwat
.ent SwatTrap
.ent SwatInterrupt

; Incoming procedures
.bext OsFinish
.bext UpdateTimer
.bext InLd, OutLd

; Outgoing statics
.bext lvAbortFlag
.bext lvSwatContextProc

	.srel

CallSwat:		.CallSwat
SwatTrap:		.SwatTrap
SwatInterrupt:		.SwatInterrupt

lvAbortFlag:		abortOK		; 0 => Permit <shift><swat> aborts
lvSwatContextProc:	swatCP		; -> Swat context switching procedure

SimIns:			.SimIns

	.nrel

			; structure SI:	// Simulated Instruction
			; [
siInstr = 0		; instr word	// simulating instruction
siDoIt = 1		; doIt word	// 0: default, 1: jmp, 2: bri
siSim = 2		; sim word	// simulation routine
siDisp = 3		; disp word	// sign extended displacement
siBase = 4		; baseReg word	// 0 or -> trapAdd, trapAC2, or AC3
siInd = 5		; ind word	// number of times to indirect
			; ]
lenSI = 6		; manifest lenSI = size SI/16

			; structure MPBP: // Multiple Proceed Break Point
			; [
			; @SI
mpbpAddr = lenSI	; mpbpAddr word	// address for this MPBP
mpbpCnt = lenSI+1	; mpbpCnt word	// proceed count
			; ]
lenMPBP = lenSI+2	; manifest lenMPBP = size MPBP/16
numMPBP = 2

; INVARIANT: trapVector!swatTrapNo = 567B -> this table:

;-------------- S W A T   C O M M U N I C A T I O N   T A B L E --------------
.SwatTrap:	jmp TrapHandler	; Entry point for traps.
		25.		; Swat Version number
resumeFlag:	.blk 1		; Non-zero => jmp to codeVector on InLd
temp:		.blk 1		; Padding to make FPs be in published place
swateeFP:	-1		; FP for Swatee -- contains REAL disk address
		-1
		-1
		-1
		-1
swatFP:		-1		; FP for swat -- contains REAL disk address
		-1
		-1
		-1
		-1
MPBPTable:	.blk numMPBP*lenMPBP
		SimIns		; Must be in codeVector-1
codeVector:	.blk 35.	; Contents known only to Swat
;-----------------------------------------------------------------------------


swatCP:		.blk 1		; zero or -> Swat context procedure

.WW:		452		; -> Wakeups waiting
.Active:	453		; -> Active interrupt channels
KBLK = 521
.KBLK:		KBLK		; -> Disk command chain head
TrapPC = 527
IntPC = 500

.SCM:		SwatTrap	; -> -> Swat Comm Table

nMPBP1:		numMPBP+1
lMPBP:		lenMPBP
oMPBP:		MPBPTable-.SwatTrap

.CallSwat:
	callSwatTrap		; All you do is just...
	jmp 1,3

; All traps come here
TrapHandler:			; Save machine state
	sta 2 @.userAC2
	dirs
	 mkzero 2 2 skp		; interrupts were off *** don't clobber carry
	 mkminusone 2 2		; interrupts were on  *** don't clobber carry
	sta 2 @.userInt
	lda 2 .KBLK
	sta 0 userAC0-KBLK,2
	sta 1 userAC1-KBLK,2
	sta 3 userAC3-KBLK,2
	subcl 0 0
	sta 0 userCry-KBLK,2	; carry bit

; Back up PC to point at trapping instruction and examine it.
	lda 3 TrapPC-KBLK,2
	mkminusone 1 1
	add 1 3
	sta 3 @.userPC		; -> trapping instruction
	lda 0 0,3
	lda 1 .abortTrap	; 77411
	sub 1 0 szr		; abort trap (77411)?
	 inc 0 1 snr		; keyboard trap (77410)?
	 jmp trap7		; yes, one of those
	inc 1 1 szr		; multiple-proceed breakpoint trap (77407)?
	 jmp trap3		; no, not a special kind of trap

	lda 2 @.SCM		; We hit a multiple proceed break point
	lda 0 nMPBP1		; number of mpbp's +1
	sta 0 temp
	lda 1 oMPBP		; offset of first mpbp
trap5:	dsz temp		; checked them all?
	 add 1 2 skp		; no, -> next mpbp
	 jmp trap3		; hmmm. not in the table
	lda 1 lMPBP
	lda 0 mpbpAddr,2
	se 0 3			; is this the mpbp we hit?
	 jmp trap5		; no
	dsz mpbpCnt,2		; has it expired?
	 jmp .SimIns		; Simulate broken instr and plunge on
trap3:	mkminusone 0 0		; not an abort trap
	jmp trap6		; Time to call Swat

trap7:	lda 1 IntPC-KBLK,2	; kbd & abort traps are special
	sta 1 @.userPC		; PC ← interrupt PC (from SwatInterrupt)
	dsz @.userInt		; Int ← true
				; fall into trap6

; Prepare to OutLd this core image onto Swatee
; ac0 = 0 if aborting, -1 otherwise
trap6:	sta 0 abortFlag		; abortFlag ← 0 if abort trap, -1 otherwise
	mkzero 0 0		; We are about to swap out
	jsr CallSCP		; Call Swat context switching procedure.
				; It skips the next word.
swatOffset:	swatFP-.SwatTrap	; ** PUBLISHED VALUE = 9. **

	lda 0 @.KBLK		; Wait for the disk controller to stop
	sz 0 0
	 jmp .-2
	incol 0 0		; 3 = etherResetCommand
	sio			; Turn off the Ethernet interface

	jsr FlushWW		; Save all pending interrupts in WW

	lda 3 @.SCM
	sta 1 resumeFlag-.SwatTrap,3 ; 0 => simply resume
	lda 0 swateeOffset
	add 3 0			; AC0 -> FP for Swatee
	jsrii .+1		; OutLd onto Swatee. ac1=0: don't want message
	 OutLd
	sz 0 0			; what did we just do?
	 jmp trap8		; We are waking up via InLd
	isz abortFlag		; We just OutLded.  Are we aborting?
	 jmp trap9		; yes

	lda 1 @.SCM		; No, entering Swat. Message is the SCM table
	lda 0 swatOffset
	add 1 0			; AC0 -> FP for Swat
	jsrii .+1		; InLd Swat
	 InLd
; Control never returns here.

trap9:	eir			; a Mesa bug depends on this order of instrs
	lda 2 @.userAC2		; stack for finishing
	mkone 0 0		; fcAbort
	jsrii .+1
	 OsFinish
; Control never returns here.

; Here when we are resumed by InLd
trap8:	lda 2 @.WW
	jsr FlushWW		; Flush any interrupts pending now
	sta 2 @.WW		; Restore saved interrupt system state

	mkminusone 0 0		; We have just swapped in
	jsr CallSCP		; Call Swat context switching procedure.
				; It skips the next word.
swateeOffset:	swateeFP-.SwatTrap	; ** PUBLISHED VALUE = 4. **

	lda 3 @.SCM
	lda 0 resumeFlag-.SwatTrap,3
	mov 0 2 szr		; What shall we do?
	 jmp codeVector-.SwatTrap,3 ; goto codeVector
	jmp useBRI		; Resume user code
				; ac2=0 => no instruction to simulate

; Routine to flush pending interrupts into WW
FlushWW:
	lda 0 @.Active
	mkzero 1 1
	sta 1 @.Active
	eir
	dir
	sta 0 @.Active
	jmp 0,3

; Call Swat context-switching procedure, if present, with argument in ac0.
; Always returns to caller+2.  Note that the Swat context-switching procedure
; cannot be a BCPL procedure and cannot assume anything about the stack.
; Therefore it cannot care what is in caller+1 (normally # of args).
CallSCP:
	lda 2 swatCP
	sz 2 2
	 jmp 0,2
	jmp 1,3

.userPC: 	700		; -> trapping instruction
userAC0 = 701			; registers at time of trap
userAC1 = 702
userAC2 = 703
.userAC2:	userAC2
userAC3 = 704
.userAC3:	userAC3
userCry = 705
; 706 is dumperFlg
.userInt:	707		; 1 => interrupts on at trap, 0 => off

.abortTrap:	abortTrap

; Come here to simulate an instruction and resume user code.
; AC2 -> SI structure.  Compute effective address, if any.
; Most of the work has already been done by Swat.
.SimIns: lda 3 siBase,2		; 0 or -> saved base register (pc, ac2, ac3)
	sz 3 3
	 lda 3 0,3
	lda 0 siDisp,2		; displacement, sign-extended if appropriate
	add 3 0
	lda 1 siInd,2		; number of indirections
	com 1 1 skp
ea2:	 lda 0 @effAdd
	sta 0 effAdd
	inc 1 1 szr
	 jmp ea2

; Dispatch on instruction class.  Dispatch indices known to Swat
	lda 1 siSim,2
	jsr Dispatch

	jmp SimMA		; 0
	jmp SimJmp		; 1
	jmp SimJsr		; 2
	jmp SimDirs		; 3
	lda 0 @.IntPC		; 4 bri
	jmp SimJmp

SimJsr:	lda 1 @.userPC		; jsr, jsrii, jsris
	inc 1 1
	sta 1 @.userAC3
SimJmp:	sta 0 @.userPC		; jmp
	jmp DoIt

SimDirs:isz @.userPC		; dirs
	lda 0 @.userInt
	sz 0 0
SimMA:	 isz @.userPC		; MRI, ALU, and NoPar except dirs and bri
	; Fall into DoIt

; Execute a given instruction, then resume user code at a given address.
DoIt:	lda 0 @.userInt		; for test in default handling case
	lda 1 siDoIt,2
	jsr Dispatch

; Default is to use whatever transfer instruction is right to restore
;  the interrupt system state to that saved in userInt.
	snz 0 0			; 0 -- dispatch indices known to Swat
	 jmp useJmp		; 1

; simulate the instruction and then use a BRI to resume user code.
useBRI:	lda 1 .bri		; 2
	lda 3 .IntPC
Exit:	sta 1 exIns		; instruction to exit with
	sta 3 pResume		; -> resume pc
	sz 2 2			; have an SI? if not, no inst to simulate
	 lda 1 siInstr,2	; get inst to simulate
	sta 1 ins
	lda 0 @.userPC
	sta 0 @pResume
	lda 3 .userAC3
	lda 0 userCry-userAC3,3	; carry
	movr 0 0
	lda 0 userAC0-userAC3,3
	lda 1 userAC1-userAC3,3
	lda 2 userAC2-userAC3,3
	lda 3 userAC3-userAC3,3
abortFlag:			; also temp for SwatTrap
ins:	.blk 1			; instruction being simulated
exIns:	.blk 1			; exit instruction: bri or jmp @resume
	isz @pResume		; simulating instruction skipped
	jmp .-2
pResume: .blk 1			; 500 or lv resume
effAdd:	.blk 1			; effective address -- must be at ins+5

; simulate the instruction and then use a JMP to resume user code.
useJmp:	lda 1 .jmpres		; jmp @resume
	jsr Exit		; make ac3 -> resume

resume:	.blk 1			; resume address used if interrupts off
.jmpres: jmp @resume-exIns 1
.IntPC:	IntPC

; Dispatch into table pointed to by ac3 using index in ac1
Dispatch: add 1 3
	jmp 0,3

.SwatInterrupt:			; 16.666 ms vertical field interrupt
	sta 0 intAC0		; save state *** except ac2 ***
	sta 1 intAC1
	sta 3 intAC3
	subcl 0 0
	sta 0 intCry

	lda 3 .bri		; normal interrupt dismiss method
	lda 0 @.KBAD3
	lda 1 c4
	and 1 0
	lda 1 keyState
	sta 0 keyState
	snz 0 0			; <swat> key down?
	snz 1 1			; yes, heavy fingers?
	 jmp Dismiss		; no or yes, ignore
	lda 0 @.KBAD2
	cycle 9.
	movzl# 0 0 szc		; <left-shift> key down too?
	 jmp Dismiss		; no

; For keyboard trap (<shift><swat> or <ctrl><shift><swat>),
; make it appear as if user executed a trap instruction.
; When the TrapHandler sees this it completes the transformation:
;	.userPC ← @IntPC
;	.userInt ← true

	lda 1 abortOK
	snz 1 1			; aborting allowed?
	 lda 3 .abortTrap	; yes, set keyboard trap
	cycle 11.
	movzl# 0 0 snc		; <control> key down too?
	 lda 3 .kbdTrap		; yes

Dismiss: sta 3 SIRet
	jsrii .+1
	 UpdateTimer
	lda 0 intCry		; restore state
	movr 0 0
	lda 0 intAC0
	lda 1 intAC1
	lda 3 intAC3
SIRet:	.blk 1			; patched to bri, 77410, or 77411

intAC0:		.blk 1
intAC1:		.blk 1
intAC3:		.blk 1
intCry:		.blk 1

.bri:		bri		; put in SIRet to dismiss interrupt
.kbdTrap:	kbdTrap	
abortOK:	.blk 1		; 0 if aborts are allowed

keyState:	.blk 1		; non-zero if <swat> key was up 16 ms ago
c4:		4		; <Swat> key mask
.KBAD2:		177036		; kbd word containing <left-shift> & <ctrl>
.KBAD3:		177037		; kbd word containing <Swat>

	.end