.enable lc ; ; Assembly Language Module for the PL/I Symbolic Debugger Task ; ; defines the assembly language code packets that are sent to the debugger ; kernal in the user task for execution. ; all code must be position independent. ; to make any sense of many of the code packets you have to be familiar with ; the debugger kernal code (PLIDEBUG.MAC). ; .title debasm .psect debasm .list meb ; ; entry points in debug kernal that we refer to ; .globl $db$pkt, $db$snd, $db$rcv, $db$rdpb, $db$sdpb, $db$ptn .globl $db$xf, $db$tv, $db$ndb, $db$vn, $db$vw, $db$qio .globl $db$mnp, $db$sgp, $db$ovp, $db$erp, $db$stp ; ; macro to invoke an executive directive ; ; calling sequence: ; ; exec directive ; .macro exec direc args .mcall direc direc args .endm pktsize = 13. ;code packet size in words, imposed by RCVD$/SDAT$ .globl $DSW ; ; macro to define a PL/I external variable of word size ; .macro extrnl name,value .nlist .psect name,d,gbl,ovr .word value ;external contents .psect debasm .list .endm ; ;define external FIXED BIN(15) containing offset (subscript) in packet array ;addr can contain dot but not globals. if no addr, dot is assumed. ; .macro defloc name,addr deflo.=. .iif nb,addr, deflo.=addr deflos name,deflo. .endm ; ;define offset (subscript), addr can include globals but not dot ; .macro deflos name,addr extrnl name,</2+1> .endm ; ;define offset (subscript) to a word, & reserve the word ; .macro defwrd name defloc name .word xxx .endm xxx = -1 ;word to be filled in by debug task, or word to be ;set by code in packet & returned to debug task filler = -1 ;word that is unused ; ; MACRO cant handle BR x, where x contains globals, so we must assemble by hand ; bbr = 400 ;value of unconditional branch instruction (BR) ;(wasnt able to get MAC to accept symbolic definition) .macro send ;branch to $db$snd (BR $db$snd-$db$pkt+pktbase) .word bbr + <<$db$snd-$db$pkt+pktbase-<.+2>>/2 & 377> .endm .macro receive ;branch to $db$rcv (BR $db$rcv-$db$pkt+pktbase) .word bbr + <<$db$rcv-$db$pkt+pktbase-<.+2>>/2 & 377> .endm ; ; make miscellaneous values available to debug task ; ;RSX error codes: extrnl no.error, IS.SUC ;DSW/iostatus code extrnl no.data, IE.ITS ;no data to receive (RCVD$) extrnl not.stopped, IE.ITS ;task not stopped (USTP$) extrnl not.active, IE.ACT ;task not active (USTP$, ABRT$) extrnl task.active, IE.ACT ;task is active (SPWN$) ;RSX exit statuses: extrnl EX.SUC, EX$SUC ;normal extrnl EX.ERR, EX$ERR ;error extrnl EX.SEV, EX$SEV ;aborted (severe) ;hardware stuff: extrnl bpt.instruction, BPT ;breakpoint instruction Tbit = 20 ;single instruct trap bit in processor status extrnl psTbit, Tbit ;kernal stuff: extrnl odsw, 1 ;kernal stores dsw in word 1 of packet on send extrnl knlvnm, $db$vn ;kernal version number extrnl oxfvw, $db$vw-$db$xf ;offset xfer addr to kernal version word extrnl opktad, $db$pkt-$db$xf ;offset from xfer addr to packet ;RSX overlay stuff: extrnl oalbp2, $ALBP2-.auto ;offset to $ALBP2 from overlay loader ; base ($db$ovp in kernal) .psect $$AUTO,ro,i ;definition must be same as in kernal (plidebug.mac) .auto: ;address of overlay loader. cant provide addr of $ALBP2 directly ;via global because non-overlaid tasks would then get an undefined .psect debasm ; ; macros for defining a code packet ; .macro defpkt pktname extrnl pktname,pktname ;define external POINTER to the packet pktname: pktbase=. .endm .macro endpkt fillpkt pktsize ;check to see that packet fits .endm .macro fillpkt fillsize fillit pktfill = fillsize - <<.-pktbase>/2> .if lt pktfill .error -pktfill ;code block overflows packet by -pktfill words .endc .nlist .if nb fillit .rept pktfill .word filler .endr .endc .list .endm ; ; initialization part 1 ; defpkt doini1 mov #nop, $db$ndb-$db$pkt+pktbase ;cancel non-debugger mode ;move debug task name into receive dpb (kernal must only receive ;from debug task, so as not to interfere with sends & receives ;user program may do with other tasks): mov $db$ptn-$db$pkt+pktbase, $db$rdpb-$db$pkt+pktbase+r.vdtn mov $db$ptn+2-$db$pkt+pktbase, $db$rdpb-$db$pkt+pktbase+r.vdtn+2 ;move debug task name into send dpb, part 1: mov $db$ptn-$db$pkt+pktbase, $db$sdpb-$db$pkt+pktbase+s.datn receive endpkt ; ; initialization part 2 ; defpkt doini2 ;move debug task name into send dpb, part 2: mov $db$ptn+2-$db$pkt+pktbase, $db$sdpb-$db$pkt+pktbase+s.datn+2 ;return bunch of addresses in user task that debug task needs: mov $db$mnp-$db$pkt+pktbase, (pc)+ defwrd oinimn ;addr of ..MAIN in user task mov $db$sgp-$db$pkt+pktbase, (pc)+ defwrd oinisg ;addr of root segment descriptor mov $db$ovp-$db$pkt+pktbase, (pc)+ defwrd oiniov ;addr of overlay loader breakpoint send endpkt $ = . .nlist meb exec rcvd$ ;must invoke the macro to get r.vdtn defined exec sdat$ ;likewise for s.datn .list meb . = $ ;dont let them take space ; ; initialization part 3 - ; set trap vector for breakpoints & single instruct traps ; defpkt dotrpv mov $db$stp-$db$pkt+pktbase, (pc)+ defwrd oinist ;return addr of .STOP in user task mov pc,r5 ;position-independent code to get address, add #vdpb-.,r5 ;r5 still available since program has not started exec dir$ send ;send to get dsw from directive vdpb: exec svdb$ deflos otrpva, vdpb+s.vdta ;debugger must fill in addr of vector ;(which is in kernal, PLIDEBUG.MAC) deflos otrpvv, $db$tv-$db$pkt+pktbase ;define external offset ;for debugger calculation of $db$tv address endpkt ; ; find root segment descriptor in possibly clustered task (see debinit) ; defpkt dortsg rtsglp: tst @(pc)+ rtsgp: defwrd ortsgp ;debugger fills in addr of up-link word beq rtsgfound ;no parent - this is root of user overlays ;(TKB puts SUPROT (cluster root) segment at end) add rtsgsz,rtsgp;next overlay descriptor for linear scan br rtsglp rtsgfound: send rtsgsz: defwrd ortsgz ;debugger fills in size of overlay segment descriptors endpkt ; ; start program execution (used by first GO command) ; defpkt dostrt jmp (r0) ;TKB puts start address in r0 endpkt ; ; STOP, for STOP command ; defpkt dostop mov stpdsw,@#$DSW ;restore user DSW mov (pc)+,r4 defwrd ostpfp ; and FP (frame pointer) ;to simulate jsr r5,.STOP from current break location takes 2 steps: mov (pc)+,r5 defwrd ostppc ;debugger fills in user PC jmp @(pc)+ defwrd ostpad ;debugger fills in address of .STOP stpdsw: defwrd ostpds endpkt ; ; exit task, for EXIT command ; defpkt doexit exec exit$s halt ;should never return endpkt ; ; resume execution from a breakpoint which no longer has a breakpoint set ; defpkt dogo0 ;restore: mov go0dsw,@#$DSW ;DSW mov (pc)+,r4 defwrd ogo0fp ;FP (frame pointer) mov (pc)+,-(sp) defwrd ogo0ps ;PS mov (pc)+,-(sp) defwrd ogo0pc ;PC rtt go0dsw: defwrd ogo0ds endpkt ; ; resume execution from a breakpoint, step 1 ; defpkt dogo1 mov #xxx,@#$DSW ;restore DSW defloc ogo1ds,.-4 ;i.e., the xxx mov #xxx,@go1pc ;put real instruction instead of BPT defloc ogo1in,.-4 ;i.e., the xxx mov (pc)+,r4 ;restore: defwrd ogo1fp ;FP mov (pc)+,-(sp) defwrd ogo1ps ;PS mov (pc)+,-(sp) go1pc: defwrd ogo1pc ;PC rtt endpkt ; ; resume execution from a breakpoint, step 2 ; defpkt dogo2 mov go2dsw,@#$DSW ;restore DSW mov #BPT,@(pc)+ ;restore BPT at original breakpoint defwrd ogo2pc bic #Tbit,2(sp) ;clear single instruct trap bit rtt go2dsw: defwrd ogo2ds endpkt ; ; get program state on breakpoint ; defpkt dobrek mov (sp)+,(pc)+ defwrd obrkpc ;PC mov (sp)+,(pc)+ defwrd obrkps ;PS mov sp,(pc)+ defwrd obrksp ;SP mov r4,(pc)+ defwrd obrkfp ;FP (PL/I frame pointer) mov r5,(pc)+ defwrd obrkr5 ;R5 send endpkt ; ; get the overlay segment descriptor address of the overlay just loaded, ; while at overlay loader breakpoint ($ALBP2). ; ; we know from empirical observation that offset 12 in the stack has the return ; address; I wasn't able to find a more direct path to the descriptor. ; ; PL/I caller: revector entry point: ; ; -4 JSR (pc-relative) --> +0 JSR (absolute) ; -2 word offset to revector __ | +2 addr of overlay loader ; return --> +0 next instruction \ | +4 addr overlay seg descriptor ; | + -- ; |________________________________/ ; defpkt doovly mov r4,-(sp) ;save a register mov 12+2(sp),r4 ;get return address at +12 from sp before our push add -2(r4),r4 ;addr of revector entry point mov 4(r4),(pc)+ ;return overlay segment descriptor address defwrd ovlysg mov (sp)+,r4 ;restore register send endpkt ; ; get remaining registers not obtained by dobrek, for RG command ; defpkt doregs mov r0,(pc)+ defwrd oregr0 mov r1,(pc)+ defwrd oregr1 mov r2,(pc)+ defwrd oregr2 mov r3,(pc)+ defwrd oregr3 send endpkt ; ; get prolog, flags, & caller fp for UPLEVEL procedure, uses r4 ; defpkt douplv mov (pc)+,r4 defwrd ouplfp ;fp for frame of interest filled in by debugger mov xxx(r4),(pc)+ defloc ouplpo,.-2 ;i.e., the xxx - offset filled in by debugger defwrd ouplpr ;returned prolog address mov xxx(r4),(pc)+ defloc ouplfo,.-2 ;i.e., the xxx - offset filled in by debugger defwrd ouplfl ;returned dsaflags word mov xxx(r4),(pc)+ defloc ouplco,.-2 ;i.e., the xxx - offset filled in by debugger defwrd ouplcl ;returned caller fp send endpkt ; ; find offset in code, given statement number, for STATEMENT procedure ; defpkt dostma mov r5,-(sp) mov (pc)+,r5 defwrd ostmat ;addr of statement number table, from debugger stmalp: mov (r5),(pc)+ defwrd ostmao ;offset returned here cmp (r5)+,#-1 beq stmaex ;not found - stmt # too big or <=0 dec (pc)+ defwrd ostman ;debugger puts statement number here bne stmalp stmaex: mov (sp)+,r5 send endpkt ; ; find statement number, given offset in code, for STMTNUM procedure ; defpkt dostmn mov r5,-(sp) mov (pc)+,r5 defwrd ostmnt ;addr of statement number table, from debugger stmnlp: cmp (r5)+,(pc)+ defwrd ostmno ;offset, from debugger blo stmnlp mov -(r5),(pc)+ defwrd ostmnw ;return word (offset) from matching table entry mov r5,(pc)+ defwrd ostmnp ;return address of matching table entry mov (sp)+,r5 send endpkt ; ; read a word from user memory, for GETWORD procedure ; defpkt dordwd mov @rdwda,rdwdx send rdwda: defwrd ordwda ;address of word to get rdwdx: defwrd ordwdx ;word stored here for debugger to pick up endpkt ; ; write a word into user memory, for PUTWORD procedure ; defpkt dowrwd mov wrwdx,@wrwda receive wrwdx: defwrd owrwdx ;word to store, set by debugger wrwda: defwrd owrwda ;address to put it at endpkt ; ; read a block of bytes from user memory, uses r4, for GETBYTES procedure ; defpkt dordb mov (pc)+,r4 defwrd ordba rdblp: movb (r4)+,rdbbuf inc .-2 ;we never claimed to be reentrant dec (pc)+ defwrd ordbn bhi rdblp send rdbbuf: defloc orwbbf maxbyt = pktsize*2+pktbase-. ;bytes left in packet = max block size extrnl rwbmax, maxbyt ;make available to debugger in external ;note: orwbbf & rwbmax also apply to dowrb since the code is same size ;note: rwbbuf in knldef independently knows max size... endpkt ; ; write a block of bytes into user memory, uses r4, for PUTSTRING procedure ; defpkt dowrb mov (pc)+,r4 defwrd owrba wrblp: movb wrbbuf,(r4)+ inc .-2 dec (pc)+ defwrd owrbn bhi wrblp receive wrbbuf: endpkt ; ; do a QIO for USERQIO procedure ; uses r4 and qio packet in kernal (PLIDEBUG.MAC) ; defpkt doqio mov pc,r4 add #$db$qio-$db$pkt+pktbase-., r4 ;calculate addr of qio packet exec dir$ bcs qiodone qiowait:tst qiostat ;cant destroy user event flag - wait for beq qiowait ; i/o status to be set qiodone:send fillpkt pktsize-3 fill ;pad out up to last three words (kernal qio packet ;is hardwired for iostat to be the last two words of the send/receive ;packet, and the characteristics buffer to be the 3rd to last word) defwrd oqioch ;SF.SMC/SF.GMC characteristics buffer defloc oqiost qiostat:.word 0,0 ;2-word i/o status block endpkt ;define qio-related values for debugger: deflos oqiofn, $db$qio+Q.IOFN-$db$pkt+pktbase ;define external offset for debugger ;access to qio packet function code extrnl read.char, IO.RAL!TF.RNE ;read passall and no echo extrnl io.att, IO.ATT extrnl io.det, IO.DET extrnl sf.gmc, SF.GMC extrnl sf.smc, SF.SMC ;extrnl set.slaved, TC.SLV+256. ;.byte TC.SLV, 1 ;extrnl set.unslaved, TC.SLV ;.byte TC.SLV, 0 extrnl flush.typeahead.buffer, TC.TBF extrnl ie.dna, IE.DNA extrnl tc.ttp, TC.TTP extrnl ttv100, T.V100*256.+TC.TTP ;.byte TC.TTP, T.V100 extrnl ttv101, T.V101*256.+TC.TTP ;.byte TC.TTP, T.V101 extrnl ttv102, T.V102*256.+TC.TTP ;.byte TC.TTP, T.V102 extrnl ttv105, T.V105*256.+TC.TTP ;.byte TC.TTP, T.V105 extrnl ttv125, T.V125*256.+TC.TTP ;.byte TC.TTP, T.V125 extrnl ttv131, T.V131*256.+TC.TTP ;.byte TC.TTP, T.V131 extrnl ttv132, T.V132*256.+TC.TTP ;.byte TC.TTP, T.V132 $ = . .nlist meb exec qio$ ;must invoke to get Q.IOFN defined .list meb . = $ ;dont let it take space ; ; temp call user procedure, partial implementation of CALL command ; defpkt docall mov (pc)+,-(sp) ;put the arg in a temp defwrd ocalarg mov sp,r4 ;mov sp,-(sp) ambiguous - do in 2 steps mov r4,-(sp) ;push arg addr mov #1,-(sp) ;push arg count mov (pc)+,r4 ;restore fp defwrd ocalfp jsr r5,@(pc)+ ;call PL/I procedure defwrd ocalad tst (sp)+ ;pop arg temp send endpkt .end