mirror of
https://github.com/hsoft/collapseos.git
synced 2024-12-25 04:58:05 +11:00
basic: parse hex, binary and char literals
Same thing as in zasm.
This commit is contained in:
parent
1cea6e71e0
commit
0bd58fd178
5
CODE.md
5
CODE.md
@ -46,6 +46,11 @@ Another exception to this rule are "top-level" routines, that is, routines that
|
||||
aren't designed to be called from other parts of Collapse OS. Those are
|
||||
generally routines close to an application's main loop.
|
||||
|
||||
It is important to note, however, that shadow registers aren't preserved.
|
||||
Therefore, shadow registers should only be used in code that doesn't call
|
||||
routines or that call a routine that explicitly states that it preserves
|
||||
shadow registers.
|
||||
|
||||
## Stack management
|
||||
|
||||
Keeping the stack "balanced" is a big challenge when writing assembler code.
|
||||
|
@ -105,7 +105,7 @@ basBYE:
|
||||
.db "Goodbye!", 0
|
||||
|
||||
basPRINT:
|
||||
call parseDecimal
|
||||
call parseLiteral
|
||||
jp nz, basERR
|
||||
push ix \ pop de
|
||||
ld hl, BAS_SCRATCHPAD
|
||||
|
@ -136,3 +136,168 @@ parseDecimal:
|
||||
.error:
|
||||
pop hl
|
||||
jp unsetZ
|
||||
|
||||
; Parse string at (HL) as a hexadecimal value and return value in IX under the
|
||||
; same conditions as parseLiteral.
|
||||
parseHexadecimal:
|
||||
call hasHexPrefix
|
||||
ret nz
|
||||
push hl
|
||||
push de
|
||||
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
|
||||
ld e, a
|
||||
cp a ; ensure Z
|
||||
jr .end
|
||||
.error:
|
||||
call unsetZ
|
||||
.end:
|
||||
push de \ pop ix
|
||||
pop de
|
||||
pop hl
|
||||
ret
|
||||
|
||||
; Sets Z if (HL) has a '0x' prefix.
|
||||
hasHexPrefix:
|
||||
ld a, (hl)
|
||||
cp '0'
|
||||
ret nz
|
||||
push hl
|
||||
inc hl
|
||||
ld a, (hl)
|
||||
cp 'x'
|
||||
pop hl
|
||||
ret
|
||||
|
||||
; Parse string at (HL) as a binary value (0b010101) and return value in IX.
|
||||
; High IX byte is always clear.
|
||||
; Sets Z on success.
|
||||
parseBinaryLiteral:
|
||||
call hasBinPrefix
|
||||
ret nz
|
||||
push bc
|
||||
push hl
|
||||
push de
|
||||
ld d, 0
|
||||
inc hl ; get rid of "0b"
|
||||
inc hl
|
||||
call strlen
|
||||
or a
|
||||
jr z, .error ; empty, error
|
||||
cp 9
|
||||
jr nc, .error ; >= 9, too long
|
||||
; We have a string of 8 or less chars. What we'll do is that for each
|
||||
; char, we rotate left and set the LSB according to whether we have '0'
|
||||
; or '1'. Error out on anything else. C is our stored result.
|
||||
ld b, a ; we loop for "strlen" times
|
||||
ld c, 0 ; our stored result
|
||||
.loop:
|
||||
rlc c
|
||||
ld a, (hl)
|
||||
inc hl
|
||||
cp '0'
|
||||
jr z, .nobit ; no bit to set
|
||||
cp '1'
|
||||
jr nz, .error ; not 0 or 1
|
||||
; We have a bit to set
|
||||
inc c
|
||||
.nobit:
|
||||
djnz .loop
|
||||
ld e, c
|
||||
cp a ; ensure Z
|
||||
jr .end
|
||||
.error:
|
||||
call unsetZ
|
||||
.end:
|
||||
push de \ pop ix
|
||||
pop de
|
||||
pop hl
|
||||
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 IX. High IX byte is always clear.
|
||||
;
|
||||
; 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
|
||||
push de
|
||||
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:
|
||||
push de \ pop ix
|
||||
pop de
|
||||
pop hl
|
||||
ret
|
||||
|
||||
; Parses the string at (HL) and returns the 16-bit value in IX. The string
|
||||
; can be a decimal literal (1234), a hexadecimal literal (0x1234) or a char
|
||||
; literal ('X').
|
||||
;
|
||||
; 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
|
||||
jp parseDecimal
|
||||
|
||||
|
@ -52,3 +52,25 @@ strcmp:
|
||||
; early, set otherwise)
|
||||
ret
|
||||
|
||||
; Returns length of string at (HL) in A.
|
||||
; Doesn't include null termination.
|
||||
strlen:
|
||||
push bc
|
||||
push hl
|
||||
ld bc, 0
|
||||
xor a ; look for null char
|
||||
.loop:
|
||||
cpi
|
||||
jp z, .found
|
||||
jr .loop
|
||||
.found:
|
||||
; How many char do we have? the (NEG BC)-1, which started at 0 and
|
||||
; decreased at each CPI call. In this routine, we stay in the 8-bit
|
||||
; realm, so C only.
|
||||
ld a, c
|
||||
neg
|
||||
dec a
|
||||
pop hl
|
||||
pop bc
|
||||
ret
|
||||
|
||||
|
@ -1,167 +1,3 @@
|
||||
; Parse string at (HL) as a hexadecimal value and return value in IX under the
|
||||
; same conditions as parseLiteral.
|
||||
parseHexadecimal:
|
||||
call hasHexPrefix
|
||||
ret nz
|
||||
push hl
|
||||
push de
|
||||
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
|
||||
ld e, a
|
||||
cp a ; ensure Z
|
||||
jr .end
|
||||
.error:
|
||||
call unsetZ
|
||||
.end:
|
||||
push de \ pop ix
|
||||
pop de
|
||||
pop hl
|
||||
ret
|
||||
|
||||
; Sets Z if (HL) has a '0x' prefix.
|
||||
hasHexPrefix:
|
||||
ld a, (hl)
|
||||
cp '0'
|
||||
ret nz
|
||||
push hl
|
||||
inc hl
|
||||
ld a, (hl)
|
||||
cp 'x'
|
||||
pop hl
|
||||
ret
|
||||
|
||||
; Parse string at (HL) as a binary value (0b010101) and return value in IX.
|
||||
; High IX byte is always clear.
|
||||
; Sets Z on success.
|
||||
parseBinaryLiteral:
|
||||
call hasBinPrefix
|
||||
ret nz
|
||||
push bc
|
||||
push hl
|
||||
push de
|
||||
ld d, 0
|
||||
inc hl ; get rid of "0b"
|
||||
inc hl
|
||||
call strlen
|
||||
or a
|
||||
jr z, .error ; empty, error
|
||||
cp 9
|
||||
jr nc, .error ; >= 9, too long
|
||||
; We have a string of 8 or less chars. What we'll do is that for each
|
||||
; char, we rotate left and set the LSB according to whether we have '0'
|
||||
; or '1'. Error out on anything else. C is our stored result.
|
||||
ld b, a ; we loop for "strlen" times
|
||||
ld c, 0 ; our stored result
|
||||
.loop:
|
||||
rlc c
|
||||
ld a, (hl)
|
||||
inc hl
|
||||
cp '0'
|
||||
jr z, .nobit ; no bit to set
|
||||
cp '1'
|
||||
jr nz, .error ; not 0 or 1
|
||||
; We have a bit to set
|
||||
inc c
|
||||
.nobit:
|
||||
djnz .loop
|
||||
ld e, c
|
||||
cp a ; ensure Z
|
||||
jr .end
|
||||
.error:
|
||||
call unsetZ
|
||||
.end:
|
||||
push de \ pop ix
|
||||
pop de
|
||||
pop hl
|
||||
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 IX. High IX byte is always clear.
|
||||
;
|
||||
; 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
|
||||
push de
|
||||
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:
|
||||
push de \ pop ix
|
||||
pop de
|
||||
pop hl
|
||||
ret
|
||||
|
||||
; Parses the string at (HL) and returns the 16-bit value in IX. The string
|
||||
; can be a decimal literal (1234), a hexadecimal literal (0x1234) or a char
|
||||
; literal ('X').
|
||||
;
|
||||
; 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
|
||||
jp parseDecimal
|
||||
|
||||
; Parse string in (HL) and return its numerical value whether its a number
|
||||
; literal or a symbol. Returns value in IX.
|
||||
; Sets Z if number or symbol is valid, unset otherwise.
|
||||
|
@ -30,28 +30,6 @@ toggleZ:
|
||||
cp a
|
||||
ret
|
||||
|
||||
; Returns length of string at (HL) in A.
|
||||
; Doesn't include null termination.
|
||||
strlen:
|
||||
push bc
|
||||
push hl
|
||||
ld bc, 0
|
||||
ld a, 0 ; look for null char
|
||||
.loop:
|
||||
cpi
|
||||
jp z, .found
|
||||
jr .loop
|
||||
.found:
|
||||
; How many char do we have? the (NEG BC)-1, which started at 0 and
|
||||
; decreased at each CPI call. In this routine, we stay in the 8-bit
|
||||
; realm, so C only.
|
||||
ld a, c
|
||||
neg
|
||||
dec a
|
||||
pop hl
|
||||
pop bc
|
||||
ret
|
||||
|
||||
; Sets Z if string at (HL) is one character long
|
||||
strIs1L:
|
||||
xor a
|
||||
|
@ -2,6 +2,7 @@ jp test
|
||||
|
||||
.inc "core.asm"
|
||||
.inc "str.asm"
|
||||
.inc "lib/util.asm"
|
||||
.inc "zasm/util.asm"
|
||||
|
||||
testNum: .db 1
|
||||
|
Loading…
Reference in New Issue
Block a user