1
0
mirror of https://github.com/hsoft/collapseos.git synced 2024-11-02 16:30:57 +11:00

Compare commits

...

5 Commits

Author SHA1 Message Date
Virgil Dupras
dcb96aefe9 lib/parse: remove parseHexPair
Also, make parseHexadecimal "tail" (HL). Soon, all routines in lib/parse
will do that, making the life of lib/expr easier.
2019-12-29 21:56:56 -05:00
Virgil Dupras
2503bdfced lib/args: remove 2019-12-29 21:05:09 -05:00
Virgil Dupras
5f2615a134 at28w: don't use lib/args 2019-12-29 21:02:04 -05:00
Virgil Dupras
346bcc3d3d zasm: don't use lib/args
This unit is being removed.
2019-12-29 20:56:13 -05:00
Virgil Dupras
d0f031939f lib/parse: make parseLiteral a little tighter
Sub-parsers are seldom used by themselves, except for parseDecimal.
I'm tightening the code of this unit for two reasons:

1. Optimization
2. Upcoming API change where HL won't be preserved anymore, but will
   point to char following the last parse char. This will allow us
   to simplify lib/expr.
2019-12-29 19:47:19 -05:00
13 changed files with 231 additions and 425 deletions

View File

@ -74,6 +74,8 @@ There are decimal, hexadecimal and binary literals. A "straight" number is
parsed as a decimal. Hexadecimal literals must be prefixed with `0x` (`0xf4`).
Binary must be prefixed with `0b` (`0b01100110`).
A decimal literal cannot start with `0`, with the exception of the `0` literal.
Decimals and hexadecimal are "flexible". Whether they're written in a byte or
a word, you don't need to prefix them with zeroes. Watch out for overflow,
however.

View File

@ -22,6 +22,5 @@ jp at28wMain
.inc "core.asm"
.inc "lib/util.asm"
.inc "lib/parse.asm"
.inc "lib/args.asm"
.inc "at28w/main.asm"
USER_RAMSTART:

View File

@ -11,15 +11,15 @@
; *** Code ***
at28wMain:
ld de, .argspecs
ld ix, AT28W_MAXBYTES
call parseArgs
ld de, AT28W_MAXBYTES
ld a, (hl)
or a
jr z, at28wInner ; no arg
call parseHexadecimal ; --> DE
jr z, at28wInner
; bad args
ld a, SHELL_ERR_BAD_ARGS
ret
.argspecs:
.db 0b111, 0b101, 0
at28wInner:
; Reminder: words in parseArgs aren't little endian. High byte is first.

View File

@ -1,111 +0,0 @@
; *** Requirements ***
; lib/parse
;
; *** Consts ***
; maximum number of bytes to receive as args in all commands. Determines the
; size of the args variable.
.equ PARSE_ARG_MAXCOUNT 3
; *** Code ***
; Parse arguments at (HL) with specifiers at (DE) into (IX).
;
; Args specifiers are a series of flag for each arg:
; Bit 0 - arg present: if unset, we stop parsing there
; Bit 1 - is word: this arg is a word rather than a byte. Because our
; destination are bytes anyway, this doesn't change much except
; for whether we expect a space between the hex pairs. If set,
; you still need to have a specifier for the second part of
; the multibyte.
; Bit 2 - optional: If set and not present during parsing, we don't error out
; and write zero
;
; Bit 3 - String argument: If set, this argument is a string. A pointer to the
; read string, null terminated (max 0x20 chars) will
; be placed in the next two bytes. This has to be the
; last argument of the list and it stops parsing.
; Sets A to nonzero if there was an error during parsing, zero otherwise.
parseArgs:
push bc
push de
push hl
push ix
; init the arg value to a default 0
xor a
ld (ix), a
ld (ix+1), a
ld (ix+2), a
ld b, PARSE_ARG_MAXCOUNT
.loop:
ld a, (hl)
; is this the end of the line?
or a ; cp 0
jr z, .endofargs
; Get the specs
ld a, (de)
bit 0, a ; do we have an arg?
jr z, .error ; not set? then we have too many args
ld c, a ; save the specs for multibyte check later
bit 3, a ; is our arg a string?
jr z, .notAString
; our arg is a string. Let's place HL in our next two bytes and call
; it a day. Little endian, remember
ld (ix), l
ld (ix+1), h
jr .success ; directly to success: skip endofargs checks
.notAString:
call parseHexPair
jr c, .error
; we have a good arg and we need to write A in (IX).
ld (ix), a
; Good! increase counters
inc de
inc ix
inc hl ; get to following char (generally a space)
; Our arg is parsed, our pointers are increased. Normally, HL should
; point to a space *unless* our argspec indicates a multibyte arg.
bit 1, c
jr nz, .nospacecheck ; bit set? no space check
; do we have a proper space char (or null char)?
ld a, (hl)
or a
jr z, .endofargs
cp ' '
jr nz, .error
inc hl
.nospacecheck:
djnz .loop
; If we get here, it means that our next char *has* to be a null char
ld a, (hl)
or a ; cp 0
jr z, .success ; zero? great!
jr .error
.endofargs:
; We encountered our null char. Let's verify that we either have no
; more args or that they are optional
ld a, (de)
or a
jr z, .success ; no arg? success
bit 2, a
jr z, .error ; if unset, arg is not optional. error
; success
.success:
xor a
jr .end
.error:
inc a
.end:
pop ix
pop hl
pop de
pop bc
ret

