Browse Source

zasm: little code reorganisation

pull/10/head
Virgil Dupras 5 years ago
parent
commit
6d4515cd03
4 changed files with 258 additions and 258 deletions
  1. +0
    -133
      apps/zasm/literal.asm
  2. +1
    -1
      apps/zasm/main.asm
  3. +111
    -124
      apps/zasm/parse.asm
  4. +146
    -0
      apps/zasm/tok.asm

+ 0
- 133
apps/zasm/literal.asm View File

@@ -1,133 +0,0 @@
; Parse the decimal char at A and extract it's 0-9 numerical value. Put the
; result in A.
;
; On success, the carry flag is reset. On error, it is set.
parseDecimalDigit:
; First, let's see if we have an easy 0-9 case
cp '0'
ret c ; if < '0', we have a problem
sub a, '0' ; our value now is valid if it's < 10
cp 10 ; on success, C is set, which is the opposite
; of what we want
ccf ; invert C flag
ret

; Parse string at (HL) as a decimal value and return value in IX under the
; same conditions as parseNumber.
parseDecimal:
push hl
push de
push bc

ld ix, 0
.loop:
ld a, (hl)
cp 0
jr z, .end ; success!
call parseDecimalDigit
jr c, .error

; Now, let's add A to IX. First, multiply by 10.
ld d, ixh ; we need a copy of the initial copy for later
ld e, ixl
add ix, ix ; x2
add ix, ix ; x4
add ix, ix ; x8
add ix, de ; x9
add ix, de ; x10
add a, ixl
jr nc, .nocarry
inc ixh
.nocarry:
ld ixl, a

; We didn't bother checking for the C flag at each step because we
; check for overflow afterwards. If ixh < d, we overflowed
ld a, ixh
cp d
jr c, .error ; carry is set? overflow

inc hl
jr .loop

.error:
call JUMP_UNSETZ
.end:
pop bc
pop de
pop hl
ret


; Parse string at (HL) as a hexadecimal value and return value in IX under the
; same conditions as parseNumber.
parseHexadecimal:
push hl
xor a
ld ixh, a
inc hl ; get rid of "0x"
inc hl
call strlen
cp 3
jr c, .single
cp 5
jr c, .double
; too long, error
jr .error
.double:
call JUMP_PARSEHEXPAIR ; moves HL to last char of pair
jr c, .error
inc hl ; now HL is on first char of next pair
ld ixh, a
.single:
call JUMP_PARSEHEXPAIR
jr c, .error
ld ixl, a
cp a ; ensure Z
jr .end
.error:
call JUMP_UNSETZ
.end:
pop hl
ret

; Sets Z if (HL) has a '0x' or '0X' prefix.
hasHexPrefix:
ld a, (hl)
cp '0'
ret nz
push hl
inc hl
ld a, (hl)
cp 'x'
jr z, .end
cp 'X'
.end:
pop hl
ret

; Parses the string at (HL) and returns the 16-bit value in IX.
; 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.
parseNumber:
call hasHexPrefix
jr z, parseHexadecimal
jr 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.
parseNumberOrSymbol:
call parseNumber
ret z
; Not a number. Try symbol
push de
call symGetVal
jr nz, .notfound ; Z already unset
; Found! value in DE. We need it in IX
ld ixh, d
ld ixl, e
; Z already set
.notfound:
pop de
ret

+ 1
- 1
apps/zasm/main.asm View File

@@ -44,8 +44,8 @@ jp zasmMain
#include "util.asm"
.equ IO_RAMSTART ZASM_RAMEND
#include "io.asm"
#include "tok.asm"
#include "parse.asm"
#include "literal.asm"
#include "instr.asm"
#include "directive.asm"
.equ SYM_RAMSTART IO_RAMEND


+ 111
- 124
apps/zasm/parse.asm View File

@@ -1,146 +1,133 @@
; *** Consts ***
TOK_INSTR .equ 0x01
TOK_DIRECTIVE .equ 0x02
TOK_LABEL .equ 0x03
TOK_EMPTY .equ 0xfe ; not a bad token, just an empty line
TOK_BAD .equ 0xff
; Parse the decimal char at A and extract it's 0-9 numerical value. Put the
; result in A.
;
; On success, the carry flag is reset. On error, it is set.
parseDecimalDigit:
; First, let's see if we have an easy 0-9 case
cp '0'
ret c ; if < '0', we have a problem
sub a, '0' ; our value now is valid if it's < 10
cp 10 ; on success, C is set, which is the opposite
; of what we want
ccf ; invert C flag
ret

.equ SCRATCHPAD_SIZE 0x20
; *** Variables ***
scratchpad:
.fill SCRATCHPAD_SIZE
; Parse string at (HL) as a decimal value and return value in IX under the
; same conditions as parseNumber.
parseDecimal:
push hl
push de
push bc

; *** Code ***
ld ix, 0
.loop:
ld a, (hl)
cp 0
jr z, .end ; success!
call parseDecimalDigit
jr c, .error

; Sets Z is A is ';' or null.
isLineEndOrComment:
cp ';'
ret z
or a ; cp 0
ret
; Now, let's add A to IX. First, multiply by 10.
ld d, ixh ; we need a copy of the initial copy for later
ld e, ixl
add ix, ix ; x2
add ix, ix ; x4
add ix, ix ; x8
add ix, de ; x9
add ix, de ; x10
add a, ixl
jr nc, .nocarry
inc ixh
.nocarry:
ld ixl, a

; Sets Z is A is ' ' '\t' or ','
isSep:
cp ' '
ret z
cp 0x09
ret z
cp ','
ret
; We didn't bother checking for the C flag at each step because we
; check for overflow afterwards. If ixh < d, we overflowed
ld a, ixh
cp d
jr c, .error ; carry is set? overflow

; Sets Z is A is ' ', ',', ';', CR, LF, or null.
isSepOrLineEnd:
call isSep
ret z
call isLineEndOrComment
inc hl
jr .loop

.error:
call JUMP_UNSETZ
.end:
pop bc
pop de
pop hl
ret

; Checks whether string at (HL) is a label, that is, whether it ends with a ":"
; Sets Z if yes, unset if no.
;
; If it's a label, we change the trailing ':' char with a null char. It's a bit
; dirty, but it's the easiest way to proceed.
isLabel:

; Parse string at (HL) as a hexadecimal value and return value in IX under the
; same conditions as parseNumber.
parseHexadecimal:
push hl
ld a, ':'
call JUMP_FINDCHAR
ld a, (hl)
cp ':'
jr nz, .nomatch
; We also have to check that it's our last char.
xor a
ld ixh, a
inc hl ; get rid of "0x"
inc hl
ld a, (hl)
or a ; cp 0
jr nz, .nomatch ; not a null char following the :. no match.
; We have a match!
; Remove trailing ':'
xor a ; Z is set
ld (hl), a
call strlen
cp 3
jr c, .single
cp 5
jr c, .double
; too long, error
jr .error
.double:
call JUMP_PARSEHEXPAIR ; moves HL to last char of pair
jr c, .error
inc hl ; now HL is on first char of next pair
ld ixh, a
.single:
call JUMP_PARSEHEXPAIR
jr c, .error
ld ixl, a
cp a ; ensure Z
jr .end
.nomatch:
.error:
call JUMP_UNSETZ
.end:
pop hl
ret

; read word in (HL) and put it in (scratchpad), null terminated, for a maximum
; of SCRATCHPAD_SIZE-1 characters. As a result, A is the read length. HL is
; advanced to the next separator char.
readWord:
push bc
push de
ld de, scratchpad
ld b, SCRATCHPAD_SIZE-1
.loop:
; Sets Z if (HL) has a '0x' or '0X' prefix.
hasHexPrefix:
ld a, (hl)
call isSepOrLineEnd
jr z, .success
call JUMP_UPCASE
ld (de), a
cp '0'
ret nz
push hl
inc hl
inc de
djnz .loop
.success:
xor a
ld (de), a
ld a, SCRATCHPAD_SIZE-1
sub a, b
.end:
pop de
pop bc
ret

; (HL) being a string, advance it to the next non-sep character.
; Set Z if we could do it before the line ended, reset Z if we couldn't.
toWord:
.loop:
ld a, (hl)
call isLineEndOrComment
jr z, .error
call isSep
jr nz, .success
inc hl
jr .loop
.error:
call JUMP_UNSETZ
ret
.success:
xor a ; ensure Z
ret

