;   DEBUG.ASM Masm/JWasm assembler source for a clone of DEBUG.COM
;           Version 1.23, 04/15/2011.

;   To assemble, use:
;       jwasm -D?PM=0 -bin -Fo debug.com debug.asm
;
;   To create DEBUGX, the DPMI aware version of debug, use:
;       jwasm -D?PM=1 -bin -Fo debugx.com debug.asm

; ============================================================================
;
; Copyright (c) 1995-2003  Paul Vojta
;
; Permission is hereby granted, free of charge, to any person obtaining a copy
; of this software and associated documentation files (the "Software"), to
; deal in the Software without restriction, including without limitation the
; rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
; sell copies of the Software, and to permit persons to whom the Software is
; furnished to do so, subject to the following conditions:
;
; The above copyright notice and this permission notice shall be included in
; all copies or substantial portions of the Software.
;
; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
; PAUL VOJTA BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
; IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
; CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
;
; ============================================================================
;
; Japheth: all extensions made by me are Public Domain. This does not
;          affect other copyrights.
;          This file is now best viewed with TAB size 4!
; ============================================================================
;   Revision history:
;       0.95e [11 January 2003]  Fixed a bug in the assember.
;       0.95f [10 September 2003]  Converted to NASM; fixed some syntax
;       incompatibilities.
;       0.98 [27 October 2003]  Added EMS commands and copyright conditions.
;
;       The changes which were done by my, japheth, are described in
;       HISTORY.TXT.
;
;   To do:
;       - support loading *.HEX files
;       - make C work with 32bit offsets in protected-mode
;       - add MMX instructions for A and U
;       - allow to modify floating point registers
;       - better syntax checks for A (so i.e. "mov ax,al" is rejected)

	option casemap:none
	option proc:private
;	option noljmp	;enable to see the short jump extensions

BS         equ 8
TAB        equ 9
LF         equ 10
CR         equ 13
TOLOWER    equ 20h
TOUPPER    equ 0dfh
TOUPPER_W  equ 0dfdfh
MNEMONOFS  equ 28	;offset in output line where mnemonics start (disassember)

VDD        equ 0
NOEXTENDER equ 0
DBGNAME    equ "DEBUG"
DBGNAME2   equ "Debug"
MMXSUPP    equ 0	;support MMX specific commands
DXSUPP     equ 0

DOSNAME    equ <"DOS">
EMSCMD     equ 0	;support Xx commands
USESDA     equ 0	;use SDA to get/set PSP in real-mode
STACKSIZ   equ 200h	;debug's stack size

LINE_IN_LEN equ 257	;length of line_in (including header stuff)

;--- PSP offsets

ALASAP	equ 02h	;Address of Last segment allocated to program
TPIV	equ 0ah	;Terminate Program Interrupt Vector (int 22h)
CCIV	equ 0eh	;Control C Interrupt Vector (int 23h)
CEIV	equ 12h	;Critical Error Interrupt Vector (int 24h)
PARENT	equ 16h	;segment of parent PSP
SPSAV	equ 2eh	;Save the stack pointer here
DTA		equ 80h	;Program arguments; also used to store file name (N cmd)

;--- mne macro, used for the assembler mnemonics table

mne macro val2:REQ, dbytes:VARARG
ASMDATA segment
CURROFS = $
	ifnb <dbytes>
	 db dbytes
	endif
ASMDATA ends
	dw CURROFS - asmtab
MN_&val2 equ $ - mnlist
tmpstr catstr <!">,@SubStr(val2,1,@SizeStr(val2)-1),<!",!'>,@SubStr(val2,@SizeStr(val2)),<!'>,<+80h>
	db tmpstr
	endm

AGRP macro num,rfld
	exitm <240h + num*8 + rfld>
	endm

variant macro opcode:req, key:req, lockb, machine
ASMDATA segment
	ifnb <lockb>
	 db lockb
	endif
	ifnb <machine>
	 db machine
	endif
ainfo = (opcode) * ASMMOD + key
	db HIGH ainfo, LOW ainfo
ASMDATA ends
	endm

fpvariant macro opcode, key, addb, lockb, machine
	variant opcode, key, lockb, machine
ASMDATA segment
	db addb
ASMDATA ends
	endm

endvariant macro
ASMDATA segment
	db -1
ASMDATA ends
	endm

;--- opl macro, used to define operand types

opidx = 0
opl macro value:VARARG
	.radix 16t
if opidx lt 10h
_line textequ <OPLIST_0>,%opidx,< equ $ - oplists>
else
_line textequ <OPLIST_>,%opidx,< equ $ - oplists>
endif
_line
	ifnb <value>
	  db value
	endif
	db 0
	opidx = opidx + 1
	.radix 10t
	endm

OT macro num
	exitm <OPLIST_&num+OPTYPES_BASE>
	endm

;--- sizeprf is to make DEBUG's support for 32bit code as small as possible.
;--- for this to achieve a patch table is created in _IDATA which is filled
;--- by memory offsets where prefix bytes 66h or 67h are found.

sizeprf macro
curreip = $
_IDATA segment
	dw curreip
_IDATA ends
	db 66h
	endm

	.8086

_TEXT segment dword public 'CODE'
	org 100h
start:
	jmp initcode
_TEXT ends

CONST segment readonly word public 'DATA'
CONST ends

ASMDATA segment word public 'DATA'
asmtab label byte
ASMDATA ends

_DATA segment dword public 'DATA'
_DATA ends

_ITEXT segment word public 'I_CODE'
_ITEXT ends

_IDATA segment word public 'I_DATA'
patches label word
_IDATA ends

DGROUP group _TEXT, CONST, ASMDATA, _DATA, _ITEXT, _IDATA

CONST segment

;--- cmds b,j,k,v,y and z don't exist yet

cmdlist	dw aa,cmd_error
	dw ff,hh
	dw cmd_error,cmd_error
	dw qq
	dw uu,cmd_error
	dw cmd_error
ENDCMD	equ <'x'>

CONST ends

_DATA segment

top_sp	dw 0		;debugger's SP top ( also end of debug's MCB )
errret	dw 0		;return here if error
run_sp	dw 0		;debugger's SP when run() is executed
spadjust dw 40h 	;adjust sp by this amount for save

psp22	dw 0,0		;original terminate address in debugger's PSP
parent	dw 0		;original parent PSP in debugger's PSP (must be next)
pInDOS	dd 0		;far16 address of InDOS flag (real mode)
hakstat	db 0		;whether we have hacked the vectors or not
machine	db 0		;cpu (0=8086,1,2,3=80386,...)
bInDbg	db 1		;1=debugger is running
mach_87	db 0		;coprocessor (0=8087,1,2,3=80387,...)
notatty	db LF		;if standard input is from a file
stdoutf	db 0		;flags stdout device
switchar db 0		;switch character
swch1	db ' '		;switch character if it's a slash
promptlen dw 0		;length of prompt
bufnext	dw line_in+2	;address of next available character
bufend	dw line_in+2	;address + 1 of last valid character

a_addr	dw 0,0,0	;address for next A command
d_addr	dw 0,0,0	;address for last D command; must follow a_addr
u_addr	dw 0,0,0	;address for last U command; must follow d_addr

eqladdr	dw 0,0,0	;optional '=' argument in G, P and T command
;run_cs	dw 0		;save original CS when running in G
run_int	dw 0		;interrupt type that stopped the running
lastcmd	dw dmycmd
eqflag	db 0		;flag indicating presence of '=' argument
bInit	db 0		;0=ensure a valid opcode is at debuggee's CS:IP
fileext	db 0		;file extension (0 if no file name)

EXT_OTHER	equ 1
EXT_COM		equ 2
EXT_EXE		equ 4
EXT_HEX		equ 8

;--- usepacket:
;--- 0: packet is not used (int 25h/26h, cx!=FFFF)
;--- 1: packet is used (int 25h/26h, cx==FFFF)
;--- 2: packet is used (int 21h, ax=7305h, cx==FFFF)

usepacket db 0

PACKET struc
secno	dd ?	;sector number
numsecs	dw ?	;number of sectors to read
dstofs	dw ?	;ofs transfer address
dstseg	dw ?	;seg transfer address
PACKET ends

packet PACKET <0,0,0,0>

intsave label dword
		dd 0	;saved vector i00
		dd 0	;saved vector i22
;--- Parameter block for exec call.


REGS struct
rDI		dw ?,?	;+00 edi
rSI		dw ?,?	;+04 esi
rBP		dw ?,?	;+08 ebp
		dw ?,?	;+12 reserved
rBX		dw ?,?	;+16 ebx
rDX		dw ?,?	;+20 edx
rCX		dw ?,?	;+24 ecx
rAX		dw ?,?	;+28 eax

rDS		dw ?	;+32 ds
rES		dw ?	;+34 es
rSS		dw ?	;+36 ss
rCS		dw ?	;+38 cs
rFS		dw ?	;+40 fs
rGS		dw ?	;+42 gs

rSP		dw ?,?	;+44 esp
rIP		dw ?,?	;+48 eip
rFL		dw ?,?	;+52 eflags
REGS ends

;--- Register save area.

	align 4		;--- must be DWORD aligned!

regs REGS <>

_DATA ends

CONST segment

;--- table of interrupt initialization

INTITEM struct
	db ?
	dw ?
INTITEM ends

inttab label INTITEM
NUMINTS = ( $ - inttab ) / sizeof INTITEM
NUMINTSX = NUMINTS

;--- register names for 'r'. One item is 2 bytes.
;--- regofs must follow regnames and order of items must match
;--- those in regnames.

regnames db 'AX','BX','CX','DX',
		'SP','BP','SI','DI','IP','FL',
		'DS','ES','SS','CS','FS','GS'
NUMREGNAMES equ ($ - regnames) / 2
regofs	dw regs.rAX, regs.rBX, regs.rCX, regs.rDX, 
		regs.rSP, regs.rBP, regs.rSI, regs.rDI, regs.rIP, regs.rFL,
		regs.rDS, regs.rES, regs.rSS, regs.rCS, regs.rFS, regs.rGS

;--- arrays flgbits, flgnams and flgnons must be consecutive
flgbits dw 800h,400h,200h,80h,40h,10h,4,1
flgnams db 'NV','UP','DI','PL','NZ','NA','PO','NC'
flgnons db 'OV','DN','EI','NG','ZR','AC','PE','CY'

;--- Instruction set information needed for the 'p' command.
;--- arrays ppbytes and ppinfo must be consecutive!

ppbytes	db 66h,67h,26h,2eh,36h,3eh,64h,65h,0f2h,0f3h	;prefixes
		db 0ach,0adh,0aah,0abh,0a4h,0a5h	;lods,stos,movs
		db 0a6h,0a7h,0aeh,0afh		;cmps,scas
		db 6ch,6dh,6eh,6fh		;ins,outs
		db 0cch,0cdh			;int instructions
		db 0e0h,0e1h,0e2h		;loop instructions
		db 0e8h				;call rel16/32
		db 09ah				;call far seg16:16/32
;		(This last one is done explicitly by the code.)
;		db 0ffh				;ff/2 or ff/3:  indirect call

;   Info for the above, respectively.
;   80h = prefix;
;   81h = address size prefix.
;   82h = operand size prefix;
;   If the high bit is not set, the next highest bit (40h) indicates that
;   the instruction size depends on whether there is an address size prefix,
;   and the remaining bits tell the number of additional bytes in the
;   instruction.

PP_ADRSIZ	equ 01h
PP_OPSIZ	equ 02h
PP_PREFIX	equ 80h
PP_VARSIZ	equ 40h

ppinfo	db 82h,81h,80h,80h,80h,80h,80h,80h,80h,80h	;prefixes
	db 0,0,0,0,0,0									;string instr
	db 0,0,0,0										;string instr
	db 0,0,0,0										;string instr
	db 0,1											;INT instr
	db 1,1,1										;LOOPx instr
	db 42h											;near CALL instr
	db 44h											;far CALL instr

PPLEN	equ $ - ppinfo

;--- Strings.

prompt1	db '-'		;main prompt
prompt2	db ':'		;prompt for register value

helpmsg db DOSNAME, ' ', DBGNAME2, ' v1.23 help screen',CR,LF
	db 'assemble',9,	'A [address]',CR,LF
	db 'compare',9,9,	'C range address',CR,LF
	db 'dump',9,9,		'D [range]',CR,LF
	db 'fill',9,9,		'F range list',CR,LF
	db 'hex add/sub',9,'H value1 value2',CR,LF
	db 'move',9,9,		'M range address',CR,LF
	db '80x86 mode',9,	'M [x] (x=0..6)',CR,LF
	db 'quit',9,9,		'Q',CR,LF
	db 'register',9,	'R [register [value]]',CR,LF
	db 'search',9,9,	'S range list',CR,LF
	db 'unassemble',9,	'U [range]',CR,LF
helpmsg2 label byte
crlf db CR,LF
;size_helpmsg2 equ $ - helpmsg2
	db '$'

presskey db '[more]'

errcarat db '^ Error'

unused	db ' (unused)',0

needsmsg db '[needs x86]'		;<--- modified (7 and 9)
needsmath db '[needs math coprocessor]'
obsolete db '[obsolete]'

int0msg	db 'Divide error',CR,LF,'$'
int1msg	db 'Unexpected single-step interrupt',CR,LF,'$'
int3msg	db 'Unexpected breakpoint interrupt',CR,LF,'$'

cantwritebp db "Can't write breakpoint",0

progtrm	db CR,LF,'Program terminated normally ('
progexit db '____)',CR,LF,'$'
nowhexe	db 'EXE and HEX files cannot be written',CR,LF,'$'
nownull	db 'Cannot write: no file name given',CR,LF,'$'
wwmsg1	db 'Writing $'
wwmsg2	db ' bytes',CR,LF,'$'
diskful	db 'Disk full',CR,LF,'$'
openerr	db 'Error '
openerr1 db '____ opening file',CR,LF,'$'
doserr2	db 'File not found',CR,LF,'$'
doserr3	db 'Path not found',CR,LF,'$'
doserr5	db 'Access denied',CR,LF,'$'
doserr8	db 'Insufficient memory',CR,LF,'$'

;--- flags for instruction operands.
;--- First the sizes.

OP_ALL	equ 40h		;byte/word/dword operand (could be 30h but ...)
OP_1632	equ 50h		;word or dword operand
OP_8	equ 60h		;byte operand
OP_16	equ 70h		;word operand
OP_32	equ 80h		;dword operand
OP_64	equ 90h		;qword operand

OP_SIZE	equ OP_ALL		;the lowest of these

;--- These operand types need to be combined with a size flag..
;--- order must match items in asm_jmp1, bittab and dis_jmp1

OP_IMM		equ 0		;immediate
OP_RM		equ 2		;reg/mem
OP_M		equ 4		;mem (but not reg)
OP_R_MOD	equ 6		;register, determined from MOD R/M part
OP_MOFFS	equ 8		;memory offset; e.g., [1234]
OP_R		equ 10		;reg part of reg/mem byte
OP_R_ADD	equ 12		;register, determined from instruction byte
OP_AX		equ 14		;al or ax or eax

;--- These don't need a size.
;--- order must match items in asm_jmp1, bittab and dis_optab.
;--- additionally, order of OP_M64 - OP_FARMEM is used
;--- in table asm_siznum

;--- value 0 is used to terminate an operand list ( see macro opl )
OP_M64		equ 2		; 0 qword memory (obsolete?)
OP_MFLOAT	equ 4		; 1 float memory
OP_MDOUBLE	equ 6		; 2 double-precision floating memory
OP_M80		equ 8		; 3 tbyte memory
OP_MXX		equ 10		; 4 memory (size unknown)
OP_FARMEM	equ 12		; 5 memory far16/far32 pointer 
OP_FARIMM	equ 14		; 6 far16/far32 immediate
OP_REL8		equ 16		; 7 byte address relative to IP
OP_REL1632	equ 18		; 8 word or dword address relative to IP
OP_1CHK		equ 20		; 9 check for ST(1)
OP_STI		equ 22		;10 ST(I)
OP_CR		equ 24		;11 CRx
OP_DR		equ 26		;12 DRx
OP_TR		equ 28		;13 TRx
OP_SEGREG	equ 30		;14 segment register
OP_IMMS8	equ 32		;15 sign extended immediate byte
OP_IMM8		equ 34		;16 immediate byte (other args may be (d)word)
OP_MMX		equ 36		;17 MMx
OP_SHOSIZ	equ 38		;18 set flag to always show the size

OP_1		equ 40		;19 1 (simple "string" ops from here on)
OP_3		equ 42		;20 3
OP_DX		equ 44		;21 DX
OP_CL		equ 46		;22 CL
OP_ST		equ 48		;23 ST (top of coprocessor stack)
OP_CS		equ 50		;24 CS
OP_DS		equ 52		;25 DS
OP_ES		equ 54		;26 ES
OP_FS		equ 56		;27 FS
OP_GS		equ 58		;28 GS
OP_SS		equ 60		;29 SS

OP_STR equ OP_1		;first "string" op

;--- Instructions that have an implicit operand subject to a segment override
;--- (outsb/w, movsb/w, cmpsb/w, lodsb/w, xlat).

prfxtab	db 06eh,06fh, 0a4h,0a5h, 0a6h,0a7h, 0ach,0adh, 0d7h
P_LEN	equ $ - prfxtab

;--- Instructions that can be used with REP/REPE/REPNE.

replist	db 06ch,06eh,0a4h,0aah,0ach	;REP (INSB, OUTSB, MOVSB, STOSB, LODSB)
N_REPNC  equ $ - replist
		db 0a6h,0aeh				;REPE/REPNE (CMPSB, SCASB)
N_REPALL equ $ - replist

	include <debugtbl.inc>
;ASMMOD EQU opidx

opindex label byte
	.radix 16t
opidx = 0
	repeat ASMMOD
if opidx lt 10h
oi_name textequ <OPLIST_0>,%opidx
else
oi_name textequ <OPLIST_>,%opidx
endif
	db oi_name
opidx = opidx + 1
	endm
	.radix 10t

CONST ends

_TEXT segment

	assume ds:DGROUP

;--- Begin main command loop.