View File

@ -22,42 +22,6 @@ parseHex:
ret
; Parses 2 characters of the string pointed to by HL and returns the numerical
; value in A. If the second character is a "special" character (<0x21) we don't
; error out: the result will be the one from the first char only.
; HL is set to point to the last char of the pair.
;
; On success, the carry flag is reset. On error, it is set.
parseHexPair:
push bc
ld a, (hl)
call parseHex
jr c, .end ; error? goto end, keeping the C flag on
rla \ rla \ rla \ rla ; let's push this in MSB
ld b, a
inc hl
ld a, (hl)
cp 0x21
jr c, .single ; special char? single digit
call parseHex
jr c, .end ; error?
or b ; join left-shifted + new. we're done!
; C flag was set on parseHex and is necessarily clear at this point
jr .end
.single:
; If we have a single digit, our result is already stored in B, but
; we have to right-shift it back.
ld a, b
and 0xf0
rra \ rra \ rra \ rra
dec hl
.end:
pop bc
ret
; Parse the decimal char at A and extract it's 0-9 numerical value. Put the
; result in A.
;
@ -140,71 +104,45 @@ parseDecimal:
pop hl ; <-- lvl 1, orig
jp unsetZ
; Parse string at (HL) as a hexadecimal value and return value in DE under the
; same conditions as parseLiteral.
; Parse string at (HL) as a hexadecimal value without the "0x" prefix and
; return value in DE.
; HL is advanced to the character following the last successfully read char.
; Sets Z on success.
parseHexadecimal:
call hasHexPrefix
ret nz
push hl
ld d, 0
inc hl ; get rid of "0x"
inc hl
call strlen
cp 3
jr c, .single
cp 4
jr c, .doubleShort ; 0x123
cp 5
jr c, .double ; 0x1234
; too long, error
jr .error
.double:
call parseHexPair
jr c, .error
inc hl ; now HL is on first char of next pair
ld d, a
jr .single
.doubleShort:
ld a, (hl)
call parseHex
jr c, .error
inc hl ; now HL is on first char of next pair
ld d, a
.single:
call parseHexPair
jr c, .error
jp c, unsetZ ; we need at least one char
push bc
ld de, 0
ld b, 0
.loop:
; we push to B to verify overflow
rl e \ rl d \ rl b
rl e \ rl d \ rl b
rl e \ rl d \ rl b
rl e \ rl d \ rl b
or e
ld e, a
cp a ; ensure Z
jr .end
.error:
call unsetZ
.end:
pop hl
ret
; Sets Z if (HL) has a '0x' prefix.
hasHexPrefix:
ld a, (hl)
cp '0'
ret nz
push hl
; did we overflow?
ld a, b
or a
jr nz, .end ; overflow, NZ already set
; next char
inc hl
ld a, (hl)
cp 'x'
pop hl
call parseHex
jr nc, .loop
cp a ; ensure Z
.end:
pop bc
ret
; Parse string at (HL) as a binary value (0b010101) and return value in E.
; D is always zero.
; Parse string at (HL) as a binary value (010101) without the "0b" prefix and
; return value in E. D is always zero.
; Sets Z on success.
parseBinaryLiteral:
call hasBinPrefix
ret nz
push bc
push hl
ld d, 0
inc hl ; get rid of "0b"
inc hl
call strlen
or a
jr z, .error ; empty, error
@ -237,49 +175,6 @@ parseBinaryLiteral:
pop bc
ret
; Sets Z if (HL) has a '0b' prefix.
hasBinPrefix:
ld a, (hl)
cp '0'
ret nz
push hl
inc hl
ld a, (hl)
cp 'b'
pop hl
ret
; Parse string at (HL) and, if it is a char literal, sets Z and return
; corresponding value in E. D is always zero.
;
; A valid char literal starts with ', ends with ' and has one character in the
; middle. No escape sequence are accepted, but ''' will return the apostrophe
; character.
parseCharLiteral:
ld a, 0x27 ; apostrophe (') char
cp (hl)
ret nz
push hl
inc hl
inc hl
cp (hl)
jr nz, .end ; not ending with an apostrophe
inc hl
ld a, (hl)
or a ; cp 0
jr nz, .end ; string has to end there
; Valid char, good
ld d, a ; A is zero, take advantage of that
dec hl
dec hl
ld a, (hl)
ld e, a
cp a ; ensure Z
.end:
pop hl
ret
; Parses the string at (HL) and returns the 16-bit value in DE. The string
; can be a decimal literal (1234), a hexadecimal literal (0x1234) or a char
; literal ('X').
@ -287,11 +182,59 @@ parseCharLiteral:
; As soon as the number doesn't fit 16-bit any more, parsing stops and the
; number is invalid. If the number is valid, Z is set, otherwise, unset.
parseLiteral:
call parseCharLiteral
ret z
call parseHexadecimal
ret z
call parseBinaryLiteral
ret z
ld de, 0 ; pre-fill
ld a, (hl)
cp 0x27 ; apostrophe
jr z, .char
cp '0'
jr z, .hexOrBin
jp parseDecimal
; Parse string at (HL) and, if it is a char literal, sets Z and return
; corresponding value in E. D is always zero.
;
; A valid char literal starts with ', ends with ' and has one character in the
; middle. No escape sequence are accepted, but ''' will return the apostrophe
; character.
.char:
push hl
inc hl
inc hl
cp (hl)
jr nz, .charEnd ; not ending with an apostrophe
inc hl
ld a, (hl)
or a ; cp 0
jr nz, .charEnd ; string has to end there
; Valid char, good
dec hl
dec hl
ld e, (hl)
cp a ; ensure Z
.charEnd:
pop hl
ret
.hexOrBin:
inc hl
ld a, (hl)
inc hl ; already place it for hex or bin
cp 'x'
jr z, .hex
cp 'b'
jr z, .bin
; special case: single '0'. set Z if we hit have null terminating.
or a
.hexOrBinEnd:
dec hl \ dec hl ; replace HL
ret ; Z already set
.hex:
push hl
call parseHexadecimal
pop hl
jr .hexOrBinEnd
.bin:
call parseBinaryLiteral
jr .hexOrBinEnd

