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
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)
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
361 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
381 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
396 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
411 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
424 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
473 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
474 ;; misc. instructions
479 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
484 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
487 ld hl,(ASM_JR_TOO_FAR_CB)
490 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
499 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
514 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
524 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
537 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
544 ret z ;REJECT LD (HL),(HL)
551 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
581 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
589 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
590 ;; parse numeric expression
597 ; HL: expression value
605 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
616 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
623 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
630 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
645 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
657 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
674 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
676 ;; common defb/defw code
677 ;; carry set if we want words
699 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
709 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
719 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
746 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
751 IF @BZ80ASM_ORGENTDISP
761 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
770 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
779 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
781 ;; error (the thing that should not be)
785 ; set "unknown instruction index"
794 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
795 ;; search the table for a token from the input buffer
796 ;; skips leading delimiters, and the token itself (if found)
797 ;; tokens in the table must have bit 7 set on the last char
798 ;; table ends with zero byte instead of first token char
799 ;; each token is followed by a data byte
803 ;; IY: new position in the text buffer
805 ;; B: token data byte
806 ;; carry flag set on error (invalid char, or token not found)
807 ;; on error, delimiters are still skipped
811 ; main table search entry point
818 ; reject chars with high bit set
824 ; check the first char
829 and %01011111 ; for case-insensitivity
831 ; first char is not matched, skip this token
836 ; skip token data byte
838 ; increment token counter
842 ; first char matched, check the rest
843 ; A holds zero (due to xor/and)
852 ; not the last token char
853 ; this compares (HL) with 0, because
854 ; A is guaranteed to hold zero here
856 ; zero byte in token means "skip delimiters"
857 ; it is used in some opcodes with fixed operands
861 ; compare with input char
863 and %01011111 ; for case-insensitivity
866 ; alas, doesn't match
868 ; restore input stream pointer
870 ; and skip this token
873 ; we'll come here if we succesfully matched a token
875 ; if it isn't followed by a delimiter, '+' or '-', this is not a valid token
880 ; this token is valid
882 ; move token index to A
884 ; load B with token data byte
886 ; drop original input stream position
889 ; note that carry flag is guaranteed to be reset
893 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
901 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
902 ;; this entry point is used by FIND
905 ;; this entry point is used to skip blanks and delimiters
906 ;; note that comma and right paren are considered blanks too
907 ;; as a consequence, operands may be delimited by spaces, or
909 ;; returns current char in A (and IY pointing to it)
910 ;; zero flag set if we hit a terminator
911 ;; zero flag reset if we hit a non-delimiter
913 ; delimiter or terminator?
916 ; if this is terminator, still stop
919 ; this is delimiter, skip it
923 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
924 ;; used by FIND and SKIP
925 ;; check if the current char is a delimiter or a terminator
926 ;; zero flag set on delimiter/terminator
928 ld a,(iy) ;ASSEMBLER DELIMITER
936 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
937 ;; entry point for SKIP
939 cp ';' ;ASSEMBLER TERMINATOR
944 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
945 ;; also used by assembler to check for command separator
946 ;; the assembler itself does nothing with separators
948 cp ':' ;ASSEMBLER SEPARATOR
954 $printf "assembler size: %d", csizeend-csizestart
956 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
957 ;; various tables -- mnemonics, operands...
961 ;; number of "trivial" opcodes without any special processing
962 OPC_COUNT_GROUPS_0_AND_1 equ 39
963 ;; number of non-ED-prefixed instructions in "trivial"
964 OPC_COUNT_GROUP_0 equ 15
966 ;; total number of CB-prefixed instructions (GROUPS 2 and 3)
967 OPC_COUNT_GROUPS_2_AND_3 equ 10
968 ;; number of direct bit manipulation instructions in CB list (GROUP 2)
969 OPC_COUNT_GROUP_2 equ 3
971 ;; push, pop, ex (sp),rr
972 OPC_COUNT_GROUP_4 equ 3
975 OPC_COUNT_GROUP_5 equ 5
977 OPC_COUNT_GROUP_6 equ 3
979 OPC_COUNT_GROUPS_5_AND_6 equ OPC_COUNT_GROUP_5+OPC_COUNT_GROUP_6
982 OPC_COUNT_GROUP_7 equ 2
985 OPC_COUNT_GROUP_8 equ 1
987 OPC_COUNT_GROUP_9 equ 1
989 OPC_COUNT_GROUPS_8_AND_9 equ OPC_COUNT_GROUP_8+OPC_COUNT_GROUP_9
992 OPC_COUNT_GROUP_10 equ 2
993 OPC_COUNT_GROUP_10_JRS equ 1
996 ;; WARNING! the assembler has some hard-coded mnemonics counts
997 ;; scattered across the code, so don't mess with the tables!
998 ;; i may document this in the future, but for now, leave it as it is.
1001 ; GROUPS 0 AND 1: "trivial" (39, OPC_COUNT_GROUPS_0_AND_1)
1002 ; GROUP 0: "trivial" one-byte (15, OPC_COUNT_GROUP_0)
1035 ; GROUP 1: "trivial" ED-prefixed (24, OPC_COUNT_GROUPS_0_AND_1-OPC_COUNT_GROUP_0)
1088 ; GROUPS 2 AND 3: CB-prefixed (10, OPC_COUNT_GROUPS_2_AND_3)
1089 ; GROUP 2: direct bit manipulation (3, OPC_COUNT_GROUP_2)
1096 ; GROUP 3: shifts (7, OPC_COUNT_GROUPS_2_AND_3-OPC_COUNT_GROUP_2)
1112 ; GROUP 4: push,pop,ex (sp),rr (OPC_COUNT_GROUP_4)
1121 ; GROUP 5: ALU with accumulator (OPC_COUNT_GROUP_5)
1132 ;k8: for some reason i cannot remove those two
1138 ; GROUP 6: ALU with accumulator or HL (OPC_COUNT_GROUP_6)
1146 ; GROUP 7: inc,dec (2, OPC_COUNT_GROUP_7)
1152 ; GROUP 8: in (1, OPC_COUNT_GROUP_8)
1155 ; GROUP 9: out (1, OPC_COUNT_GROUP_9)
1159 ; GROUP 10: jr,djnz (2, OPC_COUNT_GROUP_10)
1165 ; GROUP 11: jp (strictly one)
1169 ; GROUP 12: call (strictly one)
1173 ; GROUP 13: rst (strictly one)
1177 ; GROUP 14: ret (strictly one)
1181 ; GROUP 15: ld (strictly one)
1185 ; miscellaneous assembler instructions
1186 ; WARNING! the order matters!
1196 IF @BZ80ASM_ORGENTDISP
1206 ; softinclude user instructions
1207 include "?bzasm80_user_mnemonics.zas"
1308 $printf "assembler tables size: %d", csizeend-csizestart
1310 ; so they won't clutter symbol table