implemented "division by zero" check in asm; still, it is 7 bytes smaller! ;-)
[bz80asm.git] / bzasm80.zas
blobd1e08d50d4349dcb8425935d5b8e64b8e2f7c154
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 ; define this to support things like "LD BC,HL"
32 IF !defined(BZ80ASM_EXTLD)
33 BZ80ASM_EXTLD equ 1
34 ENDIF
36 ; define this to support things like "PUSH r16,r16..."
37 IF !defined(BZ80ASM_EXTPUSHPOP)
38 BZ80ASM_EXTPUSHPOP equ 1
39 ENDIF
41   MODULE BZ80ASM
43 PC: defw #C000  ; logical program counter for the code
45 IF @BZ80ASM_ORGENTDISP
46 ; ORG/DISP sets this to its value
47 NEW_ORGENT: defw 0
48 ; ORG/ENT sets this to the corresponding type
49 ; it is reset at each call to ASSEM
50 ORGENT_NONE equ 0
51 ORGENT_ORG equ 1
52 ORGENT_DISP equ 2
53 ORGENT_ENT equ 3
54 ORGENT_TYPE: defw ORGENT_NONE
55 ENDIF
57 ; address of the routine that will be called if JR/DJNZ destination is too far away
58 ; you can simply RET from it if you want to ignore this error
59 ; note that stack is filled with garbage, so use `ASM_ERROR_EXIT` to bail out
60 ; this is not checked for zero, so if you will not init this, "CALL 0" will be executed
61 ; ret L to the byte that will be put on normal return
62 ASM_JR_TOO_FAR_CB: defw 0
64 ; on entry, ASSEM will store SP here
65 ; you can use this to reset the stack, and use RET to return to the caller
66 ; or simply use ASM_ERROR_EXIT (which will set carry too)
67 ASM_SP0: defw 0
69 ; ASSEM sets this if it hits mnemonics without a handler
70 ; you can add your own mnemonics to the assembler, and it
71 ; will set this to non-zero if it hits them
72 ; use this byte as decrementing counter, like this:
73 ;  jr    nc,no_error
74 ;  ld    a,(ASM_BAD_B)
75 ;  or    a
76 ;  jr    z,syntax_error
77 ;  ld    b,a
78 ;  djnz  not_first_one
79 ;    handle first instruction
80 ;  not_first_one:
81 ;  djnz  not_second_one
82 ;    handle second instruction
83 ;  ...and so on
84 ; ADDITIONALLY, this will be set to #FF on invalid token
85 ; this can be used to process labels (because otherwise
86 ; there is no way to tell if we got some bad operands, or
87 ; an unknown mnemonics)
88 ASM_BAD_B: defb 0
89 ; this is set to non-zero if compiled instruction was
90 ; some data definition like DEFB, and not some real one
91 ; actually, it is set for any "pseudoinstruction", i.e.
92 ; for data defs, ORG/DISP, and user-defined mnemonics, if
93 ; there are any
94 ASM_WAS_PSEUDO: defb 0
97 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
98 ;; expression parser variables
100 ;; set this to the address of error routine
101 ;; note that you cannot return from it, you HAVE to abort everything
102 ;; also note that machine stack is undefined, and SP should be set
103 ;; to some initial value
104 ;; "undefined" means that machine stack can contain alot of garbage,
105 ;; but will never be underflowed
107 ;; this function is called with error code in A
109 ;; you can load your own error code in A, and do:
110 ;;  jp  BZ80ASM.PARSE_EXPR_ERROR_A
111 EXPR_ERROR_CB: defw 0
113 ;; error codes
114 ;; expected number, but got something incomprehensible
115 EXPR_ERR_NUMBER_EXPECTED equ 1
116 ;; expected string, but got something incomprehensible
117 EXPR_ERR_STRING_EXPECTED equ 2
118 ;; expected ")", but got something strange
119 EXPR_ERR_RPAREN_EXPECTED equ 3
120 ;; expected ")", but got something strange
121 EXPR_ERR_DIVISION_BY_ZERO equ 4
123 ;; the asm never generates the following errors, but
124 ;; they may be useful for a main driver
126 ;; you can use this to dispatch "short jump destination is too far" error
127 ;; just do this in `ASM_JR_TOO_FAR_CB`:
128 ;;   ld  a,EXPR_ERR_JR_TOO_FAR
129 ;;   jp  BZ80ASM.PARSE_EXPR_ERROR_A
130 EXPR_ERR_JR_TOO_FAR equ 5
132 ;; this "unresolved label" error can be used by label manager
133 EXPR_ERR_UNRESOLVED_LABEL equ 6
134 ;; this error can be used by label manager if it cannot parse a label
135 EXPR_ERR_INVALID_LABEL_NAME equ 7
136 ;; this error can be used by label manager
137 EXPR_ERR_DUPLICATE_LABEL equ 8
139 ;; general "bad syntax" error, when you don't have anything better
140 EXPR_ERR_BAD_SYNTAX equ 9
142 ;; offset your own error codes with this
143 EXPR_ERR_USERDEF equ 10
146 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
147 ;; label manager callback
149 ;; find a label
150 ;; this is called from assembler to find an existing label.
151 ;; IY points to the first char of a label name.
152 ;; after parsing, IY should point right after the parsed name.
153 ;; all registers expect IY can be trashed (including IX).
155 ;; for forward referencing, label manager can create unknown
156 ;; labels, and set their value to (BZ80ASM.PC).
157 ;; it is important to set new labels to PC to avoid errors
158 ;; with short jumps if your "jr too far" error handler always
159 ;; bombs out. otherwise, you can set the label to anything
160 ;; you like.
162 ;; IN:
163 ;;   IY: text input buffer
164 ;; OUT:
165 ;;   IY: text input buffer after the label
166 ;;   HL: label value
167 ;;   CARRY FLAG: set if label wasn't found
168 ;;               expression parser will bomb out in this case
169 ;; on error, IY doesn't matter, because it will be restored
170 ;; by the calling code
171 GETLABEL_CB: defw 0
174 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
175 ; string terminator
176 CR equ 13
179 asmsizest = $
180 csizest = $
182 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
183 ;; ASSEMBLER:
184 ;; LANGUAGE-INDEPENDENT CONTROL SECTION:
186 ;;  Inputs:
187 ;;    IY: text buffer
188 ;;    IX: destination to put generated code at
189 ;;  Outputs:
190 ;;    A: terminator
191 ;;    IY: terminator position in text buffer
192 ;;    IX: points right after the generated code
193 ;;    DE: length of the generated code
194 ;;    carry set if syntax error
195 ;;    in case of error, IY will be somewhere around the error position
196 ;;    all other registers are undefined
197 ;;    others are dead (including extra register set and extra accum/flags)
199 ;; input buffer must be terminated with ':', ';' or CR (13).
200 ;; unterminated input buffer will wreck havoc.
201 ;; the assembler will not modify the input buffer, but note that it
202 ;; will skip all leading delimiters.
204 ;; on exit, all trailing delimiters will be skipped too.
206 ;; also, the assembler doens't try to parse and create new labels, you
207 ;; should do it by yourself. it can be done like this:
209 ;;   call  BZ80ASM.ASSEM
210 ;;   jp    nc,assembled_ok
211 ;;   here we can try to parse a label, and then try to assemble
212 ;;   the rest of the line again
214 ASSEM:
215   ; reset org/ent type
216   xor   a
217   IF @BZ80ASM_ORGENTDISP
218   ld    (ORGENT_TYPE),a
219   ENDIF
220   ld    (ASM_BAD_B),a
221   ld    (ASM_WAS_PSEUDO),a
222   ld    (ASM_SP0),sp
223 ASSEM1:
224   call  SKIP
225   inc   iy
226   cp    ':'
227   jr    z,ASSEM1
228   cp    CR
229   ret   z
230   cp    ';'
231   ret   z
232   dec   iy
233   push  ix
234   call  ASMB
235   pop   de
236   ; exit if syntax error
237   ret   c
238   ; skip delimiters (but not terminators)
239   call  SKIP
240   ; exit with error flag if we're not at a terminator
241   scf
242   ret   nz
243   ; advance PC
244   push  ix
245   pop   hl
246   or    a
247   sbc   hl,de
248   ex    de,hl           ;DE= NO. OF BYTES
249   ld    hl,(PC)
250   add   hl,de
251   ld    (PC),hl         ;UPDATE PC
252   ; reset carry and Z
253   xor   a
254   ret
256 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
258 ;; jump here to restore stack, and exit with error (carry set)
260 ASM_ERROR_EXIT:
261   ld    sp,(ASM_SP0)
262   scf
263   ret
265 ASMB_BAD_MNEMO:
266   ld    a,255
267   ld    (ASM_BAD_B),a
268   ; carry is still set
269   ret
271 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
273 ;; PROCESSOR-SPECIFIC TRANSLATION SECTION:
275 ;; REGISTER USAGE: B - TYPE OF MOST RECENT OPERAND
276 ;;                 C - OPCODE BEING BUILT
277 ;;                 D - (IX) OR (IY) FLAG
278 ;;                 E - OFFSET FROM IX OR IY
279 ;;                HL - NUMERIC OPERAND VALUE
280 ;;                IX - CODE DESTINATION
281 ;;                IY - SOURCE TEXT POINTER
282 ;;    Inputs: A = initial character
283 ;;   Outputs: Carry set if syntax error.
285 ASMB:
286   call  SKIP
287   ret   z
288   ld    hl,OPCODS
289   call  FIND
290   ; carry flag set on error
291   jr    c,ASMB_BAD_MNEMO
292   ; A contains token index
293   ; B contains token data byte
294   ld    c,b     ;ROOT OPCODE
295   ld    d,0     ;CLEAR IX/IY FLAG
297   ; now:
298   ; A contains token index
299   ; C contains token data byte
300   ; D contains IX/IY flag
302 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
304 ;; GROUP 0 - TRIVIAL CASES REQUIRING NO COMPUTATION
305 ;; GROUP 1 - AS GROUP 0 BUT WITH "ED" PREFIX
307   sub   OPC_COUNT_GROUPS_0_AND_1
308   jr    nc,GROUP2
309   cp    OPC_COUNT_GROUP_0-OPC_COUNT_GROUPS_0_AND_1
310   call  nc,EDX
311   jr    BYTE0
313 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
315 ;; GROUP 2 - BIT, RES, SET
316 ;; GROUP 3 - RLC, RRC, RL, RR, SLA, SRA, SRL
318 GROUP2:
319   sub   OPC_COUNT_GROUPS_2_AND_3
320   jr    nc,GROUP4
321   cp    OPC_COUNT_GROUP_2-OPC_COUNT_GROUPS_2_AND_3
322   call  c,CBIT
323   ret   c
324   call  REGLO
325   ret   c
326   call  CBX
327   jr    BYTE0
329 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
331 ;; GROUP 4 - PUSH, POP, EX (SP)
333 GROUP4:
334   sub   OPC_COUNT_GROUP_4
335   jr    nc,GROUP5
337   $IF @BZ80ASM_EXTPUSHPOP
338   cp    #FF
339   jr    nz,G4_PP
340   $ENDIF
341   call  PAIR
342   ret   c
343   jr    BYTE0
344   $IF @BZ80ASM_EXTPUSHPOP
345 G4_PP_LOOP:
346   pop   bc
347   pop   af
348 G4_PP:
349   push  af
350   push  bc
351   ; i need this code to be one byte less, so...
352   call  G4GRP
353   jr    c,G4_PP_DONE
354   ;call  PAIR
355   ;jr    c,G4_PP_DONE
356   ;call  BYTE0
357   ;call  SKIP
358   jr    nz,G4_PP_LOOP
359 G4_PP_DONE:
360   pop   bc
361   pop   de
362   ret
363   $ENDIF
365 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
367 ;; GROUP 5 - SUB, AND, XOR, OR, CP
368 ;; GROUP 6 - ADD, ADC, SBC
370 GROUP5:
371   sub   OPC_COUNT_GROUPS_5_AND_6
372   jr    nc,GROUP7
373   cp    OPC_COUNT_GROUP_5-OPC_COUNT_GROUPS_5_AND_6
374   ld    b,7
375   call  nc,OPND
376   ld    a,b
377   cp    7
378   jr    nz,G6HL
380   call  REGLO
381   ld    a,c
382   jr    nc,BIND1
383   xor   46H
384   call  BIND
385 DBX:
386   call  NUMBER
387   jr    VAL8
389 G6HL:
390   and   3FH
391   cp    12
392   scf
393   ret   nz
394   ld    a,c
395   cp    80H
396   ld    c,9
397   jr    z,G4
398   xor   1CH
399   rrca
400   ld    c,a
401   call  EDX
402   jr    G4
404 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
406 ;; GROUP 7 - INC, DEC
408 GROUP7:
409   sub   OPC_COUNT_GROUP_7
410   jr    nc,GROUP8
411   call  REGHI
412   ld    a,c
413 BIND1:
414   jp    nc,BIND
415   xor   64H
416   rlca
417   rlca
418   rlca
419   ld    c,a
420   call  PAIR1
421   ret   c
422 BYTE0:
423   ld    a,c
424   jr    BYTE2
426 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
428 ;; GROUP 8 - IN
429 ;; GROUP 9 - OUT
431 GROUP8:
432   sub   OPC_COUNT_GROUPS_8_AND_9
433   jr    nc,GROUPA
434   cp    OPC_COUNT_GROUP_8-OPC_COUNT_GROUPS_8_AND_9
435   call  z,CORN
436   ex    af,af'
437   call  REGHI
438   ret   c
439   ex    af,af'
440   call  c,CORN
441   inc   h
442   jr    z,BYTE0
443   ld    a,b
444   cp    7
445   scf
446   ret   nz
447   ld    a,c
448   xor   3
449   rlca
450   rlca
451   rlca
452   call  BYTE
453   jr    VAL8
455 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
457 ;; GROUP 10 - JR, DJNZ
459 GROUPA:
460   sub   OPC_COUNT_GROUP_10
461   jr    nc,GROUPB
462   cp    OPC_COUNT_GROUP_10_JRS-OPC_COUNT_GROUP_10
463   call  nz,COND
464   ld    a,c
465   jr    nc,GRPA
466   ld    a,18H
467 GRPA:
468   call  BYTE
469   call  NUMBER
470   ld    de,(PC)
471   inc   de
472   scf
473   sbc   hl,de
474   ld    a,l
475   rla
476   sbc   a,a
477   cp    h
478 TOOFAR:
479   call  nz,JR_TOO_FAR
480 VAL8:
481   ld    a,l
482   jr    BYTE2
484 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
486 ;; GROUP 11 - JP
488 GROUPB:
489   ld    b,a
490   jr    nz,GROUPC
491   call  COND
492   ld    a,c
493   jr    nc,GRPB
494   ld    a,b
495   and   3FH
496   cp    6
497   ld    a,0E9H
498   jr    z,BYTE2
499   ld    a,0C3H
500 GRPB:
501   call  BYTE
502   jr    ADDR
504 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
506 ;; GROUP 12 - CALL
508 GROUPC:
509   djnz  GROUPD
510 GRPC:
511   call  GRPE
512 ADDR:
513   call  NUMBER
514 VAL16:
515   call  VAL8
516   ld    a,h
517   jr    BYTE2
519 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
521 ;; GROUP 13 - RST
523 GROUPD:
524   djnz  GROUPE
525   call  NUMBER
526   and   c
527   or    h
528   jr    nz,TOOFAR
529   ld    a,l
530   or    c
531 BYTE2:
532   jr    BYTE1
534 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
536 ;; GROUP 14 - RET
538 GROUPE:
539   djnz  GROUPF
540 GRPE:
541   call  COND
542   ld    a,c
543   jr    nc,BYTE1
544   or    9
545   jr    BYTE1
547 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
549 ;; GROUP 15 - LD
551 GROUPF:
552   djnz  MISC
553   call  LDOP
554   jr    nc,LDA
555   call  REGHI
556   ex    af,af'
557   call  SKIP
558   cp    '('
559   jr    z,LDIN
560   ex    af,af'
561   jp    nc,G6
562   ld    c,1
563   call  PAIR1
564   ret   c
565   ld    a,14
566   cp    b
567   ld    b,a
568   call  z,PAIR
569   ld    a,b
570   and   3FH
571   cp    12
572   ld    a,c
573   jr    nz,GRPB
574   ld    a,0F9H
575   jr    BYTE1
576   ;
577 LDIN:
578   ex    af,af'
579   push  bc
580   call  nc,REGLO
581   ld    a,c
582   pop   bc
583   jr    nc,BIND
584   ld    c,0AH
585   call  PAIR1
586   call  LD16
587   jr    nc,GRPB
588   call  NUMBER
589   ld    c,2
590   call  PAIR
591   call  LD16
592   ret   c
593   call  BYTE
594   jr    VAL16
596 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
597 ;; misc. instructions
598 MISC:
599   ld    a,1
600   ld    (ASM_WAS_PSEUDO),a
601   jp    MISC_DEFB
604 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
606 ;; SUBROUTINES
609 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
611 JR_TOO_FAR:
612   ld    hl,(ASM_JR_TOO_FAR_CB)
613   jp    (hl)
615 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
617 LDA:
618   $IF @BZ80ASM_EXTLD
619   bit   7,b
620   jr    nz,LDA_R16
621   $ENDIF
622   cp    4
623   call  c,EDX
624   ld    a,b
625 BYTE1:
626   jr    BYTE
627   $IF @BZ80ASM_EXTLD
628 LDA_R16:
629   ld    a,b
630   and   #7F
631   call  BYTE
632   add   a,9
633   jr    BYTE
634   $ENDIF
636 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
638 LD16:
639   ld    a,b
640   jr    c,LD8
641   ld    a,b
642   and   3FH
643   cp    12
644   ld    a,c
645   ret   z
646   call  EDX
647   ld    a,c
648   or    43H
649   ret
651 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
653 LD8:
654   cp    7
655   scf
656   ret   nz
657   ld    a,c
658   or    30H
659   ret
661 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
663 CORN:
664   push  bc
665   call  OPND
666   bit   5,b
667   pop   bc
668   jr    z,NUMBER
669   ld    h,-1
670 EDX:
671   ld    a,0EDH
672   jr    BYTE
674 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
676 CBX:
677   ld    a,0CBH
678 BIND:
679   cp    76H
680   scf
681   ret   z               ;REJECT LD (HL),(HL)
682   call  BYTE
683   inc   d
684   ret   p
685   ld    a,e
686   jr    BYTE
688 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
690 OPND:
691   push  hl
692   ld    hl,OPRNDS
693   call  FIND
694   pop   hl
695   ret   c
696   bit   7,b
697   ret   z
698   bit   3,b
699   push  hl
700   call  z,OFFSET
701   ld    e,l
702   pop   hl
703   ld    a,0DDH
704   bit   6,b
705   jr    z,OP1
706   ld    a,0FDH
707 OP1:
708   or    a
709   inc   d
710   ld    d,a
711   ret   m
712 BYTE:
713   ld    (ix),a
714   inc   ix
715   or    a
716   ret
718 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
720 OFFSET:
721   ld    a,(iy)
722   cp    ')'
723   ld    hl,0
724   ret   z
726 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
727 ;; parse numeric expression
728 NUMBER:
729   call  SKIP
730   push  bc
731   push  de
732   push  ix
733   call  PARSE_INT_EXPR
734   ; HL: expression value
735   pop   ix
736   pop   de
737   pop   bc
738   ld    a,l
739   or    a
740   ret
742 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
744 REG:
745   call  OPND
746   ret   c
747   ld    a,b
748   and   3FH
749   cp    8
750   ccf
751   ret
753 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
755 REGLO:
756   call  REG
757   ret   c
758   jr    ORC
760 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
762 REGHI:
763   call  REG
764   ret   c
765   jr    SHL3
767 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
769 COND:
770   call  OPND
771   ret   c
772   ld    a,b
773   and   1FH
774   sub   16
775   jr    nc,SHL3
776   cp    -15
777   scf
778   ret   nz
779   ld    a,3
780   jr    SHL3
782 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
784 PAIR:
785   call  OPND
786   ret   c
787 PAIR1:
788   ld    a,b
789   and   0FH
790   sub   8
791   ret   c
792   jr    SHL3
794 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
796 CBIT:
797   call  NUMBER
798   cp    8
799   ccf
800   ret   c
801 SHL3:
802   rlca
803   rlca
804   rlca
805 ORC:
806   or    c
807   ld    c,a
808   ret
810 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
812 G4GRP:
813   call  PAIR
814   ret   c
815   call  BYTE0
816   jp    SKIP
818 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
820 ;; common defb/defw code
821 ;;   carry set if we want words
823 COM_DEFBW:
824   ; note that we don't care about A, we only need flags here
825   push  af      ; save flags
826   push  ix
827   call  PARSE_INT_EXPR
828   pop   ix
829   ; HL: value
830   ld    (ix),l
831   inc   ix
832   pop   af      ; restore flags
833   jr    nc,COM_DEFBW_BYTE
834   ld    (ix),h
835   inc   ix
836 COM_DEFBW_BYTE:
837   ex    af,af'  ; save flags
838   ; check for comma
839   call  SKIP
840   ret   z
841   ex    af,af'  ; restore flags
842   jr    COM_DEFBW
844 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
846 ;; DEFB
848 MISC_DEFB:
849   djnz  MISC_DEFW
850 DEFB_LOOP:
851   or    a
852   jr    COM_DEFBW
854 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
856 ;; DEFW
858 MISC_DEFW:
859   djnz  MISC_DEFM
860 DEFW_LOOP:
861   scf
862   jr    COM_DEFBW
864 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
866 ;; DEFM
868 MISC_DEFM:
869   djnz  MISC_ORG
870 DEFM_LOOP:
871   push  ix
872   call  PARSE_STR_EXPR
873   pop   ix
874   ; HL: buffer address
875   ;  E: buffer length
876   xor   a
877   cp    e
878   jr    z,DEFM1_DONE_ONE
879 DEFM1:
880   ld    a,(hl)
881   inc   hl
882   call  BYTE
883   dec   e
884   jr    nz,DEFM1
885 DEFM1_DONE_ONE:
886   ; check for comma
887   call  SKIP
888   ret   z
889   jr    DEFM_LOOP
891 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
893 ;; ORG
895 MISC_ORG:
896   IF @BZ80ASM_ORGENTDISP
897   djnz  MISC_DISP
898   ld    a,ORGENT_ORG
899 COM_ORGENT:
900   ld    (ORGENT_TYPE),a
901   call  NUMBER
902   ld    (NEW_ORGENT),hl
903   or    a
904   ret
906 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
908 ;; DISP
910 MISC_DISP:
911   djnz  MISC_ENT
912   ld    a,ORGENT_DISP
913   jr    COM_ORGENT
915 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
917 ;; ENT
919 MISC_ENT:
920   djnz  MISC_WTF
921   ld    a,ORGENT_ENT
922   jr    COM_ORGENT
924 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
926 ;; error (the thing that should not be)
928 MISC_WTF:
929   ENDIF
930   ; set "unknown instruction index"
931   ; b may be zero here
932   ld    a,b
933   inc   a
934   ld    (ASM_BAD_B),a
935   scf
936   ret
939 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
940 ;; search the table for a token from the input buffer
941 ;; skips leading delimiters, and the token itself (if found)
942 ;; tokens in the table must have bit 7 set on the last char
943 ;; table ends with zero byte instead of first token char
944 ;; each token is followed by a data byte
945 ;; IN:
946 ;;   IY: text buffer
947 ;; OUT:
948 ;;   IY: new position in the text buffer
949 ;;    A: token number
950 ;;    B: token data byte
951 ;;   carry flag set on error (invalid char, or token not found)
952 ;;   on error, delimiters are still skipped
953 ;;   HL: dead
955 LDOP:
956   ld    hl,LDOPS
957 ; main table search entry point
958 FIND:
959   call  SKIP
960 EXIT:
961   ld    b,0
962   scf
963   ret   z
964   ; reject chars with high bit set
965   cp    128
966   ccf
967   ret   c
969 FIND0:
970   ; check the first char
971   ld    a,(hl)
972   or    a
973   jr    z,EXIT
974   xor   (iy)
975   and   %01011111   ; for case-insensitivity
976   jr    z,FIND2
977   ; first char is not matched, skip this token
978 FIND1:
979   bit   7,(hl)
980   inc   hl
981   jr    z,FIND1
982   ; skip token data byte
983   inc   hl
984   ; increment token counter
985   inc   b
986   jr    FIND0
988   ; first char matched, check the rest
989   ; A holds zero (due to xor/and)
990 FIND2:
991   push  iy
992 FIND3:
993   ; last token char?
994   bit   7,(hl)
995   inc   iy
996   inc   hl
997   jr    nz,FIND5
998   ; not the last token char
999   ; this compares (HL) with 0, because
1000   ; A is guaranteed to hold zero here
1001   cp    (hl)
1002   ; zero byte in token means "skip delimiters"
1003   ; it is used in some opcodes with fixed operands
1004   call  z,SKIP0
1005   ; load token char
1006   ld    a,(hl)
1007   ; compare with input char
1008   xor   (iy)
1009   and   %01011111   ; for case-insensitivity
1010   jr    z,FIND3
1012   ; alas, doesn't match
1013 FIND4:
1014   ; restore input stream pointer
1015   pop   iy
1016   ; and skip this token
1017   jr    FIND1
1019   ; we'll come here if we succesfully matched a token
1020 FIND5:
1021   ; if it isn't followed by a delimiter, '+' or '-', this is not a valid token
1022   call  DELIM
1023   call  nz,SIGN
1024   jr    nz,FIND4
1026   ; this token is valid
1027 FIND6:
1028   ; move token index to A
1029   ld    a,b
1030   ; load B with token data byte
1031   ld    b,(hl)
1032   ; drop original input stream position
1033   pop   hl
1034   ; we're done here
1035   ; note that carry flag is guaranteed to be reset
1036   ret
1039 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1040 ;; used by FIND
1041 SIGN:
1042   cp    '+'
1043   ret   z
1044   cp    '-'
1045   ret
1047 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1048 ;; this entry point is used by FIND
1049 SKIP0:
1050   inc   hl
1051 ;; this entry point is used to skip blanks and delimiters
1052 ;; note that comma and right paren are considered blanks too
1053 ;; as a consequence, operands may be delimited by spaces, or
1054 ;; right parens, lol
1055 ;; returns current char in A (and IY pointing to it)
1056 ;; zero flag set if we hit a terminator
1057 ;; zero flag reset if we hit a non-delimiter
1058 SKIP:
1059   ; delimiter or terminator?
1060   call  DELIM
1061   ret   nz
1062   ; if this is terminator, still stop
1063   call  TERM
1064   ret   z
1065   ; this is delimiter, skip it
1066   inc   iy
1067   jr    SKIP
1069 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1070 ;; used by FIND and SKIP
1071 ;; check if the current char is a delimiter or a terminator
1072 ;; zero flag set on delimiter/terminator
1073 DELIM:
1074   ld    a,(iy)          ;ASSEMBLER DELIMITER
1075   cp    ' '
1076   ret   z
1077   cp    ','
1078   ret   z
1079   cp    ')'
1080   ret   z
1082 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1083 ;; entry point used by SKIP
1084 TERM:
1085   cp    ';'             ;ASSEMBLER TERMINATOR
1086   ret   z
1088 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1089 ;; also used by assembler to check for command separator
1090 ;; the assembler itself does nothing with separators
1091 ASM_IS_SEP:
1092   cp    ':'             ;ASSEMBLER SEPARATOR
1093   ret   nc
1094   cp    CR
1095   ret
1097 csizest = $-csizest
1098 $printf "assembler size: %d", csizest
1101 csizest = $
1103 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1104 ;; parse integer number (without sign)
1105 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1107 ;; this is advanced number parser
1108 ;; it understands alot of suffixes and prefixes:
1109 ;;   $     -- lone "$" means "current PC"
1110 ;;   #nnnn -- hex
1111 ;;   $nnnn -- hex
1112 ;;   &nnnn -- hex
1113 ;;   %nnnn -- binary
1114 ;;   0Xnnn -- hex
1115 ;;   0Onnn -- octal
1116 ;;   0Bnnn -- binary
1117 ;;   nnnnH -- hex
1118 ;;   nnnnB -- binary
1119 ;;   nnnnO -- octal
1120 ;; everything is case-insensitive
1121 ;; you can separate digits with underscores
1122 ;; (i.e. "12_34_5" will work, underscores are simply ignored)
1125 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1127 ;; parse a number, push it onto the stack
1128 ;; understands prefixes and suffixes
1130 ;; IN:
1131 ;;   IY: text buffer
1132 ;; OUT:
1133 ;;   IY: text buffer after the expression
1134 ;;   HL: number
1135 ;;   carry flag reset
1136 ;;  OR: (cannot parse as a number)
1137 ;;   IY: unchanged
1138 ;;   carry flag set
1139 ;;  DE,AF,flags: dead
1141 PARSE_NUMBER:
1142   call  PARSER_SKIP_BLANKS
1143   scf
1144   ret   z
1145   push  iy          ; we will need to rollback on error
1146   ; A already contains a char, loaded by `PARSER_SKIP_BLANKS`
1147   cp    '#'
1148   jr    z,.hexprefix
1149   cp    '$'
1150   jr    z,.maybe_lone_dollar
1151   cp    '&'
1152   jr    z,.hexprefix
1153   cp    '%'
1154   jr    z,.binprefix
1155   ; no, leading zero doesn't mean "octal", this is stupid
1156   ; but we may have prefixes like "0x" and such
1157   cp    '0'
1158   jr    z,.maybe_zero_prefix
1159   ; check if we have a digit here
1160   call  PARSER_CONV_DIGIT
1161   jr    c,.not_a_number_carry_set
1162   cp    10
1163   ; nope, do not allow it, all numbers must start with a digit
1164   ;jr    nc,.must_be_hex_with_sfx
1165   ccf
1166   jr    c,.not_a_number_carry_set
1167 .do_normal_decimal:
1168   ; done with prefixes, try decimal number
1169   ; we'll switch to suffix checking on hex digit
1170   ld    hl,0        ; accumulator
1171 .decimal_loop:
1172   call  .getDigit
1173   jr    c,.decimal_done
1174   cp    10
1175   jr    nc,.must_be_hex_with_sfx
1176   ; HL=HL*10
1177   add   hl,hl
1178   ld    de,hl
1179   add   hl,hl
1180   add   hl,hl
1181   add   hl,de
1182   ; HL=HL+A
1183   ld    e,a
1184   ld    d,0
1185   add   hl,de
1186   ; next char
1187   inc   iy
1188   jr    .decimal_loop
1189 .decimal_done:
1190   ; check for suffix
1191   ld    a,(iy)
1192   and   %11011111   ; cheap uppercase
1193   cp    'H'
1194   jr    z,.must_be_hex_with_sfx
1195   cp    'B'
1196   jr    z,.bin_with_sfx
1197   cp    'O'
1198   jr    z,.oct_with_sfx
1199   ; no suffix, we're done
1201 .success:
1202   pop   de          ; drop iy
1203   ; reset carry flag
1204   or    a
1205   ret
1207 .not_a_number_carry_set:
1208   pop   iy
1209   ret
1211 .hexprefix:
1212   ; skip prefix
1213   inc   iy
1214   call  .parse_as_hex
1215 .after_prefix:
1216   jr    c,.not_a_number_carry_set
1217   jr    .success
1219 .maybe_lone_dollar:
1220   ; lone dollar means "PC"
1221   inc   iy
1222   call  .parse_as_hex
1223   ; the only case we may gen an error here is
1224   ; when our dollar isn't followed by a digit
1225   jr    nc,.success
1226   ; lone dollar is good too
1227   ; IY points right after the dollar here
1228   ld    hl,(PC)
1229   jr    .success
1231 .binprefix:
1232   ; skip prefix
1233   inc   iy
1234   call  .parse_as_bin
1235   jr    .after_prefix
1237 .maybe_binprefix:
1238   ; things like "0BEEFh" should be parsed as hex
1239   ; skip prefix
1240   inc   iy
1241   call  .parse_as_bin
1242   jr    c,.must_be_hex_with_sfx
1243   ; check for 'H'
1244   ld    a,(iy)
1245   and   %11011111   ; cheap uppercase
1246   cp    'H'
1247   jr    z,.must_be_hex_with_sfx
1248   jr    .success
1250 .octprefix:
1251   ; skip prefix
1252   inc   iy
1253   call  .parse_as_oct
1254   jr    .after_prefix
1256 .maybe_zero_prefix:
1257   ; check for '0x' and such
1258   ; skip '0'
1259   inc   iy
1260   ; load and prefix
1261   ; there's no need to skip it, as it will be
1262   ; skipped by the corresponding subroutine
1263   ld    a,(iy)
1264   ; so IY will point to the actual number
1265   and   %11011111   ; cheap uppercase
1266   cp    'X'
1267   jr    z,.hexprefix
1268   cp    'B'
1269   jr    z,.maybe_binprefix
1270   cp    'O'
1271   jr    z,.octprefix
1272   ; do not reparse '0', no need to backup
1273   jr    .do_normal_decimal
1275 .must_be_hex_with_sfx:
1276   ; reparse as hex, and check for suffix
1277   pop   iy
1278   push  iy
1279   call  .parse_as_hex
1280   jr    c,.not_a_number_carry_set
1281   ld    a,(iy)
1282   inc   iy
1283   and   %11011111   ; cheap uppercase
1284   cp    'H'
1285   jr    z,.success
1287 .bin_with_sfx:
1288   ; reparse as bin, skip suffix (it is guaranteed to be there)
1289   pop   iy
1290   push  iy
1291   call  .parse_as_bin
1292 .done_guaranteed_suffix:
1293   jr    c,.not_a_number_carry_set
1294   ; skip suffix
1295   inc   iy
1296   jr    .success
1298 .oct_with_sfx:
1299   ; reparse as bin, skip suffix (it is guaranteed to be there)
1300   pop   iy
1301   push  iy
1302   call  .parse_as_bin
1303   jr    .done_guaranteed_suffix
1305 .parse_as_hex:
1306   ld    hl,0        ; accumulator
1307   ; check first digit (as this is general parser)
1308   call  .getDigitNoUnder
1309   ret   c
1310 .parse_as_hex_loop:
1311   inc   iy
1312   add   hl,hl
1313   add   hl,hl
1314   add   hl,hl
1315   add   hl,hl
1316   ld    e,a
1317   ld    d,0
1318   add   hl,de
1319   call  .getDigit
1320   jr    nc,.parse_as_hex_loop
1321   ; clear carry flag (it is always set here)
1322   ccf
1323   ret
1325 .parse_as_bin:
1326   ld    hl,0        ; accumulator
1327   ; check first digit (as this is general parser)
1328   ld    a,(iy)
1329   call  .getDigitNoUnderBin
1330   ret   c
1331 .parse_as_bin_loop:
1332   inc   iy
1333   add   hl,hl
1334   ld    e,a
1335   ld    d,0
1336   add   hl,de
1337   call  .getBinDigit
1338   jr    nc,.parse_as_bin_loop
1339   ; clear carry flag (it is always set here)
1340   ccf
1341   ret
1343 .parse_as_oct:
1344   ld    hl,0        ; accumulator
1345   ; check first digit (as this is general parser)
1346   ld    a,(iy)
1347   call  .getDigitNoUnderOct
1348 .parse_as_oct_loop:
1349   inc   iy
1350   add   hl,hl
1351   add   hl,hl
1352   add   hl,hl
1353   ld    e,a
1354   ld    d,0
1355   add   hl,de
1356   call  .getOctDigit
1357   jr    nc,.parse_as_oct_loop
1358   ; clear carry flag (it is always set here)
1359   ccf
1360   ret
1363 .getDigit_inc:
1364   inc   iy
1365 .getDigit:
1366   ld    a,(iy)
1367   cp    '_'
1368   jr    z,.getDigit_inc
1369   jr    PARSER_CONV_DIGIT
1371   ; d: base
1372 .getDigitInBase:
1373   call  .getDigit
1374   ret   c
1375   cp    d
1376   ccf
1377   ret
1379 .getDigitNoUnder:
1380   ld    a,(iy)
1381   jr    PARSER_CONV_DIGIT
1383 .getDecDigit:
1384   ld    d,10
1385   jr    .getDigitInBase
1387 .getDigitNoUnderOct:
1388   ld    a,(iy)
1389 .getOctDigit:
1390   ld    d,8
1391   jr    .getDigitInBase
1393 .getDigitNoUnderBin:
1394   ld    a,(iy)
1395 .getBinDigit:
1396   ld    d,2
1397   jr    .getDigitInBase
1400 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1401 ;; converts 'A' to digit (assume hex)
1402 ;; carry set: not a digit char (and A is destroyed)
1404 PARSER_CONV_DIGIT:
1405   sub   '0'
1406   ret   c
1407   cp    10
1408   ccf
1409   ret   nc
1410   add   a,'0'
1411   and   %11011111   ; cheap uppercase
1412   sub   'A'-10
1413   ret   c
1414   cp    16
1415   ccf
1416   ret
1418 csizest = $-csizest
1419 $printf "assembler numparse size: %d", csizest
1422 csizest = $
1424 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1425 ;; math expression parser
1426 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1428 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1430 ;; HL=BC*DE
1432 ;; BC,DE,A,flags: dead
1434 PARSER_UMUL_BC_DE:
1435   ; DEHL=BC*DE
1436   ld    hl,0
1437   ld    a,16
1438 .loop:
1439   add   hl,hl
1440   rl    e
1441   rl    d
1442   jr    nc,.skip
1443   add   hl,bc
1444   jr    nc,.skip
1445   inc   de
1446 .skip:
1447   dec   a
1448   jr    nz,.loop
1449   ret
1452 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1454 ;; performs BC/DE
1455 ;; OUT:
1456 ;;   BC: quotient
1457 ;;   HL: remainder
1459 ;; DE,A,flags: dead
1461 PARSER_UDIV_BC_DE:
1462   ld    hl,0
1463   ld    a,16
1464 .loop:
1465   sll   c
1466   rl    b
1467   adc   hl,hl
1468   sbc   hl,de
1469   jr    nc,.skip
1470   add   hl,de
1471   dec   c
1472 .skip:
1473   dec   a
1474   jr    nz,.loop
1475   ret
1478 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1479 ;; operator table, to avoid pasta
1480 ;; bit 0 of operator name means "two equal chars"
1481 ;; name,priority,handler
1482 ;; priority 1 is term/unary
1483 ;; priority 0 is nothing, and it should stay nothing
1484 expr_optable:
1485   db 0+'*'*2,2
1486   dw expr_do_mul
1487   db 0+'/'*2,2
1488   dw expr_do_div
1489   db 0+'%'*2,2
1490   dw expr_do_mod
1492   db 0+'+'*2,3
1493   dw expr_do_add
1494   db 0+'-'*2,3
1495   dw expr_do_sub
1497   db 1+'<'*2,4
1498   dw expr_do_shl
1499   db 1+'>'*2,4
1500   dw expr_do_shr
1502   db 0+'&'*2,5
1503   dw expr_do_bitand
1505   db 0+'^'*2,6
1506   dw expr_do_bitxor
1508   db 0+'|'*2,7
1509   dw expr_do_bitor
1511   db 0  ; no more
1513 expr_max_priority equ 7
1515 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1516 ;; operator doers
1517 ;; HL is op0, DE is op1
1518 ;; result in HL
1519 ;; preserve IY
1521 expr_do_mul:
1522   ld    bc,hl
1523   jp    PARSER_UMUL_BC_DE
1525 expr_do_div:
1526   call  expr_do_mod
1527   ld    hl,bc
1528   ret
1530 expr_do_mod:
1531   ld    a,d
1532   or    e
1533   ld    a,EXPR_ERR_DIVISION_BY_ZERO
1534   jp    z,PARSE_EXPR_ERROR_A
1535   ld    bc,hl
1536   jp    PARSER_UDIV_BC_DE
1538 expr_do_add:
1539   add   hl,de
1540   ret
1542 expr_do_sub:
1543   or    a
1544   sbc   hl,de
1545   ret
1547 expr_do_shl:
1548   ld    c,1
1549   jr    expr_do_shl_shl
1551 expr_do_shr:
1552   ld    c,0
1554 expr_do_shl_shl:
1555   ; HL: number to shift
1556   ; DE: amount
1557   ;  C: =1 means "shl"
1558   ld    a,d
1559   or    a
1560   jr    nz,.shift_too_far
1561   ld    a,e
1562   cp    16
1563   jr    nc,.shift_too_far
1564   ld    b,a
1565   dec   c
1566   jr    z,.do_shl
1567   ; shr
1568 .do_shr_loop:
1569   srl   h
1570   rr    l
1571   djnz  .do_shr_loop
1572   ret
1573   ; shl
1574 .do_shl:
1575   ; shl
1576   sla   l
1577   rl    h
1578   djnz  .do_shl
1579   ret
1580 .shift_too_far:
1581   ld    hl,0
1582   ret
1584 expr_do_bitand:
1585   ld    a,l
1586   and   e
1587   ld    l,a
1588   ld    a,h
1589   and   d
1590   ld    h,a
1591   ret
1593 expr_do_bitxor:
1594   ld    a,l
1595   xor   e
1596   ld    l,a
1597   ld    a,h
1598   xor   d
1599   ld    h,a
1600   ret
1602 expr_do_bitor:
1603   ld    a,l
1604   or    e
1605   ld    l,a
1606   ld    a,h
1607   or    d
1608   ld    h,a
1609   ret
1612 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1614 ;; find operator
1616 ;; IN:
1617 ;;   IY: text buffer
1618 ;; OUT:
1619 ;;   IY: text buffer after the operator
1620 ;;   DE: handler address
1621 ;;   HL: dead
1622 ;;    A: operator priority
1623 ;;   flags: dead
1624 ;;   zero set if no operator found (and IY is not modified)
1625 PARSE_EXPR_FINDOP:
1626   ld    hl,expr_optable-4
1627 .findloop:
1628   inc   hl
1629   inc   hl
1630   inc   hl
1631   inc   hl
1632   ld    a,(hl)
1633   or    a
1634   ret   z
1635   rra       ; carry is "two chars"
1636   jr    nc,.onechar
1637   ; two chars
1638   cp    (iy)
1639   jr    nz,.findloop
1640   cp    (iy+1)
1641   jr    nz,.findloop
1642   inc   iy  ; skip first char
1643   ; it is guaranteed to match, so don't bother skipping it
1644 .onechar:
1645   cp    (iy)
1646   jr    nz,.findloop
1647   ; skip operator
1648   inc   iy
1649   ; load priority
1650   inc   hl
1651   ld    a,(hl)
1652   ; load handler address
1653   inc   hl
1654   ld    e,(hl)
1655   inc   hl
1656   ld    d,(hl)
1657   or    a   ; reset zero flag
1658   ret
1660 PARSE_EXPR_CALL_BC:
1661   push  bc
1662   ret
1665 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1667 ;; parse an integer expression
1668 ;; IN:
1669 ;;   IY: text buffer
1670 ;; OUT:
1671 ;;   IY: text buffer after the expression
1672 ;;   HL: expression value
1673 ;;   everything other (including all alternate registers) is dead
1675 ;; priorities:
1676 ;;   unaries
1677 ;;   * / %
1678 ;;   + -
1679 ;;   << >>
1681 PARSE_INT_EXPR:
1682   call  PARSER_SKIP_BLANKS
1683   jr    z,PARSE_EXPR_ERROR_INT
1684   ; unary "+" or "-"?
1685   call  SIGN
1686   jr    nz,.doexpr
1687   push  af
1688   inc   iy
1689   call  .doexpr
1690   pop   af
1691   ; check for negate
1692   cp    '-'
1693   ret   nz
1694   ; negate HL
1695   or    a
1696   ld    de,0
1697   ex    de,hl
1698   sbc   hl,de
1699   ret
1701 .doexpr:
1702   ; start with maximum priority
1703   ld    c,expr_max_priority
1705 .expr_prios:
1706   ; term?
1707   dec   c
1708   jr    z,PARSE_EXPR_TERM
1709   ; get first operand
1710   push  bc
1711   call  .expr_prios
1712   pop   bc
1713   ; loop until the priority is right
1714 .expr_prios_loop:
1715   ; C is priority-1 here
1716   ; check if we have an operator
1717   call  PARSER_SKIP_BLANKS
1718   ret   z       ; exit on EOL
1719   ; check for operator
1720   push  hl      ; save op0
1721   push  iy      ; save input pointer, we may need to restore it later
1722   call  PARSE_EXPR_FINDOP
1723   jr    z,.expr_prios_no_op
1724   ; compare priorities
1725   dec   a
1726   cp    c
1727   jr    nz,.expr_prios_no_op
1728   ; get second operand
1729   pop   hl      ; drop iy
1730   pop   hl      ; op0
1731   push  bc      ; save priority
1732   push  hl      ; save op0
1733   push  de      ; save doer
1734   ; stack: prio, op0, doer
1735   call  .expr_prios
1736   ; stack: prio, op0, doer
1737   ex    de,hl   ; de is op1
1738   pop   bc      ; bc is doer
1739   pop   hl      ; hl is op0
1740   call  PARSE_EXPR_CALL_BC
1741   ; hl is result
1742   pop   bc      ; bc is prio
1743   jr    .expr_prios_loop
1745 .expr_prios_no_op:
1746   pop   iy
1747   pop   hl
1748   ret
1751 ;; parse term, also process unaries and parens
1752 PARSE_EXPR_TERM:
1753   call  PARSER_SKIP_BLANKS
1754   jr    z,PARSE_EXPR_ERROR_INT
1755   inc   iy      ; skip operation
1756   ld    c,')'
1757   cp    '('
1758   jr    z,PARSE_EXPR_LPAREN
1759   cp    '['
1760   ld    c,']'
1761   jr    z,PARSE_EXPR_LPAREN
1762   cp    '~'
1763   jr    z,PARSE_EXPR_BITNOT
1764   ; this must be number
1765   dec   iy      ; undo skip
1766   call  PARSE_NUMBER
1767   ret   nc
1768   ; check for labels
1769   push  iy      ; for correct error position
1770   push  ix      ; the only register we care about
1771   ld    hl,.term_checklabel_ret
1772   push  hl
1773   ld    hl,(GETLABEL_CB)
1774   jp    (hl)
1775 .term_checklabel_ret:
1776   pop   ix
1777   pop   de
1778   ret   nc
1779   ; oops, error
1780   ; restore position for correct error reporting
1781   push  de
1782   pop   iy
1783   ;jp    PARSE_EXPR_ERROR_INT
1785 PARSE_EXPR_ERROR_INT:
1786   ld    a,EXPR_ERR_NUMBER_EXPECTED
1788 PARSE_EXPR_ERROR_A:
1789   ld    hl,(EXPR_ERROR_CB)
1790   jp    (hl)
1792   ;; "("
1793 PARSE_EXPR_LPAREN:
1794   ;; C contains matching rparen
1795   push  bc
1796   call  PARSE_INT_EXPR
1797   call  PARSER_SKIP_BLANKS
1798   jr    z,PARSE_EXPR_ERROR_RPAREN
1799   pop   bc
1800   inc   iy
1801   cp    c
1802   ret   z
1804 PARSE_EXPR_ERROR_RPAREN:
1805   ld    a,EXPR_ERR_RPAREN_EXPECTED
1806   jr    PARSE_EXPR_ERROR_A
1808   ;; "~"
1809 PARSE_EXPR_BITNOT:
1810   call  PARSE_EXPR_TERM
1811   ld    a,h
1812   cpl
1813   ld    h,a
1814   ld    a,l
1815   cpl
1816   ld    l,a
1817   ret
1820 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1822 ;; parse a string expression
1824 ;; IN:
1825 ;;   IY: text buffer
1826 ;; OUT:
1827 ;;   HL: string buffer start
1828 ;;    E: parsed string length
1829 ;;   everything other (including all alternate registers) is dead
1831 PARSE_STR_EXPR:
1832   call  PARSER_SKIP_BLANKS
1833   jr    z,PARSE_EXPR_ERROR_STR
1834   cp    34
1835   jr    z,.strok
1836   cp    39
1837   jr    nz,PARSE_EXPR_ERROR_STR
1838 .strok:
1839   ld    c,a  ; terminator
1840   inc   iy
1841   ; remember buffer start
1842   push  iy
1843 .strloop:
1844   ld    a,(iy)
1845   or    a
1846   jr    z,PARSE_EXPR_ERROR_STR
1847   cp    CR
1848   jr    z,PARSE_EXPR_ERROR_STR
1849   inc   iy
1850   cp    c
1851   jr    nz,.strloop
1852   ; done string parsing, calc length
1853   pop   hl  ; buffer start
1854   ex    de,hl
1855   push  iy
1856   pop   hl
1857   ; DE: buffer start
1858   ; HL: buffer end
1859   or    a
1860   sbc   hl,de
1861   ex    de,hl
1862   ret
1864 PARSE_EXPR_ERROR_STR:
1865   ld    a,EXPR_ERR_STRING_EXPECTED
1866   jr    PARSE_EXPR_ERROR_A
1869 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1870 ;; skip blanks
1871 ;; returns current char in A
1872 ;; sets zero flag on EOL
1873 ;; IN:
1874 ;;   IY: text buffer
1875 ;; OUT:
1876 ;;   IY: text buffer at non-blank or EOL
1877 ;;    A: non-blank or EOL char
1878 ;;   zero flag is set on EOL/terminator
1880 PARSER_SKIP_BLANKS:
1881   ld    a,(iy)
1882   call  TERM
1883   ret   z
1884   inc   iy
1885   cp    33
1886   jr    c,PARSER_SKIP_BLANKS
1887   dec   iy
1888   ; reset zero flag
1889   or    a
1890   ret
1892 csizest = $-csizest
1893 $printf "assembler exprparse size: %d", csizest
1896 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1897 ;; various tables -- mnemonics, operands...
1899 csizest = $
1901 ;; number of "trivial" opcodes without any special processing
1902 OPC_COUNT_GROUPS_0_AND_1 equ 39
1903 ;; number of non-ED-prefixed instructions in "trivial"
1904 OPC_COUNT_GROUP_0 equ 15
1906 ;; total number of CB-prefixed instructions (GROUPS 2 and 3)
1907 OPC_COUNT_GROUPS_2_AND_3 equ 10
1908 ;; number of direct bit manipulation instructions in CB list (GROUP 2)
1909 OPC_COUNT_GROUP_2 equ 3
1911 ;; push, pop, ex (sp),rr
1912 OPC_COUNT_GROUP_4 equ 3
1914 ;; ALU with accum
1915 OPC_COUNT_GROUP_5 equ 5
1916 ;; ALU with accum
1917 OPC_COUNT_GROUP_6 equ 3
1918 ;; GROUPS 5 and 6
1919 OPC_COUNT_GROUPS_5_AND_6 equ OPC_COUNT_GROUP_5+OPC_COUNT_GROUP_6
1921 ;; INC/DEC
1922 OPC_COUNT_GROUP_7 equ 2
1924 ;; IN
1925 OPC_COUNT_GROUP_8 equ 1
1926 ;; OUT
1927 OPC_COUNT_GROUP_9 equ 1
1928 ;; GROUPS 8 and 9
1929 OPC_COUNT_GROUPS_8_AND_9 equ OPC_COUNT_GROUP_8+OPC_COUNT_GROUP_9
1931 ; JR,DJNZ
1932 OPC_COUNT_GROUP_10 equ 2
1933 OPC_COUNT_GROUP_10_JRS equ 1
1936 ;; WARNING! the assembler has some hard-coded mnemonics counts
1937 ;;          scattered across the code, so don't mess with the tables!
1938 ;;          i may document this in the future, but for now, leave it as it is.
1939 ;; mnemonics
1940 OPCODS:
1941   ; GROUPS 0 AND 1: "trivial" (39, OPC_COUNT_GROUPS_0_AND_1)
1942   ; GROUP 0: "trivial" one-byte (15, OPC_COUNT_GROUP_0)
1943   defx  "NOP"
1944   defb  0
1945   defx  "RLCA"
1946   defb  7
1947   defm  "EX",0,"AF",0
1948     defx "AF'"
1949   defb  8
1950   defx  "RRCA"
1951   defb  #0F
1952   defx  "RLA"
1953   defb  #17
1954   defx  "RRA"
1955   defb  #1F
1956   defx  "DAA"
1957   defb  #27
1958   defx  "CPL"
1959   defb  #2F
1960   defx  "SCF"
1961   defb  #37
1962   defx  "CCF"
1963   defb  #3F
1964   defx  "HALT"
1965   defb  #76
1966   defx  "EXX"
1967   defb  #D9
1968   defm  "EX",0,"DE",0
1969     defx "HL"
1970   defb  #EB
1971   defx  "DI"
1972   defb  #F3
1973   defx  "EI"
1974   defb  #FB
1975   ; GROUP 1: "trivial" ED-prefixed (24, OPC_COUNT_GROUPS_0_AND_1-OPC_COUNT_GROUP_0)
1976   defx  "NEG"
1977   defb  #44
1978   defm  "IM",0
1979     defx  "0"
1980   defb  #46
1981   defx  "RETN"
1982   defb  #45
1983   defx  "RETI"
1984   defb  #4D
1985   defm  "IM",0
1986     defx  "1"
1987   defb  #56
1988   defm  "IM",0
1989     defx  "2"
1990   defb  #5E
1991   defx  "RRD"
1992   defb  #67
1993   defx  "RLD"
1994   defb  #6F
1995   defx  "LDI"
1996   defb  #A0
1997   defx  "CPI"
1998   defb  #A1
1999   defx  "INI"
2000   defb  #A2
2001   defx  "OUTI"
2002   defb  #A3
2003   defx  "LDD"
2004   defb  #A8
2005   defx  "CPD"
2006   defb  #A9
2007   defx  "IND"
2008   defb  #AA
2009   defx  "OUTD"
2010   defb  #AB
2011   defx  "LDIR"
2012   defb  #B0
2013   defx  "CPIR"
2014   defb  #B1
2015   defx  "INIR"
2016   defb  #B2
2017   defx  "OTIR"
2018   defb  #B3
2019   defx  "LDDR"
2020   defb  #B8
2021   defx  "CPDR"
2022   defb  #B9
2023   defx  "INDR"
2024   defb  #BA
2025   defx  "OTDR"
2026   defb  #BB
2028   ; GROUPS 2 AND 3: CB-prefixed (10, OPC_COUNT_GROUPS_2_AND_3)
2029   ; GROUP 2: direct bit manipulation (3, OPC_COUNT_GROUP_2)
2030   defx  "BIT"
2031   defb  #40
2032   defx  "RES"
2033   defb  #80
2034   defx  "SET"
2035   defb  #C0
2036   ; GROUP 3: shifts (7, OPC_COUNT_GROUPS_2_AND_3-OPC_COUNT_GROUP_2)
2037   defx  "RLC"
2038   defb  0
2039   defx  "RRC"
2040   defb  8
2041   defx  "RL"
2042   defb  #10
2043   defx  "RR"
2044   defb  #18
2045   defx  "SLA"
2046   defb  #20
2047   defx  "SRA"
2048   defb  #28
2049   defx  "SRL"
2050   defb  #38
2052   ; GROUP 4: push,pop,ex (sp),rr (OPC_COUNT_GROUP_4)
2053   defx  "POP"
2054   defb  #C1
2055   defx  "PUSH"
2056   defb  #C5
2057   defm  "EX",0
2058     defx  "(SP"
2059   defb  #E3
2061   ; GROUP 5: ALU with accumulator (OPC_COUNT_GROUP_5)
2062   defx  "SUB"
2063   defb  #90
2064   defx  "AND"
2065   defb  #A0
2066   defx  "XOR"
2067   defb  #A8
2068   defx  "OR"
2069   defb  #B0
2070   defx  "CP"
2071   defb  #B8
2072   ;k8: for some reason i cannot remove those two
2073   ;defb  TAND
2074   ;defb  #A0
2075   ;defb  TOR
2076   ;defb  #B0
2078   ; GROUP 6: ALU with accumulator or HL (OPC_COUNT_GROUP_6)
2079   defx  "ADD"
2080   defb  #80
2081   defx  "ADC"
2082   defb  #88
2083   defx  "SBC"
2084   defb  #98
2086   ; GROUP 7: inc,dec (2, OPC_COUNT_GROUP_7)
2087   defx  "INC"
2088   defb  4
2089   defx  "DEC"
2090   defb  5
2092   ; GROUP 8: in (1, OPC_COUNT_GROUP_8)
2093   defx  "IN"
2094   defb  #40
2095   ; GROUP 9: out (1, OPC_COUNT_GROUP_9)
2096   defx  "OUT"
2097   defb  #41
2099   ; GROUP 10: jr,djnz (2, OPC_COUNT_GROUP_10)
2100   defx  "JR"
2101   defb  #20
2102   defx  "DJNZ"
2103   defb  #10
2105   ; GROUP 11: jp (strictly one)
2106   defx  "JP"
2107   defb  #C2
2109   ; GROUP 12: call (strictly one)
2110   defx  "CALL"
2111   defb  #C4
2113   ; GROUP 13: rst (strictly one)
2114   defx  "RST"
2115   defb  #C7
2117   ; GROUP 14: ret (strictly one)
2118   defx  "RET"
2119   defb  #C0
2121   ; GROUP 15: ld (strictly one)
2122   defx  "LD"
2123   defb  #40
2125   ; miscellaneous assembler instructions
2126   ; WARNING! the order matters!
2127   defx  "DEFB"
2128   defb  0
2129   ;
2130   defx  "DEFW"
2131   defb  0
2132   ;
2133   defx  "DEFM"
2134   defb  0
2136   IF @BZ80ASM_ORGENTDISP
2137   defx  "ORG"
2138   defb  0
2139   ;
2140   defx  "DISP"
2141   defb  0
2142   ;
2143   defx  "ENT"
2144   defb  0
2145   ENDIF
2146   ; softinclude user instructions
2147   include "?bzasm80_user_mnemonics.zas"
2149   ; no more
2150   defb  0
2152 ;; operands
2153 OPRNDS:
2154   defx  "B"
2155   defb  0
2156   defx  "C"
2157   defb  1
2158   defx  "D"
2159   defb  2
2160   defx  "E"
2161   defb  3
2162   defx  "H"
2163   defb  4
2164   defx  "L"
2165   defb  5
2166   defx  "(HL"
2167   defb  6
2168   defx  "A"
2169   defb  7
2170   defx  "(IX"
2171   defb  #86
2172   defx  "(IY"
2173   defb  #C6
2174   ;
2175   defx  "BC"
2176   defb  8
2177   defx  "DE"
2178   defb  10
2179   defx  "HL"
2180   defb  12
2181   defx  "IX"
2182   defb  #8C
2183   defx  "IY"
2184   defb  #CC
2185   defx  "AF"
2186   defb  14
2187   defx  "SP"
2188   defb  14
2189   ;
2190   defx  "NZ"
2191   defb  16
2192   defx  "Z"
2193   defb  17
2194   defx  "NC"
2195   defb  18
2196   defx  "PO"
2197   defb  20
2198   defx  "PE"
2199   defb  21
2200   defx  "P"
2201   defb  22
2202   defx  "M"
2203   defb  23
2204   ;
2205   defx  "(C"
2206   defb  #20
2207   ;
2208   defb  0
2211 LDOPS:
2212   defm  "I",0
2213     defx  "A"
2214   defb  #47
2215   defm  "R",0
2216     defx  "A"
2217   defb  #4F
2218   defm  "A",0
2219     defx  "I"
2220   defb  #57
2221   defm  "A",0
2222     defx  "R"
2223   defb  #5F
2224   defm  "(BC",0
2225     defx  "A"
2226   defb  2
2227   defm  "(DE",0
2228     defx  "A"
2229   defb  #12
2230   defm  "A",0
2231     defx  "(BC"
2232   defb  #0A
2233   defm  "A",0
2234     defx  "(DE"
2235   defb  #1A
2236   $IF @BZ80ASM_EXTLD
2237   defm  "BC",0
2238     defx  "DE"
2239   defb  #42+#80  ; #42,#4B  (+9)
2240   defm  "BC",0
2241     defx  "HL"
2242   defb  #44+#80  ; #44,#4D  (+9)
2243   defm  "DE",0
2244     defx  "BC"
2245   defb  #50+#80  ; #50,#59  (+9)
2246   defm  "DE",0
2247     defx  "HL"
2248   defb  #54+#80  ; #54,#5D  (+9)
2249   defm  "HL",0
2250     defx  "BC"
2251   defb  #60+#80  ; #60,#69  (+9)
2252   defm  "HL",0
2253     defx  "DE"
2254   defb  #62+#80  ; #62,#6B  (+9)
2255   $ENDIF
2256   ;
2257   defb  0
2259 csizest = $-csizest
2260 $printf "assembler tables size: %d", csizest
2262 asmsizest = $-asmsizest
2263 $printf "full assembler size: %d", asmsizest
2265 ; so they won't clutter symbol table
2266 csizest = -1
2267 asmsizest = -1
2269   ENDMODULE BZ80ASM