View File

@ -5,6 +5,31 @@ isWS:
cp 0x09
ret
; Advance HL to next WS.
; Set Z if WS found, unset if end-of-string.
toWS:
ld a, (hl)
call isWS
ret z
or a
jp z, unsetZ
inc hl
jr toWS
; Consume following whitespaces in HL until a non-WS is hit.
; Set Z if non-WS found, unset if end-of-string.
rdWS:
ld a, (hl)
call isWS
jr nz, .ok
or a
jp z, unsetZ
inc hl
jr rdWS
.ok:
cp a ; ensure Z
ret
; Copy string from (HL) in (DE), that is, copy bytes until a null char is
; encountered. The null char is also copied.
; HL and DE point to the char right after the null char.

View File

@ -68,7 +68,6 @@ jp zasmMain
.inc "lib/util.asm"
.inc "lib/ari.asm"
.inc "lib/parse.asm"
.inc "lib/args.asm"
.inc "zasm/util.asm"
.equ IO_RAMSTART USER_RAMSTART
.inc "zasm/io.asm"

View File

@ -25,7 +25,6 @@ jp zasmMain
.inc "lib/util.asm"
.inc "lib/ari.asm"
.inc "lib/parse.asm"
.inc "lib/args.asm"
.inc "zasm/util.asm"
.equ IO_RAMSTART USER_RAMSTART
.inc "zasm/io.asm"