cmdloop proc
	mov sp,[top_sp]	;restore stack (this must be first)
	mov [errret],offset cmdloop
	push ds
	pop es
	mov dx,offset prompt1
	mov cx,1
	call getline	;prompted input
	cmp al,CR
	jnz @F
	mov dx, [lastcmd]
	dec si
	jmp cmd4
@@:
	cmp al,';'
	je cmdloop	;if comment
	cmp al,'?'
	je printhelp	;if request for help
	or al,TOLOWER
	sub al,'a'
	cmp al,ENDCMD - 'a'
	ja errorj1		;if not recognized
	cbw
	xchg bx,ax
	call skipcomma
	shl bx,1
	mov dx,[cmdlist+bx]
	mov [lastcmd],offset dmycmd
cmd4:
	mov di,offset line_out
	call dx
	jmp cmdloop		;back to the top

errorj1:
	jmp cmd_error

cmdloop endp


dmycmd:
	ret

printhelp:
	mov dx,offset helpmsg
	mov cx,offset helpmsg2 - offset helpmsg
	call stdout
	jmp cmdloop		;done

;--- A command - tiny assembler.

_DATA segment

asm_mn_flags	db 0	;flags for the mnemonic

AMF_D32		equ 1		;32bit opcode/data operand
AMF_WAIT	equ 2
AMF_A32		equ 4		;address operand is 32bit
AMF_SIB		equ 8		;there's a SIB in the arguments
AMF_MSEG	equ 10h		;if a seg prefix was given b4 mnemonic
AMF_FSGS	equ 20h		;if FS or GS was encountered

AMF_D16		equ 40h		;16bit opcode/data operand
AMF_ADDR	equ 80h		;address operand is given

;--- aa_saved_prefix and aa_seg_pre must be consecutive.
aa_saved_prefix	db 0	;WAIT or REP... prefix
aa_seg_pre	db 0		;segment prefix

mneminfo	dw 0		;address associated with the mnemonic
a_opcode	dw 0		;op code info for this variant
a_opcode2	dw 0		;copy of a_opcode for obs-instruction

AINSTR struct
rmaddr	dw ?		;address of operand giving the R/M byte
;--- regmem and sibbyte must be consecutive
regmem	db ?		;mod reg r/m part of instruction
sibbyte	db ?		;SIB byte
immaddr	dw ?		;address of operand giving the immed stf
xxaddr	dw ?		;address of additional stuff
;--- dismach and dmflags must be consecutive
dismach	db ?		;type of processor needed
dmflags	db ?		;flags for extra processor features

DM_COPR		equ 1	;math coprocessor
DM_MMX		equ 2	;MMX extensions

opcode_or	db ?	;extra bits in the op code
opsize		db ?	;size of this operation (2 or 4)
varflags	db ?	;flags for this variant

VAR_LOCKABLE	equ 1	;variant is lockable
VAR_MODRM		equ 2	;if there's a MOD R/M here
VAR_SIZ_GIVN	equ 4	;if a size was given
VAR_SIZ_FORCD	equ 8	;if only one size is permitted
VAR_SIZ_NEED	equ 10h	;if we need the size
VAR_D16			equ 20h	;if operand size is WORD
VAR_D32			equ 40h	;if operand size is DWORD

reqsize	db ?		;size that this arg should be
AINSTR ends

ai	AINSTR <?>

_DATA ends

CONST segment

;--- search for "obsolete" instructions
;--- dbe0: FENI
;--- dbe1: FDISI
;--- dbe4: FSETPM
;---  124: MOV TRx, reg
;---  126: MOV reg, TRx

a_obstab	dw 0dbe0h,0dbe1h,0dbe4h,124h,126h	;obs. instruction codes
obsmach		db 1,1,2,4,4	;max permissible machine for the above

modrmtab	db 11,0,13,0,15,0,14,0	;[bx], [bp], [di], [si]
			db 15,13,14,13,15,11,14,11	;[bp+di],[bp+si],[bx+di],[bx+si]

aam_args	db 'a',CR

;--- Equates for parsed arguments, stored in OPRND.flags

ARG_DEREF		equ 1	;non-immediate memory reference
ARG_MODRM		equ 2	;if we've computed the MOD R/M byte
ARG_JUSTREG		equ 4	;a solo register
ARG_WEIRDREG	equ 8	;if it's a segment register or CR, etc.
ARG_IMMED		equ 10h	;if it's just a number
ARG_FARADDR		equ 20h	;if it's of the form xxxx:yyyyyyyy

;--- For each operand type in the following table, the first byte is
;--- the bits, at least one of which must be present; the second is the
;--- bits all of which must be absent.
;--- the items in bittab must be ordered similiar to asm_jmp1 and dis_jmp1.

bittab label byte
	db ARG_IMMED			;OP_IMM
	db ARG_DEREF+ARG_JUSTREG;OP_RM
	db ARG_DEREF			;OP_M
	db ARG_JUSTREG			;OP_R_MOD
	db ARG_DEREF			;OP_MOFFS
	db ARG_JUSTREG			;OP_R
	db ARG_JUSTREG			;OP_R_ADD
	db ARG_JUSTREG			;OP_AX

	db ARG_DEREF			; 0 OP_M64
	db ARG_DEREF			; 1 OP_MFLOAT
	db ARG_DEREF			; 2 OP_MDOUBLE
	db ARG_DEREF			; 3 OP_M80
	db ARG_DEREF			; 4 OP_MXX
	db ARG_DEREF			; 5 OP_FARMEM
	db ARG_FARADDR			; 6 OP_FARIMM
	db ARG_IMMED			; 7 OP_REL8
	db ARG_IMMED			; 8 OP_REL1632
	db ARG_WEIRDREG			; 9 OP_1CHK
	db ARG_WEIRDREG			;10 OP_STI
	db ARG_WEIRDREG			;11 OP_CR
	db ARG_WEIRDREG			;12 OP_DR
	db ARG_WEIRDREG			;13 OP_TR
	db ARG_WEIRDREG			;14 OP_SEGREG
	db ARG_IMMED			;15 OP_IMMS8
	db ARG_IMMED			;16 OP_IMM8
	db ARG_WEIRDREG			;17 OP_MMX
	db 0ffh					;18 OP_SHOSIZ

	db ARG_IMMED			;OP_1
	db ARG_IMMED			;OP_3
	db ARG_JUSTREG			;OP_DX
	db ARG_JUSTREG			;OP_CL
	db ARG_WEIRDREG			;OP_ST
	db ARG_WEIRDREG			;OP_CS
	db ARG_WEIRDREG			;OP_DS
	db ARG_WEIRDREG			;OP_ES
	db ARG_WEIRDREG			;OP_FS
	db ARG_WEIRDREG			;OP_GS
	db ARG_WEIRDREG			;OP_SS

;--- special ops DX, CL, ST, CS, DS, ES, FS, GS, SS
;--- entry required if ao48 is set above

asm_regnum label byte
	db REG_DX, REG_CL, REG_ST, REG_CS, REG_DS, REG_ES, REG_FS, REG_GS, REG_SS

;--- size qualifier
;--- 1  BY=BYTE ptr
;--- 2  WO=WORD ptr
;--- 3  unused
;--- 4  DW=DWORD ptr
;--- 5  QW=QWORD ptr
;--- 6  FL=FLOAT ptr (REAL4)
;--- 7  DO=DOUBLE ptr (REAL8)
;--- 8  TB=TBYTE ptr (REAL10)
;--- 9  SH=SHORT 
;--- 10 LO=LONG
;--- 11 NE=NEAR ptr
;--- 12 FA=FAR ptr

SIZ_NONE	equ 0
SIZ_BYTE	equ 1
SIZ_WORD	equ 2
SIZ_DWORD	equ 4
SIZ_QWORD	equ 5
SIZ_FLOAT	equ 6
SIZ_DOUBLE	equ 7
SIZ_TBYTE	equ 8
SIZ_SHORT	equ 9
SIZ_LONG	equ 10
SIZ_NEAR	equ 11
SIZ_FAR		equ 12

sizetcnam	db 'BY','WO','WO','DW','QW','FL','DO','TB','SH','LO','NE','FA'

;--- sizes for OP_M64, OP_MFLOAT, OP_MDOUBLE, OP_M80, OP_MXX, OP_FARMEM

asm_siznum	db SIZ_QWORD, SIZ_FLOAT, SIZ_DOUBLE, SIZ_TBYTE
			db -1, SIZ_FAR			;-1 = none

CONST ends

;--- write byte in AL to BX/[E]DX, then increment [E]DX

writeasm proc
	call writemem
	inc dx
	ret
writeasm endp

;--- write CX bytes from DS:SI to BX:[E]DX

writeasmn proc
	jcxz nowrite
@@:
	lodsb
	call writeasm
	loop @B
nowrite:
	ret
writeasmn endp

aa proc
	mov [errret],offset aa01
	cmp al,CR
	je aa01			;if end of line
	mov bx,[regs.rCS]	;default segment to use
aa00a:
	call getaddr	;get address into bx:(e)dx
	call chkeol		;expect end of line here
	mov [a_addr+0],dx	;save the address
	mov word ptr [a_addr+4],bx

;--- Begin loop over input lines.

aa01:
	mov sp,[top_sp]	;restore the stack (this implies no "ret")
	mov di,offset line_out
	mov ax,[a_addr+4]
	call hexword
	mov al,':'
	stosb
	mov [asm_mn_flags],0
	mov bp,offset hexword
	mov ax,[a_addr+0]
	call bp
	mov al,' '
	stosb
	call getline00
	cmp al,CR
	je aa_exit	;if done
	cmp al,';'
	je aa01		;if comment
	mov word ptr [aa_saved_prefix],0 ;clear aa_saved_prefix and aa_seg_pre

;--- Get mnemonic and look it up.

aa02:
	mov di,offset line_out	;return here after LOCK/REP/SEG prefix
	push si			;save position of mnemonic
aa03:
	cmp al,'a'
	jb @F			;if not lower case letter
	cmp al,'z'
	ja @F
	and al,TOUPPER	;convert to upper case
@@:
	stosb
	lodsb
	cmp al,CR
	je @F			;if end of mnemonic
	cmp al,';'
	je @F
	cmp al,' '
	je @F
	cmp al,':'
	je @F
	cmp al,TAB
	jne aa03
@@:
	or byte ptr [di-1],80h	;set highest bit of last char of mnemonic
	call skipwh0	;skip to next field
	dec si
	push si			;save position in input line
;	mov al,0
;	stosb

;--- now search mnemonic in list

	mov si,offset mnlist
aa06:               ;<--- next mnemonic
	mov bx,si
	add si,2		;skip the 'asmtab' offset 
	mov cx,si
@@:
	lodsb			;skip to end of string
	and al,al
	jns @B			;if not end of string
	xchg cx,si
	push cx
	sub cx,si		;size of opcode in mnlist
	mov di,offset line_out
	repe cmpsb
	pop si
	je aa14			;if found it
	cmp si,offset end_mnlist
	jc aa06			;next mnemonic
	pop si			;skip position in input line
aa13a:
	pop si			;skip position of mnemonic
aa13b:
	jmp cmd_error	;complain
aa_exit:
	jmp cmdloop		;done with this command

;--- We found the mnemonic.

aa14:
	mov si,[bx]		;get the offset into asmtab
	add si,offset asmtab

;   Now si points to the spot in asmtab corresponding to this mnemonic.
;   The format of the assembler table is as follows.
;   First, there is optionally one of the following bytes:
;       ASM_DB      db mnemonic
;       ASM_DW      dw mnemonic
;       ASM_DD      dd mnemonic
;       ASM_WAIT    the mnemonic should start with a wait
;                   instruction.
;       ASM_D32     This is a 32 bit instruction variant.
;       ASM_D16     This is a 16 bit instruction variant.
;       ASM_AAX     Special for AAM and AAD instructions:
;                   put 0ah in for a default operand.
;       ASM_SEG     This is a segment prefix.
;       ASM_LOCKREP This is a LOCK or REP... prefix.
;
;   Then, in most cases, this is followed by one or more of the following
;   sequences, indicating an instruction variant.
;   ASM_LOCKABLE (optional) indicates that this instruction can
;                follow a LOCK prefix.
;   ASM_MACHx    (optional) indicates the first machine on which this
;                instruction appeared.
;   [word]       This is a 16-bit integer, most significant byte
;                first, giving ASMMOD * a + b, where b is an
;                index into the array opindex (indicating the
;                key, or type of operand list), and a is as
;                follows:
;                0-255     The (one-byte) instruction.
;                256-511   The lower 8 bits give the second byte of
;                          a two-byte instruction beginning with 0fh.
;                512-575   Bits 2-0 say which floating point instruction
;                          this is (0d8h-0dfh), and 5-3 give the /r
;                          field.
;                576-...   (a-576)/8 is the index in the array agroups
;                          (which gives the real value of a), and the
;                          low-order 3 bits gives the /r field.
;
;   [byte]       This gives the second byte of a floating
;                instruction if 0d8h <= a <= 0dfh.
;
;   Following these is an ASM_END byte.
;
;   Exceptions:
;       ASM_SEG and ASM_LOCKREP are followed by just one byte, the
;       prefix byte.
;       ASM_DB, ASM_DW, and ASM_DD don't need to be followed by
;       anything.

ASM_END		equ 0ffh
ASM_DB		equ 0feh
ASM_DW		equ 0fdh
ASM_DD		equ 0fch
ASM_ORG		equ 0fbh
ASM_WAIT	equ 0fah
ASM_D32		equ 0f9h
ASM_D16		equ 0f8h
ASM_AAX		equ 0f7h
ASM_SEG		equ 0f6h
ASM_LOCKREP	equ 0f5h
ASM_LOCKABLE equ 0f4h
ASM_MACH6	equ 0f3h
ASM_MACH5	equ 0f2h
ASM_MACH4	equ 0f1h
ASM_MACH3	equ 0f0h
ASM_MACH2	equ 0efh
ASM_MACH1	equ 0eeh
ASM_MACH0	equ 0edh

	cmp byte ptr [si],ASM_LOCKREP	;check for mnemonic flag byte
	jb aa15						;if none
	lodsb						;get the prefix
	sub al,ASM_LOCKREP			;convert to 0-9
	je aa18						;if LOCK or REP...
	cbw
	dec ax
	jz aa17						;if segment prefix (ASM_SEG)
	dec ax
	jz aa16						;if aad or aam (ASM_AAX)
	dec ax
	jz aa15_1					;if ASM_D16
	cmp al,3
	jae aa20					;if ASM_ORG or ASM_DD or ASM_DW or ASM_DB
	or [asm_mn_flags],al		;save AMF_D32 or AMF_WAIT (1 or 2)
aa15:
	jmp ab01					;now process the arguments
aa15_1:
	or [asm_mn_flags],AMF_D16
	inc si						;skip the ASM_D32 byte
	jmp ab01					;now process the arguments

aa16:
	jmp ab00

;--- segment prefix

aa17:
	lodsb			;get prefix value
	mov [aa_seg_pre],al
	mov cl,al
	or [asm_mn_flags],AMF_MSEG
	pop si			;get position in input line
	pop ax			;skip
	lodsb
	cmp al,':'
	jne aa13b
	call skipwhite
	cmp al,CR
	je @F
	cmp al,';'
	jne aa13b
@@:
	mov di,offset line_out
	mov al,cl
	stosb
	jmp aa27		;back for more

;--- LOCK or REP prefix

aa18:
	lodsb			;get prefix value
	xchg al,[aa_saved_prefix]
	cmp al,0
	jnz aa13a		;if there already was a saved prefix
	pop si
	pop ax
	lodsb
	cmp al,CR
	je @F			;if end of line
	cmp al,';'
	je @F			;if end of line (comment)
	jmp aa02		;back for more
@@:
	mov al,[aa_saved_prefix]	;just a prefix, nothing else
	mov di,offset line_out
	stosb
	jmp aa27

;--- Pseudo ops (org or db/dw/dd).

aa20:
	cmp word ptr [aa_saved_prefix],0
	jnz aa13a		;if there was a prefix or a segment: error
	pop si			;get position in input line
	sub al,3		;AX=0 if org, 1 if dd, 2 if dw, 3 if db.
	jnz aa20m		;if not ORG

;--- Process ORG pseudo op.

	call skipwhite
	cmp al,CR
	je @F				;if nothing
	mov bx,[a_addr+4]	;default segment
	jmp aa00a			;go to top
@@:
	jmp aa01			;get next line

;--- Data instructions (DB/DW/DD).

aa20m:
	mov di,offset line_out	;put the bytes here when we get them
	xchg ax,bx				;mov bx,ax
	shl bx,1
	mov bp,[bx+aadbsto-2]	;get address of storage routine
	call skipwhite
	cmp al,CR
	je aa27				;if end of line

aa21:					;<--- loop
	cmp al,'"'
	je aa22				;if string
	cmp al,"'"
	je aa22				;if string
	call aageti			;get a numerical value into dx:bx, size into cl
	cmp cl,cs:[bp-1]	;compare with size
	jg aa24				;if overflow
	xchg ax,bx
	call bp				;store value in AL/AX/DX:AX
	cmp di,offset real_end
	ja aa24				;if output line overflow
	xchg ax,bx
	jmp aa26			;done with this one

aa22:
	mov ah,al
aa23:
	lodsb
	cmp al,CR
	je aa24			;if end of line
	cmp al,ah
	je aa25			;if end of string
	stosb
	cmp di,offset real_end
	jbe aa23		;if output line not overflowing
aa24:
	jmp aa13b		;error
aa25:
	lodsb
aa26:
	call skipcomm0
	cmp al,CR
	jne aa21		;if not end of line

;--- End of line.  Copy it to debuggee's memory

aa27:
	mov si,offset line_out
	mov bx,[a_addr+4]
	mov dx,[a_addr+0]
	mov cx,di
	sub cx,si
	call writeasmn
	mov [a_addr+0],dx
	jmp aa01

CONST segment
;--- table for routine to store a number ( index dd=1,dw=2,db=3 )
aadbsto dw sto_dd,sto_dw,sto_db
CONST ends

;--- Routines to store a byte/word/dword.

	db 4            ;size to store
sto_dd:
	stosw			;store a dword value
	xchg ax,dx
	stosw
	xchg ax,dx
	ret
	db 2            ;size to store
