;
; GRDB
;
; Copyright(c) LADsoft
;
; David Lindauer, gclind01@starbase.spd.louisville.edu
;
;
; ASM.ASM
;
; Function: Assembler parser
;
; not very efficient, but, fast enough!
;
	.386
	;MASM MODE
	.MODEL SMALL

include asm.asi
include opcodes.asi
include aoperands.ase
include opcodes.ase
include opcom.ase
include prints.ase
include input.ase
include mtrap.ase
include	dispatch.ase
include operands.ase

	PUBLIC	asm
	PUBLIC	arg1
	PUBLIC	arg2
	PUBLIC	arg3
	PUBLIC	RepPfxBitmap
	PUBLIC	lastofs
	PUBLIC	lastbyte
	PUBLIC	PrefixBitmapWord

	.data
mtoofew		db	"Not enough operands",0
lastbyte	db	0		;last char of valid mnemonic
lastofs		dd	0		;current disassembly offset
say_repne	db	"repne",0	;prefix strings to look for
say_repe	db	"repe",0
say_rep		db	"rep",0
say_lock	db	"lock",0
say_word	db	"word",0	;opcode size overrides
say_byte	db	"byte",0
say_ptr		db	"ptr"
arg1		ASMOP	<>		;three args
arg2		ASMOP	<>
arg3		ASMOP	<>
arg4		ASMOP	<>		;temp for base-mode register gathering
AsmbldInstrsBuf	db	16 DUP (?)	;temporary to hold assembled instructions
OpSizeTable	db	16 DUP (?)	;sizes returned by AOP routines
OpSizeTblIndex	dw	0		;pointer into OpSizeTable
RepPfxBitmap	db	?		;bitmap of which rep prefix found
EnteredMnemonic	db	16 DUP (?)	;bucket to hold mnemonic as typed

;DefaultSeg appears to hold an index into SegmentPfxBytes.

DefaultSeg	db	0		;current default seg (DS or SS)
SegmentPfxBytes	db	26h,2eh,36h,3eh,64h,65h	;table of seg prefixes
	SEGPFXLISTSIZE	equ	$-SegmentPfxBytes
OverridePfxList	db	"asosgsfsdssscses"	;list of prefixes with colon
PrefixBitmapWord	dw	?	;bitmap of which prefix found
	.CODE
;
; interpreter stub to get params
; INPUT: DS:SI points at the user input line, just past the command char.
; I am assuming that someone issued the A command.  This can be by itself, 
; or it can be followed by some addresses
; The syntax is A [[segment:]offset]
;	where 
;	[segment:] can be any segment register, or it can be any hex
;		number of up to 4 digits.
;	[offset] can be any hex number of up to 8 digits
; OUTPUT: lastseg and lastofs set up to assemble at if user provided a legal 
;	address. Segment is also in FS
;	CY if invalid address
;
;
asm	PROC
	call	WadeSpace		; see if address given
	jnz	readaddr		; yep, get it (not CR past spaces)
	mov	eax,lastofs
	or	eax,eax
	jz	usehere			;if not, go from where we are
	mov	ebx,lastofs
	jmp	gotaddr			;no need to set up our own
usehere:
	mov	ebx,dreip		;else load our CS:IP
	jmp	gotaddr			;and use that

readaddr:
	call	ReadAddress		; read address from input line
	jc	aserrm			; out on err
	call	WadeSpace		; make sure nothing else
	jnz	aserrm			; NZ means we didn't hit a CR
gotaddr:
	mov	esi,ebx			; load address
	and	esi,0ffffh		;force to 16-bit offset
	mov	lastofs,esi		;and current 16-bit offset
	call	doasm			; do assembly
	clc				; exit
	ret
aserrm:
	stc				; exit with err
	ret
asm	ENDP
;
; prompt for a line, parse and assemble
;
; main assembler
;
; OK, the user pressed A <CR>. Here we solicit each entry, one line
; at a time, and convert the valid ones to opcodes. Invalid entries are
; complained about, and we return to the top of this routine to get
; another attempt.
; INPUT: address to assemble at is in FS (and lastseg): lastofs
; OUTPUT: Assembled code placed at target memory location.
; PROCESSING: 
;	1) Display the segment:offset for the next line 
;	2) Get an instruction from the user
;		If CR only, we are done
;	3) If error, report and goto step 1, else assemble the instruction
;		into a temporary assembly buffer
;	4) Add any prefixes or overrides to the buffer as required
;	5) Copy the buffer to the target memory location
;	6) Return to step 1 until done.
; NOTES:
;	1) GetInputLine places the user input into InputBuffer, an 80-char
;		buffer. It handles backspacing. It returns with SI pointing
;		to the beginning of the edited input, which may not make
;		sense
;	2) getCode points DI at a 16-char bucket I call EnteredMnemonic,
;		and returns the first delineated string
;
doasm	PROC
	call	crlf			;move to new line
	mov	eax,lastofs		;get last offset
	call	PrintDword		;print it
	call	PrintSpace		;and a couple of spaces
	call	PrintSpace
	call	GetInputLine           	; get input line
	call	WadeSpace		; if nothing there we are done
	jz	doasx			;since we hit the CR
	sub	al,al			;clear out AL
	call	setsize			;put 0 into these 3 fields
	mov	PrefixBitmapWord,0	;say no prefix found
	mov	DefaultSeg,3
	call	getcode			; get the opcode
	jc	badop
	mov	edi,offset arg1		; get first arg
	call	parsearg
	jc	badarg
	mov	edi,offset arg2		; get second arg
	call	parsearg
	jc	badarg
	mov	edi,offset arg3		; get third arg
	call	parsearg
	jc	badarg
	jz	assemble		;if no more, to assemble it
manyerr:
	Call	printAlignedErrorMsg	;else bitch about too many operands
	db	"Too many operands",0
	jmp	doasm
badarg:
	call	printAlignedErrorMsg	;complain about invalid operand
	db	"Unknown operand",0
	jmp	doasm
badop:		
	call	printAlignedErrorMsg	;complain about invalid opcode
	db	"Unknown opcode",0
	jmp	doasm
assemble:
	call	validops		;size check and set
	jc	doasm			;size mismatch, ignore
	mov	esi,offset mnemonicMatchAddrTable	;find table we built
	mov     cx,[mnemonicMatchCount]	;number of valid table entries
	mov	OpSizeTblIndex,0	;init index to top of table

