	.TITLE	DISASM
	.IDENT	"V1.7"
;
;   Author:	D. Mischler	10-JUN-87
;
;   This module disassembles a single PDP-11 instruction
;   and buffers the resulting text line.
;

	.PSECT	RODATA,D,RO
;
;   Opcode class dispatch table.
;
CLSTBL:	.WORD	BRANCH		;  0 - Branch instructions.
	.WORD	COND		;  1 - Condition code instructions.
	.WORD	DOUBLE		;  2 - Double operand instructions.
	.WORD	IMMED		;  3 - 3-bit immediate instructions.
	.WORD	FPADST		;  4 - Floating point accumulator destination.
	.WORD	FPASRC		;  5 - Floating point accumulator source.
	.WORD	IMMED		;  6 - 6-bit immediate instructions.
	.WORD	NONE		;  7 - No operands.
	.WORD	IMMED		; 10 - 8-bit immediate instructions.
	.WORD	REG		; 11 - Single register operand.
	.WORD	REGDST		; 12 - Register destination with any source.
	.WORD	REGSRC		; 13 - Register source with any destination.
	.WORD	SINGLE		; 14 - Single operand.
	.WORD	SOB		; 15 - Subtract one and branch instruction.
	.WORD	ILLEGAL		; 16 - Illegal class.
	.WORD	ILLEGAL		; 17 - Illegal class.
;
;   Condition code bit mask identifier table.
;
CONTBL:	.ASCIZ	"NZVC"
	.EVEN
;
;   Adressing mode dispatch table.
;
MODTBL:	.WORD	REG		; 0 - Register direct.
	.WORD	REGDEF		; 1 - Register deferred.
	.WORD	REGINC		; 2 - Register deferred post-increment.
	.WORD	REGINI		; 3 - Register deferred post-increment indirect.
	.WORD	DECREG		; 4 - Register deferred pre-decrement.
	.WORD	DEREGI		; 5 - Register deferred pre-decrement indirect.
	.WORD	INDEX		; 6 - Register base indexed.
	.WORD	INDEXI		; 7 - Register base indexed indirect.

;
;   Illegal opcode "mnemonic" text.
;
ILLOPC:	.ASCIZ	"ILLEGAL"
	.EVEN
	.PAGE
	.PSECT	CODE,I,RO
;
;   Subroutine to disassemble an instruction.
;   On entry:	R0 points to disassembly buffer.
;		R5 contains instruction address.
;
;   On exit:	R0 points after the disassembled instruction text.
;		R1 and R2 are destroyed.
;		R5 has been incremented past the instruction.
;		The carry will be set if an error is detected.
;
DISASM::
	CALL	M$RI5P		; Fetch instruction word, OK?
	BCS	ADRERR		; No, addressing error.
	CALL	FNDOPC		; Find opcode table entry, OK?
	BCS	10$		; No, indicate illegal opcode.
	MOV	R1,-(SP)	; Save opcode word for later.
	MOV	(R2)+,-(SP)	; Save opcode class word on stack.
	MOV	(R2)+,R1	; Get first RAD50 mnemonic word.
	CALL	C$R50		; Convert it to ASCII.
	MOV	(R2)+,R1	; Get second RAD50 mnemonic word.
	CALL	C$R50		; Convert it too.
	MOVB	#' ,(R0)+	; Buffer a couple blanks.
	MOVB	#' ,(R0)+
	MOV	(SP)+,R2	; Recover opcode class word.
	BIC	#007777,R2	; Leave only the class number.
	SWAB	R2		; Put class in low byte.
	ASR	R2		; Position class for word table index.
	ASR	R2
	ASR	R2
	MOV	(SP)+,R1	; Recover opcode word.
	CALLR	@CLSTBL(R2)	; Disassemble operands.
;
;   Illegal opcode: tell the poor slob debugging it.
;
10$:	MOV	#ILLOPC,R1	; Point to illegal opcode text.
20$:	MOVB	(R1)+,(R0)+	; Copy text into place.
	BNE	20$
	CLC			; Indicate success anyway.
	RETURN