sto_dw:
	stosw			;store a word value
	ret
	db 1            ;size to store
sto_db:
	stosb			;store a byte value
	ret

;   Here we process the AAD and AAM instructions.  They are special
;   in that they may take a one-byte argument, or none (in which case
;   the argument defaults to 0ah = ten).

ab00:
	mov [mneminfo],si	;save this address
	pop si
	lodsb
	cmp al,CR
	je ab00a		;if end of line
	cmp al,';'
	jne ab01b		;if not end of line
ab00a:
	mov si,offset aam_args	;fake a 0ah argument
	jmp ab01a

;--- Process normal instructions.

;   First we parse each argument into a 12-byte data block (OPRND), stored
;   consecutively at line_out, line_out+12, etc.
;   This is stored as follows.

;   [di]    Flags (ARG_DEREF, etc.)
;   [di+1]  Unused
;   [di+2]  Size argument, if any (1=byte, 2=word, 3=(unused), 4=dword,
;       5=qword, 6=float, 7=double, 8=tbyte, 9=short, 10=long, 11=near,
;       12=far), see SIZ_xxx and sizetcnam
;   [di+3]  Size of MOD R/M displacement
;   [di+4]  First register, or MOD R/M byte, or num of additional bytes
;   [di+5]  Second register or index register or SIB byte
;   [di+6]  Index factor
;   [di+7]  Sizes of numbers are or-ed here
;   [di+8]  (dword) number

;   For arguments of the form xxxx:yyyyyyyy, xxxx is stored in <num2>,
;   and yyyyyyyy in <num>.  The number of bytes in yyyyyyyy is stored in
;   opaddr, 2 is stored in <numadd>, and di is stored in xxaddr.

OPRND struc
flags	db ?	;+0
		db ?   
sizearg	db ?	;+2
sizedis	db ?	;+3
union
reg1	db ?	;+4
numadd	db ?	;+4 (additional bytes, stored at num2 (up to 4)
ends
union
struct
reg2	db ?	;+5
index	db ?	;+6
ends
num2	dw ?	;+5
ends
orednum	db ?	;+7
num		dd ?	;+8
OPRND ends

ab01:
	mov [mneminfo],si	;save this address
	pop si				;get position in line
ab01a:
	lodsb
ab01b:
	mov di,offset line_out

;--- Begin loop over operands.

ab02:               ;<--- next operand
	cmp al,CR
	je ab03			;if end of line
	cmp al,';'
	jne ab04		;if not end of line
ab03:
	jmp ab99		;to next phase

ab04:
	push di			;clear out the current OPRND storage area
	mov cx,sizeof OPRND / 2
	xor ax,ax
	rep stosw
	pop di

;--- Small loop over "BYTE PTR" and segment prefixes.

ab05:
	dec si
	mov ax,[si]
	and ax,TOUPPER_W
	cmp [di].OPRND.sizearg,SIZ_NONE
	jne ab07		;if already have a size qualifier ("BYTE PTR",...)
	push di
	mov di,offset sizetcnam
	mov cx,sizeof sizetcnam / 2
	repne scasw
	pop di
	jne ab07		;if not found
	or cx,cx
	jnz @F			;if not 'FA'
	mov al,[si+2]
	and al,TOUPPER
	cmp al,'R'
	jne ab09		;if not 'FAR' (could be hexadecimal)
@@:
	sub cl,sizeof sizetcnam / 2
	neg cl			;convert to 1, ..., 12
	mov [di].OPRND.sizearg,cl
	call skipalpha	;go to next token
	mov ah,[si]
	and ax,TOUPPER_W
	cmp ax,'TP'
	jne ab05		;if not 'PTR'
	call skipalpha	;go to next token
	jmp ab05

ab07:
	cmp [aa_seg_pre],0
	jne ab09		;if we already have a segment prefix
	push di
	mov di,offset segrgnam
	mov cx,N_SEGREGS
	repne scasw
	pop di
	jne ab09		;if not found
	push si			;save si in case there's no colon
	lodsw
	call skipwhite
	cmp al,':'
	jne ab08		;if not followed by ':'
	pop ax			;discard saved si
	call skipwhite	;skip it
	mov bx,offset prefixlist + 5
	sub bx,cx
	mov al,[bx]		;look up the prefix byte
	mov [aa_seg_pre],al	;save it away
	jmp ab05
ab08:
	pop si

;--- Begin parsing main part of argument.

;--- first check registers

ab09:
	push di			;check for solo registers
	mov di,offset rgnam816
	mov cx,N_ALLREGS;8+16bit regs, segment regs, special regs
	call aagetreg
	pop di
	jc ab14			;if not a register
	or [di].OPRND.flags, ARG_JUSTREG
	mov [di].OPRND.reg1,bl	;save register number
	cmp bl,24		;0-23 = AL-DH, AX-DI, EAX-EDI
	jae @F			;if it's not a normal register
	xchg ax,bx		;mov al,bl
	mov cl,3
	shr al,cl		;al = size:  0 -> byte, 1 -> word, 2 -> dword
	add al,-2
	adc al,3		;convert to 1, 2, 4 (respectively)
	jmp ab13
@@:
	xor [di].OPRND.flags, ARG_JUSTREG + ARG_WEIRDREG
	mov al,SIZ_WORD	;register size
	cmp bl,REG_ST	;24-29=segment registers
	ja ab11			;if it's MM, CR, DR or TR
	je @F			;if it's ST
	cmp bl,28
	jb ab13			;if it's a normal segment register (DS,ES,SS,CS)
	or [asm_mn_flags],AMF_FSGS	;flag it
	jmp ab13
@@:
	cmp byte ptr [si],'('
	jne ab12		;if just plain ST
	lodsb
	lodsb
	sub al,'0'
	cmp al,7
	ja ab10			;if not 0..7
	mov [di].OPRND.reg2,al	;save the number
	lodsb
	cmp al,')'
	je ab12			;if not error
ab10:
	jmp aa13b		;error

;--- other registers 31-34 (MM, CR, DR, TR)

ab11:
	lodsb
	sub al,'0'
	cmp al,7
	ja ab10			;if error
	mov [di].OPRND.reg2,al	;save the number
	mov al,SIZ_DWORD;register size
	cmp bl,REG_MM
	jne ab13		;if not MM register
	or [di].OPRND.flags, ARG_JUSTREG
	mov al,SIZ_QWORD
	jmp ab13
ab12:
	mov al,0		;size for ST regs
ab13:
	cmp al,[di].OPRND.sizearg	;compare with stated size
	je @F			;if same
	xchg al,[di].OPRND.sizearg
	cmp al,0
	jne ab10		;if wrong size given - error
@@:
	jmp ab44		;done with this operand

;--- It's not a register reference.  Try for a number.

ab14:
	lodsb
	call aaifnum
	jc ab17			;it's not a number
	call aageti		;get the number
	mov [di].OPRND.orednum,cl
	mov word ptr [di].OPRND.num+0,bx
	mov word ptr [di].OPRND.num+2,dx
	call skipwh0
	cmp cl,2
	jg ab17			;if we can't have a colon here
	cmp al,':'
	jne ab17		;if not xxxx:yyyy
	call skipwhite
	call aageti
	mov cx,word ptr [di].OPRND.num+0
	mov [di].OPRND.num2,cx
	mov word ptr [di].OPRND.num+0,bx
	mov word ptr [di].OPRND.num+2,dx
	or [di].OPRND.flags, ARG_FARADDR
	jmp ab43		;done with this operand

;--- Check for [...].

ab15:
	jmp ab30		;do post-processing

ab16:
	call skipwhite
ab17:
	cmp al,'['		;begin loop over sets of []
	jne ab15		;if not [
	or [di].OPRND.flags, ARG_DEREF ;set the flag
ab18:
	call skipwhite
ab19:
	cmp al,']'		;begin loop within []
	je ab16			;if done

;--- Check for a register (within []).

	dec si
	push di
	mov di,offset rgnam16
	mov cx,N_REGS16
	call aagetreg
	pop di
	jc ab25			;if not a register
	cmp bl,16
	jae @F			;if 32-bit register
	add bl,8		;adjust 0..7 to 8..15
	jmp ab21
@@:
	cmp [di].OPRND.reg2, 0
	jnz ab21		;if we already have an index
	call skipwhite
	dec si
	cmp al,'*'
	jne ab21		;if not followed by '*'
	inc si
	mov [di].OPRND.reg2,bl	;save index register
	call skipwhite
	call aageti
	call aaconvindex
	jmp ab28		;ready for next part

ab21:
	cmp [di].OPRND.reg1,0
	jne @F			;if there's already a register
	mov [di].OPRND.reg1,bl
	jmp ab23
@@:
	cmp [di].OPRND.reg2, 0
	jne ab24		;if too many registers
	mov [di].OPRND.reg2,bl
ab23:
	call skipwhite
	jmp ab28		;ready for next part
ab24:
	jmp aa13b		;error

;--- Try for a number (within []).

ab25:
	lodsb
ab26:
	call aageti		;get a number (or flag an error)
	call skipwh0
	cmp al,'*'
	je ab27			;if it's an index factor
	or [di].OPRND.orednum,cl
	add word ptr [di].OPRND.num+0,bx
	adc word ptr [di].OPRND.num+2,dx
	jmp ab28		;next part ...

ab27:
	call aaconvindex
	call skipwhite
	dec si
	push di
	mov di,offset rgnam16
	xor cx,cx
	call aagetreg
	pop di
	jc ab24			;if error
	cmp [di].OPRND.reg2, 0
	jne ab24		;if there is already a register
	mov [di].OPRND.reg2, bl
	call skipwhite

;--- Ready for the next term within [].

ab28:
	cmp al,'-'
	je ab26			;if a (negative) number is next
	cmp al,'+'
	jne @F			;if no next term (presumably)
	jmp ab18
@@:
	jmp ab19		;back for more

;--- Post-processing for complicated arguments.

ab30:
	cmp word ptr [di].OPRND.reg1,0	;check both reg1+reg2
	jnz ab32		;if registers were given ( ==> create MOD R/M)
	cmp [di].OPRND.orednum,0
	jz ab31			;if nothing was given ( ==> error)
	cmp [di].OPRND.flags,0
	jnz ab30b		;if it was not immediate
	or [di].OPRND.flags,ARG_IMMED
ab30a:
	jmp ab43		;done with this argument
ab30b:
	or [asm_mn_flags],AMF_ADDR
	mov al,2		;size of the displacement
	test [di].OPRND.orednum,4
	jz @F			;if not 32-bit displacement
	inc ax
	inc ax
	or [asm_mn_flags],AMF_A32	;32-bit addressing
@@:
	mov [di].OPRND.sizedis,al	;save displacement size
	jmp ab30a		;done with this argument
ab31:
	jmp aa13b		;flag an error

;   Create the MOD R/M byte.
;   (For disp-only or register, this will be done later as needed.)

ab32:
	or [di].OPRND.flags, ARG_MODRM
	mov al,[di].OPRND.reg1
	or al,[di].OPRND.reg2
	test al,16
	jnz ab34		;if 32-bit addressing
	test [di].OPRND.orednum,4
	jnz ab34		;if 32-bit addressing
;	or [asm_mn_flags], AMF_ADDR | AMF_A32
	or [asm_mn_flags], AMF_ADDR
	mov ax,word ptr [di].OPRND.reg1	;get reg1+reg2
	cmp al,ah
	ja @F			;make sure al >= ah
	xchg al,ah
@@:
	push di
	mov di,offset modrmtab
	mov cx,8
	repne scasw
	pop di
	jne ab31		;if not among the possibilities
	mov bx,206h		;max disp = 2 bytes; 6 ==> (non-existent) [bp]
	jmp ab39		;done (just about)

;--- 32-bit addressing

ab34:
	or [asm_mn_flags],AMF_A32 + AMF_ADDR
	mov al,[di].OPRND.reg1
	or al,[di].OPRND.index
	jnz @F			;if we can't optimize [EXX*1] to [EXX]
	mov ax,word ptr [di].OPRND.reg1	;get reg1+reg2
	xchg al,ah
	mov word ptr [di].OPRND.reg1,ax
@@:
	mov bx,405h		;max disp = 4 bytes; 5 ==> (non-existent) [bp]
	cmp [di].OPRND.reg2,0
	jne @F			;if there's a SIB
	mov cl,[di].OPRND.reg1
	cmp cl,16
	jl ab31			;if wrong register type
	and cl,7
	cmp cl,4		;check for ESP
	jne ab39		;if not, then we're done (otherwise do SIB)
@@:
	or [asm_mn_flags],AMF_SIB	;form SIB
	mov ch,[di].OPRND.index		;get SS bits
	mov cl,3
	shl ch,cl				;shift them halfway into place
	mov al,[di].OPRND.reg2	;index register
	cmp al,20
	je ab31			;if ESP ( ==> error)
	cmp al,0
	jne @F			;if not zero
	mov al,20		;set it for index byte 4
@@:
	cmp al,16
	jl ab31			;if wrong register type
	and al,7
	or ch,al		;put it into the SIB
	shl ch,cl		;shift it into place
	inc cx			;R/M for SIB = 4
	mov al,[di].OPRND.reg1	;now get the low 3 bits
	cmp al,0
	jne @F			;if there was a first register
	or ch,5
	jmp ab42		;MOD = 0, disp is 4 bytes
@@:
	cmp al,16
	jl ab45			;if wrong register type
	and al,7		;first register
	or ch,al		;put it into the SIB
	cmp al,5
	je ab40			;if it's EBP, then we don't recognize disp=0
					;otherwise bl will be set to 0

;--- Find the size of the displacement.

ab39:
	cmp cl,bl
	je ab40			;if it's [(E)BP], then disp=0 is still 1 byte
	mov bl,0		;allow 0-byte disp

ab40:
	push cx
	mov al,byte ptr [di].OPRND.num+0
	mov cl,7
	sar al,cl
	pop cx
	mov ah,byte ptr [di].OPRND.num+1
	cmp al,ah
	jne @F			;if it's bigger than 1 byte
	cmp ax,word ptr [di].OPRND.num+2
	jne @F			;ditto
	mov bh,0		;no displacement
	or bl,byte ptr [di].OPRND.num+0
	jz ab42			;if disp = 0 and it's not (E)BP
	inc bh			;disp = 1 byte
	or cl,40h		;set MOD = 1
	jmp ab42		;done
@@:
	or cl,80h		;set MOD = 2
ab42:
	mov [di].OPRND.sizedis,bh	;store displacement size
	mov word ptr [di].OPRND.reg1, cx	;store MOD R/M and maybe SIB

;--- Finish up with the operand.

ab43:
	dec si
ab44:
	call skipwhite
	add di,sizeof OPRND
	cmp al,CR
	je ab99			;if end of line
	cmp al,';'
	je ab99			;if comment (ditto)
	cmp al,','
	jne ab45		;if not comma ( ==> error)
	cmp di,offset line_out+ 3 * sizeof OPRND
	jae ab45		;if too many operands
	call skipwhite
	jmp ab02

ab45:
	jmp aa13b		;error jump

ab99:
	mov [di].OPRND.flags,-1;end of parsing phase
	push si			;save the location of the end of the string

;   For the next phase, we match the parsed arguments with the set of
;   permissible argument lists for the opcode.  The first match wins.
;   Therefore the argument lists should be ordered such that the
;   cheaper ones come first.

;   There is a tricky issue regarding sizes of memory references.
;   Here are the rules:
;      1.   If a memory reference is given with a size, then it's OK.
;      2.   If a memory reference is given without a size, but some
;       other argument is a register (which implies a size),
;       then the memory reference inherits that size.
;           Exceptions: OP_CL does not imply a size
;                   OP_SHOSIZ
;      3.   If 1 and 2 do not apply, but this is the last possible argument
;       list, and if the argument list requires a particular size, then
;       that size is used.
;      4.   In all other cases, flag an error.

ac01:				;<--- next possible argument list
	xor ax,ax
	mov di,offset ai
	mov cx,sizeof ai/2
	rep stosw
	mov si,[mneminfo]	;address of the argument variant

;--- Sort out initial bytes.  At this point:
;--- si = address of argument variant

ac02:               ;<--- next byte of argument variant
	lodsb
	sub al,ASM_MACH0
	jb ac05			;if no more special bytes
	cmp al,ASM_LOCKABLE - ASM_MACH0
	je @F			;if ASM_LOCKABLE
	ja ac04			;if ASM_END ( ==> error)
	mov [ai.dismach],al;save machine type
	jmp ac02		;back for next byte
@@:
	or [ai.varflags],VAR_LOCKABLE
	jmp ac02		;back for next byte

ac04:
	jmp aa13a		;error

;--- Get and unpack the word.

ac05:
	dec si
	lodsw
	xchg al,ah			;put into little-endian order
	xor dx,dx
	mov bx,ASMMOD
	div bx				;ax = a_opcode; dx = index into opindex
	mov [a_opcode],ax	;save ax
	mov [a_opcode2],ax	;save the second copy
	cmp ax,0dfh
	ja @F				;if not coprocessor instruction
	cmp al,0d8h
	jb @F				;ditto
	or [ai.dmflags],DM_COPR;flag it as an x87 instruction
	mov ah,al			;ah = low order byte of opcode
	lodsb				;get extra byte
	mov [ai.regmem],al		;save it in regmem
	mov [a_opcode2],ax	;save this for obsolete-instruction detection
	or [ai.varflags],VAR_MODRM	;flag its presence
@@:
	mov [mneminfo],si	;save si back again
	mov si,dx
	mov bl,[opindex+si]
	lea si,[oplists+bx]		;si = the address of our operand list
	mov di,offset line_out	;di = array of OPRNDs

;--- Begin loop over operands.

ac06:               ;<--- next operand
	lodsb			;get next operand byte
	cmp al,0
	je ac10			;if end of list
	cmp [di].OPRND.flags,-1
	je ac01			;if too few operands were given
	cmp al,OP_SIZE
	jb @F			;if no size needed
;	mov ah,0
;	mov cl,4
;	shl ax,cl		;move bits 4-7 (size) to ah (OP_1632=5,OP_8=6,OP_16=7,...)
;	shr al,cl		;move bits 0-3 back
	db 0d4h,10h		;=aam 10h (AX=00XY -> AX=0X0Y)
	mov [ai.reqsize],ah	;save size away
	jmp ac08
@@:					;AL = OP_M64 - ...
	add al,ASM_OPOFF - OP_M64	;adjust for the start entries im asm_jmp1
ac08:
	cbw
	xchg ax,bx		;now bx contains the offset
	mov cx,[asm_jmp1+bx] ;subroutine address
	shr bx,1
	mov al,[bittab+bx]
	test al,[di].OPRND.flags
	jz ac09			;if no required bits are present
	call cx			;call its specific routine
	cmp word ptr [si-1],(OP_1632+OP_R)*256+(OP_1632+OP_R_MOD)
	je ac06			;(hack) for IMUL instruction
	add di,sizeof OPRND	;next operand
	jmp ac06		;back for more

ac09:
	jmp ac01		;back to next possibility

;--- End of operand list.

ac10:
	cmp [di].OPRND.flags,-1
	jne ac09		;if too many operands were given

;--- Final check on sizes

	mov al,[ai.varflags]
	test al,VAR_SIZ_NEED
	jz ac12			;if no size needed
	test al,VAR_SIZ_GIVN
	jnz ac12		;if a size was given
	test al,VAR_SIZ_FORCD
	jz ac09			;if the size was not forced ( ==> reject)
	mov si,[mneminfo]
	cmp byte ptr [si],ASM_END
	je ac12			;if this is the last one
ac11:
	jmp aa13a		;it was not ==> error (not a retry)

;--- Check other prefixes.

ac12:
	mov al,[aa_saved_prefix]
	cmp al,0
	jz ac14			;if no saved prefixes to check
	cmp al,0f0h
	jne @F			;if it's a rep prefix
	test [ai.varflags],VAR_LOCKABLE
	jz ac11			;if this variant is not lockable - error
	jmp ac14		;done
@@:
	mov ax,[a_opcode]	;check if opcode is OK for rep{,z,nz}
	and al,not 1		;clear low order bit (MOVSW -> MOVSB)
	cmp ax,0ffh
	ja ac11				;if it's not a 1 byte instruction - error
	mov di,offset replist	;list of instructions that go with rep
	mov cx,N_REPALL			;scan all (REP + REPxx)
	repne scasb
	jnz ac11			;if it's not among them - error

ac14:
	test [asm_mn_flags],AMF_MSEG
	jz @F				;if no segment prefix before mnemonic
	mov ax,[a_opcode]	;check if opcode allows this
	cmp ax,0ffh
	ja ac11				;if it's not a 1 byte instruction - error
	mov di,offset prfxtab
	mov cx,P_LEN
	repne scasb
	jnz ac11			;if it's not in the list - error
@@:
	mov bx,[ai.immaddr]
	or bx,bx
	jz ac16			;if no immediate data
	mov al,[ai.opsize]
	neg al
	shl al,1
	test al,[bx+7]
	jnz ac11		;if the immediate data was too big - error

;   Put the instruction together
;   (maybe is this why they call it an assembler)

;   First, the prefixes (including preceding WAIT instruction)

ac16:
	mov dx,[a_addr+0]
	mov bx,[a_addr+4]
	test [asm_mn_flags],AMF_WAIT
	jz @F			;if no wait instruction beforehand
	mov al,9bh
	call writeasm
@@:
	mov al,[aa_saved_prefix]
	cmp al,0
	jz @F			;if no LOCK or REP prefix
	call writeasm
@@:

;--- a 67h address size prefix is needed
;--- 1. for CS32: if AMF_ADDR=1 and AMF_A32=1
;--- 2. for CS16: if AMF_ADDR=1 and AMF_A32=0

	mov al,[asm_mn_flags]
	test al,AMF_ADDR
	jz @F
	and al,AMF_A32
	and al,AMF_A32 + 40h
	jz @F
	cmp al,AMF_A32 + 40h
	jz @F
	mov al,67h
	call writeasm
@@:

;--- a 66h data size prefix is needed
;--- for CS16: if VAR_D32 == 1 or AMF_D32 == 1
;--- for CS32: if VAR_D16 == 1 or AMF_D16 == 1

	mov ah,[asm_mn_flags]
	mov al,[ai.varflags]
	test al,VAR_D32
	jnz ac20_1
	test ah,AMF_D32
	jz ac21
ac20_1:
	mov al,66h
	call writeasm		;store operand-size prefix
ac21:
	mov al,[aa_seg_pre]
	cmp al,0
	jz @F			;if no segment prefix
	call writeasm
	cmp al,64h
	jb @F			;if not 64 or 65 (FS or GS)
	or [asm_mn_flags],AMF_FSGS	;flag it
@@:

;--- Now emit the instruction itself.

	mov ax,[a_opcode]
	mov di,ax
	sub di,240h
	jae @F			;if 576-...
	cmp ax,200h
	jb ac24			;if regular instruction
	or [ai.dmflags],DM_COPR	;flag it as an x87 instruction
	and al,038h		;get register part
	or [ai.regmem],al
	xchg ax,di		;mov ax,di (the low bits of di are good)
	and al,7
	or al,0d8h
	jmp ac25		;on to decoding the instruction
@@:
	mov cl,3		;one instruction of a group
	shr di,cl
	and al,7
	shl al,cl
	or [ai.regmem],al
	shl di,1
	mov ax,[agroups+di]	;get actual opcode

ac24:
	cmp ah,0
	jz ac25			;if no 0fh first
	push ax			;store a 0fh
	mov al,0fh
	call writeasm
	pop ax

ac25:
	or al,[ai.opcode_or]	;put additional bits into the op code
	call writeasm		;store the op code itself

;--- Now store the extra stuff that comes with the instruction.

	mov ax,word ptr [ai.regmem]
	test [ai.varflags],VAR_MODRM
	jz @F			;if no mod reg/mem
	call writeasm
	test [asm_mn_flags],AMF_SIB
	jz @F			;if no SIB
	mov al,ah
	call writeasm	;store the MOD R/M and SIB, too
@@:

	mov di,[ai.rmaddr]
	or di,di
	jz @F			;if no offset associated with the R/M
	mov cl,[di].OPRND.sizedis
	mov ch,0
	lea si,[di].OPRND.num	;store the R/M offset (or memory offset)
	call writeasmn
@@:

;--- Now store immediate data

	mov di,[ai.immaddr]
	or di,di
	jz @F			;if no immediate data
	mov al,[ai.opsize]
	cbw
	xchg ax,cx		;mov cx,ax
	lea si,[di].OPRND.num
	call writeasmn
@@:

;--- Now store additional bytes (needed for, e.g., enter instruction)
;--- also for FAR memory address

	mov di,[ai.xxaddr]
	or di,di
	jz @F			;if no additional data
	lea si,[di].OPRND.numadd	;number of bytes (2 for FAR, size of segment)
	lodsb
	cbw
	xchg ax,cx		;mov cx,ax
	call writeasmn
@@:

;--- Done emitting. Update asm address offset.

	mov [a_addr],dx

;--- Compute machine type.

	cmp [ai.dismach],3
	jae ac31		;if we already know a 386 is needed
	test [asm_mn_flags], AMF_D32 or AMF_A32 or AMF_FSGS
	jnz ac30		;if 386
	test [ai.varflags],VAR_D32
	jz ac31			;if not 386
ac30:
	mov [ai.dismach],3
ac31:
	;mov di,offset a_obstab	;obsolete instruction table
	;mov cx,[a_opcode2]
	;call showmach		;get machine message into si, length into cx
	;jcxz ac33			;if no message
	jmp aa01		;back for the next input line

ac32:
	mov di,offset line_out
	rep movsb		;copy the line to line_out
	call putsline

ac33:
	jmp aa01		;back for the next input line

CONST segment

	align 2

;--- Jump table for operand types.
;--- order of entries in asm_jmp1 must match 
;--- the one in dis_jmp1 / dis_optab.

asm_jmp1 label word
	dw aop_imm,aop_rm,aop_m,aop_r_mod	;OP_IMM, OP_RM, OP_M, OP_R_MOD
	dw aop_moffs,aop_r,aop_r_add,aop_ax	;OP_MOFFS, OP_R, OP_R_ADD, OP_AX
ASM_OPOFF equ $ - asm_jmp1
;--- order must match the one in dis_optab
	dw ao17,ao17,ao17		;OP_M64, OP_MFLOAT, OP_MDOUBLE
	dw ao17,ao17,ao17		;OP_M80, OP_MXX, OP_FARMEM
	dw aop_farimm,aop_rel8,aop_rel1632;OP_FARIMM, OP_REL8, OP_REL1632
	dw ao29,aop_sti,aop_cr	;OP_1CHK, OP_STI, OP_CR
	dw ao34,ao35,ao39		;OP_DR, OP_TR, OP_SEGREG
	dw ao41,ao42,aop_mmx	;OP_IMMS8, OP_IMM8, OP_MMX
	dw ao44,ao46,ao47		;OP_SHOSIZ, OP_1, OP_3
	dw ao48,ao48,ao48		;OP_DX, OP_CL, OP_ST
	dw ao48,ao48,ao48		;OP_CS, OP_DS, OP_ES
	dw ao48,ao48,ao48		;OP_FS, OP_GS, OP_SS

CONST ends

;   Routines to check for specific operand types.
;   Upon success, the routine returns.
;   Upon failure, it pops the return address and jumps to ac01.
;   The routines must preserve si and di.

;--- OP_RM, OP_M, OP_R_MOD:  form MOD R/M byte.

aop_rm:
aop_m:
aop_r_mod:
	call ao90		;form reg/mem byte
	jmp ao07		;go to the size check

;--- OP_R:  register.

aop_r:
	mov al,[di].OPRND.reg1	;register number
	and al,7
	mov cl,3
	shl al,cl		;shift it into place
	or [ai.regmem],al	;put it into the reg/mem byte
	jmp ao07		;go to the size check

;--- OP_R_ADD:  register, added to the instruction.

aop_r_add:
	mov al,[di].OPRND.reg1
	and al,7
	mov [ai.opcode_or],al	;put it there
	jmp ao07		;go to the size check

;--- OP_IMM:  immediate data.

aop_imm:
	mov [ai.immaddr],di	;save the location of this
	jmp ao07		;go to the size check

;--- OP_MOFFS:  just the memory offset

aop_moffs:
	test [di].OPRND.flags,ARG_MODRM
	jnz ao11		;if MOD R/M byte ( ==> reject)
	mov [ai.rmaddr],di	;save the operand pointer
	jmp ao07		;go to the size check

;--- OP_AX:  check for AL/AX/EAX

aop_ax:
	test [di].OPRND.reg1,7
	jnz ao11		;if wrong register
	;jmp ao07		;go to the size check

;--- Size check

ao07:               ;<--- entry for OP_RM, OP_M, OP_R_MOD, OP_R, OP_R_ADD...
	or [ai.varflags],VAR_SIZ_NEED
	mov al,[ai.reqsize]
	sub al,5		;OP_1632 >> 4
	jl ao12			;if OP_ALL
	jz ao13			;if OP_1632
;--- OP_8=1, OP_16=2, OP_32=3, OP_64=4
	add al,-3
	adc al,3		;convert 3 --> 4 and 4 --> 5
ao08:               ;<--- entry for OP_M64 ... OP_FARMEM
	or [ai.varflags],VAR_SIZ_FORCD + VAR_SIZ_NEED
ao08_1:
	mov bl,[di].OPRND.sizearg
	or bl,bl
	jz @F			;if no size given
	or [ai.varflags],VAR_SIZ_GIVN
	cmp al,bl
	jne ao11		;if sizes conflict
@@:
	cmp al,[ai.opsize]
	je @F			;if sizes agree
	xchg al,[ai.opsize]
	cmp al,0
	jnz ao11		;if sizes disagree
	or [ai.varflags],VAR_SIZ_GIVN	;v1.18 added!!!
@@:
	ret

ao11:
	jmp ao50		;reject

;--- OP_ALL - Allow all sizes.

ao12:
	mov al,[di].OPRND.sizearg
	cmp al,SIZ_BYTE
	je ao15			;if byte
	jb ao14			;if unknown
	or [ai.opcode_or],1;set bit in instruction
	jmp ao14		;if size is 16 or 32

;--- OP_1632 - word or dword.

ao13:
	mov al,[di].OPRND.sizearg
ao14:
	cmp al,SIZ_NONE
	je ao16			;if still unknown
	cmp al,SIZ_WORD
	jne @F			;if word
	or [ai.varflags],VAR_D16
	jmp ao15
@@:
	cmp al,SIZ_DWORD
	jne ao11		;if not dword
	or [ai.varflags],VAR_D32
ao15:
	mov [ai.opsize],al
	or [ai.varflags],VAR_SIZ_GIVN
ao16:
	ret

;   OP_M64 - 64-bit memory reference.
;   OP_MFLOAT - single-precision floating point memory reference.
;   OP_MDOUBLE - double-precision floating point memory reference.
;   OP_M80 - 80-bit memory reference.
;   OP_MXX - memory reference, size unknown.
;   OP_FARMEM - far memory pointer

;--- bx contains byte index for bittab
ao17:
	call ao90		;form reg/mem byte
	mov al,[asm_siznum+bx-ASM_OPOFF/2]
	jmp ao08		;check size

;--- OP_FARIMM - far address contained in instruction

aop_farimm:
	mov al,2
	cmp word ptr [di].OPRND.num+2,0
	jz ao22			;if 16 bit address
@@:
	or [ai.varflags],VAR_D32
	mov al,4
ao22:
	mov [di].OPRND.numadd,2	;2 additional bytes (segment part)
	mov [ai.immaddr],di
	mov [ai.opsize],al			;2/4, size of offset
ao22_1:
	mov [ai.xxaddr],di
	ret

;--- OP_REL8 - relative address
;--- Jcc, LOOPx, JxCXZ

aop_rel8:
	mov al,SIZ_SHORT
	call aasizchk	;check the size
	mov cx,2		;size of instruction
	mov al,[asm_mn_flags]

	test al,AMF_D32 or AMF_D16
	jz ao23_1		;if not JxCXZ, LOOPx
	test al,AMF_D32
	jz @F
	or al,AMF_A32	; JxCXZ and LOOPx need a 67h, not a 66h prefix
@@:
	and al,not (AMF_D32 or AMF_D16)
	or al, AMF_ADDR
	mov [asm_mn_flags],al
	mov ah,0
	and al,AMF_A32
	or al,ah
	jz ao23_1
	cmp al,AMF_A32+40h
	jz ao23_1
	inc cx        ;instruction size = 3
ao23_1:
	mov bx,[a_addr+0]
	add bx,cx
	mov cx,[a_addr+2];v1.22: handle HiWord(EIP) properly
	adc cx,0
	mov ax,word ptr [di].OPRND.num+0
	mov dx,word ptr [di].OPRND.num+2
;--- CX:BX holds E/IP (=src), DX:AX holds dst
	sub ax,bx
	sbb dx,cx
	mov byte ptr [di].OPRND.num2,al
	mov cl,7        ;range must be ffffff80 <= x <= 0000007f
	sar al,cl       ;1xxxxxxxb -> FF, 0xxxxxxxb -> 00
	cmp al,ah
	jne ao_err1		;if too big
	cmp ax,dx
	jne ao_err1		;if too big
	mov [di].OPRND.numadd,1	;save the length
	jmp ao22_1		;save it away

;--- OP_REL1632:  relative jump/call to a longer address.
;--- size of instruction is
;--- a) CS 16-bit:
;---  3 (xx xxxx, jmp/call) or
;---  4 (0F xx xxxx)
;---  6 (66 xx xxxxxxxx)
;---  7 (66 0F xx xxxxxxxx)
;---
;--- b) CS 32-bit:
;---  5 (xx xxxxxxxx, jmp/call) or
;---  6 (0F xx xxxxxxxx)