;mnemonicMatchAddrTable contains a list of up to 10h addresses. Each address
;points to a structure. Each structure contains (among other things) a
;pointer to a string for this mnemonic.
;  This loop is examining every valid element of the table we build to
;isolate those instances of this mnemonic that are valid in this case (that
;is, valid for size, addressing mode, etc).

assl:
	push	ecx			;save count
	push	esi			;save table address
	mov	esi,[esi]			;find structure address
	mov	edi,offset AsmbldInstrsBuf	;where to put binary 
	call	oneasm			;dispatch for this operand
					; copies opcode for this instruction
					; into the assembled code buffer
	mov	cl,0			;assume we didn't do anything
	jc	assx2			;and if true, cl is right
	mov	ecx,edi			;else get offset we ended up at
	sub	ecx,offset AsmbldInstrsBuf	;minus starting offset
assx2:		   	
	movzx	ebx,OpSizeTblIndex	;pointer into opsize table
	mov	[ebx+OpSizeTable],cl	;save how many we did
	inc	OpSizeTblIndex		;bump to next location
	pop	esi			;restore mnemonic ptr table address
	pop	ecx			;and how many to examine
	add	esi,4			;point to next possibility
	loop	assl			;do them all
	movzx	ecx,byte ptr OpSizeTblIndex	;see if we did anything
	or	ecx,ecx
	jz	nomatch
	sub	ebx,ebx			;else init for next loop


;OK, we found at least one match between the structure we built and the
;structure in the table, is my guess.  It is possible with the Intel
;instruction set, that several different combinations of bytes evaluate
;into the same functional operation. The assembled bytes for this structure 
;are in the buffer now. The next goal is, if more than one such sequence
;was found, we want the shortest sequence that will do the job.
; CX appears to contain an index into a table

szllp:
	or	bh,bh			;if bh is zero
	jz	szlg			;skip this stuff
	cmp	bh,[ecx+OpSizeTable-1]	;see if high byte of table value
					;is greater than bh
	jb	sslc			;go here if it is
	test	byte ptr [ecx+OpSizeTable-1],0FFh	;see if table value is 0
	jz	sslc			;and if it is, same place

;OK, BH is keeping track of the length of the sequence, and BL is keeping
;track of the table offset to that sequence.  We get here in case a) this
;is the first valid sequence we found, or b) this sequence is shorter than
;the prior shortest sequence we found.

szlg:
	mov	bh,[ecx+OpSizeTable-1]	;get high byte of tbl element->bh
	mov	bl,cl			;track the index to the shortest
sslc:
	loop	szllp			;do for as many valid match instances
					;as we found
ssgot:
	or	bh,bh			;did we find anything?
	jz	nomatch			;no, we did not
	sub	bh,bh			;convert BL into a word index
	shl	ebx,2			;into the match table
	mov	esi,[ebx+mnemonicMatchAddrTable-4]	;get an address
	mov	edi,offset AsmbldInstrsBuf	;point to buffer
	call	oneasm				;build our favorite sequence
	mov	ecx,edi				;DI is new buffer offset
	mov	esi,offset AsmbldInstrsBuf	;switch top to SI
	sub	ecx,esi			;get buffer bytecount
	mov	edi,lastofs		;point to last assemble offset
	push	ecx			;save count

;Now DI points to the actual location in memory where we want to put our
;assembled buffer.  Since prefixes are not in the assembly buffer, we first
;stick in as many prefixes as we found, then paste the remainder of the
;buffer beyond them.

	call	InsertPrefixes
	pop	ecx			;restore count
	jc	pfxerr			;if carry, too many prefixes
	rep	movsb			;copy to memory location for asm
	mov	lastofs,edi		;update assemble in mem location
doasmn:
	jmp	doasm			;and get next instruction
pfxerr:
	call	printAlignedErrorMsg
	db	"Too many prefixes",0
	jmp	doasm

nomatch:
	call	printAlignedErrorMsg
	db	"Invalid opcode/operand combo",0
	jmp	doasm
doasx:
	ret
doasm	ENDP

;Apparently, SI now points to the opcode structure for this OPCODE.
;	edi points to a temporary buffer into which we are assembling
;	this OPCODE.

oneasm	PROC
	test	[esi+OPCODE.FLAGS],prefix0F	;0F prefix on this guy?
	jz	no386p				;nope
	mov	al,0fh				;else stash the 0F
	stosb					;into the buffer
no386p:

;The syntax here works, but it can be clarified a little. OPCODE.OPERANDS
;was never explicitly written to.  Instead, we build a table of pointers
;into a table of opcode structures. SI contains one of the pointers out
;of that table. Maybe it's just because I'm more used to it, but I prefer
;the MASM syntax to indicate this:
;	mov	al,(opcode ptr [esi]).OPERANDS

	mov	al,[esi+OPCODE.OPERANDS]		;get addressing mode
	push	0				;TableDispatch calls this subkey
	call	TableDispatch			;and dispatch it
	dd	58				;length of table

	dd	aop0,  aop1,  aop2,  aop3,  aop4,  aop5,  aop6,  aop7
	dd	aop8,  aop9,  aop10, aop11, aop12, aop13, aop14, aop15
	dd	aop16, aop17, aop18, aop19, aop20, aop21, aop22, aop23
	dd	aop24, aop25, aop26, aop27, aop28, aop29, aop30, aop31
	dd	aop32, aop33, aop34, aop35, aop36, aop37, aop38, aop39
	dd	aop40, aop41, aop42, aop43, aop44, aop45, aop46, aop47
	dd	aop48, aop49, aop50, aop51, aop52, aop53, aop54, aop55
	dd	aop56, aop57, aop58
	ret
oneasm	ENDP



InsertPrefixes	PROC
	sub	edx,edx
	test	[PrefixBitmapWord],AS_OPSIZE	;see if 66 override
	jz	nopsiz
	mov	al,66h
	stosb
nopsiz:
	test	[PrefixBitmapWord],AS_ADDRSIZE	;see if 67 override
	jz	naddrsiz
	mov	al,67h
	stosb

naddrsiz:
	sub	edx,edx			;start with no override byte count
	mov	dh,byte ptr [PrefixBitmapWord]	;get prefix bitmap lo byte
	mov	ebx,offset SegmentPfxBytes	;list of prefix bytes
	mov	ecx,SEGPFXLISTSIZE		;there are 6 of these

; now we only find segment overrides in the prefix bitmap word

sl2:
	shr	dh,1			;see if this one required?
	jnc	nsl2
	mov	al,[ebx]
	stosb
	inc	edx