;
;   Addressing error: a question mark should do it.
;
ADRERR:	MOVB	#'<,(R0)+	; Indicate an addressing error occurred.
	MOVB	#'?,(R0)+
	MOVB	#'>,(R0)+
ILLEGA:	SEC			; Indicate failure.
	RETURN
	.PAGE
;
;   Process instructions with no operands.
;
NONE:	CALL	U$RMTB		; Remove trailing blanks.
	CLC			; Indicate everything is OK.
	RETURN

;
;   Process a single operand.
;
SINGLE:	MOV	R1,R2		; Copy instruction word.
	BIC	#^C<70>,R2	; Mask to addressing mode.
	ASR	R2		; Put mode in place for word dispatch.
	ASR	R2
	CALLR	@MODTBL(R2)	; Dispatch on addressing mode.

;
;   Print out the correct mnemonic for the register in R1(0:2).
;
REG:	BIC	#^C<7>,R1	; Mask to register number.
	ASL	R1		; Make it a word offset.
	ADD	#REGTBL,R1	; Point into register table.
	MOVB	(R1)+,(R0)+	; Buffer register mnemonic.
	MOVB	(R1)+,(R0)+
	RETURN

;
;   Handle register deferred post-increment indirect.
;
REGINI:	MOVB	#'@,(R0)+	; Buffer indirection sign.
;
;   Handle register deferred post-increment.
;
REGINC:	MOV	R1,R2		; Copy instruction word.
	BIC	#^C<7>,R1	; Mask to register number.
	CMP	#7,R1		; Is the register the PC?
	BEQ	10$		; Yes, process as immediate mode.
	CALL	REGDEF		; Process register deferred.
	MOVB	#'+,(R0)+	; Drop plus sign.
	RETURN
;   Process full 16-bit immediate mode.
10$:	MOVB	#'#,(R0)+	; Indicate immediate addressing.
	CALL	M$RI5P		; Fetch operand word, OK?
	BCS	ADRERR		; No, buffer a question mark.
	CALL	C$VALU		; Convert value to ASCII.
	CLC			; Indicate everything is OK.
	RETURN
	.PAGE
;
;   Process indexed indirect addressing.
;
INDEXI:	MOVB	#'@,(R0)+	; Indicate indirect addressing.
;
;   Handle indexed addressing.
;
INDEX:	BIC	#^C<7>,R1	; Mask opcode to register number.
	MOV	R1,R2		; Copy it for safe keeping.
	CALL	M$RI5P		; Fetch operand word, OK?
	BCS	ADRERR		; No, indicate failure.
	CMP	#7,R2		; Is the register the PC?
	BEQ	10$		; Yes, handle it as relative mode.
	CALL	C$VALU		; Convert value to ASCII.
	MOV	R2,R1		; Recover register number.
	BR	REGDEF		; Put parenthesis around the register.
;   Take care of full 16-bit relative addressing.
10$:	ADD	R5,R1		; Offset relative address value by the PC.
	CALL	C$VALU		; Convert value to ASCII.
	CLC			; Indicate everything is OK.
	RETURN

;
;   Handle pre-decrement register deferred indirect addressing.
;
DEREGI:	MOVB	#'@,(R0)+	; Buffer indirection indicator.
;
;   Process pre-decrement register deferred.
;
DECREG:	MOVB	#'-,(R0)+	; Buffer pre-decrement indicator.
;
;   Process register deferred addressing.
;
REGDEF:	MOVB	#'(,(R0)+	; Dump open parenthesis.
	CALL	REG		; Process register.
	MOVB	#'),(R0)+	; Drop close parenthesis.
	RETURN
	.PAGE
