Firstly let me introduce an auxillary routine this uses. It is called 'scaws' and scans past white space. It is very simple, and the definition of whitespace here is SPACE (020h) or TAB (09h):-
========START OF CODE======================================================
;
;scaws- scan whitespace
;
;entry: DS:SI=string
; DF=0
;
;exit: SI=updated to first non-whitespace character
; AL=value of the character
;
scaws PROC NEAR
;
;there is nothing to explain here but you might take note now
;that I always use the same label names in different PROC blocks,
;in MASM you can do this with OPTION SCOPED
;
lop: LODSB
CMP AL,020h
JZ lop
CMP AL,09h
JZ lop
DEC SI
RET
scaws ENDP
========END OF CODE========================================================
'scalst' is basically a routine to scan-convert a list which can
consist of values and strings. The radix of the values must be set
before hand by calling 'scanur' as the routine uses 'scanu' to convert
values and doesn't set the radix itself. The syntax of the list is
almost the same as the list in DEBUG, where in fact I got the idea from.
You have from 0+ data items, optionally seperated by commas. Whitespace
can be used freely as a delimitor and no delimitors are necessary where
there is no need for them (eg: between a value and a string).
The routine takes several parameters, the address of your string
(DS:SI), the address of somewhere to store the converted data (ES:DI),
the size of the data store (CX) and the size of a unit (AL). The unit size
can be byte (AL=1), word (AL=2), dword (AL=4).
Each data item, as in value/string character, is zero-padded to the
unit size for storing. Also values are checked that they are in range for
the unit size. This method therefore allows us to have those silly
word strings.
Here are some examples, all of these assume that we had set the
radix = 010h (by calling 'scanur' with AL=010h) :-
Calling with AL=1, and our string=1 2 3 "ABC" yields:-
01 02 03 41 42 43
Calling with AL=4, and our string="0"1FE08 2 yields:-
30 00 00 00 08 FE 01 00 02 00 00 00
Calling with AL=2, and our string=9A06 87"DEF" yields:-
06 9A 87 00 44 00 45 00 46 00
Calling with AL=2, and our string="ABC"FE0FE 0 1 2 yields:-
ERROR! CF=1 (FE0FE>FFFF)
A particularly powerful feature of this routine is that it takes
a parameter giving the size of your data store (in bytes). This means
that it will be impossible for the program to be crashed because there
was too much data. Programmers are generally too lazy to do this sort of
range checking, and much to their woe as one particularly wily hacker
attack called 'crashing the stack' has taught.
Example; if we called with AL=2, CX=4 and string=1 9 F
ERROR! CF=1 (01 00 09 00 0F 00 > 4bytes)
As an aside, the function is not entirely the same as DEBUG's list
scanner. With DEBUG the strings are always converted to byte lists, no
matter what the unit size is. It is trivial to modify the routine to
work in this way.
One last note is that the end of the list is the first invalid
character in the string, this not being an error of course since it is
the responsibilty of the controlling parser to decide this based on the
context; eg: DEBUG might check for a semicolon comment on the end of
the line, though as a matter of fact it doesn't. A premature ending (ie:
0 byte appearing inside the quotes of a string token) will abort with
error, thus;
AL=1, string=0A 98"unterminated string yields:-
ERROR! CF=1 (unterminated string)
========START OF CODE======================================================
;
;scalst- data list scan/convert routine
;
;entry: DS:SI=string
; ES:DI=store
; CX=bytes size of store
; AL=unit size (1=byte,2=word,4=dword)
; DF=0
;
; "scanur" must have been called at least once previously
; in order to set the radix of scanned values
;
; !! entry parameters are not validated and invalid entry
; !! parameters will cause undefined behaviour
;
;exit: CF=1=>error (parse/overflow)
; CF=0=>okay, then:
; ZF=1=>no data scanned, ie: CX=0
; ZF=0=>data scanned
; SI=updated to the first invalid character
; DI=updated to the end of converted data + 1
; CX=bytes converted data (invalid on overflow error)
;
;note: requires routines "scaws" and "scanu"
;
scalst PROC NEAR
;
;initialise stack frame
;[BP-4] (dw) size mask
; =000000FFh for unit size 1
; =0000FFFFh for unit size 2
; =FFFFFFFFh for unit size 4
;[BP-6] (w) unit size
;[BP-8] (w) original data offset DI
;
;EAX is preserved and the main loop is entered
;
ENTER 8,0
PUSH EAX
CBW
MOV [BP-6],AX
NEG AL
AND AL,3
SHL AL,3
PUSH CX
XCHG CX,AX
OR EAX,-1
SHR EAX,CL
POP CX
MOV [BP-4],EAX
MOV [BP-8],DI
JMP SHORT inlop
;
;main loop head
; ignore any whitespace and skip the optional comma
;
lop: CALL NEAR PTR scaws
CMP BYTE PTR [SI],','
JNZ SHORT ko
INC SI
;
;main loop entry
; ignore any whitespace and if a value token is recognised
; write it to data store and continue loop
;
inlop: CALL NEAR PTR scaws
ko: CALL NEAR PTR scanu
JC SHORT don
JZ SHORT ko2
;
; check that the value is in range for the unit size, if not
; abort here with an error
;
CMP [BP-4],EAX
JC SHORT don
CALL NEAR PTR wracc
JMP lop
;
; no value was present so check for a string
;
ko2: CMP BYTE PTR [SI],022h
CLC
JNZ SHORT don
;
; get string into data store
;
INC SI
XOR EAX,EAX
lop1: MOV AL,[SI]
;
; unterminated string causes an error abort, LODSB is not used for the
;load in order to ensure that [SI] will point to the invalid character
;
CMP AL,1
JC SHORT don
INC SI
CMP AL,022h
JZ lop
CALL NEAR PTR wracc
JMP lop1
;
; exit point for 'wracc' routine below, clean-up the stack
;
err0: POP EAX
;
; main exit point. the carry flag is preserved as this is used
; for both error and normal exits. the number of bytes stored
; is calculated into CX, the INC/DEC ensuring ZF=1 if this was zero
;
don: LAHF
MOV CX,DI
SUB CX,[BP-8]
SAHF
INC CX
DEC CX
;
; restore the only corrupted register and 'LEAVE'
;
POP EAX
LEAVE
RET
;
;wracc- write datum in accumalator to data store
; AL/AX/EAX is written to the data store depending on the unit size.
; throughout the routine DI is the offset into the data store and
; CX is the #bytes left in it. these are updated but if there are
; insufficient bytes remaining in the store we abort with error, taking
; care to clear the 4 bytes (AX + return address) off the stack first
;
wracc: PUSH AX
MOV AX,[BP-6]
SUB CX,AX
JC err0
CMP AL,2
POP AX
JZ SHORT ko0
JNS SHORT ko1
STOSB
RET
;
; note that 066h STOSW = STOSD
;
ko1: DB 066h
ko0: STOSW
RET
scalst ENDP
========END OF CODE========================================================
|