nsl2:
	inc	ebx
	loop	sl2
	
	cmp	dl,2			;count of segment prefixes added
	jb	lpnerr			;can't exceed 1
	stc
	ret
lpnerr:
	test	RepPfxBitmap,AF_LOCK	;see if lock set
	jz	nlock			;nope, no lock
	mov	al,0f0h			;else stash lock prefix
	stosb
nlock:
	test	RepPfxBitmap,AF_REPNE OR AF_REP
	jz	nrepne
	mov	al,0f2h			;stick in repne prefix
	stosb
nrepne:
	test	RepPfxBitmap,AF_REPE
	jz	nrepe
	mov	al,0f3h			;stick in repe prefix
	stosb
nrepe:
	clc
	ret
InsertPrefixes	ENDP

operr	PROC
	call	printAlignedErrorMsg
	db	"Unusable operand combination",0
	stc
	ret
operr	ENDP
;
; check for size mismatches and get a size.
; INPUT: SI points to user's input buffer, as far as I can tell
; OK, a little bit of clarification has happened on this one, thankfully.
;  Turns out that opcodes fall into several categories with respect to this
; routine. In general, for any opcode with more than one operand, all
; subsequent operands must match one another in size except for the
; exceptions. The exceptions to be permitted are:
;	1) movzx and movsx, which by definition have mismatched operands
;	2) In and Out instructions, where DX holds the port, and we can
;	   read or write any size operand through that port
;	3) SHR,SAR,SHL,SAL,RCR,RCL CL, since any size operand can be shifted 
;	   by CL bits
;	4) Immediate operands, which can be smaller but not larger than
;	   their targets
;	5) BASED, which is not understood at all yet.
;

validops	PROC
	cmp	lastbyte,"x" 		;if movzx or movsx
	je	voerr			;then this is OK


;OK, the logic here is really hosed. What it is supposed to say is this:
;IF any operand is provided
;	IF more than one operand
;		IF all operands are not equal in size
;			IF not an allowed exception
;				THEN error
;			ELSE OK
;		ELSE OK
;	ELSE OK
;ELSE OK

;The problem here appears to be, immediates have size 0 unless they are
;prefixed with byte ptr, word ptr, etc.  So for example, any size operand
;can be stuck into [44], like AL, AX, or EAX.  Furthermore, it was a
;conscious design decision that mov al,FFFF is allowed as an editing
;function. That is, you can enter (I think) up to 8 hex digits, and only
;the last two get put into AL. This allows you to fix a typo without
;using a backspace!


chsize:
	mov	al,arg1.ASIZE		;see if first arg size is 0
	or	al,al			;used later, so load into AL
	jnz	szch			;if non0 size, more checking
	mov	al,arg2.ASIZE		;else load up size of 2d arg
	or	al,al			;used again
	jnz	szch			;if non0, keep checking?
	mov	al,arg3.ASIZE		;check size of final arg
	or	al,al
	jnz	szch			;if it has non0 size
	clc				; no size, let it go
	ret
szch:
;	test	arg1.ASIZE,0FFh		;these 4 lines do nothing, since
;	jz	noa1			;we fall through whether or not
;	cmp	al,arg1.ASIZE		;arg1.ASIZE was 0
;	jnz	absx			
noa1:
	test	arg2.ASIZE,0FFh		
	jz	noa2			
	cmp	al,arg2.ASIZE		
	jne	absx  
	
;Near as I can tell, we get here if:
;1) Both arg1.ASIZE and arg2.ASIZE are 0
;2) Either of them is 0
;3) Both are non-0, but the sizes match	
;  At this point, I need to do some real thinking. If only one of the above
;was non-0, and arg3.ASIZE is 0, the arg1.ASIZE or arg2.ASIZE that was 0
;still might be an immediate. So we check, and if it is immediate, we do a 
;size match.  This permits us to say mov al,FFFF but does NOT permit us to 
;say mov byte ptr [44], FFFF
;  Now, if arg3.ASIZE is not 0, it can't be an immediate (immediates are 0)
;AL at this point could hold any of the 3 sizes. I think we actually want
;to check allowed exceptions if arg3.ASIZE differs from either previous
;non-0 asize, and check immediates if matches their size or is the only
;non-0 sized argument.
;  SO, if AL contains a prior size, we want to compare it with arg3.ASIZE
;to see if they match.  If AL contains arg3.ASIZE, the other two must be 0,
;so we need to ??? I can't think of any example of this. There is NO
;instruction of the form DOIT [mem],[mem],immediate.
	
noa2:
	test	arg3.ASIZE,0FFh		
	jz	finalsize
	cmp	al,arg3.ASIZE
	jne	absx
finalsize:
	call	setsize			;set the size we found
	jmp	chimmsize		;chk immediates and based addressing
absx:
	mov	eax,dword ptr [EnteredMnemonic]
	and	eax,0ffffffh
	cmp	eax,"tuo"		; no size check on in/out
	je	voerr
	cmp	eax,"ni"
	je	voerr
	mov	edi,offset arg2		; get out if arg2 or arg 3 is cl
	call	chkcl
	jz	voerr
	mov	edi,offset arg3
	call	chkcl
	jz	voerr
	call	printAlignedErrorMsg
	db	"Bad size",0
	stc
voerr:
	ret
chimmsize:
     	mov	edi,offset arg1
	call	immsize			;see if immediate that will fit
	jc	voerr			;nope, won't fit
	call	chkbase			;else chk for valid based addressing
	jc	voerr			;not valid
     	mov	edi,offset arg2
	call	immsize
	call	chkbase
	jc	voerr
	jc	voerr	
     	mov	edi,offset arg3
	call	immsize
	jc	voerr	
	call	chkbase
	jc	voerr
	ret
validops	ENDP


;One of the allowed exceptions is a shift or rotate of a register exceeding
;8 bits by CL, which is 8 bits.  Here we check for that CL, and allow the
;exception if we find it
;INPUT: DI points to this structure
;OUTPUT: ZF if we found CL, NZ if we didn't

chkcl	PROC
	cmp	[edi+ASMOP.MODE],AM_REG	; check if register involved
	jne	cclerr			; if not, can't be CL
	cmp	arg2.AREG1,isECX	; else see if ECX involved at all
	jne	cclerr			; if not, can't be CL
	cmp	arg2.ASIZE,BYTESIZE	; if ECX is byte, must be CL
cclerr:
	ret
chkcl	ENDP


