	.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  <arg1,arg2,...>
;

	.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,<<addr-pktbase>/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$ <r5>
	send		;send to get dsw from directive

vdpb:	exec svdb$ <xxx,3>
	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$ <r4>
	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
