1 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2 ;; Z80 assembler, based on the code from BBC Basic for Z80
3 ;; original code was written by R.T.Russell
4 ;; the original license:
6 ;; Copyright (c) 1984-2000 R.T. Russell
8 ;; This software is provided 'as-is', without any express or implied
9 ;; warranty. In no event will the authors be held liable for any damages
10 ;; arising from the use of this software.
12 ;; Permission is granted to anyone to use this software for any purpose,
13 ;; including commercial applications, and to alter it and redistribute it
14 ;; freely, subject to the following restrictions:
16 ;; 1. The origin of this software must not be misrepresented; you must not
17 ;; claim that you wrote the original software. If you use this software
18 ;; in a product, an acknowledgment in the product documentation would be
19 ;; appreciated but is not required.
20 ;; 2. Altered source versions must be plainly marked as such, and must not be
21 ;; misrepresented as being the original software.
22 ;; 3. This notice may not be removed or altered from any source distribution.
24 ;; modifications, cleanups, etc. by Ketmar Dark // Invisible Vector
26 ; define this if you want ORG/ENT/DISP support (not really tested)
27 IF !defined(BZ80ASM_ORGENTDISP)
28 BZ80ASM_ORGENTDISP equ 0
31 ; define this to support things like "LD BC,HL"
32 IF !defined(BZ80ASM_EXTLD)
36 ; define this to support things like "PUSH r16,r16..."
37 IF !defined(BZ80ASM_EXTPUSHPOP)
38 BZ80ASM_EXTPUSHPOP equ 1
43 PC: defw #C000 ; logical program counter for the code
45 IF @BZ80ASM_ORGENTDISP
46 ; ORG/DISP sets this to its value
48 ; ORG/ENT sets this to the corresponding type
49 ; it is reset at each call to ASSEM
54 ORGENT_TYPE: defw ORGENT_NONE
57 ; address of the routine that will be called if JR/DJNZ destination is too far away
58 ; you can simply RET from it if you want to ignore this error
59 ; note that stack is filled with garbage, so use `ASM_ERROR_EXIT` to bail out
60 ; this is not checked for zero, so if you will not init this, "CALL 0" will be executed
61 ; ret L to the byte that will be put on normal return
62 ASM_JR_TOO_FAR_CB: defw 0
64 ; on entry, ASSEM will store SP here
65 ; you can use this to reset the stack, and use RET to return to the caller
66 ; or simply use ASM_ERROR_EXIT (which will set carry too)
69 ; ASSEM sets this if it hits mnemonics without a handler
70 ; you can add your own mnemonics to the assembler, and it
71 ; will set this to non-zero if it hits them
72 ; use this byte as decrementing counter, like this:
79 ; handle first instruction
82 ; handle second instruction
84 ; ADDITIONALLY, this will be set to #FF on invalid token
85 ; this can be used to process labels (because otherwise
86 ; there is no way to tell if we got some bad operands, or
87 ; an unknown mnemonics)
89 ; this is set to non-zero if compiled instruction was
90 ; some data definition like DEFB, and not some real one
91 ; actually, it is set for any "pseudoinstruction", i.e.
92 ; for data defs, ORG/DISP, and user-defined mnemonics, if
94 ASM_WAS_PSEUDO: defb 0
97 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
98 ;; expression parser variables
100 ;; set this to the address of error routine
101 ;; note that you cannot return from it, you HAVE to abort everything
102 ;; also note that machine stack is undefined, and SP should be set
103 ;; to some initial value
104 ;; "undefined" means that machine stack can contain alot of garbage,
105 ;; but will never be underflowed
107 ;; this function is called with error code in A
109 ;; you can load your own error code in A, and do:
110 ;; jp BZ80ASM.PARSE_EXPR_ERROR_A
111 EXPR_ERROR_CB: defw 0
114 ;; expected number, but got something incomprehensible
115 EXPR_ERR_NUMBER_EXPECTED equ 1
116 ;; expected string, but got something incomprehensible
117 EXPR_ERR_STRING_EXPECTED equ 2
118 ;; expected ")", but got something strange
119 EXPR_ERR_RPAREN_EXPECTED equ 3
120 ;; expected ")", but got something strange
121 EXPR_ERR_DIVISION_BY_ZERO equ 4
123 ;; the asm never generates the following errors, but
124 ;; they may be useful for a main driver
126 ;; you can use this to dispatch "short jump destination is too far" error
127 ;; just do this in `ASM_JR_TOO_FAR_CB`:
128 ;; ld a,EXPR_ERR_JR_TOO_FAR
129 ;; jp BZ80ASM.PARSE_EXPR_ERROR_A
130 EXPR_ERR_JR_TOO_FAR equ 5
132 ;; this "unresolved label" error can be used by label manager
133 EXPR_ERR_UNRESOLVED_LABEL equ 6
134 ;; this error can be used by label manager if it cannot parse a label
135 EXPR_ERR_INVALID_LABEL_NAME equ 7
136 ;; this error can be used by label manager
137 EXPR_ERR_DUPLICATE_LABEL equ 8
139 ;; general "bad syntax" error, when you don't have anything better
140 EXPR_ERR_BAD_SYNTAX equ 9
142 ;; offset your own error codes with this
143 EXPR_ERR_USERDEF equ 10
146 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
147 ;; label manager callback
150 ;; this is called from assembler to find an existing label.
151 ;; IY points to the first char of a label name.
152 ;; after parsing, IY should point right after the parsed name.
153 ;; all registers expect IY can be trashed (including IX).
155 ;; for forward referencing, label manager can create unknown
156 ;; labels, and set their value to (BZ80ASM.PC).
157 ;; it is important to set new labels to PC to avoid errors
158 ;; with short jumps if your "jr too far" error handler always
159 ;; bombs out. otherwise, you can set the label to anything
163 ;; IY: text input buffer
165 ;; IY: text input buffer after the label
167 ;; CARRY FLAG: set if label wasn't found
168 ;; expression parser will bomb out in this case
169 ;; on error, IY doesn't matter, because it will be restored
170 ;; by the calling code
174 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
182 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
184 ;; LANGUAGE-INDEPENDENT CONTROL SECTION:
188 ;; IX: destination to put generated code at
191 ;; IY: terminator position in text buffer
192 ;; IX: points right after the generated code
193 ;; DE: length of the generated code
194 ;; carry set if syntax error
195 ;; in case of error, IY will be somewhere around the error position
196 ;; all other registers are undefined
197 ;; others are dead (including extra register set and extra accum/flags)
199 ;; input buffer must be terminated with ':', ';' or CR (13).
200 ;; unterminated input buffer will wreck havoc.
201 ;; the assembler will not modify the input buffer, but note that it
202 ;; will skip all leading delimiters.
204 ;; on exit, all trailing delimiters will be skipped too.
206 ;; also, the assembler doens't try to parse and create new labels, you
207 ;; should do it by yourself. it can be done like this:
209 ;; call BZ80ASM.ASSEM
210 ;; jp nc,assembled_ok
211 ;; here we can try to parse a label, and then try to assemble
212 ;; the rest of the line again
217 IF @BZ80ASM_ORGENTDISP
221 ld (ASM_WAS_PSEUDO),a
236 ; exit if syntax error
238 ; skip delimiters (but not terminators)
240 ; exit with error flag if we're not at a terminator
248 ex de,hl ;DE= NO. OF BYTES
251 ld (PC),hl ;UPDATE PC
256 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
258 ;; jump here to restore stack, and exit with error (carry set)
271 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
273 ;; PROCESSOR-SPECIFIC TRANSLATION SECTION:
275 ;; REGISTER USAGE: B - TYPE OF MOST RECENT OPERAND
276 ;; C - OPCODE BEING BUILT
277 ;; D - (IX) OR (IY) FLAG
278 ;; E - OFFSET FROM IX OR IY
279 ;; HL - NUMERIC OPERAND VALUE
280 ;; IX - CODE DESTINATION
281 ;; IY - SOURCE TEXT POINTER
282 ;; Inputs: A = initial character
283 ;; Outputs: Carry set if syntax error.
290 ; carry flag set on error
292 ; A contains token index
293 ; B contains token data byte
295 ld d,0 ;CLEAR IX/IY FLAG
298 ; A contains token index
299 ; C contains token data byte
300 ; D contains IX/IY flag
302 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
304 ;; GROUP 0 - TRIVIAL CASES REQUIRING NO COMPUTATION
305 ;; GROUP 1 - AS GROUP 0 BUT WITH "ED" PREFIX
307 sub OPC_COUNT_GROUPS_0_AND_1
309 cp OPC_COUNT_GROUP_0-OPC_COUNT_GROUPS_0_AND_1
313 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
315 ;; GROUP 2 - BIT, RES, SET
316 ;; GROUP 3 - RLC, RRC, RL, RR, SLA, SRA, SRL
319 sub OPC_COUNT_GROUPS_2_AND_3
321 cp OPC_COUNT_GROUP_2-OPC_COUNT_GROUPS_2_AND_3
329 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
331 ;; GROUP 4 - PUSH, POP, EX (SP)
334 sub OPC_COUNT_GROUP_4
337 $IF @BZ80ASM_EXTPUSHPOP
344 $IF @BZ80ASM_EXTPUSHPOP
351 ; i need this code to be one byte less, so...
365 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
367 ;; GROUP 5 - SUB, AND, XOR, OR, CP
368 ;; GROUP 6 - ADD, ADC, SBC
371 sub OPC_COUNT_GROUPS_5_AND_6
373 cp OPC_COUNT_GROUP_5-OPC_COUNT_GROUPS_5_AND_6
404 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
406 ;; GROUP 7 - INC, DEC
409 sub OPC_COUNT_GROUP_7
426 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
432 sub OPC_COUNT_GROUPS_8_AND_9
434 cp OPC_COUNT_GROUP_8-OPC_COUNT_GROUPS_8_AND_9
455 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
457 ;; GROUP 10 - JR, DJNZ
460 sub OPC_COUNT_GROUP_10
462 cp OPC_COUNT_GROUP_10_JRS-OPC_COUNT_GROUP_10
484 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
504 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
519 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
534 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
547 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
596 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
597 ;; misc. instructions
600 ld (ASM_WAS_PSEUDO),a
604 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
609 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
612 ld hl,(ASM_JR_TOO_FAR_CB)
615 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
636 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
651 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
661 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
674 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
681 ret z ;REJECT LD (HL),(HL)
688 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
718 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
726 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
727 ;; parse numeric expression
734 ; HL: expression value
742 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
753 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
760 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
767 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
782 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
794 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
810 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
818 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
820 ;; common defb/defw code
821 ;; carry set if we want words
824 ; note that we don't care about A, we only need flags here
832 pop af ; restore flags
837 ex af,af' ; save flags
841 ex af,af' ; restore flags
844 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
854 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
864 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
891 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
896 IF @BZ80ASM_ORGENTDISP
906 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
915 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
924 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
926 ;; error (the thing that should not be)
930 ; set "unknown instruction index"
939 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
940 ;; search the table for a token from the input buffer
941 ;; skips leading delimiters, and the token itself (if found)
942 ;; tokens in the table must have bit 7 set on the last char
943 ;; table ends with zero byte instead of first token char
944 ;; each token is followed by a data byte
948 ;; IY: new position in the text buffer
950 ;; B: token data byte
951 ;; carry flag set on error (invalid char, or token not found)
952 ;; on error, delimiters are still skipped
957 ; main table search entry point
964 ; reject chars with high bit set
970 ; check the first char
975 and %01011111 ; for case-insensitivity
977 ; first char is not matched, skip this token
982 ; skip token data byte
984 ; increment token counter
988 ; first char matched, check the rest
989 ; A holds zero (due to xor/and)
998 ; not the last token char
999 ; this compares (HL) with 0, because
1000 ; A is guaranteed to hold zero here
1002 ; zero byte in token means "skip delimiters"
1003 ; it is used in some opcodes with fixed operands
1007 ; compare with input char
1009 and %01011111 ; for case-insensitivity
1012 ; alas, doesn't match
1014 ; restore input stream pointer
1016 ; and skip this token
1019 ; we'll come here if we succesfully matched a token
1021 ; if it isn't followed by a delimiter, '+' or '-', this is not a valid token
1026 ; this token is valid
1028 ; move token index to A
1030 ; load B with token data byte
1032 ; drop original input stream position
1035 ; note that carry flag is guaranteed to be reset
1039 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1047 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1048 ;; this entry point is used by FIND
1051 ;; this entry point is used to skip blanks and delimiters
1052 ;; note that comma and right paren are considered blanks too
1053 ;; as a consequence, operands may be delimited by spaces, or
1054 ;; right parens, lol
1055 ;; returns current char in A (and IY pointing to it)
1056 ;; zero flag set if we hit a terminator
1057 ;; zero flag reset if we hit a non-delimiter
1059 ; delimiter or terminator?
1062 ; if this is terminator, still stop
1065 ; this is delimiter, skip it
1069 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1070 ;; used by FIND and SKIP
1071 ;; check if the current char is a delimiter or a terminator
1072 ;; zero flag set on delimiter/terminator
1074 ld a,(iy) ;ASSEMBLER DELIMITER
1082 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1083 ;; entry point used by SKIP
1085 cp ';' ;ASSEMBLER TERMINATOR
1088 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1089 ;; also used by assembler to check for command separator
1090 ;; the assembler itself does nothing with separators
1092 cp ':' ;ASSEMBLER SEPARATOR
1098 $printf "assembler size: %d", csizest
1103 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1104 ;; parse integer number (without sign)
1105 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1107 ;; this is advanced number parser
1108 ;; it understands alot of suffixes and prefixes:
1109 ;; $ -- lone "$" means "current PC"
1120 ;; everything is case-insensitive
1121 ;; you can separate digits with underscores
1122 ;; (i.e. "12_34_5" will work, underscores are simply ignored)
1125 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1127 ;; parse a number, push it onto the stack
1128 ;; understands prefixes and suffixes
1133 ;; IY: text buffer after the expression
1136 ;; OR: (cannot parse as a number)
1139 ;; DE,AF,flags: dead
1142 call PARSER_SKIP_BLANKS
1145 push iy ; we will need to rollback on error
1146 ; A already contains a char, loaded by `PARSER_SKIP_BLANKS`
1150 jr z,.maybe_lone_dollar
1155 ; no, leading zero doesn't mean "octal", this is stupid
1156 ; but we may have prefixes like "0x" and such
1158 jr z,.maybe_zero_prefix
1159 ; check if we have a digit here
1160 call PARSER_CONV_DIGIT
1161 jr c,.not_a_number_carry_set
1163 ; nope, do not allow it, all numbers must start with a digit
1164 ;jr nc,.must_be_hex_with_sfx
1166 jr c,.not_a_number_carry_set
1168 ; done with prefixes, try decimal number
1169 ; we'll switch to suffix checking on hex digit
1170 ld hl,0 ; accumulator
1175 jr nc,.must_be_hex_with_sfx
1192 and %11011111 ; cheap uppercase
1194 jr z,.must_be_hex_with_sfx
1199 ; no suffix, we're done
1207 .not_a_number_carry_set:
1216 jr c,.not_a_number_carry_set
1220 ; lone dollar means "PC"
1223 ; the only case we may gen an error here is
1224 ; when our dollar isn't followed by a digit
1226 ; lone dollar is good too
1227 ; IY points right after the dollar here
1238 ; things like "0BEEFh" should be parsed as hex
1242 jr c,.must_be_hex_with_sfx
1245 and %11011111 ; cheap uppercase
1247 jr z,.must_be_hex_with_sfx
1257 ; check for '0x' and such
1261 ; there's no need to skip it, as it will be
1262 ; skipped by the corresponding subroutine
1264 ; so IY will point to the actual number
1265 and %11011111 ; cheap uppercase
1269 jr z,.maybe_binprefix
1272 ; do not reparse '0', no need to backup
1273 jr .do_normal_decimal
1275 .must_be_hex_with_sfx:
1276 ; reparse as hex, and check for suffix
1280 jr c,.not_a_number_carry_set
1283 and %11011111 ; cheap uppercase
1288 ; reparse as bin, skip suffix (it is guaranteed to be there)
1292 .done_guaranteed_suffix:
1293 jr c,.not_a_number_carry_set
1299 ; reparse as bin, skip suffix (it is guaranteed to be there)
1303 jr .done_guaranteed_suffix
1306 ld hl,0 ; accumulator
1307 ; check first digit (as this is general parser)
1308 call .getDigitNoUnder
1320 jr nc,.parse_as_hex_loop
1321 ; clear carry flag (it is always set here)
1326 ld hl,0 ; accumulator
1327 ; check first digit (as this is general parser)
1329 call .getDigitNoUnderBin
1338 jr nc,.parse_as_bin_loop
1339 ; clear carry flag (it is always set here)
1344 ld hl,0 ; accumulator
1345 ; check first digit (as this is general parser)
1347 call .getDigitNoUnderOct
1357 jr nc,.parse_as_oct_loop
1358 ; clear carry flag (it is always set here)
1369 jr PARSER_CONV_DIGIT
1381 jr PARSER_CONV_DIGIT
1387 .getDigitNoUnderOct:
1393 .getDigitNoUnderBin:
1400 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1401 ;; converts 'A' to digit (assume hex)
1402 ;; carry set: not a digit char (and A is destroyed)
1411 and %11011111 ; cheap uppercase
1419 $printf "assembler numparse size: %d", csizest
1424 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1425 ;; math expression parser
1426 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1428 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1432 ;; BC,DE,A,flags: dead
1452 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1478 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1479 ;; operator table, to avoid pasta
1480 ;; bit 0 of operator name means "two equal chars"
1481 ;; name,priority,handler
1482 ;; priority 1 is term/unary
1483 ;; priority 0 is nothing, and it should stay nothing
1513 expr_max_priority equ 7
1515 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1517 ;; HL is op0, DE is op1
1523 jp PARSER_UMUL_BC_DE
1533 ld a,EXPR_ERR_DIVISION_BY_ZERO
1534 jp z,PARSE_EXPR_ERROR_A
1536 jp PARSER_UDIV_BC_DE
1555 ; HL: number to shift
1560 jr nz,.shift_too_far
1563 jr nc,.shift_too_far
1612 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1619 ;; IY: text buffer after the operator
1620 ;; DE: handler address
1622 ;; A: operator priority
1624 ;; zero set if no operator found (and IY is not modified)
1626 ld hl,expr_optable-4
1635 rra ; carry is "two chars"
1642 inc iy ; skip first char
1643 ; it is guaranteed to match, so don't bother skipping it
1652 ; load handler address
1657 or a ; reset zero flag
1665 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1667 ;; parse an integer expression
1671 ;; IY: text buffer after the expression
1672 ;; HL: expression value
1673 ;; everything other (including all alternate registers) is dead
1682 call PARSER_SKIP_BLANKS
1683 jr z,PARSE_EXPR_ERROR_INT
1702 ; start with maximum priority
1703 ld c,expr_max_priority
1708 jr z,PARSE_EXPR_TERM
1713 ; loop until the priority is right
1715 ; C is priority-1 here
1716 ; check if we have an operator
1717 call PARSER_SKIP_BLANKS
1719 ; check for operator
1721 push iy ; save input pointer, we may need to restore it later
1722 call PARSE_EXPR_FINDOP
1723 jr z,.expr_prios_no_op
1724 ; compare priorities
1727 jr nz,.expr_prios_no_op
1728 ; get second operand
1731 push bc ; save priority
1734 ; stack: prio, op0, doer
1736 ; stack: prio, op0, doer
1737 ex de,hl ; de is op1
1740 call PARSE_EXPR_CALL_BC
1751 ;; parse term, also process unaries and parens
1753 call PARSER_SKIP_BLANKS
1754 jr z,PARSE_EXPR_ERROR_INT
1755 inc iy ; skip operation
1758 jr z,PARSE_EXPR_LPAREN
1761 jr z,PARSE_EXPR_LPAREN
1763 jr z,PARSE_EXPR_BITNOT
1764 ; this must be number
1769 push iy ; for correct error position
1770 push ix ; the only register we care about
1771 ld hl,.term_checklabel_ret
1775 .term_checklabel_ret:
1780 ; restore position for correct error reporting
1783 ;jp PARSE_EXPR_ERROR_INT
1785 PARSE_EXPR_ERROR_INT:
1786 ld a,EXPR_ERR_NUMBER_EXPECTED
1789 ld hl,(EXPR_ERROR_CB)
1794 ;; C contains matching rparen
1797 call PARSER_SKIP_BLANKS
1798 jr z,PARSE_EXPR_ERROR_RPAREN
1804 PARSE_EXPR_ERROR_RPAREN:
1805 ld a,EXPR_ERR_RPAREN_EXPECTED
1806 jr PARSE_EXPR_ERROR_A
1810 call PARSE_EXPR_TERM
1820 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1822 ;; parse a string expression
1827 ;; HL: string buffer start
1828 ;; E: parsed string length
1829 ;; everything other (including all alternate registers) is dead
1832 call PARSER_SKIP_BLANKS
1833 jr z,PARSE_EXPR_ERROR_STR
1837 jr nz,PARSE_EXPR_ERROR_STR
1841 ; remember buffer start
1846 jr z,PARSE_EXPR_ERROR_STR
1848 jr z,PARSE_EXPR_ERROR_STR
1852 ; done string parsing, calc length
1853 pop hl ; buffer start
1864 PARSE_EXPR_ERROR_STR:
1865 ld a,EXPR_ERR_STRING_EXPECTED
1866 jr PARSE_EXPR_ERROR_A
1869 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1871 ;; returns current char in A
1872 ;; sets zero flag on EOL
1876 ;; IY: text buffer at non-blank or EOL
1877 ;; A: non-blank or EOL char
1878 ;; zero flag is set on EOL/terminator
1886 jr c,PARSER_SKIP_BLANKS
1893 $printf "assembler exprparse size: %d", csizest
1896 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1897 ;; various tables -- mnemonics, operands...
1901 ;; number of "trivial" opcodes without any special processing
1902 OPC_COUNT_GROUPS_0_AND_1 equ 39
1903 ;; number of non-ED-prefixed instructions in "trivial"
1904 OPC_COUNT_GROUP_0 equ 15
1906 ;; total number of CB-prefixed instructions (GROUPS 2 and 3)
1907 OPC_COUNT_GROUPS_2_AND_3 equ 10
1908 ;; number of direct bit manipulation instructions in CB list (GROUP 2)
1909 OPC_COUNT_GROUP_2 equ 3
1911 ;; push, pop, ex (sp),rr
1912 OPC_COUNT_GROUP_4 equ 3
1915 OPC_COUNT_GROUP_5 equ 5
1917 OPC_COUNT_GROUP_6 equ 3
1919 OPC_COUNT_GROUPS_5_AND_6 equ OPC_COUNT_GROUP_5+OPC_COUNT_GROUP_6
1922 OPC_COUNT_GROUP_7 equ 2
1925 OPC_COUNT_GROUP_8 equ 1
1927 OPC_COUNT_GROUP_9 equ 1
1929 OPC_COUNT_GROUPS_8_AND_9 equ OPC_COUNT_GROUP_8+OPC_COUNT_GROUP_9
1932 OPC_COUNT_GROUP_10 equ 2
1933 OPC_COUNT_GROUP_10_JRS equ 1
1936 ;; WARNING! the assembler has some hard-coded mnemonics counts
1937 ;; scattered across the code, so don't mess with the tables!
1938 ;; i may document this in the future, but for now, leave it as it is.
1941 ; GROUPS 0 AND 1: "trivial" (39, OPC_COUNT_GROUPS_0_AND_1)
1942 ; GROUP 0: "trivial" one-byte (15, OPC_COUNT_GROUP_0)
1975 ; GROUP 1: "trivial" ED-prefixed (24, OPC_COUNT_GROUPS_0_AND_1-OPC_COUNT_GROUP_0)
2028 ; GROUPS 2 AND 3: CB-prefixed (10, OPC_COUNT_GROUPS_2_AND_3)
2029 ; GROUP 2: direct bit manipulation (3, OPC_COUNT_GROUP_2)
2036 ; GROUP 3: shifts (7, OPC_COUNT_GROUPS_2_AND_3-OPC_COUNT_GROUP_2)
2052 ; GROUP 4: push,pop,ex (sp),rr (OPC_COUNT_GROUP_4)
2061 ; GROUP 5: ALU with accumulator (OPC_COUNT_GROUP_5)
2072 ;k8: for some reason i cannot remove those two
2078 ; GROUP 6: ALU with accumulator or HL (OPC_COUNT_GROUP_6)
2086 ; GROUP 7: inc,dec (2, OPC_COUNT_GROUP_7)
2092 ; GROUP 8: in (1, OPC_COUNT_GROUP_8)
2095 ; GROUP 9: out (1, OPC_COUNT_GROUP_9)
2099 ; GROUP 10: jr,djnz (2, OPC_COUNT_GROUP_10)
2105 ; GROUP 11: jp (strictly one)
2109 ; GROUP 12: call (strictly one)
2113 ; GROUP 13: rst (strictly one)
2117 ; GROUP 14: ret (strictly one)
2121 ; GROUP 15: ld (strictly one)
2125 ; miscellaneous assembler instructions
2126 ; WARNING! the order matters!
2136 IF @BZ80ASM_ORGENTDISP
2146 ; softinclude user instructions
2147 include "?bzasm80_user_mnemonics.zas"
2239 defb #42+#80 ; #42,#4B (+9)
2242 defb #44+#80 ; #44,#4D (+9)
2245 defb #50+#80 ; #50,#59 (+9)
2248 defb #54+#80 ; #54,#5D (+9)
2251 defb #60+#80 ; #60,#69 (+9)
2254 defb #62+#80 ; #62,#6B (+9)
2260 $printf "assembler tables size: %d", csizest
2262 asmsizest = $-asmsizest
2263 $printf "full assembler size: %d", asmsizest
2265 ; so they won't clutter symbol table