;INPUT: AL is an argument size 1=byte, 2=word, 4=dword
;	EnteredMnemonic is a bucket containing what the user typed
;	PrefixBitmapWord containing a bit for each type of prefix allowed.
;		AS_OPSIZE is for operand size override prefix OS:, which
;		means stick in a 66h
;	arg1,2, and 3 are instances of structure ASMOP. asize is the size
;		of the operand.  An instruction can have up to 3 operands
; What we do here is set the passed size as the size of all 3 operands,
; setting the 66 override if dword size (and not FP), and returning ZF
; if it was a dword, and NZ if it was not

setsize	PROC
	cmp	al,WORDSIZE
	jne	ssnoop
	cmp	byte ptr [EnteredMnemonic],'f'	; no opsize on fp stuff
	je	ssnoop
	or	[PrefixBitmapWord],AS_OPSIZE	;include 'OS:' prefix
ssnoop:
	mov	arg1.ASIZE,al			;set all sizes the same
	mov	arg2.ASIZE,al
	mov	arg3.ASIZE,al
	cmp	arg1.ASIZE,WORDSIZE		;rtn NZ if NOT a dword
	clc
	ret
setsize	ENDP
;
;INPUT: DI points to structure built for this instruction
;	AL contains size of first argument
;If immediate operand, make sure that the size of the argument passed in
;AL is valid. Apparently an immediate dword is always OK, but it is necessary
;in that case to set a bit indicating AS_OPSIZE, whatever that means.
;Otherwise, in cases of byte or word, it is necessary to make sure that
;the target offset (or segment if seg:ofs) does not exceed the immediate
;value in size.  This is not nearly well enough understood to draw any
;conclusions yet.
;  My guess is that if we have something like mov eax, immediate, then any
;immediate is OK.  If it is mov ax, immediate, then the value to be moved in
;must be a byte or word. Finally, if mov al, immediate, then the immediate
;value must be a byte
;OUTPUT: NC if immediate operand will fit in target, CY if not.

immsize	PROC
	cmp	[edi+ASMOP.MODE],AM_IMM		;see if immediate value
	clc					;assume not
	jne	immok				;and if not, we're OK
	cmp	al,WORDSIZE			;else chk for dword size
	jz	immokl				;go if AL >=4 (dword)
	cmp	al,BYTESIZE			;else if AL is word size
	jz	bytech
immok:
	clc					;assume it is
	ret
immokl:
	or	[PrefixBitmapWord],AS_OPSIZE	;set this
	test	[edi+ASMOP.ADDRX],0ffff0000h	;else test for word offset
	jz	immok				;if so, we're ok
	stc					;else error
	ret
bytech:
	test	[edi+ASMOP.ADDRX],0ffffff00h	;test for byte offset
	jz	immok				;OK if byte
	stc					;else error
	ret
immsize	ENDP


chkbase	PROC
	cmp	[edi+ASMOP.MODE],AM_BASED	;is it base+something?
	jne	cbxnb				;if not, get out
	cmp	[edi+ASMOP.MSIZE],BYTEMODE	;see if byte-mode addressing
	je	cberr				;no can do this
	cmp	[edi+ASMOP.MSIZE],DWORDMODE	;how about dword?
	je	cb32				;it's ok for now

;OK, we don't have dword addressing.  Scaling factors allowed are *1,2,4 or 8
;If we are multiplying by 1 (why?) this seems OK, else not allowed. I guess
;scaling is only allowed in dword addressing mode, but scaling by 1 isn't
;really scaling, it's just syntactically permitted.

	cmp	[edi+ASMOP.ASCALE],TIMES1	;else scaled by 1?
	jne	cberr				;must be real scaling
	cmp	[edi+ASMOP.AREG1],TIMES4		;OK, is register=ESP
	je	cberr				;can't scale ESP

;Weird locution. 3 is the magic value for EBX. The carry will be set if
;areg1 is smaller than 3, since a compare is really a subtract without
;modifying the registers.

	cmp	[edi+ASMOP.AREG1],isEBX		;how about EBX
	jb	cberr				;can't scale by EAX,ECX,EDX

;areg2 is any second register (like [eax+ebx+nnnn]

	cmp	[edi+ASMOP.AREG2],0FFh		;any more in here?

;areg2 was initialized to FF (perversely defined as -1 to make the code more
;unreadable) in order to make unnecessary use of the obscure jns instruction.
;Anyway, FF means there never was an areg2. JZ (rather than the readable JE)
;here means no areg2 (second base reg) found

;A table is emerging from the following.  It tells us:
;

	je	cbx				;didn't get to second base
	cmp	[edi+ASMOP.AREG2],isESP		;is 2d base ESP
	je	cberr				;if so, illegal
	cmp	[edi+ASMOP.AREG2],isEBX		;compare with EBX value
	jb	cberr				;jb is EAX,ECX,EDX
	cmp	[edi+ASMOP.AREG1],isESI		;compare with ESI
	jae	cbdown				;jae for ESI, EDI
	cmp	[edi+ASMOP.AREG2],isESI		;cmpare 2d arg with ESI
	jb	cberr				;jb to error
	jmp	cbx

;We got here if the first register was ESI or EDI. In that case, the second
;register must be...damn, why use the carry instructions.  We should rewrite
;this for any clarity at all:
;	cmp	second base register,ESI
;	jb	ok
;By implication, if the first was ESI or EDI, the second can be EBX or EBP	
;but apparently nothing else???

cbdown:
	cmp	[edi+ASMOP.AREG2],isESI		;if below ESI
	jb	cbx				;we're OK

;Errors go here. By implication, these errors are:
;1) using ESP at all for a base register
;2) using EAX, ECX or EDX as a second base register
;3) using something after ESI or EDI, but this logic is so bassackwards
;   I'm not sure whether esi must be followed by edi, or can't be followed by
;   edi, or can't be followed by another esi, or whether edi must come before
;   esi, or edi can't follow edi, or what????? And does this apply only to
;   scaling, or only when not scaling, or is scaling irrelevant here????

;Tomorrow when I'm less tired I'll give meaningful labels to all these
;magic numbers, change the jumps to meaningful jumps, and derive the rules...

cberr:
	call	printAlignedErrorMsg
	db	"Invalid base or index register",0
	stc
	ret

;We get here if we have based addressing and dword addressing mode.

cb32:
	or	[PrefixBitmapWord],AS_ADDRSIZE	;else set this flag
	cmp	[edi+ASMOP.AREG1],isEBP		;see if EBP is first reg
	jne	cb32n2bp			;skip if not
	cmp	[ di+ASMOP.AREG2],isEBP		;else if second is also EBP
	je	cberr				;that's an error
