 	name	dasm
;++
;
; Disassembler for absolute images of Z-80, 8080, 8051, or 8086 code.
;
; Traces code before disassembling to determine what's code and what's data,
; and where there should be labels.
;
; Copyright (C) 1994-1999 by John Wilson.  All rights reserved.
;
; This program may be distributed freely provided that no charge is made for
; distribution and proper credit is given to John Wilson in all versions.
;
; 01/31/92	JMBW	Created.
; 02/02/92	JMBW	Added Intel mnemonics.
; 02/04/92	JMBW	8086 .COM files.
; 03/10/94	JMBW	Fixed names of undoc'ed IX/IY halves to common usage.
; 12/13/97	JMBW	8051 mode.  V1.1.
; 06/12/99	JMBW	Improved support for 80386 and later.
; 08/21/99	JMBW	Preliminary PDP-8 support.  V1.2.
;
;--
	.radix	8
;
tab=	11
lf=	12
cr=	15
;
op	macro	name,opcode,mask,dispat
	local	foo,bar
	db	mask,opcode
ifidn <name>,<''>
	db	0		;;no output
else
ifidn <name>,<"">
	db	200		;;same as previous mnemonic
else
foo	db	bar,name
bar=	$-foo-1
endif
endif
	dw	dispat		;;addr called to display operands
				;;dispat-2 is called during trace (DRY)
	endm
;
opt	macro	name,opcode,mask,dispat
	local	foo,bar
	db	mask,opcode
foo	db	bar,name,tab	;;same thing with a tab after it
bar=	$-foo-1
	dw	dispat
	endm
;
; Word (16-bit) versions of above:
opw	macro	name,opcode,mask,dispat
	local	foo,bar
	dw	mask,opcode
ifidn <name>,<''>
	db	0		;;no output
else
ifidn <name>,<"">
	db	200		;;same as previous mnemonic
else
foo	db	bar,name
bar=	$-foo-1
endif
endif
	dw	dispat		;;addr called to display operands
				;;dispat-2 is called during trace (DRY)
	endm
;
opwt	macro	name,opcode,mask,dispat
	local	foo,bar
	dw	mask,opcode
foo	db	bar,name,tab	;;same thing with a tab after it
bar=	$-foo-1
	dw	dispat
	endm
;
; Stuff that precedes the beginning of a mnemonic table
porg=	word ptr -5		;routine to print "ORG" statement
plabel=	word ptr -3		;routine to print a label
ocsize=	byte ptr -1		;opcode size in bytes (1 or 2)
;
symtab	macro	nam,org,lab,siz	;start a symbol table
	dw	&org		;routine to print "ORG" statement
	dw	&lab		;routine to print a label
	db	&siz		;opcode size (1 or 2 bytes)
&nam	label	byte		;beginning of table
	endm
;
; Symbol table is in the first 16 KB of OBUF, and contains two bits for each of
; the possible 64 K addresses of the Z-80.  Of each pair, the even-numbered bit
; is set if that address is jumped to, and the odd bit is set if that address
; is referred to as data.
;
inst=	125			;even bits mean it's an instruction (jump dest)
data=	252			;odd bits mean it's data (can be both)
stsize=	40000			;size of symbol table in bytes (16 KB)
;
code	segment
	assume	cs:code
;
; Assumed registers:
;
; In general, DF=0 (always),
; BP=Z-80 PC (while disassembling),
; and DS=IBUF, ES=OBUF (after init).
;
start:	; start here
	cld			;move forwards
	push	cs		;toss CS to ES
	pop	es
	; parse command line
	mov	ax,3700h	;func=get SWITCHAR
	int	21h		;into DL (throughout startup code)
	mov	si,80h		;pt at JCL
	lodsb			;get length
	cbw			;AH=0
	mov	bx,ax		;copy
	mov	[bx+si],ah	;.ASCIZ
nxtatm:	; parse next atom from command line
	call	skip		;skip blanks
	jz	eol		;eol
	cmp	al,dl		;SWITCHAR?
	je	swit1		;yes
	 jmp	fn		;no, must be filename
swit1:	; handle switch
	inc	si		;eat it
	lodsw			;get the switch, colon
	and	al,not 40	;upper case
	cmp	al,'I'		;Intel mnemonics?
	je	switi
	cmp	al,'N'		;native mnemonics?
	je	switn
	cmp	ah,':'		;others must have colon
	jne	badjcl
	cmp	al,'S'		;disassembly starting addr
	je	swits
	cmp	al,'E'		;disassembly ending addr
	je	swite
	cmp	al,'T'		;transfer address (or @file (contains list))
	je	switt
	cmp	al,'F'		;file starting addr (if not 100h)
	je	switf
	cmp	al,'C'		;CPU type
	je	switc
badjcl:	; invalid switch, punt with help message
	mov	di,offset help+2 ;point at help text
	mov	cx,lhelp	;length
	mov	al,'~'		;char to replace with SWITCHAR
	push	cs		;copy CS to ES
	pop	es
bjcl1:	repne	scasb		;look for next ~
	jne	bjcl2		;none left
	mov	es:[di-1],dl	;replace with SWITCHAR
	jmp	short bjcl1	;keep looking
bjcl2:	mov	si,offset help	;pt at msg
	jmp	errout		;punt
eol:	jmp	init		;init and go
swits:	; /S:starting disassembly address (default=0100)
	call	gethex		;get #
	jc	badjcl		;error
	mov	cs:dstart,ax	;save
	jmp	short nxtatm	;loop
swite:	; /E:ending disassembly address (default=FFFF)
	call	gethex		;get #
	jc	badjcl		;error
	mov	cs:dend,ax	;save
	jmp	short nxtatm	;loop
switf:	; /F:file load address (default=0100)
	call	gethex		;get #
	jc	badjcl		;error
	mov	cs:faddr,ax	;save
	jmp	short nxtatm	;loop
switc:	; /C:cpu CPU type
	call	getwd		;get keyword
	jc	badjcl		;error if none
	mov	ax,offset cpus	;point a table
	call	tbluk		;look up this CPU
	jc	badjcl		;error if not found
	mov	cs:mnem,ax	;save starting addr of mnem table
	jmp	nxtatm		;loop
