;       SCCSID = @(#)debeval.asm        6.1 90/11/17
;      SCCSID = @(#)debeval.asm 13.9 90/04/19
PAGE    ,132
NAME    DEBEVAL
TITLE   DEBEVAL.ASM - Debug Expression Evaluator
.286p

;***    DEBEVAL.ASM
;
;       Contains routines to evaluate the debugger expressions.
;
;

.xlist
        include debseg.inc
        include debmac.inc
        include debequ.inc
        include debdesc.inc
.list

ArgSlen         equ     64              ; Length of Arg Stack in bytes

DataSegmentStart

Extern pTXT,    WORD
Extern sbrtab,  BYTE
Extern pRegSA,  WORD
Extern DefType, BYTE

Extern AXmsg,   BYTE
Extern BXmsg,   BYTE
Extern CXmsg,   BYTE
Extern DXmsg,   BYTE
Extern SPmsg,   BYTE
Extern BPmsg,   BYTE
Extern SImsg,   BYTE
Extern DImsg,   BYTE
Extern IPmsg,   BYTE
Extern EAXmsg,  BYTE
Extern EBXmsg,  BYTE
Extern ECXmsg,  BYTE
Extern EDXmsg,  BYTE
Extern ESPmsg,  BYTE
Extern EBPmsg,  BYTE
Extern ESImsg,  BYTE
Extern EDImsg,  BYTE
Extern EIPmsg,  BYTE
Extern CSmsg,   BYTE
Extern DSmsg,   BYTE
Extern ESmsg,   BYTE
Extern SSmsg,   BYTE
Extern FSmsg,   BYTE
Extern GSmsg,   BYTE
Extern TRmsg,   BYTE
Extern LDTRmsg, BYTE
Extern MSWmsg,  BYTE
Extern GDTRmsg, BYTE
Extern IDTRmsg, BYTE
Extern GDTBmsg, BYTE
Extern GDTLmsg, BYTE
Extern IDTBmsg, BYTE
Extern IDTLmsg, BYTE
Extern CR0msg, BYTE
Extern CR2msg, BYTE
Extern CR3msg, BYTE
Extern CR4msg, BYTE			; 148688
Extern DR0msg, BYTE
Extern DR1msg, BYTE
Extern DR2msg, BYTE
Extern DR3msg, BYTE
Extern DR6msg, BYTE
Extern DR7msg, BYTE
Extern TR6msg, BYTE
Extern TR7msg, BYTE

Static GotArg,  BYTE,   0               ; non-zero if arg on stack
Static pTXTopr, WORD,   0               ; ptr to text of last opr encountered
Static ArgSptr, WORD,   0               ; current arg stack ptr

Static ArgSbot, ArgSlen, 0              ; room for some args
Static ArgStop, LABEL, BYTE

Static TaskNum, WORD,   0               ; local task number

Static ExprArg, STRUC, ExprS, <>
Static TmpExpr, STRUC, ExprS, <>

Global AtomBuf, <BUFLEN>, 0             ; format: len of atom, atom

Static SuffixTable, BYTE, <"Y", 2>      ; binary
Static            , BYTE, <"T", 10>     ; decimal
Static            , BYTE, <"Q", 8>      ; octal
Static            , BYTE, <"O", 8>      ; octal
Static            , BYTE, <"H", 16>     ; hex
Static            , BYTE, <0, 16>       ; (default) hex

Static EFLGmsg, BYTE,   "E"             ; must be contigious
Static FLGmsg,  STRING, "FLG"           ;

RegTableS       struc
        RegNameStr      dw      ?
        RegAddress      dw      ?
        RegType         dw      ?
RegTableS       ends

RegTable  label word
RegTableS <dataOFFSET AXmsg, AXsav, EXPR_SIZE_WORD_W + EXPR_TYPE_DATA_W>
RegTableS <dataOFFSET BXmsg, BXsav, EXPR_SIZE_WORD_W + EXPR_TYPE_DATA_W>
RegTableS <dataOFFSET CXmsg, CXsav, EXPR_SIZE_WORD_W + EXPR_TYPE_DATA_W>
RegTableS <dataOFFSET DXmsg, DXsav, EXPR_SIZE_WORD_W + EXPR_TYPE_DATA_W>
RegTableS <dataOFFSET SPmsg, SPsav, EXPR_SIZE_WORD_W + EXPR_TYPE_DATA_W>
RegTableS <dataOFFSET BPmsg, BPsav, EXPR_SIZE_WORD_W + EXPR_TYPE_DATA_W>
RegTableS <dataOFFSET SImsg, SIsav, EXPR_SIZE_WORD_W + EXPR_TYPE_DATA_W>
RegTableS <dataOFFSET DImsg, DIsav, EXPR_SIZE_WORD_W + EXPR_TYPE_DATA_W>
RegTableS <dataOFFSET IPmsg, IPsav, EXPR_SIZE_WORD_W + EXPR_TYPE_DATA_W>
RegTableS <dataOFFSET EAXmsg, EAXsav, EXPR_SIZE_DWORD_W + EXPR_TYPE_DATA_W>
RegTableS <dataOFFSET EBXmsg, EBXsav, EXPR_SIZE_DWORD_W + EXPR_TYPE_DATA_W>
RegTableS <dataOFFSET ECXmsg, ECXsav, EXPR_SIZE_DWORD_W + EXPR_TYPE_DATA_W>
RegTableS <dataOFFSET EDXmsg, EDXsav, EXPR_SIZE_DWORD_W + EXPR_TYPE_DATA_W>
RegTableS <dataOFFSET ESPmsg, ESPsav, EXPR_SIZE_DWORD_W + EXPR_TYPE_DATA_W>
RegTableS <dataOFFSET EBPmsg, EBPsav, EXPR_SIZE_DWORD_W + EXPR_TYPE_DATA_W>
RegTableS <dataOFFSET ESImsg, ESIsav, EXPR_SIZE_DWORD_W + EXPR_TYPE_DATA_W>
RegTableS <dataOFFSET EDImsg, EDIsav, EXPR_SIZE_DWORD_W + EXPR_TYPE_DATA_W>
RegTableS <dataOFFSET EIPmsg, EIPsav, EXPR_SIZE_DWORD_W + EXPR_TYPE_DATA_W>
RegTableS <dataOFFSET CSmsg, CSsav, EXPR_SIZE_WORD_W + EXPR_TYPE_DATA_W>
RegTableS <dataOFFSET DSmsg, DSsav, EXPR_SIZE_WORD_W + EXPR_TYPE_DATA_W>
RegTableS <dataOFFSET ESmsg, ESsav, EXPR_SIZE_WORD_W + EXPR_TYPE_DATA_W>
RegTableS <dataOFFSET SSmsg, SSsav, EXPR_SIZE_WORD_W + EXPR_TYPE_DATA_W>
RegTableS <dataOFFSET FSmsg, FSsav, EXPR_SIZE_WORD_W + EXPR_TYPE_DATA_W>
RegTableS <dataOFFSET GSmsg, GSsav, EXPR_SIZE_WORD_W + EXPR_TYPE_DATA_W>
RegTableS <dataOFFSET TRmsg, TRsav, EXPR_SIZE_WORD_W + EXPR_TYPE_DATA_W>
RegTableS <dataOFFSET FLGmsg, Fsav, EXPR_SIZE_WORD_W + EXPR_TYPE_DATA_W>
RegTableS <dataOFFSET EFLGmsg, EFsav, EXPR_SIZE_DWORD_W + EXPR_TYPE_DATA_W>
RegTableS <dataOFFSET MSWmsg, MSWsav, EXPR_SIZE_WORD_W + EXPR_TYPE_DATA_W>
RegTableS <dataOFFSET LDTRmsg, LDTsav, EXPR_SIZE_WORD_W + EXPR_TYPE_DATA_W>
RegTableS <dataOFFSET GDTBmsg, GDTsav.DTRBaseLow, EXPR_SIZE_DWORD_W + EXPR_TYPE_LIN_W>
RegTableS <dataOFFSET GDTLmsg, GDTsav.DTRLimit, EXPR_SIZE_WORD_W + EXPR_TYPE_DATA_W>
RegTableS <dataOFFSET IDTBmsg, IDTsav.DTRBaseLow, EXPR_SIZE_DWORD_W + EXPR_TYPE_LIN_W>
RegTableS <dataOFFSET IDTLmsg, IDTsav.DTRLimit, EXPR_SIZE_WORD_W + EXPR_TYPE_DATA_W>
RegTableS <dataOFFSET CR0msg, CR0sav, EXPR_SIZE_DWORD_W + EXPR_TYPE_DATA_W>
RegTableS <dataOFFSET CR2msg, CR2sav, EXPR_SIZE_DWORD_W + EXPR_TYPE_LIN_W>
RegTableS <dataOFFSET CR3msg, CR3sav, EXPR_SIZE_DWORD_W + EXPR_TYPE_PHY_W>
RegTableS <dataOFFSET CR4msg, CR4sav, EXPR_SIZE_DWORD_W + EXPR_TYPE_PHY_W>	; 148688
RegTableS <dataOFFSET DR0msg, DR0sav, EXPR_SIZE_DWORD_W + EXPR_TYPE_LIN_W>
RegTableS <dataOFFSET DR1msg, DR1sav, EXPR_SIZE_DWORD_W + EXPR_TYPE_LIN_W>
RegTableS <dataOFFSET DR2msg, DR2sav, EXPR_SIZE_DWORD_W + EXPR_TYPE_LIN_W>
RegTableS <dataOFFSET DR3msg, DR3sav, EXPR_SIZE_DWORD_W + EXPR_TYPE_LIN_W>
RegTableS <dataOFFSET DR6msg, DR6sav, EXPR_SIZE_DWORD_W + EXPR_TYPE_DATA_W>
RegTableS <dataOFFSET DR7msg, DR7sav, EXPR_SIZE_DWORD_W + EXPR_TYPE_DATA_W>
RegTableS <dataOFFSET TR6msg, TR6sav, EXPR_SIZE_DWORD_W + EXPR_TYPE_DATA_W>
RegTableS <dataOFFSET TR7msg, TR7sav, EXPR_SIZE_DWORD_W + EXPR_TYPE_DATA_W>
RegTableS <0, 0, 0>

Opr_frame   struc
        Oprec   db      ?       ; Operator precedence
        Opadr   dw      ?       ; Operator cction routine addr
        Oplen   db      ?       ; Operator text length
        Opnam   db      ?       ; Operator (text)
Opr_frame   ends

opr     macro   prec, action, len, optext
        .errnz  Oprec
        db      prec
        .errnz  Opadr-1
        dw      codeOFFSET action
        .errnz  Oplen-3
        db      len
        .errnz  Opnam-4
        db      optext
        endm

;
; Operator and Operator precedence table
;

; Odd precedence byte indicates unary operator
; Even precedence byte indicates binary operator

OprTab  label   byte

opr 24, ColOpr, 1, ":"          ; Seg:Offset or Sel:Offset connector
opr 24, TaskOpr, 1, "|"         ; TaskNum|Addr connector
opr 18, MulOpr, 1, "*"          ; Multiplication
opr 18, DivOpr, 1, "/"          ; Division, the result is the quotient
opr 18, MODopr, 3, "MOD"        ; Modulo reduction (remainder)
opr 16, AddOpr, 1, "+"          ; Addition
opr 16, SubOpr, 1, "-"          ; Subtraction
opr 12, GTopr, 1, ">"           ; Greater than relational operator
opr 12, LTopr, 1, "<"           ; Less than relational operator
opr 12, GEopr, 2, ">="          ; Greater than or equal relational opr
opr 12, LEopr, 2, "<="          ; Less than or equal relational operator
opr 10, EQopr, 2, "=="          ; Equal relational operator
opr 10, NEopr, 2, "!="          ; Not Equal relational operator
opr 8,  ANDopr, 3, "AND"        ; Bitwise AND
opr 6,  XORopr, 3, "XOR"        ; Bitwise exclusive OR
opr 4,  ORopr, 2, "OR"          ; Bitwise inclusive OR
opr 2,  LogANDopr, 2, "&&"      ; Logical AND
opr 2,  LogORopr, 2, "||"       ; Logical OR

; Unary operators

OpUnary label byte

opr 21, SegAddrOpr, 1, "&"      ; address is segment:offset type addr
opr 21, SelAddrOpr, 1, "#"      ; address is selector:offset type addr
opr 21, LinAddrOpr, 1, "%"      ; address is linear type addr
opr 21, PhyAddrOpr, 2, "%%"     ; address is physical type addr
opr 21, NegOpr, 1, "-"          ; 2's complement of n,modulo 2^16
opr 21, LogNOTopr, 1, "!"       ; Logical NOT operator
opr 21, NOTopr, 3, "NOT"        ; Bitwise 1's complement
opr 21, SEGopr, 3, "SEG"        ; Returns the segment portion
opr 21, OFFopr, 3, "OFF"        ; Returns the offset portion
opr 21, BYopr, 2, "BY"          ; 1 byte value from the address
opr 21, WOopr, 2, "WO"          ; 2 byte value from the address
opr 21, DWopr, 2, "DW"          ; 4 byte value from the address
opr 21, POIopr, 3, "POI"        ; 4 byte value from the address
opr 21, INopr,  4, "PORT"       ; 1 byte value from 8 bit I/O port
opr 21, INWopr, 5, "WPORT"      ; 2 byte value from 16 bit I/O port

; End of Table

OPend   label   byte
        .errnz  Oprec-0
        db      0                       ; end of table mark
        .errnz  Opadr-1
        dw      codeOFFSET ExprError

; OPLpar is special and need not be in OprTab

OPLpar  label   byte
        .errnz  Oprec-0
        db      31                      ; left Paren "("
        .errnz  Opadr-1
        dw      codeOFFSET LeftParOpr

DataSegmentEnd

CodeSegmentStart

Extern CmdBreak, NEAR


;***    EvalRange
;
;       Gets a Range expression from command line using default AddrS if
;       necessary.
;
;       Pcall   EvalRange, <DefAddrPtr, RetAddrPtr, DefLen>
;
;       ENTRY:  (DefAddrPtr) = Pointer to Default AddrS structure
;               (RetAddrPtr) = Pointer to AddrS structure to return data in
;               (DefLen)     = Default Length
;
;       EXIT:   (CX) = Number of bytes, if 'C' Set
;               (CX) = Number of objects, if 'C' Clear
;
;       USES:   CX, Flags

DefProc EvalRange, NEAR, PUBLIC

ArgVar  DefAddrPtr, WORD
ArgVar  RetAddrPtr, WORD
ArgVar  DefLen,     WORD

BeginProc
        SaveReg <ax, dx>
        Pcall   EvalAddr, <DefAddrPtr, RetAddrPtr>
        Pcall   EvalLength, <RetAddrPtr, DefLen, 0>
        pushf
        or      ax,ax
        jz      evr1                    ; jump if <64k length
        jmp     ExprError

evr1:   POPFF
        mov     cx,dx                   ; (CX) = length
        RestReg <dx, ax>
EndProc


;***    EvalLength - evaluate length parameter
;
;       Evaluates the length part of an address range.
;
;       Pcall EvalLength, <AddrPtr, DefLen>
;
;       ENTRY:  (AddrPtr) = pointer to AddrS
;               (DefLen) = default length
;
;       EXIT:   (AX:DX) = number of bytes or number of objects (Lxxxx).
;               if carry set then (AX:DX) is the number of bytes.
;               if carry clear then (AX:DX) is number of objects.
;               if EOL, the default length and carry is clear (objects)
;
;       USES:   AX, DX, FLAGS
;

DefProc EvalLength, NEAR, PUBLIC

ArgVar  AddrPtr, WORD
ArgVar  DefLen, DWORD
LocVar  TmpAddr, <size AddrS>

BeginProc
        SaveReg <bx, si>
        Pcall   ScanP                   ; Get to next parameter
        jz      evl1                    ; If EOL, return
        cmp     al,"L"                  ; Length indicator?
        je      evl4

        lea     si,TmpAddr
        Pcall   EvalAddr, <AddrPtr, si>
        Pcall   SubtractAddr, <si, AddrPtr>
        jc      evl2                    ; jump if error

        add     dx,1
        adc     ax,0                    ; Include last location

;       Carry set means DX is in bytes (end address - start address).

        stc
        jmp     short evl5

evl1:   mov     ax,DefLen.HighWord
        mov     dx,DefLen.LowWord       ; (AX:DX) = default length (objects)
        clc
        jmp     short evl5

evl2:   or      ax,ax
        jnz     evl3                    ; jump if error code
        jmp     ExprError

evl3:   Jump    ErrBreak

evl4:   Pcall   SkipB                   ; Skip over "L" to length
        Pcall   EvalDword               ; Get the length

;       Carry clear means DX is in objects (L option).

        clc
evl5:   RestReg <si, bx>
EndProc


;***    EvalAddr
;
;       Gets an AddrS expression from command line using default AddrS if
;       necessary.
;
;       Pcall   EvalAddr, <DefAddrPtr, RetAddrPtr>
;
;       ENTRY:  (DefAddrPtr) = Pointer to Default AddrS structure
;               (RetAddrPtr) = Pointer to AddrS structure to return data in
;
;       EXIT:   RetAddrS filled in
;
;       USES:   Flags

DefProc EvalAddr, NEAR, PUBLIC

ArgVar  DefAddrPtr, WORD
ArgVar  RetAddrPtr, WORD
LocVar  TmpAddr, <size AddrS>

BeginProc
        SaveReg <ax, bx, dx, si>
        mov     bx,DefAddrPtr           ; (BX) = default AddrS pointer
        Pcall   ScanP
        jz      eva4                    ; jump if EOL

        lea     si,TmpAddr              ; (SI) = temporary buffer
        Pcall   EvalExpr
        mov     al,[si].AddrType        ; (AL) = address type
        test    al,EXPR_TYPE_DEF
        jz      eva1

        mov     al,[bx].AddrType        ; (AL) = the default address type
        test    al,EXPR_TYPE_SEGSEL     ; if seg or sel address type
        jnz     eva2
        mov     al,DefType              ; (AL) = the default address type

eva1:   cmp     al,EXPR_TYPE_DATA
        jne     eva2
        mov     al,[bx].AddrType        ; (AL) = the default address type

        mov     dx,[bx].AddrSeg         ; use the default segment
        mov     [si].AddrSeg,dx

eva2:   cmp     [si].AddrTask,0         ; if no task number
        jne     eva3
        mov     dx,[bx].AddrTask        ; use the default task number
        mov     [si].AddrTask,dx

eva3:   mov     [si].AddrType,al        ; fill in final address type
        lea     bx,TmpAddr              ; (BX) = ptr to newly formed AddrS
eva4:   Pcall   CopyAddr, <bx, RetAddrPtr>
        RestReg <si, dx, bx, ax>
EndProc


;***    EvalDword - evaluate dword
;
;       Evaluate a dword expresion from command line buffer.
;
;       ENTRY:  NONE
;
;       EXIT:   AX:DX = dword
;
;       USES:   AX, DX, FLAGS
;

DefProc EvalDword, NEAR, PUBLIC
        SaveReg <bx, cx>
        Pcall   ScanP
        Pcall   Expr
        cmp     cl,EXPR_TYPE_DATA
        je      gd1
        jmp     ExprError
gd1:
        RestReg <cx, bx>
EndProc


;***    EvalWord - evaluate word
;
;       Evaluate a word expression from command line buffer.
;
;       ENTRY:  NONE
;
;       EXIT:   AX:DX = word (zero extended).
;
;       USES:   AX, DX, FLAGS
;

DefProc EvalWord, NEAR, PUBLIC
        SaveReg <bx, cx>
        Pcall   ScanP
        Pcall   Expr
        cmp     cl,EXPR_TYPE_DATA
        jne     gw0
        cmp     ch,EXPR_SIZE_WORD
        jbe     gw1
gw0:
        jmp     ExprError
gw1:
        RestReg <cx, bx>
EndProc


;***    EvalByte - evaluate byte
;
;       Evaluate a byte expression from command line buffer.
;
;       ENTRY:  NONE
;
;       EXIT:   AX:DX = byte (zero extended).
;
;       USES:   AX, DX, FLAGS
;

DefProc EvalByte, NEAR, PUBLIC
        SaveReg <bx, cx>
        Pcall   ScanP
        Pcall   Expr
        cmp     cx,EXPR_SIZE_BYTE_W + EXPR_TYPE_DATA_W
        je      gb1
        jmp     ExprError
gb1:
        RestReg <cx, bx>
EndProc


;***    EvalExpr
;
;       ENTRY:  (SI) = pointer to AddrS struc to fill
;               [pTXT] = input buffer pointer
;
;       EXIT:   NONE
;
;       USES:   Flags
;

DefProc EvalExpr, NEAR, PUBLIC
        SaveReg <ax, bx, cx, dx>
        Pcall   Expr
        Pcall   PutExprS
        RestReg <dx, cx, bx, ax>
EndProc


;***    Expr
;
;       Expression Evaluator
;
;       ENTRY:  [pTXT] = input buffer pointer.
;
;       EXIT:   (BX) = segment value
;               (AX:DX) = offset or data value
;               (CH) = value size
;               (CL) = value type
;
;       USES:   AX, BX, CX, DX, FLAGS
;

DefProc Expr, NEAR, LOCAL
        SaveReg <si, di>
        xor     ax,ax
        mov     ExprArg.ExprTypeSize,EXPR_TYPE_DATA_W + EXPR_SIZE_WORD_W
        mov     ExprArg.ExprData.LowWord,ax
        mov     ExprArg.ExprData.HighWord,ax
        mov     ExprArg.ExprSeg,ax
        mov     ExprArg.ExprTask,ax
        mov     TaskNum,ax
        mov     ArgSptr,dataOFFSET ArgSbot      ; Reset arg stack
        Pcall   ExprA                           ; Evaluate something
        Pcall   PopArg                          ; Get answer
        RestReg <di, si>
EndProc

DefProc ExprA, NEAR, LOCAL
        mov     si,dataOFFSET OPend
ExprB:
        push    si                      ; Opr preced/Action ptr
        mov     GotArg,0                ; No arg on stack yet
ExprC:
        cmp     GotArg,0
        jnz     expr3                   ; Brif got arg, get opr
expr2:
        Pcall   GetArg                  ; Put arg on stack (CF set if not arg)
        jnc     expr3                   ; brif arg

        Pcall   TestOp                  ; May be Opr
        jnc     expr4                   ; Brif was opr

        Pcall   GetChr                  ; Skip fill
        cmp     al, "("
        jne     expr3

        mov     si,dataOFFSET OPLpar    ; Recursing
        push    si
        Pcall   SkipB                   ; Eat "("
        call    ExprA                   ; Evaluate
        Pcall   GetChr
        cmp     al, ")"                 ; must be ")"
        jne     ExprError               ; else error
        Pcall   SkipB                   ; Eat ")"
        pop     si
        jmp     short expr9
expr3:
        Pcall   ScanOp                  ; SI = opr adr, carry set if illegal opr
        jnc     expr4
        jmp     expr_exit
expr4:
        cmp     GotArg,0
        jne     expr6                   ; Brif got arg, else must be unary op
        cmp     al,"+"
        jne     expr5                   ; Not unary plus, try unary minus
        Pcall   SkipB                   ; Eat "+"
        jmp     short expr2             ; Start over
expr5:
        Pcall   TestOpU                 ; Must be unary opr
        jnc     expr7                   ; Brif was
ExprError:
        putstr  "Expression error"
        jmp     CmdBreak
expr6:
        Pcall   TestOp                  ; Get Operator
        jc      ExprError
expr7:
        pop     di
        mov     al,[di].Oprec           ; [AL] = previous precedence
        push    di
        cmp     al,[si].Oprec           ; Old preced less than new preced?
        jb      expr8                   ; Brif Higher precedence
        mov     ax,pTXTopr              ; Restore pTXT to start
        mov     pTXT,ax                 ;  of last opr
        jmp     short expr_exit         ; Do same or lower
expr8:
        push    si                      ; Save new precedence
        call    ExprB                   ; Recurse
        pop     si                      ; get curr precedence
expr9:
        push    codeOFFSET ExprC        ; will continue eval with next arg fetch
        Pcall   PopArg                  ; (arg 2) [BX:AX,DX], [CX] = type
        mov     ExprArg.ExprData.HighWord,ax
        mov     ExprArg.ExprData.LowWord,dx
        mov     ExprArg.ExprSeg,bx
        mov     ExprArg.ExprTypeSize,cx
        test    [si].Oprec,1            ; Binary or Unary Op?
        jnz     expr10                  ; Brif Unary
        Pcall   PopArg                  ; (arg 1) [BX:AX,DX], [CX] = type
expr10:
        call    [si].Opadr              ; Perform arith operation
        Pcall   FindSize
        jmp     PshArg                  ; Result on Stack
expr_exit:
        pop     si                      ; Op prec ptr
EndProc

;
;       Operator routines
;

LeftParOpr:
        ret

AddOpr:
        add     dx,ExprArg.ExprData.LowWord
        adc     ax,ExprArg.ExprData.HighWord
        ret

SubOpr:
        sub     dx,ExprArg.ExprData.LowWord
        sbb     ax,ExprArg.ExprData.HighWord
        ret

;
; AHI = AX, ALO = DX
; BHI = ExprArg.ExprData.HighWord
; BLO = ExprArg.ExprData.LowWord
;
; (ALO * BLO) + ((AHI * BLO) + (ALO * BHI)) * (2**16)
;

MulOpr:
        push    dx                      ; save ALO
        push    ax                      ; save AHI

        mov     ax,dx
        mul     ExprArg.ExprData.LowWord; ALO * BLO
        mov     si,ax
        mov     di,dx                   ; DI:SI = (ALO * BLO)

        pop     ax                      ; AX = AHI
        mul     ExprArg.ExprData.LowWord; AHI * BLO
        add     di,ax                   ; DI:SI =+ (AHI * BLO) * (2**16)

        pop     ax                      ; AX = ALO
        mul     ExprArg.ExprData.HighWord ; ALO * BHI
        add     di,ax                   ; DI:SI =+ (ALO * BHI) * (2**16)

        mov     ax,di
        mov     dx,si                   ; AX:DX = DI:SI
        ret

;       Divide only is word long right now.

DivOpr:
        cmp     ExprArg.ExprData.LowWord,0
        jnz     dvo1
        jmp     ExprError               ; Brif Div/0
dvo1:
        xchg    ax,dx
        div     ExprArg.ExprData.LowWord
        xchg    ax,dx
        xor     ax,ax
        ret

TaskOpr:
        mov     TaskNum,dx              ; task number = low word of arg 1
        mov     bx,ExprArg.ExprSeg
        mov     ax,ExprArg.ExprData.HighWord
        mov     dx,ExprArg.ExprData.LowWord
        mov     cx,ExprArg.ExprTypeSize ; get type/size
        ret

ColOpr:
        mov     bx,dx                   ; segment = low word of arg 1
        mov     ax,ExprArg.ExprData.HighWord
        mov     dx,ExprArg.ExprData.LowWord
        mov     ch,EXPR_SIZE_DWORD      ; make double word
        test    cl,EXPR_TYPE_SEGSEL     ; check for seg/sel class
        jnz     cop1

;       If data, lin, phy address make the address
;       the default address type.

        mov     cl,EXPR_TYPE_DEF
cop1:   ret

SegAddrOpr:
        mov     cl,EXPR_TYPE_SEG
        ret

SelAddrOpr:
        mov     cl,EXPR_TYPE_SEL
        ret

LinAddrOpr:
        mov     si,dataOFFSET TmpExpr
        Pcall   PutExprS
        mov     bx,si
        mov     cx,1
        Pcall   AddrToLin
        jc      MemErr
        xor     bx,bx                   ; make segment portion zero
        mov     cl,EXPR_TYPE_LIN
        ret

PhyAddrOpr:
        mov     si,dataOFFSET TmpExpr
        Pcall   PutExprS
        mov     bx,si
        mov     cx,1
        Pcall   AddrToPhy
        jc      MemErr
        xor     bx,bx                   ; make segment portion zero
        mov     cl,EXPR_TYPE_PHY
        ret

MemErr:
        mov     bx,dataOFFSET TmpExpr
        Jump    ErrBreak

NegOpr:
        not     dx                      ; invert low word
        not     ax                      ; invert high word
        add     dx,1                    ; add 1 to result
        adc     ax,0                    ; extend carry to high word
        ret

MODopr:
        cmp     ExprArg.ExprData.LowWord,0
        jnz     mod1
        jmp     ExprError               ; Brif Div/0
mod1:
        xchg    ax,dx
        div     ExprArg.ExprData.LowWord; Remainder [DX] is word result
        xor     ax,ax
        ret

NOTopr:
        not     dx
        not     ax
        ret

ANDopr:
        and     dx,ExprArg.ExprData.LowWord
        and     ax,ExprArg.ExprData.HighWord
        ret

ORopr:
        or      dx,ExprArg.ExprData.LowWord
        or      ax,ExprArg.ExprData.HighWord
        ret

XORopr:
        xor     dx,ExprArg.ExprData.LowWord
        xor     ax,ExprArg.ExprData.HighWord
        ret

SEGopr:                                 ; Return segment portion (word)
        mov     dx,bx
        jmp     short MakeDataWord

OFFopr:                                 ; Return offset portion
        jmp     short MakeData

BYopr:
        mov     si,dataOFFSET TmpExpr
        Pcall   PutExprS
        mov     bx,si
        Pcall   GetByte                 ; (AL) = byte at this address
        jc      MemErr
        mov     dl,al
        jmp     short MakeDataByte      ; (DL) = byte at this address

WOopr:
        mov     si,dataOFFSET TmpExpr
        Pcall   PutExprS
        mov     bx,si
        Pcall   GetWord                 ; (DX) = word at this address
        jc      MemErr
        jmp     short MakeDataWord

POIopr:
        call    DWopr
        mov     bx,ax                   ; (BX) = segment
        xor     ax,ax                   ; (AX:DX) = offset
        mov     cl,EXPR_TYPE_DEF        ; make the address the default type
        ret

DWopr:
        mov     si,dataOFFSET TmpExpr
        Pcall   PutExprS
        mov     bx,si
        Pcall   GetDword                ; (AX:DX) = dword at this address
        jc      MemErr
        xor     bx,bx
        mov     cx,EXPR_SIZE_DWORD_W + EXPR_TYPE_DATA_W
        ret

INopr:
        in      al,dx                   ; Get 8 bit from PORT [DX]
        mov     dx,ax
MakeDataByte:
        xor     ax,ax
        xor     dh,dh
        mov     ch,EXPR_SIZE_BYTE
        jmp     short MakeData

INWopr:
        in      ax,dx                   ; Get 16 bit from PORT [DX]
        mov     dx,ax
MakeDataWord:
        xor     ax,ax
        mov     ch,EXPR_SIZE_WORD
MakeData:
        mov     cl,EXPR_TYPE_DATA
        xor     bx,bx
        ret

GEopr:
        SaveReg <ax,bx,dx>
        call    GTopr
        call    IsTrue
        RestReg <dx,bx,ax>
        jc      RetTrue1                ; if true, return true
        jmp     short EQopr             ; if false, check if equal

GTopr:
        cmp     bx,ExprArg.ExprSeg
        ja      RetTrue1
        jne     RetFalse1
        cmp     ax,ExprArg.ExprData.HighWord
        ja      RetTrue
        jne     RetFalse
        cmp     dx,ExprArg.ExprData.LowWord
        ja      RetTrue
RetFalse1:
        jmp     short RetFalse

LTopr:
        cmp     bx,ExprArg.ExprSeg
        jb      RetTrue
        jne     RetFalse
        cmp     ax,ExprArg.ExprData.HighWord
        jb      RetTrue
        jne     RetFalse
        cmp     dx,ExprArg.ExprData.LowWord
        jb      RetTrue
        jmp     short RetFalse

LEopr:
        SaveReg <ax,bx,dx>
        call    LTopr
        call    IsTrue
        RestReg <dx,bx,ax>
        jc      RetTrue                 ; if true, return true
EQopr:
        cmp     bx,ExprArg.ExprSeg
        jne     RetFalse
        cmp     ax,ExprArg.ExprData.HighWord
        jne     RetFalse
        cmp     dx,ExprArg.ExprData.LowWord
        jne     RetFalse
RetTrue1:
        jmp     short RetTrue

LogANDopr:
        call    IsTrue                  ; is BX:AX,DX true?
        jnc     RetFalse                ; jump if false, returning false
        mov     bx,ExprArg.ExprSeg
        mov     ax,ExprArg.ExprData.HighWord
        mov     dx,ExprArg.ExprData.LowWord
        call    IsTrue
        jnc     RetFalse                ; jump if false, returning false
        jmp     short RetTrue

LogORopr:
        call    IsTrue                  ; is BX:AX,DX true?
        jc      RetTrue                 ; jump if true, returning true
        mov     bx,ExprArg.ExprSeg
        mov     ax,ExprArg.ExprData.HighWord
        mov     dx,ExprArg.ExprData.LowWord
        call    IsTrue                  ; is BX:AX,DX true?
        jc      RetTrue                 ; jump if true, returning true
        jmp     short RetFalse

NEopr:
        call    EQopr
LogNOTopr:
        call    IsTrue                  ; is BX:AX,DX true (not zero)?
        jc      RetFalse                ; jump if true, returning false
RetTrue:
        mov     dl,1                    ; true is not zero
        jmp     MakeDataByte
RetFalse:
        xor     dl,dl                   ; false is zero
        jmp     MakeDataByte


;***    IsTrue
;
;       Checks if a value is true (!0) or false (0).
;
;       ENTRY:  BX:AX,BX = value to check
;
;       EXIT:   Carry set if TRUE, Carry clear if FALSE
;
;       USES:   Flags
;

DefProc IsTrue, NEAR, PUBLIC
        or      bx,bx                   ; check if segment is zero
        jnz     ist1
        push    ax
        or      ax,dx
        pop     ax
        jnz     ist1
        clc                             ; carry clear if FALSE
        jmp     short ist2

ist1:   stc                             ; carry set if TRUE
ist2:
EndProc


;***    ScanOp - scan for operator
;
;       Checks to see if there is a operator in input buffer
;       without changing the buffer point (pTXT).
;
;       ENTRY:  NONE
;
;       EXIT:   AL = operator character.
;               If carry set, then there is an operator.
;               If carry clear, no operator.
;
;       USES:   AX, BX, CX, DX, DI, SI, FLAGS
;

DefProc ScanOp, NEAR, LOCAL
        mov     si,pTXT                 ; (SI) = old pTXT
        mov     pTXTopr,si              ; save ptr to start of opr
        push    si                      ; save old pTXT
        Pcall   ScanAtom                ; scan Opr into AtomBuf
        cmp     AtomBuf[0],0
        jnz     sop1                    ; got something

;       check if two special charater operator

        mov     AtomBuf[0],2            ; len = 2
        mov     si,pTXT
        lodsw                           ; (AX) = two character operator
        mov     pTXT,si
        mov     word ptr AtomBuf[1],ax
        Pcall   TestOp
        jnc     sop2                    ; jump if found a two char operator

;       check if one special character operator

        dec     pTXT
        mov     AtomBuf[0],1            ; len = 1, AtomBuf already has char

sop1:   Pcall   TestOp

sop2:   mov     al,AtomBuf[1]           ; Get possible operator
        pop     si                      ; old txt ptr
        jnc     sop3                    ; Was opr, keep new txt ptr
        mov     pTXT,si                 ; Restore old txt ptr
sop3:
EndProc


;***    GetArg - get argument
;
;       Gets an argument and puts the data on the argument stack.
;
;       ENTRY:  NONE
;
;       EXIT:   If carry clear, no error (valid argument).
;               If carry set, error.
;
;       USES:   AX, BX, CX, DX, SI, FLAGS
;

DefProc GetArg, NEAR, LOCAL
        Pcall   GetArgValue             ; Get a value
        jc      garx                    ; Brif not value (may be opr or eos)
        mov     GotArg,1                ; Got arg
        Pcall   PshArg
        clc
garx:
EndProc


;***    PshArg
;
;       Pushes CX, BX:AX,DX on argument stack.
;
;       ENTRY:  BX:AX,DX = value to push.
;               CH, CL = value size, value type.
;
;       EXIT:   NONE
;
;       USE:    AX, FLAGS
;

DefProc PshArg, NEAR, LOCAL
        SaveReg <di>
        mov     di,ArgSptr
        cmp     di,(dataOFFSET ArgStop) - 6
        jbe     psh1
        jmp     ExprError               ; error if expression is too big

psh1:   stosw                           ; Push high word
        mov     ax,dx
        stosw                           ; Push low word
        mov     ax,bx
        stosw                           ; Push segment
        mov     ax,cx
        stosw                           ; Push type
        mov     ax,TaskNum
        stosw                           ; Push task number
        mov     ArgSptr,di
        RestReg <di>
EndProc


;***    PopArg
;
;       Pop a number off the argument stack.
;
;       ENTRY:  NONE
;
;       EXIT:   BX:AX,DX = value poped.
;               CH, CL = value size, value type.
;
;       USES:   AX, BX, CX, DX
;

DefProc PopArg, NEAR, LOCAL
        SaveReg <si>
        mov     si,ArgSptr
        cmp     si,dataOFFSET ArgSbot
        ja      par1
        jmp     ExprError

par1:   std                             ; reverse direction flag
        dec     si                      ; move back to task number word
        dec     si
        lodsw                           ; Pop task number
        mov     TaskNum,ax
        lodsw                           ; Pop type
        mov     cx,ax
        lodsw                           ; Pop segment
        mov     bx,ax
        lodsw                           ; Pop low word
        mov     dx,ax
        lodsw                           ; Pop high word
        inc     si                      ; move pointer to empty slot
        inc     si                      ;
        mov     ArgSptr,si
        cld                             ; must leave the direction clear
        RestReg <si>
EndProc


;***    PutExprS - fills in the ExprS structure
;
;       Pcall   PutExprS
;
;       ENTRY:  (BX:AX,DX) = address
;               (CX) = type/size
;               (SI) = pointer to ExprS structure
;               [TaskNum] = task number
;
;       EXIT:   NONE
;
;       USES:   Flags
;

DefProc PutExprS, NEAR, LOCAL
        mov     [si].ExprData.LowWord,dx
        mov     [si].ExprData.HighWord,ax
        mov     [si].ExprSeg,bx
        mov     [si].ExprTypeSize,cx
        push    TaskNum
        pop     [si].ExprTask
EndProc


;***    GetExprS - gets the values in the ExprS structure
;
;       Pcall   GetExprS
;
;       ENTRY:  (SI) = pointer to ExprS structure
;
;       EXIT:   (BX:AX,DX) = address
;               (CX) = type/size
;               [TaskNum] = task number
;
;       USES:   Flags
;

DefProc GetExprS, NEAR, PUBLIC
        mov     dx,[si].ExprData.LowWord
        mov     ax,[si].ExprData.HighWord
        mov     bx,[si].ExprSeg
        mov     cx,[si].ExprTypeSize
        push    [si].ExprTask
        pop     TaskNum
EndProc


;***    GetArgValue
;
;       Gets an argument value from text in input buffer.
;
;       ENTRY:  NONE
;
;       EXIT:   BX:AX,DX = value.
;               CH, CL = value size, value type.
;               [TaskNum] = task number (0 = default)
;               If carry clear, no error (valid data).
;               If carry set, error.
;
;       USES:   AX, BX, CX, DX, SI
;

DefProc GetArgValue, NEAR, PUBLIC
        Pcall   ScanAtom                ; Get an atom into AtomBuf
        cmp     AtomBuf[0],0
        je      short gav50             ; No arg if empty

        mov     al,AtomBuf[1]           ; (AL) = first char in buffer
        Pcall   NumChkAL
        jc      short gav30             ; Brif Numeric, get hex value

        Pcall   TryRegister             ; Get Register or symbol
        jnc     short gav40             ; Brif was Reg ([CL:DX]= type:value)

gav10:  mov     di,dataOFFSET AtomBuf   ; (DI) = Pointer to string
        mov     si,dataOFFSET TmpExpr   ; (SI) = AddrS to return data in
        Pcall   GetSymbolVal            ; See if this is a symbol.
        jc      short gav20
        mov     si,dataOFFSET TmpExpr   ; (SI) = AddrS returned data in
        Pcall   GetExprS                ; Get data out of AddrS (ExprS)
        jmp     short gav40             ; Go exit

gav20:  Pcall   TestOp                  ; If not arg, maybe opr
        jnc     short gav50             ; Brif was operator

gav30:  Pcall   GetArgNum
        jnc     short gav40
        jmp     ExprError

gav40:  clc
        jmp     short gavx

gav50:  stc
gavx:
EndProc


;***    TestOpU - test unary operator
;
;       Checks if an unary operator for a match to the text
;       in atom buffer.
;
;       ENTRY:  AtomBuf[] = text to check if an operator.
;
;       EXIT:   SI = operator table entry if match.
;               If carry clear, text is an operator.
;               If carry set, text is not an operator.
;
;       USES:   AX, BX, CX, DX, SI, DI, FLAGS
;

DefProc TestOpU, NEAR, LOCAL
        mov     bx,dataOFFSET OpUnary
        call    TestOp1
EndProc


;***    TestOp - test operator
;
;       Checks if an operator for a match to the text in atom buffer.
;
;       ENTRY:  AtomBuf[] = text to check if an operator.
;
;       EXIT:   SI = operator table entry if match.
;               If carry clear, text is an operator.
;               If carry set, text is not an operator.
;
;       USES:   AX, BX, CX, DX, SI, DI, FLAGS
;

DefProc TestOp, NEAR, LOCAL
        mov     bx,dataOFFSET OprTab
Entry   TestOp1, NEAR, LOCAL            ; entry used by TestOpU
        mov     di,dataOFFSET AtomBuf
        xor     cx,cx
        mov     cl,[di]                 ; [CX]= Len of opr to match
        inc     di                      ; [DI]= Start of opr to match

top2:   cmp     [bx].Oprec,0
        je      top5                    ; End of Table, not found
        cmp     [bx].Oplen,cl
        jne     top3                    ; len's don't match, try next
        lea     si,[bx].Opnam
        push    cx
        push    di
        repe    cmpsb                   ; Match?
        pop     di
        pop     cx
        je      top4                    ; Brif so, return goodies

top3:   xor     ax,ax
        mov     al,[bx].Oplen
        add     ax,size Opr_frame - 1
        add     bx,ax
        jmp     short top2
top4:   clc
        jmp     short topx

top5:   stc
topx:   mov     si,bx                   ; (SI) = operator table entry
EndProc


;***    GetArgNum - get number argument
;
;       Gets a converts the characters in the atom buffer
;       into a number using the suffix as the radix. If no
;       type suffix then hex is the default.
;
;       ENTRY:  AtomBuf[] = characters to convert
;
;       EXIT:   BX:AX,DX = value.
;               CH, CL = value size, value type.
;               [TaskNum] = 0
;               If carry clear then no errors.
;               If carry set then invalid digit.
;
;       USES:   AX, BX, CX, DX, SI, FLAGS
;

DefProc GetArgNum, NEAR, LOCAL
        Pcall   NumType
        mov     si,dataOFFSET SuffixTable
        mov     bl,al                   ; put suffix character in BL

gan1:   lodsw                           ; get type char and radix
        or      al,al
        jz      gan2                    ; jump if end of table (default radix)
        cmp     al,bl
        jne     gan1                    ; jump if type character doesn't match
        dec     AtomBuf[0]              ; remove suffix character from buffer

gan2:   mov     bl,ah                   ; radix is in AH, move to BX
        xor     bh,bh                   ;
        Pcall   GetNum
EndProc


;***    NumType - get type suffix
;
;       Gets the possible number type suffix.
;
;       ENTRY:  AtomBuf[] = text of possible number with suffix.
;
;       EXIT:   AL = possible number type suffix character.
;
;       USES:   AX, BX, FLAGS
;

DefProc NumType, NEAR, LOCAL
        mov     bl,AtomBuf[0]                   ; get size
        xor     bh,bh                           ; make word
        mov     al,AtomBuf[bx]                  ; get last character of buffer
EndProc


;***    GetNum - convert ascii to number
;
;       Converts ascii characters to any radix number.
;
;       ENTRY:  AtomBuf[] = text to convert to a number.
;               BX = radix.
;
;       EXIT:   BX:AX,DX = data.
;               CH, CL = value size, value type.
;               [TaskNum] = 0
;               If carry clear, no errors.
;               If carry set, invalid character.
;
;       USES:   AX, BX, CX, DX, SI, FLAGS
;

DefProc GetNum, NEAR, LOCAL

LocVar  Data, DWORD

BeginProc
        mov     si,dataOFFSET AtomBuf
        lodsb                                   ; get character count
        mov     cl,al                           ; put in CX
        xor     ax,ax                           ; clear AX
        mov     ch,al                           ; clear upper part of CX
        mov     Data.LowWord,ax                 ; clear data accumulator
        mov     Data.HighWord,ax                ;

;       Get a character from the buffer and convert to an digit

gnm1:   lodsb
        cmp     al,'0'                          ; error if below a '0' character
        jb      gnm5
        cmp     al,'9'                          ; jump to alpha conversion
        ja      gnm2
        sub     al,'0'                          ; make character into digit
        jmp     short gnm3

gnm2:   cmp     al,'A'                          ; error if not an alpha
        jb      gnm5
        cmp     al,'Z'                          ; error if not an alpha
        ja      gnm5
        sub     al,'A' - 10                     ; make character into digit

;       See if digit is valid for this radix

gnm3:   cmp     al,bl
        jae     gnm5

        push    ax                              ; save digit

;       Muliply the current data by the radix

        mov     ax,Data.LowWord                 ; Multiply low word by radix
        mul     bx                              ;
        mov     Data.LowWord,ax                 ; AX is the low word of result
        mov     ax,Data.HighWord                ; Get high word of data
        mov     Data.HighWord,dx                ; Save DX for later
        mul     bx                              ; Multiply high word by radix
        add     Data.HighWord,ax                ; Add result to DX above

;       Add the digit to the current data

        pop     ax                              ; restore digit

        xor     ah,ah
        add     Data.LowWord,ax
        adc     Data.HighWord,0

;       Next character

        loop    gnm1

;       Get converted number

gnm4:   xor     bx,bx                           ; no segment value
        mov     TaskNum,bx                      ; no task number
        mov     ax,Data.HighWord
        mov     dx,Data.LowWord
        mov     cl,EXPR_TYPE_DATA

;       Determine the value size

        Pcall   FindSize

        clc
        jmp     short gnmx

gnm5:   stc
gnmx:
EndProc


;***    FindSize
;
;       This routine determines the size of the
;       value passed if it is a data or physical
;       address type.
;
;       ENTRY:  BX:AX,DX = value to check.
;               CL = value type.
;
;       EXIT:   CH = size.
;
;       USES:   CH, FLAGS
;

DefProc FindSize, NEAR, LOCAL
        mov     ch,EXPR_SIZE_DWORD
        or      ax,ax                           ; dword size if high word is
        jnz     fsz2                            ;  not zero.
        mov     ch,EXPR_SIZE_WORD
        or      dh,dh                           ; word size if high byte of
        jnz     fsz2                            ;  low word is not zero.
        mov     ch,EXPR_SIZE_BYTE
fsz2:
EndProc


;***    TryRegister
;
;       Return a registers value in [DX] if reg otherwise, just return.
;
;       ENTRY:  AtomBuf[] contains text to try.
;
;       EXIT:   BX:AX,DX = value of register.
;               CL = value type.
;               CH = value size.
;               [TaskNum] = task number
;
;       FLAGS:  Carry set if was a register.
;               Carry clear if not.
;
;       USES:   AX, CX, DX, SI.
;

DefProc TryRegister, NEAR, LOCAL
        mov     bx,dataOFFSET AtomBuf
        mov     cl,[bx]                         ; (CL) = atom size
        inc     bx

        cmp     cl,3                            ; May be BR0 .. BR9
        jne     trg1                            ; Brif not

        cmp     word ptr [bx],"RB"              ; Must be BRn
        jne     trg1

        mov     al,[bx + 2]
        call    NumChkAL
        jnc     trg5                            ; if not BR0 .. BR9

        sub     al,"0"                          ; if not BR0 thru BR9
        xor     ah,ah
        imul    ax,size StickyBP_struc          ; (AX) = offset into sbrtab

        mov     bx,dataOFFSET sbrtab
        add     bx,ax

        cmp     [bx].BrkFlag,0                  ; return error if bp not set
        je      trg5

        lea     si,[bx].BrkAddr
        Pcall   GetExprS                        ; (BX:AX,DX) = addr,(CX) = type
        jmp     short trg4

trg1:   mov     si,dataOFFSET RegTable          ; (SI) = ptr to RegTable
        xor     ch,ch
        mov     dx,size RegTableS               ; (DX) = size of RegTable entry

;       (SI) = string table address
;       (CX) = length of atom to chk
;       (BX) = pointer to atom buffer

        Pcall   SearchString                    ; (SI) = ptr to matching entry
        jc      trg5                            ; jump if no match

        xor     bx,bx                           ; (BX) = clear segment
        xor     ax,ax                           ; (AX) = cleared highword
        mov     cx,[si].RegType                 ; (CX) = address type
        mov     si,[si].RegAddress
        add     si,[pRegSA]                     ; (SI) = pointer to RegSA entry

        cmp     ch,EXPR_SIZE_WORD
        je      trg3                            ; jump if word size
        mov     ax,[si].HighWord
trg3:   mov     dx,[si].LowWord                 ; (BX:AX,DX) = register value
        mov     TaskNum,0                       ; no task number

trg4:   clc
        jmp     short trg6

trg5:   stc
trg6:
EndProc


;***    ScanAtom
;
;       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.
;
;       USES:   AX, Flags.
;

DefProc ScanAtom, NEAR, PUBLIC
        SaveReg <si, di>
        xor     ax, ax                  ; initially no atom - count zero;124354
;124354 mov     AtomBuf[0],0            ; initially no atom             ;124354
        Pcall   GetChr                  ; skip leading blanks
        je      sca3                    ; brif empty line
        mov     si,pTXT
        mov     di,dataOFFSET AtomBuf[1]
sca1:   lodsb
        stosb
        inc     ah                      ; increment pointer             ;124354
        cmp     ah, BUFLEN              ; quit if end of buffer reached ;124354
        jge     short sca3                                              ;124354
;124354 inc     AtomBuf[0]              ; increment pointer             ;124354
        cmp     al,"$"
        je      sca1
        cmp     al,"?"
        je      sca1
        cmp     al,"@"
        je      sca1
        cmp     al,"_"
        je      sca1
        call    NumChkAL
        jb      sca1                    ; "0-9" Ok
        cmp     al,"A"
        jb      sca2
        cmp     al,"Z" + 1
        jb      sca1                    ; "A-Z" Ok
sca2:   dec     si                      ; point at termination character
        mov     pTXT,si
        dec     ah                      ; fix count                     ;124354
;124354 dec     AtomBuf[0]              ; fix count                     ;124354
sca3:   RestReg <di, si>
        mov     AtomBuf[0], ah          ; initially no atom             ;124354
EndProc


;***    NumChkAL
;
;       Checks if AL is numeric.
;
;       ENTRY:  AL = character to check.
;
;       EXIT:   If carry set then numeric.
;               If carry clear then not numeric.
;

DefProc NumChkAL, NEAR, LOCAL
        cmp     al,"9"+1
        jnc     numx
        cmp     al,"0"
        cmc
numx:
EndProc


;***    HexChk
;
;       Check if AL has a hex digit and convert it to binary if it
;       is. Carry set if not.
;
;       ENTRY:  AL = hex character
;
;       EXIT:   If carry clear, AL = hex nibble.
;               If carry set invalid hex digit.
;
;       USES:   AX, FLAGS
;

DefProc HexChk, NEAR, PUBLIC
        sub     al,"0"                  ; Kill ASCII numeric bias
        jc      hckx
        cmp     al,10
        cmc
        jnc     hckx                    ; OK if 0-9
        and     al,5fh
        sub     al,7                    ; Kill A-F bias
        cmp     al,10
        jc      hckx
        cmp     al,16
        cmc
hckx:
EndProc

CodeSegmentEnd

        end