cb32n2bp:
	cmp	[edi+ASMOP.AREG2],isESP
	jne	cbx
	cmp	[edi+ASMOP.ASCALE],TIMES1
	jne	cberr
cbx:
	push	eax
	mov	al,[edi+ASMOP.AREG1]

;Well, let's see here. After ANDing with 6, the only way AL could be for
;is for areg1 to contain a value that has bit 2 set and bit 1 clear. The
;following registers meet this requirement: ESP, EBP. It follows that we
;are looking at SS. So maybe the magic number of 2 for DefaultSeg means
;SS is the default segment?

	and	al,6		;EBP or ESP will meet this test
	cmp	al,isESP	;after this cmp
	je	cbx1	
	mov	DefaultSeg,2
cbx1:
	mov	al,[edi+ASMOP.AREG2]

;But this escapes me completely. ANDing AL with 6 clears bit 0, so the
;result CANNOT be 5.  So this always fails...
;  I am going to change this to 4

	and	al,6
	cmp	al,isESP	;modified this also
	je	cbxnb
	mov	DefaultSeg,2
cbxnb:
	pop	eax

	clc
	ret
chkbase	ENDP


; print out error message
; INPUT: The error message is embedded in the code immediately following the
;	call to printAlignedErrorMsg
; Another non-obvious locution, so let's see... CX starts at FFFF and is
; decremented until we find a CR. Chances are, CX is a Very Big Number when
; we find CR. then we add 32d to CX, so CX will be a Very Small Number if
; the CR was less than 32 locations away, and NS will be true. But it it took
; MORE than 20h locations to find that CR, SF will be true.
;	To summarize then: If the string in the buffer was greater than
; 32 characters away, we paint our message one space beyond this string. If
; it was less than or equal to 32 locations away, we space out 0 or more
; spaces to the desired offset and print the message

printAlignedErrorMsg	proc
	mov	ecx,0FFFFh	;search a segment worth
	mov	edi,offset InputBuffer	;in buffer
	mov	al,13		;for a CR
	repne	scasb		;find it
	add	ecx,32		; space to line up errs
	jcxz	nospace
	jns	ok
	mov	ecx,1		;if can't align, use single space
ok:
	call	PrintSpace
	loop	ok
nospace:
	Msg	"??? "
	jmp	Message
printAlignedErrorMsg	ENDP
;
; get the opcode and scan the tables for it
;
; Apparently, EnteredMnemonic is a buffer used to contain a mnemonic string. 
;	My guess is that RepPfxBitmap is set if we find a repeat prefix?
;	Anyway, we stick everything into the buffer until we reach space
;	or below, or a colon.  If a colon (which was also stored), we back 
;	esi up to point to that colon, else we don't back up SI. Then we
;	stick the last char we stored into lastbyte. Then we stick a
;	0 terminator on the string we just stored.
;	  At this point, we look up the mnemonic in a table. It might appear
;	multiple times, so we build a list of appearances by address, and
;	a count of valid addresses in the list, and return these.
;
getcode	PROC
	mov	RepPfxBitmap,0	;possibly if we find a rep?
getcode3:
	mov	edi,offset EnteredMnemonic	;point to mnemonic buffer
getcode2:
	lodsb			;get input character
	cmp	al,' '		;see if space or below
	jbe	nomore		;if so, done, don't store
	stosb			;else store it
	cmp	al,':'		;was it a colon?
	je	nomore2		;if so, leave SI alone
	jmp	getcode2
nomore:
	dec	esi   	       	; all done, backtrack
nomore2:
	mov	ah,[edi-1]      	;get last char we stuffed in buffer
	mov	[lastbyte],ah	; last byte is used by some commands
				; string &c
	mov	al,0		; store the trailer
	stosb
	push	esi		;save where we left off in input string
	mov	esi,offset EnteredMnemonic	;point to buffer we just stuffed
	call	strlen		; length of name in buffer into AX
	inc	eax		; plus trailer
	mov	esi,offset EnteredMnemonic	; check for repeats and lock
	mov	edi,offset say_repne	;actual string 'repne'
	mov	ecx,eax		;length to compare
	repe	cmpsb		;see if a match, lowercase
	mov	bl,AF_REPNE	;this is 2
	jz	reps		;if a match, go to reps to stuff in RepPfxBitmap
	mov	esi,offset EnteredMnemonic	;else lets look for repe
	mov	edi,offset say_repe
	mov	ecx,eax
	repe	cmpsb
	mov	bl,AF_REPE	;this is 4
	jz	reps
	mov	esi,offset EnteredMnemonic
	mov	edi,offset say_rep	;just look for rep
	mov	ecx,eax
	repe	cmpsb
	mov	bl,AF_REP	;this is 1
	jz	reps
	mov	esi,offset EnteredMnemonic	
	mov	edi,offset say_lock	;look for lock
	mov	ecx,eax
	repe	cmpsb
	mov	bl,AF_LOCK	;this is 8
	jz	reps
	cmp	eax,4		;is the length 4 (including 0-terminator?)
	jnz	npf		;if not, go look it up
	cmp	[EnteredMnemonic+2],':'	;else maybe segment override, so check colon
	jne	npf		;not a colon, so go look it up
	mov	ax,word ptr [EnteredMnemonic]	;else, get 1st 2 chars in AX
	mov	edi,offset OverridePfxList	;point to string of possible prefixes
	mov	ecx,8		;there are 8, 2 of which I've never heard of
	repne	scasw		;see if any match
	jnz	npf		;if not, go look it up the hard way
	bts	[PrefixBitmapWord],cx	;set prefix word bit for this prefix
	pop	esi		;back to our input line
	call	WadeSpace	;find next string
	jnz	getcode3	;got one, so start over
	stc			;else we failed???
	ret
npf:
	mov	esi,offset EnteredMnemonic	;point to buffer containing instruction
	call	LookupOpName	;and go look it up
	pop	esi		;restore SI ptr to next input
	jc	gcx		; get out if nonexistant
	call	WadeSpace	; see if any more...
gcx:
	ret
reps:
	pop	esi		;restore pointer to input
	or	RepPfxBitmap,bl	;set bitmap for rep prefix found
	call	WadeSpace	;find next
	jnz	getcode3	;if more, parse that
	stc			;else invalid - something must follow rep
	ret
getcode	ENDP
;
; exchange two opcodes if *di is preferable to *si
;
;
; INPUT: Unknown
; OUTPUT: Unknown
; PROCESSING: This routine is never called by anyone
;
sortone PROC
	mov	ebx,[edi]
	cmp	[ebx+OPCODE.OPERANDS],OP_ACCIMM
	jne	switchx
	mov	eax,[esi]
	xchg	eax,[edi]
	mov	[esi],eax