aop_rel1632:
	mov bx,[a_addr+0]
	mov cx,3
	mov dx,word ptr [di].OPRND.num+2
	mov al,[di].OPRND.sizearg
	cmp [a_opcode],100h	;is a 0F xx opcode?
	jb @F
	inc cx
@@:
	cmp al,SIZ_NONE
	je @F			;if no size given
	cmp al,SIZ_DWORD
	je ao27			;if size "dword"
	cmp al,SIZ_LONG
	jne ao_err1		;if not size "long"
@@:
	or dx,dx
	jnz ao_err1		;if operand is too big
	mov al,2        ;displacement size 2
	jmp ao28
ao27:
	mov al,4        ;displacement size 4
	or [ai.varflags],VAR_D32
	add cx,3		;add 3 to instr size (+2 for displ, +1 for 66h)
ao28:
	add bx,cx
	mov cx,[a_addr+2]
	adc cx,0
	mov [di].OPRND.numadd,al	;store size of displacement (2 or 4)
	mov ax,word ptr [di].OPRND.num+0
	sub ax,bx		;compute DX:AX - CX:BX
	sbb dx,cx
	mov [di].OPRND.num2,ax
	mov [di].OPRND.num2+2,dx
	mov [ai.xxaddr],di
	ret
ao_err1:
	jmp ao50		;reject

;--- OP_1CHK - The assembler can ignore this one.

ao29:
	pop ax			;discard return address
	jmp ac06		;next operand

;--- OP_STI - ST(I).

aop_sti:
	mov al,REG_ST	;code for ST
	mov bl,[di].OPRND.reg2
	jmp ao38		;to common code

;--- OP_MMX [previously was OP_ECX (used for LOOPx)]

aop_mmx:
	mov al,REG_MM
	jmp ao37		;to common code

;--- OP_CR

aop_cr:
	mov al,[di].OPRND.reg2	;get the index
	cmp al,4
	ja ao_err1		;if too big
	jne @F			;if not CR4
	mov [ai.dismach],5	;CR4 is new to the 586
