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
27 IF !defined(BZ80ASM_ORGENTDISP)
28 BZ80ASM_ORGENTDISP equ 0
33 PC: defw #C000 ; logical program counter for the code
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)
85 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
87 ;; LANGUAGE-INDEPENDENT CONTROL SECTION:
90 ;; IX: destination to put generated code at
93 ;; IY: delimiter position in text buffer
94 ;; IX: points right after the generated code
95 ;; carry set if syntax error (and A is undefined in this case)
96 ;; others are dead (including extra register set and extra accum/flags)
101 IF BZ80ASM_ORGENTDISP
111 ; this was used to terminate assembler section in BBC Basic
122 ; exit if syntax error
124 ; skip delimiters (but not terminators)
126 ; exit with error flag if we're not at a terminator
134 ex de,hl ;DE= NO. OF BYTES
137 ld (PC),hl ;UPDATE PC
142 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
144 ;; jump here to restore stack, and exit with error (carry set)
157 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
159 ;; PROCESSOR-SPECIFIC TRANSLATION SECTION:
161 ;; REGISTER USAGE: B - TYPE OF MOST RECENT OPERAND
162 ;; C - OPCODE BEING BUILT
163 ;; D - (IX) OR (IY) FLAG
164 ;; E - OFFSET FROM IX OR IY
165 ;; HL - NUMERIC OPERAND VALUE
166 ;; IX - CODE DESTINATION
167 ;; IY - SOURCE TEXT POINTER
168 ;; Inputs: A = initial character
169 ;; Outputs: Carry set if syntax error.
178 ; done with the label
182 ; this code seems to be for tokenized input
183 ; we don't have tokenizer
191 ; carry flag set on error
193 ; A contains token index
194 ; B contains token data byte
196 ld d,0 ;CLEAR IX/IY FLAG
199 ; A contains token index
200 ; C contains token data byte
201 ; D contains IX/IY flag
203 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
205 ;; GROUP 0 - TRIVIAL CASES REQUIRING NO COMPUTATION
206 ;; GROUP 1 - AS GROUP 0 BUT WITH "ED" PREFIX
208 sub OPC_COUNT_GROUPS_0_AND_1
210 cp OPC_COUNT_GROUP_0-OPC_COUNT_GROUPS_0_AND_1
214 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
216 ;; GROUP 2 - BIT, RES, SET
217 ;; GROUP 3 - RLC, RRC, RL, RR, SLA, SRA, SRL
220 sub OPC_COUNT_GROUPS_2_AND_3
222 cp OPC_COUNT_GROUP_2-OPC_COUNT_GROUPS_2_AND_3
230 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
232 ;; GROUP 4 - PUSH, POP, EX (SP)
235 sub OPC_COUNT_GROUP_4
242 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
244 ;; GROUP 5 - SUB, AND, XOR, OR, CP
245 ;; GROUP 6 - ADD, ADC, SBC
248 sub OPC_COUNT_GROUPS_5_AND_6
250 cp OPC_COUNT_GROUP_5-OPC_COUNT_GROUPS_5_AND_6
281 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
283 ;; GROUP 7 - INC, DEC
286 sub OPC_COUNT_GROUP_7
303 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
309 sub OPC_COUNT_GROUPS_8_AND_9
311 cp OPC_COUNT_GROUP_8-OPC_COUNT_GROUPS_8_AND_9
332 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
334 ;; GROUP 10 - JR, DJNZ
337 sub OPC_COUNT_GROUP_10
339 cp OPC_COUNT_GROUP_10_JRS-OPC_COUNT_GROUP_10
356 ;jp nz,@error_out_of_range
362 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
382 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
397 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
412 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
425 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
474 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
475 ;; misc. instructions
480 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
485 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
488 ld hl,(ASM_JR_TOO_FAR_CB)
491 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
500 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
515 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
525 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
538 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
545 ret z ;REJECT LD (HL),(HL)
552 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
582 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
590 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
591 ;; parse numeric expression
598 ; HL: expression value
606 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
617 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
624 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
631 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
646 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
658 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
675 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
677 ;; common defb/defw code
678 ;; carry set if we want words
702 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
712 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
722 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
749 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
754 IF BZ80ASM_ORGENTDISP
764 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
773 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
782 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
784 ;; error (the thing that should not be)
788 ; set "unknown instruction index"
797 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
798 ;; search the table for a token from the input buffer
799 ;; skips leading delimiters, and the token itself (if found)
800 ;; tokens in the table must have bit 7 set on the last char
801 ;; table ends with zero byte instead of first token char
802 ;; each token is followed by a data byte
806 ;; IY: new position in the text buffer
808 ;; B: token data byte
809 ;; carry flag set on error (invalid char, or token not found)
810 ;; on error, delimiters are still skipped
814 ; main table search entry point
821 ; reject chars with high bit set
827 ; check the first char
832 and %01011111 ; for case-insensitivity
834 ; first char is not matched, skip this token
839 ; skip token data byte
841 ; increment token counter
845 ; first char matched, check the rest
846 ; A holds zero (due to xor/and)
855 ; not the last token char
856 ; this compares (HL) with 0, because
857 ; A is guaranteed to hold zero here
859 ; zero byte in token means "skip delimiters"
860 ; it is used in some opcodes with fixed operands
864 ; compare with input char
866 and %01011111 ; for case-insensitivity
869 ; alas, doesn't match
871 ; restore input stream pointer
873 ; and skip this token
876 ; we'll come here if we succesfully matched a token
878 ; if it isn't followed by a delimiter, '+' or '-', this is not a valid token
883 ; this token is valid
885 ; move token index to A
887 ; load B with token data byte
889 ; drop original input stream position
892 ; note that carry flag is guaranteed to be reset
896 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
904 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
905 ;; this entry point is used by FIND
908 ;; this entry point is used to skip blanks and delimiters
909 ;; note that comma and right paren are considered blanks too
910 ;; as a consequence, operands may be delimited by spaces, or
912 ;; returns current char in A (and IY pointing to it)
913 ;; zero flag set if we hit a terminator
914 ;; zero flag reset if we hit a non-delimiter
916 ; delimiter or terminator?
919 ; if this is terminator, still stop
922 ; this is delimiter, skip it
926 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
927 ;; used by FIND and SKIP
928 ;; check if the current char is a delimiter or a terminator
929 ;; zero flag set on delimiter/terminator
931 ld a,(iy) ;ASSEMBLER DELIMITER
939 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
940 ;; entry point for SKIP
942 cp ';' ;ASSEMBLER TERMINATOR
947 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
948 ;; also used by assembler to check for command separator
949 ;; the assembler itself does nothing with separators
951 cp ':' ;ASSEMBLER SEPARATOR
957 $printf "assembler size: %d", csizeend-csizestart
959 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
960 ;; various tables -- mnemonics, operands...
964 ;; number of "trivial" opcodes without any special processing
965 OPC_COUNT_GROUPS_0_AND_1 equ 39
966 ;; number of non-ED-prefixed instructions in "trivial"
967 OPC_COUNT_GROUP_0 equ 15
969 ;; total number of CB-prefixed instructions (GROUPS 2 and 3)
970 OPC_COUNT_GROUPS_2_AND_3 equ 10
971 ;; number of direct bit manipulation instructions in CB list (GROUP 2)
972 OPC_COUNT_GROUP_2 equ 3
974 ;; push, pop, ex (sp),rr
975 OPC_COUNT_GROUP_4 equ 3
978 OPC_COUNT_GROUP_5 equ 5
980 OPC_COUNT_GROUP_6 equ 3
982 OPC_COUNT_GROUPS_5_AND_6 equ OPC_COUNT_GROUP_5+OPC_COUNT_GROUP_6
985 OPC_COUNT_GROUP_7 equ 2
988 OPC_COUNT_GROUP_8 equ 1
990 OPC_COUNT_GROUP_9 equ 1
992 OPC_COUNT_GROUPS_8_AND_9 equ OPC_COUNT_GROUP_8+OPC_COUNT_GROUP_9
995 OPC_COUNT_GROUP_10 equ 2
996 OPC_COUNT_GROUP_10_JRS equ 1
999 ;; WARNING! the assembler has some hard-coded mnemonics counts
1000 ;; scattered across the code, so don't mess with the tables!
1001 ;; i may document this in the future, but for now, leave it as it is.
1004 ; GROUPS 0 AND 1: "trivial" (39, OPC_COUNT_GROUPS_0_AND_1)
1005 ; GROUP 0: "trivial" one-byte (15, OPC_COUNT_GROUP_0)
1038 ; GROUP 1: "trivial" ED-prefixed (24, OPC_COUNT_GROUPS_0_AND_1-OPC_COUNT_GROUP_0)
1091 ; GROUPS 2 AND 3: CB-prefixed (10, OPC_COUNT_GROUPS_2_AND_3)
1092 ; GROUP 2: direct bit manipulation (3, OPC_COUNT_GROUP_2)
1099 ; GROUP 3: shifts (7, OPC_COUNT_GROUPS_2_AND_3-OPC_COUNT_GROUP_2)
1115 ; GROUP 4: push,pop,ex (sp),rr (OPC_COUNT_GROUP_4)
1124 ; GROUP 5: ALU with accumulator (OPC_COUNT_GROUP_5)
1135 ;k8: for some reason i cannot remove those two
1141 ; GROUP 6: ALU with accumulator or HL (OPC_COUNT_GROUP_6)
1149 ; GROUP 7: inc,dec (2, OPC_COUNT_GROUP_7)
1155 ; GROUP 8: in (1, OPC_COUNT_GROUP_8)
1158 ; GROUP 9: out (1, OPC_COUNT_GROUP_9)
1162 ; GROUP 10: jr,djnz (2, OPC_COUNT_GROUP_10)
1168 ; GROUP 11: jp (strictly one)
1172 ; GROUP 12: call (strictly one)
1176 ; GROUP 13: rst (strictly one)
1180 ; GROUP 14: ret (strictly one)
1184 ; GROUP 15: ld (strictly one)
1188 ; miscellaneous assembler instructions
1189 ; WARNING! the order matters!
1199 IF BZ80ASM_ORGENTDISP
1209 ; softinclude user instructions
1210 include "?bzasm80_user_mnemonics.zas"
1311 $printf "assembler tables size: %d", csizeend-csizestart