switchx:
	ret
sortone ENDP

;
; get an operand
;
; INPUT: DI points to ASMOP structure for this arg
;	esi points to input buffer past opcode string
; OUTPUT: CY if arg is invalid
; PROCESSING:
;	1) init ASMOP structure
;
parsearg	PROC
	mov	[edi+ASMOP.ASIZE],NOSIZE
	mov	[edi+ASMOP.AREG1],0FFh
	mov	[edi+ASMOP.AREG2],0FFh
	mov	[edi+ASMOP.ASCALE],1
	mov	[edi+ASMOP.ADDRX],0
	mov	[edi+ASMOP.MODE],AM_NONE
	mov	[edi+ASMOP.MSIZE],0
	call	WadeSpace      		;see if any more???
	jz	gax	 		; comma taken care of by WadeSpace
	cmp	byte ptr [esi],'['	;see if opening an indirect addr
	je	getbrack		;if so, look for contents
	call	parsecontrol		;else chk for control register
	jc	gax			;error, bad ctrl reg
	jz	gaxc			;good ctrol reg, we got it
	call	parsesize		;set width, 1-10
	jc	gax			;bad width, bomb
	jz	getbrack		;else found width, get inside bracket
	call	parseseg		;else check for segment arg
	jc	gax			;bad seg arg
	jz	gaxc			;if good one, find more
	js	getbrack		;if SF, seg is inside brackets
	call	parsereg		;so find register
	jz	gaxc			;got it, find mire
	mov	[edi+ASMOP.MODE],AM_NONE	;assume it is just a number
	call	parseval		;look for an immediate
	jc	gax			;nope, bomb
	mov	[edi+ASMOP.MODE],AM_IMM	;else say it is an immediate
	mov	[edi+ASMOP.ADDRX],ebx	;so save that
	call	WadeSpace  		;find next
	cmp	al,':'			;a colon?
	jnz	gaxc			;if not, done
	inc	esi			;else move past colon
	call	parseval		;and get target value
	jc	gax			;sorry, no value found
	mov	[edi+ASMOP.ADDRX2],ebx	;else stash the value in addrx2

;Aha, I think I dig. This flag indicates that addrx1 contains a segment
;value and addrx2 has the offset.  If this flag is clear, addrx1 has an
;offset and addrx2 is inoperative.

	mov	[edi+ASMOP.MODE],AM_SEGOFFS	;set flag
gaxc:
	call	WadeSpace

;Now why do you suppose we'd jump around the CLC if ZF. The ZF got set by
;comparing AL with 0Dh in WadeSpace.  Therefore if ZF is true, NC MUST
;be true.  There is NO WAY that we could have ZF and CY both.  So what
;the hell is going on here?

	jz	gax
	clc
gax:
	ret

;Handle the case where we have something in brackets.
;SI points to the opening bracket character


getbrack:
	lodsb			;consume the bracket
	mov	al,[edi+ASMOP.AREG1] ;see if any segment reg
	cmp	al,0ffh
	jz	brklp		; none
	mov	[edi + ASMOP.AREG1],0ffh ; set it back
	sub	ah,ah
	bts	[PrefixBitmapWord],ax	; set the prefix bit
brklp:
	call	WadeSpace	;get next string
	jz	brackerr	;found CR before closing bracket
	cmp	al,'+'		;see if addition
	jne	brnp		;might mean bracket-not-plus
	inc	esi		;else move past plus sign
	call	WadeSpace	;and find next
	jz	brackerr	;oops, no closing bracket

;We have ignored any plus sign if it was there

brnp:
	cmp	al,'-'		;is it a minus sign?
	je	brmem		;if so, must be a value?
	cmp	al,']'		;if not, closing bracket already?
	je	brackx		;if so, go here and do ???
	push	edi		;save pointer to buffer
	mov	edi,offset arg4	;point to arg4 for base-mode reg gathering
	call	parsereg	;see if a register?
	pop	edi		;restore buffer pointer

;parsereg cannot return CY, and does no bracket check anyway. No idea why 
;this line is even here

	jc	brackerr	;not at all clear yet what this means
				; until I examine parsereg. Certainly the
				; carry is not set by any part of parsereg
				; in any straightforward manner
	jz	brreg		;ZF means we found a register
brmem:
	call	parseval		;glom a number into EBX
	jc	brackerr		;cannot return CY in any circumstance
	add	[edi+ASMOP.ADDRX],ebx	;stick in as offset or segment
	call	WadeSpace		;get next
	jz	brackerr		;still no closing bracket
	jmp	brklp			;else get next bracket value

;We get here if we found a named register inside the brackets, like [ebx

brreg:
	mov	[edi+ASMOP.MODE],AM_BASED	;say base reg involved
	mov	ah,[arg4.AREG1]		;get which register it is
	mov	bl,[arg4.ASIZE]		;and width of register
	test	[edi+ASMOP.MSIZE],0FFh	;see if anything assigned yet
	jz	notszyet		;nope, not yet
	cmp	[edi+ASMOP.MSIZE],bl	;ok, bl is the size
	jnz	brackerr		;mismatch, I guess???
notszyet:
	mov	[edi+ASMOP.MSIZE],bl	;else set the size
	call	WadeSpace
	cmp	al,'*'			;multiply operation
	jne	notscale		;nope, no scaling
	cmp	bl,DWORDSIZE		;else dword scaling?
	jne	brackerr		;must be dword reg for multiply?
	inc	esi			;move past *
	call	WadeSpace		;find next
	sub	al,'0'			;last char returned, cvt to decimal?
	cmp	al,TIMES1		;*1 is OK
	je	brackok1
	cmp	al,TIMES2		;*2 is OK
	je	brackok1
	cmp	al,TIMES4		;*4 is OK
	je	brackok1
	cmp	al,TIMES8		;*8 is OK
	jne	brackerr		;else, can't do it
brackok1:
	inc	esi			;bump SI past scaling factor
	mov	byte ptr [edi+ASMOP.ASCALE],al	;and set factor in struct
reg2x:
	test	[edi+ASMOP.AREG2],0FFh	;initialized to FF
	jns	brackerr		;so bit 7 better be set
	mov	[edi+ASMOP.AREG2],ah	;if so stick areg1=reg into it
	jmp	brklp			;get next thing inside brackets

;Found a register that was NOT followed by a scaling factor. The magic code
;for the found register is in AH. We stick this register into areg1 unless
;areg1 is already in use, in which case we stick it in areg2.
;  It would be MUCH clearer to test with 80h and jz

notscale:
	test	[edi+ASMOP.AREG1],0FFh	;has reg been assigned yet?
	jns	reg2x			;if not, stick reg into areg2
	mov	[edi+ASMOP.AREG1],ah	;else, stick reg into areg1
	jmp	brklp

;OK, we found the closing bracket.  Presumably everything between brackets
;was kosher. We also get here with the construct [], with nothing in there.

brackx:
	cmp	[edi+ASMOP.MSIZE],WORDMODE	;addressing mode size=32?
	jne	brackn32			;if not, skip
	or	[PrefixBitmapWord],AS_ADDRSIZE	;else set flag for this
brackn32:
	inc	esi				;move past ]
	cmp	[edi+ASMOP.MODE],AM_NONE		;see if empty
	Jne	gaxc				;if not, cool
	mov	[edi+ASMOP.MODE],AM_MEM		;else set mode to memory
	mov	[edi+ASMOP.MSIZE],DWORDMODE	;see if word mode
	test	[edi+ASMOP.ADDRX],NOT 0FFFFH	;see if any address
	jnz	gaxc				;if not, skip out
	or	[PrefixBitmapWord],AS_ADDRSIZE	;else set for ???
	mov	[edi+ASMOP.MSIZE],WORDMODE	;and say dword assumed??
	jmp	gaxc				;and jmp