@@:
	cmp al,1
	jne @F
	cmp [di+sizeof OPRND].OPRND.flags,-1
	jne ao_err1		;if another arg (can't mov CR1,xx)
@@:
	mov al,REG_CR	;code for CR
	jmp ao37		;to common code

;--- OP_DR

ao34:
	mov al,REG_DR	;code for DR
	jmp ao37		;to common code

;--- OP_TR

ao35:
	mov al,[di].OPRND.reg2	;get the index
	cmp al,3
	jb ao_err1		;if too small
	cmp al,6
	jae @F
	mov [ai.dismach],4	;TR3-5 are new to the 486
@@:
	mov al,REG_TR	;code for TR

;--- Common code for these weird registers.

ao37:
	mov bl,[di].OPRND.reg2
	mov cl,3
	shl bl,cl
ao38:
	or [ai.regmem],bl
	or [ai.varflags],VAR_MODRM
	cmp al,[di].OPRND.reg1	;check for the right numbered register
	je ao40			;if yes, then return
ao38a:
	jmp ao50		;reject

;--- OP_SEGREG

ao39:
	mov al,[di].OPRND.reg1
	sub al,24
	cmp al,6
	jae ao38a		;if not a segment register
	mov cl,3
	shl al,cl
	or [ai.regmem],al
ao40:
	ret

;--- OP_IMMS8 - Sign-extended immediate byte (PUSH xx)

ao41:
	and [ai.varflags],not VAR_SIZ_NEED	;added for v1.09. Ok?
	mov ax,word ptr [di].OPRND.num+0
	mov cl,7
	sar al,cl
	jmp ao43		;common code

;--- OP_IMM8 - Immediate byte

ao42:
	mov ax,word ptr [di].OPRND.num+0
	mov al,0
ao43:
	cmp al,ah
	jne ao50		;if too big
	cmp ax,word ptr [di].OPRND.num+2
	jne ao50		;if too big
	mov al,SIZ_BYTE
	call aasizchk	;check that size == 0 or 1
	mov ah,byte ptr [di].OPRND.num+0
	mov word ptr [di].OPRND.numadd,ax	;store length (0/1) + the byte
	mov [ai.xxaddr],di
ao43r:
	ret

;--- OP_SHOSIZ - force the user to declare the size of the next operand

ao44:
	test [ai.varflags],VAR_SIZ_NEED
	jz ao45			;if no testing needs to be done
	test [ai.varflags],VAR_SIZ_GIVN
	jz ao50			;if size was given ( ==> reject)
ao45:
	and [ai.varflags],not VAR_SIZ_GIVN	;clear the flag
	cmp byte ptr [si],OP_IMM8
	je ao45a		;if OP_IMM8 is next, then don't set VAR_SIZ_NEED
	or [ai.varflags],VAR_SIZ_NEED
ao45a:
	mov byte ptr [ai.opsize],0
	pop ax			;discard return address
	jmp ac06		;next operand

;--- OP_1

ao46:
	cmp word ptr [di+7],101h	;check both size and value
	jmp ao49		;test it later

;--- OP_3

ao47:
	cmp word ptr [di+7],301h	;check both size and value
	jmp ao49		;test it later

;--- OP_DX, OP_CL, OP_ST, OP_ES, ..., OP_GS
;--- bx contains index for bittab

ao48:
	mov al,[asm_regnum+bx-(ASM_OPOFF + OP_DX - OP_M64)/2]
	cbw
	cmp ax,word ptr [di].OPRND.reg1

ao49:
	je ao51

;--- Reject this operand list.

ao50:
	pop ax			;discard return address
	jmp ac01		;go back to try the next alternative

ao51:
	ret

;--- AASIZCHK - Check that the size given is 0 or AL.

aasizchk:
	cmp [di].OPRND.sizearg,SIZ_NONE
	je ao51
	cmp [di].OPRND.sizearg,al
	je ao51
	pop ax		;discard return address
	jmp ao50

aa endp

;--- Do reg/mem processing.
;--- in: DI->OPRND
;--- Uses AX

ao90 proc
	test [di].OPRND.flags, ARG_JUSTREG
	jnz ao92		;if just register
	test [di].OPRND.flags, ARG_MODRM
	jz @F			;if no precomputed MOD R/M byte
	mov ax,word ptr [di].OPRND.reg1	;get the precomputed bytes
	jmp ao93		;done
@@:
	mov al,6		;convert plain displacement to MOD R/M
	test [asm_mn_flags],AMF_A32
	jz ao93			;if 16 bit addressing
	dec ax
	jmp ao93		;done

ao92:
	mov al,[di].OPRND.reg1	;convert register to MOD R/M
if 1
	cmp al,REG_MM
	jnz @F
	mov al,[di].OPRND.reg2
@@:
endif
	and al,7		;get low 3 bits
	or al,0c0h

ao93:
	or word ptr [ai.regmem],ax	;store the MOD R/M and SIB
	or [ai.varflags],VAR_MODRM	;flag its presence
	mov [ai.rmaddr],di			;save a pointer
	ret						;done
ao90 endp

;   AAIFNUM - Determine if there's a number next.
;   Entry   AL First character of number
;           SI Address of next character of number
;   Exit    CY Clear if there's a number, set otherwise.
;   Uses    None.

aaifnum proc
	cmp al,'-'
	je aai2			;if minus sign (carry is clear)
	push ax
	sub al,'0'
	cmp al,10
	pop ax
	jb aai1			;if a digit
	push ax
	and al,TOUPPER
	sub al,'A'
	cmp al,6
	pop ax
aai1:
	cmc				;carry clear <==> it's a number
aai2:
	ret
aaifnum endp

;   AAGETI - Get a number from the input line.
;   Entry   AL First character of number
;           SI Address of next character of number
;   Exit    DX:BX Resulting number
;           CL 1 if it's a byte ptr, 2 if a word, 4 if a dword
;           AL Next character not in number
;           SI Address of next character after that
;   Uses    AH, CH

aageti proc
	cmp al,'-'
	je aag1			;if negative
	call aag4		;get the bare number
	mov cx,1		;set up cx
	or dx,dx
	jnz aag2		;if dword
	or bh,bh
	jnz aag3		;if word
	ret				;it's a byte

aag1:
	lodsb
	call aag4		;get the bare number
	mov cx,bx
	or cx,dx
	mov cx,1
	jz aag1a		;if -0
	not dx		;negate the answer
	neg bx
	cmc
	adc dx,0
	test dh,80h
	jz aag7			;if error
	cmp dx,-1
	jne aag2		;if dword
	test bh,80h
	jz aag2			;if dword
	cmp bh,-1
	jne aag3		;if word
	test bl,80h
	jz aag3			;if word
aag1a:
	ret				;it's a byte

aag2:
	inc cx		;return:  it's a dword
	inc cx
aag3:
	inc cx		;return:  it's a word
	ret

aag4:
	xor bx,bx		;get the basic integer
	xor dx,dx
	call getnyb
	jc aag7			;if not a hex digit
aag5:
	or bl,al		;add it to the number
	lodsb
	call getnyb
	jc aag1a		;if done
	test dh,0f0h
	jnz aag7		;if overflow
	mov cx,4
aag6:
	shl bx,1		;shift it by 4
	rcl dx,1
	loop aag6
	jmp aag5

aag7:
	jmp cmd_error	;error

aageti endp

;	AACONVINDEX - Convert results from AAGETI and store index value
;	Entry   DX:BX,CL As in exit from AAGETI
;	        DI Points to information record for this arg
;	Exit    SS bits stored in [di].OPRND.index
;	Uses    DL

aaconvindex proc
	cmp cl,1
	jne aacv1		;if the number is too large
	cmp bl,1
	je aacv2		;if 1
	inc dx
	cmp bl,2
	je aacv2		;if 2
	inc dx
	cmp bl,4
	je aacv2		;if 4
	inc dx
	cmp bl,8
	je aacv2		;if 8
aacv1:
	jmp cmd_error	;error

aacv2:
	mov [di].OPRND.index,dl	;save the value
	ret
aaconvindex endp

;   AAGETREG - Get register for the assembler.
;   Entry   DI Start of register table
;           CX Length of register table ( or 0 )
;           SI Address of first character in register name
;   Exit    NC if a register was found
;           SI Updated if a register was found
;           BX Register number, defined as in the table below.
;   Uses    AX, CX, DI

;   Exit value of BX:
;       DI = rgnam816, CX = 27  DI = rgnam16, CX = 8
;       ----------------------  --------------------
;       0  ..  7:  AL .. BH     0  ..  7:  AX .. DI
;       8  .. 15:  AX .. DI     16 .. 23:  EAX..EDI
;       16 .. 23:  EAX..EDI
;       24 .. 29:  ES .. GS
;       30 .. 34:  ST .. TR

aagetreg proc
	mov ax,[si]
	and ax,TOUPPER_W	;convert to upper case
	cmp al,'E'			;check for EAX, etc.
	jne aagr1			;if not
	push ax
	mov al,ah
	mov ah,[si+2]
	and ah,TOUPPER
	push di
	mov di,offset rgnam16
	push cx
	mov cx,N_REGS16
	repne scasw
	mov bx,cx
	pop cx
	pop di
	pop ax
	jne aagr1		;if no match
	inc si
	not bx
	add bl,8+16		;adjust BX
	jmp aagr2		;finish up

aagr1:
	mov bx,cx		;(if cx = 0, this is always reached with
	repne scasw		; ZF clear)
	jne aagr3		;if no match
	sub bx,cx
	dec bx
	cmp bl,16
	jb aagr2		;if AL .. BH or AX .. DI
	add bl,8
aagr2:
	inc si			;skip the register name
	inc si
	clc
	ret
aagr3:
	stc				;not found
	ret
aagetreg endp


	.8086

errorj4:
	jmp cmd_error

;--- F command - fill memory

ff proc
	xor cx,cx		;get address range (no default length)
	mov bx,[regs.rDS]
	call getrange	;get address range into bx:(e)dx/(e)cx
	sub cx,dx
	inc cx		;cx = number of bytes
	push cx		;save it
	push dx		;save start address
ff_01:
	call skipcomm0
	call getstr		;get string of bytes
	mov cx,di
	sub cx,offset line_out
	pop di
	mov es,bx
	cmp cx,1
	je ff3		;a common optimization
	pop ax		;get size
	xor dx,dx	;now size in DX:AX
	cmp ax,1
	adc dx,0	;convert 0000:0000 to 0001:0000
	div cx		;compute number of whole repetitions
	or ax,ax
	jz ff2		;if less than one whole rep
ff1:
	mov si,offset line_out
	push cx
	rep movsb
	pop cx
	dec ax
	jnz ff1		;if more to go
ff2:
	mov cx,dx
	jcxz ff_exit;if no partial copies
	mov si,offset line_out
	rep movsb
	jmp ff_exit	;done (almost)
ff3:
	pop cx
	mov al,byte ptr [line_out]
	stosb		;cx=0 -> 64 kB
	dec cx
	rep stosb
ff_exit:
	push ss		;restore es
	pop es
	ret
ff endp

;--- breakpoints are stored in line_out, with this format
;--- WORD cnt
;--- array:
;--- DWORD/WORD offset of bp
;--- WORD segment of bp
;--- BYTE old value

;--- H command - hex addition and subtraction.

hh proc
	call getdword	;get dword in BX:DX
	push bx
	push dx
	call skipcomm0
	call getdword
	call chkeol		;expect end of line here
	pop cx
	pop ax			;first value in AX:CX, second in BX:DX
hh32:
	mov si,ax
	mov bp,cx			;first value in SI:BP now
	mov ax,cx
	add ax,dx
	push ax
	mov ax,si
	adc ax,bx
	jz @F
	call hexword
@@:
	pop ax
	call hexword
	mov ax,'  '
	stosw
	mov ax,bp
	sub ax,dx
	push ax
	mov ax,si
	sbb ax,bx
	jz @F
	or si,bx
	jz @F
	call hexword
@@:
	pop ax
	call hexword
	call putsline
	ret
hh endp

errorj3:
	jmp cmd_error

;--- read [EIP+x] value 
;--- in: CX=x 
;--- [regs.rCS]=CS
;--- [regh_(e)ip]=EIP
;--- out: AL=[CS:(E)IP]
;--- [E]BX=[E]IP+x 
;--- called by T and G

getcseipbyte proc
	push es
	mov es,[regs.rCS]
	mov bx,[regs.rIP]
	add bx,cx
	mov al,es:[bx]
	pop es
	ret
getcseipbyte endp

;--- set [EIP+x] value 
;--- in: CX=x 
;--- AL=byte to write
;--- [regs.rCS]=CS
;--- [regs.rIP]=EIP
;--- modifies [E]BX

setcseipbyte proc
	push es
	mov bx,[regs.rCS]
	mov es,bx
	mov bx, [regs.rIP]
	add bx,cx
	mov es:[bx],al
	pop es
	ret
setcseipbyte endp

;--- write a byte (AL) at BX:E/DX
;--- OUT: AH=old value at that location
;--- C if byte couldn't be written

writemem proc
	push ds
	mov ds,bx
	push bx
	mov bx,dx
	mov ah,[bx]
	mov [bx],al
	cmp al,[bx]
	pop bx
done:
	pop ds
	jnz @F
	ret
@@:
	stc
	ret
writemem endp

;--- read byte at BX:EDX into AL

readmem proc
	push ds
	mov ds,bx
	mov bx,dx
	mov al,[bx]
	pop ds
	ret
readmem endp

;--- check if an unexpected int 3 has occured
;--- CS:(E)IP in regs.rCS:regs.rIP
;--- in: DX=0, out: DX=msg
;--- called by P, T

IsUnexpInt3 proc
	mov cx,-1
	call getcseipbyte
	cmp al,0CCh
	jz isunexp_exit
	mov dx,offset int3msg
	mov [regs.rIP],bx
isunexp_exit:
	ret
IsUnexpInt3 endp

;--- Q command - quit.

qq proc

;--- Restore interrupt vectors.

	mov di,offset intsave
	mov si,offset inttab
	mov cx,NUMINTSX
nextint:
	lodsb
	mov bl,al
	add si,2	;skip rest of INTITEM
	xchg si,di
	lodsw
	mov dx,ax
	lodsw
	xchg si,di
	cmp bl,22h
	jz norestore
	and ax,ax
	jz norestore
	push ds
	mov ds,ax
	mov al,bl
	mov ah,25h
	int 21h
	pop ds
norestore:
	loop nextint

;--- Restore termination address.

	mov si,offset psp22	;restore termination address
	mov di,TPIV
	movsw
	movsw
	mov di,PARENT		;restore PSP of parent
	movsw

;--- Really done.

;--- int 20h sets error code to 0.
;--- might be better to use int 21h, ax=4Cxxh
;--- and load the error code returned by the debuggee
;--- into AL.

	int 20h			;won't work if format == MZ!
	jmp cmdloop		;returned? then something is terribly wrong.
qq endp


rr_err:
	dec si		;back up one before flagging an error
errorj9:
	jmp cmd_error

;--- clear TF in the copy of flags register onto the stack

clear_tf_onstack proc
	push es
	mov es,[regs.rSS]
	mov bx,[regs.rSP]
	and byte ptr es:[bx+1],not 1
ctos_1:
	pop es
	ret
clear_tf_onstack endp

;--- U command - disassemble.

uu proc
	mov [lastcmd],offset lastuu	
	cmp al,CR
	jne uu1		;if an address was given
lastuu:
	mov cx,word ptr [u_addr]
	add cx,1fh
	jnc uu2		;if no overflow
	mov cx,-1
	jmp uu2

uu1:
	mov cx,20h		;default length
	mov bx,[regs.rCS]
	call getrangeX	;get address range into bx:(e)dx
	call chkeol		;expect end of line here
	mov [u_addr+0],dx
	mov [u_addr+4],bx

;--- At this point, cx holds the last address, and dx the address.

uu2:
	inc cx
uu3:
	push cx
	push dx
	call disasm1	;do it
	pop bx
	pop cx
	mov ax,[u_addr]
	mov dx,ax
	sub ax,cx		;current position - end
	sub bx,cx		;previous position - end
	cmp ax,bx
	jnb uu3		;if we haven't reached the goal
	ret
uu endp


;--- Error opening file.  This is also called by the load command.

int21ah9::
	;mov ah,9
	;call doscall
	push	si
	push	bx
	mov si, dx
@@:
	lodsb
	cmp al, 0
	je @F
	cmp al, '$'
	je @F
	mov ah,0eh
	mov bx,7
	int 10h
	jmp @B
@@:
	pop	bx
	pop	si
	ret

;--- syntax error handler.
;--- in: SI->current char in line_in

cmd_error proc
	mov cx,si
	sub cx,offset line_in+4
	add cx,[promptlen]
	mov di,offset line_out
	mov dx,di
	cmp cx,127
	ja @F			;if we're really messed up
	inc cx			;number of spaces to skip
	mov al,' '
	rep stosb
@@:
	mov si,offset errcarat
	mov cl,sizeof errcarat
	rep movsb
	call putsline	;print string
	jmp [errret]
cmd_error endp

stdoutal:
	push bx
	push cx
	push dx
	push ax
	mov cx,1
	mov dx,sp
	call stdout
	pop ax
	pop dx
	pop cx
	pop bx
	ret

fullbsout:
	mov al,8
	call stdoutal
	mov al,20h
	call stdoutal
	mov al,8
	jmp stdoutal

;   GETLINE - Print a prompt (address in DX, length in CX) and read a line
;   of input.
;   GETLINE0 - Same as above, but use the output line (so far), plus two
;   spaces and a colon, as a prompt.
;   GETLINE00 - Same as above, but use the output line (so far) as a prompt.
;   Entry   CX  Length of prompt (getline only)
;       DX  Address of prompt string (getline only)
;
;       DI  Address + 1 of last character in prompt (getline0 and
;           getline00 only)
;
;   Exit    AL  First nonwhite character in input line
;       SI  Address of the next character after that
;   Uses    AH,BX,CX,DX,DI

getline0:
	mov ax,'  '		;add two spaces and a colon
	stosw
	mov al,':'
	stosb
getline00:
	mov dx,offset line_out
	mov cx,di
	sub cx,dx

getline proc
	mov [promptlen],cx	;save length of prompt
	call stdout
	mov dx,offset line_in
	jmp rawinput
gl6:
	mov al,10
	call stdoutal
	mov si,offset line_in + 2
	call skipwhite
	ret

rawinput:
	push di
	push ds
	pop es
	inc dx
	inc dx
	mov di,dx
rawnext:
	mov ah,00h
	int 16h
	cmp al,0
	jz rawnext
	cmp al,0E0h
	jz rawnext
	cmp al,08h
	jz del_key
	cmp al,7Fh
	jz del_key
	stosb
	call stdoutal
	cmp al,0Dh
	jnz rawnext
	dec di
	sub di,dx
	mov ax,di
	mov di,dx
	mov byte ptr [di-1],al
	dec dx
	dec dx
	pop di
	jmp gl6
del_key:
	cmp di,dx
	jz rawnext
	dec di
	call fullbsout
	jmp rawnext

getline endp

errorj7:
	jmp cmd_error

;--- get a valid offset for segment in BX

getofsforbx proc
	call getword
	ret
getofsforbx endp

errorj10:
	jmp cmd_error

;--- a range is entered with the L/ength argument
;--- get a valid length for segment in BX
;--- L=0 means 64 kB (at least in 16bit mode)

getlenforbx proc
	push dx
	call getword
	mov cx,dx
	pop dx
;   stc
;	jcxz glfbx_2	;0 means 64k
	dec cx
	add cx,dx		;C if it wraps around
glfbx_2:
	ret
getlenforbx endp

;   GETRANGE - Get address range from input line.
;    a range consists of either start and end address
;    or a start address, a 'L' and a length.
;   Entry   AL  First character of range
;       SI  Address of next character
;       BX  Default segment to use
;       CX  Default length to use (or 0 if not allowed)
;   Exit    AL  First character beyond range
;       SI  Address of the character after that
;       BX:(E)DX    First address in range
;       BX:(E)CX    Last address in range
;   Uses    AH

getrangeX:
	push cx
	call getaddrX
	jmp getrange_1

getrange proc
	push cx			;save the default length
	call getaddr	;get address into bx:(e)dx (sets bAddr32)
getrange_1::		;<-- entry getrangeX
	push si
	call skipcomm0
	cmp al,' '
	ja gr2
	pop si			;restore si and cx
	pop cx
	jcxz errorj10	;if a range is mandatory
	dec cx
	add cx,dx
	jnc gr1			;if no wraparound
	mov cx,0ffffh	;go to end of segment
gr1:
	dec si			;restore al
	lodsb
	ret

gr2:
	or al,TOLOWER
	cmp al,'l'
	je gr3			;if a range is given
;	call skipwh0	;get next nonblank
	push dx
	call getword
	mov cx,dx
	pop dx
	cmp dx,cx
	ja errorj2			;if empty range
	jmp gr4

gr3:
	call skipcomma		;discard the 'l'
	call getlenforbx
	jc errorj2
gr4:
	add sp,4			;discard saved cx, si
	ret
getrange endp

errorj2:
	jmp cmd_error

;   GETADDR - Get address from input line.
;   Entry   AL  First character of address
;       SI  Address of next character
;       BX  Default segment to use
;   Exit    AL  First character beyond address
;       SI  Address of the character after that
;       BX:(E)DX    Address found
;   Uses    AH,CX

;--- entry getaddrX differs from getaddr in that BX isn't made
;--- writeable in pmode

getaddr proc
getaddrX::
ga1_1:
	call getofsforbx
ga1_2:
	push si
	call skipwh0
	cmp al,':'
	je ga2		;if this is a segment descriptor
	pop si
	dec si
	lodsb
	ret

ga2:
	pop ax		;throw away saved si
	mov bx,dx	;mov segment into BX
ga3:
	call skipwhite	;skip to next word
	call getofsforbx
	ret
getaddr endp

;   GETSTR - Get string of bytes.  Put the answer in line_out.
;   Entry   AL first character
;           SI address of next character
;   Exit    [line_out] first byte of string
;           DI address of last+1 byte of string
;   Uses    AX,CL,DL,SI

getstr proc
	mov di,offset line_out
	cmp al,CR
	je errorj2		;we don't allow empty byte strings
gs1:
	cmp al,"'"
	je gs2		;if string
	cmp al,'"'
	je gs2		;ditto
	call getbyte;byte in DL
	mov [di],dl	;store the byte
	inc di
	jmp gs6

gs2:
	mov ah,al	;save quote character
gs3:
	lodsb
	cmp al,ah
	je gs5		;if possible end of string
	cmp al,CR
	je errorj2	;if end of line
gs4:
	stosb		;save character and continue
	jmp gs3

gs5:
	lodsb
	cmp al,ah
	je gs4		;if doubled quote character
gs6:
	call skipcomm0	;go back for more
	cmp al,CR
	jne gs1		;if not done yet
	ret
getstr endp

;--- in: AL=first char
;---     SI->2. char
;--- out: value in BX:DX

issymbol proc
	push ax
	push di
	push cx
	mov di,offset regnames
	mov cx,NUMREGNAMES
	mov ah,[si]		;get second char of name 
	and ax,TOUPPER_W
	cmp byte ptr [si+1],'A'
	jnc maybenotasymbol
	repnz scasw
	jnz notasymbol
	xor bx,bx
	mov di, [di+NUMREGNAMES*2 - 2]
getsymlow:
	mov dx,[di]
	inc si		;skip over second char
	clc
	pop cx
	pop di
	pop ax
	ret
maybenotasymbol:
	cmp al,'E'		;386 standard register names start with E
	jnz notasymbol
	mov al,[si+1]
	xchg al,ah
	and ax,TOUPPER_W
	cmp ax,"PI"
	jnz @F
	mov di,offset regs.rIP
	jmp iseip
@@:
	mov cx,8	;scan for the 8 standard register names only
	repnz scasw
	jnz notasymbol
	mov di,[di+NUMREGNAMES*2 - 2]
iseip:
	mov bx,[di+2]	;get HiWord of DWORD register
	inc si
	jmp getsymlow
notasymbol:
	pop cx
	pop di
	pop ax
	stc
	ret
issymbol endp

;   GETDWORD - Get (hex) dword from input line.
;       Entry   AL  first character
;           SI  address of next character
;       Exit    BX:DX   dword
;           AL  first character not in the word
;           SI  address of the next character after that
;       Uses    AH,CL

getdword proc
	call issymbol
	jc @F
	lodsb
	ret
@@:
	call getnyb
	jc errorj6		;if error
	cbw
	xchg ax,dx
	xor bx,bx		;clear high order word
gd1:
	lodsb
	call getnyb
	jc gd3
	test bh,0f0h
	jnz errorj6		;if too big
	mov cx,4
gd2:
	shl dx,1		;double shift left
	rcl bx,1
	loop gd2
	or dl,al
	jmp gd1
gd3:
	ret
getdword endp

errorj6:
	jmp cmd_error

;   GETWORD - Get (hex) word from input line.
;       Entry   AL  first character
;           SI  address of next character
;       Exit    DX  word
;           AL  first character not in the word
;           SI  address of the next character after that
;       Uses    AH,CL

getword proc
	push bx
	call getdword
	and bx,bx		;hiword clear?
	pop bx
	jnz errorj6		;if error
	ret
getword endp

;   GETBYTE - Get (hex) byte from input line into DL.
;       Entry   AL  first character
;           SI  address of next character
;       Exit    DL  byte
;           AL  first character not in the word
;           SI  address of the next character after that
;       Uses    AH,CL

getbyte:
	call getword
	and dh,dh
	jnz errorj6		;if error
	ret

;--- GETNYB - Convert the hex character in AL into a nybble.  Return
;--- carry set in case of error.

getnyb:
	push ax
	sub al,'0'
	cmp al,9
	jbe gn1		;if normal digit
	pop ax
	push ax
	or al,TOLOWER
	sub al,'a'
	cmp al,'f'-'a'
	ja gn2		;if not a-f or A-F
	add al,10
gn1:
	inc sp		;normal return (first pop old AX)
	inc sp
	clc
	ret
gn2:
	pop ax		;error return
	stc
	ret

;--- CHKEOL1 - Check for end of line.

chkeol:
	call skipwh0
	cmp al,CR
	jne errorj8		;if not found
	ret

errorj8:
	jmp cmd_error

;   SKIPCOMMA - Skip white space, then an optional comma, and more white
;       space.
;   SKIPCOMM0 - Same as above, but we already have the character in AL.

skipcomma:
	lodsb
skipcomm0:
	call skipwh0
	cmp al,','
	jne sc2		;if no comma
	push si
	call skipwhite
	cmp al,CR
	jne sc1		;if not end of line
	pop si
	mov al,','
	ret
sc1:
	add sp,2	;pop si into nowhere
sc2:
	ret

;--- SKIPALPHA - Skip alphabetic character, and then white space.

skipalpha:
	lodsb
	and al,TOUPPER
	sub al,'A'
	cmp al,'Z'-'A'
	jbe skipalpha
	dec si
;	jmp skipwhite	;(control falls through)

;--- SKIPWHITE - Skip spaces and tabs.
;--- SKIPWH0 - Same as above, but we already have the character in AL.

skipwhite:
	lodsb
skipwh0:
	cmp al,' '
	je skipwhite
	cmp al,TAB
	je skipwhite
	ret

;--- IFSEP Compare AL with separators ' ', '\t', ',', ';', '='.

ifsep:
	cmp al,' '
	je @F
	cmp al,TAB
	je @F
	cmp al,','
	je @F
	cmp al,';'
	je @F
	cmp al,'='
@@:
	ret

;   Here is the start of the disassembly part of the program.

_DATA segment

dis_n	dw 0		;number of bytes in instruction so far
		dw 0		;must follow dis_n (will always remain 0)
;--- preflags and preused must be consecutive
preflags db 0		;flags for prefixes found so far
preused	db 0		;flags for prefixes used so far

PRESEG	equ 1		;segment prefix
PREREP	equ 2		;rep prefixes
PREREPZ	equ 4		;f3, not f2
PRELOCK	equ 8		;lock prefix
PRE32D	equ 10h		;flag for 32-bit data
PRE32A	equ 20h		;flag for 32-bit addressing
PREWAIT	equ 40h		;prefix wait (not really a prefix)
GOTREGM	equ 80h		;set if we have the reg/mem part

instru	db 0		;the main instruction byte
rmsize	db 0		;<0 or 0 or >0 means mod r/m is 8 or 16 or 32
segmnt	db 0		;segment determined by prefix (or otherwise)
idxins	dw 0		;index of the instruction (unsqueezed)
addrr	dw 0		;address in mod r/m byte (16bit only)
savesp2	dw 0		;save the stack pointer here (used in disasm)

disflags db 0		;flags for the disassembler

;--- equates for disflags:

DIS_F_REPT		equ 1	;repeat after pop ss, etc.
DIS_F_SHOW		equ 2	;show memory contents
DIS_I_SHOW		equ 4	;there are memory contents to show
DIS_I_UNUSED	equ 8	;(internal) print " (unused)"
DIS_I_SHOWSIZ	equ 10h	;(internal) always show the operand size
DIS_I_KNOWSIZ	equ 20h	;(internal) we know the operand size of instr.

disflags2 db 0		;another copy of DIS_I_KNOWSIZ

sizeloc dw 0		;address of size words in output line

_DATA ends

CONST segment

;--- table of obsolete-instruction values.
;--- instructions are FENI, FDISI, FSETPM, MOV to/from TRx
obsinst	dw SFPGROUP3, SFPGROUP3+1, SFPGROUP3+4
		dw SPARSE_BASE+24h, SPARSE_BASE+26h

;--- Table for 16-bit mod r/m addressing.  8 = BX, 4 = BP, 2 = SI, 1 = DI.

rmtab	db 8+2, 8+1, 4+2, 4+1, 2, 1, 4, 8

DefGPR macro regist
REG_&regist& equ ($ - rgnam816)/2
	db "&regist&"
	endm

REG_NO_GPR	equ 24	;16-23 are registers EAX-EDI

DefSR macro regist
REG_&regist& equ REG_NO_GPR + ($ - segrgnam)/2
	db "&regist&"
	endm

;   Tables of register names.
;   rgnam816/rgnam16/segrgnam must be consecutive.

rgnam816 label byte
	DefGPR AL
	DefGPR CL
	DefGPR DL
	DefGPR BL
	DefGPR AH
	DefGPR CH
	DefGPR DH
	DefGPR BH
rgnam16 label byte
	DefGPR AX
	DefGPR CX
	DefGPR DX
	DefGPR BX
	DefGPR SP
	DefGPR BP
	DefGPR SI
	DefGPR DI
N_REGS16 equ ( $ - rgnam16 ) / 2
segrgnam label byte
	DefSR ES
	DefSR CS
	DefSR SS
	DefSR DS
	DefSR FS
	DefSR GS
N_SEGREGS equ ( $ - segrgnam ) / 2
	DefSR ST
	DefSR MM
	DefSR CR
	DefSR DR
	DefSR TR
N_ALLREGS equ ( $ - rgnam816 ) / 2

segrgaddr	dw regs.rES,regs.rCS,regs.rSS,regs.rDS

;--- Tables for handling of named prefixes.

prefixlist	db 26h,2eh,36h,3eh,64h,65h	;segment prefixes (in order)
			db 9bh,0f0h,0f2h,0f3h		;WAIT,LOCK,REPNE,REPE
N_PREFIX equ $ - prefixlist
prefixmnem	dw MN_WAIT,MN_LOCK,MN_REPNE,MN_REPE

CONST ends

disasm1:				;<--- standard entry
	mov [disflags],0

disasm proc				;<--- entry with disflags set
	mov [savesp2],sp
	xor ax,ax
	mov [dis_n],ax
	mov word ptr [preflags],ax	;clear preflags and preused
	mov [segmnt],3			;initially use DS segment
	mov [rmsize],80h		;don't display any memory
	mov word ptr [ai.dismach],0;no special machine needed, so far
	call disgetbyte			;get a byte of the instruction
	cmp al,9bh				;wait instruction (must be the first prefix)
	jne da2					;if not

;   The wait instruction is actually a separate instruction as far as
;   the x86 is concerned, but we treat it as a prefix since there are
;   some mnemonics that incorporate it.  But it has to be treated specially
;   since you can't do, e.g., seg cs wait ... but must do wait seg cs ...
;   instead.  We'll catch it later if the wait instruction is not going to
;   be part of a shared mnemonic.

	or [preflags],PREWAIT

;   If we've found a prefix, we return here for the actual instruction
;   (or another prefix).

da1:
	call disgetbyte
da2:
	mov [instru],al	;save away the instruction
	mov ah,0

;--- Now we have the sequence number of the instruction in AX.  Look it up.

da3:
	mov bx,ax
	mov [idxins],ax	;save the compressed index
	cmp ax,SPARSE_BASE
	jb @F			;if it's not from the squeezed part of the table
	mov bl,[sqztab+bx-SPARSE_BASE]
	mov bh,0
	add bx,SPARSE_BASE	;bx = compressed index
@@:
	mov cl,[optypes+bx]	;cx = opcode type
	mov ch,0
	shl bx,1
	mov bx,[opinfo+bx]	;bx = other info (mnemonic if a true instruction)
	mov si,cx
	mov ax,bx
	mov cl,12
	shr ax,cl
	cmp al,[ai.dismach]
	jb @F				;if a higher machine is already required
	mov [ai.dismach],al	;set machine type
@@:
	and bh,0fh			;=and bx,0fffh - remove the machine field
	cmp si,OPTYPES_BASE
	jae da13			;if this is an actual instruction
	call [dis_jmp2+si]	;otherwise, do more specific processing
	jmp da3				;back for more

CONST segment

	align 2

;   Jump table for OP_IMM, OP_RM, OP_M, OP_R_MOD, OP_MOFFS, OP_R, OP_R_ADD,
;   and OP_AX.
;   See orders of asm_jmp1 and bittab.

dis_jmp1 label word
	dw dop_imm, dop_rm, dop_m, dop_r_mod
	dw dop_moffs, dop_r, dop_r_add, dop_ax

;   jump table for displaying operands
;   See orders of asm_jmp1 and bittab.

dis_optab label word
	dw dop_m64,  dop_mfloat, dop_mdouble, dop_m80	;00-03
	dw dop_mxx,  dop_farmem, dop_farimm,  dop_rel8	;04-07
	dw dop_rel1632, dop49,   dop_sti,     dop_cr	;08-11
	dw dop_dr,   dop_tr,     dop_segreg,  dop_imms8	;12-15
	dw dop_imm8, dop_mmx,    dop_shosiz				;16-18
;--- string items OP_1 .. OP_SS
	db '1',0	;19
	db '3',0	;20
	db 'DX'		;21
	db 'CL'		;22
	db 'ST'		;23
	db 'CS','DS','ES','FS','GS','SS'	;24-29

;--- Jump table for a certain place.
;--- the size of this table matches OPTYPES_BASE

dis_jmp2 label word
	dw disbad		;illegal instruction
	dw da_twobyte	;two byte instruction (0F xx)
	dw da_insgrp	;instruction group
	dw da_fpuins	;coprocessor instruction
	dw da_fpugrp	;coprocessor instruction group
	dw da_insprf	;instruction prefix (including 66h/67h)
OPTYPES_BASE equ $ - dis_jmp2

CONST ends

;--- Two-byte instruction 0F xx: index 1E0-2DF.

da_twobyte:
	call disgetbyte
	mov [instru],al
	mov ah,0
	add ax,SPARSE_BASE
	ret

;--- Instruction group.
;--- BX contains "instruction base": 100h, 110h, ...

da_insgrp:
	call getregmem_r	;get the middle 3 bits of the R/M byte
	cbw
	add ax,bx			;offset
	ret

;--- Coprocessor instruction.
;--- BX contains "instruction base": 148h, 158h, ...

da_fpuins:
	or [disflags], DIS_I_SHOWSIZ
	or [ai.dmflags], DM_COPR
	call getregmem
	cmp al,0c0h
	jb da_insgrp	;range 00-bfh is same as an instruction group
	mov cl,3
	shr al,cl		;C0-FF --> 18-1F
	sub al,18h-8	;18-1F --> 08-0F
	cbw
	add ax,bx		;offset
	ret

;--- Coprocessor instruction group.
;--- BX contains "instruction base": 1C8h, 1D0h, 1D8h

da_fpugrp:
	mov al,[ai.regmem]
	and al,7
	cbw
	add ax,bx
	ret

;--- Instruction prefix.  At this point, bl = prefix bits; bh = segment

da_insprf:
if 1
	mov al,bl
	and bl,not (PRE32D or PRE32A)	;these flags are XORed!
endif
	test bl,[preflags]
	jnz da12		;if there are duplicates
	or [preflags],bl
if 1
	mov bl,al
	and al,PRE32D or PRE32A
	xor [preflags],al
endif
	test bl,PRESEG
	jz @F			;if not a segment
	mov [segmnt],bh	;save the segment
@@:
	pop ax			;discard return address
	jmp da1

da12:
	jmp disbad		;we don't allow duplicate prefixes

;   OK.  Here we go.  This is an actual instruction.
;   BX=offset of mnemonic in mnlist
;   SI=offset of operand list in oplists
;   First print the op mnemonic.

da13:
	push si
	lea si,[mnlist+bx]	;offset of mnemonic
	cmp si,offset mnlist+MN_BSWAP
	jne @F				;if not BSWAP
	call dischk32d
	jz da12				;if no operand-size prefix
@@:
	call showop			;print out the op code (at line_out+28)
	mov [sizeloc],0		;clear out this flag
	pop si				;recover list of operands
	add si,offset oplists - OPTYPES_BASE
	cmp byte ptr [si],0
	je da21				;if we're done

;   Loop over operands.  si -> operand type.
;   Fortunately the operands appear in the instruction in the same
;   order as they appear in the disassembly output.

da14:
	mov [disflags2],0		;clear out size-related flags
	lodsb					;get the operand type
	cmp al,OP_SIZE
	jb da18					;if it's not size dependent
	mov [disflags2],DIS_I_KNOWSIZ	;indicate variable size
	cmp al,OP_8
	jae da16				;if the size is fixed (8,16,32,64)
	cmp al,OP_1632
	jae da15				;if word or dword
	mov ah,-1
	test [instru],1
	jz da17					;if byte
da15:
	or [preused],PRE32D		;mark this flag as used
	mov ah,[preflags]
	and ah,PRE32D			;this will be 10h for dword, 00h for word
	jmp da17				;done

da16:
	mov ah,al		;OP_8, OP_16, OP_32 or OP_64 (we know which)
	and ah,0f0h		;this converts ah to <0 for byte, =0 for word,
	sub ah,OP_16	;and >0 for dword (byte=F0,word=0,dword=10,qword=20)

;--- Now we know the size (in ah); branch off to do the operand itself.

da17:
	mov bl,al
	and bx,0eh			;8 entries (IMM, RM, M, R_MOD, M_OFFS, R, R_ADD, AX)
	call [dis_jmp1+bx]	;print out the operand
	jmp da20			;done with operand

;--- Sizeless operands.

da18:
	cbw
	xchg ax,bx
	cmp bl,OP_STR
	jb @F				;if it's not a string
	mov ax,[dis_optab+bx-2]
	stosw
	cmp ah,0
	jnz da20			;if it's two characters
	dec di
	jmp da20			;done with operand
@@:
	call [dis_optab+bx-2]	;otherwise, do something else

;--- operand done, check if there's another one

da20:
	cmp byte ptr [si],0
	jz da21				;if we're done
	mov al,','
	stosb
	jmp da14			;another operand

;--- all operands done.
;--- now check and loop for unused prefixes:
;--- OPSIZE (66h), ADDRSIZE (67h), WAIT, segment, REP[N][Z], LOCK

da21:
	mov al,[preused]
	not al
	and al,[preflags]
	jnz @F			;if some flags remain unused
	jmp da28		;if all flags were used
@@:
	mov cx,N_WTAB
	mov bx,offset wtab1
	mov dx,2*N_WTAB-2
	mov ah,PREWAIT
	test al,ah
	jnz @F			;if there's a WAIT prefix hanging

	mov cx,N_LTABO
	mov bx,offset ltabo1
	mov dx,2*N_LTABO-2
	mov ah,PRE32D
	test al,ah
	jnz @F			;if it's not a 66h prefix that's hanging

	mov cx,N_LTABA
	mov bx,offset ltaba1
	mov dx,2*N_LTABA-2
	mov ah,PRE32A
	test al,ah
	jnz @F			;if it's not a 67h prefix that's hanging
	jmp da24
@@:
	or [preused],ah	;mark this prefix as used
	push di
	mov di,bx
	mov bl,ah
	mov ax,[idxins]
	repne scasw
	jne da23_1		;if not found in the list
	add di,dx		;replace the mnemonic with the 32-bit name
	mov si,[di]
	add si,offset mnlist
	call showop		;copy op mnemonic
da23_0:
	pop di
	jmp da21
da23_1:
disbad2:
	jmp disbad

da24:
	test al,PRESEG
	jz da25			;if not because of a segment prefix
	mov ax,[idxins]
	cmp ah,0
	jnz disbad2		;if index > 256
	push di
	mov cx,P_LEN
	mov di,offset prfxtab
	repne scasb
	pop di
	jne disbad2		;if it's not on the list
	mov cx,3
	call moveover
	push di
	mov di,offset line_out+MNEMONOFS
	call showseg	;show segment register
	mov al,':'
	stosb
	pop di
	or [preused],PRESEG		;mark it as used
	jmp da21

da25:
	test al,PREREP
	jz da26			;if not a REP prefix
	and al,PREREP+PREREPZ
	or [preused],al
	mov ax,[idxins]
	cmp ah,0
	jnz disbad2		;if not in the first 256 bytes
	and al,not 1	;clear bit0 (MOVSW -> MOVSB)
	push di
	mov di,offset replist
	mov cx,N_REPNC	;scan those for REP first
	repne scasb
	mov si,offset mnlist+MN_REP
	je da27			;if one of the REP instructions
	mov cl,N_REPALL - N_REPNC
	repne scasb
	jne disbad2		;if not one of the REPE/REPNE instructions
	mov si,offset mnlist+MN_REPE
	test [preused],PREREPZ
	jnz da27		;if REPE
	mov si,offset mnlist+MN_REPNE
	jmp da27		;it's REPNE

disbad3:
	jmp disbad

da26:
	test al,PRELOCK
	jz disbad3		;if not a lock prefix, either
	push di
	mov ax,[idxins]
	mov di,offset locktab
	mov cx,N_LOCK
	repne scasw
	jne disbad3		;if not in the approved list
	test [preused],PRESEG
	jz disbad3		;if memory was not accessed
	mov si,offset mnlist+MN_LOCK
	or [preused],PRELOCK

;--- Slip in another mnemonic: REP/REPE/REPNE/LOCK.
;--- SI = offset of mnemonic, what should be
;--- DI is on the stack.

da27:
	pop di
	mov cx,8
	push si
	call moveover
	pop si
	push di
	call showop
	pop di
	jmp da21

;--- Done with instruction.  Erase the size indicator, if appropriate.

da28:
	mov cx,[sizeloc]
	cmp cx,0
	jz da28b		;if there was no size given
	mov al,[disflags]
	test al,DIS_I_SHOWSIZ
	jnz da28b		;if we need to show the size
	test al,DIS_I_KNOWSIZ
	jz da28b		;if the size is not known already
	xchg cx,di
	mov si,di		;save old di
	mov al,' '
@@:
	scasb			;skip size name
	jne @B			;if not done yet
					;(The above is the same as repne scasb, but
					;has no effect on cx.)
	add di,4		;skip 'PTR '
	xchg si,di
	sub cx,si
	rep movsb		;move the line

;--- Now we're really done.  Print out the bytes on the left.

da28b:
	push di		;print start of disassembly line
	mov di,offset line_out
	mov ax,[u_addr+4]	;print address
	call hexword
	mov al,':'
	stosb
	mov ax,[u_addr+0]
	call hexword
	mov al,' '
	stosb
	mov bx,[dis_n]
@@:
	mov si,offset line_out+MNEMONOFS - 1
	sub si, di
	shr si, 1
	cmp bx,si
	jle da29		;if it's a short instruction which fits in one line
	sub bx,si
	push bx
	mov bx,si
	push di
	call disshowbytes
	call putsline
	pop cx
	pop bx
	mov di,offset line_out
	sub cx,di
	mov al,' '
	rep stosb
	jmp @B
da29:
	call disshowbytes
da30:
	mov al,' '		;pad to op code
	mov cx,offset line_out+MNEMONOFS
	sub cx,di
	jc @F
	rep stosb
@@:
	pop di
	test [disflags], DIS_I_UNUSED
	jz da32			;if we don't print ' (unused)'
	mov si,offset unused
	cmp byte ptr [di-1],' '
	jne da31		;if there's already a space here
	inc si
da31:
	call copystring	;si->di

;--- Print info. on minimal processor needed.

da32:
	;push di
	;mov di,offset obsinst
	;mov cx,[idxins]
	;call showmach	;show the machine type, if needed
	;pop di
	;jcxz da32f		;if no message
	jmp da32f		;if no message

;--- Print a message on the far right.

	mov ax,offset line_out+79
	sub ax,cx
	push cx
	call tab_to		;tab out to the location
	pop cx
	rep movsb		;copy the string
	jmp da32z		;done

;--- Dump referenced memory location.

da32f:
	mov al,[disflags]
	xor al,DIS_F_SHOW + DIS_I_SHOW
	test al,DIS_F_SHOW + DIS_I_SHOW
	jnz da32z		;if there is no memory location to show
	cmp [segmnt],3
	ja da32z		;if FS or GS
	mov ax,offset line_out+79-10
	cmp [rmsize],0
	jl da32h		;if byte
	jz da32g		;if word
	sub ax,4
da32g:
	dec ax
	dec ax
da32h:
	call tab_to
	call showseg		;show segment register
	mov al,':'
	stosb
	mov ax,[addrr]
	call hexword		;show offset
	mov al,'='
	stosb
	mov al,[segmnt]		;segment number
	cbw
	shl ax,1
	xchg ax,bx			;mov bx,ax
	mov bx,[segrgaddr+bx] ;get address of value
	push es
	mov es,[bx]
	mov bx,[addrr]
	mov al,es:[bx+0]	;avoid a "mov ax,[-1]"
	mov ah,es:[bx+1]
	mov dl,es:[bx+2]	;avoid a "mov dx,[-1]"
	mov dh,es:[bx+3]
	pop es
	cmp [rmsize],0
	jl da32j		;if byte
	jz da32i		;if word
	xchg ax,dx
	call hexword
	xchg ax,dx
da32i:
	call hexword
	jmp da32z		;done
da32j:
	call hexbyte	;display byte

da32z:
	call trimputs	;done with operand list
	mov al,[disflags]
	test al,DIS_F_REPT
	jz	da34		;if we're not allowed to repeat ourselves
	test al,DIS_I_UNUSED
	jnz da33		;if we printed ' (unused)'
	mov ax,[idxins]
	cmp ax,17h
	je da33			;if it was 'pop ss'
	cmp ax,8eh
	je da33			;if it was 'mov ss,--'
	cmp ax,0fbh
	jne da34		;if it was not 'sti'
da33:
	jmp disasm1
da34:
	ret

;--- MOD R/M (OP_RM)

dop_rm:
	call getregmem
	cmp al,0c0h
	jb dop05
	jmp dop33			;if pure register reference
dop05:					;<--- used by OP_M, OP_M64, OP_M80
	call showsize		;print out size in AH
dop06:					;<--- used by OP_MOFFS, OP_MXX, OP_MFLOAT, OP_MDOUBLE
	or [preused],PRESEG	;needed even if there's no segment override
						;because handling of LOCK prefix relies on it
	test [preflags],PRESEG
	jz @F				;if no segment override
	call showseg		;print segment name
	mov al,':'
	stosb
@@:
	mov al,[ai.regmem]
	and al,0c7h
	or [preused],PRE32A
	test [preflags],PRE32A
	jz @F
	jmp dop18		;if 32-bit addressing
@@:
	or [disflags], DIS_I_SHOW	;we'd like to show this address
	mov word ptr [addrr],0		;zero out the address initially
	cmp al,6
	xchg ax,bx		;mov bx,ax
	mov al,'['
	stosb
	je dop16		;if [xxxx]
	and bx,7
	mov bl,[rmtab+bx]
	test bl,8
	jnz dop09		;if BX
	test bl,4
	jz dop11		;if not BP
	mov ax,'PB'
	mov cx,[regs.rBP]
	test [preflags],PRESEG
	jnz dop10		;if segment override
	dec [segmnt]	;default is now SS
	jmp dop10

dop09:
	mov ax,'XB'		;BX
	mov cx,[regs.rBX]

dop10:
	mov [addrr],cx	;print it out, etc.
	stosw
	test bl,2+1
	jz dop13		;if done
	mov al,'+'
	stosb
dop11:
	mov ax,'IS'		;SI?
	mov cx,[regs.rSI]
	test bl,1
	jz @F			;if SI
	mov al,'D'		;DI
	mov cx,[regs.rDI]
@@:
	add [addrr],cx	;print it out, etc.
	stosw
dop13:
	test byte ptr [ai.regmem],0c0h
	jz dop17		;if no displacement
	test byte ptr [ai.regmem],80h
	jnz dop15		;if word displacement
	call disgetbyte
	cbw
	add [addrr],ax
	cmp al,0
	mov ah,'+'
	jge @F			;if >= 0
	mov ah,'-'
	neg al
@@:
	mov [di],ah
	inc di
	call hexbyte	;print the byte displacement
	jmp dop17		;done

dop15:
	mov al,'+'
	stosb
dop16:
	call disgetword
	add [addrr],ax
	call hexword    ;print word displacement

dop17:
	mov al,']'
	stosb
	ret

;--- 32-bit MOD REG R/M addressing.

dop18:
	cmp al,5
	jne @F			;if not just a disp32 address
	mov al,'['
	stosb
	call disp32		;display 32bit offset
	jmp dop27

@@:
	push ax
	and al,7
	cmp al,4
	jne @F			;if no SIB
	call disgetbyte	;get and save it
	mov [ai.sibbyte],al
@@:
	pop ax
	test al,80h
	jnz dop22		;if disp32
	test al,40h
	jz dop23		;if no disp8
	call disgetbyte
	cmp al,0
	jge @F			;if >= 0
	neg al
	mov byte ptr [di],'-'
	inc di
@@:
	call hexbyte
	jmp dop23		;done

dop22:
	call disp32		;print disp32

dop23:
	mov al,[ai.regmem]
	and al,7
	cmp al,4
	jne dop28		;if no SIB
	mov al,[ai.sibbyte]
if 1               ;bugfix: make 'u' correctly handle [ESP],[ESP+x]
	cmp al,24h
	jnz @F
	mov al,4
	jmp dop28
@@:
endif
	and al,7
	cmp al,5
	jne @F			;if not [EBP]
	test byte ptr [ai.regmem],0c0h
	jnz @F			;if MOD != 0
	call disp32		;show 32-bit displacement instead of [EBP]
	jmp dop25

@@:
	mov word ptr [di],'E['
	inc di
	inc di
	call showreg16	;show 16bit register name (number in AL)
	mov al,']'
	stosb

dop25:
	mov al,[ai.sibbyte]
	shr al,1
	shr al,1
	shr al,1
	and al,7
	cmp al,4
	je disbad1		;if illegal
	mov word ptr [di],'E['
	inc di
	inc di
	call showreg16
	mov ah,[ai.sibbyte]
	test ah,0c0h
	jz dop27		;if SS = 0
	mov al,'*'
	stosb
	mov al,'2'
	test ah,80h
	jz @F			;if *2
	mov al,'4'
	test ah,40h
	jz @F			;if *4
	mov al,'8'
@@:
	stosb
dop27:
	mov al,']'
	stosb
	ret

;--- 32-bit addressing without SIB

dop28:
	mov word ptr [di],'E['
	inc di
	inc di
	call showreg16
	mov al,']'
	stosb
	ret

;--- Memory-only reference (OP_M)

dop_m:
	call getregmem
	cmp al,0c0h
	jae disbad1		;if it's a register reference
	jmp dop05

disbad1:
	jmp disbad		;this is not supposed to happen

;--- Register reference from MOD R/M part (OP_R_MOD)

dop_r_mod:
	call getregmem
	cmp al,0c0h
	jb disbad1		;if it's a memory reference
	jmp dop33

;--- Memory offset reference (OP_MOFFS)

dop_moffs:
	call showsize	;print the size and save various things
	mov al,5
	test [preflags],PRE32A
	jnz @F			;if 32-bit addressing
	inc ax
@@:
	mov [ai.regmem],al
	jmp dop06		;don't show size

;--- Pure register reference (OP_R)

dop_r:
	call getregmem_r

dop33:					;<--- used by OP_RM, OP_R_MOD and OP_R_ADD
	and al,7			;entry point for regs from MOD R/M, and others
	mov cl,[disflags2]
	or [disflags],cl	;if it was variable size operand, the size
						;should now be marked as known.
	cmp ah,0
	jl dop35			;if byte register
	jz dop34			;if word register
	cmp ah,20h			;qword register (mmx)?
	jz dop35_1
	mov byte ptr [di],'E'
	inc di
dop34:
	add al,8
dop35:
	cbw
	shl ax,1
	xchg ax,bx			;mov bx,ax
	mov ax,word ptr [rgnam816+bx];get the register name
	stosw
	ret
dop35_1:
	push ax
	mov ax,"MM"
	stosw
	pop ax
	add al,'0'
	stosb
	ret

;--- Register number embedded in the instruction (OP_R_ADD)

dop_r_add:
	mov al,[instru]
	jmp dop33

;--- AL or AX or EAX (OP_AX)

dop_ax:
	mov al,0
	jmp dop33

;--- QWORD mem (OP_M64).
;--- this operand type is used by:
;--- + cmpxchg8b
;--- + fild, fistp

dop_m64:
;	mov ax,'Q'		;print 'Q' +'WORD'
	mov ah,20h		;size QWORD
	jmp dop40

;--- FLOAT (=REAL4) mem (OP_MFLOAT).

dop_mfloat:
	mov ax,'LF'
	stosw
	mov al,'O'
	stosb
	mov ax,'TA'
	jmp dop38c

;--- DOUBLE (=REAL8) mem (OP_MDOUBLE).

dop_mdouble:
	mov ax,'OD'
	stosw
	mov ax,'BU'
	stosw
	mov ax,'EL'
dop38c:
	stosw
	call showptr
	jmp dop42a

;--- TBYTE (=REAL10) mem (OP_M80).

dop_m80:
	mov ax,0ff00h+'T'	;print 't' + 'byte'
	stosb
dop40:
	call getregmem
	cmp al,0c0h
	jae disbad5		;if it's a register reference
	and [disflags],not DIS_F_SHOW	;don't show this
	jmp dop05

;--- far memory (OP_FARMEM).
;--- this is either a FAR16 (DWORD) or FAR32 (FWORD) pointer

dop_farmem:
	call dischk32d
	jz @F			;if not dword far
	call showdwd
	sub di,4		;erase "PTR "
@@:
	mov ax,'AF'		;store "FAR "
	stosw
	mov ax,' R'
	stosw

;--- mem (OP_MXX).

dop_mxx:
	and [disflags],not DIS_F_SHOW	;don't show this
dop42a:
	call getregmem
	cmp al,0c0h
	jae disbad5		;if it's a register reference
	jmp dop06		;don't show size

disbad5:
	jmp disbad

;--- far immediate (OP_FARIMM). Either FAR16 or FAR32

dop_farimm:
	call disgetword
	push ax
	call dischk32d
	jz @F			;if not 32-bit address
	call disgetword
	push ax
@@:
	call disgetword
	call hexword
	mov al,':'
	stosb
	call dischk32d
	jz @F			;if not 32-bit address
	pop ax
	call hexword
@@:
	pop ax
	call hexword
	ret

;--- 8-bit relative jump (OP_REL8)

dop_rel8:
	call disgetbyte
	cbw
	jmp dop48

;--- 16/32-bit relative jump (OP_REL1632)

dop_rel1632:
	call disgetword
	call dischk32d
	jz dop48		;if not 32-bit offset
	push ax
	call showdwd
	sub di,4		;erase "PTR "
dop47_1:
	pop dx
	call disgetword
	mov bx,[u_addr+0]
	add bx,[dis_n]
	add dx,bx
	adc ax,[u_addr+2]
	call hexword
	xchg ax,dx
	jmp hexword		;call hexword and return

dop48:
	add ax,[u_addr]
	add ax,[dis_n]
	jmp hexword		;call hexword and return

;--- Check for ST(1) (OP_1CHK).

dop49:
	pop ax		;discard return address
	mov al,[ai.regmem]
	and al,7
	cmp al,1
	je dop50		;if it's ST(1)
	jmp da14		;another operand (but no comma)

dop50:
	jmp da21		;end of list

;--- ST(I) (OP_STI).

dop_sti:
	mov al,[ai.regmem]
	and al,7
	xchg ax,bx		;mov bx,ax
	mov ax,'TS'
	stosw			;store ST(bl)
	mov al,'('
	stosb
	mov ax,')0'
	or al,bl
	stosw
	ret

;--- CRx (OP_CR).

dop_cr:
	mov bx,'RC'
	call getregmem_r
	cmp al,4
	ja disbad4		;if too large
	jne @F
	mov [ai.dismach],5	;CR4 is new to the 586
@@:
	cmp [idxins],SPARSE_BASE+22h
	jne dop55		;if not MOV CRx,xx
	cmp al,1
	jne dop55		;if not CR1

disbad4:
	jmp disbad		;can't MOV CR1,xx

;--- DRx (OP_DR).

dop_dr:
	call getregmem_r
	mov bx,'RD'
	mov cx,-1		;no max or illegal value
	jmp dop55

;--- TRx (OP_TR).

dop_tr:
	call getregmem_r
	cmp al,3
	jb disbad		;if too small
	cmp al,6
	jae @F			;if TR6-7
	mov [ai.dismach],4	;TR3-5 are new to the 486
@@:
	mov bx,'RT'

dop55:
	xchg ax,bx
	stosw			;store XX
	xchg ax,bx
	or al,'0'
	stosb
	ret

;--- Segment register (OP_SEGREG).

dop_segreg:
	call getregmem_r
	cmp al,6
	jae disbad		;if not a segment register
	cmp al,2
	je @F			;if SS
	and [disflags],not DIS_F_REPT	;clear flag:  don't repeat
@@:
	cmp al,4
	jb @F			;if not FS or GS
	mov [ai.dismach],3	;(no new 486-686 instructions involve seg regs)
@@:
	add al,16
	jmp dop35		;go print it out

;--- Sign-extended immediate byte (OP_IMMS8). "push xx"

dop_imms8:
	call disgetbyte
	cmp al,0
	xchg ax,bx		;mov bl,al
	mov al,'+'
	jge @F			;if >= 0
	neg bl
	mov al,'-'
@@:
	stosb
	xchg ax,bx		;mov al,bl
	jmp dop59a		;call hexbyte and return

;--- Immediate byte (OP_IMM8).

dop_imm8:
	call disgetbyte
dop59a:
	jmp hexbyte		;call hexbyte and return

;--- Show MMx reg (OP_MMX; previously was "Show ECX if LOOPxx is 32bit)"

dop_mmx:
	mov bx,'MM'
	call getregmem_r
	and al,7
	jmp dop55

;--- Set flag to always show size (OP_SHOSIZ).

dop_shosiz:
	or [disflags],DIS_I_SHOWSIZ
dop60a:
	pop ax			;discard return address
	jmp da14		;next...

disbad:
	mov sp,[savesp2]	;pop junk off stack
	mov ax,offset da13
	push ax
	mov [dis_n],0
	mov word ptr [preflags],0		;clear preflags and preused
	mov [rmsize],80h				;don't display any memory
	mov word ptr [ai.dismach],0		;forget about the machine type
	and [disflags],not DIS_I_SHOW	;and flags
	call disgetbyte
	mov di,offset prefixlist
	mov cx,N_PREFIX
	repne scasb
	je @F			;if it's a named prefix
	dec [dis_n]
	mov bx,MN_DB	;offset of 'DB' mnemonic
	mov si,OPLIST_26+OPTYPES_BASE;this says OP_IMM8
	ret
@@:
	or [disflags],DIS_I_UNUSED	;print special flag
	mov bx,N_PREFIX-1
	sub bx,cx
	shl bx,1
	cmp bx,6*2
	jb @F			;if SEG directive
	mov bx,[prefixmnem+bx-6*2]
	mov si,OPTYPES_BASE	;no operand
	ret
@@:
	lea si,[bx+OPLIST_40+OPTYPES_BASE]	;this is OP_ES
	mov bx,MN_SEG
	ret

;   GETREGMEM_R - Get the reg part of the reg/mem part of the instruction
;   Uses    CL

getregmem_r:
	call getregmem
	mov cl,3
	shr al,cl
	and al,7
	ret

;--- GETREGMEM - Get the reg/mem part of the instruction

getregmem:
	test [preused],GOTREGM
	jnz @F			;if we have it already
	or [preused],GOTREGM
	call disgetbyte	;get the byte
	mov [ai.regmem],al	;save it away
@@:
	mov al,[ai.regmem]
	ret

;   SHOWSEG - Show the segment descriptor in SEGMNT
;   Entry   DI  Where to put it
;   Exit    DI  Updated
;   Uses    AX, BX

showseg:
	mov al,[segmnt]	;segment number
	cbw
	shl ax,1
	xchg ax,bx		;mov bx,ax
	mov ax,word ptr [segrgnam+bx] ;get register name
	stosw
	ret

;   DISP32 - Print 32-bit displacement for addressing modes.
;   Entry   None
;   Exit    None
;   Uses    AX

disp32:
	call disgetword
	push ax
	call disgetword
	call hexword
	pop ax
	call hexword
	ret

;   SHOWREG16 - Show 16-bit register name.
;   Entry   AL  register number (0-7)
;   Exit    None
;   Uses    AX

showreg16:
	cbw
	shl ax,1
	xchg ax,bx
	push ax
	mov ax,word ptr [rgnam16+bx]
	stosw
	pop ax
	xchg ax,bx
	ret

;--- DISCHK32D - Check for 32 bit operand size prefix (66h).

dischk32d:
	or [preused],PRE32D
	test [preflags],PRE32D
	ret

disasm endp

;--- Here are the routines for printing out the operands themselves.
;--- Immediate data (OP_IMM)

dop_imm proc
	cmp ah,0
	jl dop03		;if just a byte
	pushf
	test [disflags], DIS_I_SHOWSIZ
	jz @F			;if we don't need to show the size
	call showsize	;print size in AH
	sub di,4		;erase "PTR "
@@:
	call disgetword
	popf
	jz @F			;if just a word
	push ax
	call disgetword	;print the high order word
	call hexword
	pop ax
@@:
	call hexword
	ret

dop03:
	call disgetbyte	;print immediate byte
	call hexbyte
	ret
dop_imm endp

;   SHOWOP  Show the op code
;   Entry   SI  Null-terminated string containing the op mnemonic
;   Exit    DI  Address of next available byte in output line
;           (>= offset line_out + 32 due to padding)
;   Uses    AL

showop proc
	mov di,offset line_out+MNEMONOFS
@@:
	lodsb
	mov ah,al
	and al,7Fh
	stosb
	and ah,ah
	jns @B
	mov al,' '
@@:
	stosb
	cmp di,offset line_out+MNEMONOFS+8
	jb @B
	ret
showop endp

;   SHOWSIZE - Print a description of the size
;   Entry   AH  10h=DWORD, 00h=WORD, F0h=BYTE, 20h=QWORD
;   Uses    AX

;   SHOWPTR - Print " PTR"
;   Uses    AX

;   SHOWDWD - Print "DWORD PTR"
;   Uses    AX

showsize proc
	mov [rmsize],ah	;save r/m size
	mov [sizeloc],di;save where we're putting this
	mov al,'Q'
	cmp ah,20h
	jz showqwd
	cmp ah,0
	jg showdwd	;if dword
	je showwd	;if word
	mov ax,'YB'
	stosw
	mov ax,'ET'
	jmp ssz3
showdwd::		;<---
	mov al,'D'
showqwd:
	stosb
showwd:
	mov ax,'OW'
	stosw
	mov ax,'DR'
ssz3:
	stosw
showptr::		;<---
	mov ax,'P '
	stosw
	mov ax,'RT'
	stosw
	mov al,' '
	stosb
	ret
showsize endp

;   DISGETBYTE - Get byte for disassembler.
;   Entry   None
;   Exit    AL  Next byte in instruction stream
;   Uses    None

disgetbyte proc
	push ds
	push si
	mov si,[u_addr]
	mov ds,[u_addr+4]
	add si,cs:[dis_n]	;index to the right byte
	lodsb 				;get the byte
	pop si
	pop ds
	inc [dis_n]			;indicate that we've gotten this byte
	ret
disgetbyte endp

;   DISGETWORD - Get word for disassembler.
;   Entry   None
;   Exit    AX  Next word
;   Uses    None

disgetword proc
	push ds
	push si		;save si
	mov si,[u_addr]
	mov ds,[u_addr+4]
	add si,cs:[dis_n]	;index to the right byte
	lodsw
	pop si		;restore things
	pop ds
	add [dis_n],2
	ret
disgetword endp

;   DISSHOWBYTES - Show bytes for the disassembler.
;   Entry   BX  Number of bytes (must be > 0)
;   Exit        u_addr updated
;   Uses    BX, SI.

disshowbytes proc
	mov si,[u_addr]
	mov ds,[u_addr+4]
@@:
	lodsb
	call hexbyte
	dec bx
	jnz @B
	push ss
	pop ds
	mov [u_addr],si
	ret
disshowbytes endp

;   MOVEOVER - Move the line to the right - disassembler subfunction.
;   Entry   DI  Last address + 1 of line so far
;   Exit    CX  Number of bytes to move
;   DI  Updated
;   Uses    SI

moveover proc
	cmp [sizeloc],0
	je @F		;if sizeloc not saved
	add [sizeloc],cx
@@:
	mov si,di
	add di,cx
	mov cx,di
	sub cx,offset line_out+MNEMONOFS
	push di
	std
	dec si
	dec di
	rep movsb
	pop di
	cld
	ret
moveover endp

;--- the layout for FSAVE/FRSTOR depends on mode and 16/32bit

CONST segment
fregnames label byte
	db "CW", "SW", "TW"
	db "OPC=", "IP=", "DP="
dEmpty db "empty"
dNaN db "NaN"
CONST ends

;--- copystring - copy non-empty null-terminated string.
;--- SI->string
;--- DI->buffer

copystring proc
	lodsb
@@:
	stosb
	lodsb
	cmp al,0
	jne @B
	ret
copystring endp

;   HEXDWORD - Print hex word (in EAX).
;   clears HiWord(EAX)

;   HEXWORD - Print hex word (in AX).
;   HEXBYTE - Print hex byte (in AL).
;   HEXNYB - Print hex digit.
;   Uses    al, di.

hexdword proc
	push ax
	.386
	shr eax,16
	.8086
	call hexword
	pop ax
hexdword endp	;fall through!

hexword proc
	push ax
	mov al,ah
	call hexbyte
	pop ax
hexword endp	;fall through!

hexbyte:
	push ax
	push cx
	mov cl,4
	shr al,cl
	call hexnyb
	pop cx
	pop ax

hexnyb:
	and al,0fh
	add al,90h		;these four instructions change to ascii hex
	daa
	adc al,40h
	daa
	stosb
	ret

;   TAB_TO - Space fill until reaching the column indicated by AX.
;   (Print a new line if necessary.)

tab_to proc
	push ax
	sub ax,di
	ja @F			;if there's room on this line
	call trimputs
	mov di,offset line_out
@@:
	pop cx
	sub cx,di
	mov al,' '
	rep stosb		;space fill to the right end
	ret
tab_to endp

;   TRIMPUTS - Trim excess blanks from string and print (with CR/LF).
;   PUTSLINE - Add CR/LF to string and print it.
;   PUTS - Print string through DI.

trimputs:
	dec di
	cmp byte ptr [di],' '
	je trimputs
	inc di

putsline:
	mov ax,LF * 256 + CR
	stosw

puts:
	mov cx,di
	mov dx,offset line_out
	sub cx,dx

stdout proc			;write DS:DX, size CX to STDOUT (1)
	;use BIOS for output
	jcxz nooutput
	push si
	mov si,dx
@@:
	lodsb
	mov bx,0007
	mov ah,0Eh
	int 10h
	loop @B
	pop si
nooutput:
	ret
stdout endp

_TEXT ends

_DATA segment

;--- I/O buffers.  (End of permanently resident part.)

line_in		db 255,0,CR				;length = 257
line_out	equ line_in+LINE_IN_LEN+1;length = 1 + 263
real_end	equ line_in+LINE_IN_LEN+1+264

_DATA ends

_ITEXT segment

;---------------------------------------
;--- Debug initialization code.
;---------------------------------------

imsg1	db DBGNAME,' version 1.23.  Debugger.',CR,LF,CR,LF
	db 'Usage: ', DBGNAME, CR,LF,CR,LF
	db '  progname (executable) file to debug or examine',CR,LF
	db '  arglist parameters given to program',CR,LF,CR,LF
	db 'For a list of debugging commands, '
	db 'run ', DBGNAME, ' and type ? at the prompt.',CR,LF,'$'

imsg2	db 'Invalid switch - '
imsg2a	db 'x',CR,LF,'$'

initcode proc
	cld

	.8086		 	;back to 1980s technology

;--- Interpret switches and erase them from the command line.
if 0
	mov ax,3700h		;get switch character
	int 21h
else
	mov dl,'-'
endif
	mov [switchar],dl
	cmp dl,'/'
	jne @F
	mov [swch1],dl
@@:
	mov si,DTA+1
@@:
	lodsb
	cmp al,' '
	je @B
	cmp al,TAB
	je @B

;--- Process the /? switch (or the [switchar]? switch).
;--- If switchar != / and /? occurs, make sure nothing follows.

	cmp al,dl
	je init11		;if switch character
	cmp al,'/'
	jne init12		;if not the help switch
	mov al,[si]
	cmp al,'?'
	jne init12		;if not /?
	mov al,[si+1]
	cmp al,' '
	je init10		;if nothing after /?
	cmp al,TAB
	je init10		;ditto
	cmp al,CR
	jne init12		;if not end of line

;--- Print a help message

init10:
	mov dx,offset imsg1	;command-line help message
	call int21ah9	;print string
if 0
	int 20h			;done
else
	;jmp	0000:8200
	db	0EAh		; JMP FAR dword
	dd	00008200h
endif
;--- Do the (proper) switches.

init11:
	lodsb
	cmp al,'?'
	je init10		;if -?

;--- ||| Other switches may go here.

if 0
	mov [imsg2a],al
	mov dx,offset imsg2	;Invalid switch
	call int21ah9	;print string
	mov ax,4c01h	;Quit and return error status
	int 21h
else
	;jmp	0000:8200
	db	0EAh		; JMP FAR dword
	dd	00008200h
endif

;--- Feed the remaining command line to the 'n' command.

init12:

;--- Set up interrupt vectors.

	mov cx,NUMINTS
	mov si,offset inttab
	mov di,offset intsave
@@:
	lodsb
if 0
	mov ah,35h
	int 21h
else
	mov bl, al
	xor bh, bh
	shl bx, 1
	shl bx, 1
	les bx, [bx]
endif
	mov [di+0],bx
	mov [di+2],es
	add di,4
	xchg ax,dx		;save int # in dl
	lodsw			;get address
	xchg ax,dx		;restore int # in al, set int offset in dx
if 0
	mov ah,25h		;set interrupt vector
	int 21h
else
	mov bl, al
	mov bh, bh
	shl bx, 1
	shl bx, 1
	mov [bx+0], dx
	mov [bx+2], ds
endif
	loop @B

	push ds
	pop es

;--- Save and modify termination address and the parent PSP field.

	mov si,TPIV
	mov di,offset psp22
	movsw
	movsw

	mov [si-2],cs
	mov si,PARENT
	movsw
	mov [si-2],cs

;--- shrink DEBUG and set its stack

	mov ax,offset real_end + STACKSIZ + 15
	and al,not 15		;new stack pointer
	mov bx,ax
	dec ax
	dec ax
	mov [top_sp],ax	;save new SP minus two (for the word we'll push)
	inc ax
	inc ax
	mov cl,4
	shr bx,cl
	jmp cmdloop
initcode endp

_ITEXT ends

_IDATA segment
cntpatch = ($ - patches) / 2
_IDATA ends

	end start