View File

@ -23,34 +23,34 @@
; HL is set to the last lineno to be read.
; Sets Z on success, unset on error. On error, A contains an error code (ERR_*)
zasmMain:
; Parse args. HL points to string already
; We don't allocate memory just to hold this. Because this happens
; before initialization, we don't really care where those args are
; parsed. That's why we borrow zasm's RAMSTART for a little while.
ld de, .argspecs
ld ix, ZASM_RAMSTART
call parseArgs
jr z, .goodargs
; bad args
ld hl, 0
ld de, 0
ld a, SHELL_ERR_BAD_ARGS
ret
.goodargs:
; HL now points to parsed args
; Init I/O
ld a, (ZASM_RAMSTART) ; blkdev in ID
; Parse args in (HL)
; blkdev in
call parseHexadecimal ; --> DE
jr nz, .badargs
ld a, e
ld de, IO_IN_BLK
call blkSel
ld a, (ZASM_RAMSTART+1) ; blkdev out ID
; blkdev in
call rdWS
jr nz, .badargs
call parseHexadecimal ; --> DE
jr nz, .badargs
ld a, e
ld de, IO_OUT_BLK
call blkSel
; Init .org
; This is the 3rd argument, optional, will be zero if not given.
; .org high byte
ld e, 0 ; in case we .skipOrgSet
call rdWS
jr nz, .skipOrgSet ; no org argument
call parseHexadecimal ; --> DE
jr nz, .badargs
.skipOrgSet:
; Init .org with value of E
; Save in "@" too
ld a, (ZASM_RAMSTART+2)
ld a, e
ld (ZASM_ORG+1), a ; high byte of .org
ld (DIREC_LASTVAL+1), a
xor a
@ -82,8 +82,11 @@ zasmMain:
.end:
jp ioLineNo ; --> HL, --> DE, returns
.argspecs:
.db 0b001, 0b001, 0b101
.badargs:
; bad args
ld a, SHELL_ERR_BAD_ARGS
ret
.sFirstPass:
.db "First pass", 0
.sSecondPass:

View File

@ -16,7 +16,6 @@
jp upcase
jp findchar
jp parseHex
jp parseHexPair
jp blkSel
jp blkSet
jp fsFindFN
@ -145,9 +144,9 @@ basFindCmdExtra:
jp basPgmHook
.mycmds:
.db "ed", 0
.dw 0x1e00
.dw 0x1f00
.db "zasm", 0
.dw 0x2300
.dw 0x2400
.db 0xff
f0GetB:
@ -166,13 +165,13 @@ f1PutB:
ld ix, FS_HANDLES+FS_HANDLE_SIZE
jp fsPutB
; last time I checked, PC at this point was 0x1df8. Let's give us a nice margin
; last time I checked, PC at this point was 0x1e92. Let's give us a nice margin
; for the start of ed.
.fill 0x1e00-$
.fill 0x1f00-$
.bin "ed.bin"
; Last check: 0x22dd
.fill 0x2300-$
; Last check: 0x23b0
.fill 0x2400-$
.bin "zasm.bin"
.fill 0x7ff0-$

View File