brackerr:
	stc
	ret
parsearg	ENDP

; Parse possible control register reference
;
; INPUT: SI points to buffer containing this argument (a string)
; OUTPUT: For this arg, the mode and areg1 fields of the structure are
;	filled in.
;	NZ if we can't recognize the register
;	CY if index for register is out of range
; PROCESSING: Look for any control reg, debug reg, FP reg or TR reg. The
;	TR regs are the Appendix H test registers.
;		Beyond this, we allow up to 8 of each of the ST, CR and DR
;	even though there is no DR1 or DR2, nor any CR4,5,6 or 7. I guess
;	these exist in opcode space, just not in the CPU!
;
parsecontrol	PROC
	mov	ax,word ptr [esi]
	mov	cl,AM_CR
	cmp	ax,"rc"			;Control register CRx
	je	gotcontrol
	mov	cl,AM_DR
	cmp	ax,"rd"			;Debug register DRx
	je	gotcontrol
	mov	cl,AM_TR
	cmp	ax,"rt"			;Test registers, Appendix H
	je	gotcontrol
	cmp	ax,"ts"			;ST(x for FP
	je	gotfpreg
	or	al,1
	ret

;Um. There are 8 each of the debug registers and FP stack registers, although
;DR1 and DR2 don't exist.  There are only 4 control registers, and I can't
;find any reference at all to any TRx registers.


gotcontrol:
	lodsw				;grab control reg 1st 2 chars
	lodsb				;and number
	sub	al,'0'			;convert to binary
	jc	gcerrx			;oops, below 0
	cmp	al,8			;see if register 8 or above
	jae	gcerrx			;error if so
	mov	[edi+ASMOP.MODE],cl	;save CL 
	mov	[edi+ASMOP.AREG1],al	;save which one
	sub	eax,eax			;set ZF
	ret
gcerrx:
	stc     
	ret
gotfpreg:
	lodsw				;consume the 'ST'
	call	WadeSpace		;find next 
	cmp	al,'('			;should be this
	jne	asmgotch		;if not, check other
	inc	esi			;bump past (
	call	WadeSpace		;find next
	push	eax			;stack the char found
	inc	esi			;move past it
	call	WadeSpace		;find the next
	cmp	al,')'			;end of stack reference?
	pop	eax			;restore char between ()
	jne	badfpreg		;no close, so bitch

;We get here in two cases: we found (x), or we didn't find (
;In the first case, x is in AL, in the second, AL has what we found instead
;By implication, we accept either ST(x) or STx

asmgotch:
	inc	esi			;move to next position anyway
	sub	al,'0'			;convert to binary
	jc	badfpreg		;must be some number
	cmp	al,8			;else see if in range
	jae	badfpreg		;if >=8, out of range
	mov	[edi+ASMOP.MODE],AM_FPREG	;set flag for FP
	mov	[edi+ASMOP.AREG1],al	;save binary value of this reg
	sub	eax,eax			;and return ZF
	ret
badfpreg:
	stc
	ret
parsecontrol	ENDP

;
; INPUT: SI points to buffer containing this argument (a string)
; OUTPUT:
; PROCESSING:
;
parsesize	PROC
	mov	ax,[PrefixBitmapWord]	;get bitmap into AX
	call	isbyte			;chk buffer for 'byte'
	mov	bl,BYTESIZE		;assume byte size
	jz	gotsize			;if match, fine
	call	isword			;else chk for word
	mov	bl,WORDSIZE		;assume it was
	jz	gotsize			;if so, fine
	inc	esi			;go past first char (could be t)
	call	isbyte			;and check if it was 'tbyte'
	jnz	notbyte			;if not, really not a byte
	cmp	byte ptr [esi-1],'t'	;else, was it a t
	mov	bl,TBYTESIZE		;assume it was a tbyte
	jz	gotsize			;yes, it was
	or	bl,bl			;else return NZ
	ret

;OK, it's not 'byte', 'word' or 'tbyte'. Maybe it's 'dword'. To get here,
;SI has been moved past the first char, so see if it is dword, qword'or fword

notbyte:
	call	isword			;check for xword
	jnz	notsize			;nope, not it
	mov	al,[esi-1]		;else get that x
	cmp	al,'d'			;was it dword
	mov	bl,DWORDSIZE		;assume it was
	je	gotsize			;yep, got it
	cmp	al,'q'			;was it qword
	mov	bl,QWORDSIZE		;assume it was
	je	gotsize			;yep, that's it
	cmp	al,'f'			;how about fword
	mov	bl,FWORDSIZE		;assume that
	je	gotsize			;yes
notsize:
	dec	esi			;back to beginning of string
	or	bl,1			;set NZ
	ret
gotsize:
	mov	[edi+ASMOP.ASIZE],bl	;set requested size
	add	esi,4			;move past string in buffer

;By implication here, it is legal to type 'ptr' as many times as you want.
;You can say mov word ptr ptr ptr ptr ptr [44],5

gs2:
	call	WadeSpace		;find next nonspace
	jz	operrx			;didn't find, so error
	cmp	al,'['			;'ptr' is optional, so chk bracket
	je	opok			;if so, fine
	push	esi			;else look for 'ptr'
	push	edi
	mov	ecx,3
	mov	edi,offset say_ptr
	repe	cmpsb
	pop	edi
	pop	esi
	clc
	jnz	opok			;if not, we're ok
	add	esi,3			;else skip wasted 'ptr'
	jmp	gs2			;
operrx:
	stc
opok:
	ret
parsesize	ENDP

;
; INPUT: SI points to buffer containing this argument (a string)
; OUTPUT: ZF if the argument is 'byte' NZ otherwise
; PROCESSING: Simply compare
; NOTE: AX must be preserved
;
isbyte 	PROC
	push	esi
	push	edi
	mov	ecx,4
	mov	edi, offset say_byte
	repe	cmpsb
	pop	edi
	pop	esi
	ret
isbyte	endp
;
; INPUT: SI points to buffer containing this argument (a string)
; OUTPUT: ZF if we matched 'word', else NZ
; PROCESSING: just compare
;	eax cannot be modified
;
isword 	PROC
	push	esi
	push	edi
	mov	ecx,4
	mov	edi, offset say_word
	repe	cmpsb
	pop	edi
	pop	esi
	ret
isword	endp

;
; INPUT: SI points to buffer containing this argument (a string)
; OUTPUT: CY if bad segment argument
;	ZF if good segment argument on its own
;		In this case, mode and size are set
;	SF if valid segment is followed by [
; PROCESSING: 
;	1) See if string matches list of seg names, return NZ if not
;	2) See if segment is only arg (like mov ax,cs. If so, return ZF
;	3) Else, see if it is seg:[ and if so, return SF
;	4) Else return carry, bad segment argument
;
parseseg	PROC
	mov	ecx,6	  		;6 possible segments by name
	push	edi			;save ptr to struct
	mov	edi,offset psegs		;names of registers
	lodsw				;get what user entered
	repne	scasw			;find it in the strings list
	pop	edi			;restore struct ptr
	jz	gotseg			;found a match
	sub	esi,2			;not a seg, undo the lodsw
	ret				;return NZ

;Here we have a significant departure from debug.exe syntax.  If you want
;a segment override in debug, you must put the override like CS: or ES:
;on a separate line, and the remainder on the next line.  Debug requires you
;to say:
;xxxx:xxxx cs:
;xxxx:xxxx mov al,[44]
;
;Here you are NOT allowed to do this.  Instead, you must enter:
;xxxx:xxxx mov al,cs:[44]

gotseg:
	sub	ecx,6			;sub starting value
	not	ecx			;convert to index
	mov	[edi+ASMOP.AREG1],cl	;save that index in areg1
	call	WadeSpace		;find next non-0
	jz	segalone		;if nothing, fine, just seg
	cmp	al,':'			;else chk for colon
	jne	segalone		;if not, we just got the segment
	inc	esi			;bump past colon
	call	WadeSpace		;find next stuff
	jz	segerr			;nothing else is error
	cmp	al,'['	 		;do we have a fixed address next?
	jne	segerr			;if not, real trouble
	or	al,80h			;else return SF
	ret
segalone:
	mov	[edi+ASMOP.MODE],AM_SEG	;say mode is just a segment
	mov	[edi+ASMOP.ASIZE],WORDSIZE	;so size is 2 (all segregs are word)
	sub	eax,eax			;return ZF
	ret
segerr:
	stc				;error so return CY
	ret
parseseg	ENDP

;
; INPUT: SI points to input being parsed
;	edi points to structure for this argument, or to arg4 if we are
;	inside brackets.
; OUTPUT: ZF if reg found, and mode and size set
;	NZ if not a name
; PROCESSING: parse for all register names, set size and mode if found any,
;	else return NZ if not found
;NOTES:
;	1) If a register name was found, SI was bumped past it, else SI
;	   remained unmodified
;	2) At least one caller to this routine checks the carry. I can't
;	   find anything in here that would set or clear the carry as a
;	   return value...	
;
parsereg	PROC
	mov	bl,2			;set word size
	cmp	byte ptr [esi],'e'	;is first char an e
	jne	nextreg			;if not, fine
	mov	bl,4			;else set dword size
	inc	esi			;and move past the e
