title	ldebeval Debug Expression Evaluator
;	SCCSID = @(#)ldebeval.asm	11.1 87/10/29

.sall
.xlist
.xcref
include ldebequ.inc
.list
.cref

include ldebseg.inc

ArgSlen =   64				; Length of Arg Stack in bytes

DStyp	=   0				; type String	 (0)
DBtyp	=   1				; type Byte	 (1)
DWtyp	=   2				; type word	 (2)
DLtyp	=   3				; type Long Word (3)
DDtyp	=   4				; type Dbl  Word (4)


DBDATA	segment
	public	_AtomBuf

	extrn	_pTXT:word
	extrn	_SBRTAB:byte

	extrn	_REGTAB:byte
	extrn	_RegSA:word

DEFLEN		dw	?
GotArg		db	?		;non-zero if arg on stack
pTXTopr		dw	?		;ptr to text of last opr encountered
ArgSptr		dw	?		;Current arg stack ptr
ArgSbot	    label   byte
		db	ArgSlen Dup (?) ;room for some args
ArgStop	    label   byte

ValType		db	?		;Value Type
DBvalue	    label   byte
DWvalue	    label   word
		db	?		;Byte (1)	 part of value
DLvalue	    label   word
		db	?		;Word (2)	 part of value
DDvalue	    label   word
		db	?		;Long word (3)	 part of value
		db	?		;Double word (4) part of value

Itemp4	    label   word
		dd	?		;DWord Temp for Decimal conversion

_AtomBuf  	db	64+1 dup (?)	; len of symbol name, symbol name

Opr_frame   struc
	Oprec	db	?
	Opadr	dw	?
	Oplen	db	?
	Opnam	db	?
Opr_frame   ends

opr	Macro	prec, action, len, optext
	db	prec		    ;; Operator Precedence
	dw	IGROUP:action	    ;; Operator Action routine addr
	db	len		    ;; Operator text length
	db	optext		    ;; Operator (text)
	Endm

; Operator and Operator precedence table

; Odd  precedence byte indicates unary	operator (u)
; Even precedence byte indicates binary operator (b)

OprTab	label byte	       ;Type Description
			       ;---- ------------------------------------
opr	 6,AddOpr,1,"+"		;b  Word addition
opr	 6,SubOpr,1,"-"		;b  Word subtraction
opr	10,MulOpr,1,"*"		;b  Word multiplication.
opr	10,DivOpr,1,"/"		;b  Word division, the result is the word
				;   quotient.  the remainder (if any) is lost.
opr	16,ColOpr,1,":"		;b  Segment:Offset connector for segmented
				;   addresses. (eg. 1234:5678 or CS:IP)
				;   Note: FOO+5:100 evaluates as 5:100+FOO

opr	10,MODopr,3,"MOD"	;b  Modulo reduction.  The remainder
				 ;   after division expressed as a word.
opr	 2,ANDopr,3,"AND"	;b  Bitwise AND (word).
opr	 2,XORopr,3,"XOR"	;b  Bitwise exclusive OR  (word).
opr	 2,ORopr, 2,"OR"	;b  Bitwise inclusive OR  (word).

OpUnary label byte		;u   Unary minus, (-n) means (65535-n).
	opr 13,NegOpr,1,"-"	;    (2's complement of n,modulo 2^16).

opr	 3,NOTopr,3,"NOT"	;u  Unary logical 1's complement (word).

opr	15,SEGopr,3,"SEG"	;u  Returns a word that is the Segment portion
				;   of a pointer.  (eg. SEG 1234:5678 is 1234).
opr	15,OFFopr,3,"OFF"	;u  Returns a word that is the Offset portion
				;   of a pointer.  (eg. OFF 1234:5678 is 5678).
opr	15,BYopr, 2,"BY"	;u   1 byte value from the addressed location.
opr	15,WOopr, 2,"WO"	;u   2 byte value from the addressed location.
opr	15,DWopr, 2,"DW"	;u   4 byte value from the addressed location.
opr	15,POIopr,3,"POI"	;u   4 byte value from the addressed location.

opr	15,INopr, 4,"PORT"	;u   1 byte value from the addressed  8 bit I/O port.
opr	15,INWopr,5,"WPORT"	;u   2 byte value from the addressed 16 bit I/O port.

opr	15,REAopr,4,"REAL"	;u*  4 byte floating number from the addressed location.
opr	15,DREopr,5,"DREAL"	;u*  8 byte floating number from the addressed location.
opr	15,TREopr,5,"TREAL"	;u* 10 byte floating number from the addressed location.
;* (No other operation may be performed upon a floating value)

OPend	db	 0
	dw	IGROUP:_PERROR

; OPLpar is special and need not be in OprTab

OPLpar	db	15		; Left Paren "("
	dw	IGROUP:ExpRet

DBDATA	ends


DBCODE	segment

	public	_DSRANGE, _DEFAULT
	public	_ADDRESS, _GET_ADDRESS
	public	_ScanAtom
	public	_GetWord, _GetByte
	public	_Dec16Out
	public	_HEXCHK

	extrn	_OUT:near
	extrn	_ScanP:near, _SkipB:near, _GetChr:near, _GETEOL:near
	extrn	_PERR:near, _PERROR:near


;  Display 16 or 32 bit integers

; _Dec16Out displays 16 bit integers in [DX].
; Dec32Out displays 32 bit integers in [AXDX].
; All regs saved except [AX] in _Dec16Out case.

Ten9	dd    1000000000
Ten8	dd     100000000
Ten7	dd	10000000
Ten6	dd	 1000000
Ten5	dd	  100000
Ten4	dd	   10000
Ten3	dd	    1000
Ten2	dd	     100
Ten1	dd	      10

_Dec16Out:
	xor	ax,ax			;zeros upper word of dword.
;;Dec32Out:
	mov	[Itemp4+2],ax		;store upper half of dword.
	mov	[Itemp4],dx		;store lower half of dword.
DecOut:
	push	ax
	push	bx
	push	dx
	push	si
	mov	dh,0			;Set Zero suppress flag.
	mov	si,offset cs:Ten4	;Point at 10^4
	cmp	word ptr [Itemp4+2],0
	jz	bdec3			;Brif only 16 bit number
	mov	si,offset cs:Ten7	;Point at 10^7
	cmp	byte ptr [Itemp4+3],0
	jz	bdec1			;Brif only 24 bit number
	mov	si,offset cs:Ten9	;Point at 10^9
	call	bdecq			;x,000,000,000 position.
	or	dh,dh			;See if comma needed.
	jz	bdec0			;No, skip ahead alittle
	mov	al,","
	call	_OUT
bdec0:
	call	bdecq			; x00,000,000 position.
bdec1:
	call	bdecq			;  x0,000,000 position.
	call	bdecq			;   x,000,000 position.
	or	dh,dh			;See if comma needed.
	jz	bdec2			;No, skip ahead alittle
	mov	al,","
	call	_OUT
bdec2:
	call	bdecq			;     x00,000 digit.
bdec3:
	call	bdecq			;      x0,000 digit.
	call	bdecq			;	x,000 digit.
	or	dh,dh			;See if comma needed.
	jz	bdec4			;No, skip ahead alittle.
	mov	al,","
	call	_OUT
bdec4:
	call	bdecq			;	  x00 digit.
	call	bdecq			;	   x0 digit.
	mov	ax,[Itemp4]
	add	al,"0"			;Convert last digit to ascii.
	call	_OUT			;And send it.
	pop	si
	pop	dx
	pop	bx
	pop	ax
	ret

; 24 bit subtraction, enter with [Itemp4+2] = 16 hsw of counter.
;				 [Itemp4]   = 16 lsw of counter.
; SI points to 4 byte divisor.

bdecq:
	mov	dl,255			;Set digit count.
	mov	ax,CS:[si]		;[AX]= low 16 of divisor
	mov	bx,CS:[si+2]		;[DX]= Hi  16 of divisor
	add	si,4			;Point at next smaller power of ten
	push	si
bdeq1:
	inc	dl			;Incr. digit counter.
	sub	[Itemp4],ax
	sbb	[Itemp4+2],bx
	jnb	bdeq1			;Go until no carry.
	add	[Itemp4],ax		;Add back in overflow.
	adc	[Itemp4+2],bx
bdeq3:	or	dl,dl			;See if zero there.
	jnz	bdeq4			;No, then output it.
	or	dh,dh			;See if zero suppress active.
	jz	bdeq5			;Yes, then don't print anything.
bdeq4:
	mov	dh,1			;Set flag.
	mov	al,dl
	add	al,"0"			;Convert to ascii.
	call	_OUT			;Print digit.
bdeq5:
	pop	si			;Power of 10 pointer
	ret


; RANGE - Looks for parameters defining an address range.
; The first parameter is the starting address. The second parameter
; may specify the ending address, or it may be preceded by
; "L" and specify a length (4 digits max), or it may be
; omitted and a length of 128 bytes is assumed. Returns with
; segment in AX, displacement in DX, and length in CX.

_DSRANGE:
	MOV	BP,[_RegSA.DSSAV]	; Set default segment to DS
	MOV	[DEFLEN],128		; And default length to 128 bytes
RANGE:
	CALL	_ADDRESS
	PUSH	AX			; Save segment
	PUSH	DX			; Save offset
	MOV	DX,[DEFLEN]		; Default length
	CALL	_ScanP			; Get to next parameter
	JE	GetDef			; If eol, use default
	CMP	AL,"L"			; Length indicator?
	JE	GETLEN
	CALL	_GetWord		; Get ending address (same segment)
	MOV	CX,DX			; Low 16 bits of ending addr.
	POP	DX			; Low 16 bits of starting addr.
	SUB	CX,DX			; Compute range
	JAE	DSRNG2
DSRNG1: JMP	_PERROR			; Negative range
DSRNG2: INC	CX			; Include last location
	JCXZ	DSRNG1			; Wrap around error
	POP	AX			; Restore segment
	RET
GetDef:
	POP	CX			; get original offset
	PUSH	CX			; save it
	NEG	CX			; rest of segment
	JZ	RngRet			; use default
	CMP	CX,DX			; more room in segment?
	JAE	RngRet			; yes, use default
	JMP	RngRet1			; no, length is in CX

GETLEN:
	CALL	_SkipB			; Skip over "L" to length
	CALL	_GetWord		; Get the length
RngRet:
	MOV	CX,DX			; Length
RngRet1:
	POP	DX			; Offset
	MOV	AX,CX
	ADD	AX,DX
	JNC	OKRET
	CMP	AX,1
	JAE	DSRNG1			; Look for wrap error
OKRET:
	POP	AX			; Segment
	RET

_DEFAULT:
; DI points to default address and CX has default length
	CALL	_ScanP
	JZ	USEDEF			; Use default if no parameters
	MOV	[DEFLEN],CX
	CALL	RANGE
	JMP	_GETEOL
USEDEF:
	MOV	SI,DI
	LODSW				; Get default displacement
	MOV	DX,AX
	LODSW				; Get default segment
	RET

; _Get_Address - Gets an address in Segment:Displacement format.
;		Segment may be omitted and a default (kept in BP)
;		will be used, or it may be a segment register
;		(DS, ES, SS, CS).
;   Returns with segment:offset in [AX:DX]

SYNERR:
	JMP	_PERROR

_ADDRESS:
_GET_ADDRESS:
	PUSH	BP
	CALL	Expr
	POP	BP
	CMP	CL,DDtyp
	JE	GetAexit
	MOV	AX,BP
	JMP	SHORT GetAexit

GetDword:
	CALL	_GetWord
	JMP	SHORT GetAexit

_GetWord:
	CALL	Expr
	JMP	SHORT GetAexit

_GetByte:
	CALL	Expr
	CMP	word ptr [DWvalue],256
	JB	GetAexit
	JMP	_PERROR
GetAexit:
	CLC			    ;No errors here
	RET


;	Expression Evaluator
; Entry:    [SI]= Text pointer
; Exit:	    [SI]= Text pointer
;	    [AX]= Segment value
;	    [DX]= Offset  value
;	    [CL]= Value	  Type
;
;	    [ValType] = Value type
;	    [DBvalue ... DTvalue] = value

Expr:
	PUSH	BX
	PUSH	DI
	XOR	AX,AX
	MOV	DI,Offset ds:ValType
	MOV	CX,4
	CLD
	REP	STOSW			;Zero type and Accum
	CALL	Exp			; Evaluate something
	CALL	PopArg			; Get answer
	MOV	[DDvalue],AX
	MOV	[DWvalue],DX
	MOV	[ValType],CL
	POP	DI
	POP	BX
	RET
Exp:
	MOV	[ArgSptr],Offset ds:ArgSbot ;Reset arg stack
ExprA:
	MOV	BX,Offset ds:OPend
ExprB:
	PUSH	BX			;Opr preced/Action ptr
	MOV	[GotArg],0		;No arg on stack yet
Expr1:
	CMP	[GotArg],0
	JNZ	Expr2			;Brif got arg, get opr
Expr1A:
	CALL	GetArg			;Put arg on Stack (TC if not arg)
	JNB	Expr2			;brif arg
	CALL	TestOp			;May be Opr
	JNB	Xop			;Brif was opr
	CALL	_GetChr			;Skip fill
	CMP	AL,"("
	JNE	Expr2
	MOV	BX,Offset ds:OPLpar	;Recursing
	PUSH	BX
	CALL	_SkipB			;Eat "("
	CALL	ExprA			;Evaluate
	CMP	AL,")"			;must be ")"
	JNE	Xerror			;else error
	CALL	_SkipB			;Eat ")"
	POP	BX
	JMP	SHORT Xop2
Expr2:
	CALL	ScanOp			;[BX]= op adr, TC if illegal operator
	JNB	Xop			;Got operator, go execute
ExpEnd:
	POP	BX			;Op prec ptr
ExpRet:
	RET				;Evaluated

Xop:
	CMP	[GotArg],0
	JNE	Xop1			;Brif got arg, else must be unary op
	CMP	AL,"+"
	JNE	XopM			;Not unary plus, try unary minus
	CALL	_SkipB			;Eat "+"
	JMP	Expr1A			;Start over
XopM:
	CALL	TestOpU			;Must be unary opr
	JNB	Xop1A			;Brif was
Xerror:
	JMP	_PERROR
Xperr:
	JMP	_PERR

Xop1:
	CALL	TestOp			;Must be binary Opr
	JB	Xerror			; else error
Xop1A:
	POP	SI
	MOV	AL,[SI]			;[AL]= previous precedence
	PUSH	SI
	CMP	AL,[BX]			;Old preced less than new preced?
	JB	Xop1H			;Brif Higher precedence
	MOV	AX,[pTXTopr]		;Restore _pTXT to start
	MOV	[_pTXT],AX		; of last opr
	JMP	ExpEnd			;Do same or lower
Xop1H:
; else Increasing precedence case
	PUSH	BX			;Save new precedence
	CALL	ExprB			;Recurse
	POP	BX			;get curr precedence
Xop2:
	MOV	AX,Offset cs:Expr1
	PUSH	AX			;will continue eval with next arg fetch
	CALL	PopArg			;(arg 2) [AX:DX], [CL]= type
	MOV	[DDvalue],AX
	MOV	[DWvalue],DX
	MOV	[ValType],CL
	TEST	byte ptr [BX].Oprec,1	;Binary or Unary Op?
	JNZ	DoArith			;Brif Unary
	CALL	PopArg			;(arg 1) [AX:DX], [CL]= type
DoArith:
	CALL	CS:[BX].Opadr		;Perform Arith operation
	JMP	PshArg			;Result on Stack

AddOpr:
	ADD	DX,[DWvalue]
	RET

SubOpr:
	SUB	DX,[DWvalue]
	RET

MulOpr:
	PUSH	AX
	MOV	AX,DX
	MUL	[DWvalue]
	MOV	DX,AX
	POP	AX
	RET

DivOpr:
	CMP	[DWvalue],0
	JZ	Xerror			;Brif Div/0
	PUSH	AX
	MOV	AX,DX
	XOR	DX,DX
	DIV	[DWvalue]
	MOV	DX,AX
	POP	AX
	RET

ColOpr:
	MOV	AX,DX			;Seg = Arg 1 word val
	MOV	DX,[DWvalue]		;Off = Arg 2 word val
	MOV	CL,DDtyp		;Double Word
	RET

NegOpr:
	NEG	DX
	RET

MODopr:
	PUSH	AX
	MOV	AX,DX
	XOR	DX,DX
	DIV	[DWvalue]		;Remainder [DX] is word result
	POP	AX
	RET

NOTopr:
	NOT	DX
	RET

ANDopr:
	AND	DX,[DWvalue]
	RET

ORopr:
	OR	DX,[DWvalue]
	RET

XORopr:
	XOR	DX,[DWvalue]
	RET

SEGopr:
	MOV	DX,AX			;Return segment portion (word)
	XOR	AX,AX			;Clear Segment
	MOV	CL,DWtyp		;force type to word
	RET

OFFopr:
	XOR	AX,AX			;Clear Segment
	MOV	CL,DWtyp		;force type to word
	RET

BYopr:
	CALL	WOopr
BYopri:
	XOR	DH,DH
	MOV	CL,DBtyp		;force type to Byte
	RET

WOopr:
	CALL	DWopr
WOopri:
	XOR	AX,AX
	MOV	CL,DWtyp		;force type to Word
	RET

DWopr:
POIopr:
	PUSH	DS
	MOV	DS,AX			;segment
	MOV	BX,DX			;offset
	MOV	DX,[BX]			;Get the Offset
	MOV	AX,[BX+2]		;Get the Segment
	POP	DS
	MOV	CL,DDtyp		;force type to Double Word
	RET

INopr:
	IN	AL,DX			;Get 8 bit from PORT [DX]
	MOV	DX,AX
	XOR	AX,AX
	JMP	BYopri
INWopr:
	IN	AX,DX			;Get 16 bit from PORT [DX]
	MOV	DX,AX
	XOR	AX,AX
	JMP	WOopri

REAopr:
DREopr:
TREopr:
	RET



; TestOp  - Returns [bx]= opr preced ptr if operator else TC if not.
; TESTOpU - Same as TestOp only opr must be unary    else TC if not.

TestOpU:
	MOV	BX,Offset ds:OpUnary
	Call	TestOp1
	JB	TestOpx		    ;Brif no opr found
	TEST	[BX].Oprec,1
	JNZ	TestOpx		    ;Brif was Unary
TestOpE:
	STC
TestOpx:
	RET

ScanOp:
	PUSH	[_pTXT]
	MOV	AX,[_pTXT]
	MOV	[pTXTopr],AX	    ;Save ptr to start of opr
	CALL	_ScanAtom	    ;Scan Opr into _AtomBuf
	CMP	[_AtomBuf],0
	JNZ	ScanOp2		    ;Got something
	INC	[_AtomBuf]	    ;len = 1
	MOV	[_AtomBuf+1],AL	    ;Assume special char operator
	CALL	_SkipB		    ;Eat Operator
ScanOp2:
	CALL	TestOp
	MOV	AL,[_AtomBuf+1]	    ;Get possible operator
	POP	SI		    ;old txt ptr
	JNB	ScanOpx		    ;Was opr, keep new txt ptr
	MOV	[_pTXT],SI	    ;Restore old txt ptr
ScanOpx:
	RET

TestOp:
	MOV	BX,Offset ds:OprTab
TestOp1:
	MOV	DI,Offset ds:_AtomBuf
	XOR	CX,CX
	MOV	CL,[DI]		    ;[CX]= Len of opr to match
	INC	DI		    ;[DI]= Start of opr to match
TestOps:
	CMP	[BX].Oprec,0
	JE	TestOpE		    ;End of Table, not found
	CMP	[BX].Oplen,CL
	JNE	NextOpr		    ;len's don't match, try next
	LEA	SI,[BX].Opnam
	PUSH	CX
	PUSH	DI
	REPE	CMPSB		    ;Match?
	POP	DI
	POP	CX
	JE	TestOpx		    ;Brif so, return goodies
NextOpr:
	XOR	AX,AX
	MOV	AL,[BX].Oplen
	ADD	AX,SIZE Opr_frame-1
	ADD	BX,AX
	JMP	TestOps
page

PopArg:
	PUSH	SI
	MOV	SI,[ArgSptr]
	DEC	SI
	STD
	LODSB				    ;Pop type
	DEC	SI
	MOV	CL,AL
	LODSW				    ;Pop off
	MOV	DX,AX
	XOR	AX,AX
	CMP	CL,DLtyp
	JB	PopArg2
	LODSW				    ;Pop seg
PopArg2:
	cld
	INC	SI
	INC	SI
	MOV	[ArgSptr],SI
	POP	SI
PopArgx:
	RET

GetArg:
	CALL	GetArgWord		; Get a word in [DX]
	JB	PopArgx			;Brif not arg (may be opr or eos)
	CMP	CL,1
	JE	GotArgDD		; Brif offset only, [AX] has segment
	XOR	AX,AX
	MOV	CL,DWtyp		; else valtype = Word
	JMP	SHORT GotArgDW		; Done, [CL:DX]= type:value
GotArgDD:
	MOV	CL,DDtyp		; [CL](valtype)= Dword (Seg:Off)
GotArgDW:
	MOV	[GotArg],1		    ;Got arg
PshArg:
	PUSH	SI
	CLD
	MOV	DI,[ArgSptr]
	MOV	SI,DI
	ADD	SI,3
	CMP	CL,DLtyp
	JB	PshArg2
	ADD	SI,2
	STOSW				    ;Push seg
PshArg2:
	CMP	SI,Offset ds:ArgStop
	JB	PshArg3
	JMP	_PERROR
PshArg3:
	MOV	AX,DX
	STOSW				    ;Push off
	MOV	AL,CL
	STOSB				    ;Push type
	MOV	[ArgSptr],DI
	POP	SI
	CLC
	RET

GetArgWord:
	CALL	_ScanAtom		; Get an atom into _AtomBuf
	CMP	[_AtomBuf],0
	JZ	NotArg			; No arg if empty
	CALL	Numeric
	JB	GetArgW2		; Brif Numeric, get hex value
	CALL	TryRegister		; Get Register or symbol
	JNB	GetArgWfnd		; Brif was Reg ([CL:DX]= type:value)
	CALL	TestOp			; If not arg, maybe opr
	JNB	NotArg			; Brif was operator
	CALL	NUMTYPE			; Last chance before
	CMP	AL,"H"			; error is hex number
	JE	GetArgHex
	INC	CX			; Len +1 (no suffix)
GetArgHex:
	CALL	GETHEX			; If not symbol, assume hex
	JMP	Short GetArgWx
GetArgW2:
	CALL	GetArgW3
GetArgWx:
	JNB	GetArgWfnd		; Brif good hex number
	JMP	_PERROR
GetArgWfnd:
	CLC
	RET

GetArgW3:
	CALL	NUMTYPE
	CMP	AL,"Y"
	JE	GETBIN			; Binary
	CMP	AL,"T"
	JE	GETDEC			; Decimal
	CMP	AL,"Q"
	JE	GETOCT			; Octal
	CMP	AL,"O"
	JE	GETOCT			; Octal
	CMP	AL,"H"
	JE	GETHEXV			; Hex
	INC	CX			; Len +1 (no suffix)
GETHEXV:
	JMP	GETHEX			; Hex (default)

NUMTYPE:
	MOV	SI,Offset ds:_AtomBuf
	XOR	AX,AX
	MOV	DX,AX
	LODSB
	DEC	AX
	MOV	CX,AX
	MOV	BX,AX
	MOV	AL,byte ptr [SI+BX]
	RET

NotArg:
	STC
	RET

GETBIN:
	LODSB				; Get a digit
	CALL	NumCkAL			; see if decimal
	JNB	NotArg			; Brif not valid digit
	CMP	AL,"2"			; 0 or 1
	JNB	NotArg			; Brif not valid digit
	SUB	AL,"0"
	MOV	DL,AL			; First digit in position
GETBINL:
	DEC	CX			; Digit count
	JCXZ	RETHX			; All done if no more digits
	LODSB				; Get a digit
	CALL	NumCkAL			; see if digit
	JNB	NotArg			; Brif not valid digit
	CMP	AL,"2"			; 0 or 1
	JNB	NotArg			; Brif not valid digit
	SUB	AL,"0"
	RCR	AL,1			; isolate 0 or 1 in carry
	RCL	DX,1			; Shift into lsb
	JMP	GETBINL			; Get more digits

GETDEC:
	LODSB				; Get a digit
	CALL	NumCkAL			; see if decimal
	JNB	NotArg			; Brif not valid digit
	SUB	AL,"0"
	MOV	DL,AL			; First digit in position
GETDECL:
	DEC	CX			; Digit count
	JCXZ	RETHX			; All done if no more digits
	LODSB				; Get a digit
	CALL	NumCkAL			; see if digit
	JNB	NotArg			; Brif not valid digit
	SUB	AL,"0"
	MOV	BX,DX
	SHL	DX,1			; Multiply by 10
	SHL	DX,1
	ADD	DX,BX
	SHL	DX,1
	ADD	DX,AX			; and combine new digit
	JMP	GETDECL			; Get more digits

GETOCT:
	LODSB				; Get a digit
	CALL	NumCkAL			; see if decimal
	JNB	NotArg			; Brif not valid digit
	CMP	AL,"8"			; 0 -7
	JNB	NotArg			; Brif not valid digit
	SUB	AL,"0"
	MOV	DL,AL			; First digit in position
GETOCTL:
	DEC	CX			; Digit count
	JCXZ	RETHX			; All done if no more digits
	LODSB				; Get a digit
	CALL	NumCkAL			; see if digit
	JNB	NotArg			; Brif not valid digit
	CMP	AL,"8"			; 0 -7
	JNB	NotArg			; Brif not valid digit
	SUB	AL,"0"
	JCXZ	NotArg			; Too many digits?
	SHL	DX,1			; Multiply by 8
	SHL	DX,1
	SHL	DX,1
	OR	DL,AL			; and combine new digit
	JMP	GETOCTL			; Get more digits

GETHEX:
	LODSB				; Get a digit
	CALL	_HEXCHK			; see if hex
	JC	HXERR			; Must be one valid digit
	MOV	DL,AL			; First 4 bits in position
GET16HL:
	DEC	CX			; Digit count
	JCXZ	RETHX			; All done if no more digits
	LODSB				; Get a digit
	CALL	_HEXCHK			; see if hex
	JC	HXERR			; All done if no more digits
	SHL	DX,1			; Multiply by 16
	SHL	DX,1
	SHL	DX,1
	SHL	DX,1
	OR	DL,AL			; and combine new digit
	JMP	GET16HL			; Get more digits
RETHX:	CLC
	MOV	CX,0			; Size of arg is Word
	RET
HXERR:
	STC
	RET

Numeric:
	MOV	AL,[_AtomBuf+1]
NumCkAL:
	CMP	AL,"9"+1
	JNB	RET2
	CMP	AL,"0"
	CMC
	RET				;TC-Numeric, NC-Alpha

; Check if AL has a hex digit and convert it to binary if it
; is. Carry set if not.

_HEXCHK:
	SUB	AL,"0"			; Kill ASCII numeric bias
	JC	RET2
	CMP	AL,10
	CMC
	JNC	RET2			; OK if 0-9
	AND	AL,5FH
	SUB	AL,7			; Kill A-F bias
	CMP	AL,10
	JC	RET2
	CMP	AL,16
	CMC
RET2:	RET

; TryRegister	Return a registers value in [DX] if reg
;		otherwise, just return.
; Entry:  _AtomBuf contains text to try
; Exit:
;	  [CL] = Type, 0 = Word, 1 = DWord
; Flags:
;	  TZ if was reg ([CL,AX:DX]= type,value)
;	  NZ if symbol
;   Uses:   AX,CX,DX,SI.
;   Others preserved

SBRTAB_frame struc
	BrkFlg	db  ?		    ;Break flags, 1= Set, 2= Enable
	BrkCntr	dw  ?		    ;Repetition down  counter
	BrkReps	dw  ?		    ;Repetition count before Break
	BrkOff	dw  ?		    ;Break addr offset
	BrkSeg	dw  ?		    ;Break addr Segment
	BrkInst	db  ?		    ;Save instruction
	BrkCmdL	db  ?		    ;Breakpoint commands length
	BrkCmnd	db  32	Dup (?)	    ;Breakpoint commands
SBRTAB_frame ends

TryRegister:
	CMP	byte ptr [_AtomBuf],3	; May be BR0 .. BR9
	JNE	TryReg1			; Brif not
	MOV	BX,OFFSET ds:_SBRTAB
	CMP	word ptr [_AtomBuf+1],"RB" ; Must be BRn
	JNE	TryRegNF
	MOV	AL,[_AtomBuf+3]
	CALL	NumCkAL
	JNB	TryRegNF		;if not BR0 .. BR9
	SUB	AL,"0"			;if not BR0 thru BR9
	XOR	AH,AH
	MOV	CX,SIZE SBRTAB_frame
	MUL	CX
	ADD	BX,AX
	MOV	DX,[BX].BrkOff		;BRn addr offset
	MOV	AX,[BX].BrkSeg		;BRn addr Segment
	MOV	CX,1			;Dword result
	JMP	TryRegF
TryReg1:
	CMP	byte ptr [_AtomBuf],2
	JNE	TryRegNF
	PUSH	DI
	MOV	DI,OFFSET ds:_REGTAB
	MOV	CX,14
	MOV	AX,word ptr [_AtomBuf+1] ; [AX]= Reg to check
	CLD
	REPNE	SCASW			; Try matching all 14 regs
	POP	DI
	JNZ	TryRegNF		; Brif not reg
	OR	CX,CX
	JNZ	TryReg2
	INC	CX			; If last reg (PC) then make IP
TryReg2:
	MOV	AX,13
	SUB	AX,CX			; REGno = maxreg - counter
	SHL	AX,1
	PUSH	BX
	MOV	BX,AX			; [BX]= _REGTAB index
	MOV	DX,[BX+_RegSA.AXSAV]	; [DX]= Reg value
	POP	BX
	XOR	CX,CX			; Word result
TryRegF:
	CLC
	RET
TryRegNF:
	STC
	RET

; _ScanAtom- Parse a text string from [_pTXT] into _AtomBuf
;	    Parse terminates when character is not:
;		    "A-Z" (alpha)
;		    "0-9" (numeric)
;		    "?@_$" (special chars)
;   Entry:  [_pTXT] = pointer to src string
;   Exit:   [_pTXT] = points at terminator
;	    _AtomBuf[0] = len of string copied
;	    _AtomBuf[1 ..] =	 string copied
;	    AL	= character terminated on.  TZ if CR.
;   Uses:   AX, DI, SI.
;   Others preserved

_ScanAtom:
	MOV	[_AtomBuf],0	    ; initially no atom
	CALL	_ScanP		    ; skip leading blanks
	JE	ScanAtomx	    ; brif empty line
	PUSH	BX
	PUSH	CX
	PUSH	SI		    ;save start of string
ScanAtom1:
	LODSB
	CMP	AL,"$"
	JE	ScanAtom1
	CMP	AL,"?"
	JE	ScanAtom1
	CMP	AL,"@"
	JE	ScanAtom1
	CMP	AL,"_"
	JE	ScanAtom1
	CALL	NumCkAL
	JB	ScanAtom1	     ; "0-9" Ok
	CMP	AL,"A"
	JB	ScanAtom2
	CMP	AL,"Z"+1
	JB	ScanAtom1	     ; "A-Z" Ok
ScanAtom2:
	POP	BX		    ;[BX] = start
	DEC	SI		    ;Point at termination character
	PUSH	SI
	PUSH	DI
	MOV	CX,SI		    ;[CX] = end
	SUB	CX,BX		    ;[CX] = len
	MOV	[_AtomBuf],CL	    ;Store name len
	MOV	SI,BX
	MOV	DI,OFFSET ds:_AtomBuf+1
	CLD
	REP	MOVSB		    ;Copy name to search buf
	POP	DI
	POP	SI
	POP	CX
	POP	BX
	CMP	AL," "
	JE	ScanAtomx
	CMP	AL,";"
	JE	ScanAtomx
	CMP	AL,CR		    ; [AL] has terminate character
ScanAtomx:
	MOV	[_pTXT],SI
	RET			    ; TZ if end of symbol

DBCODE	ends
	end
