


; Because this stuff (disassembly) is tricky, I'll explain my initial
; attempt at it. We have the mnemonic table (mnemtable.asm) with the
; opcodes and names of all the instructions. Instructions like
; 'anda' which require only one byte for the instruction are 
; called inherent. So the disassembler checks for inherent codes
; first because they're easy. Actually, it checks for "page 2
; inherents" first because they're easy to separate from all 
; other instructions (because they all have two bytes, the first of
; which is the "page 2 prebyte" -- $18.) So we can easily look for
; page2 inherent instructions first. If the opcode we have isn't 
; found among the inherent or p2inh instructions, then its probably
; a relative (branch) or general (like ldaa) instruction. The relative
; instructions are easiest to find and deal with so we check for them
; next. The general ("gen") instructions are those like "ldaa" that
; have 3/4 opcodes associated with the one instruction. To find out 
; is our opcode is a general one, we need to AND it with a constant
; to get to it's "base" opcode. (ldaa, for example, can be $a6 so we
; AND it with $cf to get its base opcode, $86.) This AND'ing thing 
; seems to work very well. (Or maybe it doesn't.) There is another
; strange class of codes that we need to deal with (marked "BAD" in
; mnemtable.asm) but I don't know how to do that yet.



; -------------= search mnemonic table =--------------
; A=opcode to search for
; B=class of opcode
; returns: Y=addr of name of opcode
;          B=$ff on error, "real" class on exit
; ----------------------------------------------------

*						* not sure why he just didn't do
*						* ldy	#mnemtable + 4
*						* it would save lots of time since he's using
*						* immediate addressing anyways

searchmnem:
	ldy		#mnemtable		; Y points to the table
	pshb					; saves the state of B, which is the "class" of instruction
	ldab		#$04			; loads 4, which is needed to increment the mneumonic pointer
	aby					; we need to add 4 to Y to get it to the class field
	pulb					; restore B

sm_loop1:

	cmpb		0,y			; checks to see the instruction is the same class
	beq		sm_same_class		; this means that it is the same class
	orab		#%10000000		; check for the long bit instructions, too
	cmpb		0,y			; checks again to see if it is the same class
	beq		sm_same_class		; if yes, check the opcode for similarity
	andb		#%01111111		; remove the long bit
		
	pshb					; saves state of B
	ldab		#$06			; skip to the next mnemonic. see "mnemtable.asm" for more
	aby					; increments mnemonic table pointer to the next instruction
	pulb					; restore B (class)

sm_chk_end:
	cpy		#lastclass		; have we reached the end of the table?
	beq		sm_error_exit		; if we have, exit becuase it was not found
	bra		sm_loop1		; otherwise, continue the search


						; the class of the opcode we're looking for
						; is the same as this opcode so check the
sm_same_class:					; opcodes -- see if they're similar
	iny					; move Y to opcode in list. the opcode is after the class
	cmpa		0,y			; opcodes the same? see "mnemtable.asm" for more information
	beq		sm_opcode_found		; yes, we found it so return success!
	
	pshb					; save state of B
	ldab		#$05			; if it isn't the same opcode,
	aby					; add 5, now it points to the next class in the table
	pulb					; restore state of B
	
	bra		sm_chk_end		; and then jump to continue checking


						; we found the opcode so we need to subtract 5 from Y
						; to get it to point at the name of this opcode
						; we also need to load into B the "real" class of this
sm_opcode_found:				; code
	ldab		-1,y			; loads the class into B

*						* There has to be a better way of doing this next part

	xgdy					; X <=> D
	subd		#$05			; take 5 off 	
	xgdy					; put it back	

	rts					; Y is set and B is still the class so
						; we can just return
sm_error_exit:
	ldab		#$ff			; error code returned in B
	rts					; return to calling function
	
	
; --------------= disassembly! =---------------
; in: X = addr of first byte in instruction
; out: X = addr of next instruction!
; ---------------------------------------------

rr_list	dc.b		'xysp'

disasm:
	stx		tempw			; save the addr of the instruction

	ldaa		0,x			; A=first byte of instruction
	
	cmpa		#PAGE2			; is it a page 2 instruction? in "mnemtable.asm"
	beq		da_page2_instr		; yes, go check for page2 instructions

	jsr		scan_spec_instr		; check if its a "special" instruction!
	cmpb		#$ff			; $ff means it wasn't a special instruction
	lbne		da_rts			; incrementing x is done in the special instruction handler

	jsr		scan_spec2_instr	; check for the other special class
	cmpb		#$ff
	lbne		da_rts

						; otherwise we'll check all of the inh instructions
	; look for inh instructions
	ldab		#INH			; class
	jsr		searchmnem		; scan the table for this mnemonic (returns ptr to
						; name in Y, $ff in B on error)
	cmpb		#$ff			; error (means opcode not found)?
	beq		da_scan_rel		; yes, check for relative instructions (REL)

	jsr		print_mnem		; prints the mnemonic. see end of file for more info
	inx					; move X to next instruction
	rts
	
da_page2_instr:
	inx					; we need the next op -- the real opcode
	ldaa		0,x			; get it

	ldab		#P2INH			; look for a page2 inherent op
	jsr		searchmnem		; search for P2INH mnemonic

	cmpb		#$ff			; error (mnemonic not found)
	bne		da_p2_print		; if found, print the mnemonic
						; else look here
	ldab		#P2LREL			; look for the long relative branches
	jsr		searchmnem		; search for P2LREL mnemonic
	ldx		tempw			; tempw is the address of the instruction
	inx	
	inx	
	inx
	inx					; move to next instruction
	rts					; return, because the next address is in x

da_p2_print:
	jsr		print_mnem		; print it
	
	ldx		tempw
	inx
	inx
	inx					; increment X to the next instruction
	rts					; return because the next address is in x

da_scan_rel:
	ldx		tempw			; restore the addr of the instruction
	ldaa		0,x			; and re-read the opcode

	ldab		#REL			; look for relative (rel) instructions
	jsr		searchmnem		; do it

	cmpb		#$ff			; error (opcode not found)
	beq		da_scan_gen		; yes, go to general instructions

	jsr		print_mnem		; print the mnemonic
	inx					; get the relative part at
	ldaa		0,x			; the next byte in the object code

	psha
	ldaa		#' '
	jsr		put_char
	pula
	jsr		print_number		; print the relative part (fix this later?)
						; we'll need to do ADDR+relative part. print that.
	inx					; move X to next instruction
	rts


da_scan_gen:
	; now the general one's need to be AND'd with
	; 0xCF to get the "base opcode". like there is
	; an LDAA with an opcode of $A6. But the base 
	; is $86 (the one in my table) so we do
	; $a6 AND $cf = $86. See?

	; after finding the general instruction, we need to 
	; evaluate which "flavor" of that instruction it is.
	; ldaa, for example, has 4 "flavors" and the "$A6"
	; flavor has 5 different types!


	ldx		tempw			; get teh addr of the op again
	ldaa		0,x			; and the op
	
	anda		#$cf			; AND it with CF, remember?
	ldab		#GEN			; we're looking for general instructions

	jsr		searchmnem		; look for it

	cmpb		#$ff			; couldn't find it?
	lbeq		da_scan_gen2		; nope

	jsr		print_mnem

	; lets decode the type of general instruction
	pshb
	psha
	pulb					; transfer A to B
	ldaa		0,x			; get the original instruction
	
	sba					; A-B => A
	asra					; we need to shift A right 4 to get
	asra					; a usable number
	asra					; 0=immediate, 1=direct, 2=extended, 3=index
	asra
	
	pulb
	cmpa		#$00	
	beq		da_gen_imm		; immediate
	cmpa		#$01			
	beq		da_gen_dir		; direct
	cmpa		#$03			
	beq		da_gen_extend		; extended
	cmpa		#$02			
	beq		da_gen_index		; indexed	


da_gen_imm:
	; immediate is easy: the next byte is the value
	; but we have to check B to make sure it isn't
	; a long immediate instructions (ldd, ldy, ldx)
	ldaa		1,x
	psha
	ldaa		#'#'
	jsr		put_char
	pula
	jsr		print_number
	andb		#%10000000		; check for long instruction
	beq		da_imm_end		; if zero, then it wasn't a long inst

	ldaa		2,x
	jsr		print_number	

	inx					; return with X pointing to the next instr
da_imm_end:
	inx
	inx

	rts		

da_gen_dir:
	; direct is easy, too: next byte is address
	ldaa		1,x
	jsr		print_number

	ldx		tempw			; load the addr of the op
	inx
	inx					; increment to the next instr

	rts

da_gen_extend:
	; extended is easy: next two bytes are address
	ldd		1,x
	xgdx
	jsr		print_address
	xgdx
	
	inx					; advance to next instr
	inx
	inx

	rts

da_gen_index:
						; index is tough. see p. 3-6 of the CPU12 manual for
						; info on how to decode the 'xb' or instruction
						; post-byte to get the index info.
	ldx		tempw			; get the addr of the instruction again
	inx					; skip to the post-byte
	ldaa		0,x			; get the post-byte

						; first we check for 5-bit constant offset
	anda		#%00100000		; if this returns zero, it is
	beq		da_index_5bit		; the 5 bit offset

						; now we check for the 9,16-bit constant offset
	ldaa		0,x
	anda		#%11100100
	cmpa		#%11100000		; if its equal to this, then its either 
						; 9 or 16 bit offset
	bne		da_cont			; continue with checking

	ldaa		0,x		
	anda		#%00000011		; if those two bits are set, its 16
	lbeq		da_index_16		; if its 16 bit const offset
	lbra		da_index_9		; if its 9 bit const offset

da_cont:
						; check for accumulator offset (A,B,D, or D indexed indirect)
	ldaa		0,x			; loads the post byte again
	anda		#%11100100
	cmpa		#%11100100
	lbeq		da_accum_offset		; means it is an accumulator offset

	bra		da_auto_inc_dec		; if that fails, then its auto-pre/post increment/decrement


da_index_5bit:
	ldx		tempw			
	inx					; to get to the post-byte
	ldaa		0,x			; gets the post byte
	anda		#%11000000		; get the index reg involved
	psha					; saves the result
	ldaa		0,x			; loads the postbyte again
	anda		#%00011111		; get the offset
	pulb					; gets back the index reg involved
	asrb					; faster to shift than divide!
	asrb
	asrb
	asrb
	asrb
	asrb
						; now A is the offset (signed) and B is the register
						; print the offset
	jsr		print_number		; prints the number
	ldaa		#','			; loads a comma
	jsr		put_char		; prints the comma
						; find the register and print
	pshx					; stores x
	ldx		#rr_list		; the register list( at top of this file )
*						* tab here
	pshb
	pula					; switches b into a
	abx					; adds a to x
	ldaa		0,x			; loads the character from x
	jsr		put_char		; prints it out
	pulx					; gets back the instr

	inx					; increment X to the next instr
	rts					; returns with X = next instr

da_index_9:
	ldx		tempw			; get addr of the op
	ldaa		1,x			; get the xb
	anda		#%00011000		; 
	asra
	asra
	asra					; extract the register code
	ldx		#rr_list
	psha
	pulb
	abx
	ldaa		0,x			; get the register character
	psha
	ldaa		2,x
	jsr		print_number
	ldaa		#','
	jsr		put_char
	pula
	jsr		put_char
	
	ldx		tempw
	inx					; increment X to next instr
	inx
	inx
	rts	

da_scan_gen2:
	; this could be combined with da_scan_gen
	; but I think this makes it clearer. There
	; is a block of general STORE instructions
	; (staa, stab, std, sty, stx, sts) that need 
	; to be AND'd with $cf and 16 added so that
	; a $6a (staa 0,y) becomes $5a ($6a AND $cf+$10)
	adda		#$10			; add 16
	ldab		#GEN
	jsr		searchmnem		; do the search
	
	cmpb		#$ff			; didn't find it?
	beq		da_done			; nope

	jsr		print_mnem

	; found it
	; do like in gen where we figure out which 'version' of
	; the instruction it is
	psha
	pulb
	ldaa		0,x
	sba					; A-B => A
	asra
	asra
	asra
	asra

	cmpa		#$00
	lbeq		da_gen_dir		; direct, not immediate
	cmpa		#$02
	lbeq		da_gen_extend		; indexed
	
	; otherwise its indexed -- hard!
	lbra		da_gen_index

	rts


da_done:
da_index_16:
da_accum_offset:
da_auto_inc_dec:
	ldab		#$ff
da_rts:
	rts

						; scan_spec2_instr looks for SPEC2 instructions and decodes them
scan_spec2_instr:
	psha					; stores state of A
	ldx		tempw			; loads the address
	ldaa		0,x			; get the instr opcode

	ldab		#SPEC2			; loads value of spec2
	jsr		searchmnem		; look for it

	cmpb		#$ff			; if $ff it wasn't found
	bne		ssi_found		; otherwise it was found
						; go to place for ssi

	suba		#$10			; all SPEC2 instructions have two 
	ldab		#SPEC2			; codes: n and n-$10
	jsr		searchmnem		; look for the mnemonic again
	
	cmpb		#$ff			; 
	bne		ssi_found2		; not equal to $ff then it was found
	
	beq		ssi_err_done		; otherwise it was not found at all -- ret error

ssi_found2:
	adda		#$10			; re-add $10 to opcode
ssi_found:
	; print the opcode
	jsr		print_mnem

						; we need to get the "base" opcode
	ldab		#$05			; loads 5 so it can be added
	aby					; add 5 to Y to get base op
	ldab		0,y			; Y is the mnem name

	sba					; A-B => A 
	beq		ssi_index_op		; if A=0, the op we have is the "base op" (low one)
						; which is really the indexed op
ssi_ext_op:
						; otherwise its the extended version with the next two
						; bytes being the 16 bit addr
	ldd		1,x			; load D with the 16 bit addr
	xgdx					; X <-> D
	jsr		print_address		; prints out the address
	ldx		tempw			; loads the address
	inx					; advance to the next instr
	inx
	inx
	bra		ssi_done_good		; has been incremented

ssi_index_op:
	jsr		da_gen_index		; use pre-existing code!
	bra		ssi_done_good		; finish off

ssi_err_done:
	ldab		#$ff			; loads b with an error
ssi_done_good:
	pula					; restores state of A
	rts					; returns to stack with X = next instr


; da_scan_spec scans for special instructions like JSR and deals with 'em
scan_spec_instr:
	psha		

	ldx		tempw
	ldaa		0,x
		
	; check for JSR which has opcodes $15, $16, $17
	ldab		jsr_op
	
	deca
	cba
	beq		dss_is_jsr

	deca		
	cba	
	beq		dss_is_jsr
	
	bra		dss_done_bad

dss_is_jsr:
	ldy		#jsr_mnem
	jsr		print_mnem
	ldaa		0,x			; get the op
	cmpa		#$17
	beq		dss_jsr_dir		; direct
	cmpa		#$16			
	beq		dss_jsr_extend		; extended
	
	; otherwise indirect

dss_jsr_dir:
	ldaa		1,x			; load the direct byte
	jsr		print_number
	inx
	inx					; increment X to next instr
	bra		dss_done_good		; all done

dss_jsr_extend:	
	pshx
	ldd		1,x			; load D with extended word	
	xgdx
	jsr		print_address
	pulx
	inx					; increment X to next instr
	inx
	inx
	bra		dss_done_good

dss_done_bad:
	ldab		#$ff
dss_done_good:
	pula
	rts



; print_mnem just prints the 4 characters at Y
print_mnem:
	pshd					; saves state of D
	ldaa		#13			; loads LF character
	jsr		put_char		; transmits serially. see "serial.asm"
	ldaa		#10			; loads CR character
	jsr		put_char		; transmits serially. see "serial.asm"
	ldaa		0,y			; loads first character of mnemonic
	jsr		put_char		; transmits serially. see "serial.asm"
	iny					; increments pointer to next of mnemonic
	ldaa		0,y			; loads second character of mnemonic
	jsr		put_char		; transmits serially. see "serial.asm"
	iny					; increments pointer to next of mnemonic
	ldaa		0,y			; loads third character of mnemonic	
	jsr		put_char		; transmits serially. see "serial.asm"
	iny					; increments pointer to next of mnemonic
	ldaa		0,y			; loads fourth character of mnemonic	
	jsr		put_char		; transmits serially. see "serial.asm"
	iny					; increments pointer to next of mnemonic
	ldaa		#32			; loads space character
	jsr		put_char		; transmits serially. see "serial.asm"
	puld					; restores state of D
	rts


; we need the mnemonics table, duh
#include "mnemtable.asm"