@ -14,7 +14,6 @@
.equ upcase @+3
.equ findchar @+3
.equ parseHex @+3
.equ parseHexPair @+3
.equ blkSel @+3
.equ blkSet @+3
.equ fsFindFN @+3

View File

@ -0,0 +1,85 @@
jp test
.inc "core.asm"
.inc "lib/util.asm"
.inc "lib/parse.asm"
zasmGetPC:
ret
testNum: .db 1
test:
ld sp, 0xffff
call testParseHex
call testParseHexadecimal
; success
xor a
halt
testParseHex:
ld a, '8'
call parseHex
jp c, fail
cp 8
jp nz, fail
call nexttest
ld a, 'e'
call parseHex
jp c, fail
cp 0xe
jp nz, fail
call nexttest
ld a, 'x'
call parseHex
jp nc, fail
call nexttest
ret
testParseHexadecimal:
ld hl, .s99
call parseHexadecimal
jp nz, fail
ld a, e
cp 0x99
jp nz, fail
call nexttest
ld hl, .saB
call parseHexadecimal
jp nz, fail
ld a, e
cp 0xab
jp nz, fail
call nexttest
; The string "Foo" will not cause a failure. We will parse up to "o"
; and then stop.
ld hl, .sFoo
call parseHexadecimal
jp nz, fail
ld a, e
cp 0xf
call nexttest
ret
.sFoo: .db "Foo", 0
.saB: .db "aB", 0
.s99: .db "99", 0
nexttest:
ld a, (testNum)
inc a
ld (testNum), a
ret
fail:
ld a, (testNum)
halt
; used as RAM
sandbox:

View File

@ -1,136 +0,0 @@
jp test
.inc "core.asm"
.inc "lib/util.asm"
.inc "lib/parse.asm"
.inc "lib/args.asm"
zasmGetPC:
ret
testNum: .db 1
test:
ld hl, 0xffff
ld sp, hl
call testParseHex
call testParseHexPair
call testParseArgs
; success
xor a
halt
testParseHex:
ld a, '8'
call parseHex
jp c, fail
cp 8
jp nz, fail
call nexttest
ld a, 'e'
call parseHex
jp c, fail
cp 0xe
jp nz, fail
call nexttest
ld a, 'x'
call parseHex
jp nc, fail
call nexttest
ret
testParseHexPair:
ld hl, .s99
call parseHexPair
jp c, fail
cp 0x99
jp nz, fail
call nexttest
ld hl, .saB
call parseHexPair
jp c, fail
cp 0xab
jp nz, fail
call nexttest
ld hl, .sFoo
call parseHexPair
jp nc, fail
call nexttest
ret
.sFoo: .db "Foo", 0
.saB: .db "aB", 0
.s99: .db "99", 0
testParseArgs:
ld hl, .t1+6
ld de, .t1
ld iy, .t1+3
call .testargs
ld hl, .t2+6
ld de, .t2
ld iy, .t2+3
call .testargs
ld hl, .t3+6
ld de, .t3
ld iy, .t3+3
call .testargs
ret
; HL and DE must be set, and IY must point to expected results in IX
.testargs:
ld ix, sandbox
call parseArgs
jp nz, fail
ld a, (ix)
cp (iy)
jp nz, fail
ld a, (ix+1)
cp (iy+1)
jp nz, fail
ld a, (ix+2)
cp (iy+2)
jp nz, fail
jp nexttest
; Test data format: 3 bytes specs, 3 bytes expected (IX), then the arg string.
; Empty args with empty specs
.t1:
.db 0b0000, 0b0000, 0b0000
.db 0, 0, 0
.db 0
; One arg, one byte spec
.t2:
.db 0b0001, 0b0000, 0b0000
.db 0xe4, 0, 0
.db "e4", 0
; 3 args, 3 bytes spec
.t3:
.db 0b0001, 0b0001, 0b0001
.db 0xe4, 0xab, 0x99
.db "e4 ab 99", 0
nexttest:
ld a, (testNum)
inc a
ld (testNum), a
ret
fail:
ld a, (testNum)
halt
; used as RAM
sandbox: