From dbd895bbc9e16c678047c3a75d3f292d8713f2d3 Mon Sep 17 00:00:00 2001 From: Ketmar Dark Date: Sat, 15 Aug 2020 05:34:53 +0300 Subject: [PATCH] merged expression parser with assembler itself --- bzasm80.zas | 805 +++++++++++++++++++++++++++++++++++++++++++++++++++++- labman.zas | 2 +- main.zas | 20 +- output.zas | 6 +- parser_expr.zas | 444 ------------------------------ parser_number.zas | 314 --------------------- 6 files changed, 799 insertions(+), 792 deletions(-) delete mode 100644 parser_expr.zas delete mode 100644 parser_number.zas diff --git a/bzasm80.zas b/bzasm80.zas index 9584b1a..e9f6bd5 100644 --- a/bzasm80.zas +++ b/bzasm80.zas @@ -77,11 +77,42 @@ ASM_SP0: defw 0 ; an unknown mnemonics) ASM_BAD_B: defw 0 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; expression parser variables + +;; set this to the address of error routine +;; note that you cannot return from it, you HAVE to abort everything +;; also note that machine stack is undefined, and SP should be set +;; to some initial value +;; "undefined" means that machine stack can contain alot of garbage, +;; but will never be underflowed +;; +;; this function is called with error code in A +EXPR_ERROR_CB: defw 0 + +;; error codes +;; expected number, but got something incomprehensible +EXPR_ERR_NUMBER_EXPECTED equ 1 +;; expected string, but got something incomprehensible +EXPR_ERR_STRING_EXPECTED equ 2 +;; expected ")", but got something strange +EXPR_ERR_RPAREN_EXPECTED equ 3 +;; expected ")", but got something strange +EXPR_ERR_DIVISION_BY_ZERO equ 4 + +;; offset your own error codes with this +EXPR_ERR_USERDEF equ 5 + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; string terminator CR equ 13 -csizestart = $ +asmsizest = $ +csizest = $ + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ASSEMBLER: ;; LANGUAGE-INDEPENDENT CONTROL SECTION: @@ -593,7 +624,7 @@ NUMBER: push bc push de push ix - call @PARSE_INT_EXPR + call PARSE_INT_EXPR ; HL: expression value pop ix pop de @@ -679,7 +710,7 @@ ORC: COM_DEFBW: push af push ix - call @PARSE_INT_EXPR + call PARSE_INT_EXPR pop ix ; HL: value ld (ix),l @@ -724,7 +755,7 @@ MISC_DEFM: djnz MISC_ORG DEFM_LOOP: push ix - call @PARSE_STR_EXPR + call PARSE_STR_EXPR pop ix ; HL: buffer address ; E: buffer length @@ -950,13 +981,762 @@ ASM_IS_SEP: cp CR ret -csizeend = $ -$printf "assembler size: %d", csizeend-csizestart +csizest = $-csizest +$printf "assembler size: %d", csizest + + +csizest = $ + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; parse integer number (without sign) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; this is advanced number parser +;; it understands alot of suffixes and prefixes: +;; $ -- lone "$" means "current PC" +;; #nnnn -- hex +;; $nnnn -- hex +;; &nnnn -- hex +;; %nnnn -- binary +;; 0Xnnn -- hex +;; 0Onnn -- octal +;; 0Bnnn -- binary +;; nnnnH -- hex +;; nnnnB -- binary +;; nnnnO -- octal +;; everything is case-insensitive +;; you can separate digits with underscores +;; (i.e. "12_34_5" will work, underscores are simply ignored) + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; parse a number, push it onto the stack +;; understands prefixes and suffixes +;; +;; IN: +;; IY: text buffer +;; OUT: +;; IY: text buffer after the expression +;; HL: number +;; carry flag reset +;; OR: (cannot parse as a number) +;; IY: unchanged +;; carry flag set +;; DE,AF,flags: dead +;; +PARSE_NUMBER: + call PARSER_SKIP_BLANKS + scf + ret z + push iy ; we will need to rollback on error + ; A already contains a char, loaded by `PARSER_SKIP_BLANKS` + cp '#' + jr z,.hexprefix + cp '$' + jr z,.maybe_lone_dollar + cp '&' + jr z,.hexprefix + cp '%' + jr z,.binprefix + ; no, leading zero doesn't mean "octal", this is stupid + ; but we may have prefixes like "0x" and such + cp '0' + jr z,.maybe_zero_prefix + ; check if we have a digit here + call PARSER_CONV_DIGIT + jr c,.not_a_number_carry_set + cp 10 + ; nope, do not allow it, all numbers must start with a digit + ;jr nc,.must_be_hex_with_sfx + ccf + jr c,.not_a_number_carry_set +.do_normal_decimal: + ; done with prefixes, try decimal number + ; we'll switch to suffix checking on hex digit + ld hl,0 ; accumulator +.decimal_loop: + call .getDigit + jr c,.decimal_done + cp 10 + jr nc,.must_be_hex_with_sfx + ; HL=HL*10 + add hl,hl + ld de,hl + add hl,hl + add hl,hl + add hl,de + ; HL=HL+A + ld e,a + ld d,0 + add hl,de + ; next char + inc iy + jr .decimal_loop +.decimal_done: + ; check for suffix + ld a,(iy) + and %11011111 ; cheap uppercase + cp 'H' + jr z,.must_be_hex_with_sfx + cp 'B' + jr z,.bin_with_sfx + cp 'O' + jr z,.oct_with_sfx + ; no suffix, we're done + +.success: + pop de ; drop iy + ; reset carry flag + or a + ret + +.not_a_number_carry_set: + pop iy + ret + +.hexprefix: + ; skip prefix + inc iy + call .parse_as_hex +.after_prefix: + jr c,.not_a_number_carry_set + jr .success + +.maybe_lone_dollar: + ; lone dollar means "PC" + inc iy + call .parse_as_hex + ; the only case we may gen an error here is + ; when our dollar isn't followed by a digit + jr nc,.success + ; lone dollar is good too + ; IY points right after the dollar here + ld hl,(BZ80ASM.PC) + jr .success + +.binprefix: + ; skip prefix + inc iy + call .parse_as_bin + jr .after_prefix + +.maybe_binprefix: + ; things like "0BEEFh" should be parsed as hex + ; skip prefix + inc iy + call .parse_as_bin + jr c,.must_be_hex_with_sfx + ; check for 'H' + ld a,(iy) + and %11011111 ; cheap uppercase + cp 'H' + jr z,.must_be_hex_with_sfx + jr .success + +.octprefix: + ; skip prefix + inc iy + call .parse_as_oct + jr .after_prefix + +.maybe_zero_prefix: + ; check for '0x' and such + ; skip '0' + inc iy + ; load and prefix + ; there's no need to skip it, as it will be + ; skipped by the corresponding subroutine + ld a,(iy) + ; so IY will point to the actual number + and %11011111 ; cheap uppercase + cp 'X' + jr z,.hexprefix + cp 'B' + jr z,.maybe_binprefix + cp 'O' + jr z,.octprefix + ; do not reparse '0', no need to backup + jr .do_normal_decimal + +.must_be_hex_with_sfx: + ; reparse as hex, and check for suffix + pop iy + push iy + call .parse_as_hex + jr c,.not_a_number_carry_set + ld a,(iy) + inc iy + and %11011111 ; cheap uppercase + cp 'H' + jr z,.success + +.bin_with_sfx: + ; reparse as bin, skip suffix (it is guaranteed to be there) + pop iy + push iy + call .parse_as_bin +.done_guaranteed_suffix: + jr c,.not_a_number_carry_set + ; skip suffix + inc iy + jr .success + +.oct_with_sfx: + ; reparse as bin, skip suffix (it is guaranteed to be there) + pop iy + push iy + call .parse_as_bin + jr .done_guaranteed_suffix + +.parse_as_hex: + ld hl,0 ; accumulator + ; check first digit (as this is general parser) + call .getDigitNoUnder + ret c +.parse_as_hex_loop: + inc iy + add hl,hl + add hl,hl + add hl,hl + add hl,hl + ld e,a + ld d,0 + add hl,de + call .getDigit + jr nc,.parse_as_hex_loop + ; clear carry flag (it is always set here) + ccf + ret + +.parse_as_bin: + ld hl,0 ; accumulator + ; check first digit (as this is general parser) + ld a,(iy) + call .getDigitNoUnderBin + ret c +.parse_as_bin_loop: + inc iy + add hl,hl + ld e,a + ld d,0 + add hl,de + call .getBinDigit + jr nc,.parse_as_bin_loop + ; clear carry flag (it is always set here) + ccf + ret + +.parse_as_oct: + ld hl,0 ; accumulator + ; check first digit (as this is general parser) + ld a,(iy) + call .getDigitNoUnderOct +.parse_as_oct_loop: + inc iy + add hl,hl + add hl,hl + add hl,hl + ld e,a + ld d,0 + add hl,de + call .getOctDigit + jr nc,.parse_as_oct_loop + ; clear carry flag (it is always set here) + ccf + ret + + +.getDigit_inc: + inc iy +.getDigit: + ld a,(iy) + cp '_' + jr z,.getDigit_inc + jp PARSER_CONV_DIGIT + + ; d: base +.getDigitInBase: + call .getDigit + ret c + cp d + ccf + ret + +.getDigitNoUnder: + ld a,(iy) + jp PARSER_CONV_DIGIT + +.getDecDigit: + ld d,10 + jr .getDigitInBase + +.getDigitNoUnderOct: + ld a,(iy) +.getOctDigit: + ld d,8 + jr .getDigitInBase + +.getDigitNoUnderBin: + ld a,(iy) +.getBinDigit: + ld d,2 + jr .getDigitInBase + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; converts 'A' to digit (assume hex) +;; carry set: not a digit char (and A is destroyed) +;; +PARSER_CONV_DIGIT: + sub '0' + ret c + cp 10 + ccf + ret nc + add a,'0' + and %11011111 ; cheap uppercase + sub 'A'-10 + ret c + cp 16 + ccf + ret + +csizest = $-csizest +$printf "assembler numparse size: %d", csizest + + +csizest = $ + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; math expression parser +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +PARSE_EXPR_ERROR_A: + ld hl,(EXPR_ERROR_CB) + jp (hl) + +PARSE_EXPR_ERROR_INT: + ld a,EXPR_ERR_NUMBER_EXPECTED + jr PARSE_EXPR_ERROR_A + +PARSE_EXPR_ERROR_STR: + ld a,EXPR_ERR_STRING_EXPECTED + jr PARSE_EXPR_ERROR_A + +PARSE_EXPR_ERROR_0DIV: + ld a,EXPR_ERR_DIVISION_BY_ZERO + jr PARSE_EXPR_ERROR_A + +PARSE_EXPR_ERROR_RPAREN: + ld a,EXPR_ERR_RPAREN_EXPECTED + jr PARSE_EXPR_ERROR_A + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; parse an integer expression +;; IN: +;; IY: text buffer +;; OUT: +;; IY: text buffer after the expression +;; HL: expression value +;; everything other (including all alternate registers) is dead +;; +;; priorities: +;; unaries +;; * / % +;; + - +;; << >> +;; +PARSE_INT_EXPR: + call PARSER_SKIP_BLANKS + jp z,PARSE_EXPR_ERROR_INT + ; unary "+" or "-"? + call BZ80ASM.SIGN + jr nz,.doexpr + push af + inc iy + call .doexpr + pop af + ; check for negate + cp '-' + ret nz + ; negate HL + or a + ld de,0 + ex de,hl + sbc hl,de + ret + +.doexpr: +;; | +.bitor: + ; get first operand + ; already done above + call .bitxor +.bitor_next: + ; check for operation + call PARSER_SKIP_BLANKS + ret z ; exit on EOL + cp '|' + ret nz + ; get second operand + ld bc,.bitxor + call .go_down_bc + ld a,l + or e + ld l,a + ld a,h + or d + ld h,a + jr .bitor_next + +;; ^ +.bitxor: + ; get first operand + call .bitand +.bitxor_next: + ; check for operation + call PARSER_SKIP_BLANKS + ret z ; exit on EOL + cp '^' + ret nz + ; get second operand + ld bc,.bitand + call .go_down_bc + ld a,l + xor e + ld l,a + ld a,h + xor d + ld h,a + jr .bitxor_next + +;; & +.bitand: + ; get first operand + call .shlshr +.bitand_next: + ; check for operation + call PARSER_SKIP_BLANKS + ret z ; exit on EOL + cp '&' + ret nz + ; get second operand + ld bc,.shlshr + call .go_down_bc + ld a,l + and e + ld l,a + ld a,h + and d + ld h,a + jr .bitand_next + +;; << >> +.shlshr: + call .addsub +.shlshr_next: + ; check for operation + call PARSER_SKIP_BLANKS + ret z ; exit on EOL + ; (iy+0) and (iy+1) should be equal + cp (iy+1) + ret nz + cp '<' ; %0011_1100 + jr z,.doshift + cp '>' ; %0011_1110 + ret nz +.doshift: + ; get second operand + inc iy ; skip operation part + ld bc,.addsub + call .go_down_bc + ex af,af' + ; HL: number to shift + ; DE: amount + ld a,d + or a + jr nz,.shift_too_far + ld a,e + cp 16 + jr nc,.shift_too_far + ld b,a + ex af,af' + cp '<' + jr z,.do_shl + ; shr +.do_shr_loop: + srl h + rr l + djnz .do_shr_loop + jr .shlshr_next + ; shl +.do_shl: + ; shl + sla l + rl h + djnz .do_shl + jr .shlshr_next +.shift_too_far: + ld hl,0 + jr .shlshr_next + +;; + - +.addsub: + ; get first operand + call .muldiv +.addsub_next: + ; check for operation + call PARSER_SKIP_BLANKS + ret z ; exit on EOL + cp '+' ; %0010_1011 + jr z,.doaddsub + cp '-' ; %0010_1101 + ret nz +.doaddsub: + ; get second operand + ld bc,.muldiv + call .go_down_bc + cp '-' + jr z,.dosub + add hl,de + jr .addsub_next +.dosub: + sbc hl,de + jr .addsub_next + +;; * / % +.muldiv: + ; get first operand + call .term +.muldiv_next: + ; check for operation + call PARSER_SKIP_BLANKS + ret z ; exit on EOL + cp '*' ; %0010_1010 + jr z,.domuldiv + cp '/' ; %0010_1111 + jr z,.domuldiv + cp '%' ; %0010_0101 + ret nz +.domuldiv: + ; get second operand + ld bc,.term + call .go_down_bc + ld bc,hl + cp '*' + jr z,.domul + ex af,af' ; save operation + ; div or mod + ld a,e + or d + jp z,PARSE_EXPR_ERROR_0DIV + call PARSER_UDIV_BC_DE + ; was it div or mod? + ex af,af' + cp '%' + jr z,.muldiv_next ; remainder already in hl + ; division + ld hl,bc + jr .muldiv_next +.domul: + call PARSER_UMUL_BC_DE + jr .muldiv_next + +;; parse term, also process unaries and parens +.term: + call PARSER_SKIP_BLANKS + jp z,PARSE_EXPR_ERROR_INT + inc iy ; skip operation + ld c,')' + cp '(' + jr z,.term_lparen + cp '[' + ld c,']' + jr z,.term_lparen + cp '~' + jr z,.term_ubitnot + ; this must be number + dec iy ; undo skip + call PARSE_NUMBER + ; here we can check labels + jp c,PARSE_EXPR_ERROR_INT + ret + + ;; "(" +.term_lparen: + ;; C contains matching rparen + push bc + call PARSE_INT_EXPR + call PARSER_SKIP_BLANKS + jp z,PARSE_EXPR_ERROR_RPAREN + pop bc + cp c + jp nz,PARSE_EXPR_ERROR_RPAREN + inc iy + ret + + ;; "~" +.term_ubitnot: + call .term + ld a,h + cpl + ld h,a + ld a,l + cpl + ld l,a + ret + + ;; call subroutine at BC + ;; AF: preserved + ;; HL: preserved + ;; DE: subroutine result + ;; i.e. HL is op0, DE is op1 +.go_down_bc: + push hl + push af ; A holds operation + inc iy ; skip operation + ld hl,.go_down_bc_ret + push hl + push bc + ret +.go_down_bc_ret: + pop af + pop de + ex de,hl + ret + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; HL=BC*DE +;; +;; BC,DE,A,flags: dead +;; +PARSER_UMUL_BC_DE: + ; DEHL=BC*DE + ld hl,0 + ld a,16 +.loop: + add hl,hl + rl e + rl d + jr nc,.skip + add hl,bc + jr nc,.skip + inc de +.skip: + dec a + jr nz,.loop + ret + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; performs BC/DE +;; OUT: +;; BC: quotient +;; HL: remainder +;; +;; DE,A,flags: dead +;; +PARSER_UDIV_BC_DE: + ld hl,0 + ld a,16 +.loop: + sll c + rl b + adc hl,hl + sbc hl,de + jr nc,.skip + add hl,de + dec c +.skip: + dec a + jr nz,.loop + ret + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; +;; parse a string expression +;; +;; IN: +;; IY: text buffer +;; OUT: +;; HL: string buffer start +;; E: parsed string length +;; everything other (including all alternate registers) is dead +;; +PARSE_STR_EXPR: + call PARSER_SKIP_BLANKS + jp z,PARSE_EXPR_ERROR_STR + cp 34 + jr z,.strok + cp 39 + jp nz,PARSE_EXPR_ERROR_STR +.strok: + ld c,a ; terminator + inc iy + ; remember buffer start + push iy +.strloop: + ld a,(iy) + or a + jp z,PARSE_EXPR_ERROR_STR + cp CR + jp z,PARSE_EXPR_ERROR_STR + inc iy + cp c + jr nz,.strloop + ; done string parsing, calc length + pop hl ; buffer start + ex de,hl + push iy + pop hl + ; DE: buffer start + ; HL: buffer end + or a + sbc hl,de + ex de,hl + ret + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; skip blanks +;; returns current char in A +;; sets zero flag on EOL +;; IN: +;; IY: text buffer +;; OUT: +;; IY: text buffer at non-blank or EOL +;; A: non-blank or EOL char +;; zero flag is set on EOL +;; +PARSER_SKIP_BLANKS: + ld a,(iy) + or a + ret z + cp 13 + ret z + inc iy + cp 33 + jr c,PARSER_SKIP_BLANKS + dec iy + ; reset zero flag + or a + ret + +csizest = $-csizest +$printf "assembler exprparse size: %d", csizest + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; various tables -- mnemonics, operands... ;; -csizestart = $ +csizest = $ ;; number of "trivial" opcodes without any special processing OPC_COUNT_GROUPS_0_AND_1 equ 39 @@ -1304,11 +2084,14 @@ LDOPS: ; defb 0 -csizeend = $ -$printf "assembler tables size: %d", csizeend-csizestart +csizest = $-csizest +$printf "assembler tables size: %d", csizest + +asmsizest = $-asmsizest +$printf "full assembler size: %d", asmsizest ; so they won't clutter symbol table -csizeend = -1 -csizestart = -1 +csizest = -1 +asmsizest = -1 ENDMODULE BZ80ASM diff --git a/labman.zas b/labman.zas index 43cd0c4..834db0d 100644 --- a/labman.zas +++ b/labman.zas @@ -62,7 +62,7 @@ DEFLABEL: .error_identifier_expected: ld a,EXPR_ERR_ID_EXPECTED - jp PARSE_EXPR_ERROR_A + jp BZ80ASM.PARSE_EXPR_ERROR_A msg_getvar: defx "NEWLABEL:<" diff --git a/main.zas b/main.zas index 8f83276..cf6bb6a 100644 --- a/main.zas +++ b/main.zas @@ -3,27 +3,9 @@ org #8000 ent main -totalasm_start = $ include "bzasm80.zas" csizestart = $ - include "parser_number.zas" -csizeend = $ -$printf "parser_number size: %d", csizeend-csizestart - -csizestart = $ - include "parser_expr.zas" -csizeend = $ -$printf "parser_expr size: %d", csizeend-csizestart - -totalasm_end = $ -$printf "total assembler size: %d", totalasm_end-totalasm_start - -; so they won't clutter symbol table -totalasm_end = -1 -totalasm_start = -1 - -csizestart = $ include "labman.zas" csizeend = $ $printf "label manager size: %d", csizeend-csizestart @@ -49,7 +31,7 @@ main: ; setup callbacks ld hl,expr_error_cb - ld (EXPR_ERROR_CB),hl + ld (BZ80ASM.EXPR_ERROR_CB),hl ld hl,expr_error_jrfar_cb ld (BZ80ASM.ASM_JR_TOO_FAR_CB),hl diff --git a/output.zas b/output.zas index b3b6275..a4a2c1e 100644 --- a/output.zas +++ b/output.zas @@ -32,9 +32,9 @@ printstrnl: jp EMIT -EXPR_ERR_ID_EXPECTED equ EXPR_ERR_USERDEF+0 -EXPR_ERR_JR_TOO_FAR equ EXPR_ERR_USERDEF+1 -EXPR_ERR_BAD_SYNTAX equ EXPR_ERR_USERDEF+2 +EXPR_ERR_ID_EXPECTED equ BZ80ASM.EXPR_ERR_USERDEF+0 +EXPR_ERR_JR_TOO_FAR equ BZ80ASM.EXPR_ERR_USERDEF+1 +EXPR_ERR_BAD_SYNTAX equ BZ80ASM.EXPR_ERR_USERDEF+2 errmsg_table: defx "number expected" diff --git a/parser_expr.zas b/parser_expr.zas deleted file mode 100644 index 00055bc..0000000 --- a/parser_expr.zas +++ /dev/null @@ -1,444 +0,0 @@ -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; math expression parser -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - - -;; set this to the address of error routine -;; note that you cannot return from it, you HAVE to abort everything -;; also note that machine stack is undefined, and SP should be set -;; to some initial value -;; "undefined" means that machine stack can contain alot of garbage, -;; but will never be underflowed -;; -;; this function is called with error code in A -EXPR_ERROR_CB: defw 0 - -;; error codes -;; expected number, but got something incomprehensible -EXPR_ERR_NUMBER_EXPECTED equ 1 -;; expected string, but got something incomprehensible -EXPR_ERR_STRING_EXPECTED equ 2 -;; expected ")", but got something strange -EXPR_ERR_RPAREN_EXPECTED equ 3 -;; expected ")", but got something strange -EXPR_ERR_DIVISION_BY_ZERO equ 4 - -;; offset your own error codes with this -EXPR_ERR_USERDEF equ 5 - - -PARSE_EXPR_ERROR_A: - ld hl,(EXPR_ERROR_CB) - jp (hl) - -PARSE_EXPR_ERROR_INT: - ld a,EXPR_ERR_NUMBER_EXPECTED - jr PARSE_EXPR_ERROR_A - -PARSE_EXPR_ERROR_STR: - ld a,EXPR_ERR_STRING_EXPECTED - jr PARSE_EXPR_ERROR_A - -PARSE_EXPR_ERROR_0DIV: - ld a,EXPR_ERR_DIVISION_BY_ZERO - jr PARSE_EXPR_ERROR_A - -PARSE_EXPR_ERROR_RPAREN: - ld a,EXPR_ERR_RPAREN_EXPECTED - jr PARSE_EXPR_ERROR_A - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; -;; parse an integer expression -;; IN: -;; IY: text buffer -;; OUT: -;; IY: text buffer after the expression -;; HL: expression value -;; everything other (including all alternate registers) is dead -;; -;; priorities: -;; unaries -;; * / % -;; + - -;; << >> -;; -PARSE_INT_EXPR: - call PARSER_SKIP_BLANKS - jp z,PARSE_EXPR_ERROR_INT - ; unary "+" or "-"? - call BZ80ASM.SIGN - jr nz,.doexpr - push af - inc iy - call .doexpr - pop af - ; check for negate - cp '-' - ret nz - ; negate HL - or a - ld de,0 - ex de,hl - sbc hl,de - ret - -.doexpr: -;; | -.bitor: - ; get first operand - ; already done above - call .bitxor -.bitor_next: - ; check for operation - call PARSER_SKIP_BLANKS - ret z ; exit on EOL - cp '|' - ret nz - ; get second operand - ld bc,.bitxor - call .go_down_bc - ld a,l - or e - ld l,a - ld a,h - or d - ld h,a - jr .bitor_next - -;; ^ -.bitxor: - ; get first operand - call .bitand -.bitxor_next: - ; check for operation - call PARSER_SKIP_BLANKS - ret z ; exit on EOL - cp '^' - ret nz - ; get second operand - ld bc,.bitand - call .go_down_bc - ld a,l - xor e - ld l,a - ld a,h - xor d - ld h,a - jr .bitxor_next - -;; & -.bitand: - ; get first operand - call .shlshr -.bitand_next: - ; check for operation - call PARSER_SKIP_BLANKS - ret z ; exit on EOL - cp '&' - ret nz - ; get second operand - ld bc,.shlshr - call .go_down_bc - ld a,l - and e - ld l,a - ld a,h - and d - ld h,a - jr .bitand_next - -;; << >> -.shlshr: - call .addsub -.shlshr_next: - ; check for operation - call PARSER_SKIP_BLANKS - ret z ; exit on EOL - ; (iy+0) and (iy+1) should be equal - cp (iy+1) - ret nz - cp '<' ; %0011_1100 - jr z,.doshift - cp '>' ; %0011_1110 - ret nz -.doshift: - ; get second operand - inc iy ; skip operation part - ld bc,.addsub - call .go_down_bc - ex af,af' - ; HL: number to shift - ; DE: amount - ld a,d - or a - jr nz,.shift_too_far - ld a,e - cp 16 - jr nc,.shift_too_far - ld b,a - ex af,af' - cp '<' - jr z,.do_shl - ; shr -.do_shr_loop: - srl h - rr l - djnz .do_shr_loop - jr .shlshr_next - ; shl -.do_shl: - ; shl - sla l - rl h - djnz .do_shl - jr .shlshr_next -.shift_too_far: - ld hl,0 - jr .shlshr_next - -;; + - -.addsub: - ; get first operand - call .muldiv -.addsub_next: - ; check for operation - call PARSER_SKIP_BLANKS - ret z ; exit on EOL - cp '+' ; %0010_1011 - jr z,.doaddsub - cp '-' ; %0010_1101 - ret nz -.doaddsub: - ; get second operand - ld bc,.muldiv - call .go_down_bc - cp '-' - jr z,.dosub - add hl,de - jr .addsub_next -.dosub: - sbc hl,de - jr .addsub_next - -;; * / % -.muldiv: - ; get first operand - call .term -.muldiv_next: - ; check for operation - call PARSER_SKIP_BLANKS - ret z ; exit on EOL - cp '*' ; %0010_1010 - jr z,.domuldiv - cp '/' ; %0010_1111 - jr z,.domuldiv - cp '%' ; %0010_0101 - ret nz -.domuldiv: - ; get second operand - ld bc,.term - call .go_down_bc - ld bc,hl - cp '*' - jr z,.domul - ex af,af' ; save operation - ; div or mod - ld a,e - or d - jp z,PARSE_EXPR_ERROR_0DIV - call PARSER_UDIV_BC_DE - ; was it div or mod? - ex af,af' - cp '%' - jr z,.muldiv_next ; remainder already in hl - ; division - ld hl,bc - jr .muldiv_next -.domul: - call PARSER_UMUL_BC_DE - jr .muldiv_next - -;; parse term, also process unaries and parens -.term: - call PARSER_SKIP_BLANKS - jp z,PARSE_EXPR_ERROR_INT - inc iy ; skip operation - ld c,')' - cp '(' - jr z,.term_lparen - cp '[' - ld c,']' - jr z,.term_lparen - cp '~' - jr z,.term_ubitnot - ; this must be number - dec iy ; undo skip - call PARSE_NUMBER - ; here we can check labels - jp c,PARSE_EXPR_ERROR_INT - ret - - ;; "(" -.term_lparen: - ;; C contains matching rparen - push bc - call PARSE_INT_EXPR - call PARSER_SKIP_BLANKS - jp z,PARSE_EXPR_ERROR_RPAREN - pop bc - cp c - jp nz,PARSE_EXPR_ERROR_RPAREN - inc iy - ret - - ;; "~" -.term_ubitnot: - call .term - ld a,h - cpl - ld h,a - ld a,l - cpl - ld l,a - ret - - ;; call subroutine at BC - ;; AF: preserved - ;; HL: preserved - ;; DE: subroutine result - ;; i.e. HL is op0, DE is op1 -.go_down_bc: - push hl - push af ; A holds operation - inc iy ; skip operation - ld hl,.go_down_bc_ret - push hl - push bc - ret -.go_down_bc_ret: - pop af - pop de - ex de,hl - ret - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; -;; HL=BC*DE -;; -;; BC,DE,A,flags: dead -;; -PARSER_UMUL_BC_DE: - ; DEHL=BC*DE - ld hl,0 - ld a,16 -.loop: - add hl,hl - rl e - rl d - jr nc,.skip - add hl,bc - jr nc,.skip - inc de -.skip: - dec a - jr nz,.loop - ret - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; -;; performs BC/DE -;; OUT: -;; BC: quotient -;; HL: remainder -;; -;; DE,A,flags: dead -;; -PARSER_UDIV_BC_DE: - ld hl,0 - ld a,16 -.loop: - sll c - rl b - adc hl,hl - sbc hl,de - jr nc,.skip - add hl,de - dec c -.skip: - dec a - jr nz,.loop - ret - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; -;; parse a string expression -;; -;; IN: -;; IY: text buffer -;; OUT: -;; HL: string buffer start -;; E: parsed string length -;; everything other (including all alternate registers) is dead -;; -PARSE_STR_EXPR: - call PARSER_SKIP_BLANKS - jp z,PARSE_EXPR_ERROR_STR - cp 34 - jr z,.strok - cp 39 - jp nz,PARSE_EXPR_ERROR_STR -.strok: - ld c,a ; terminator - inc iy - ld hl,ACCS - push hl -.strloop: - ld a,(iy) - or a - jp z,PARSE_EXPR_ERROR_STR - inc iy - cp c - jr z,.strdone - ld (hl),a - inc hl - jr .strloop -.strdone: - pop de - or a - sbc hl,de - ex de,hl - ret - -;; string accumulator -ACCS: defs 256,0 - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; skip blanks -;; returns current char in A -;; sets zero flag on EOL -;; IN: -;; IY: text buffer -;; OUT: -;; IY: text buffer at non-blank or EOL -;; A: non-blank or EOL char -;; zero flag is set on EOL -;; -PARSER_SKIP_BLANKS: - ld a,(iy) - or a - ret z - cp 13 - ret z - inc iy - cp 33 - jr c,PARSER_SKIP_BLANKS - dec iy - ; reset zero flag - or a - ret diff --git a/parser_number.zas b/parser_number.zas deleted file mode 100644 index 2104a6a..0000000 --- a/parser_number.zas +++ /dev/null @@ -1,314 +0,0 @@ -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; parse integer number (without sign) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -;; this is advanced number parser -;; it understands alot of suffixes and prefixes: -;; $ -- lone "$" means "current PC" -;; #nnnn -- hex -;; $nnnn -- hex -;; &nnnn -- hex -;; %nnnn -- binary -;; 0Xnnn -- hex -;; 0Onnn -- octal -;; 0Bnnn -- binary -;; nnnnH -- hex -;; nnnnB -- binary -;; nnnnO -- octal -;; everything is case-insensitive -;; you can separate digits with underscores -;; (i.e. "12_34_5" will work, underscores are simply ignored) - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; -;; parse a number, push it onto the stack -;; understands prefixes and suffixes -;; -;; IN: -;; IY: text buffer -;; OUT: -;; IY: text buffer after the expression -;; HL: number -;; carry flag reset -;; OR: (cannot parse as a number) -;; IY: unchanged -;; carry flag set -;; DE,AF,flags: dead -;; -PARSE_NUMBER: - call PARSER_SKIP_BLANKS - scf - ret z - push iy ; we will need to rollback on error - ; A already contains a char, loaded by `PARSER_SKIP_BLANKS` - cp '#' - jr z,.hexprefix - cp '$' - jr z,.maybe_lone_dollar - cp '&' - jr z,.hexprefix - cp '%' - jr z,.binprefix - ; no, leading zero doesn't mean "octal", this is stupid - ; but we may have prefixes like "0x" and such - cp '0' - jr z,.maybe_zero_prefix - ; check if we have a digit here - call PARSER_CONV_DIGIT - jr c,.not_a_number_carry_set - cp 10 - ; nope, do not allow it, all numbers must start with a digit - ;jr nc,.must_be_hex_with_sfx - ccf - jr c,.not_a_number_carry_set -.do_normal_decimal: - ; done with prefixes, try decimal number - ; we'll switch to suffix checking on hex digit - ld hl,0 ; accumulator -.decimal_loop: - call .getDigit - jr c,.decimal_done - cp 10 - jr nc,.must_be_hex_with_sfx - ; HL=HL*10 - add hl,hl - ld de,hl - add hl,hl - add hl,hl - add hl,de - ; HL=HL+A - ld e,a - ld d,0 - add hl,de - ; next char - inc iy - jr .decimal_loop -.decimal_done: - ; check for suffix - ld a,(iy) - and %11011111 ; cheap uppercase - cp 'H' - jr z,.must_be_hex_with_sfx - cp 'B' - jr z,.bin_with_sfx - cp 'O' - jr z,.oct_with_sfx - ; no suffix, we're done - -.success: - pop de ; drop iy - ; reset carry flag - or a - ret - -.not_a_number_carry_set: - pop iy - ret - -.hexprefix: - ; skip prefix - inc iy - call .parse_as_hex -.after_prefix: - jr c,.not_a_number_carry_set - jr .success - -.maybe_lone_dollar: - ; lone dollar means "PC" - inc iy - call .parse_as_hex - ; the only case we may gen an error here is - ; when our dollar isn't followed by a digit - jr nc,.success - ; lone dollar is good too - ; IY points right after the dollar here - ld hl,(BZ80ASM.PC) - jr .success - -.binprefix: - ; skip prefix - inc iy - call .parse_as_bin - jr .after_prefix - -.maybe_binprefix: - ; things like "0BEEFh" should be parsed as hex - ; skip prefix - inc iy - call .parse_as_bin - jr c,.must_be_hex_with_sfx - ; check for 'H' - ld a,(iy) - and %11011111 ; cheap uppercase - cp 'H' - jr z,.must_be_hex_with_sfx - jr .success - -.octprefix: - ; skip prefix - inc iy - call .parse_as_oct - jr .after_prefix - -.maybe_zero_prefix: - ; check for '0x' and such - ; skip '0' - inc iy - ; load and prefix - ; there's no need to skip it, as it will be - ; skipped by the corresponding subroutine - ld a,(iy) - ; so IY will point to the actual number - and %11011111 ; cheap uppercase - cp 'X' - jr z,.hexprefix - cp 'B' - jr z,.maybe_binprefix - cp 'O' - jr z,.octprefix - ; do not reparse '0', no need to backup - jr .do_normal_decimal - -.must_be_hex_with_sfx: - ; reparse as hex, and check for suffix - pop iy - push iy - call .parse_as_hex - jr c,.not_a_number_carry_set - ld a,(iy) - inc iy - and %11011111 ; cheap uppercase - cp 'H' - jr z,.success - -.bin_with_sfx: - ; reparse as bin, skip suffix (it is guaranteed to be there) - pop iy - push iy - call .parse_as_bin -.done_guaranteed_suffix: - jr c,.not_a_number_carry_set - ; skip suffix - inc iy - jr .success - -.oct_with_sfx: - ; reparse as bin, skip suffix (it is guaranteed to be there) - pop iy - push iy - call .parse_as_bin - jr .done_guaranteed_suffix - -.parse_as_hex: - ld hl,0 ; accumulator - ; check first digit (as this is general parser) - call .getDigitNoUnder - ret c -.parse_as_hex_loop: - inc iy - add hl,hl - add hl,hl - add hl,hl - add hl,hl - ld e,a - ld d,0 - add hl,de - call .getDigit - jr nc,.parse_as_hex_loop - ; clear carry flag (it is always set here) - ccf - ret - -.parse_as_bin: - ld hl,0 ; accumulator - ; check first digit (as this is general parser) - ld a,(iy) - call .getDigitNoUnderBin - ret c -.parse_as_bin_loop: - inc iy - add hl,hl - ld e,a - ld d,0 - add hl,de - call .getBinDigit - jr nc,.parse_as_bin_loop - ; clear carry flag (it is always set here) - ccf - ret - -.parse_as_oct: - ld hl,0 ; accumulator - ; check first digit (as this is general parser) - ld a,(iy) - call .getDigitNoUnderOct -.parse_as_oct_loop: - inc iy - add hl,hl - add hl,hl - add hl,hl - ld e,a - ld d,0 - add hl,de - call .getOctDigit - jr nc,.parse_as_oct_loop - ; clear carry flag (it is always set here) - ccf - ret - - -.getDigit_inc: - inc iy -.getDigit: - ld a,(iy) - cp '_' - jr z,.getDigit_inc - jp PARSER_CONV_DIGIT - - ; d: base -.getDigitInBase: - call .getDigit - ret c - cp d - ccf - ret - -.getDigitNoUnder: - ld a,(iy) - jp PARSER_CONV_DIGIT - -.getDecDigit: - ld d,10 - jr .getDigitInBase - -.getDigitNoUnderOct: - ld a,(iy) -.getOctDigit: - ld d,8 - jr .getDigitInBase - -.getDigitNoUnderBin: - ld a,(iy) -.getBinDigit: - ld d,2 - jr .getDigitInBase - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; converts 'A' to digit (assume hex) -;; carry set: not a digit char (and A is destroyed) -;; -PARSER_CONV_DIGIT: - sub '0' - ret c - cp 10 - ccf - ret nc - add a,'0' - and %11011111 ; cheap uppercase - sub 'A'-10 - ret c - cp 16 - ccf - ret -- 2.11.4.GIT