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
33 PC: defw #C000 ; logical program counter for the code
35 IF @BZ80ASM_ORGENTDISP
36 ; ORG/DISP sets this to its value
38 ; ORG/ENT sets this to the corresponding type
39 ; it is reset at each call to ASSEM
44 ORGENT_TYPE: defw ORGENT_NONE
47 ; address of the routine that will be called if JR/DJNZ destination is too far away
48 ; you can simply RET from it if you want to ignore this error
49 ; note that stack is filled with garbage, so use `ASM_ERROR_EXIT` to bail out
50 ; this is not checked for zero, so if you will not init this, "CALL 0" will be executed
51 ; ret L to the byte that will be put on normal return
52 ASM_JR_TOO_FAR_CB: defw 0
54 ; on entry, ASSEM will store SP here
55 ; you can use this to reset the stack, and use RET to return to the caller
56 ; or simply use ASM_ERROR_EXIT (which will set carry too)
59 ; ASSEM sets this if it hits mnemonics without a handler
60 ; you can add your own mnemonics to the assembler, and it
61 ; will set this to non-zero if it hits them
62 ; use this byte as decrementing counter, like this:
69 ; handle first instruction
72 ; handle second instruction
74 ; ADDITIONALLY, this will be set to #FF on invalid token
75 ; this can be used to process labels (because otherwise
76 ; there is no way to tell if we got some bad operands, or
77 ; an unknown mnemonics)
79 ; this is set to non-zero if compiled instruction was
80 ; some data definition like DEFB, and not some real one
81 ; actually, it is set for any "pseudoinstruction", i.e.
82 ; for data defs, ORG/DISP, and user-defined mnemonics, if
84 ASM_WAS_PSEUDO: defb 0
87 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
88 ;; expression parser variables
90 ;; set this to the address of error routine
91 ;; note that you cannot return from it, you HAVE to abort everything
92 ;; also note that machine stack is undefined, and SP should be set
93 ;; to some initial value
94 ;; "undefined" means that machine stack can contain alot of garbage,
95 ;; but will never be underflowed
97 ;; this function is called with error code in A
99 ;; you can load your own error code in A, and do:
100 ;; jp BZ80ASM.PARSE_EXPR_ERROR_A
101 EXPR_ERROR_CB: defw 0
104 ;; expected number, but got something incomprehensible
105 EXPR_ERR_NUMBER_EXPECTED equ 1
106 ;; expected string, but got something incomprehensible
107 EXPR_ERR_STRING_EXPECTED equ 2
108 ;; expected ")", but got something strange
109 EXPR_ERR_RPAREN_EXPECTED equ 3
110 ;; expected ")", but got something strange
111 EXPR_ERR_DIVISION_BY_ZERO equ 4
113 ;; the asm never generates the following errors, but
114 ;; they may be useful for a main driver
116 ;; you can use this to dispatch "short jump destination is too far" error
117 ;; just do this in `ASM_JR_TOO_FAR_CB`:
118 ;; ld a,EXPR_ERR_JR_TOO_FAR
119 ;; jp BZ80ASM.PARSE_EXPR_ERROR_A
120 EXPR_ERR_JR_TOO_FAR equ 5
122 ;; this "unresolved label" error can be used by label manager
123 EXPR_ERR_UNRESOLVED_LABEL equ 6
124 ;; this error can be used by label manager if it cannot parse a label
125 EXPR_ERR_INVALID_LABEL_NAME equ 7
126 ;; this error can be used by label manager
127 EXPR_ERR_DUPLICATE_LABEL equ 8
129 ;; general "bad syntax" error, when you don't have anything better
130 EXPR_ERR_BAD_SYNTAX equ 9
132 ;; offset your own error codes with this
133 EXPR_ERR_USERDEF equ 10
136 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
137 ;; label manager callback
140 ;; this is called from assembler to find an existing label.
141 ;; IY points to the first char of a label name.
142 ;; after parsing, IY should point right after the parsed name.
143 ;; all registers expect IY can be trashed (including IX).
145 ;; for forward referencing, label manager can create unknown
146 ;; labels, and set their value to (BZ80ASM.PC).
147 ;; it is important to set new labels to PC to avoid errors
148 ;; with short jumps if your "jr too far" error handler always
149 ;; bombs out. otherwise, you can set the label to anything
153 ;; IY: text input buffer
155 ;; IY: text input buffer after the label
157 ;; CARRY FLAG: set if label wasn't found
158 ;; expression parser will bomb out in this case
159 ;; on error, IY doesn't matter, because it will be restored
160 ;; by the calling code
164 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
172 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
174 ;; LANGUAGE-INDEPENDENT CONTROL SECTION:
178 ;; IX: destination to put generated code at
181 ;; IY: terminator position in text buffer
182 ;; IX: points right after the generated code
183 ;; DE: length of the generated code
184 ;; carry set if syntax error
185 ;; in case of error, IY will be somewhere around the error position
186 ;; all other registers are undefined
187 ;; others are dead (including extra register set and extra accum/flags)
189 ;; input buffer must be terminated with ':', ';' or CR (13).
190 ;; unterminated input buffer will wreck havoc.
191 ;; the assembler will not modify the input buffer, but note that it
192 ;; will skip all leading delimiters.
194 ;; on exit, all trailing delimiters will be skipped too.
196 ;; also, the assembler doens't try to parse and create new labels, you
197 ;; should do it by yourself. it can be done like this:
199 ;; call BZ80ASM.ASSEM
200 ;; jp nc,assembled_ok
201 ;; here we can try to parse a label, and then try to assemble
202 ;; the rest of the line again
207 IF @BZ80ASM_ORGENTDISP
211 ld (ASM_WAS_PSEUDO),a
226 ; exit if syntax error
228 ; skip delimiters (but not terminators)
230 ; exit with error flag if we're not at a terminator
238 ex de,hl ;DE= NO. OF BYTES
241 ld (PC),hl ;UPDATE PC
246 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
248 ;; jump here to restore stack, and exit with error (carry set)
261 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
263 ;; PROCESSOR-SPECIFIC TRANSLATION SECTION:
265 ;; REGISTER USAGE: B - TYPE OF MOST RECENT OPERAND
266 ;; C - OPCODE BEING BUILT
267 ;; D - (IX) OR (IY) FLAG
268 ;; E - OFFSET FROM IX OR IY
269 ;; HL - NUMERIC OPERAND VALUE
270 ;; IX - CODE DESTINATION
271 ;; IY - SOURCE TEXT POINTER
272 ;; Inputs: A = initial character
273 ;; Outputs: Carry set if syntax error.
280 ; carry flag set on error
282 ; A contains token index
283 ; B contains token data byte
285 ld d,0 ;CLEAR IX/IY FLAG
288 ; A contains token index
289 ; C contains token data byte
290 ; D contains IX/IY flag
292 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
294 ;; GROUP 0 - TRIVIAL CASES REQUIRING NO COMPUTATION
295 ;; GROUP 1 - AS GROUP 0 BUT WITH "ED" PREFIX
297 sub OPC_COUNT_GROUPS_0_AND_1
299 cp OPC_COUNT_GROUP_0-OPC_COUNT_GROUPS_0_AND_1
303 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
305 ;; GROUP 2 - BIT, RES, SET
306 ;; GROUP 3 - RLC, RRC, RL, RR, SLA, SRA, SRL
309 sub OPC_COUNT_GROUPS_2_AND_3
311 cp OPC_COUNT_GROUP_2-OPC_COUNT_GROUPS_2_AND_3
319 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
321 ;; GROUP 4 - PUSH, POP, EX (SP)
324 sub OPC_COUNT_GROUP_4
331 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
333 ;; GROUP 5 - SUB, AND, XOR, OR, CP
334 ;; GROUP 6 - ADD, ADC, SBC
337 sub OPC_COUNT_GROUPS_5_AND_6
339 cp OPC_COUNT_GROUP_5-OPC_COUNT_GROUPS_5_AND_6
370 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
372 ;; GROUP 7 - INC, DEC
375 sub OPC_COUNT_GROUP_7
392 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
398 sub OPC_COUNT_GROUPS_8_AND_9
400 cp OPC_COUNT_GROUP_8-OPC_COUNT_GROUPS_8_AND_9
421 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
423 ;; GROUP 10 - JR, DJNZ
426 sub OPC_COUNT_GROUP_10
428 cp OPC_COUNT_GROUP_10_JRS-OPC_COUNT_GROUP_10
450 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
470 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
485 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
500 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
513 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
562 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
563 ;; misc. instructions
566 ld (ASM_WAS_PSEUDO),a
570 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
575 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
578 ld hl,(ASM_JR_TOO_FAR_CB)
581 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
590 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
605 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
615 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
628 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
635 ret z ;REJECT LD (HL),(HL)
642 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
672 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
680 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
681 ;; parse numeric expression
688 ; HL: expression value
696 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
707 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
714 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
721 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
736 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
748 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
765 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
767 ;; common defb/defw code
768 ;; carry set if we want words
771 ; note that we don't care about A, we only need flags here
779 pop af ; restore flags
784 ex af,af' ; save flags
788 ex af,af' ; restore flags
791 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
801 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
811 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
838 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
843 IF @BZ80ASM_ORGENTDISP
853 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
862 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
871 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
873 ;; error (the thing that should not be)
877 ; set "unknown instruction index"
886 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
887 ;; search the table for a token from the input buffer
888 ;; skips leading delimiters, and the token itself (if found)
889 ;; tokens in the table must have bit 7 set on the last char
890 ;; table ends with zero byte instead of first token char
891 ;; each token is followed by a data byte
895 ;; IY: new position in the text buffer
897 ;; B: token data byte
898 ;; carry flag set on error (invalid char, or token not found)
899 ;; on error, delimiters are still skipped
903 ; main table search entry point
910 ; reject chars with high bit set
916 ; check the first char
921 and %01011111 ; for case-insensitivity
923 ; first char is not matched, skip this token
928 ; skip token data byte
930 ; increment token counter
934 ; first char matched, check the rest
935 ; A holds zero (due to xor/and)
944 ; not the last token char
945 ; this compares (HL) with 0, because
946 ; A is guaranteed to hold zero here
948 ; zero byte in token means "skip delimiters"
949 ; it is used in some opcodes with fixed operands
953 ; compare with input char
955 and %01011111 ; for case-insensitivity
958 ; alas, doesn't match
960 ; restore input stream pointer
962 ; and skip this token
965 ; we'll come here if we succesfully matched a token
967 ; if it isn't followed by a delimiter, '+' or '-', this is not a valid token
972 ; this token is valid
974 ; move token index to A
976 ; load B with token data byte
978 ; drop original input stream position
981 ; note that carry flag is guaranteed to be reset
985 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
993 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
994 ;; this entry point is used by FIND
997 ;; this entry point is used to skip blanks and delimiters
998 ;; note that comma and right paren are considered blanks too
999 ;; as a consequence, operands may be delimited by spaces, or
1000 ;; right parens, lol
1001 ;; returns current char in A (and IY pointing to it)
1002 ;; zero flag set if we hit a terminator
1003 ;; zero flag reset if we hit a non-delimiter
1005 ; delimiter or terminator?
1008 ; if this is terminator, still stop
1011 ; this is delimiter, skip it
1015 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1016 ;; used by FIND and SKIP
1017 ;; check if the current char is a delimiter or a terminator
1018 ;; zero flag set on delimiter/terminator
1020 ld a,(iy) ;ASSEMBLER DELIMITER
1028 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1029 ;; entry point used by SKIP
1031 cp ';' ;ASSEMBLER TERMINATOR
1034 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1035 ;; also used by assembler to check for command separator
1036 ;; the assembler itself does nothing with separators
1038 cp ':' ;ASSEMBLER SEPARATOR
1044 $printf "assembler size: %d", csizest
1049 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1050 ;; parse integer number (without sign)
1051 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1053 ;; this is advanced number parser
1054 ;; it understands alot of suffixes and prefixes:
1055 ;; $ -- lone "$" means "current PC"
1066 ;; everything is case-insensitive
1067 ;; you can separate digits with underscores
1068 ;; (i.e. "12_34_5" will work, underscores are simply ignored)
1071 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1073 ;; parse a number, push it onto the stack
1074 ;; understands prefixes and suffixes
1079 ;; IY: text buffer after the expression
1082 ;; OR: (cannot parse as a number)
1085 ;; DE,AF,flags: dead
1088 call PARSER_SKIP_BLANKS
1091 push iy ; we will need to rollback on error
1092 ; A already contains a char, loaded by `PARSER_SKIP_BLANKS`
1096 jr z,.maybe_lone_dollar
1101 ; no, leading zero doesn't mean "octal", this is stupid
1102 ; but we may have prefixes like "0x" and such
1104 jr z,.maybe_zero_prefix
1105 ; check if we have a digit here
1106 call PARSER_CONV_DIGIT
1107 jr c,.not_a_number_carry_set
1109 ; nope, do not allow it, all numbers must start with a digit
1110 ;jr nc,.must_be_hex_with_sfx
1112 jr c,.not_a_number_carry_set
1114 ; done with prefixes, try decimal number
1115 ; we'll switch to suffix checking on hex digit
1116 ld hl,0 ; accumulator
1121 jr nc,.must_be_hex_with_sfx
1138 and %11011111 ; cheap uppercase
1140 jr z,.must_be_hex_with_sfx
1145 ; no suffix, we're done
1153 .not_a_number_carry_set:
1162 jr c,.not_a_number_carry_set
1166 ; lone dollar means "PC"
1169 ; the only case we may gen an error here is
1170 ; when our dollar isn't followed by a digit
1172 ; lone dollar is good too
1173 ; IY points right after the dollar here
1184 ; things like "0BEEFh" should be parsed as hex
1188 jr c,.must_be_hex_with_sfx
1191 and %11011111 ; cheap uppercase
1193 jr z,.must_be_hex_with_sfx
1203 ; check for '0x' and such
1207 ; there's no need to skip it, as it will be
1208 ; skipped by the corresponding subroutine
1210 ; so IY will point to the actual number
1211 and %11011111 ; cheap uppercase
1215 jr z,.maybe_binprefix
1218 ; do not reparse '0', no need to backup
1219 jr .do_normal_decimal
1221 .must_be_hex_with_sfx:
1222 ; reparse as hex, and check for suffix
1226 jr c,.not_a_number_carry_set
1229 and %11011111 ; cheap uppercase
1234 ; reparse as bin, skip suffix (it is guaranteed to be there)
1238 .done_guaranteed_suffix:
1239 jr c,.not_a_number_carry_set
1245 ; reparse as bin, skip suffix (it is guaranteed to be there)
1249 jr .done_guaranteed_suffix
1252 ld hl,0 ; accumulator
1253 ; check first digit (as this is general parser)
1254 call .getDigitNoUnder
1266 jr nc,.parse_as_hex_loop
1267 ; clear carry flag (it is always set here)
1272 ld hl,0 ; accumulator
1273 ; check first digit (as this is general parser)
1275 call .getDigitNoUnderBin
1284 jr nc,.parse_as_bin_loop
1285 ; clear carry flag (it is always set here)
1290 ld hl,0 ; accumulator
1291 ; check first digit (as this is general parser)
1293 call .getDigitNoUnderOct
1303 jr nc,.parse_as_oct_loop
1304 ; clear carry flag (it is always set here)
1315 jr PARSER_CONV_DIGIT
1327 jr PARSER_CONV_DIGIT
1333 .getDigitNoUnderOct:
1339 .getDigitNoUnderBin:
1346 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1347 ;; converts 'A' to digit (assume hex)
1348 ;; carry set: not a digit char (and A is destroyed)
1357 and %11011111 ; cheap uppercase
1365 $printf "assembler numparse size: %d", csizest
1370 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1371 ;; math expression parser
1372 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1375 ld hl,(EXPR_ERROR_CB)
1378 PARSE_EXPR_ERROR_0DIV:
1379 ld a,EXPR_ERR_DIVISION_BY_ZERO
1380 jr PARSE_EXPR_ERROR_A
1382 PARSE_EXPR_ERROR_INT:
1383 ld a,EXPR_ERR_NUMBER_EXPECTED
1384 jr PARSE_EXPR_ERROR_A
1387 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1389 ;; parse an integer expression
1393 ;; IY: text buffer after the expression
1394 ;; HL: expression value
1395 ;; everything other (including all alternate registers) is dead
1404 call PARSER_SKIP_BLANKS
1405 jr z,PARSE_EXPR_ERROR_INT
1427 ; already done above
1430 ; check for operation
1431 call PARSER_SKIP_BLANKS
1435 ; get second operand
1451 ; check for operation
1452 call PARSER_SKIP_BLANKS
1456 ; get second operand
1472 ; check for operation
1473 call PARSER_SKIP_BLANKS
1477 ; get second operand
1492 ; check for operation
1493 call PARSER_SKIP_BLANKS
1495 ; (iy+0) and (iy+1) should be equal
1503 ; get second operand
1504 inc iy ; skip operation part
1508 ; HL: number to shift
1512 jr nz,.shift_too_far
1515 jr nc,.shift_too_far
1542 ; check for operation
1543 call PARSER_SKIP_BLANKS
1550 ; get second operand
1566 ; check for operation
1567 call PARSER_SKIP_BLANKS
1576 ; get second operand
1582 ex af,af' ; save operation
1586 jp z,PARSE_EXPR_ERROR_0DIV
1587 call PARSER_UDIV_BC_DE
1588 ; was it div or mod?
1591 jr z,.muldiv_next ; remainder already in hl
1596 call PARSER_UMUL_BC_DE
1599 ;; parse term, also process unaries and parens
1601 call PARSER_SKIP_BLANKS
1602 jp z,PARSE_EXPR_ERROR_INT
1603 inc iy ; skip operation
1612 ; this must be number
1617 push iy ; for correct error position
1618 push ix ; the only register we care about
1619 ld hl,.term_checklabel_ret
1623 .term_checklabel_ret:
1628 ; restore position for correct error reporting
1631 jp PARSE_EXPR_ERROR_INT
1635 ;; C contains matching rparen
1638 call PARSER_SKIP_BLANKS
1639 jr z,PARSE_EXPR_ERROR_RPAREN
1642 jr nz,PARSE_EXPR_ERROR_RPAREN
1657 ;; call subroutine at BC
1660 ;; DE: subroutine result
1661 ;; i.e. HL is op0, DE is op1
1664 push af ; A holds operation
1665 inc iy ; skip operation
1666 ld hl,.go_down_bc_ret
1676 PARSE_EXPR_ERROR_RPAREN:
1677 ld a,EXPR_ERR_RPAREN_EXPECTED
1678 jp PARSE_EXPR_ERROR_A
1681 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1685 ;; BC,DE,A,flags: dead
1705 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1731 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1733 ;; parse a string expression
1738 ;; HL: string buffer start
1739 ;; E: parsed string length
1740 ;; everything other (including all alternate registers) is dead
1743 call PARSER_SKIP_BLANKS
1744 jr z,PARSE_EXPR_ERROR_STR
1748 jr nz,PARSE_EXPR_ERROR_STR
1752 ; remember buffer start
1757 jr z,PARSE_EXPR_ERROR_STR
1759 jr z,PARSE_EXPR_ERROR_STR
1763 ; done string parsing, calc length
1764 pop hl ; buffer start
1775 PARSE_EXPR_ERROR_STR:
1776 ld a,EXPR_ERR_STRING_EXPECTED
1777 jp PARSE_EXPR_ERROR_A
1780 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1782 ;; returns current char in A
1783 ;; sets zero flag on EOL
1787 ;; IY: text buffer at non-blank or EOL
1788 ;; A: non-blank or EOL char
1789 ;; zero flag is set on EOL/terminator
1797 jr c,PARSER_SKIP_BLANKS
1804 $printf "assembler exprparse size: %d", csizest
1807 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1808 ;; various tables -- mnemonics, operands...
1812 ;; number of "trivial" opcodes without any special processing
1813 OPC_COUNT_GROUPS_0_AND_1 equ 39
1814 ;; number of non-ED-prefixed instructions in "trivial"
1815 OPC_COUNT_GROUP_0 equ 15
1817 ;; total number of CB-prefixed instructions (GROUPS 2 and 3)
1818 OPC_COUNT_GROUPS_2_AND_3 equ 10
1819 ;; number of direct bit manipulation instructions in CB list (GROUP 2)
1820 OPC_COUNT_GROUP_2 equ 3
1822 ;; push, pop, ex (sp),rr
1823 OPC_COUNT_GROUP_4 equ 3
1826 OPC_COUNT_GROUP_5 equ 5
1828 OPC_COUNT_GROUP_6 equ 3
1830 OPC_COUNT_GROUPS_5_AND_6 equ OPC_COUNT_GROUP_5+OPC_COUNT_GROUP_6
1833 OPC_COUNT_GROUP_7 equ 2
1836 OPC_COUNT_GROUP_8 equ 1
1838 OPC_COUNT_GROUP_9 equ 1
1840 OPC_COUNT_GROUPS_8_AND_9 equ OPC_COUNT_GROUP_8+OPC_COUNT_GROUP_9
1843 OPC_COUNT_GROUP_10 equ 2
1844 OPC_COUNT_GROUP_10_JRS equ 1
1847 ;; WARNING! the assembler has some hard-coded mnemonics counts
1848 ;; scattered across the code, so don't mess with the tables!
1849 ;; i may document this in the future, but for now, leave it as it is.
1852 ; GROUPS 0 AND 1: "trivial" (39, OPC_COUNT_GROUPS_0_AND_1)
1853 ; GROUP 0: "trivial" one-byte (15, OPC_COUNT_GROUP_0)
1886 ; GROUP 1: "trivial" ED-prefixed (24, OPC_COUNT_GROUPS_0_AND_1-OPC_COUNT_GROUP_0)
1939 ; GROUPS 2 AND 3: CB-prefixed (10, OPC_COUNT_GROUPS_2_AND_3)
1940 ; GROUP 2: direct bit manipulation (3, OPC_COUNT_GROUP_2)
1947 ; GROUP 3: shifts (7, OPC_COUNT_GROUPS_2_AND_3-OPC_COUNT_GROUP_2)
1963 ; GROUP 4: push,pop,ex (sp),rr (OPC_COUNT_GROUP_4)
1972 ; GROUP 5: ALU with accumulator (OPC_COUNT_GROUP_5)
1983 ;k8: for some reason i cannot remove those two
1989 ; GROUP 6: ALU with accumulator or HL (OPC_COUNT_GROUP_6)
1997 ; GROUP 7: inc,dec (2, OPC_COUNT_GROUP_7)
2003 ; GROUP 8: in (1, OPC_COUNT_GROUP_8)
2006 ; GROUP 9: out (1, OPC_COUNT_GROUP_9)
2010 ; GROUP 10: jr,djnz (2, OPC_COUNT_GROUP_10)
2016 ; GROUP 11: jp (strictly one)
2020 ; GROUP 12: call (strictly one)
2024 ; GROUP 13: rst (strictly one)
2028 ; GROUP 14: ret (strictly one)
2032 ; GROUP 15: ld (strictly one)
2036 ; miscellaneous assembler instructions
2037 ; WARNING! the order matters!
2047 IF @BZ80ASM_ORGENTDISP
2057 ; softinclude user instructions
2058 include "?bzasm80_user_mnemonics.zas"
2159 $printf "assembler tables size: %d", csizest
2161 asmsizest = $-asmsizest
2162 $printf "full assembler size: %d", asmsizest
2164 ; so they won't clutter symbol table