; Parse line in (HL) and read the next token in BC. The token is written on
; two bytes (B and C). B is a token type (TOK_* constants) and C is an ID
; specific to that token type.
; Advance HL to after the read word.
; If no token matches, TOK_BAD is written to B
tokenize:
call toWord
jr nz, .emptyline
call readWord
push hl ; Save advanced HL for later
ld hl, scratchpad
call isLabel
jr z, .label
call getInstID
jr z, .instr
call getDirectiveID
jr z, .direc
; no match
ld b, TOK_BAD
jr .end
.instr:
ld b, TOK_INSTR
jr .end
.direc:
ld b, TOK_DIRECTIVE
jr .end
.label:
ld b, TOK_LABEL
cp 'x'
jr z, .end
cp 'X'
.end:
ld c, a
pop hl
ret
.emptyline:
ld b, TOK_EMPTY
; no HL to pop, we jumped before the push

; Parses the string at (HL) and returns the 16-bit value in IX.
; 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.
parseNumber:
call hasHexPrefix
jr z, parseHexadecimal
jr 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.
parseNumberOrSymbol:
call parseNumber
ret z
; Not a number. Try symbol
push de
call symGetVal
jr nz, .notfound ; Z already unset
; Found! value in DE. We need it in IX
ld ixh, d
ld ixl, e
; Z already set
.notfound:
pop de
ret

+ 146
- 0
apps/zasm/tok.asm View File

@@ -0,0 +1,146 @@
; *** Consts ***
TOK_INSTR .equ 0x01
TOK_DIRECTIVE .equ 0x02
TOK_LABEL .equ 0x03
TOK_EMPTY .equ 0xfe ; not a bad token, just an empty line
TOK_BAD .equ 0xff

.equ SCRATCHPAD_SIZE 0x20
; *** Variables ***
scratchpad:
.fill SCRATCHPAD_SIZE

; *** Code ***

; Sets Z is A is ';' or null.
isLineEndOrComment:
cp ';'
ret z
or a ; cp 0
ret

; Sets Z is A is ' ' '\t' or ','
isSep:
cp ' '
ret z
cp 0x09
ret z
cp ','
ret

; Sets Z is A is ' ', ',', ';', CR, LF, or null.
isSepOrLineEnd:
call isSep
ret z
call isLineEndOrComment
ret

; Checks whether string at (HL) is a label, that is, whether it ends with a ":"
; Sets Z if yes, unset if no.
;
; If it's a label, we change the trailing ':' char with a null char. It's a bit
; dirty, but it's the easiest way to proceed.
isLabel:
push hl
ld a, ':'
call JUMP_FINDCHAR
ld a, (hl)
cp ':'
jr nz, .nomatch
; We also have to check that it's our last char.
inc hl
ld a, (hl)
or a ; cp 0
jr nz, .nomatch ; not a null char following the :. no match.
; We have a match!
; Remove trailing ':'
xor a ; Z is set
ld (hl), a
jr .end
.nomatch:
call JUMP_UNSETZ
.end:
pop hl
ret

; read word in (HL) and put it in (scratchpad), null terminated, for a maximum
; of SCRATCHPAD_SIZE-1 characters. As a result, A is the read length. HL is
; advanced to the next separator char.
readWord:
push bc
push de
ld de, scratchpad
ld b, SCRATCHPAD_SIZE-1
.loop:
ld a, (hl)
call isSepOrLineEnd
jr z, .success
call JUMP_UPCASE
ld (de), a
inc hl
inc de
djnz .loop
.success:
xor a
ld (de), a
ld a, SCRATCHPAD_SIZE-1
sub a, b
.end:
pop de
pop bc
ret

; (HL) being a string, advance it to the next non-sep character.
; Set Z if we could do it before the line ended, reset Z if we couldn't.
toWord:
.loop:
ld a, (hl)
call isLineEndOrComment
jr z, .error
call isSep
jr nz, .success
inc hl
jr .loop
.error:
call JUMP_UNSETZ
ret
.success:
xor a ; ensure Z
ret

; Parse line in (HL) and read the next token in BC. The token is written on
; two bytes (B and C). B is a token type (TOK_* constants) and C is an ID
; specific to that token type.
; Advance HL to after the read word.
; If no token matches, TOK_BAD is written to B
tokenize:
call toWord
jr nz, .emptyline
call readWord
push hl ; Save advanced HL for later
ld hl, scratchpad
call isLabel
jr z, .label
call getInstID
jr z, .instr
call getDirectiveID
jr z, .direc
; no match
ld b, TOK_BAD
jr .end
.instr:
ld b, TOK_INSTR
jr .end
.direc:
ld b, TOK_DIRECTIVE
jr .end
.label:
ld b, TOK_LABEL
.end:
ld c, a
pop hl
ret
.emptyline:
ld b, TOK_EMPTY
; no HL to pop, we jumped before the push
ret

Loading…
Cancel
Save