example driver: show current pass number
[bz80asm.git] / bzasm80.zas
blob24ec246a12b20c78656c72add27d711c6a48dd79
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:
5 ;;
6 ;; Copyright (c) 1984-2000 R.T. Russell
7 ;;
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
29 ENDIF
31   MODULE BZ80ASM
33 PC: defw #C000  ; logical program counter for the code
35 IF @BZ80ASM_ORGENTDISP
36 ; ORG/DISP sets this to its value
37 NEW_ORGENT: defw 0
38 ; ORG/ENT sets this to the corresponding type
39 ; it is reset at each call to ASSEM
40 ORGENT_NONE equ 0
41 ORGENT_ORG equ 1
42 ORGENT_DISP equ 2
43 ORGENT_ENT equ 3
44 ORGENT_TYPE: defw ORGENT_NONE
45 ENDIF
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)
57 ASM_SP0: defw 0
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:
63 ;  jr    nc,no_error
64 ;  ld    a,(ASM_BAD_B)
65 ;  or    a
66 ;  jr    z,syntax_error
67 ;  ld    b,a
68 ;  djnz  not_first_one
69 ;    handle first instruction
70 ;  not_first_one:
71 ;  djnz  not_second_one
72 ;    handle second instruction
73 ;  ...and so on
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)
78 ASM_BAD_B: defb 0
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
83 ; there are any
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
103 ;; error codes
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
139 ;; find a label
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
150 ;; you like.
152 ;; IN:
153 ;;   IY: text input buffer
154 ;; OUT:
155 ;;   IY: text input buffer after the label
156 ;;   HL: label value
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
161 GETLABEL_CB: defw 0
164 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
165 ; string terminator
166 CR equ 13
169 asmsizest = $
170 csizest = $
172 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
173 ;; ASSEMBLER:
174 ;; LANGUAGE-INDEPENDENT CONTROL SECTION:
176 ;;  Inputs:
177 ;;    IY: text buffer
178 ;;    IX: destination to put generated code at
179 ;;  Outputs:
180 ;;    A: terminator
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
204 ASSEM:
205   ; reset org/ent type
206   xor   a
207   IF @BZ80ASM_ORGENTDISP
208   ld    (ORGENT_TYPE),a
209   ENDIF
210   ld    (ASM_BAD_B),a
211   ld    (ASM_WAS_PSEUDO),a
212   ld    (ASM_SP0),sp
213 ASSEM1:
214   call  SKIP
215   inc   iy
216   cp    ':'
217   jr    z,ASSEM1
218   cp    CR
219   ret   z
220   cp    ';'
221   ret   z
222   dec   iy
223   push  ix
224   call  ASMB
225   pop   de
226   ; exit if syntax error
227   ret   c
228   ; skip delimiters (but not terminators)
229   call  SKIP
230   ; exit with error flag if we're not at a terminator
231   scf
232   ret   nz
233   ; advance PC
234   push  ix
235   pop   hl
236   or    a
237   sbc   hl,de
238   ex    de,hl           ;DE= NO. OF BYTES
239   ld    hl,(PC)
240   add   hl,de
241   ld    (PC),hl         ;UPDATE PC
242   ; reset carry and Z
243   xor   a
244   ret
246 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
248 ;; jump here to restore stack, and exit with error (carry set)
250 ASM_ERROR_EXIT:
251   ld    sp,(ASM_SP0)
252   scf
253   ret
255 ASMB_BAD_MNEMO:
256   ld    a,255
257   ld    (ASM_BAD_B),a
258   ; carry is still set
259   ret
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.
275 ASMB:
276   call  SKIP
277   ret   z
278   ld    hl,OPCODS
279   call  FIND
280   ; carry flag set on error
281   jr    c,ASMB_BAD_MNEMO
282   ; A contains token index
283   ; B contains token data byte
284   ld    c,b     ;ROOT OPCODE
285   ld    d,0     ;CLEAR IX/IY FLAG
287   ; now:
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
298   jr    nc,GROUP2
299   cp    OPC_COUNT_GROUP_0-OPC_COUNT_GROUPS_0_AND_1
300   call  nc,EDX
301   jr    BYTE0
303 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
305 ;; GROUP 2 - BIT, RES, SET
306 ;; GROUP 3 - RLC, RRC, RL, RR, SLA, SRA, SRL
308 GROUP2:
309   sub   OPC_COUNT_GROUPS_2_AND_3
310   jr    nc,GROUP4
311   cp    OPC_COUNT_GROUP_2-OPC_COUNT_GROUPS_2_AND_3
312   call  c,CBIT
313   ret   c
314   call  REGLO
315   ret   c
316   call  CBX
317   jr    BYTE0
319 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
321 ;; GROUP 4 - PUSH, POP, EX (SP)
323 GROUP4:
324   sub   OPC_COUNT_GROUP_4
325   jr    nc,GROUP5
327   call  PAIR
328   ret   c
329   jr    BYTE0
331 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
333 ;; GROUP 5 - SUB, AND, XOR, OR, CP
334 ;; GROUP 6 - ADD, ADC, SBC
336 GROUP5:
337   sub   OPC_COUNT_GROUPS_5_AND_6
338   jr    nc,GROUP7
339   cp    OPC_COUNT_GROUP_5-OPC_COUNT_GROUPS_5_AND_6
340   ld    b,7
341   call  nc,OPND
342   ld    a,b
343   cp    7
344   jr    nz,G6HL
346   call  REGLO
347   ld    a,c
348   jr    nc,BIND1
349   xor   46H
350   call  BIND
351 DBX:
352   call  NUMBER
353   jr    VAL8
355 G6HL:
356   and   3FH
357   cp    12
358   scf
359   ret   nz
360   ld    a,c
361   cp    80H
362   ld    c,9
363   jr    z,G4
364   xor   1CH
365   rrca
366   ld    c,a
367   call  EDX
368   jr    G4
370 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
372 ;; GROUP 7 - INC, DEC
374 GROUP7:
375   sub   OPC_COUNT_GROUP_7
376   jr    nc,GROUP8
377   call  REGHI
378   ld    a,c
379 BIND1:
380   jp    nc,BIND
381   xor   64H
382   rlca
383   rlca
384   rlca
385   ld    c,a
386   call  PAIR1
387   ret   c
388 BYTE0:
389   ld    a,c
390   jr    BYTE2
392 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
394 ;; GROUP 8 - IN
395 ;; GROUP 9 - OUT
397 GROUP8:
398   sub   OPC_COUNT_GROUPS_8_AND_9
399   jr    nc,GROUPA
400   cp    OPC_COUNT_GROUP_8-OPC_COUNT_GROUPS_8_AND_9
401   call  z,CORN
402   ex    af,af'
403   call  REGHI
404   ret   c
405   ex    af,af'
406   call  c,CORN
407   inc   h
408   jr    z,BYTE0
409   ld    a,b
410   cp    7
411   scf
412   ret   nz
413   ld    a,c
414   xor   3
415   rlca
416   rlca
417   rlca
418   call  BYTE
419   jr    VAL8
421 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
423 ;; GROUP 10 - JR, DJNZ
425 GROUPA:
426   sub   OPC_COUNT_GROUP_10
427   jr    nc,GROUPB
428   cp    OPC_COUNT_GROUP_10_JRS-OPC_COUNT_GROUP_10
429   call  nz,COND
430   ld    a,c
431   jr    nc,GRPA
432   ld    a,18H
433 GRPA:
434   call  BYTE
435   call  NUMBER
436   ld    de,(PC)
437   inc   de
438   scf
439   sbc   hl,de
440   ld    a,l
441   rla
442   sbc   a,a
443   cp    h
444 TOOFAR:
445   call  nz,JR_TOO_FAR
446 VAL8:
447   ld    a,l
448   jr    BYTE2
450 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
452 ;; GROUP 11 - JP
454 GROUPB:
455   ld    b,a
456   jr    nz,GROUPC
457   call  COND
458   ld    a,c
459   jr    nc,GRPB
460   ld    a,b
461   and   3FH
462   cp    6
463   ld    a,0E9H
464   jr    z,BYTE2
465   ld    a,0C3H
466 GRPB:
467   call  BYTE
468   jr    ADDR
470 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
472 ;; GROUP 12 - CALL
474 GROUPC:
475   djnz  GROUPD
476 GRPC:
477   call  GRPE
478 ADDR:
479   call  NUMBER
480 VAL16:
481   call  VAL8
482   ld    a,h
483   jr    BYTE2
485 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
487 ;; GROUP 13 - RST
489 GROUPD:
490   djnz  GROUPE
491   call  NUMBER
492   and   c
493   or    h
494   jr    nz,TOOFAR
495   ld    a,l
496   or    c
497 BYTE2:
498   jr    BYTE1
500 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
502 ;; GROUP 14 - RET
504 GROUPE:
505   djnz  GROUPF
506 GRPE:
507   call  COND
508   ld    a,c
509   jr    nc,BYTE1
510   or    9
511   jr    BYTE1
513 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
515 ;; GROUP 15 - LD
517 GROUPF:
518   djnz  MISC
519   call  LDOP
520   jr    nc,LDA
521   call  REGHI
522   ex    af,af'
523   call  SKIP
524   cp    '('
525   jr    z,LDIN
526   ex    af,af'
527   jp    nc,G6
528   ld    c,1
529   call  PAIR1
530   ret   c
531   ld    a,14
532   cp    b
533   ld    b,a
534   call  z,PAIR
535   ld    a,b
536   and   3FH
537   cp    12
538   ld    a,c
539   jr    nz,GRPB
540   ld    a,0F9H
541   jr    BYTE1
542   ;
543 LDIN:
544   ex    af,af'
545   push  bc
546   call  nc,REGLO
547   ld    a,c
548   pop   bc
549   jr    nc,BIND
550   ld    c,0AH
551   call  PAIR1
552   call  LD16
553   jr    nc,GRPB
554   call  NUMBER
555   ld    c,2
556   call  PAIR
557   call  LD16
558   ret   c
559   call  BYTE
560   jr    VAL16
562 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
563 ;; misc. instructions
564 MISC:
565   ld    a,1
566   ld    (ASM_WAS_PSEUDO),a
567   jp    MISC_DEFB
570 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
572 ;; SUBROUTINES
575 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
577 JR_TOO_FAR:
578   ld    hl,(ASM_JR_TOO_FAR_CB)
579   jp    (hl)
581 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
583 LDA:
584   cp    4
585   call  c,EDX
586   ld    a,b
587 BYTE1:
588   jr    BYTE
590 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
592 LD16:
593   ld    a,b
594   jr    c,LD8
595   ld    a,b
596   and   3FH
597   cp    12
598   ld    a,c
599   ret   z
600   call  EDX
601   ld    a,c
602   or    43H
603   ret
605 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
607 LD8:
608   cp    7
609   scf
610   ret   nz
611   ld    a,c
612   or    30H
613   ret
615 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
617 CORN:
618   push  bc
619   call  OPND
620   bit   5,b
621   pop   bc
622   jr    z,NUMBER
623   ld    h,-1
624 EDX:
625   ld    a,0EDH
626   jr    BYTE
628 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
630 CBX:
631   ld    a,0CBH
632 BIND:
633   cp    76H
634   scf
635   ret   z               ;REJECT LD (HL),(HL)
636   call  BYTE
637   inc   d
638   ret   p
639   ld    a,e
640   jr    BYTE
642 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
644 OPND:
645   push  hl
646   ld    hl,OPRNDS
647   call  FIND
648   pop   hl
649   ret   c
650   bit   7,b
651   ret   z
652   bit   3,b
653   push  hl
654   call  z,OFFSET
655   ld    e,l
656   pop   hl
657   ld    a,0DDH
658   bit   6,b
659   jr    z,OP1
660   ld    a,0FDH
661 OP1:
662   or    a
663   inc   d
664   ld    d,a
665   ret   m
666 BYTE:
667   ld    (ix),a
668   inc   ix
669   or    a
670   ret
672 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
674 OFFSET:
675   ld    a,(iy)
676   cp    ')'
677   ld    hl,0
678   ret   z
680 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
681 ;; parse numeric expression
682 NUMBER:
683   call  SKIP
684   push  bc
685   push  de
686   push  ix
687   call  PARSE_INT_EXPR
688   ; HL: expression value
689   pop   ix
690   pop   de
691   pop   bc
692   ld    a,l
693   or    a
694   ret
696 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
698 REG:
699   call  OPND
700   ret   c
701   ld    a,b
702   and   3FH
703   cp    8
704   ccf
705   ret
707 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
709 REGLO:
710   call  REG
711   ret   c
712   jr    ORC
714 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
716 REGHI:
717   call  REG
718   ret   c
719   jr    SHL3
721 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
723 COND:
724   call  OPND
725   ret   c
726   ld    a,b
727   and   1FH
728   sub   16
729   jr    nc,SHL3
730   cp    -15
731   scf
732   ret   nz
733   ld    a,3
734   jr    SHL3
736 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
738 PAIR:
739   call  OPND
740   ret   c
741 PAIR1:
742   ld    a,b
743   and   0FH
744   sub   8
745   ret   c
746   jr    SHL3
748 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
750 CBIT:
751   call  NUMBER
752   cp    8
753   ccf
754   ret   c
755 SHL3:
756   rlca
757   rlca
758   rlca
759 ORC:
760   or    c
761   ld    c,a
762   ret
765 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
767 ;; common defb/defw code
768 ;;   carry set if we want words
770 COM_DEFBW:
771   ; note that we don't care about A, we only need flags here
772   push  af      ; save flags
773   push  ix
774   call  PARSE_INT_EXPR
775   pop   ix
776   ; HL: value
777   ld    (ix),l
778   inc   ix
779   pop   af      ; restore flags
780   jr    nc,COM_DEFBW_BYTE
781   ld    (ix),h
782   inc   ix
783 COM_DEFBW_BYTE:
784   ex    af,af'  ; save flags
785   ; check for comma
786   call  SKIP
787   ret   z
788   ex    af,af'  ; restore flags
789   jr    COM_DEFBW
791 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
793 ;; DEFB
795 MISC_DEFB:
796   djnz  MISC_DEFW
797 DEFB_LOOP:
798   or    a
799   jr    COM_DEFBW
801 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
803 ;; DEFW
805 MISC_DEFW:
806   djnz  MISC_DEFM
807 DEFW_LOOP:
808   scf
809   jr    COM_DEFBW
811 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
813 ;; DEFM
815 MISC_DEFM:
816   djnz  MISC_ORG
817 DEFM_LOOP:
818   push  ix
819   call  PARSE_STR_EXPR
820   pop   ix
821   ; HL: buffer address
822   ;  E: buffer length
823   xor   a
824   cp    e
825   jr    z,DEFM1_DONE_ONE
826 DEFM1:
827   ld    a,(hl)
828   inc   hl
829   call  BYTE
830   dec   e
831   jr    nz,DEFM1
832 DEFM1_DONE_ONE:
833   ; check for comma
834   call  SKIP
835   ret   z
836   jr    DEFM_LOOP
838 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
840 ;; ORG
842 MISC_ORG:
843   IF @BZ80ASM_ORGENTDISP
844   djnz  MISC_DISP
845   ld    a,ORGENT_ORG
846 COM_ORGENT:
847   ld    (ORGENT_TYPE),a
848   call  NUMBER
849   ld    (NEW_ORGENT),hl
850   or    a
851   ret
853 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
855 ;; DISP
857 MISC_DISP:
858   djnz  MISC_ENT
859   ld    a,ORGENT_DISP
860   jr    COM_ORGENT
862 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
864 ;; ENT
866 MISC_ENT:
867   djnz  MISC_WTF
868   ld    a,ORGENT_ENT
869   jr    COM_ORGENT
871 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
873 ;; error (the thing that should not be)
875 MISC_WTF:
876   ENDIF
877   ; set "unknown instruction index"
878   ; b may be zero here
879   ld    a,b
880   inc   a
881   ld    (ASM_BAD_B),a
882   scf
883   ret
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
892 ;; IN:
893 ;;   IY: text buffer
894 ;; OUT:
895 ;;   IY: new position in the text buffer
896 ;;    A: token number
897 ;;    B: token data byte
898 ;;   carry flag set on error (invalid char, or token not found)
899 ;;   on error, delimiters are still skipped
901 LDOP:
902   ld    hl,LDOPS
903 ; main table search entry point
904 FIND:
905   call  SKIP
906 EXIT:
907   ld    b,0
908   scf
909   ret   z
910   ; reject chars with high bit set
911   cp    128
912   ccf
913   ret   c
915 FIND0:
916   ; check the first char
917   ld    a,(hl)
918   or    a
919   jr    z,EXIT
920   xor   (iy)
921   and   %01011111   ; for case-insensitivity
922   jr    z,FIND2
923   ; first char is not matched, skip this token
924 FIND1:
925   bit   7,(hl)
926   inc   hl
927   jr    z,FIND1
928   ; skip token data byte
929   inc   hl
930   ; increment token counter
931   inc   b
932   jr    FIND0
934   ; first char matched, check the rest
935   ; A holds zero (due to xor/and)
936 FIND2:
937   push  iy
938 FIND3:
939   ; last token char?
940   bit   7,(hl)
941   inc   iy
942   inc   hl
943   jr    nz,FIND5
944   ; not the last token char
945   ; this compares (HL) with 0, because
946   ; A is guaranteed to hold zero here
947   cp    (hl)
948   ; zero byte in token means "skip delimiters"
949   ; it is used in some opcodes with fixed operands
950   call  z,SKIP0
951   ; load token char
952   ld    a,(hl)
953   ; compare with input char
954   xor   (iy)
955   and   %01011111   ; for case-insensitivity
956   jr    z,FIND3
958   ; alas, doesn't match
959 FIND4:
960   ; restore input stream pointer
961   pop   iy
962   ; and skip this token
963   jr    FIND1
965   ; we'll come here if we succesfully matched a token
966 FIND5:
967   ; if it isn't followed by a delimiter, '+' or '-', this is not a valid token
968   call  DELIM
969   call  nz,SIGN
970   jr    nz,FIND4
972   ; this token is valid
973 FIND6:
974   ; move token index to A
975   ld    a,b
976   ; load B with token data byte
977   ld    b,(hl)
978   ; drop original input stream position
979   pop   hl
980   ; we're done here
981   ; note that carry flag is guaranteed to be reset
982   ret
985 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
986 ;; used by FIND
987 SIGN:
988   cp    '+'
989   ret   z
990   cp    '-'
991   ret
993 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
994 ;; this entry point is used by FIND
995 SKIP0:
996   inc   hl
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
1004 SKIP:
1005   ; delimiter or terminator?
1006   call  DELIM
1007   ret   nz
1008   ; if this is terminator, still stop
1009   call  TERM
1010   ret   z
1011   ; this is delimiter, skip it
1012   inc   iy
1013   jr    SKIP
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
1019 DELIM:
1020   ld    a,(iy)          ;ASSEMBLER DELIMITER
1021   cp    ' '
1022   ret   z
1023   cp    ','
1024   ret   z
1025   cp    ')'
1026   ret   z
1028 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1029 ;; entry point used by SKIP
1030 TERM:
1031   cp    ';'             ;ASSEMBLER TERMINATOR
1032   ret   z
1034 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1035 ;; also used by assembler to check for command separator
1036 ;; the assembler itself does nothing with separators
1037 ASM_IS_SEP:
1038   cp    ':'             ;ASSEMBLER SEPARATOR
1039   ret   nc
1040   cp    CR
1041   ret
1043 csizest = $-csizest
1044 $printf "assembler size: %d", csizest
1047 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"
1056 ;;   #nnnn -- hex
1057 ;;   $nnnn -- hex
1058 ;;   &nnnn -- hex
1059 ;;   %nnnn -- binary
1060 ;;   0Xnnn -- hex
1061 ;;   0Onnn -- octal
1062 ;;   0Bnnn -- binary
1063 ;;   nnnnH -- hex
1064 ;;   nnnnB -- binary
1065 ;;   nnnnO -- octal
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
1076 ;; IN:
1077 ;;   IY: text buffer
1078 ;; OUT:
1079 ;;   IY: text buffer after the expression
1080 ;;   HL: number
1081 ;;   carry flag reset
1082 ;;  OR: (cannot parse as a number)
1083 ;;   IY: unchanged
1084 ;;   carry flag set
1085 ;;  DE,AF,flags: dead
1087 PARSE_NUMBER:
1088   call  PARSER_SKIP_BLANKS
1089   scf
1090   ret   z
1091   push  iy          ; we will need to rollback on error
1092   ; A already contains a char, loaded by `PARSER_SKIP_BLANKS`
1093   cp    '#'
1094   jr    z,.hexprefix
1095   cp    '$'
1096   jr    z,.maybe_lone_dollar
1097   cp    '&'
1098   jr    z,.hexprefix
1099   cp    '%'
1100   jr    z,.binprefix
1101   ; no, leading zero doesn't mean "octal", this is stupid
1102   ; but we may have prefixes like "0x" and such
1103   cp    '0'
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
1108   cp    10
1109   ; nope, do not allow it, all numbers must start with a digit
1110   ;jr    nc,.must_be_hex_with_sfx
1111   ccf
1112   jr    c,.not_a_number_carry_set
1113 .do_normal_decimal:
1114   ; done with prefixes, try decimal number
1115   ; we'll switch to suffix checking on hex digit
1116   ld    hl,0        ; accumulator
1117 .decimal_loop:
1118   call  .getDigit
1119   jr    c,.decimal_done
1120   cp    10
1121   jr    nc,.must_be_hex_with_sfx
1122   ; HL=HL*10
1123   add   hl,hl
1124   ld    de,hl
1125   add   hl,hl
1126   add   hl,hl
1127   add   hl,de
1128   ; HL=HL+A
1129   ld    e,a
1130   ld    d,0
1131   add   hl,de
1132   ; next char
1133   inc   iy
1134   jr    .decimal_loop
1135 .decimal_done:
1136   ; check for suffix
1137   ld    a,(iy)
1138   and   %11011111   ; cheap uppercase
1139   cp    'H'
1140   jr    z,.must_be_hex_with_sfx
1141   cp    'B'
1142   jr    z,.bin_with_sfx
1143   cp    'O'
1144   jr    z,.oct_with_sfx
1145   ; no suffix, we're done
1147 .success:
1148   pop   de          ; drop iy
1149   ; reset carry flag
1150   or    a
1151   ret
1153 .not_a_number_carry_set:
1154   pop   iy
1155   ret
1157 .hexprefix:
1158   ; skip prefix
1159   inc   iy
1160   call  .parse_as_hex
1161 .after_prefix:
1162   jr    c,.not_a_number_carry_set
1163   jr    .success
1165 .maybe_lone_dollar:
1166   ; lone dollar means "PC"
1167   inc   iy
1168   call  .parse_as_hex
1169   ; the only case we may gen an error here is
1170   ; when our dollar isn't followed by a digit
1171   jr    nc,.success
1172   ; lone dollar is good too
1173   ; IY points right after the dollar here
1174   ld    hl,(BZ80ASM.PC)
1175   jr    .success
1177 .binprefix:
1178   ; skip prefix
1179   inc   iy
1180   call  .parse_as_bin
1181   jr    .after_prefix
1183 .maybe_binprefix:
1184   ; things like "0BEEFh" should be parsed as hex
1185   ; skip prefix
1186   inc   iy
1187   call  .parse_as_bin
1188   jr    c,.must_be_hex_with_sfx
1189   ; check for 'H'
1190   ld    a,(iy)
1191   and   %11011111   ; cheap uppercase
1192   cp    'H'
1193   jr    z,.must_be_hex_with_sfx
1194   jr    .success
1196 .octprefix:
1197   ; skip prefix
1198   inc   iy
1199   call  .parse_as_oct
1200   jr    .after_prefix
1202 .maybe_zero_prefix:
1203   ; check for '0x' and such
1204   ; skip '0'
1205   inc   iy
1206   ; load and prefix
1207   ; there's no need to skip it, as it will be
1208   ; skipped by the corresponding subroutine
1209   ld    a,(iy)
1210   ; so IY will point to the actual number
1211   and   %11011111   ; cheap uppercase
1212   cp    'X'
1213   jr    z,.hexprefix
1214   cp    'B'
1215   jr    z,.maybe_binprefix
1216   cp    'O'
1217   jr    z,.octprefix
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
1223   pop   iy
1224   push  iy
1225   call  .parse_as_hex
1226   jr    c,.not_a_number_carry_set
1227   ld    a,(iy)
1228   inc   iy
1229   and   %11011111   ; cheap uppercase
1230   cp    'H'
1231   jr    z,.success
1233 .bin_with_sfx:
1234   ; reparse as bin, skip suffix (it is guaranteed to be there)
1235   pop   iy
1236   push  iy
1237   call  .parse_as_bin
1238 .done_guaranteed_suffix:
1239   jr    c,.not_a_number_carry_set
1240   ; skip suffix
1241   inc   iy
1242   jr    .success
1244 .oct_with_sfx:
1245   ; reparse as bin, skip suffix (it is guaranteed to be there)
1246   pop   iy
1247   push  iy
1248   call  .parse_as_bin
1249   jr    .done_guaranteed_suffix
1251 .parse_as_hex:
1252   ld    hl,0        ; accumulator
1253   ; check first digit (as this is general parser)
1254   call  .getDigitNoUnder
1255   ret   c
1256 .parse_as_hex_loop:
1257   inc   iy
1258   add   hl,hl
1259   add   hl,hl
1260   add   hl,hl
1261   add   hl,hl
1262   ld    e,a
1263   ld    d,0
1264   add   hl,de
1265   call  .getDigit
1266   jr    nc,.parse_as_hex_loop
1267   ; clear carry flag (it is always set here)
1268   ccf
1269   ret
1271 .parse_as_bin:
1272   ld    hl,0        ; accumulator
1273   ; check first digit (as this is general parser)
1274   ld    a,(iy)
1275   call  .getDigitNoUnderBin
1276   ret   c
1277 .parse_as_bin_loop:
1278   inc   iy
1279   add   hl,hl
1280   ld    e,a
1281   ld    d,0
1282   add   hl,de
1283   call  .getBinDigit
1284   jr    nc,.parse_as_bin_loop
1285   ; clear carry flag (it is always set here)
1286   ccf
1287   ret
1289 .parse_as_oct:
1290   ld    hl,0        ; accumulator
1291   ; check first digit (as this is general parser)
1292   ld    a,(iy)
1293   call  .getDigitNoUnderOct
1294 .parse_as_oct_loop:
1295   inc   iy
1296   add   hl,hl
1297   add   hl,hl
1298   add   hl,hl
1299   ld    e,a
1300   ld    d,0
1301   add   hl,de
1302   call  .getOctDigit
1303   jr    nc,.parse_as_oct_loop
1304   ; clear carry flag (it is always set here)
1305   ccf
1306   ret
1309 .getDigit_inc:
1310   inc   iy
1311 .getDigit:
1312   ld    a,(iy)
1313   cp    '_'
1314   jr    z,.getDigit_inc
1315   jr    PARSER_CONV_DIGIT
1317   ; d: base
1318 .getDigitInBase:
1319   call  .getDigit
1320   ret   c
1321   cp    d
1322   ccf
1323   ret
1325 .getDigitNoUnder:
1326   ld    a,(iy)
1327   jr    PARSER_CONV_DIGIT
1329 .getDecDigit:
1330   ld    d,10
1331   jr    .getDigitInBase
1333 .getDigitNoUnderOct:
1334   ld    a,(iy)
1335 .getOctDigit:
1336   ld    d,8
1337   jr    .getDigitInBase
1339 .getDigitNoUnderBin:
1340   ld    a,(iy)
1341 .getBinDigit:
1342   ld    d,2
1343   jr    .getDigitInBase
1346 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1347 ;; converts 'A' to digit (assume hex)
1348 ;; carry set: not a digit char (and A is destroyed)
1350 PARSER_CONV_DIGIT:
1351   sub   '0'
1352   ret   c
1353   cp    10
1354   ccf
1355   ret   nc
1356   add   a,'0'
1357   and   %11011111   ; cheap uppercase
1358   sub   'A'-10
1359   ret   c
1360   cp    16
1361   ccf
1362   ret
1364 csizest = $-csizest
1365 $printf "assembler numparse size: %d", csizest
1368 csizest = $
1370 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1371 ;; math expression parser
1372 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1374 PARSE_EXPR_ERROR_A:
1375   ld    hl,(EXPR_ERROR_CB)
1376   jp    (hl)
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
1390 ;; IN:
1391 ;;   IY: text buffer
1392 ;; OUT:
1393 ;;   IY: text buffer after the expression
1394 ;;   HL: expression value
1395 ;;   everything other (including all alternate registers) is dead
1397 ;; priorities:
1398 ;;   unaries
1399 ;;   * / %
1400 ;;   + -
1401 ;;   << >>
1403 PARSE_INT_EXPR:
1404   call  PARSER_SKIP_BLANKS
1405   jr    z,PARSE_EXPR_ERROR_INT
1406   ; unary "+" or "-"?
1407   call  BZ80ASM.SIGN
1408   jr    nz,.doexpr
1409   push  af
1410   inc   iy
1411   call  .doexpr
1412   pop   af
1413   ; check for negate
1414   cp    '-'
1415   ret   nz
1416   ; negate HL
1417   or    a
1418   ld    de,0
1419   ex    de,hl
1420   sbc   hl,de
1421   ret
1423 .doexpr:
1424 ;; |
1425 .bitor:
1426   ; get first operand
1427   ; already done above
1428   call  .bitxor
1429 .bitor_next:
1430   ; check for operation
1431   call  PARSER_SKIP_BLANKS
1432   ret   z       ; exit on EOL
1433   cp    '|'
1434   ret   nz
1435   ; get second operand
1436   ld    bc,.bitxor
1437   call  .go_down_bc
1438   ld    a,l
1439   or    e
1440   ld    l,a
1441   ld    a,h
1442   or    d
1443   ld    h,a
1444   jr    .bitor_next
1446 ;; ^
1447 .bitxor:
1448   ; get first operand
1449   call  .bitand
1450 .bitxor_next:
1451   ; check for operation
1452   call  PARSER_SKIP_BLANKS
1453   ret   z       ; exit on EOL
1454   cp    '^'
1455   ret   nz
1456   ; get second operand
1457   ld    bc,.bitand
1458   call  .go_down_bc
1459   ld    a,l
1460   xor   e
1461   ld    l,a
1462   ld    a,h
1463   xor   d
1464   ld    h,a
1465   jr    .bitxor_next
1467 ;; &
1468 .bitand:
1469   ; get first operand
1470   call  .shlshr
1471 .bitand_next:
1472   ; check for operation
1473   call  PARSER_SKIP_BLANKS
1474   ret   z       ; exit on EOL
1475   cp    '&'
1476   ret   nz
1477   ; get second operand
1478   ld    bc,.shlshr
1479   call  .go_down_bc
1480   ld    a,l
1481   and   e
1482   ld    l,a
1483   ld    a,h
1484   and   d
1485   ld    h,a
1486   jr    .bitand_next
1488 ;; << >>
1489 .shlshr:
1490   call  .addsub
1491 .shlshr_next:
1492   ; check for operation
1493   call  PARSER_SKIP_BLANKS
1494   ret   z       ; exit on EOL
1495   ; (iy+0) and (iy+1) should be equal
1496   cp    (iy+1)
1497   ret   nz
1498   cp    '<'     ; %0011_1100
1499   jr    z,.doshift
1500   cp    '>'     ; %0011_1110
1501   ret   nz
1502 .doshift:
1503   ; get second operand
1504   inc   iy      ; skip operation part
1505   ld    bc,.addsub
1506   call  .go_down_bc
1507   ex    af,af'
1508   ; HL: number to shift
1509   ; DE: amount
1510   ld    a,d
1511   or    a
1512   jr    nz,.shift_too_far
1513   ld    a,e
1514   cp    16
1515   jr    nc,.shift_too_far
1516   ld    b,a
1517   ex    af,af'
1518   cp    '<'
1519   jr    z,.do_shl
1520   ; shr
1521 .do_shr_loop:
1522   srl   h
1523   rr    l
1524   djnz  .do_shr_loop
1525   jr    .shlshr_next
1526   ; shl
1527 .do_shl:
1528   ; shl
1529   sla   l
1530   rl    h
1531   djnz  .do_shl
1532   jr    .shlshr_next
1533 .shift_too_far:
1534   ld    hl,0
1535   jr    .shlshr_next
1537 ;; + -
1538 .addsub:
1539   ; get first operand
1540   call  .muldiv
1541 .addsub_next:
1542   ; check for operation
1543   call  PARSER_SKIP_BLANKS
1544   ret   z       ; exit on EOL
1545   cp    '+'     ; %0010_1011
1546   jr    z,.doaddsub
1547   cp    '-'     ; %0010_1101
1548   ret   nz
1549 .doaddsub:
1550   ; get second operand
1551   ld    bc,.muldiv
1552   call  .go_down_bc
1553   cp    '-'
1554   jr    z,.dosub
1555   add   hl,de
1556   jr    .addsub_next
1557 .dosub:
1558   sbc   hl,de
1559   jr    .addsub_next
1561 ;; * / %
1562 .muldiv:
1563   ; get first operand
1564   call  .term
1565 .muldiv_next:
1566   ; check for operation
1567   call  PARSER_SKIP_BLANKS
1568   ret   z       ; exit on EOL
1569   cp    '*'     ; %0010_1010
1570   jr    z,.domuldiv
1571   cp    '/'     ; %0010_1111
1572   jr    z,.domuldiv
1573   cp    '%'     ; %0010_0101
1574   ret   nz
1575 .domuldiv:
1576   ; get second operand
1577   ld    bc,.term
1578   call  .go_down_bc
1579   ld    bc,hl
1580   cp    '*'
1581   jr    z,.domul
1582   ex    af,af'  ; save operation
1583   ; div or mod
1584   ld    a,e
1585   or    d
1586   jp    z,PARSE_EXPR_ERROR_0DIV
1587   call  PARSER_UDIV_BC_DE
1588   ; was it div or mod?
1589   ex    af,af'
1590   cp    '%'
1591   jr    z,.muldiv_next  ; remainder already in hl
1592   ; division
1593   ld    hl,bc
1594   jr    .muldiv_next
1595 .domul:
1596   call  PARSER_UMUL_BC_DE
1597   jr    .muldiv_next
1599 ;; parse term, also process unaries and parens
1600 .term:
1601   call  PARSER_SKIP_BLANKS
1602   jp    z,PARSE_EXPR_ERROR_INT
1603   inc   iy      ; skip operation
1604   ld    c,')'
1605   cp    '('
1606   jr    z,.term_lparen
1607   cp    '['
1608   ld    c,']'
1609   jr    z,.term_lparen
1610   cp    '~'
1611   jr    z,.term_ubitnot
1612   ; this must be number
1613   dec   iy      ; undo skip
1614   call  PARSE_NUMBER
1615   ret   nc
1616   ; check for labels
1617   push  iy      ; for correct error position
1618   push  ix      ; the only register we care about
1619   ld    hl,.term_checklabel_ret
1620   push  hl
1621   ld    hl,(GETLABEL_CB)
1622   jp    (hl)
1623 .term_checklabel_ret:
1624   pop   ix
1625   pop   de
1626   ret   nc
1627   ; oops, error
1628   ; restore position for correct error reporting
1629   push  de
1630   pop   iy
1631   jp    PARSE_EXPR_ERROR_INT
1633   ;; "("
1634 .term_lparen:
1635   ;; C contains matching rparen
1636   push  bc
1637   call  PARSE_INT_EXPR
1638   call  PARSER_SKIP_BLANKS
1639   jr    z,PARSE_EXPR_ERROR_RPAREN
1640   pop   bc
1641   cp    c
1642   jr    nz,PARSE_EXPR_ERROR_RPAREN
1643   inc   iy
1644   ret
1646   ;; "~"
1647 .term_ubitnot:
1648   call  .term
1649   ld    a,h
1650   cpl
1651   ld    h,a
1652   ld    a,l
1653   cpl
1654   ld    l,a
1655   ret
1657   ;; call subroutine at BC
1658   ;; AF: preserved
1659   ;; HL: preserved
1660   ;; DE: subroutine result
1661   ;; i.e. HL is op0, DE is op1
1662 .go_down_bc:
1663   push  hl
1664   push  af      ; A holds operation
1665   inc   iy      ; skip operation
1666   ld    hl,.go_down_bc_ret
1667   push  hl
1668   push  bc
1669   ret
1670 .go_down_bc_ret:
1671   pop   af
1672   pop   de
1673   ex    de,hl
1674   ret
1676 PARSE_EXPR_ERROR_RPAREN:
1677   ld    a,EXPR_ERR_RPAREN_EXPECTED
1678   jp    PARSE_EXPR_ERROR_A
1681 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1683 ;; HL=BC*DE
1685 ;; BC,DE,A,flags: dead
1687 PARSER_UMUL_BC_DE:
1688   ; DEHL=BC*DE
1689   ld    hl,0
1690   ld    a,16
1691 .loop:
1692   add   hl,hl
1693   rl    e
1694   rl    d
1695   jr    nc,.skip
1696   add   hl,bc
1697   jr    nc,.skip
1698   inc   de
1699 .skip:
1700   dec   a
1701   jr    nz,.loop
1702   ret
1705 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1707 ;; performs BC/DE
1708 ;; OUT:
1709 ;;   BC: quotient
1710 ;;   HL: remainder
1712 ;; DE,A,flags: dead
1714 PARSER_UDIV_BC_DE:
1715   ld    hl,0
1716   ld    a,16
1717 .loop:
1718   sll   c
1719   rl    b
1720   adc   hl,hl
1721   sbc   hl,de
1722   jr    nc,.skip
1723   add   hl,de
1724   dec   c
1725 .skip:
1726   dec   a
1727   jr    nz,.loop
1728   ret
1731 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1733 ;; parse a string expression
1735 ;; IN:
1736 ;;   IY: text buffer
1737 ;; OUT:
1738 ;;   HL: string buffer start
1739 ;;    E: parsed string length
1740 ;;   everything other (including all alternate registers) is dead
1742 PARSE_STR_EXPR:
1743   call  PARSER_SKIP_BLANKS
1744   jr    z,PARSE_EXPR_ERROR_STR
1745   cp    34
1746   jr    z,.strok
1747   cp    39
1748   jr    nz,PARSE_EXPR_ERROR_STR
1749 .strok:
1750   ld    c,a  ; terminator
1751   inc   iy
1752   ; remember buffer start
1753   push  iy
1754 .strloop:
1755   ld    a,(iy)
1756   or    a
1757   jr    z,PARSE_EXPR_ERROR_STR
1758   cp    CR
1759   jr    z,PARSE_EXPR_ERROR_STR
1760   inc   iy
1761   cp    c
1762   jr    nz,.strloop
1763   ; done string parsing, calc length
1764   pop   hl  ; buffer start
1765   ex    de,hl
1766   push  iy
1767   pop   hl
1768   ; DE: buffer start
1769   ; HL: buffer end
1770   or    a
1771   sbc   hl,de
1772   ex    de,hl
1773   ret
1775 PARSE_EXPR_ERROR_STR:
1776   ld    a,EXPR_ERR_STRING_EXPECTED
1777   jp    PARSE_EXPR_ERROR_A
1780 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1781 ;; skip blanks
1782 ;; returns current char in A
1783 ;; sets zero flag on EOL
1784 ;; IN:
1785 ;;   IY: text buffer
1786 ;; OUT:
1787 ;;   IY: text buffer at non-blank or EOL
1788 ;;    A: non-blank or EOL char
1789 ;;   zero flag is set on EOL/terminator
1791 PARSER_SKIP_BLANKS:
1792   ld    a,(iy)
1793   call  TERM
1794   ret   z
1795   inc   iy
1796   cp    33
1797   jr    c,PARSER_SKIP_BLANKS
1798   dec   iy
1799   ; reset zero flag
1800   or    a
1801   ret
1803 csizest = $-csizest
1804 $printf "assembler exprparse size: %d", csizest
1807 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1808 ;; various tables -- mnemonics, operands...
1810 csizest = $
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
1825 ;; ALU with accum
1826 OPC_COUNT_GROUP_5 equ 5
1827 ;; ALU with accum
1828 OPC_COUNT_GROUP_6 equ 3
1829 ;; GROUPS 5 and 6
1830 OPC_COUNT_GROUPS_5_AND_6 equ OPC_COUNT_GROUP_5+OPC_COUNT_GROUP_6
1832 ;; INC/DEC
1833 OPC_COUNT_GROUP_7 equ 2
1835 ;; IN
1836 OPC_COUNT_GROUP_8 equ 1
1837 ;; OUT
1838 OPC_COUNT_GROUP_9 equ 1
1839 ;; GROUPS 8 and 9
1840 OPC_COUNT_GROUPS_8_AND_9 equ OPC_COUNT_GROUP_8+OPC_COUNT_GROUP_9
1842 ; JR,DJNZ
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.
1850 ;; mnemonics
1851 OPCODS:
1852   ; GROUPS 0 AND 1: "trivial" (39, OPC_COUNT_GROUPS_0_AND_1)
1853   ; GROUP 0: "trivial" one-byte (15, OPC_COUNT_GROUP_0)
1854   defx  "NOP"
1855   defb  0
1856   defx  "RLCA"
1857   defb  7
1858   defm  "EX",0,"AF",0
1859     defx "AF'"
1860   defb  8
1861   defx  "RRCA"
1862   defb  #0F
1863   defx  "RLA"
1864   defb  #17
1865   defx  "RRA"
1866   defb  #1F
1867   defx  "DAA"
1868   defb  #27
1869   defx  "CPL"
1870   defb  #2F
1871   defx  "SCF"
1872   defb  #37
1873   defx  "CCF"
1874   defb  #3F
1875   defx  "HALT"
1876   defb  #76
1877   defx  "EXX"
1878   defb  #D9
1879   defm  "EX",0,"DE",0
1880     defx "HL"
1881   defb  #EB
1882   defx  "DI"
1883   defb  #F3
1884   defx  "EI"
1885   defb  #FB
1886   ; GROUP 1: "trivial" ED-prefixed (24, OPC_COUNT_GROUPS_0_AND_1-OPC_COUNT_GROUP_0)
1887   defx  "NEG"
1888   defb  #44
1889   defm  "IM",0
1890     defx  "0"
1891   defb  #46
1892   defx  "RETN"
1893   defb  #45
1894   defx  "RETI"
1895   defb  #4D
1896   defm  "IM",0
1897     defx  "1"
1898   defb  #56
1899   defm  "IM",0
1900     defx  "2"
1901   defb  #5E
1902   defx  "RRD"
1903   defb  #67
1904   defx  "RLD"
1905   defb  #6F
1906   defx  "LDI"
1907   defb  #A0
1908   defx  "CPI"
1909   defb  #A1
1910   defx  "INI"
1911   defb  #A2
1912   defx  "OUTI"
1913   defb  #A3
1914   defx  "LDD"
1915   defb  #A8
1916   defx  "CPD"
1917   defb  #A9
1918   defx  "IND"
1919   defb  #AA
1920   defx  "OUTD"
1921   defb  #AB
1922   defx  "LDIR"
1923   defb  #B0
1924   defx  "CPIR"
1925   defb  #B1
1926   defx  "INIR"
1927   defb  #B2
1928   defx  "OTIR"
1929   defb  #B3
1930   defx  "LDDR"
1931   defb  #B8
1932   defx  "CPDR"
1933   defb  #B9
1934   defx  "INDR"
1935   defb  #BA
1936   defx  "OTDR"
1937   defb  #BB
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)
1941   defx  "BIT"
1942   defb  #40
1943   defx  "RES"
1944   defb  #80
1945   defx  "SET"
1946   defb  #C0
1947   ; GROUP 3: shifts (7, OPC_COUNT_GROUPS_2_AND_3-OPC_COUNT_GROUP_2)
1948   defx  "RLC"
1949   defb  0
1950   defx  "RRC"
1951   defb  8
1952   defx  "RL"
1953   defb  #10
1954   defx  "RR"
1955   defb  #18
1956   defx  "SLA"
1957   defb  #20
1958   defx  "SRA"
1959   defb  #28
1960   defx  "SRL"
1961   defb  #38
1963   ; GROUP 4: push,pop,ex (sp),rr (OPC_COUNT_GROUP_4)
1964   defx  "POP"
1965   defb  #C1
1966   defx  "PUSH"
1967   defb  #C5
1968   defm  "EX",0
1969     defx  "(SP"
1970   defb  #E3
1972   ; GROUP 5: ALU with accumulator (OPC_COUNT_GROUP_5)
1973   defx  "SUB"
1974   defb  #90
1975   defx  "AND"
1976   defb  #A0
1977   defx  "XOR"
1978   defb  #A8
1979   defx  "OR"
1980   defb  #B0
1981   defx  "CP"
1982   defb  #B8
1983   ;k8: for some reason i cannot remove those two
1984   ;defb  TAND
1985   ;defb  #A0
1986   ;defb  TOR
1987   ;defb  #B0
1989   ; GROUP 6: ALU with accumulator or HL (OPC_COUNT_GROUP_6)
1990   defx  "ADD"
1991   defb  #80
1992   defx  "ADC"
1993   defb  #88
1994   defx  "SBC"
1995   defb  #98
1997   ; GROUP 7: inc,dec (2, OPC_COUNT_GROUP_7)
1998   defx  "INC"
1999   defb  4
2000   defx  "DEC"
2001   defb  5
2003   ; GROUP 8: in (1, OPC_COUNT_GROUP_8)
2004   defx  "IN"
2005   defb  #40
2006   ; GROUP 9: out (1, OPC_COUNT_GROUP_9)
2007   defx  "OUT"
2008   defb  #41
2010   ; GROUP 10: jr,djnz (2, OPC_COUNT_GROUP_10)
2011   defx  "JR"
2012   defb  #20
2013   defx  "DJNZ"
2014   defb  #10
2016   ; GROUP 11: jp (strictly one)
2017   defx  "JP"
2018   defb  #C2
2020   ; GROUP 12: call (strictly one)
2021   defx  "CALL"
2022   defb  #C4
2024   ; GROUP 13: rst (strictly one)
2025   defx  "RST"
2026   defb  #C7
2028   ; GROUP 14: ret (strictly one)
2029   defx  "RET"
2030   defb  #C0
2032   ; GROUP 15: ld (strictly one)
2033   defx  "LD"
2034   defb  #40
2036   ; miscellaneous assembler instructions
2037   ; WARNING! the order matters!
2038   defx  "DEFB"
2039   defb  0
2040   ;
2041   defx  "DEFW"
2042   defb  0
2043   ;
2044   defx  "DEFM"
2045   defb  0
2047   IF @BZ80ASM_ORGENTDISP
2048   defx  "ORG"
2049   defb  0
2050   ;
2051   defx  "DISP"
2052   defb  0
2053   ;
2054   defx  "ENT"
2055   defb  0
2056   ENDIF
2057   ; softinclude user instructions
2058   include "?bzasm80_user_mnemonics.zas"
2060   ; no more
2061   defb  0
2063 ;; operands
2064 OPRNDS:
2065   defx  "B"
2066   defb  0
2067   defx  "C"
2068   defb  1
2069   defx  "D"
2070   defb  2
2071   defx  "E"
2072   defb  3
2073   defx  "H"
2074   defb  4
2075   defx  "L"
2076   defb  5
2077   defx  "(HL"
2078   defb  6
2079   defx  "A"
2080   defb  7
2081   defx  "(IX"
2082   defb  #86
2083   defx  "(IY"
2084   defb  #C6
2085   ;
2086   defx  "BC"
2087   defb  8
2088   defx  "DE"
2089   defb  10
2090   defx  "HL"
2091   defb  12
2092   defx  "IX"
2093   defb  #8C
2094   defx  "IY"
2095   defb  #CC
2096   defx  "AF"
2097   defb  14
2098   defx  "SP"
2099   defb  14
2100   ;
2101   defx  "NZ"
2102   defb  16
2103   defx  "Z"
2104   defb  17
2105   defx  "NC"
2106   defb  18
2107   defx  "PO"
2108   defb  20
2109   defx  "PE"
2110   defb  21
2111   defx  "P"
2112   defb  22
2113   defx  "M"
2114   defb  23
2115   ;
2116   defx  "(C"
2117   defb  #20
2118   ;
2119   defb  0
2122 LDOPS:
2123   defm  "I"
2124   defb  0
2125   defx  "A"
2126   defb  #47
2127   defm  "R"
2128   defb  0
2129   defx  "A"
2130   defb  #4F
2131   defm  "A"
2132   defb  0
2133   defx  "I"
2134   defb  #57
2135   defm  "A"
2136   defb  0
2137   defx  "R"
2138   defb  #5F
2139   defm  "(BC"
2140   defb  0
2141   defx  "A"
2142   defb  2
2143   defm  "(DE"
2144   defb  0
2145   defx  "A"
2146   defb  #12
2147   defm  "A"
2148   defb  0
2149   defx  "(BC"
2150   defb  #0A
2151   defm  "A"
2152   defb  0
2153   defx  "(DE"
2154   defb  #1A
2155   ;
2156   defb  0
2158 csizest = $-csizest
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
2165 csizest = -1
2166 asmsizest = -1
2168   ENDMODULE BZ80ASM