;
;   Process instructions with a FP accumulator as source.
;
FPASRC:	BIC	#177400,R1	; Trick REGSRC into working right.
;
;   Process register source instructions.
;
REGSRC:	BIC	#177000,R1	; Make source mode field looks OK.
;
;   Process double operand instructions.
;
DOUBLE:	MOV	R1,-(SP)	; Save instruction word.
	ASH	#-6,R1		; Position source operand.
	CALL	SINGLE		; Process source operand.
	MOVB	#',,(R0)+	; Separate source and destination.
	MOV	(SP)+,R1	; Pop instruction word.
	CALLR	SINGLE		; Process destination operand and return.

;
;   Process short immediate instructions.
;
IMMED:	ASR	R2		; Correct number of bits.
	MOV	R2,-(SP)	; Stack it.
	MOV	#-1,R2		; Turn all bits on.
	ASH	(SP)+,R2	; Shift in the correct number of zeroes.
	BIC	R2,R1		; Mask to immediate mode field.
	CLR	R2		; Suppress leading zeroes.
	CALL	C$NUMB		; Convert short value to ASCII.
	CLC			; Make sure success is indicated.
	RETURN

;
;   Process instructions with a FP accumulator as destination.
;
FPADST:	BIC	#177400,R1	; Dispose of extraneous fewmets.
;
;   Process register destination instructions.
;
REGDST:	MOV	R1,-(SP)	; Save instruction word.
	CALL	SINGLE		; Process source operand.
	MOVB	#',,(R0)+	; Separate source and destination.
	MOV	(SP)+,R1	; Recover instruction word.
	ASH	#-6,R1		; Position destination register.
	CALLR	REG		; Finish up and return.
	.PAGE
;
;   Take care of the SOB instruction.
;
SOB:	MOV	R1,-(SP)	; Save instruction word.
	ASH	#-6,R1		; Position register.
	CALL	REG		; Take care of register.
	MOVB	#',,(R0)+	; Separate source and destination.
	MOV	(SP)+,R1	; Recover instruction word.
	BIC	#^C<77>,R1	; Mask to branch offset.
	NEG	R1		; Correct sign of branch offset.
;
;   Handle branch instructions.
;
BRANCH:	MOVB	R1,R1		; Sign extend branch offset to 16 bits.
	ASL	R1		; Produce byte offset.
	ADD	R5,R1		; Produce branch target address.
	CALLR	C$VALU		; Convert value to ASCII and return.

;
;   Take care of condition code instructions.
;
COND:	CALL	U$RMTB		; Remove blanks after mnemonic.
	ASH	#12.,R1		; Put condition bits in top of word.
	MOV	#CONTBL,R2	; Point to condition code table.
10$:	ASL	R1		; Current bit set?
	BCC	20$		; No, check next.
	MOVB	(R2),(R0)+	; Buffer bit identifier.
20$:	TSTB	(R2)+		; All done?
	BNE	10$		; No, try next bit.
	RETURN
	.PAGE
;
;   Subroutine to locate the appropriate opcode table entry.
;   On entry:	R1 contains instruction word.
;   On exit:	R2 points to opcode table entry.
;		The carry will be set if an appropriate entry cannot be found.
;
FNDOPC:	MOV	R0,-(SP)	; Save volatile registers.
	MOV	R1,-(SP)
	MOV	#OPCTBL,R2	; Point to start of opcode table.
	BR	20$		; Enter loop at test.
;   Loop to find the correct opcode.
10$:	BIC	#170000,R0	; Mask class word to decode mask.
	MOV	(SP),R1		; Recover instruction word.
	BIC	R0,R1		; Mask off don't cares.
	CMP	OP.VAL(R2),R1	; Hit on this table entry?
	BEQ	30$		; Yup, take it with us (carry is clear).
	ADD	#OP.LEN,R2	; Point to next table entry.
20$:	MOV	(R2),R0		; Get opcode class, zero?
	BNE	10$		; No, check it out.
;   End of table reached.
	SEC			; Indicate failure.
;   Return with opcode pointer (carry is already set up).
30$:	MOV	(SP)+,R1	; Pop saved registers.
	MOV	(SP)+,R0
	RETURN

	.END