nextreg:
	lodsw				;get next 2 bytes
	mov	ecx,16			;there are 16 2-char strings for regs
	push	edi			;save di
	mov	edi,offset regs		;to scan list of reg names
	repne	scasw
	pop	edi
	jz	gotreg			;if ZF, we hit a match
	sub	esi,2			;else back to beginning of input
	cmp	bl,4			;unless we moved past an e
	jnz	nextreg2		;if bl=4, we did
	dec	esi			;so back up to first char
nextreg2:
	or	esi,esi			;set NZ
	ret				;since not a reg

;It just so happens that the regs string has 16 2-char entries, the first 8
;are byte registers and the last 8 are word registers

gotreg:
	xor	cl,15			;really, how about 0Fh?
	mov	[edi+ASMOP.ASIZE],BYTESIZE	;say byte arg
	cmp	ecx,8			;if found in positions 0-7
	jb	gr1			;then it was a byte
	mov	[edi+ASMOP.ASIZE],bl	;else word or dword depending on e
gr1:
	and	cl,7		   	;get mod8 for index
	mov	[edi+ASMOP.AREG1],cl	;and save that
	mov	[edi+ASMOP.MODE],AM_REG	;say a reg asked for by name
	sub	eax,eax			;return ZF
	ret
	
parsereg	ENDP
;
; Make sure that the next string is a number, and return in EBX if so
;
; INPUT: SI points to string to be parsed
; OUTPUT: NC if we find a number
;	EBX contains that number (0=extended if required)
;	CY if not a number, or not found???
; PROCESSING: DI points to the argument structure being built, and must
;	be preserved.
;NOTES: ReadNumber simply assumes that all bytes in the input buffer are
;	hex values up to some special character like space, comma, colon
;	or CR. So ReadNumber cannot return an error. In this case, we
;	already know that we have no named register at [esi], which is
;	a good thing, because if we did, ReadNumber would return the
;	current contents of that logical register.
;
parseval	PROC
	call	WadeSpace	;find arg string
	jz	noval		;oops, not there
	push	edi		;else save location
	call	ReadNumber	;read a number into eax
	pop	edi		;restore pointer
	mov	ebx,eax		;return number in ebx
	ret
noval:
	stc
	ret
parseval	ENDP
end