switi:	; /I Intel 8080/8085 mnemonics (default is Zilog)
	dec	si		;unget next char (don't want colon)
	mov	cs:mnem,offset intel ;set ptr
	jmp	nxtatm		;loop
switn:	; /N native (80x86) mnemonics
	dec	si		;unget next char (don't want colon)
	mov	cs:mnem,offset iapx86 ;set ptr
	jmp	nxtatm		;loop
switt:	; /T:transfer address (or @file -- list of addresses)
	call	gethex		;get #
	jc	tradr1		;nope
	mov	cs:xfer,ax	;save
	jmp	nxtatm
tradr1:	lodsb			;get char
	cmp	al,'@'		;@?
	jne	badjcl
tradr2:	lodsb			;skip blanks/tabs
	or	al,al		;EOL?
	jz	badjcl
	cmp	al,' '		;blank or CC?
	jbe	tradr2		;yes, ignore
	dec	si		;back up
	mov	di,si		;save
tradr3:	lodsb			;get a char
	cmp	al,' '		;blank/ctrl char?
	jbe	tradr4		;yes, end of filename
	cmp	al,dl		;SWITCHAR?
	jne	tradr3		;loop if not
tradr4:	dec	si		;unget
	xor	dh,dh		;get a 0
	xchg	dh,[si]		;save, mark end
	xchg	di,dx		;save dx, get start of string
	mov	ax,3D00h	;func=open, /RONLY
	int	21h		;do it
	jc	tradr5		;oops
	mov	cs:xhand,ax	;save
	mov	dx,di		;restore
	mov	[si],dh		;replace terminating char
	jmp	nxtatm		;loop
tradr5:	mov	si,offset xfrerr ;msg
	jmp	errout
fn:	; must be a filename
	mov	di,offset fname	;pt at buffer
	xor	bx,bx		;no extension yet
fn1:	lodsb			;get a char
	cmp	al,' '		;end of filename?
	jbe	fn5
	cmp	al,dl		;hm? (SWITCHAR)
	je	fn5
	cmp	al,'/'		;pathname separator?
	je	fn4
	cmp	al,'\'		;either is OK with me
	je	fn4
	cmp	al,'.'		;OK, then could it be an extension?
	je	fn3
fn2:	stosb			;bo-ring
	jmp	short fn1	;loop
fn3:	; . seen, this is the extension unless / or \ follows
	mov	bx,di		;copy ptr
	jmp	short fn2	;save the .
fn4:	; / or \, if we saw a . it wasn't the extension
	xor	bx,bx		;zap
	jmp	short fn2	;save
fn5:	; end of filename
	dec	si		;unget terminator
	test	bx,bx		;do we have an extension?
	jnz	fn6		;yes
	; add .COM extension
	mov	bx,di		;ext will be here
	mov	ax,"C."		;.C
	stosw
	mov	ax,"MO"		;OM
	stosw
fn6:	xor	al,al		;mark end
	stosb
	; open the input file
	push	ds		;save
	push	dx
	push	bx
	xor	bx,bx		;first close old STDIN
	mov	ah,3Eh		;func=close
	int	21h
	push	cs		;copy cs
	pop	ds		;to ds
	mov	dx,offset fname	;pt at filename
	mov	ax,3D00h	;func=open, /RONLY
	int	21h		;should appear on STDIN
	jc	inoper		;punt
	; now create output file
	pop	bx		;restore ptr to extension
	inc	bx		;+1
	mov	di,bx		;copy
	mov	ax,[bx]		;get 1st 2 chars of ext
	and	ax,not 20040	;cvt to upper
	cmp	ax,"SA"		;AS
	jne	notasm
	mov	al,[bx+2]	;get 3rd char
	and	al,not 40
	cmp	al,'M'		;ASM?
	je	opokay		;sigh, just write on STDOUT
notasm:	mov	ax,"SA"		;AS
	stosw
	mov	ax,'M'		;M<0>
	stosw
	mov	bx,1		;STDOUT
	mov	ah,3Eh		;func=close old STDOUT
	int	21h
	mov	dx,offset fname	;pt at filename
	xor	cx,cx		;mode=0
	mov	ah,3Ch		;func=create
	int	21h
	jnc	opokay		;opened OK
	; open error, print msg (on STDERR!)
	mov	si,offset enterr ;error entering file
	jmp	short errout
inoper:	; input file open error
	mov	si,offset lukerr ;error looking up file
errout:	; dump msg, CS:SI=<cnt>'message'
	push	cs		;toss CS to DS
	pop	ds
	lodsw			;get length
	mov	dx,si		;addr
	mov	cx,ax		;len
	mov	bx,2		;STDERR
	mov	ah,40h		;func=write
	int	21h
	mov	ax,4C01h	;func=punt
	int	21h
opokay:	; (gasp) everying is OK
	pop	dx		;restore
	pop	ds
	jmp	nxtatm		;around for more
;
init:	; initialize memory to HALTs
	; (so we won't get lost if they JMP into space)
	mov	ax,seg ibuf	;pt at bufs
	mov	ds,ax
	mov	es,ax		;for now
	xor	di,di		;pt at begn
	mov	cx,8000h	;32KW
	mov	ax,166+(400*166) ;fill with HALT instructions
	rep	stosw		;leaving di at 0 again
	; load the file
	mov	dx,cs:faddr	;get file load addr
	xor	bx,bx		;STDIN
	mov	ah,3Fh		;func=read
	sub	cx,dx		;get length (CX=0 from REP STOSW above)
	jnz	load1		;not reading 64 K, skip
	; all because DOS can't read more than 64 K -1 at a time (groan)
	mov	ch,80h		;count=8000h (32 KB)
	int	21h		;do the read
	jc	lderr		;err
	add	dx,ax		;update load addr
	mov	ah,3Fh		;func=read
load1:	int	21h		;read (either way we got here)
	jc	lderr		;err
	add	ax,dx		;update load addr
	dec	ax		;-1=last addr
	mov	cs:last,ax	;save it
	cmp	cs:dend,ax	;is end off end of file?
	jbe	$+6		;no
	 mov	cs:dend,ax	;yes, fix
	mov	ax,seg obuf	;pt at output buf
	mov	es,ax
	; erase symbol table (di still 0 from above)
	mov	cx,stsize/2	;zap symbol table
	xor	ax,ax		;0's
	rep	stosw		;yep (leave di pointing at free buf space)
	; trace from xfer address(es)
	mov	bx,cs:xhand	;get handle, if any
	or	bx,bx
	jnz	xa1		;file is open, read it
	; easy case, just one transfer address
	mov	bp,cs:xfer	;get transfer address
	call	dry		;dry run (trace execution)
	jmp	short xa6
lderr:	mov	si,offset lderrm ;error msg
	jmp	errout		;go dump it
xa1:	push	ds		;save
	push	es		;toss ES to DS
	pop	ds
	mov	dx,di		;pt at free space (=STSIZE)
	mov	cx,177000-stsize-1 ;get # free bytes -1
	mov	ah,3Fh		;func=read
	int	21h		;do it
	jc	xa3
	push	ax		;save
	mov	ah,3Eh		;func=close
	int	21h
	pop	bx		;restore
	mov	[bx+di],dl	;mark EOF with 0 (low byte of STSIZE)
	mov	si,dx		;point at begn
xa2:	call	skip		;skip blanks
	jz	xa5		;eof
	push	ax		;save start char
	cmp	al,'#'		;#?
	jne	$+3
	 inc	si		;yes, skip
	call	gethex		;get an address (DL=0 from STSIZE above)
	pop	bx		;[restore char]
	jc	xa5		;no more
	cmp	bl,'#'		;#?
	je	xa4		;yes
	mov	bp,ax		;set PC
	pop	ds		;restore
	push	si		;save
	call	dry		;dry trace
	pop	si		;save
	push	ds		;save again
	mov	ax,es		;copy ES to DS
	mov	ds,ax
	jmp	short xa2	;loop for next addr
xa3:	mov	si,offset xrderr ;read error
	jmp	errout
xa4:	; #HHHH, enter HHHH in data S.T.
	mov	bl,data		;flag
	call	addsym		;add to table
	jmp	short xa2	;next
xa5:	pop	ds		;restore
xa6:	; do the initial ORG statement
	mov	ax,cs:dstart	;starting addr
	mov	bp,ax		;set PC
	mov	si,cs:mnem	;pt at table
	call	word ptr cs:porg[si] ;print ORG
	call	crlf		;crlf
	mov	ax,cs:dend	;get ending addr
	mov	cs:last,ax
	jmp	newblk		;actually disassemble
;+
;
; "Dry run" entry.
;
; Enter with BP set to instruction to start at.
;
; Instructions are disassembled from this point on until we reach an
; instruction at which we must stop (HLT, RST 0, unconditional RET, JP (HL), or
; any undefined opcode).
;
; We follow unconditional branches;  when we encounter CALLs and conditional
; branches we recursively take both paths.
;
; Once we have entered the program at every possible entry point (initially the
; only entry is at 100h (by default), but after looking at our listing the user
; may find jump tables, interrupt service routines, etc.  that we couldn't
; interpret, and so they may tell us about other entries to get a better
; disassembly).
;
;-
dry:	mov	ax,bp		;get PC
	call	chksym		;defined?
	test	ah,inst		;as an instruction?
	jnz	dry3		;yes, no point in continuing
	mov	ax,bp		;no, define it
	mov	bl,inst		;as a jump destination
	call	addsym
xdry:	; (enter here if above already done)
	cmp	bp,cs:last	;off EOF?
	ja	dry3
	; init prefixes in case 8086
	mov	byte ptr cs:opsize,0
	mov	byte ptr cs:adsize,0
	mov	si,cs:mnem	;pt at table
	cmp	byte ptr cs:ocsize[si],2 ;2-byte opcodes?
	je	dry4
drypfx:	; reenter here to search for prefixed opcodes
	call	getb		;get byte
	mov	dl,al		;copy
dry1:	lods	word ptr cs:[si] ;get opcode,,mask
	and	al,dl		;AND this opcode with mask
	cmp	al,ah		;does this give the opcode we want?
	je	dry2
	lods	byte ptr cs:[si] ;no, get length
	and	ax,177		;remove "ditto" bit, AH=0
	add	si,ax		;skip
	inc	si		;skip vector
	inc	si
	jmp	short dry1	;loop
dry2:	; got it
	lods	byte ptr cs:[si] ;get length
	and	ax,177		;remove "ditto" bit, AH=0
	add	si,ax		;skip
	lods	word ptr cs:[si] ;get vector
	dec	ax		;-2 (JMP SHORT before display routine)
	dec	ax
	call	ax		;call it
	mov	ax,bp		;get PC
	call	chksym		;defined?
	test	ah,inst		;as a jump dest?
	jz	xdry		;no, around for more
dry3:	; we've been here before, don't get caught in a loop
	ret
dry4:	; disassembling code with 2-byte opcodes
	call	getw		;get word
	mov	dx,ax		;copy
dry5:	lods	word ptr cs:[si] ;get mask
	and	ax,dx		;AND this opcode with mask
	cmp	ax,cs:[si]	;match?
	lea	si,[si+2]	;[advance]
	je	dry2
	lods	byte ptr cs:[si] ;no, get length
	and	ax,177		;remove "ditto" bit, AH=0
	add	si,ax		;skip
	inc	si		;skip vector
	inc	si
	jmp	short dry5	;loop
;+
;
; Actually disassemble.
;
; Come back to NEWBLK each time we hit a new symbol that might not be code, to
; see whether it's referenced code, referenced data, or dead data.
;
;-
newblk:	; start a new block of code or data
	mov	ax,bp		;get PC
	cmp	ax,cs:last	;EOF?
	ja	eof1
	call	chksym		;on symbol table?
	jz	newbk0		;yes
	test	ah,inst		;code?
	jz	newbk1		;no
	jmp	next		;yes, handle it
newbk0:	; it's a dead location, see if more than one (might be half of DW)
	mov	ax,bp		;get PC again
	inc	ax		;+1
	call	chksym		;next loc too?
	jnz	newbk2		;yes, treat this one as data (no label)
	; dead code, say so and dump it until next symbol (drop through)
newbk1:	; loc has a label, print it
	push	ax		;save flag
	mov	ax,bp		;addr
	mov	si,cs:mnem	;pt at table
	call	word ptr cs:plabel[si] ;print label
	pop	ax		;restore
	test	ah,inst		;is it a jump target?
	jnz	next1		;yes
	test	ah,data		;it was data right?
	jz	newbk3		;must have been dead (from above)
newbk2:	; it's data
	mov	ax,tab+(400*'d') ;<tab>d
	stosw
	mov	ax,'b'+(400*tab) ;b<tab>
	stosw
	call	imm8		;display data
	jmp	short newblk	;try next
eof1:	jmp	eof2		;can't reach from here
next1:	jmp	next2
newbk3:	; dead code, dump as DB statements until next label or EOF
	mov	si,offset dead	;pt at msg
	mov	cx,9d		;LEN('<tab>; dead<cr><lf>')
	rep	movs [di],byte ptr cs:[si] ;copy
newbk4:	; dump next line
	xor	bx,bx		;offset (0-7)
	push	bx		;save
	mov	ax,tab+(400*'d') ;<tab>d
	stosw
	mov	ax,'b'+(400*tab) ;b<tab>
	stosw
	jmp	short newbk6
newbk5:	; dump next byte
	mov	ax,bp		;get PC
	cmp	ax,cs:last	;eof?
	ja	newbk7
	call	chksym		;symbol defined?
	jnz	newbk7		;yes, stop dumping
	mov	al,','		;,
	stosb
newbk6:	call	getb		;get a byte
	mov	dl,al
	call	hex2		;display
	cmp	dl,' '		;printable?
	jb	$+7
	cmp	dl,'~'
	jbe	$+4
	 mov	dl,'.'		;replace with . if not
	pop	bx		;restore
	mov	cs:fname[bx],dl	;save for ASCII dump
	inc	bx		;count it
	push	bx
	cmp	bl,8d		;line full?
	jb	newbk5		;loop if not
newbk7:	; finished line
	pop	cx		;restore count
	mov	ax,tab+(400*';') ;comment
	stosw
	mov	si,offset fname	;pt at buffer
	rep	movs [di],byte ptr cs:[si] ;copy
	call	crlf		;flush it
	mov	ax,bp		;get next addr
	cmp	ax,cs:last	;eof?
	ja	eof1
	call	chksym		;on ST?
	jz	newbk4		;loop if not
	jmp	newblk		;restart if so
;
next:	; process next instruction
	mov	ax,bp		;get PC
	cmp	ax,cs:last	;EOF?
	ja	eof1
	call	chksym		;see if symbol
	jz	next2
	; display a label
	mov	ax,bp		;addr
	mov	si,cs:mnem	;pt at table
	call	word ptr cs:plabel[si] ;print label
next2:	; we're definitely going to go for it
	mov	al,tab		;write a tab
	stosb
	mov	si,cs:mnem	;pt at table
	cmp	byte ptr cs:ocsize[si],2 ;2-byte opcodes?
	je	ins4
	; init in case 8086 mode (seg prefixes, etc.)
	mov	byte ptr cs:segpfx,0 ;none yet
	mov	byte ptr cs:reppfx,0 ;no rept pfx either
	mov	byte ptr cs:opsize,0 ;should have switch for seg default
	mov	byte ptr cs:adsize,0
inspfx:	; reenter here to search for prefixed opcodes
	call	getb		;get byte
	mov	dl,al		;copy
ins1:	; enter here with opcode in DL already
	lods	word ptr cs:[si] ;get mask
	and	al,dl		;mask
	cmp	al,ah		;is this it?
	je	ins2
	lods	byte ptr cs:[si] ;no, get length
	inc	si		;(+2 to skip vector)
	inc	si
	test	al,al		;ditto bit?
	js	ins1		;yes, loop
	mov	cx,si		;copy ptr+3
	cbw			;AH=0
	add	si,ax		;skip
	jmp	short ins1	;loop
ins2:	; found it
	lods	byte ptr cs:[si] ;get length
	test	al,al		;ditto bit?
	js	ins3		;yes
	cbw			;AH=0
	mov	cx,ax		;copy
	rep	movs byte ptr [di],byte ptr cs:[si] ;copy name
	lods	word ptr cs:[si] ;get dispatch addr
	call	ax		;call it
	jmp	short next	;loop
ins3:	; ditto flag, use previous valid addr
	lods	word ptr cs:[si] ;get dispatch addr
	mov	si,cx		;get prev addr
	dec	si		;correct
	dec	si
	mov	cl,cs:[si-1]	;get length
	xor	ch,ch		;CH=0
	rep	movs byte ptr [di],byte ptr cs:[si] ;copy name
	call	ax		;dispatch
	jmp	next		;loop
ins4:	; disassembling code with 2-byte opcodes
	call	getw		;get word
	mov	dx,ax		;copy
ins5:	lods	word ptr cs:[si] ;get mask
	and	ax,dx		;AND this opcode with mask
	cmp	ax,cs:[si]	;match?
	lea	si,[si+2]	;[advance]
	je	ins2
	lods	byte ptr cs:[si] ;no, get length
	and	ax,177		;remove "ditto" bit, AH=0
	add	si,ax		;skip
	inc	si		;skip vector
	inc	si
	jmp	short ins5	;loop
eof2:	call	flush		;flush output buf
	mov	ax,4C00h	;func=exit, no error
	int	21h
;
	jmp	short cbpfx1
cbpfx:	; CB instruction prefix
	mov	si,offset cbtab	;pt at table
	pop	ax		;flush R.A.
	jmp	short inspfx	;jump back in
cbpfx1:	mov	si,offset cbtab	;trace entry
	pop	ax		;flush R.A.
	jmp	drypfx		;back in
;
	jmp	short ddpfx1
ddpfx:	; DD instruction prefix
	mov	dh,'x'		;IX
	mov	si,offset ixtab	;pt at table
	pop	ax		;flush R.A.
	jmp	short inspfx	;jump back in
ddpfx1:	mov	si,offset ixtab	;trace entry
	pop	ax		;flush R.A.
	jmp	drypfx		;back in
;
	jmp	short edpfx1
edpfx:	; ED instruction prefix
	mov	si,offset edtab	;pt at table
	pop	ax		;flush R.A.
	jmp	inspfx		;jump back in
edpfx1:	mov	si,offset edtab	;trace entry
	pop	ax		;flush R.A.
	jmp	drypfx
;
	jmp	short ddpfx1	;use IX code
fdpfx:	; FD instruction prefix
	mov	dh,'y'		;IY
	mov	si,offset ixtab	;pt at table
	pop	ax		;flush R.A.
	jmp	inspfx		;jump back in
;
	jmp	short pfx0Fa
pfx0F:	; 0F instruction prefix
	mov	si,offset tab0F	;pt at table
	pop	ax		;flush R.A.
	jmp	inspfx		;jump back in
pfx0Fa:	mov	si,offset tab0F	;trace entry
	pop	ax		;flush R.A.
	jmp	drypfx		;back in
;
porgh:	; print hex "ORG"
	mov	si,offset org1	;pt at it
	mov	cx,5		;<TAB>org<TAB>
	rep	movs [di],byte ptr cs:[si] ;copy
	jmp	hexw		;display it, return
;
plabh:	; print hex label
	call	labl		;print L plus four hex digits
	mov	al,':'		;:
	stosb
	ret
;
crlf:	; write <CRLF>, maybe flush buffer
	mov	ax,cr+(400*lf)	;<CRLF>
	stosw			;write it
	cmp	di,-2000	;in last block of buf?
	jae	flush		;yes, flush buf
	ret
;
flush:	; flush output buffer
	push	ds		;save regs
	push	es
	push	es		;copy ES to DS
	pop	ds
	mov	dx,stsize	;pt at buffer
	mov	cx,di		;length
	sub	cx,dx
	mov	bx,0001h	;stdout
	mov	ah,40h		;func=write
	int	21h		;flush buffer
	pop	es		;restore
	pop	ds
	mov	di,dx		;reset ptr
	ret
;
	subttl	operand decoding routines
;+
;
; No operands.
;
;-
	ret
	nop
no:	jmp	short crlf
;+
;
; Same as above, but stop if tracing (end of this branch -- RET etc.).
;
;-
	pop	ax		;flush R.A.
	ret			;return to caller's caller
nostop:	call	crlf		;<CRLF>
stop:	; jump here on HALT, RST 0, RET, etc.
	pop	ax		;lose return addr
	jmp	newblk		;start a new block (may be dead)
;+
;
; Two register operands.
;
;-
	ret
	nop
rr:	call	reg1		;display 1st
	stosb			;,
	call	reg		;2nd
	jmp	short crlf
;+
;
; Register, immediate byte.
;
;-
	inc	bp		;eat the byte
	ret
rn:	call	reg1		;display it
	stosb			;,
	jmp	short imm8	;immediate byte, return
;
	inc	bp		;eat 1 byte
	ret
imm8:	call	getb		;get another byte
dbyte:	; enter here with data byte in AL
	push	ax		;save
	call	hexb		;do the hex
	pop	dx		;restore
dchar:	; enter here with data byte in DL, hex already displayed
	cmp	dl,' '		;printing char?
	jb	crlf		;no
	cmp	dl,'~'		;hm?
	ja	crlf
	mov	ax,tab+(400*';') ;comment
	stosw
	mov	al,"'"		;'
	mov	ah,dl		;char
	stosw
	mov	al,"'"		;'
	stosb
	jmp	crlf
;+
;
; r (in bits 3-5 of opcode).
;
;-
	ret
	nop
r1:	call	reg1		;display
	jmp	crlf
;+
;
; a,(mm)   ("a," already displayed).
;
;-
	jmp	short mm1
amm:	dec	di		;unput tab
	call	mm		;(mm)
	jmp	crlf
mm1:	; trace entry
	call	getw		;get address
	mov	bl,data		;it's data
	jmp	addsym		;add to symbol table
;+
;
; (mm),a.
;
;-
	jmp	short mm1
mma:	call	mm		;(mm)
	stosb			;,
	mov	al,'a'		;a
	stosb
	jmp	crlf
;+
;
; dd,nn.
;
;-
	jmp	short ddnn1
ddnn:	call	regpr		;dd
	mov	al,','		;,
	stosb
	call	getw		;get a word
	call	hexw		;display
	jmp	crlf
ddnn1:	; trace entry
	inc	bp		;eat a word
	inc	bp
	ret
;+
;
; dd,(mm).
;
;-
	jmp	short mm1
ddmm:	call	regpr
	mov	al,','		;,
	stosb
	call	mm		;(mm)
	jmp	crlf
;
mm:	; 16-bit address
	mov	al,'('		;(
	stosb
	call	getw		;mm
	call	labl
	mov	al,')'		;)
	stosb
	mov	al,','		;often needed
	ret
;+
;
; (mm),dd.
;
;-
	jmp	short mm1
mmdd:	call	mm		;(mm)
	stosb			;,
	call	regpr		;dd
	jmp	crlf
;+
;
; rp.
;
;-
	ret
	nop
rp:	call	regpr		;dd
	jmp	crlf
;+
;
; r.
;
;-
	ret
	nop
r:	call	reg		;display reg
	jmp	crlf
;+
;
; b,r.
;
;-
	ret
	nop
br:	mov	al,dl		;get opcode
	mov	cl,3		;bit count
	shr	al,cl		;right 3
	and	ax,7		;isolate
	or	ax,",0"		;convert to ascii, add a comma
	stosw			;b,
	jmp	short r		;later
;+
;
; JP cc,nn.
;
;-
	jmp	short cndjmp
ccnn:	mov	bl,dl		;get opcode
	shr	bl,1		;right 2 bits
	shr	bl,1
	and	bx,16		;isolate
	mov	ax,word ptr cs:cond[bx] ;look up condition
	stosw			;save
	mov	al,','		;,
	cmp	al,ah		;already written?
	je	$+3
	 stosb
	call	getw		;get addr
	call	labl		;display
	jmp	crlf
;+
;
; JP nn.	(won't come back)
;
;-
	jmp	short undjmp
nn:	call	getw		;get addr
	call	labl		;display
	call	crlf		;crlf
	jmp	stop		;execution stops (gone somewhere else)
;+
;
; CALL nn.
;
; Like JP nn only we will return.
;
;-
	jmp	short cndjmp
callnn:	call	getw		;get addr
	call	labl		;display
	jmp	crlf		;crlf, return
;
; The next few instructions are the whole beauty of the disassembler!
cndjmp:	; trace entry for conditional jump and CALL
	call	getw		;get a word
cndjm1:	; enter with address in AX
	push	bp		;save where we are
	mov	bp,ax		;trace the called code
	call	dry		;recurse
	pop	bp		;restore
	ret			;continue where we left off
;
undjmp:	; trace entry for unconditional jump
	call	getw		;get a word
undjm1:	; enter with address in AX
	mov	bp,ax		;transfer to there
	pop	ax		;flush R.A.
	jmp	dry		;enter in sym tab w/o stopping on it
;+
;
; JR e.
;
;-
	jmp	short undjr
ujr:	; unconditional jump-relative
	mov	dh,dl		;set "don't continue" flag
	jmp	short cjr1
;
	jmp	short cndjr
cjr:	; conditional jump-relative
	xor	dh,dh		;clear the "don't continue" flag
cjr1:	; enter here with DH<>0 if unconditional jump rel8
	call	getb		;get offset
	cbw			;sxt
	push	ax		;save
	add	ax,bp		;add PC address
	call	labl		;display as a label
	mov	ax,tab+(400*';') ;comment
	stosw
	mov	al,'$'		;$
	stosb
	pop	ax		;get offset
	inc	ax		;+2 to compensate for (opcode,displacement)
	inc	ax
	jz	$+5		;no offset (sit 'n spin)
	 call	offst		;display it
	push	dx		;save flag
	call	crlf		;crlf
	pop	dx		;restore
	; start a new blk (with a label) if jump was unconditional
	test	dh,dh		;continue?
	jnz	$+3		;yes
	 ret
	jmp	stop
;
undjr:	; trace entry for JR
	call	getb		;get offset
	cbw			;SXT
	add	ax,bp		;add offset
	jmp	short undjm1	;continue
;
cndjr:	; trace entry for JR cc, and DJNZ
	call	getb		;get offset
	cbw			;SXT
	add	ax,bp		;add offset
	jmp	short cndjm1	;continue
;+
;
; RET cc.
;
;-
	ret			;doesn't end trace since it's conditional
	nop
cc:	mov	bl,dl		;get opcode
	shr	bl,1		;right 2
	shr	bl,1
	and	bx,16		;isolate
	mov	ax,word ptr cs:cond[bx] ;look up condition
	stosb			;save 1st byte
	cmp	ah,','		;just 1 char?
	je	cc1		;yes
	mov	al,ah		;copy
	stosb			;write 2nd char
cc1:	jmp	crlf
;+
;
; RST n.
;
;-
	jmp	short rst1
rst:	mov	al,dl		;copy
	and	al,70		;isolate
	call	hex2		;display
	mov	al,'h'		;h
	stosb
	cmp	dl,307		;RST 0?
	je	$+5		;yes
	 jmp	crlf
	call	crlf		;CRLF
	jmp	stop		;stop
rst1:	; trace entry
	cmp	dl,307		;RST 0?
	jne	$+3		;no
	 pop	ax		;lose return address
	ret			;drop through to caller's caller if RST 0
;+
;
; [IN	A,](n).
;
;-
	inc	bp		;eat n
	ret
upn:	dec	di		;unput tab
	call	pn		;(n)
	jmp	crlf
;+
;
; (n),a.
;
;-
	inc	bp		;eat n
	ret
pna:	call	pn		;(n)
	mov	ax,"a,"		;,a
	stosw
	jmp	crlf
;
pn:	; print next byte in paranthesis
	mov	al,'('		;(
	stosb
	call	getb		;get n
	call	hexb		;print in hex
	mov	al,')'		;)
	stosb
	ret
;+
;
; r,(c).
;
;-
	ret
	nop
rpc:	call	reg1		;reg
	mov	ah,'('		;,(
	stosw			;(, in al already)
	mov	ax,")c"		;c)
	stosw
	jmp	crlf
;+
;
; Anything else is a DB pseudo-op.
;
; Since we're pretty definitely screwing up (unless they knew about some
; undocumented instructions that we don't), we'll treat this as a HALT and stop
; tracing/disassembling.
;
;-
	pop	ax		;lose return addr
	ret			;return to caller's caller
ubite:	dec	di		;unput tab
	jmp	short bite
;
	pop	ax		;lose r.a.
	ret			;return to caller's caller
bite:	mov	al,dl		;opcode
	call	dbyte		;display
	jmp	stop		;execution stops
;
	subttl	annoying IX/IY instructions
;+
;
; In these routines, when I say "IX" I mean IX or IY, either x or y was put in
; DH by DDPFX and FDPFX.
;
; r,(ix+d).
;
;-
	inc	bp		;eat D
	ret
rixm:	call	reg1		;dest reg
	stosb			;,
	call	ixp		;(ix+d)
	jmp	crlf
;+
;
; (ix+d),r.
;
;-
	inc	bp		;eat D
	ret
ixmr:	call	ixp		;(ix+d)
	stosb			;,
	jmp	r
;+
;
; (ix+d),n.
;
;-
	jmp	short ixeat2
ixmn:	call	ixp		;(ix+d)
	stosb			;,
	jmp	imm8		;n
;+
;
; ix,nn.
;
;-
	jmp	short ixeat2
ixnn:	call	ix		;ix
	stosb			;,
	call	getw		;nn
	call	hexw
	jmp	crlf
;+
;
; ix,(mm).
;
;-
	jmp	short ixeat2
ixmm:	call	ix		;ix
	stosb			;,
	call	mm		;(mm)
	jmp	crlf
;+
;
; (mm),ix.
;
;-
	jmp	short ixeat2
mmix:	call	mm		;(mm)
	stosb			;,
	call	ix		;ix
	jmp	crlf
;+
;
; ix.
;
;-
ixeat2:	; trace to eat 2 bytes
	inc	bp
	inc	bp		;steal a ret from UIXX trace entry
	ret
	ret
uixx:	dec	di		;unput tab
ixx:	call	ix		;ix
	jmp	crlf
;+
;
; (ix+d).
;
;-
	inc	bp		;eat d
	ret
uixm:	dec	di		;unput tab
	call	ixp		;(ix+d)
	jmp	crlf
;
	inc	bp		;eat d
	ret
ixm:	call	ixp		;(ix+d)
	jmp	crlf
;+
;
; ix,rp.  (if rp is hl, use the "opposite" index reg instead)
;
;-
	ret
	nop
ixpp:	call	ix		;ix
	stosb			;,
	mov	al,dl		;get opcode
	and	al,60		;isolate rp bits
	cmp	al,40		;hl?
	je	ixpp1		;yes
	call	rp		;rp
	jmp	crlf
ixpp1:	xor	dh,('x' xor 'y') ;use opposite of 1st operand if hl
	call	ix		;iy
	jmp	crlf
;+
;
; JP (ix).
;
; Unconditional jump, something new starts immediately after it.
;
;-
	pop	ax		;lose R.A.
	ret			;return to caller's caller
ixxp:	mov	al,'('		;(
	stosb
	call	ix		;ix
	mov	al,')'		;)
	stosb
	call	crlf
	jmp	stop
;+
;
; Undefined opcode with DD or FD prefix.
;
;-
	pop	ax		;lose R.A.
	ret			;return to caller's caller
ixbite:	mov	ax,"D0"		;0D
	cmp	dh,'x'		;ix, right?
	je	$+4
	 mov	ah,'F'		;no, change to 0F
	stosw
	mov	ax,"hD"		;Dh
	stosw
	mov	al,','		;,
	stosb
	mov	al,dl		;get opcode
	call	dbyte		;go dump it
	jmp	stop
;+
;
; Instructions prefixed by DD CB or FD CB come here.
; These are new rot/shifts and bit instructions which reference ix or iy.
; Note that the displacement comes between the second and third opcode bytes,
; for your convenience.
;
;-
;;;;;;;; should parse so we stop on invalid opcodes
	jmp	short ixeat2	;no point in parsing, always 2 more bytes
cbix:	call	getb		;get displacement
	mov	bl,al		;save (NEXT won't hurt it)
	mov	si,offset cbixtb ;pt at new table
	jmp	inspfx		;continue scanning
;+
;
; b,(ix+d) (d was already read & saved in bl).
;
;-
	; no trace entry, handled in CBIX
bixm:	mov	al,dl		;get opcode
	mov	cl,3		;bit count
	shr	al,cl		;right 3
	and	ax,7		;isolate
	or	ax,",0"		;0, (convert to ASCII, add comma)
	stosw
	;jmp	short cbixm	;(ix+d)
;+
;
; (ix+d)  (d already read and saved in bl).
;
;-
	; no trace entry
cbixm:	mov	al,'('		;(
	stosb
	call	ix		;ix
	mov	al,bl		;d
	call	ixpd
	jmp	crlf
;+
;
; Undefined opcode, but we already read DD/FD, CB, d, opcode.
; Dump the whole mess.  Sorry about no ASCII for d.
;
;-
	; no trace entry
cbixbt:	mov	al,'D'		;assume IX (DD)
	cmp	dh,'x'		;right?
	je	$+4
	 mov	al,'F'		;iy, use FD
	mov	byte ptr cs:ddcb+1,al ;patch
	mov	si,offset ddcb	;pt at string
	mov	cx,12/2		;len('0DDh,0CBh,')
	rep	movs word ptr [di],word ptr cs:[si] ;copy
	mov	al,bl		;get d
	cmp	al,0A0h		;need leading 0?
	jb	$+4
	 movs	[di],byte ptr cs:[si] ;copy a 0
	call	hex2		;nn
	mov	ax,",h"		;h,
	stosw
	mov	al,dl		;get final opcode byte
	call	dbyte		;gasp!
	jmp	stop
;+
;
; ix or iy.
;
;-
ix:	mov	al,'i'		;i
	mov	ah,dh		;x or y
	stosw
	mov	al,','		;may be needed
	ret
;+
;
; lx or ly.
;
;-
lx:	mov	al,'l'		;l
	mov	ah,dh		;x or y
	stosw
	mov	al,','		;may be needed
	ret
;+
;
; hx or hy.
;
;-
hx:	mov	al,'h'		;h
	mov	ah,dh		;x or y
	stosw
	mov	al,','		;may be needed
	ret
;+
;
; (ix+d) or (iy+d).
;
;-
ixp:	mov	ax,"i("		;(i
	stosw
	mov	al,dh		;x or y
	stosb
	call	getb		;get offset
ixpd:	; enter here with d in al
	call	offst		;show the offset
	mov	al,')'		;)
	stosb
	mov	al,','		;may be needed
	ret
;
offst:	; display AL as +/-n
	mov	ah,'+'		;assume positive
	or	al,al		;negative?
	jns	offst1		;no
	mov	ah,'-'		;make '-'
	neg	al		;and take abs. val
offst1:	mov	es:[di],ah	;sign
	inc	di
	cmp	al,9d		;can use 1 decimal digit?
	jbe	$+5		;yep
	 jmp	hexb		;offset
	or	al,'0'		;convert
	stosb
	ret
;
	subttl	really annoying undocumented IX/IY instructions
;+
;
; The basic principal is that the DD or FD prefix tells the Z-80 to substitute
; IX [IY] for the next reference to HL, or (IX+d) [(IY+d)] for (HL).  The
; Lifeboat Model II BIOS uses some of these.  I haven't tried them for ugly
; cases like DD;  LD H,H (which operand is really H?).
;
; It seems risky to assume that just because these work on your Z-80 means they
; work on all Z-80's by all manufacturers, but that's not my prob.
;
; r,lx.
;
;-
	ret
	nop
rixl:	call	reg1		;r
	stosb			;,
	call	lx		;lx
	jmp	crlf
;+
;
; r,lx.
;
;-
	ret
	nop
rixh:	call	reg1		;r
	stosb			;,
	call	hx		;hx
	jmp	crlf
;+
;
; lx,r.
;
;-
	ret
	nop
ixlr:	call	lx		;lx
	stosb			;,
	call	reg		;r
	jmp	crlf
;+
;
; hx,r.
;
;-
	ret
	nop
ixhr:	call	hx		;hx
	stosb			;,
	call	reg		;r
	jmp	crlf
;+
;
; lx.
;
;-
	ret
	ret
uixl:	dec	di		;unput tab
ixl:	call	lx		;ix
	jmp	crlf
;+
;
; hx.
;
;-
	ret
	ret
uixh:	dec	di		;unput tab
ixh:	call	hx		;hx
	jmp	crlf
;
	subttl	Intel mnemonics for the same thing
;+
;
; MOV r,r.
;
;-
	ret
	nop
irr:	call	ireg1		;r1
	mov	al,','		;,
	stosb
	mov	al,dl		;r2
	call	ireg
	jmp	crlf
;+
;
; r,n.
;
;-
	inc	bp
	ret
irn:	call	ireg1		;r
	mov	al,','		;,
	stosb
	jmp	imm8		;immediate byte
;+
;
; rp,nn.
;
;-
	jmp	short ieat2
irpnn:	call	iregpr		;rp
	mov	al,','		;,
	stosb
	call	getw		;nn
	call	hexw
	jmp	crlf
;
ieat2:	; trace entry -- eat nn
	inc	bp		;yep
	inc	bp
	ret
;+
;
; mm.
;
;-
	jmp	short imm1
imm:	call	getw		;get address
	call	labl		;make a label
	jmp	crlf
imm1:	; trace entry
	call	getw		;get address
	mov	bl,data		;data reference
	jmp	addsym		;add symbol, return
;+
;
; rp.
;
;-
	ret
	nop
irpp:	call	iregpr		;rp
	jmp	crlf
;+
;
; RST n.
;
; Intel uses the value of the 3-bit field (0::7), not the value that you get
; when you isolate it (0,8,10h,18h, etc.).
;
;-
	jmp	short irst1
irst:	mov	al,dl		;get value
	mov	cl,3		;bit count
	shr	al,cl		;right 3
	and	al,7		;isolate
	or	al,'0'		;cvt to ASCII
	stosb			;save
	push	dx		;save value
	call	crlf		;crlf
	pop	dx		;restore
	cmp	dl,307		;RST 0?
	je	$+3
	 ret
	jmp	stop		;yes, code stops here
irst1:	; trace entry
	cmp	dl,307		;RST 0?
	jne	$+3
	 pop	ax		;flush r.a. if so
	ret
;+
;
; IN/OUT n.
;
; Like iimm except there's no point in saying if it's an ASCII char.
;
;-
	inc	bp
	ret
iio:	call	getb		;get a byte
	call	hexb		;display
	jmp	crlf
;+
;
; r.  (in bits 5:3 of opcode)
;
;-
	ret
	nop
ir1:	call	ireg1		;print it
	jmp	crlf
;+
;
; r.  (in bits 2:0 of opcode)
;
;-
	ret
	nop
ir:	call	ireg		;print it
	jmp	crlf
;+
;
; n.  Display in ASCII too if printing char.
;
;-
	inc	bp
	ret
iimm:	jmp	imm8		;yep
;+
;
; Non-8080/8085 instruction, try it as a Z-80 instruction.
; If that's wrong too then they'll get a comment as well as a DB, but tough!
;
;-
	jmp	short non85a
non85:	push	dx		;save opcode
	call	crlf		;crlf
	pop	dx		;restore
	mov	al,tab		;<tab>
	stosb
	mov	si,offset z80	;pt at Z-80 table
	dec	bp		;unget the opcode
	jmp	inspfx		;go
non85a:	; trace entry
	mov	si,offset z80	;pt at Z-80 table
	dec	bp		;unget the opcode
	jmp	drypfx		;go
;
	subttl	Intel 8051 routines
;+
;
; ACALL dest
;
;-
	jmp	short acall1
acall:	call	abs11		;get addr
	call	labl		;display
	jmp	crlf		;<CRLF>, return
acall1:	call	abs11		;get addr
	jmp	cndjm1		;trace both paths
;
abs11:	; fetch 11-bit abs addr for ACALL/AJMP
	; low 11 bits are from opcode, high 5 are from PC after fetch
	call	getb		;get low bits
	mov	ah,dl		;copy high bits
	mov	cl,5		;shift count
	shr	ah,cl		;right-justify, pad with zeros
	mov	dx,bp		;fetch addr
	and	dh,not 7	;trim low 3 bits
	or	ah,dh		;compose
	ret
;+
;
; Inst "a,xxx"
;
;-
	jmp	short tsop51
asrc51:	mov	ax,",a"		;'a,'
	stosw
	call	opn51		;handle operand
crlf51:	mov	dx,cs:immop	;get immediate operand
	test	dx,dx		;if any
	js	crlf52		;no
	jmp	dchar		;display it, CRLF, return
crlf52:	jmp	crlf
;+
;
; Single operand instructions.
;
;-
	jmp	short tsop51
sop51:	call	opn51		;display
	jmp	crlf
tsop51:	; trace entry
	and	dl,0Fh		;isolate
	cmp	dl,4		;immed?
	je	tsop52
;;; will this ever happen?  if it's a single operand, 0100 means A not imm8
;;; (which is handled separately in MCS51 symbol table)
	cmp	dl,5		;direct?
	jne	tsop53
tsop52:	inc	bp		;eat immed/direct byte
tsop53:	ret
;+
;
; Handle generic 8051 operand.
;
; xxxx0100	#imm8
; xxxx0101	direct
; xxxx011n	@Rn
; xxxx1nnn	Rn
;
;-
opn51:	mov	cs:immop,-1	;assume not immediate
	and	dl,0Fh		;isolate
	test	dl,10		;xxxx1rrr?
	jz	opn52		;no
	; a,rn
	mov	al,'r'		;r
	mov	ah,dl		;copy reg #
	add	ah,'0'-10	;0-7
	stosw
	mov	al,','		;often needed
	ret
opn52:	cmp	dl,04h		;immed?
	jne	opn53		;no
	; a,#n
	mov	al,'#'		;#
	stosb
	call	getb		;get data byte
	xor	ah,ah		;>=0
	mov	cs:immop,ax	;save for display
	call	hexb		;do the hex
	mov	al,','		;often needed
	ret
opn53:	cmp	dl,05h		;direct?
	jne	opn54		;no
	; a,n
	call	getb		;get the byte
	call	hexb		;write it out
	mov	al,','		;often needed
	ret
opn54:	; a,@rn
	mov	ax,"r@"		;'@r'
	stosw
	mov	al,dl		;copy
	add	al,'0'-6	;convert to 0-1
	stosb
	mov	al,','		;often needed
	ret
;+
;
; Inst direct,A
;
;-
	inc	bp		;eat direct addr
	ret
dira51:	call	getb		;get byte
	call	hexb		;display
	mov	ax,"a,"		;',a'
	stosw
	jmp	crlf
;+
;
; Inst [dest,]bit
;
;-
	inc	bp		;eat it
	ret
bit51:	call	getb		;get byte
	call	bitnam		;display it
	jmp	crlf
;+
;
; Display name of 8051 bit in AL.
;
; Well OK for now we just give the hex bit #, but we could use a table for the
; ones that do have names.
;
; al	returns ','
;
;-
bitnam:	jmp	hexb		;display, return
;;	mov	al,','		;(if we have our own code some day)
;;	ret
;+
;
; Inst direct,#imm8
;
;-
	jmp	short diri52
diri51:	call	getb		;get byte
	call	hexb		;display
	stosb			;','
	jmp	imm8		;immediate value
diri52:	call	getw		;get imm8,,addr
	xor	ah,ah		;AH=0
	mov	bl,data		;data symbol
	jmp	addsym		;add to symbol table
;+
;
; AJMP dest
;
;-
	jmp	short ajmp1
ajmp:	jmp	acall		;looks just like ACALL
ajmp1:	call	abs11		;get addr
	jmp	undjm1		;change direction
;+
;
; CJNE A,xxx
;
;-
	jmp	short cjnea1
cjnea:	call	opn51		;sort out 2nd operand
cjne:	stosb			;,
	call	getb		;get a byte
	cbw			;SXT
	push	ax		;save
	add	ax,bp		;add PC address
	call	labl		;display as a label
	mov	ax,tab+(400*';') ;comment
	stosw
	mov	dx,cs:immop	;get immediate operand
	test	dx,dx		;if any
	js	cjne1		;no
	cmp	dl,' '		;printing char?
	jb	cjne1		;no
	cmp	dl,'~'		;hm?
	ja	cjne1
	mov	al,"'"		;'
	mov	ah,dl		;char
	stosw
	mov	ax," '"		;'<sp>
	stosw
cjne1:	mov	al,'$'		;$
	stosb
	pop	ax		;get offset
	add	ax,3		;+3 to compensate for opcode bytes
	jz	cjne2		;no offset (sit 'n spin)
	 call	offst		;display it
cjne2:	jmp	crlf		;<CRLF>, return
cjnea1:	inc	bp		;skip direct/immed operand
	jmp	cndjr		;handle conditional rel8 jump
;+
;
; CJNE A,xxx
;
;-
	jmp	short cjnea1	;imm8/rel8 bytes as usual
cjnei:	call	opn51		;sort out 1st operand
	stosb			;,
	mov	dl,04h		;pretend inst had an imm8 arg
	call	opn51		;handle that
	jmp	short cjne	;pick it up from there
;+
;
; DJNZ direct,rel8
;
;-
	jmp	short cjnea1	;skip operand, handle conditional rel8 jump
djnzd:	call	opn51		;handle operand
djnzd1:	; (entry from JB51)
	stosb			;,
	call	getb		;get a byte
	cbw			;SXT
	push	ax		;save
	add	ax,bp		;add PC address
	call	labl		;display as a label
	mov	ax,tab+(400*';') ;comment
	stosw
	mov	al,'$'		;$
	stosb
	pop	ax		;get offset
	add	ax,3		;+3 to compensate for opcode bytes
	jz	djnzd2		;no offset (sit 'n spin)
	 call	offst		;display it
djnzd2:	jmp	crlf		;<CRLF>, return
;+
;
; DJNZ Rn,rel8
;
;-
	jmp	short djnz52
djnz51:	call	opn51		;handle operand
	stosb			;,
	jmp	cjr		;go finish up (2-byte opcode)
djnz52:	jmp	cndjr
;+
;
; Jx bit,rel8
;
;-
	jmp	short jb52
jb51:	call	getb		;get byte
	call	bitnam		;display
	jmp	short djnzd1	;the rest is like DJNZ
jb52:	inc	bp		;eat bit address
	jmp	cndjr		;handle conditional jump
;+
;
; LCALL addr16
;
;-
	jmp	short lcall1
lcall:	call	getw		;get next 2 bytes
	xchg	al,ah		;MSB first
	call	labl		;display
	jmp	crlf		;<CRLF>, return
lcall1:	call	getw		;get next 2 bytes
	xchg	al,ah		;MSB first
	jmp	cndjm1		;trace both paths
;+
;
; LJMP dest
;
;-
	jmp	short ljmp1
ljmp:	jmp	short lcall	;looks just like LCALL
ljmp1:	call	getw		;get next 2 bytes
	xchg	al,ah		;MSB first
	jmp	undjm1		;change direction
;+
;
; MOV Rn,A
;
;-
	ret
	nop
movrna:	call	opn51		;Rn
	stosb			;,
	mov	al,'a'		;a
	stosb
	jmp	crlf
;+
;
; MOV Rn,direct
;
;-
	inc	bp		;eat direct addr
	ret
movrnd:	call	opn51		;Rn
	stosb			;,
	mov	dl,05h		;direct
	call	opn51
	jmp	crlf
;+
;
; MOV Rn,#imm8
;
;-
	inc	bp		;eat immed value
	ret
movrni:	call	opn51		;Rn
	stosb			;,
	mov	dl,04h		;#imm8
	call	opn51
	jmp	crlf
;+
;
; MOV direct,A
;
;-
	inc	bp		;skip direct addr
	ret
movda:	call	opn51		;direct
	stosb			;,
	mov	al,'a'		;a
	stosb
	jmp	crlf
;+
;
; MOV direct,xxx  (xxx is Rn or @Ri)
;
;-
	inc	bp		;skip direct adr
	ret
movdx:	push	dx		;save
	mov	dl,05h		;direct
	call	opn51
	stosb			;,
	pop	dx		;restore
	call	opn51		;Rn or @Ri
	jmp	crlf
;+
;
; MOV direct,direct
;
;-
	jmp	short movdd1
movdd:	inc	bp		;skip source addr
	call	opn51		;dest
	stosb			;,
	dec	bp		;back to dest addr
	dec	bp
	mov	dl,05h		;(restore DL=direct)
	call	opn51		;source
	inc	bp		;skip dest again
	jmp	crlf
movdd1:	inc	bp		;skip both addrs
	inc	bp
	ret
;+
;
; MOV direct,#imm8
;
;-
	jmp	short movdd1	;skip both args
movdi:	call	opn51		;direct
	stosb			;,
	mov	dl,04h		;#imm8
	call	opn51
	jmp	crlf
;+
;
; MOV @Ri,A
;
;-
	ret
	nop
movira:	call	opn51		;@Ri
	stosb			;,
	mov	al,'a'		;a
	stosb
	jmp	crlf
;+
;
; MOV @Ri,direct
;
;-
	inc	bp		;skip direct addr
	ret
movird:	call	opn51		;@Ri
	stosb			;,
	mov	dl,05h		;direct
	call	opn51
	jmp	crlf
;+
;
; MOV @Ri,#imm8
;
;-
	inc	bp		;eat immed operand
	ret
moviri:	call	opn51		;@Ri
	stosb			;,
	mov	dl,04h		;#imm8
	call	opn51
	jmp	crlf
;+
;
; MOV bit,C
;
;-
	inc	bp		;skip bit addr
	ret
movbc:	call	bitnam		;bit
	stosb			;,
	mov	al,'c'		;c
	stosb
	jmp	crlf
;+
;
; MOV DPTR,#imm16
;
;-
	jmp	short mdptr1
mdptr:	call	getw		;get value
	xchg	al,ah		;><
	jmp	imm16		;value, <CRLF>
mdptr1:	inc	bp		;skip imm16 value
	inc	bp
	ret
;+
;
; MOVX A,@Ri
;
;-
	ret
	nop
movxar:	or	dl,06h		;make sure OPN51 recognizes it
	call	opn51		;@Ri
	jmp	crlf
;+
;
; MOVX @Ri,A
;
;-
	ret
	nop
movxra:	or	dl,06h		;make sure OPN51 recognizes it
	call	opn51		;@Ri
	stosb			;,
	mov	al,'a'		;a
	stosb
	jmp	crlf
;+
;
; PUSH/POP direct
;
;-
	inc	bp		;eat direct addr
	ret
stk51:	mov	dl,05h		;direct
	call	opn51
	jmp	crlf
;
	subttl	Intel 8086 routines
;+
;
; Segment & size prefixes.
;
;-
	ret
	nop
es86:	mov	byte ptr cs:segpfx,'e' ;es:
	jmp	short pfx1
;
	ret
	nop
cs86:	mov	byte ptr cs:segpfx,'c' ;cs:
	jmp	short pfx1
;
	ret
	nop
ss86:	mov	byte ptr cs:segpfx,'s' ;ss:
	jmp	short pfx1
;
	ret
	nop
ds86:	mov	byte ptr cs:segpfx,'d' ;ds:
	jmp	short pfx1
;
	ret
	nop
fs86:	mov	byte ptr cs:segpfx,'f' ;fs:
	jmp	short pfx1
;
	ret
	nop
gs86:	mov	byte ptr cs:segpfx,'g' ;gs:
	jmp	short pfx1
;
	ret
	nop
repp86:	mov	cs:reppfx,dl	;save opcode
	jmp	short pfx1
;
	jmp	short ops86a
opsz86:	not	cs:opsize	;invert operand size
	jmp	short pfx1
ops86a:	; trace entry
	not	cs:opsize	;same as above
	jmp	short dpfx1
;
	jmp	short ads86a
adsz86:	not	cs:adsize	;invert address size
pfx1:	pop	ax		;lose return addr
	mov	si,offset iapx86 ;pt at instructions again
	jmp	inspfx
ads86a:	; trace entry
	not	cs:adsize	;same as above
dpfx1:	pop	ax
	mov	si,offset iapx86
	jmp	drypfx		;but dry
;+
;
; r/m,r.
;
;-
	jmp	short eatrm1
rmr86:	call	modrmg		;r/m
	stosb			;,
	call	reg86a		;r
	jmp	crlf
eatrm1:	jmp	eatrm		;eat mod-r/m byte
;+
;
; r,r/m.
;
;-
	jmp	short eatrm1
rrm86:	call	reg86g		;r
	stosb			;,
	call	modrm		;r/m
	jmp	crlf
;+
;
; MOVSX, MOVZX instructions.
;
;-
	jmp	short eatrm1
zx86:	inc	dx		;set "word" bit (was 0)
	call	reg86g		;r
	stosb			;,
	dec	dx		;byte source
	call	ptr86
	call	modrm		;mod-r/m
	jmp	crlf
;
	jmp	short eatrm1
zx86d:	; 32-bit dest, 16-bit source
	mov	byte ptr cs:opsize,-1 ;always 32-bit dest
	call	reg86g		;r (DL<0> already set)
	stosb			;,
	inc	byte ptr cs:opsize ;back to 0
	call	ptr86		;word source
	call	modrm		;mod-r/m
	jmp	crlf
;
	jmp	short eatrm1
les86:	; LES instruction, has even opcode (W bit=0)
	inc	dx		;opcode is even, make odd (word)
	jmp	short rrm86
;+
;
; a,imm.
;
;-
	jmp	short eatai
ai86:	xor	bl,bl		;al/ax
	test	dl,1		;byte?
	jz	ri86		;yes
	cmp	byte ptr [bp+1],4Ch ;could it be an exit?
	jne	ri86		;no
	mov	ax,bp		;copy
	inc	ax		;skip the word
	inc	ax
	mov	cs:addr4C,ax	;save addr of next inst (not if 32-bit!)
ri86:	; reg,imm
	call	reg86		;display
imm86:	stosb			;,
	test	dl,1		;byte or word?
	jnz	$+5		;word
	 jmp	imm8		;yep
	call	getw		;get a word
	test	byte ptr cs:opsize,-1 ;32 bits?
	jnz	imm32		;yes
	jmp	imm16		;just 16
imm32:	push	ax		;save word
	call	getw		;get a word
	cmp	ah,0A0h		;need leading 0?
	jb	imm32a		;no
	mov	byte ptr es:[di],'0' ;yes
	inc	di
imm32a:	call	hex4		;write high 16 bits
	pop	ax		;restore
	call	hex4		;low 16
	mov	al,'h'		;h
	stosb
	jmp	crlf		;crlf
eatai:	; trace entry
	test	dl,1		;byte or word?
	jz	eati86		;byte
	cmp	byte ptr [bp+1],4Ch ;exit command?
	jne	eati86		;no
	inc	bp		;+2
	inc	bp
;;; +2 more if 32 bits
	mov	cs:addr4C,bp	;save
	ret
eati86:	; trace entry for general immediates
	inc	bp		;eat a byte
	test	dl,1		;word flag set?
	jnz	$+3		;yes
	 ret
	inc	bp		;word anyway
	test	byte ptr cs:opsize,-1 ;32 bits?
	jz	$+4		;no
	 inc	bp		;+2 more
	 inc	bp
	ret
;+
;
; MOV mod-r/m,seg.
;
;-
	jmp	short eatrm2
rmsg86:	inc	dx		;opcode is 8C, make it odd (word)
	call	modrmg		;mod-r/m
	stosb			;,
	call	seg86a		;seg
	jmp	crlf
;+
;
; MOV seg,mod-r/m.
;
;-
	jmp	short eatrm2
sgrm86:	call	getb		;get a byte
	mov	bl,al		;copy
	call	seg86a		;seg
	mov	al,','		;,
	stosb
	inc	dx		;opcode is 8E, make it odd (word)
	call	modrm		;mod-r/m
	jmp	crlf
eatrm2:	jmp	eatrm1
;+
;
; r/m,imm.
; r/m,+imm8.
;
; Last 3 bits of opcode are in mod-r/m byte.
;
;-
	jmp	short eatrmi
rmi86:	call	getb		;get mod-r/m, opcode
	mov	bl,al		;save
	shr	bl,1		;right 2
	shr	bl,1
	and	bx,7*2		;isolate last 3 bits of opcode
	cmp	dl,0F6h		;which prefix is it?
	jb	$+5		;ADD, etc.
	 add	bl,10*2		;TEST, etc.
	mov	si,cs:add86[bx]	;get ptr
	mov	bl,cs:[si]	;get length
	inc	si
	mov	cx,bx		;copy
	rep	movs [di],byte ptr cs:[si] ;copy inst name
rmi86a:	; enter here with mod-r/m in AL if name already displayed
	mov	bl,al		;get mod-r/m byte
	call	ptr86		;ptr message
	call	modrm		;mod-r/m
	cmp	dl,83h		;+imm8?
	jne	rmi86b		;no, immediate value
	stosb			;save comma
	call	getb		;get byte
	call	offst		;offset
	jmp	crlf
rmi86b:	cmp	dl,0F6h		;which prefix?
	jb	rmi86c		;ADD, etc.
	and	bl,70		;isolate opcode
	cmp	bl,10		;TEST or ??? ?
	ja	rmi86d		;no
rmi86c:	jmp	imm86		;not sign-extended 8-bit #
rmi86d:	jmp	crlf		;no immediate op
eatrmi:	; trace entry
	call	eatrm		;eat mod-r/m
	cmp	dl,83h		;always imm8?
	je	ermi2		;yes
	cmp	dl,366		;which pfx?
	jb	ermi1		;ADD etc.
	and	bl,70		;isolate opcode
	cmp	bl,10		;TEST or ??? ?
	ja	ermi3		;no, no immediate val
ermi1:	jmp	eati86		;eat whatever
ermi2:	inc	bp
ermi3:	ret
;+
;
; MOV mod-r/m,imm.
;
; Apparently bits 5:3 of mod-r/m are don't cares.
;
;-
	jmp	short eatrmi
mrmi86:	call	getb		;get mod-r/m
	jmp	short rmi86a	;go display (DL is not 83h so no +imm8)
;+
;
; Conditional near jumps.
;
;-
	jmp	short cndnj
cnjmp:	call	getw		;get offset
	cwd			;SXT
	test	byte ptr cs:adsize,-1 ;32-bit addr?
	jz	cnjmp1		;no
	push	ax		;save
	call	getw		;get high word
;;	mov	dx,ax		;copy
	pop	ax		;restore
cnjmp1:	add	ax,bp		;add PC address
;;	adc	dx,...
	call	labl		;display as a label
	jmp	crlf		;CRLF, return
;
cndnj:	; trace entry for conditional near jump
	call	getw		;get offset
	cwd			;SXT
	test	byte ptr cs:adsize,-1 ;32-bit addr?
	jz	cndnj1		;no
	push	ax		;save
	call	getw		;get high word
;;	mov	dx,ax		;copy
	pop	ax		;restore
cndnj1:	add	ax,bp		;add PC address
;;	adc	dx,...
	jmp	cndjm1		;continue
;+
;
; JCXZ -- might be JECXZ if 67h prefix.
;
;-
	jmp	short jcxz3
jcxz1:	test	byte ptr cs:adsize,-1 ;JECXZ?
	jz	jcxz2		;no
	sub	di,4		;un-put "cxz<tab>"
	mov	ax,"ce"		;"ec"
	stosw
	mov	ax,"zx"		;"xz"
	stosw
	mov	al,tab		;tab
	stosb
jcxz2:	jmp	cjr
jcxz3:	jmp	cndjr
;
add86	dw	tadd,tor,tadc,tsbb,tand,tsub,txor,tcmp
	dw	ttest,ttstq,tnot,tneg,tmul,timul,tdiv,tidiv
;
tadd	db	4,'add',tab	;prefixed by 200, 201, 203
tor	db	3,'or',tab
tadc	db	4,'adc',tab
tsbb	db	4,'sbb',tab
tand	db	4,'and',tab
tsub	db	4,'sub',tab
txor	db	4,'xor',tab
tcmp	db	4,'cmp',tab
ttest	db	5,'test',tab	;prefixed by 366
ttstq	db	4,'???',tab	;;; seems to take an immediate value
tnot	db	4,'not',tab
tneg	db	4,'neg',tab
tmul	db	4,'mul',tab
timul	db	5,'imul',tab
tdiv	db	4,'div',tab
tidiv	db	5,'idiv',tab
;+
;
; IMUL r16,r/m16 (or 32,32)  386+ only
;
;-
imulaf:

if 0
!@#$	opt	'imul',257,377,imulaf


endif ;;;
;+
;
; mod-r/m.
;
; Last 3 bits of opcode are in mod-r/m byte.
;
;-
	jmp	short eatrmb
rm86:	call	getb		;get a byte
	mov	bl,al		;copy
	shr	bl,1		;convert to opcode*2
	shr	bl,1
	and	bx,7*2		;isolate
	mov	si,cs:inc86[bx]	;get ptr
	cmp	dl,8Fh		;POP instruction?
	jne	$+5
	 mov	si,offset tpop	;yes
	mov	bl,cs:[si]	;get length
	inc	si
	mov	cx,bx		;copy
	rep	movs [di],byte ptr cs:[si] ;copy
	mov	bl,al		;copy opcode
	call	ptr86		;PTR msg, if needed
	push	ax		;save
	call	modrm		;display operand
	call	crlf		;CRLF
	pop	ax		;restore
	and	al,70		;isolate
	cmp	al,40		;JMP or JMP FAR?
	jb	rm86a		;no
	cmp	al,50
	ja	rm86a
	jmp	stop
rm86a:	ret
eatrmb:	; trace entry
	call	eatrm		;eat it (preserve DL)
	cmp	dl,8Fh		;POP?
	je	ermb1		;yes, skip
	and	bl,70		;isolate
	cmp	bl,40		;JMP or JMP FAR?
	jb	ermb1
	cmp	bl,50
	ja	ermb1
	pop	ax		;flush R.A. if JMP
ermb1:	ret
;
inc86	dw	tinc,tdec,tcall,tcallf
	dw	tjmp,tjmpf,tpush,tpush	;7 undef'ed (works as PUSH on V20)
;
tinc	db	4,'inc',tab
tdec	db	4,'dec',tab
tcall	db	5,'call',tab
tcallf	db	6,'call',tab,'d'
tjmp	db	4,'jmp',tab
tjmpf	db	5,'jmp',tab,'d'
tpush	db	5,'push',tab
tpop	db	4,'pop',tab	;bits 5:3 of mod-r/m seem to be don't cares
;+
;
; ROL etc. mod-r/m,1/cl/imm8.
;
; Last 3 bits of opcode are in mod-r/m byte.
;
;-
	jmp	short trot86
rot86:	call	getb		;get a byte
	mov	si,ax		;copy
	shr	si,1		;convert to opcode*4
	and	si,34		;isolate 3 bits
	add	si,offset rol86	;get ptr
	mov	cx,4		;length
	rep	movs [di],byte ptr cs:[si] ;copy mnemonic
	mov	bl,al		;copy opcode
	call	ptr86		;PTR msg if needed
	call	modrm		;1st operand
	stosb			;,
	test	dl,10h		;imm8?
	jz	rot86b		;yes
	test	dl,2		;cl?
	jnz	rot86a		;yes
	mov	al,'1'		;1
	stosb
	jmp	crlf
rot86a:	mov	ax,"lc"		;cl
	stosw
	jmp	crlf
rot86b:	jmp	iio		;byte, crlf
;
trot86:	; trace entry
	call	eatrm		;eat r/m
	test	dl,10h		;imm8?
	jnz	$+3
	 inc	bp		;yes, eat it
	ret
;
rol86	db	'rol',tab
	db	'ror',tab
	db	'rcl',tab
	db	'rcr',tab
	db	'sal',tab	;;I bet this is really SHL
	db	'shr',tab
	db	'sal',tab	;;not really (but works!)
	db	'sar',tab
;+
;
; a,moffs.
;
;-
	jmp	short eata86
amof86:	xor	bl,bl		;al/ax/eax
	call	reg86		;yep
	stosb			;,
	call	moff86		;mem offset
	jmp	crlf
eata86:	; trace entry
	call	getw		;get offset
	mov	bl,data		;add to symbol table
	jmp	addsym		;and return
;+
;
; moffs,a.
;
;-
	jmp	short eata86
mofa86:	call	moff86		;mem offset
	stosb			;,
	xor	bl,bl		;al/ax/eax
	call	reg86		;yep
	jmp	crlf
;+
;
; byte reg,imm.
;
;-
	inc	bp
	ret
bi86:	mov	bl,dl		;copy reg #
	xor	dl,dl		;byte instruction
	jmp	ri86		;reg,imm
;+
;
; word reg,imm.
;
;-
	jmp	short eatw86
wi86:	mov	bl,dl		;copy reg #
	mov	dl,1		;word instruction
	jmp	ri86		;reg,imm
eatw86:	; trace entry
	inc	bp
	inc	bp
	ret
;+
;
; word reg.
;
;-
	ret
	nop
r86:	mov	bl,dl		;copy reg #
	mov	dl,1		;word instruction
	call	reg86		;reg name
	jmp	crlf
;+
;
; ax/eax,reg.
;
;-
	ret
	nop
xchg86:	mov	dh,dl		;save
	mov	dl,1		;word
	xor	bl,bl		;ax/eax
	call	reg86
	stosb			;,
	mov	bl,dh		;reg (dl=1 for word)
	call	reg86
	jmp	crlf
;+
;
; Short branches.
;
;-
	jmp	short undjr1
jsh86:	; unconditional short branch
	mov	si,offset tshort ;pt at string
	mov	cx,6/2		;LEN('short ')
	rep	movs word ptr [di],word ptr cs:[si] ;copy
	mov	dh,dl		;set "don't continue" flag
	jmp	cjr1		;finish up with Z-80 code
undjr1:	jmp	undjr		;trace
tshort	db	'short '
;+
;
; JMP near.
;
;-
	jmp	short undj86
jmpn86:	call	getw		;get offset
	cwd			;sign-extend
	test	byte ptr cs:adsize,-1 ;32-bit offset?
	jz	jn86a
	push	ax		;save
	call	getw		;get offset
;;	mov	dx,ax		;copy
	pop	ax
jn86a:	add	ax,bp		;compute addr
;;	adc	dx,...
	call	labl
	call	crlf		;crlf
	jmp	stop		;start new block
undj86:	; trace entry
	call	getw		;get a word
	test	byte ptr cs:adsize,-1 ;32-bit offset?
	jz	uj86a
	push	ax		;save
	call	getw		;get offset
;;	mov	dx,ax		;copy
	pop	ax
uj86a:	add	ax,bp		;compute addr
;;	adc	dx,...
	jmp	undjm1		;go follow the jump
;+
;
; JMP far.
;
;-
	pop	ax		;just lose
	ret
jmpf86:	call	calf86		;display address
	jmp	stop
;+
;
; CALL near.
;
;-
	jmp	short undc86
caln86:	call	getw		;get offset
	add	ax,bp		;compute addr
	call	labl		;label
	jmp	crlf		;crlf, return
undc86:	; trace entry
	call	getw		;get a word
	add	ax,bp		;find addr
	jmp	cndjm1		;recurse
;+
;
; CALL far.
;
;-
	jmp	short unfc86
calf86:	call	getw		;get offset
	push	ax
	call	getw		;seg
	call	hex4
	mov	al,':'		;:
	stosb
	pop	ax		;offset
	call	hex4
	jmp	crlf
unfc86:	add	bp,4		;skip seg:offset
	ret
;+
;
; PUSH/POP seg reg
;
;-
	ret
	nop
pseg86:	mov	bl,dl		;copy
	call	seg86a		;reg name
	jmp	crlf
;+
;
; PUSH immediate value.
;
;-
	jmp	short pim86a
pimm86:	call	getw		;get a word
	test	byte ptr cs:opsize,-1 ;32 bits?
	jnz	pimm32		;yes
	jmp	imm16		;just 16
pimm32:	jmp	imm32
pim86a:	call	getw		;get a word
	test	byte ptr cs:opsize,-1 ;32 bits?
	jz	pim86b
	call	getw		;get another word if so
pim86b:	ret
;+
;
; PUSH sign-extended 8-bit immediate value.
;
;-
	inc	bp		;eat imm val
	ret
pimm8:	call	getb		;get byte
	call	offst		;offset
	jmp	crlf
;+
;
; INT n.
;
; Stop on INT 20h, or INT 21h if prev instruction was MOV AX,4CXX.
;
;-
	jmp	short tint86
int86:	call	getb		;get a byte
	push	ax		;save
	call	hexb		;display
	call	crlf		;crlf
	pop	ax		;restore
	cmp	al,20h		;exit?
	je	int86a		;yes
	cmp	al,21h		;DOS call?
	jne	int86b		;no
	mov	ax,bp		;copy
	dec	ax		;-2
	dec	ax
	cmp	ax,cs:addr4C	;prev instruction=MOV AX,4CXX?
	jne	int86b		;no
int86a:	jmp	stop		;new block
int86b:	ret
tint86:	; trace entry
	call	getb		;get a byte
	cmp	al,20h		;exit?
	je	ti86a		;yes
	cmp	al,21h		;DOS call?
	jne	ti86b		;no
	mov	ax,bp		;copy
	dec	ax		;-2
	dec	ax
	cmp	ax,cs:addr4C	;prev inst=MOV AX,4CXX?
	jne	ti86b		;no
ti86a:	pop	ax		;flush r.a.
ti86b:	ret
;+
;
; RET[F] nnnn
;
;-
	jmp	short ret86a
ret86:	call	getw		;get parm
	call	hexw		;display
	call	crlf
	jmp	stop		;stop
ret86a:	; trace entry
	pop	ax		;flush (forget about parm)
	ret
;+
;
; ENTER imm8,imm16.
;
;-
	jmp	short ent86a
ent86:	call	getb		;imm8
	call	hexb
	stosb			;,
	call	getw		;imm16
	call	hexw
	jmp	crlf
ent86a:	add	bp,3		;skip 3
	ret
;+
;
; IN a,port.
;
;-
	inc	bp
	ret
in86:	xor	bl,bl		;zap reg #
	call	reg86		;al/ax/eax
	stosb			;,
	call	getb		;port #
	call	hexb
	jmp	crlf
;+
;
; IN a,DX.
;
;-
	ret
	nop
indx86:	xor	bl,bl		;zap reg #
	call	reg86		;al/ax/eax
	stosb			;,
	mov	ax,"xd"		;DX
	stosw
	jmp	crlf
;+
;
; OUT port,a.
;
;-
	inc	bp
	ret
out86:	call	getb		;get a byte
	call	hexb		;byte
	stosb			;,
	xor	bl,bl		;al/ax/eax
	call	reg86
	jmp	crlf
;+
;
; OUT DX,a.
;
;-
	ret
	nop
oudx86:	xor	bl,bl		;al/ax/eax
	call	reg86
	jmp	crlf
;+
;
; String instructions.
; Give the simple form unless we have to show it all.
;
; This is an ugly mess.
;
;-
	ret
	nop
cmps86:	call	rep86		;REP prefix
	mov	si,offset tcmps	;string
	mov	cx,4		;length
	test	byte ptr cs:adsize,-1 ;32-bit address?
	jnz	cmps1
	test	byte ptr cs:segpfx,-1 ;prefix?
	jz	cmps2		;no
cmps1:	inc	cx		;add tab
	rep	movs [di],byte ptr cs:[si] ;copy
	mov	bl,2		;2
	and	bl,cs:adsize	;or 0 if 16-bit
	or	bl,4		;4=[SI], 6=[ESI]
	call	ptr86		;PTR text
	call	modrm		;mod-r/m
	stosb			;,
	call	bdi86		;[di]
	jmp	crlf
cmps2:	jmp	stsz86		;go show size
tcmps	db	'cmps',tab

	ret
	nop
ins86:	; INS
	mov	si,offset tins
	jmp	stor86
tins	db	3,'ins'
;
	ret
	nop
lods86:	; LODS
	mov	si,offset tlods	;string
load86:	call	rep86		;repeat prefix
	mov	cx,4		;length
	rep	movs [di],byte ptr cs:[si] ;copy
	test	byte ptr cs:adsize,-1 ;32-bit addressing?
	jnz	src86		;yes, hard case
	test	byte ptr cs:segpfx,-1 ;prefix?
	jnz	src86		;yes
	 jmp	stsz86		;no, just show size
src86:	mov	al,tab		;tab
src86a:	stosb
	mov	bl,2		;[SI]
	and	bl,cs:adsize	;or 0
	or	bl,4		;4=[SI], 6=[ESI]
	call	ptr86		;PTR msg
	call	modrm		;show it
	jmp	crlf		;crlf, return
tlods	db	'lods'
;
	ret
	nop
movs86:	; MOVS
	call	rep86		;REP prefix
	mov	si,offset tmovs	;pt at string
	mov	cx,4		;length
	test	byte ptr cs:adsize,-1 ;32-bit addressing?
	jnz	dsrc86		;show dest and source if so
	test	byte ptr cs:segpfx,-1 ;seg prefix?
	jz	stsz86		;no, just show size
dsrc86:	inc	cx		;add tab
	rep	movs [di],byte ptr cs:[si] ;copy
	call	bdi86		;[di]
	mov	al,','		;,
	jmp	short src86a	;go show source
tmovs	db	'movs',tab
;
	ret
	nop
outs86:	; OUTS
	mov	si,offset touts	;string
	jmp	short load86	;go deal
touts	db	'outs'
;
	ret
	nop
scas86:	; SCAS
	mov	si,offset tscas
	jmp	short stor86
tscas	db	4,'scas'
;
	ret
	nop
stos86:	; STOS
	mov	si,offset tstos
stor86:	call	rep86		;prefix
	lods	byte ptr cs:[si] ;get length
	cbw
	mov	cx,ax		;in cx
	test	byte ptr cs:opsize,-1 ;normal addressing?
	jz	stsz86		;yes
	mov	al,tab		;tab
	stosb
	xor	bl,bl		;mod-r/m=memory
	call	ptr86		;PTR
	call	bdi86		;[di]
	jmp	crlf
stsz86:	; just show size on string instruction
	; si=ptr, cx=length
	rep	movs [di],byte ptr cs:[si] ;copy
	mov	al,'b'		;assume byte
	test	dl,1		;byte or word?
	jz	str86a		;byte
	mov	al,'w'		;word then
	test	byte ptr cs:opsize,-1 ;word or dword?
	jz	$+4		;word
	 mov	al,'d'		;dword
str86a:	stosb			;write size byte
	jmp	crlf		;crlf, return
;
tstos	db	4,'stos'
;+
;
; Display "[(E)DI]".
;
;-
bdi86:	mov	al,'['		;[
	stosb
	test	byte ptr cs:adsize,-1 ;32 bits?
	jz	$+5
	 mov	al,'e'		;e
	 stosb
	mov	ax,"id"		;di
	stosw
	mov	al,']'		;]
	stosb
	ret
;+
;
; XLAT (BYTE PTR xS:[(E)BX]).
;
;-
	ret
	nop
xlat86:	mov	bl,3		;mod-r/m=[EBX]
	test	byte ptr ds:adsize,-1 ;[EBX]?
	jnz	xlat1		;yes
	mov	bl,7		;mod-r/m=[BX]
	test	byte ptr ds:segpfx,-1 ;DS:?
	jnz	xlat1		;no
	jmp	crlf
xlat1:	mov	al,tab		;<tab>
	stosb
	mov	si,offset bytptr ;pt at msg
	mov	cx,11		;length
	rep	movs [di],byte ptr cs:[si] ;copy
	xor	dl,dl		;byte instruction
	call	modrm		;display seg:[(E)BX]
	jmp	crlf
;+
;
; Display a "BYTE PTR" message or whatever, if needed.
;
; bl	mod-r/m byte
; dl	opcode (b0=word)
;
;-
ptr86:	cmp	bl,300		;register?
	jae	ptr86b		;yes, no msg
	mov	cx,11		;length
	mov	si,offset bytptr ;pt at msg
	test	dl,1		;byte?
	jz	ptr86a		;yes
	add	si,cx		;skip
	test	byte ptr cs:opsize,-1 ;32 bits?
	jz	ptr86a		;skip
	add	si,cx		;skip again
	inc	cx		;count the "d"
ptr86a:	rep	movs [di],byte ptr cs:[si] ;copy text
ptr86b:	ret
;
bytptr	db	'byte ptr '
	db	'word ptr '
	db	'dword ptr '
;+
;
; Display operand shown by "mod r/m" byte.
;
; bl	mod r/m byte
; dl<0>	0 => byte, 1 => word
;
; Returns BL preserved, comma in AL.
;
;-
modrmg:	; getb the byte first
	call	getb		;get byte
	mov	bl,al		;save
modrm:	; byte in bl
	cmp	bl,300		;mod=3?
	jae	modrm4
;; see if 32-bit
	mov	al,bl		;copy
	and	al,307		;lose 3 bits in the middle
	cmp	al,6		;[DISP16]?
	je	moff86
	mov	cl,al		;save
	call	pfx86		;do prefix, if any
	mov	al,cl		;restore
	mov	cx,3		;bit/byte count
	sal	al,cl		;*8
	test	al,40		;r/m <3?
	jnz	$+4
	 sal	cl,1		;*2=6
	and	ax,70		;isolate bits 5:3
	add	ax,offset ea86	;index
	mov	si,ax		;point at name
	rep	movs [di],byte ptr cs:[si] ;copy all but ']'
	cmp	bl,100		;mod=0?
	jb	modrm1
	cmp	bl,200		;mod=2?
	jae	modrm3
	; [reg(s)+HH] (mod=1)
	push	bx		;save
	call	getb		;get byte
	call	offst		;do offset
	pop	bx
modrm1:	; [reg(s)] (mod=0)
	inc	si		;skip +
	movs	[di],byte ptr cs:[si] ;]
	mov	al,','		;may be needed
	ret
moff86:	; [HHHH] (mod=0, r/m=6)
	cmp	byte ptr cs:segpfx,0 ;prefix defined?
	jnz	$+8d
	 mov	byte ptr cs:segpfx,'d' ;use ds: to show it's a mem ref
	push	bx		;save
	call	pfx86		;do prefix
	call	getw		;get word
	call	labl		;label
	pop	bx		;restore
	mov	al,','		;may be needed
	ret
modrm3:	; [reg(s)+HHHH] (mod=2)
	movs	[di],byte ptr cs:[si] ;+
	push	bx		;save
	call	getw		;get word
	call	labl		;label
	pop	bx		;restore
	movs	[di],byte ptr cs:[si] ;copy ]
	mov	al,','		;may be needed
	ret
modrm4:	; register
	jmp	short reg86	;go
;
eatrm:	; same as above, but eat it (mod r/m byte returned in BL, DL preserved)
	call	getb		;get mod r/m
	push	ax		;save
	and	al,307		;isolate mode stuff
	cmp	al,6		;[DISP16]?
	je	eatr1
	cmp	al,100		;mod=0?
	jb	eatr3
	cmp	al,200		;mod=1?
	jb	eatr2
	cmp	al,300		;mod=2?
	jae	eatr3
eatr1:	call	getw		;get 16-bit offset
	mov	bl,data		;enter in symbol table
	call	addsym		;and return
	dec	bp		;undo next instruction
eatr2:	inc	bp		;eat byte offset
eatr3:	pop	bx		;restore mod-r/m into BL
	ret
;+
;
; Display register.
;
; bl	reg #
; dl<0>	1 => word, else byte
;
; Returns ',' in AL, BL preserved.
;
;-
reg86g:	call	getb		;get the byte
	mov	bl,al		;copy
reg86a:	mov	al,bl		;get from old mod r/m byte
	mov	cl,3		;bit count
	shr	al,cl		;right 3
	jmp	short $+4
reg86:	 mov	al,bl		;copy
	and	ax,7		;reg #
	sal	al,1		;left 1
	test	dl,1		;word?
	jz	r86a		;no
	add	al,20		;yes, skip to 16-bit regs
	cmp	byte ptr cs:opsize,0 ;really?
	jz	r86a		;yes
	mov	byte ptr es:[di],'e' ;no, 32-bit regs
	inc	di
r86a:	add	ax,offset gr86	;add base
	mov	si,ax		;point
	movs	word ptr [di],word ptr cs:[si] ;copy
	mov	al,','		;may be needed
	ret
;+
;
; Display segment register in BL.
;
;-
seg86a:	mov	al,bl		;copy
	mov	cl,3		;bit count
	shr	al,cl		;right 3
	jmp	short $+4
seg86:	 mov	al,bl		;copy
	sal	al,1		;*2
	and	ax,7*2		;isolate
	add	ax,offset s86	;add offset
	mov	si,ax		;copy
	movs	word ptr [di],word ptr cs:[si] ;copy the name
	ret
;+
;
; Display a repeat prefix, as appropriate.
;
; dl	prefixed opcode
;
;-
rep86:	xor	al,al		;load 0
	xchg	al,cs:reppfx	;zap prefix, get it
	or	al,al		;anything?
	jz	rep86d		;no
	push	si		;save
	mov	si,offset trepn	;pt at REPN
	mov	cx,3		;count for now
	cmp	al,145		;REPC or REPNC?
	jbe	rep86b		;yes, just show 'em
	cmp	dl,246		;REPE/REPNE, check opcode
	je	rep86a		;CMPS, show E/NE
	cmp	dl,256		;SCAS?
	jne	rep86c		;no
rep86a:	; show E/NE
	mov	ah,'e'		;load it
	jmp	short $+4	;show all
rep86b:	 mov	ah,'c'		;REPC/REPNC (NEC V20 only)
	test	al,1		;N?
	jnz	$+3
	 inc	cx		;yes
	rep	movs [di],byte ptr cs:[si] ;copy
	mov	al,ah		;copy letter
	mov	ah,tab
	stosw			;save letter
	pop	si
	ret
rep86c:	rep	movs [di],byte ptr cs:[si] ;copy REP
	mov	al,tab		;tab
	stosb
	pop	si		;restore
rep86d:	ret
;
trepn	db	'repn'
;+
;
; Display segment prefix, if one has been seen.
;
;-
pfx86:	xor	al,al		;load 0
	xchg	al,cs:segpfx	;set prefix
	test	al,al		;is there one?
	jz	pfx86a		;no
	stosb			;x
	mov	ax,":s"		;s:
	stosw
pfx86a:	ret
;+
;
; Display 16-bit immediate value, CRLF.
;
; ax	value
;
;-
imm16:	call	hexw		;display it
	jmp	crlf
;
ea86	db	'[bx+si+][bx+di+][bp+si+][bx+si+]' ;all 8 bytes
	db	'[si+]   [di+]   [bp+]   [bx+]'
gr86	db	'alcldlblahchdhbhaxcxdxbxspbpsidi' ;byte/word regs
s86	db	'escsssdsfsgss6s7' ;seg regs, s6/s7 are undefined
segpfx	db	?	;NZ => seg prefix
reppfx	db	?	;NZ => repeat prefix opcode
opsize	db	?	;NZ => 32-bit operands
adsize	db	?	;NZ => 32-bit addresses
addr4C	dw	-1	;addr of last MOV AX,4CXX instruction
;;; this would be better, not implemented yet:
ahval	dw	-1	;GE => known value of AH
alval	dw	-1	;GE => known value of AL
;
	subttl	PDP-8 routines
;+
;
; Memory reference instructions.
;
;-
	ret
	nop
mri:	mov	al,' '		;blank
	stosb
	test	dh,400/400	;indirect?
	jz	mri1		;no
	mov	ax," I"		;'I '
	stosw
mri1:	call	addr8		;compute address
	call	labl8		;print label name or number
	jmp	crlf
;+
;
; ISZ -- MRI with conditional skip
;
;-
	jmp	short isz1	;tracing is special
isz:	jmp	short mri	;but looks like any other MRI
isz1:	; trace conditional skip
	push	bp		;save where we are
	inc	bp		;skip following opcode
	inc	bp
	call	dry		;recurse
	pop	bp		;restore
	ret			;continue where we left off
;+
;
; JMS (jump to subroutine).
;
;-
	jmp	short jms9
jms8:	jmp	short mri	;looks like any other MRI
jms9:	test	dh,400/400	;indirect?
	jnz	jms11		;yes, don't know where to go
	call	addr8		;sort out address
	add	ax,ax		;byte addr
	push	bp		;save where we are
	mov	bp,ax		;trace the called code
	call	chksym		;defined?
	test	ah,inst		;as an instruction?
	jnz	jms10		;yes, no point in continuing
	mov	ax,bp		;no, define it
	mov	bl,inst		;as a jump destination
	call	addsym
	inc	bp		;but actually trace from the following word
	inc	bp
	call	xdry
jms10:	pop	bp		;restore
jms11:	ret			;continue where we left off
;+
;
; JMP
;
;-
	jmp	short jmp9
jmp8:	jmp	short mri	;looks like any other MRI
jmp9:	test	dh,400/400	;indirect?
	jnz	jmp10		;yes, don't know where to go
	call	addr8		;sort out address
	add	ax,ax		;byte addr
	jmp	undjm1		;unconditional jump
jmp10:	pop	ax		;flush R.A.
	ret			;return to caller's caller
;
addr8:	; figure out PDP-8 addr from MRI opcode in DX, return in AX
	mov	cx,dx		;copy
	mov	ax,bp		;copy addr
	shr	ax,1		;word addr
	dec	ax		;back to addr of opcode word
	and	ax,7600		;isolate page addr
	test	dl,200		;this page?
	jnz	addr9
	 xor	ax,ax		;no, page 0
addr9:	and	dl,177		;isolate offset within page
	or	al,dl		;combine
	ret
;+
;
; IOT nnn
;
;-
	ret
	nop
iot:	mov	al,' '		;blank
	stosb
	mov	ax,dx		;get opcode
	mov	cx,3		;3 digits
	call	octn
	jmp	crlf
;+
;
; Group 1 operate instructions.
;
;-
	ret			;nothing to it
	nop
opra:	mov	si,offset oprg1	;point at table
	jmp	short opr	;print, return
;+
;
; Group 2 operate instructions (including skips).
;
;-
	jmp	short oprb1
oprb:	mov	si,offset oprg2	;point at table
	jmp	short opr	;print, return
oprb1:	test	dl,170		;any "skip" bits set?
	jz	oprb2		;no
	push	bp		;save where we are
	inc	bp		;skip following opcode
	inc	bp
	call	dry		;recurse
	pop	bp		;restore
oprb2:	ret			;continue where we left off
;+
;
; Group 3 operate instructions.
;
;-
	ret
	nop
oprc:	mov	si,offset oprg3	;point at table
	jmp	short opr	;print, return
;+
;
; Compose "operate" instruction mnemonics.
;
; cs:si	pointer to list of bits to check and strings to add
; dl	low 8 bits of opcode word
;
;-
opr:	xor	bl,bl		;nothing written yet (so no blank)
opr1:	lods	word ptr cs:[si] ;fetch expected value,,mask
	test	al,al		;end?
	je	opr4
	and	al,dl		;isolate
	cmp	al,ah		;matches expected value?
	lods	byte ptr cs:[si] ;[fetch string length]
	cbw			;[AH=0]
	jz	opr2		;yes
	add	si,ax		;skip string
	jmp	short opr1	;back for more
opr2:	mov	cx,ax		;copy string length
	test	bl,bl		;first time through?
	jz	opr3
	mov	al,' '		;no, add blank
	stosb
opr3:	rep	movs [di],byte ptr cs:[si] ;copy string
	mov	bl,1		;definitely add a blank next time
	jmp	short opr1
opr4:	jmp	crlf		;CRLF, return
;
oprg1	label	byte		;group 1 operate instructions
	; sequence 1
	db	0200,0200,3,'CLA'
	db	0100,0100,3,'CLL'
	; sequence 2
	db	0040,0040,3,'CMA'
	db	0020,0020,3,'CML'
	; sequence 3
	db	0001,0001,3,'IAC'
	; sequence 4
	db	0016,0010,3,'RAR'
	db	0016,0004,3,'RAL'
	db	0016,0012,3,'RTR'
	db	0016,0006,3,'RTL'
	db	0016,0002,3,'BSW'
	db	0016,0014,7,'RAR RAL' ;(officially illegal)
	db	0016,0016,7,'RTR RTL' ;(officially illegal)
	dw	0		;end of table
;
oprg2	label	byte		;group 2 operate instructions
	; sequence 1
	db	0110,0100,3,'SMA'
	db	0110,0110,3,'SPA'
	db	0050,0040,3,'SZA'
	db	0050,0050,3,'SNA'
	db	0030,0020,3,'SNL'
	db	0030,0030,3,'SZL'
	db	0170,0010,3,'SKP'
	; sequence 2
	db	0200,0200,3,'CLA'
	; sequence 3
	db	0004,0004,3,'OSR'
	db	0002,0002,3,'HLT'
	dw	0		;end of table
;
oprg3	label	byte		;group 3 operate instructions
	; sequence 1
	db	0200,0200,3,'CLA'
	; sequence 2
	db	0100,0100,3,'MQA'
	db	0020,0020,3,'MQL'
;;; don't have the other KE8/E EAE instructions yet
	dw	0		;end of table
;
porg8:	; print PDP-8 style ORG statement
	push	ax		;save
	mov	al,'*'		;asterisk
	stosb
	pop	ax		;restore
	shr	ax,1		;word addr
	jmp	oct4		;print it, return
;
plab8:	; print PDP-8 style label
	shr	ax,1		;word addr
	call	labl8		;print L plus four octal digits
	mov	al,','		;,
	stosb
	ret
;
	subttl	utility routines
;+
;
; Display register in AL.
;
; REG: al<2:0>
; REG1: al<5:3>
;
;-
reg1:	mov	al,dl		;get opcode
	mov	cl,3		;bit count
	shr	al,cl		;right 3
	jmp	short $+4
reg:	 mov	al,dl		;get opcode
	and	ax,7		;isolate
	cmp	al,6		;(hl)?
	je	regm		;skip
	mov	bx,ax		;copy
	mov	al,byte ptr cs:regs[bx] ;look up
	stosb			;save
	mov	al,','		;often needed
	ret
regm:	; (hl)
	mov	ax,"h("		;write it
	stosw
	mov	ax,")l"
	stosw
	mov	al,','		;often needed
	ret
;+
;
; Same as above for Intel mnemonics, reg in DL.
; Same deal, only "(hl)" is shown as "m".
;
;-
ireg:	mov	bl,dl		;copy
ireg2:	and	bx,7		;isolate
	mov	al,byte ptr cs:regs[bx] ;look up
	stosb			;save
	ret
;
ireg1:	; same thing with reg in DL<5:3>
	mov	bl,dl		;copy
	mov	cl,3		;bit count
	shr	bl,cl		;shift into place
	jmp	short ireg2
;+
;
; Display register pair in DL<5:4>.
;
;-
regpr:	mov	bl,dl		;copy opcode
	mov	cl,3		;bit count
	shr	bl,cl		;move
	and	bx,6		;isolate
	mov	ax,word ptr cs:regprs[bx] ;look up
	stosw			;save
	ret
;+
;
; Same as above for Intel mnemonics, opcode in DL.
; Only the first letter of bc, de, or hl is shown (sp is OK though).
;
;-
iregpr:	mov	bl,dl		;copy
	mov	cl,3		;bit count
	shr	bl,cl		;move
	and	bx,6		;isolate
	mov	ax,word ptr cs:regprs[bx] ;look up
	cmp	al,'s'		;sp?
	je	$+4
	 stosb			;no, write only 1st byte
	 ret
	stosw			;sp
	ret
;+
;
; Display a byte in hex with a leading 0 if needed, and trailing h.
;
; Byte comes in AL.
;
;-
hexb:	cmp	al,0A0h		;starts with a letter?
	jb	hexb1		;no
	mov	byte ptr es:[di],'0' ;add leading 0
	inc	di		;+1
hexb1:	call	hex2		;show it
	mov	al,'h'		;h
	stosb
	mov	al,','		;often needed
	ret
;+
;
; Same as above for a word (in AX).
;
;-
hexw:	cmp	ah,0A0h		;starts with a letter?
	jb	hexw1		;no
	mov	byte ptr es:[di],'0' ;add leading 0
	inc	di
hexw1:	call	hex4		;display
	mov	al,'h'		;h
	stosb
	mov	al,','
	ret
;+
;
; Display address in AX as a label if it is within the range of the
; disassembly.
;
;-
labl:	cmp	ax,cs:dstart	;hm?
	jb	hexw
	cmp	ax,cs:dend
	ja	hexw
	mov	byte ptr es:[di],'L' ;prefix an L
	inc	di
	jmp	short hex4	;yep
;+
;
; Print hex # in AL/AX.
;
;-
hex2:	; 2 digits
	mov	bx,2		;length
	jmp	short hex
hex4:	; 4 digits
	mov	bx,4		;length
hex:	; print BX-digit hex #
	push	si		;save
	push	bx
	add	di,bx		;advance ptr
	mov	ch,bl		;copy
	mov	bx,ax		;copy
	mov	cl,4		;bit count
hex1:	mov	si,bx		;copy
	and	si,17		;isolate
	mov	al,cs:hextab[si] ;look up
	dec	di		;back 1
	mov	es:[di],al
	shr	bx,cl		;shift
	dec	ch		;done?
	jnz	hex1		;loop if not
	pop	ax		;restore
	add	di,ax		;advance
	pop	si		;restore
	ret
;+
;
; Display address in AX as an octal PDP-8 label if it is within the range of
; the disassembly.
;
;-
labl8:	mov	cx,ax		;copy
	add	cx,cx		;byte addr
	cmp	cx,cs:dstart	;hm?
	jb	oct4
	cmp	cx,cs:dend
	ja	oct4
	mov	byte ptr es:[di],'L' ;prefix an L
	inc	di
	;jmp	short oct4	;yep
;+
;
; Print 4-digit octal number in AX.
;
;-
oct4:	mov	cx,4		;digit count
octn:	; enter with # digits in CX
	add	di,cx		;skip to end
	push	di		;save
octn1:	mov	bl,al		;copy
	and	bl,7		;isolate
	or	bl,'0'		;convert to digit
	dec	di		;back up
	mov	es:[di],bl	;save digit
	shr	ax,1		;right 3 bits
	shr	ax,1
	shr	ax,1
	loop	octn1		;do all digits
	pop	di		;restore ptr past end
	ret
;+
;
; Skip blanks.
;
; ds:si	pointer
;
; AL returns the char we stopped on, ZF=1 if it's 0.
;
;-
skip:	lodsb			;get a char
	or	al,al		;end?
	jz	skip1
	cmp	al,' '		;blank or ctrl?
	jbe	skip		;loop if so
skip1:	dec	si		;unget
	or	al,al		;set ZF
	ret
;+
;
; Get a hex number.
;
; ds:si	pointer
;
; Returns number in AX, DS:SI stopped at first non-whitespace, non-hex
; character.  CF=1 if there were no good hex characters.
;
;-
gethex:	lodsb			;get a char
	or	al,al		;eol?
	jz	ghex5
	cmp	al,' '		;blank or cc?
	jbe	gethex		;yes, ignore
	dec	si		;unget
	xor	ah,ah		;zap good-digit-seen flag
	xor	bx,bx		;zap #
	mov	cl,4		;bit count
ghex1:	lodsb			;get a char
	cmp	al,dl		;SWITCHAR?
	je	ghex4
	sub	al,'0'		;see if #
	cmp	al,9d		;0-9?
	jbe	ghex3
	sub	al,'A'-'0'	;try for letter
	cmp	al,5		;A-F?
	jbe	ghex2
	sub	al,'a'-'A'	;lower case
	cmp	al,5		;a-f?
	ja	ghex4
ghex2:	add	al,10d		;A-F => 10.-15.
ghex3:	sal	bx,cl		;left 4
	or	bl,al		;OR in new digit
	inc	ah		;set flag
	jmp	short ghex1	;continue
ghex4:	; invalid hex char (including null)
	dec	si		;unget
	sub	ah,1		;set CF if nothing gotten
	mov	ax,bx		;[copy]
	ret
ghex5:	stc			;error return
	ret
;+
;
; Parse a word from the input line.
;
; ds:si	current position
; dl	SWITCHAR (preserved)
;
; On return:
; si	points at posn after last char of word
; bx	points at begn of word if CF=0
; cx	length of word
;
;-
getwd:	mov	bx,si		;save ptr
getwd1:	lodsb			;get a char
	cmp	al,' '		;blank/ctrl (or NUL at end of string)?
	jbe	getwd2		;yes, end
	cmp	al,dl		;SWITCHAR?
	je	getwd2		;yes, end
	cmp	al,'a'		;lower case?
	jb	getwd1		;loop if not
	cmp	al,'z'
	ja	getwd1
	and	byte ptr [si-1],not 40 ;convert to UC if so
	jmp	short getwd1	;and loop
getwd2:	dec	si		;unget char we stopped on
	mov	cx,si		;compute length
	sub	cx,bx		;find length
	cmp	cx,1		;CF=1 if null
	ret
;+
;
; Look up a keyword in a table.
;
; ds:bx	keyword } from GETWD
; cx	length  }
; cs:ax	table
;
; Returns CF=1 if not found, otherwise AX=number from table.
;
; This routine doesn't require that DS=CS, so it may be used to parse
; environment strings.
;
; si,dl	preserved either way (presumably line ptr and SWITCHAR value).
;
;-
tbluk:	push	si		;save
	push	es
	push	ds		;copy DS to ES
	pop	es
	push	cs		;copy CS to DS
	pop	ds
	mov	si,ax		;point at table
tbluk1:	lodsb			;get length of next string
	and	ax,377		;AH=0, see if 0
	jz	tbluk3		;end of table if so
	xchg	ax,cx		;CX=len of this string, AX=len of test string
	cmp	ax,cx		;does length match?
	jne	tbluk2		;no
	mov	di,bx		;point at test string
	repe	cmpsb		;match?
	je	tbluk4		;yes
tbluk2:	add	si,cx		;skip rest of string
	mov	cx,ax		;restore length of test string
	lodsw			;skip value
	jmp	short tbluk1	;keep looking
tbluk3:	push	es		;restore DS
	pop	ds
	pop	es		;and ES/SI
	pop	si
	stc			;failed
	ret
tbluk4:	lodsw			;get value
	push	es		;restore DS
	pop	ds
	pop	es		;and ES/SI
	pop	si
	clc			;happy
	ret
;+
;
; Add an address to symbol table.
;
; ax	address
; bl	bit mask to set (INST or DATA)
; dl	preserved (required by EATRM for EATRMB to detect POP)
;
; Returns NZ if it was already defined.
;
;-
addsym:	mov	cl,al		;copy low bits
	shr	ax,1		;right 2
	shr	ax,1
	and	cl,3		;isolate low 2
	sal	cl,1		;*2
	mov	bh,3		;load 2 1's
	sal	bh,cl		;shift into place
	and	bl,bh		;isolate mask
	xchg	ax,bx		;swap
	and	ah,es:[bx]	;get old bits
	or	es:[bx],al	;set bit
	or	ah,ah		;ZF=0 if it was already defined
	ret
;+
;
; Same thing again but just checks to see if defined.
;
;-
chksym:	mov	cl,al		;copy low bits
	shr	ax,1		;right 2
	shr	ax,1
	and	cl,3		;isolate low 2
	sal	cl,1		;*2
	mov	bh,3		;load 2 1's
	sal	bh,cl		;shift into place
	xchg	ax,bx		;swap
	and	ah,es:[bx]	;get bits
	ret
;+
;
; Get next byte into AL, update BP, preserve DX.
;
; (BP contains the Z-80 PC value throughout this program).
;
;-
getb:	mov	al,ds:[bp]	;fetch byte
	inc	bp		;advance pc
	ret
;+
;
; Get a word.
;
;-
getw:	mov	ax,ds:[bp]	;fetch word
	inc	bp		;advance
	inc	bp
	ret
;
cpus	label	byte		;TBLUK table for CPU types
	db	4,'8051'
	dw	mcs51
	db	4,'8080'
	dw	intel
	db	4,'8086'
	dw	iapx86
	db	3,'Z80'
	dw	z80
	db	4,'PDP8'
	dw	pdp8
	db	0		;end of table
;
mnem	dw	z80		;starting posn in symbol table
;
faddr	dw	100h		;/F:file load addr
dstart	dw	100h		;/S:disassembly starting addr
dend	dw	177777		;/E:disassembly ending addr
xfer	dw	100h		;/T:transfer address
;
xhand	dw	0		;NZ => handle of /T:@file
;
last	dw	177777		;actual last addr of file
;
lukerr	db	33,0,'?Error opening input file',cr,lf
enterr	db	35,0,'?Error creating output file',cr,lf
xfrerr	db	46,0,'?Error opening transfer address file',cr,lf
xrderr	db	46,0,'?Error reading transfer address file',cr,lf
lderrm	db	30,0,'?Input file read error',cr,lf
;
help	dw	lhelp
	db	'DASM V1.2  Tracing disassembler   <wilson@dbit.com>',cr,lf
	db	'Copyright (C) 1994-1999 by John Wilson.'
	db	'  All rights reserved.',cr,lf
	db	'command line: filename[.COM] (out=.ASM, or STDIN/OUT)',cr,lf
	db	'  ~S:hhhh	starting addr for disassembly',cr,lf
	db	'  ~E:hhhh	ending addr for disassembly',cr,lf
	db	'  ~T:hhhh	transfer addr (def=0100), or:',cr,lf
	db	'  ~T:@file	blank- or CRLF-separated list of xfer addrs in'
	db	' file',cr,lf
	db	'  ~F:hhhh	load addr of file (def=0100)',cr,lf
	db	'  ~C:xxxx	give mnemonics for specified CPU',cr,lf
	db	'		~C:Z80	Zilog Z80 (default)',cr,lf
	db	'		~C:8051	Intel 8051/8052/8751',cr,lf
	db	'		~C:8080	Intel 8080 (syn. ~I)',cr,lf
	db	'		~C:8086	Intel 80x86 (syn. ~N)',cr,lf
lhelp=	$-help-2
;
ddcb	db	'0DDh,0CBh,0'		;prefix for pfx'ed invalid opcodes
org1	db	tab,'org',tab		;initial ORG statement
dead	db	tab,'; dead',cr,lf	;comment for dead data
hextab	db	'0123456789ABCDEF'	;hex digits
regs	db	'bcdehlma'		;register names
regprs	db	'bcdehlsp'		;register pair names (2 chars each)
cond	db	'nzz,ncc,popep,m,'	;conditions (2 chars each)
;
; Intel mnemonics for 8080/8085 instruction set.
;
	symtab	intel,porgh,plabh,1	;hex orgs/labels, 1-byte opcodes
	op	'halt',166,377,nostop	;=MOV M,M (must come before MOV)
	; move, load, store
	opt	'mov',100,300,irr
	opt	'mvi',006,307,irn
	opt	'lxi',001,317,irpnn
	opt	'stax',002,357,irpp
	opt	'ldax',012,357,irpp
	opt	'sta',062,377,imm
	opt	'lda',072,377,imm
	opt	'shld',042,377,imm
	opt	'lhld',052,377,imm
	op	'xchg',353,377,no
	; stack ops
	op	'push	psw',365,377,no
	opt	'push',305,317,irpp
	op	'pop	psw',361,377,no
	opt	'pop',301,317,irpp
	op	'xthl',343,377,no
	op	'sphl',371,377,no
	; jumps
	opt	'jmp',303,377,nn
	opt	'jc',332,377,callnn
	opt	'jnc',322,377,callnn
	opt	'jz',312,377,callnn
	opt	'jnz',302,377,callnn
	opt	'jp',362,377,callnn
	opt	'jm',372,377,callnn
	opt	'jpe',352,377,callnn
	opt	'jpo',342,377,callnn
	op	'pchl',351,377,nostop
	; calls
	opt	'call',315,377,callnn
	opt	'cc',334,377,callnn
	opt	'cnc',324,377,callnn
	opt	'cz',314,377,callnn
	opt	'cnz',304,377,callnn
	opt	'cp',364,377,callnn
	opt	'cm',374,377,callnn
	opt	'cpe',354,377,callnn
	opt	'cpo',344,377,callnn
	; returns
	op	'ret',311,377,nostop
	op	'rc',330,377,no
	op	'rnc',320,377,no
	op	'rz',310,377,no
	op	'rnz',300,377,no
	op	'rp',360,377,no
	op	'rm',370,377,no
	op	'rpe',350,377,no
	op	'rpo',340,377,no
	; restart
	opt	'rst',307,307,irst
	; I/O
	opt	'in',333,377,iio	;like iimm but don't give ASCII
	opt	'out',323,377,iio
	; inc and dec
	opt	'inr',004,307,ir1
	opt	'dcr',005,307,ir1
	opt	'inx',003,317,irpp
	opt	'dcx',013,317,irpp
	; add
	opt	'add',200,370,ir
	opt	'adc',210,370,ir
	opt	'adi',306,377,iimm
	opt	'aci',316,377,iimm
	opt	'dad',011,317,irpp
	; subtract
	opt	'sub',220,370,ir
	opt	'sbb',230,370,ir
	opt	'sui',326,377,iimm
	opt	'sbi',336,377,iimm
	; logical
	opt	'ana',240,370,ir
	opt	'xra',250,370,ir
	opt	'ora',260,370,ir
	opt	'cmp',270,370,ir
	opt	'ani',346,377,iimm
	opt	'xri',356,377,iimm
	opt	'ori',366,377,iimm
	opt	'cpi',376,377,iimm
	; rotate
	op	'rlc',007,377,no
	op	'rrc',017,377,no
	op	'ral',027,377,no
	op	'rar',037,377,no
	; specials
	op	'cma',057,377,no
	op	'stc',067,377,no
	op	'cmc',077,377,no
	op	'daa',047,377,no
	; control
	op	'ei',373,377,no
	op	'di',363,377,no
	op	'nop',000,377,no
	; 8085A-specific instructions (opcodes interfere with Z-80)
	op	'rim',040,377,no	;read interrupt mask (opcode=JR NZ,)
	op	'sim',060,377,no	;set interrupt mask (opcode=JR NC,)
	; not valid MCS-80/85 instruction
	opt	'; non-8080/85 instruction',000,000,non85
;
; General Z-80 instructions.
;
	symtab	z80,porgh,plabh,1	;hex orgs/labels, 1-byte opcodes
	op	'',313,377,cbpfx	;CB prefix
	op	'',335,377,ddpfx	;DD prefix (IX replaces HL)
	op	'',355,377,edpfx	;ED prefix
	op	'',375,377,fdpfx	;FD prefix (IY replaces HL)
	; (from G.P. arith/CPU control group)
	op	'halt',166,377,nostop	;=LD (HL),(HL) (must come before LD)
	; 8-bit load group
	opt	'ld',100,300,rr
	opt	'ld',006,307,rn
	op	'ld	a,(bc)',012,377,no
	op	'ld	a,(de)',032,377,no
	opt	'ld	a,',072,377,amm
	op	'ld	(bc),a',002,377,no
	op	'ld	(de),a',022,377,no
	opt	'ld',062,377,mma
	; 16-bit load group
	opt	'ld',001,317,ddnn
	opt	'ld',052,377,ddmm	;dd bits happen to show HL
	opt	'ld',042,377,mmdd	;dd bits happen to show HL
	op	'ld	sp,hl',371,377,no
	op	'push	af',365,377,no	;special case of dd
	opt	'push',305,317,rp
	op	'pop	af',361,377,no	;special case of dd
	opt	'pop',301,317,rp
	; exch, blt, blk srch group
	op	'ex	de,hl',353,377,no
	op	"ex	af,af'",010,377,no
	op	'exx',331,377,no
	op	'ex	(sp),hl',343,377,no
	; 8-bit arith/logical group
	op	'add	a,',200,370,r
	op	'add	a,',306,377,imm8
	op	'adc	a,',210,370,r
	op	'adc	a,',316,377,imm8
	opt	'sub',220,370,r
	opt	'sub',326,377,imm8
	opt	'sbc',230,370,r
	opt	'sbc',336,377,imm8
	opt	'and',240,370,r
	opt	'and',346,377,imm8
	opt	'xor',250,370,r
	opt	'xor',356,377,imm8
	opt	'or',260,370,r
	opt	'or',366,377,imm8
	opt	'cp',270,370,r
	opt	'cp',376,377,imm8
	opt	'inc',004,307,r1
	opt	'dec',005,307,r1
	; G.P. arith/CPU control group
	op	'daa',047,377,no
	op	'cpl',057,377,no
	op	'ccf',077,377,no
	op	'scf',067,377,no
	op	'nop',000,377,no
	op	'di',363,377,no
	op	'ei',373,377,no
	; 16-bit arithmetic group
	op	'add	hl,',011,317,rp
	opt	'inc',003,317,rp
	opt	'dec',013,317,rp
	; rotate/shift group
	op	'rlca',007,377,no
	op	'rla',027,377,no
	op	'rrca',017,377,no
	op	'rra',037,377,no
	; jump group
	opt	'jp',303,377,nn
	opt	'jp',302,307,ccnn
	opt	'jr',030,377,ujr
	op	'jr	c,',070,377,cjr
	op	'jr	nc,',060,377,cjr
	op	'jr	z,',050,377,cjr
	op	'jr	nz,',040,377,cjr
	op	'jp	(hl)',351,377,nostop
	opt	'djnz',020,377,cjr
	; call/return group
	opt	'call',315,377,callnn
	opt	'call',304,307,ccnn
	op	'ret',311,377,nostop
	opt	'ret',300,307,cc
	opt	'rst',307,307,rst
	; I/O group
	opt	'in	a,',333,377,upn
	opt	'out',323,377,pna
	; anything else is undefined
	opt	'db',000,000,bite	;matches everything else
;
; Instructions prefixed by DD or FD, in which accesses to HL (or (HL)) are
; replaced by IX (or (IX+d)) or IY (or (IY+d)), respectively.
ixtab	label	byte
	op	'',313,377,cbix		;xD CB prefix (argh!)
	; 8-bit load group
	opt	'ld',106,307,rixm	;not 105 -- Z-80 Prod. Spec. is WRONG
	opt	'ld',105,307,rixl	;undoc'ed (0DDh; LD r,L)
	opt	'ld',104,307,rixh	;undoc'ed (0DDh; LD r,H)
	opt	'ld',160,370,ixmr
	opt	'ld',150,370,ixlr	;same deal (0DDh; LD L,r)
	opt	'ld',140,370,ixhr
	opt	'ld',066,377,ixmn
	; 16-bit load group
	opt	'ld',041,377,ixnn
	opt	'ld',052,377,ixmm
	opt	'ld',042,377,mmix
	opt	'ld	sp,',371,377,uixx
	opt	'push',345,377,ixx
	opt	'pop',341,377,ixx
	; exch, blt, blk srch group
	opt	'ex	(sp),',343,377,uixx
	; 8-bit arith/logical group
	opt	'add	a,',206,377,uixm
	opt	'add	a,',205,377,uixl ;undoc'ed
	opt	'add	a,',204,377,uixh ;undoc'ed
	opt	'adc	a,',216,377,uixm
	opt	'adc	a,',215,377,uixl ;undoc'ed
	opt	'adc	a,',214,377,uixh ;undoc'ed
	opt	'sub',226,377,ixm
	opt	'sub',225,377,ixl	;undoc'ed
	opt	'sub',224,377,ixh	;undoc'ed
	opt	'sbc',236,377,ixm
	opt	'sbc',235,377,ixl	;undoc'ed
	opt	'sbc',234,377,ixh	;undoc'ed
	opt	'and',246,377,ixm
	opt	'and',245,377,ixl	;undoc'ed
	opt	'and',244,377,ixh	;undoc'ed
	opt	'xor',256,377,ixm
	opt	'xor',255,377,ixl	;undoc'ed
	opt	'xor',254,377,ixh	;undoc'ed
	opt	'or',266,377,ixm
	opt	'or',265,377,ixl	;undoc'ed
	opt	'or',264,377,ixh	;undoc'ed
	opt	'cp',276,377,ixm
	opt	'cp',275,377,ixl	;undoc'ed
	opt	'cp',274,377,ixh	;undoc'ed
	opt	'inc',064,377,ixm
	opt	'inc',054,377,ixl	;undoc'ed
	opt	'inc',044,377,ixh	;undoc'ed
	opt	'dec',065,377,ixm
	opt	'dec',055,377,ixl	;undoc'ed
	opt	'dec',045,377,ixh	;undoc'ed
	; 16-bit arithmetic group
	opt	'add',011,317,ixpp	;not 111 (Z-80 prod. spec. is wrong)
	opt	'inc',043,377,ixx
	opt	'dec',053,377,ixx
	; jump group
	opt	'jp',351,377,ixxp	;just (ix) or (iy)
	; anything else is undefined
	opt	'db',000,000,ixbite
;
; Instructions prefixed by CB.
cbtab	label	byte
	; rotate/shift group
	opt	'rlc',000,370,r
	opt	'rrc',010,370,r
	opt	'rl',020,370,r
	opt	'rr',030,370,r
	opt	'sla',040,370,r
	opt	'sra',050,370,r
	opt	'?sll?',060,370,r	;would be consistent (works? = SLA?)
	opt	'srl',070,370,r
	; bit set/reset/test group
	opt	'bit',100,300,br
	opt	'set',300,300,br
	opt	'res',200,300,br
	; anything else is undefined
	opt	'db	0CBh,',000,000,ubite
;
; Instructions prefixed by DD CB or FC CB (CB w/HL replaced by IX/IY).
; The really gross thing is that the displacement comes before the final
; opcode byte.  Yech.
cbixtb	label	byte
	; rotate/shift group
	opt	'rlc',006,377,cbixm
	opt	'rrc',016,377,cbixm
	opt	'rl',026,377,cbixm
	opt	'rr',036,377,cbixm
	opt	'sla',046,377,cbixm
	opt	'sra',056,377,cbixm
	opt	'?sll?',066,377,cbixm	;would make sense, but no
	opt	'srl',076,377,cbixm
	; bit set/reset/test group
	opt	'bit',106,307,bixm
	opt	'set',306,307,bixm
	opt	'res',206,307,bixm
	; anything else is undefined
	op	'db',000,000,cbixbt
;
; Instructions prefixed by ED.
edtab	label	byte
	; 8-bit load group
	op	'ld	a,i',127,377,no
	op	'ld	a,r',137,377,no
	op	'ld	i,a',107,377,no
	op	'ld	r,a',117,377,no
	; 16-bit load group
	opt	'ld',113,317,ddmm
	opt	'ld',103,317,mmdd
	; exch, blt, blk srch group
	op	'ldi',240,377,no
	op	'ldir',260,377,no
	op	'ldd',250,377,no
	op	'lddr',270,377,no
	op	'cpi',241,377,no
	op	'cpir',261,377,no
	op	'cpd',251,377,no
	op	'cpdr',271,377,no
	; G.P. arith/CPU control group
	op	'neg',104,377,no
	op	'im	0',106,377,no
	op	'im	1',126,377,no
	op	'im	2',136,377,no
	; 16-bit arithmetic group
	op	'adc	hl,',112,317,rp
	op	'sbc	hl,',102,317,rp
	; rotate/shift group
	op	'rld',157,377,no
	op	'rrd',147,377,no
	; call/return group
	op	'reti',115,377,nostop
	op	'retn',105,377,nostop
	; I/O group
	opt	'in',100,307,rpc
	op	'ini',242,377,no
	op	'inir',262,377,no
	op	'ind',252,377,no
	op	'indr',272,377,no
	op	'out	(c),',101,307,r1
	op	'outi',243,377,no
	op	'otir',263,377,no
	op	'outd',253,377,no
	op	'otdr',273,377,no
	; anything else is undefined
	opt	'db	0EDh,',000,000,ubite
;
; Intel 8051/8052/8751 instructions.
;
	symtab	mcs51,porgh,plabh,1	;hex orgs/labels, 1-byte opcodes
	opt	'acall',021,037,acall
	opt	'add',044,374,asrc51
	op	"",050,370,asrc51
	opt	'addc',064,374,asrc51
	op	"",070,370,asrc51
	opt	'ajmp',001,037,ajmp
	opt	'anl',124,374,asrc51
	op	"",130,370,asrc51
	op	"",122,377,dira51
	op	"",123,377,diri51
	op	'anl	c,',202,377,bit51
	op	'anl	c,/',260,377,bit51
	op	'cjne	a,',264,376,cjnea
	opt	'cjne',266,376,cjnei
	op	"",270,370,cjnei
	op	'clr	a',344,377,no
	op	'clr	c',303,377,no
	opt	'clr',302,377,bit51
	op	'cpl	a',364,377,no
	op	'cpl	c',263,377,no
	opt	'cpl',262,377,bit51
	op	'da	a',324,377,no
	op	'dec	a',024,377,no
	opt	'dec',024,374,sop51
	op	"",030,370,sop51
	op	'div	ab',204,377,no
	opt	'djnz',325,377,djnzd
	op	"",330,370,djnz51
	op	'inc	a',010,377,no
	opt	'inc',004,374,sop51
	op	"",010,370,sop51
	op	'inc	dptr',243,377,no
	opt	'jb',040,377,jb51
	opt	'jbc',020,377,jb51
	opt	'jc',100,377,cjr
	op	'jmp	@a+dptr',163,377,no
	opt	'jnb',060,377,jb51
	opt	'jnc',120,377,cjr
	opt	'jnz',160,377,cjr
	opt	'jz',140,377,cjr
	opt	'lcall',022,377,lcall
	opt	'ljmp',002,377,ljmp
	opt	'mov',350,370,asrc51
	op	"",345,377,asrc51
	op	"",346,376,asrc51
	op	"",164,377,asrc51
	op	"",370,370,movrna
	op	"",250,370,movrnd
	op	"",170,370,movrni
	op	"",365,377,movda
	op	"",210,370,movdx
	op	"",205,377,movdd
	op	"",206,376,movdx
	op	"",165,377,movdi
	op	"",366,376,movira
	op	"",246,376,movird
	op	"",166,376,moviri
	op	'mov	c,',242,377,bit51
	opt	'mov',222,377,movbc
	op	'mov	dptr,#',220,377,mdptr
	op	'movc	a,@a+dptr',223,377,no
	op	'movc	a,@a+pc',203,377,no
	op	'movx	a,',342,376,movxar
	op	'movx	a,@dptr',340,377,no
	opt	'movx',362,376,movxra
	op	'movx	@dptr,a',360,376,no
	op	'mul	ab',244,376,no
	op	'nop',000,377,no
	opt	'orl',104,374,asrc51
	op	"",110,370,asrc51
	op	"",102,377,dira51
	op	"",103,377,diri51
	op	'orl	c,',162,377,bit51
	op	'orl	c,/',240,377,bit51
	opt	'pop',320,377,stk51
	opt	'push',300,377,stk51
	op	'ret',042,377,nostop
	op	'reti',062,377,nostop
	op	'rl	a',043,377,no
	op	'rlc	a',063,377,no
	op	'rr	a',003,377,no
	op	'rrc	a',023,377,no
	op	'setb	c',323,377,no
	opt	'setb',322,377,bit51
	opt	'sjmp',200,377,ujr
	opt	'subb',224,374,asrc51
	op	"",230,370,asrc51
	op	'swap	a',304,377,no
	opt	'xch',305,377,asrc51
	op	"",306,376,asrc51
	op	"",310,370,asrc51
	opt	'xchd',326,376,asrc51
	opt	'xrl',144,374,asrc51
	op	"",150,370,asrc51
	op	"",142,377,dira51
	op	"",143,377,diri51
	opt	'db',000,000,bite
;
; This is definitely way out of hand.
;
	symtab	iapx86,porgh,plabh,1	;hex orgs/labels, 1-byte opcodes
	op	'aaa',067,377,no
;	op	'',0D5h,377,i86pD5	;AAD=0A
;	op	'',0D4h,377,i86pD4	;AAM=0A
	op	'',017,377,pfx0F	;80386-specific prefix
	op	'',046,377,es86		;ES:
	op	'',056,377,cs86		;CS:
	op	'',066,377,ss86		;SS:
	op	'',076,377,ds86		;DS:
	op	'',144,377,fs86		;FS:
	op	'',145,377,gs86		;GS:
	op	'',146,377,opsz86	;complement default operand size
	op	'',147,377,adsz86	;complement default address size
	op	'',200,376,rmi86	;mod-r/m,imm
;;; are next 2 two separate cases???  (haven't looked 'em up)
	op	'',203,377,rmi86	;mod-r/m,+imm8
;;;;	op	'',202,376,rmi86	;mod-r/m,+imm8	;;;; right?
	op	'',217,377,rm86		;POP mod-r/m
	op	'',300,376,rot86
	op	'',320,374,rot86
	op	'',366,376,rmi86	;mod-r/m,imm
	op	'',376,376,rm86		;mod-r/m
	op	'aas',077,377,no
	opt	'adc',020,376,rmr86
	op	"",022,376,rrm86
	op	"",024,376,ai86
	opt	'add',000,376,rmr86
	op	"",002,376,rrm86
	op	"",004,376,ai86
	opt	'and',040,376,rmr86
	op	"",042,376,rrm86
	op	"",044,376,ai86
	opt	'arpl',143,377,rmr86
	opt	'bound',142,377,rmr86
	opt	'call',232,377,calf86
	op	"",350,377,caln86
	op	'cbw',230,377,no	;CWDE with size pfx
	op	'clc',370,377,no
	op	'cld',374,377,no
	op	'cli',372,377,no
	op	'cmc',365,377,no
	opt	'cmp',070,376,rmr86
	op	"",072,376,rrm86
	op	"",074,376,ai86
	op	'',246,376,cmps86	;CMPS
	op	'cwd',231,377,no	;CDQ with size prefix
	op	'daa',047,377,no
	op	'das',057,377,no
	opt	'dec',110,370,r86
	opt	'enter',310,377,ent86
	op	'hlt',364,377,no
	opt	'inc',100,370,r86
	opt	'in',344,376,in86
	op	"",354,376,indx86
	op	'',154,376,ins86	;INS
	op	'int	3',314,377,no
	opt	'int',315,377,int86
	op	'into',316,377,no
	op	'iret',317,377,nostop	;;;IRETD with prefix
	opt	'ja',167,377,cjr
	opt	'jb',162,377,cjr
	opt	'jbe',166,377,cjr
	opt	'jcxz',343,377,jcxz1	;special case (67h pfx => JECXZ)
	opt	'je',164,377,cjr
	opt	'jg',177,377,cjr
	opt	'jge',175,377,cjr
	opt	'jl',174,377,cjr
	opt	'jle',176,377,cjr
	opt	'jmp',351,377,jmpn86
	op	"",352,377,jmpf86
	op	"",353,377,jsh86
	opt	'jnb',163,377,cjr
	opt	'jne',165,377,cjr
	opt	'jno',161,377,cjr
	opt	'jns',171,377,cjr
	opt	'jo',160,377,cjr
	opt	'jpe',172,377,cjr
	opt	'jpo',173,377,cjr
	opt	'js',170,377,cjr
	op	'lahf',237,377,no
	opt	'lds',305,377,rrm86
	opt	'lea',215,377,rrm86
	op	'leave',311,377,no
	opt	'les',304,377,les86
	op	'lock',360,377,no
	op	'',254,376,lods86
	opt	'loop',342,377,cjr
	opt	'loope',341,377,cjr
	opt	'loopne',340,377,cjr
	opt	'mov',210,376,rmr86
	op	"",212,376,rrm86
	op	"",214,377,rmsg86
	op	"",216,377,sgrm86
	op	"",240,376,amof86
	op	"",242,376,mofa86
	op	"",260,370,bi86
	op	"",270,370,wi86
	op	"",306,376,mrmi86
	op	'',244,376,movs86	;MOVS
	op	'nop',220,377,no
	opt	'or',010,376,rmr86
	op	"",012,376,rrm86
	op	"",014,376,ai86
	opt	'out',346,376,out86
	op	'out	dx,',356,376,oudx86
	op	'',156,376,outs86	;OUTS
	opt	'pop',130,370,r86
	op	"",007,347,pseg86
	op	'popa',141,377,no	;;;POPAD w/pfx
	op	'popf',235,377,no	;;;POPFD w/pfx
	opt	'push',120,370,r86
	op	"",006,347,pseg86
	op	"",150,377,pimm86	;PUSH imm16/imm32
	op	"",152,377,pimm8	;PUSH +imm8 (16/32-bit looks same!)
	op	'pusha',140,377,no	;;;PUSHAD w/pfx
	op	'pushf',234,377,no	;;;PUSHFD w/pfx
	op	'',144,377,repp86	;REPNC - NEC V-series only
	op	'',145,377,repp86	;REPC - V-series
	op	'',362,377,repp86	;REPNE
	op	'',363,377,repp86	;REPE
	opt	'ret',302,377,ret86
	op	'ret',303,377,nostop
	opt	'retf',312,377,ret86
	op	'retf',313,377,nostop
	op	'sahf',236,377,no
	opt	'sbb',030,376,rmr86
	op	"",032,376,rrm86
	op	"",034,376,ai86
	op	'',256,376,scas86	;SCAS
	op	'stc',371,377,no
	op	'std',375,377,no
	op	'sti',373,377,no
	op	'',252,376,stos86	;STOS
	opt	'sub',050,376,rmr86
	op	"",052,376,rrm86
	op	"",054,376,ai86
	opt	'test',250,376,ai86
	op	"",204,376,rmr86
	op	'wait',233,377,no
	opt	'xchg',206,376,rmr86
	op	"",220,370,xchg86
	op	'xlat',327,377,xlat86
	opt	'xor',060,376,rmr86
	op	"",062,376,rrm86
	op	"",064,376,ai86
	opt	'db',000,000,bite
;
; Instructions prefixed by 0F.
tab0F	label	byte
	op	'clts',006,377,no
	op	'cpuid',242,377,no
;;	opt	'imul',257,377,imulaf
	opt	'ja',207,377,cnjmp
	opt	'jae',203,377,cnjmp
	opt	'jb',202,377,cnjmp
	opt	'jbe',206,377,cnjmp
	;opt	'jc',202,377,cnjmp
	opt	'je',204,377,cnjmp
	;opt	'jz',204,377,cnjmp
	opt	'jg',217,377,cnjmp
	opt	'jge',215,377,cnjmp
	opt	'jl',214,377,cnjmp
	opt	'jle',216,377,cnjmp
	;opt	'jna',206,377,cnjmp
	;opt	'jnae',202,377,cnjmp
	opt	'jnb',203,377,cnjmp
	;opt	'jnbe',207,377,cnjmp
	;opt	'jnc',203,377,cnjmp
	opt	'jne',205,377,cnjmp
	;opt	'jng',216,377,cnjmp
	;opt	'jnge',214,377,cnjmp
	;opt	'jnl',215,377,cnjmp
	;opt	'jnle',217,377,cnjmp
	opt	'jno',201,377,cnjmp
	;opt	'jnp',213,377,cnjmp
	opt	'jns',211,377,cnjmp
	;opt	'jnz',205,377,cnjmp
	opt	'jo',200,377,cnjmp
	;opt	'jp',212,377,cnjmp
	opt	'jpe',212,377,cnjmp
	opt	'jpo',213,377,cnjmp
	opt	'js',210,377,cnjmp
	;opt	'jz',204,377,cnjmp
	opt	'movsx',276,377,zx86
	op	"",277,377,zx86d
	opt	'movzx',266,377,zx86
	op	"",267,377,zx86d
	op	'pop	fs',241,377,no
	op	'pop	gs',251,377,no
	op	'push	fs',240,377,no
	op	'push	gs',250,377,no
	; anything else is undefined
	opt	'db	0Fh,',000,000,ubite
;
; PDP-8 instructions.
;
	symtab	pdp8,porg8,plab8,2	;octal orgs/labels, 2-byte opcodes
	opw	'AND',0000,7000,mri
	opw	'TAD',1000,7000,mri
	opw	'ISZ',2000,7000,isz
	opw	'DCA',3000,7000,mri
	opw	'JMS',4000,7000,jms8
	opw	'JMP',5000,7000,jmp8
	opw	'IOT',6000,7000,iot
	; special operate instruction opcodes
	opw	'NOP',7000,7777,no	;no op
	opw	'NOP',7400,7776,no	;(group 2/3 equiv)
	opw	'STA',7240,7777,no	;set AC to all ones (CLA CMA)
	opw	'CIA',7041,7777,no	;complement/increment AC (CMA IAC)
	opw	'LSR',7604,7777,no	;load switch register (CLA OSR)
	opw	'CAM',7621,7777,no	;clear AC and MQ (CLA MQL)
	opw	'SWP',7521,7777,no	;swap AC and MQ (MQA MQL)
	; generic operate instructions (these will pick up all the rest)
	opw	'',7000,7400,opra	;group 1 operate instructions
	opw	'',7400,7401,oprb	;group 2 operate instructions
	opw	'',7401,7401,oprc	;group 3 operate instructions
;
immop	dw	1 dup(?)		;8-bit immediate operand if >=0
;
fname	db	80h dup(?)		;filename buffer
;
code	ends
;
ibuf	segment
	db	177777 dup(?)	;(filled out to 200000 by PARA alignment)
ibuf	ends
;
obuf	segment
	db	177000 dup(?)
obuf	ends
;
ifdef ??version
	nowarn	res		;goddamn TASM!
endif
stack	segment	stack 'stack'	;goddamn TASM needs class
	dw	400h dup(?)
stack	ends
;
	end	start
