TITLE "mp6.a by Douglas W. Jones, 4/22/2015" ; Virtual Hawk instruction-trap handler ; Overlay this over the Sparrowhawk monitor ; to support the full Hawk instruction set ; (except that it offers no coprocessor support). ; sections marked with ==== are missing STRICTSPARROW = 1 USE "sparrowhawk.h" USE "ascii.h" USE "monitor.h" PC = R0 ; added for clarity ; ---------------------------------- ; static memory allocation support MACRO ALLOC =n . = . + n ENDMAC ; ---------------------------------- ; machine configuration ; trap vector INSTRTRAP = #20 ; ---------------------------------- ; trap handler framework ; the following code intercepts all traps and interrupts, ; saves registers in the trap save area. Note that it is ; essential that trap service routines that intend to return ; to the user not cause traps themselves. ; save area structure (displacements from start of area) LC = . . = 0 PSWSV: ALLOC 4 TMASV: ALLOC 4 PCSV: ALLOC 4 R1SV: ALLOC 4 R2SV: ALLOC 4 R3SV: ALLOC 4 R4SV: ALLOC 4 R5SV: ALLOC 4 R6SV: ALLOC 4 R7SV: ALLOC 4 R8SV: ALLOC 4 R9SV: ALLOC 4 RASV: ALLOC 4 RBSV: ALLOC 4 RCSV: ALLOC 4 RDSV: ALLOC 4 RESV: ALLOC 4 RFSV: ALLOC 4 SVSTACK:ALLOC 4 ; minimum stack needed by PUTHEX SVSIZE: . = LC COMMON SVINSTR,SVSIZE ; trap save area for instruction trap SUBTITLE "Trap vector code" ;------------------------ . = INSTRTRAP CPUSET R2,TSV ; 2 LIS R2,INSTCON >> 8 ; 2 ORIS R2,INSTCON & #FF; 2 JSRS R2,R2 ; 2 -- tricky code W SVINSTR+R1SV ; 4 ; 4 bytes unused SUBTITLE "Instruction Trap Continuation" ;------------------------ . = #500 ; address set by inspecting end of sparrowmon.l ; addresses of monitor routines DSPINIP:W DSPINI PUTSP: W PUTS PUTHEXP:W PUTHEX INSTMSG:ASCII "Instruction Trap. Trap PC = #",0 MSGXX: ASCII " ",0 INSTCON: ; assumes TSV = oldR2 ; R2 points to pointer to savearea.r1sv ; all other registers still hold user values ; including PSW, TMA and TPC LOADS R2,R2 ; -- get save area pointer STORES R1,R2 ; savearea.r1sv = oldR1 ADJUST R2,PLUS8 STORES R3,R2 ; savearea.r3sv = oldR3 ADJUST R2,PLUS4 STORES R4,R2 ; savearea.r4sv = oldR4 ADJUST R2,PLUS4 STORES R5,R2 ; savearea.r5sv = oldR5 ADJUST R2,PLUS4 STORES R6,R2 ; savearea.r6sv = oldR6 ADJUST R2,PLUS4 STORES R7,R2 ; savearea.r7sv = oldR7 ; here: oldR1, oldR3 - oldR7 are properly saved ; R2 points to savearea.r7sv ; next: save TSV (oldR2), PSW, TMA and TPC LIS R1,PSWSV-R7SV PLUS R1,R2 CPUGET R3,PSW STORES R3,R1 ; savearea.pswsv = oldPSW ADJUST R1,PLUS4 CPUGET R3,TMA STORES R3,R1 ; savearea.tmasv = TMA ADJUST R1,PLUS4 CPUGET R3,TPC STORES R3,R1 ; savearea.pcsv = TPC = oldPC ADJUST R1,PLUS8 CPUGET R3,TSV STORES R3,R1 ; savearea.r2sv = TSV = oldR2 ; here: (R1-R7, PSW, TMA and TPC) all saved in savearea ; R2 -- still points to savearea.r7sv ; next: resume saving registers ADJUST R2,PLUS4 STORES R8,R2 ; savearea.r8sv = oldR8 ADJUST R2,PLUS4 STORES R9,R2 ; savearea.r9sv = oldR9 ADJUST R2,PLUS4 STORES R10,R2 ; savearea.r10sv = oldR10 ADJUST R2,PLUS4 STORES R11,R2 ; savearea.r11sv = oldR11 ADJUST R2,PLUS4 STORES R12,R2 ; savearea.r12sv = oldR12 ADJUST R2,PLUS4 STORES R13,R2 ; savearea.r13sv = oldR13 ADJUST R2,PLUS4 STORES R14,R2 ; savearea.r14sv = oldR14 ADJUST R2,PLUS4 STORES R15,R2 ; savearea.r15sv = oldR15 ; here: (R1-R15, PSW, TMA and TPC) all saved in savearea ; R2 -- points to savearea.r15sv ; next: decode instruction that caused trap ; uses: R3 = points to savearea.pc ; R4 = pc -- user's ; R5 = M[pc] -- fullword ; R6 = ir = M[pc] -- halfword ; R7 = op field of IR (bits 7 to 4) ; R8 = dst field of IR (bits 3 to 0) LIS R3,PCSV-RFSV PLUS R3,R2 ; -- point to user's pc LOADS R4,R3 ; -- get user's pc LOADS R5,R4 ; -- fetch instruction word EXTH R6,R5,R4 ; ir = M[tpc] EXTB R7,R6,R0 ; -- get first byte MOVE R8,R7 SR R7,4 ; op = ir & #00F0 >> 4 TRUNC R8,4 ; dst = ir & #000F BTRUNC R7,4 ; select (op) in { -- note, CC's unchanged! BR INSTBR ; -- opcode 0000 BR INSTTWO ; -- opcode 0001 BR INSTERR ; -- never 0010 BR INSTERR ; -- never 0011 BR INSTERR ; -- never 0100 BR INSTERR ; -- never 0101 BR INSTERR ; -- never 0110 BR INSTERR ; -- never 0111 BR INSTERR ; -- never 1000 BR INSTERR ; -- never 1001 BR INSTERR ; -- never 1010 BR INSTERR ; -- never 1011 BR INSTERR ; -- never 1100 BR INSTERR ; -- never 1101 BR INSTLIL ; -- opcode 1110 BR INSTMEM ; -- opcode 1111 INSTERR: ; case 0010 to 1101 -- should never happen INSTBR: ; case 0000 -- dst = 1000 is undefined INSTTWO: ; case 0001 { -- op1 = 1000 is undefined ; -- op1 = 001x is COGET/COSET ADDSI R2,4 ; -- setup for monitor calls VVV= DSPINIP-(.+6) LIS R1,VVV >> 8 ORIS R1,VVV & #FF PLUS R1,PC LOADS R1,R1 JSRS R1,R1 ; (columns, lines) = dspini() -- uses R3-4 VVV= INSTMSG-(.+6) LIS R3,VVV >> 8 ORIS R3,VVV & #FF PLUS R3,PC ; -- parameter instmsg VVV= PUTSP-(.+6) LIS R1,VVV >> 8 ORIS R1,VVV & #FF PLUS R1,PC JSRS R1,R1 ; puts( "Inst trap. PC=#" ) -- uses R3-7 LIS R4,PCSV-SVSTACK ; -- here, R2 points to SVSTACK field PLUS R4,R2 LOADS R3,R4 ; -- parameter pcsv STORES R0,R4 ; pc = 0 -- force restart on return VVV= PUTHEXP-(.+6) LIS R1,VVV >> 8 ORIS R1,VVV & #FF PLUS R1,PC JSRS R1,R1 ; puthex( pcsv ) -- uses R3-7, 1 word stack ADDSI R2,-4 ; -- make R2 point back to savearea.r15sv BR INSTDONN ; } INSTLIL: ; case 1110 { -- LIL instruction ; here: R3 = points to savearea.pc ; R4 = pc -- user's ; R5 = M[pc] -- fullword ; R6 = ir = M[pc] -- halfword ; R7 = op field of IR (bits 7 to 4) ; R8 = dst field of IR (bits 3 to 0) BZS INSTERR ; if (dst = 0) go fail MOVE R7,R6 SR R7,8 ; acc = ir bits 8 to 15 -- part 1 of const ADDSI R4,2 ; pc = pc + 2 LOADS R5,R4 EXTH R6,R5,R4 ; ir = M[pc] -- part 2 of const ADDSI R4,2 ; pc = pc + 2 SL R6,16 SR R6,8 OR R7,R6 ; acc = acc | sxt(ir << 8) ADDSL R8,R3,2 ; -- compute address of saved r[dst] STORES R7,R8 ; r[dst] = acc STORES R4,R3 ; -- save updated pc to finish job BR INSTDONN ; } INSTMEM: ; case 1111 { -- sparrowhawk long mem instrs ; here: R3 = points to savearea.pc ; R4 = pc -- user's ; R5 = M[pc] -- fullword ; R6 = ir = M[pc] -- halfword ; R7 = op field of IR, no longer needed ; R8 = dst field of IR (bits 3 to 0) ; uses: R7 = op2 field of IR (bits 12 to 15) MOVE R7,R6 SR R7,12 BTRUNC R7,4 ; select (op2) in { BR INSTERR ; -- never 0000 (undefined instrs) BR INSTERR ; -- never 0001 BR INSTORE ; -- opcode 0010 BR INSTJSR ; -- opcode 0011 BR INSTLODC ; -- opcode 0100 BR INSTLOAD ; -- opcode 0101 BR INSTLEAC ; -- opcode 0110 BR INSTLEA ; -- opcode 0111 BR INSTERR ; -- never 1000 (short instrs should BR INSTERR ; -- never 1001 never cause traps!) BR INSTERR ; -- never 1010 BR INSTERR ; -- never 1011 BR INSTERR ; -- never 1100 BR INSTERR ; -- never 1101 BR INSTLIL ; -- opcode 1110 BR INSTMEM ; -- opcode 1111 INSTDONN: BR INSTDONE ; -- relay to solve branch too far INSTERRR: BZS INSTERR ; -- relay to solve branch too far INSTORE: ; case 0010 { -- STORE ; here: R3 = points to savearea.pc ; R4 = pc -- user's ; R5 = M[pc] -- fullword (update after PC increment) ; R6 = ir = M[pc] -- halfword ; R7 = op2 field of IR (no longer needed) ; R8 = dst field of IR (bits 3 to 0) ; uses R7 = address of r[x] -- and then value of r[x] ; R9 = disp (displacement) -- becomes ea MOVE R7,R6 SR R7,8 TRUNC R7,4 ; x = ir & #0F00 >> 8 ADDSL R7,R3,2 ; -- address of r[x] ADDSI R4,2 ; pc = pc + 2 -- user's PC LOADS R5,R4 EXTH R9,R5,R4 ; disp = M[pc] -- displacement SXT R9,16 ADDSI R4,2 ; pc = pc + 2 STORES R4,R3 ; -- save updated pc before getting r[x] LOADS R7,R7 ; -- value of r[x] ADD R9,R7,R9 ; ea = r[x] + disp TESTR R8 BZS INSTSR0 ; if (dst != 0) { -- store register ADDSL R8,R3,2 ; -- address of r[dst] LOADS R8,R8 ; dst = r[dst] INSTSR0: ; } -- else it is store constant zero STORES R8,R9 ; M[ea] = dst -- r[dst] or zero BR INSTDONE ; } INSTJSR: ; case 0011 { -- JSR ; here: R3 = points to savearea.pc ; R4 = pc -- user's ; R5 = M[pc] -- fullword (update after PC increment) ; R6 = ir = M[pc] -- halfword ; R7 = op2 field of IR (no longer needed) ; R8 = dst field of IR (bits 3 to 0) ; uses R7 = address of r[x] -- and then value of r[x] ; R9 = disp (displacement) -- becomes ea MOVE R7,R6 SR R7,8 TRUNC R7,4 ; x = ir & #0F00 >> 8 ADDSL R7,R3,2 ; -- address of r[x] ADDSI R4,2 ; pc = pc + 2 -- user's PC LOADS R5,R4 EXTH R9,R5,R4 ; disp = M[pc] -- displacement SXT R9,16 ADDSI R4,2 ; pc = pc + 2 STORES R4,R3 ; -- save updated pc before getting r[x] LOADS R7,R7 ; -- value of r[x] ADD R9,R7,R9 ; ea = r[x] + disp ADDSL R8,R3,2 ; -- address of r[dst] STORES R4,R8 ; r[dst] = pc -- uses fact that R0 = PC STORES R9,R3 ; pc = ea BR INSTDONE ; } INSTLODC: ; case 0100 { -- LOADCC ; here: R3 = points to savearea.pc ; R4 = pc -- user's ; R5 = M[pc] -- fullword (update after PC increment) ; R6 = ir = M[pc] -- halfword ; R7 = op2 field of IR (no longer needed) ; R8 = dst field of IR (bits 3 to 0) ; uses R7 = address of r[x] -- and then value of r[x] ; R9 = disp (displacement) -- becomes ea and then m[ea] (data) ; R10 = used in cond code setting MOVE R7,R6 SR R7,8 TRUNC R7,4 ; x = ir & #0F00 >> 8 ADDSL R7,R3,2 ; -- address of r[x] ADDSI R4,2 ; pc = pc + 2 -- user's PC LOADS R5,R4 EXTH R9,R5,R4 ; disp = M[pc] -- displacement SXT R9,16 ADDSI R4,2 ; pc = pc + 2 STORES R4,R3 ; -- save updated pc before getting r[x] LOADS R7,R7 ; -- value of r[x] ADD R9,R7,R9 ; ea = r[x] + disp LOADSCC R9,R9 ; data = M[ea] -- CC set same as LOADCC CPUGET R10,PSW ; cc = psw -- get copy of CC's TESTR R8 ADDSL R8,R3,2 ; -- address of r[dst] STORES R9,R8 ; r[dst] = data STORES R4,R3 ; -- save updated pc, overwrites r[0] LIS R7,PSWSV-PCSV PLUS R7,R3 ; -- address of savearea.pswsv LOADS R9,R7 ; -- original psw TRUNC R10,16 ; cc = (cc & #0000FFFF) SRU R9,16 SL R9,16 ; psw = (psw & #FFFF0000) OR R9,R10 STORES R9,R7 ; savearea.pswsv = psw | cc BR INSTDONE ; } INSTLOAD: ; case 0101 { -- LOAD ; here: R3 = points to savearea.pc ; R4 = pc -- user's ; R5 = M[pc] -- fullword (update after PC increment) ; R6 = ir = M[pc] -- halfword ; R7 = op2 field of IR (no longer needed) ; R8 = dst field of IR (bits 3 to 0) ; uses R7 = address of r[x] -- and then value of r[x] ; R9 = disp (displacement) -- becomes ea and then m[ea] (data) ; R10 = used in cond code setting ; ===== about 25 lines of code were deleted from here ===== BR INSTDONE ; } INSTLEAC: ; case 0110 { -- LEACC (ADDI, CMPI) ; here: R3 = points to savearea.pc ; R4 = pc -- user's ; R5 = M[pc] -- fullword (update after PC increment) ; R6 = ir = M[pc] -- halfword ; R7 = op2 field of IR (no longer needed) ; R8 = dst field of IR (bits 3 to 0) ; uses R7 = address of r[x] -- and then value of r[x] ; R9 = disp (displacement) -- becomes ea ; R10 = used in cond code setting ; ===== about 30 lines of code were deleted from here ===== BR INSTDONE ; } INSTLEA: ; case 0111 { -- LEA ; here: R3 = points to savearea.pc ; R4 = pc -- user's ; R5 = M[pc] -- fullword (update after PC increment) ; R6 = ir = M[pc] -- halfword ; R7 = op2 field of IR (no longer needed) ; R8 = dst field of IR (bits 3 to 0) ; uses R7 = address of r[x] -- and then value of r[x] ; R9 = disp (displacement) -- becomes ea MOVE R7,R6 SR R7,8 TRUNC R7,4 ; x = ir & #0F00 >> 8 ADDSL R7,R3,2 ; -- address of r[x] ADDSI R4,2 ; pc = pc + 2 -- user's PC LOADS R5,R4 EXTH R9,R5,R4 ; disp = M[pc] -- displacement SXT R9,16 ADDSI R4,2 ; pc = pc + 2 STORES R4,R3 ; -- save updated pc before getting r[x] LOADS R7,R7 ; -- value of r[x] ADD R9,R7,R9 ; ea = r[x] + disp TESTR R8 BZS INSTERRR ; if (dst == 0) it's illegal! ADDSL R8,R3,2 ; -- address of r[dst] STORES R9,R8 ; r[dst] = ea STORES R4,R3 ; -- save updated pc to finish job ; } ; } -- end of select on op2 field ; } -- end of case 1111 on op1 field INSTDONE: ; } -- end of select on op1 field ; here: (R1-R7, PSW, TMA and TPC) all saved in savearea ; R2 -- points to savearea.r15sv ; next: restore (R4-R7) to prepare for restart LOADS R15,R2 ; R15 = savearea.r15sv ADDSI R2,-4 LOADS R14,R2 ; R14 = savearea.r14sv ADDSI R2,-4 LOADS R13,R2 ; R13 = savearea.r13sv ADDSI R2,-4 LOADS R12,R2 ; R12 = savearea.r12sv ADDSI R2,-4 LOADS R11,R2 ; R11 = savearea.r11sv ADDSI R2,-4 LOADS R10,R2 ; R10 = savearea.r10sv ADDSI R2,-4 LOADS R9,R2 ; R9 = savearea.r9sv ADDSI R2,-4 LOADS R8,R2 ; R8 = savearea.r8sv ADDSI R2,-4 LOADS R7,R2 ; R7 = savearea.r7sv ADDSI R2,-4 LOADS R6,R2 ; R6 = savearea.r6sv ADDSI R2,-4 LOADS R5,R2 ; R5 = savearea.r5sv ADDSI R2,-4 LOADS R4,R2 ; R4 = savearea.r4sv ; here: R2 -- points to savearea.r4sv ; Then, stop to restore (TPC, PSW) before restoring (R1-R3) LIS R3,PSWSV-R4SV PLUS R3,R2 LOADS R1,R3 CPUSET R1,PSW ; PSW = savearea.pswsv ADJUST R3,PLUS8 ; -- skip TMA (no need to restore it) LOADS R1,R3 ; R1 = savearea.pcsv CPUSET R1,TPC ; TPC = R1 = pcsv ADJUST R3,PLUS4 LOADS R1,R3 ; R1 = savearea.r1sv ADJUST R3,PLUS4 LOADS R2,R3 ; R2 = savearea.r2sv ADJUST R3,PLUS4 LOADS R3,R3 ; R3 = savearea.r3sv RTT ; -- done!