replaced expression parse in asm with table-driven: it is slower, but smaller by...
[bz80asm.git] / bzasm80.zas
blob2a108589cca6af108284641aca9ccd8ef17cb9cf
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 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1429 ;; operator table, to avoid pasta
1430 ;; bit 0 of operator name means "two equal chars"
1431 ;; name,priority,handler
1432 ;; priority 1 is term/unary
1433 ;; priority 0 is nothing, and it should stay nothing
1434 expr_optable:
1435   db 0+'*'*2,2
1436   dw expr_do_mul
1437   db 0+'/'*2,2
1438   dw expr_do_div
1439   db 0+'%'*2,2
1440   dw expr_do_mod
1442   db 0+'+'*2,3
1443   dw expr_do_add
1444   db 0+'-'*2,3
1445   dw expr_do_sub
1447   db 1+'<'*2,4
1448   dw expr_do_shl
1449   db 1+'>'*2,4
1450   dw expr_do_shr
1452   db 0+'&'*2,5
1453   dw expr_do_bitand
1455   db 0+'^'*2,6
1456   dw expr_do_bitxor
1458   db 0+'|'*2,7
1459   dw expr_do_bitor
1461   db 0  ; no more
1463 expr_max_priority equ 7
1465 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1466 ;; operator doers
1467 ;; HL is op0, DE is op1
1468 ;; result in HL
1469 ;; preserve IY
1471 expr_do_mul:
1472   ld    bc,hl
1473   jp    PARSER_UMUL_BC_DE
1475 expr_do_div:
1476   call  expr_do_mod
1477   ld    hl,bc
1478   ret
1480 expr_do_mod:
1481   ld    bc,hl
1482   jp    PARSER_UDIV_BC_DE
1484 expr_do_add:
1485   add   hl,de
1486   ret
1488 expr_do_sub:
1489   or    a
1490   sbc   hl,de
1491   ret
1493 expr_do_shl:
1494   ld    c,1
1495   jr    expr_do_shl_shl
1497 expr_do_shr:
1498   ld    c,0
1500 expr_do_shl_shl:
1501   ; HL: number to shift
1502   ; DE: amount
1503   ;  C: =1 means "shl"
1504   ld    a,d
1505   or    a
1506   jr    nz,.shift_too_far
1507   ld    a,e
1508   cp    16
1509   jr    nc,.shift_too_far
1510   ld    b,a
1511   dec   c
1512   jr    z,.do_shl
1513   ; shr
1514 .do_shr_loop:
1515   srl   h
1516   rr    l
1517   djnz  .do_shr_loop
1518   ret
1519   ; shl
1520 .do_shl:
1521   ; shl
1522   sla   l
1523   rl    h
1524   djnz  .do_shl
1525   ret
1526 .shift_too_far:
1527   ld    hl,0
1528   ret
1530 expr_do_bitand:
1531   ld    a,l
1532   and   e
1533   ld    l,a
1534   ld    a,h
1535   and   d
1536   ld    h,a
1537   ret
1539 expr_do_bitxor:
1540   ld    a,l
1541   xor   e
1542   ld    l,a
1543   ld    a,h
1544   xor   d
1545   ld    h,a
1546   ret
1548 expr_do_bitor:
1549   ld    a,l
1550   or    e
1551   ld    l,a
1552   ld    a,h
1553   or    d
1554   ld    h,a
1555   ret
1558 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1560 ;; find operator
1562 ;; IN:
1563 ;;   IY: text buffer
1564 ;; OUT:
1565 ;;   IY: text buffer after the operator
1566 ;;   DE: handler address
1567 ;;   HL: dead
1568 ;;    A: operator priority
1569 ;;   flags: dead
1570 ;;   zero set if no operator found (and IY is not modified)
1571 PARSE_EXPR_FINDOP:
1572   ld    hl,expr_optable-4
1573 .findloop:
1574   inc   hl
1575   inc   hl
1576   inc   hl
1577   inc   hl
1578   ld    a,(hl)
1579   or    a
1580   ret   z
1581   rra       ; carry is "two chars"
1582   jr    nc,.onechar
1583   ; two chars
1584   cp    (iy)
1585   jr    nz,.findloop
1586   cp    (iy+1)
1587   jr    nz,.findloop
1588   inc   iy  ; skip first char
1589   ; it is guaranteed to match, so don't bother skipping it
1590 .onechar:
1591   cp    (iy)
1592   jr    nz,.findloop
1593   ; skip operator
1594   inc   iy
1595   ; load priority
1596   inc   hl
1597   ld    a,(hl)
1598   ; load handler address
1599   inc   hl
1600   ld    e,(hl)
1601   inc   hl
1602   ld    d,(hl)
1603   or    a   ; reset zero flag
1604   ret
1606 PARSE_EXPR_CALL_BC:
1607   push  bc
1608   ret
1611 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1612 PARSE_EXPR_ERROR_A:
1613   ld    hl,(EXPR_ERROR_CB)
1614   jp    (hl)
1616 PARSE_EXPR_ERROR_0DIV:
1617   ld    a,EXPR_ERR_DIVISION_BY_ZERO
1618   jr    PARSE_EXPR_ERROR_A
1620 PARSE_EXPR_ERROR_INT:
1621   ld    a,EXPR_ERR_NUMBER_EXPECTED
1622   jr    PARSE_EXPR_ERROR_A
1625 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1627 ;; parse an integer expression
1628 ;; IN:
1629 ;;   IY: text buffer
1630 ;; OUT:
1631 ;;   IY: text buffer after the expression
1632 ;;   HL: expression value
1633 ;;   everything other (including all alternate registers) is dead
1635 ;; priorities:
1636 ;;   unaries
1637 ;;   * / %
1638 ;;   + -
1639 ;;   << >>
1641 PARSE_INT_EXPR:
1642   call  PARSER_SKIP_BLANKS
1643   jr    z,PARSE_EXPR_ERROR_INT
1644   ; unary "+" or "-"?
1645   call  SIGN
1646   jr    nz,.doexpr
1647   push  af
1648   inc   iy
1649   call  .doexpr
1650   pop   af
1651   ; check for negate
1652   cp    '-'
1653   ret   nz
1654   ; negate HL
1655   or    a
1656   ld    de,0
1657   ex    de,hl
1658   sbc   hl,de
1659   ret
1661 .doexpr:
1662   ; start with maximum priority
1663   ld    c,expr_max_priority
1665 .expr_prios:
1666   ; term?
1667   dec   c
1668   jr    z,.term
1669   ; get first operand
1670   push  bc
1671   call  .expr_prios
1672   pop   bc
1673   ; loop until the priority is right
1674 .expr_prios_loop:
1675   ; C is priority-1 here
1676   ; check if we have an operator
1677   call  PARSER_SKIP_BLANKS
1678   ret   z       ; exit on EOL
1679   ; check for operator
1680   push  hl      ; save op0
1681   push  iy      ; save input pointer, we may need to restore it later
1682   call  PARSE_EXPR_FINDOP
1683   jr    z,.expr_prios_no_op
1684   ; compare priorities
1685   dec   a
1686   cp    c
1687   jr    nz,.expr_prios_no_op
1688   ; get second operand
1689   pop   hl      ; drop iy
1690   pop   hl      ; op0
1691   push  bc      ; save priority
1692   push  hl      ; save op0
1693   push  de      ; save doer
1694   ; stack: prio, op0, doer
1695   call  .expr_prios
1696   ; stack: prio, op0, doer
1697   ex    de,hl   ; de is op1
1698   pop   bc      ; bc is doer
1699   pop   hl      ; hl is op0
1700   call  PARSE_EXPR_CALL_BC
1701   ; hl is result
1702   pop   bc      ; bc is prio
1703   jr    .expr_prios_loop
1705 .expr_prios_no_op:
1706   pop   iy
1707   pop   hl
1708   ret
1711 ;; parse term, also process unaries and parens
1712 .term:
1713   call  PARSER_SKIP_BLANKS
1714   jp    z,PARSE_EXPR_ERROR_INT
1715   inc   iy      ; skip operation
1716   ld    c,')'
1717   cp    '('
1718   jr    z,.term_lparen
1719   cp    '['
1720   ld    c,']'
1721   jr    z,.term_lparen
1722   cp    '~'
1723   jr    z,.term_ubitnot
1724   ; this must be number
1725   dec   iy      ; undo skip
1726   call  PARSE_NUMBER
1727   ret   nc
1728   ; check for labels
1729   push  iy      ; for correct error position
1730   push  ix      ; the only register we care about
1731   ld    hl,.term_checklabel_ret
1732   push  hl
1733   ld    hl,(GETLABEL_CB)
1734   jp    (hl)
1735 .term_checklabel_ret:
1736   pop   ix
1737   pop   de
1738   ret   nc
1739   ; oops, error
1740   ; restore position for correct error reporting
1741   push  de
1742   pop   iy
1743   jp    PARSE_EXPR_ERROR_INT
1745   ;; "("
1746 .term_lparen:
1747   ;; C contains matching rparen
1748   push  bc
1749   call  PARSE_INT_EXPR
1750   call  PARSER_SKIP_BLANKS
1751   jr    z,PARSE_EXPR_ERROR_RPAREN
1752   pop   bc
1753   cp    c
1754   jr    nz,PARSE_EXPR_ERROR_RPAREN
1755   inc   iy
1756   ret
1758   ;; "~"
1759 .term_ubitnot:
1760   call  .term
1761   ld    a,h
1762   cpl
1763   ld    h,a
1764   ld    a,l
1765   cpl
1766   ld    l,a
1767   ret
1769 PARSE_EXPR_ERROR_RPAREN:
1770   ld    a,EXPR_ERR_RPAREN_EXPECTED
1771   jp    PARSE_EXPR_ERROR_A
1774 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1776 ;; HL=BC*DE
1778 ;; BC,DE,A,flags: dead
1780 PARSER_UMUL_BC_DE:
1781   ; DEHL=BC*DE
1782   ld    hl,0
1783   ld    a,16
1784 .loop:
1785   add   hl,hl
1786   rl    e
1787   rl    d
1788   jr    nc,.skip
1789   add   hl,bc
1790   jr    nc,.skip
1791   inc   de
1792 .skip:
1793   dec   a
1794   jr    nz,.loop
1795   ret
1798 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1800 ;; performs BC/DE
1801 ;; OUT:
1802 ;;   BC: quotient
1803 ;;   HL: remainder
1805 ;; DE,A,flags: dead
1807 PARSER_UDIV_BC_DE:
1808   ld    hl,0
1809   ld    a,16
1810 .loop:
1811   sll   c
1812   rl    b
1813   adc   hl,hl
1814   sbc   hl,de
1815   jr    nc,.skip
1816   add   hl,de
1817   dec   c
1818 .skip:
1819   dec   a
1820   jr    nz,.loop
1821   ret
1824 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1826 ;; parse a string expression
1828 ;; IN:
1829 ;;   IY: text buffer
1830 ;; OUT:
1831 ;;   HL: string buffer start
1832 ;;    E: parsed string length
1833 ;;   everything other (including all alternate registers) is dead
1835 PARSE_STR_EXPR:
1836   call  PARSER_SKIP_BLANKS
1837   jr    z,PARSE_EXPR_ERROR_STR
1838   cp    34
1839   jr    z,.strok
1840   cp    39
1841   jr    nz,PARSE_EXPR_ERROR_STR
1842 .strok:
1843   ld    c,a  ; terminator
1844   inc   iy
1845   ; remember buffer start
1846   push  iy
1847 .strloop:
1848   ld    a,(iy)
1849   or    a
1850   jr    z,PARSE_EXPR_ERROR_STR
1851   cp    CR
1852   jr    z,PARSE_EXPR_ERROR_STR
1853   inc   iy
1854   cp    c
1855   jr    nz,.strloop
1856   ; done string parsing, calc length
1857   pop   hl  ; buffer start
1858   ex    de,hl
1859   push  iy
1860   pop   hl
1861   ; DE: buffer start
1862   ; HL: buffer end
1863   or    a
1864   sbc   hl,de
1865   ex    de,hl
1866   ret
1868 PARSE_EXPR_ERROR_STR:
1869   ld    a,EXPR_ERR_STRING_EXPECTED
1870   jp    PARSE_EXPR_ERROR_A
1873 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1874 ;; skip blanks
1875 ;; returns current char in A
1876 ;; sets zero flag on EOL
1877 ;; IN:
1878 ;;   IY: text buffer
1879 ;; OUT:
1880 ;;   IY: text buffer at non-blank or EOL
1881 ;;    A: non-blank or EOL char
1882 ;;   zero flag is set on EOL/terminator
1884 PARSER_SKIP_BLANKS:
1885   ld    a,(iy)
1886   call  TERM
1887   ret   z
1888   inc   iy
1889   cp    33
1890   jr    c,PARSER_SKIP_BLANKS
1891   dec   iy
1892   ; reset zero flag
1893   or    a
1894   ret
1896 csizest = $-csizest
1897 $printf "assembler exprparse size: %d", csizest
1900 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1901 ;; various tables -- mnemonics, operands...
1903 csizest = $
1905 ;; number of "trivial" opcodes without any special processing
1906 OPC_COUNT_GROUPS_0_AND_1 equ 39
1907 ;; number of non-ED-prefixed instructions in "trivial"
1908 OPC_COUNT_GROUP_0 equ 15
1910 ;; total number of CB-prefixed instructions (GROUPS 2 and 3)
1911 OPC_COUNT_GROUPS_2_AND_3 equ 10
1912 ;; number of direct bit manipulation instructions in CB list (GROUP 2)
1913 OPC_COUNT_GROUP_2 equ 3
1915 ;; push, pop, ex (sp),rr
1916 OPC_COUNT_GROUP_4 equ 3
1918 ;; ALU with accum
1919 OPC_COUNT_GROUP_5 equ 5
1920 ;; ALU with accum
1921 OPC_COUNT_GROUP_6 equ 3
1922 ;; GROUPS 5 and 6
1923 OPC_COUNT_GROUPS_5_AND_6 equ OPC_COUNT_GROUP_5+OPC_COUNT_GROUP_6
1925 ;; INC/DEC
1926 OPC_COUNT_GROUP_7 equ 2
1928 ;; IN
1929 OPC_COUNT_GROUP_8 equ 1
1930 ;; OUT
1931 OPC_COUNT_GROUP_9 equ 1
1932 ;; GROUPS 8 and 9
1933 OPC_COUNT_GROUPS_8_AND_9 equ OPC_COUNT_GROUP_8+OPC_COUNT_GROUP_9
1935 ; JR,DJNZ
1936 OPC_COUNT_GROUP_10 equ 2
1937 OPC_COUNT_GROUP_10_JRS equ 1
1940 ;; WARNING! the assembler has some hard-coded mnemonics counts
1941 ;;          scattered across the code, so don't mess with the tables!
1942 ;;          i may document this in the future, but for now, leave it as it is.
1943 ;; mnemonics
1944 OPCODS:
1945   ; GROUPS 0 AND 1: "trivial" (39, OPC_COUNT_GROUPS_0_AND_1)
1946   ; GROUP 0: "trivial" one-byte (15, OPC_COUNT_GROUP_0)
1947   defx  "NOP"
1948   defb  0
1949   defx  "RLCA"
1950   defb  7
1951   defm  "EX",0,"AF",0
1952     defx "AF'"
1953   defb  8
1954   defx  "RRCA"
1955   defb  #0F
1956   defx  "RLA"
1957   defb  #17
1958   defx  "RRA"
1959   defb  #1F
1960   defx  "DAA"
1961   defb  #27
1962   defx  "CPL"
1963   defb  #2F
1964   defx  "SCF"
1965   defb  #37
1966   defx  "CCF"
1967   defb  #3F
1968   defx  "HALT"
1969   defb  #76
1970   defx  "EXX"
1971   defb  #D9
1972   defm  "EX",0,"DE",0
1973     defx "HL"
1974   defb  #EB
1975   defx  "DI"
1976   defb  #F3
1977   defx  "EI"
1978   defb  #FB
1979   ; GROUP 1: "trivial" ED-prefixed (24, OPC_COUNT_GROUPS_0_AND_1-OPC_COUNT_GROUP_0)
1980   defx  "NEG"
1981   defb  #44
1982   defm  "IM",0
1983     defx  "0"
1984   defb  #46
1985   defx  "RETN"
1986   defb  #45
1987   defx  "RETI"
1988   defb  #4D
1989   defm  "IM",0
1990     defx  "1"
1991   defb  #56
1992   defm  "IM",0
1993     defx  "2"
1994   defb  #5E
1995   defx  "RRD"
1996   defb  #67
1997   defx  "RLD"
1998   defb  #6F
1999   defx  "LDI"
2000   defb  #A0
2001   defx  "CPI"
2002   defb  #A1
2003   defx  "INI"
2004   defb  #A2
2005   defx  "OUTI"
2006   defb  #A3
2007   defx  "LDD"
2008   defb  #A8
2009   defx  "CPD"
2010   defb  #A9
2011   defx  "IND"
2012   defb  #AA
2013   defx  "OUTD"
2014   defb  #AB
2015   defx  "LDIR"
2016   defb  #B0
2017   defx  "CPIR"
2018   defb  #B1
2019   defx  "INIR"
2020   defb  #B2
2021   defx  "OTIR"
2022   defb  #B3
2023   defx  "LDDR"
2024   defb  #B8
2025   defx  "CPDR"
2026   defb  #B9
2027   defx  "INDR"
2028   defb  #BA
2029   defx  "OTDR"
2030   defb  #BB
2032   ; GROUPS 2 AND 3: CB-prefixed (10, OPC_COUNT_GROUPS_2_AND_3)
2033   ; GROUP 2: direct bit manipulation (3, OPC_COUNT_GROUP_2)
2034   defx  "BIT"
2035   defb  #40
2036   defx  "RES"
2037   defb  #80
2038   defx  "SET"
2039   defb  #C0
2040   ; GROUP 3: shifts (7, OPC_COUNT_GROUPS_2_AND_3-OPC_COUNT_GROUP_2)
2041   defx  "RLC"
2042   defb  0
2043   defx  "RRC"
2044   defb  8
2045   defx  "RL"
2046   defb  #10
2047   defx  "RR"
2048   defb  #18
2049   defx  "SLA"
2050   defb  #20
2051   defx  "SRA"
2052   defb  #28
2053   defx  "SRL"
2054   defb  #38
2056   ; GROUP 4: push,pop,ex (sp),rr (OPC_COUNT_GROUP_4)
2057   defx  "POP"
2058   defb  #C1
2059   defx  "PUSH"
2060   defb  #C5
2061   defm  "EX",0
2062     defx  "(SP"
2063   defb  #E3
2065   ; GROUP 5: ALU with accumulator (OPC_COUNT_GROUP_5)
2066   defx  "SUB"
2067   defb  #90
2068   defx  "AND"
2069   defb  #A0
2070   defx  "XOR"
2071   defb  #A8
2072   defx  "OR"
2073   defb  #B0
2074   defx  "CP"
2075   defb  #B8
2076   ;k8: for some reason i cannot remove those two
2077   ;defb  TAND
2078   ;defb  #A0
2079   ;defb  TOR
2080   ;defb  #B0
2082   ; GROUP 6: ALU with accumulator or HL (OPC_COUNT_GROUP_6)
2083   defx  "ADD"
2084   defb  #80
2085   defx  "ADC"
2086   defb  #88
2087   defx  "SBC"
2088   defb  #98
2090   ; GROUP 7: inc,dec (2, OPC_COUNT_GROUP_7)
2091   defx  "INC"
2092   defb  4
2093   defx  "DEC"
2094   defb  5
2096   ; GROUP 8: in (1, OPC_COUNT_GROUP_8)
2097   defx  "IN"
2098   defb  #40
2099   ; GROUP 9: out (1, OPC_COUNT_GROUP_9)
2100   defx  "OUT"
2101   defb  #41
2103   ; GROUP 10: jr,djnz (2, OPC_COUNT_GROUP_10)
2104   defx  "JR"
2105   defb  #20
2106   defx  "DJNZ"
2107   defb  #10
2109   ; GROUP 11: jp (strictly one)
2110   defx  "JP"
2111   defb  #C2
2113   ; GROUP 12: call (strictly one)
2114   defx  "CALL"
2115   defb  #C4
2117   ; GROUP 13: rst (strictly one)
2118   defx  "RST"
2119   defb  #C7
2121   ; GROUP 14: ret (strictly one)
2122   defx  "RET"
2123   defb  #C0
2125   ; GROUP 15: ld (strictly one)
2126   defx  "LD"
2127   defb  #40
2129   ; miscellaneous assembler instructions
2130   ; WARNING! the order matters!
2131   defx  "DEFB"
2132   defb  0
2133   ;
2134   defx  "DEFW"
2135   defb  0
2136   ;
2137   defx  "DEFM"
2138   defb  0
2140   IF @BZ80ASM_ORGENTDISP
2141   defx  "ORG"
2142   defb  0
2143   ;
2144   defx  "DISP"
2145   defb  0
2146   ;
2147   defx  "ENT"
2148   defb  0
2149   ENDIF
2150   ; softinclude user instructions
2151   include "?bzasm80_user_mnemonics.zas"
2153   ; no more
2154   defb  0
2156 ;; operands
2157 OPRNDS:
2158   defx  "B"
2159   defb  0
2160   defx  "C"
2161   defb  1
2162   defx  "D"
2163   defb  2
2164   defx  "E"
2165   defb  3
2166   defx  "H"
2167   defb  4
2168   defx  "L"
2169   defb  5
2170   defx  "(HL"
2171   defb  6
2172   defx  "A"
2173   defb  7
2174   defx  "(IX"
2175   defb  #86
2176   defx  "(IY"
2177   defb  #C6
2178   ;
2179   defx  "BC"
2180   defb  8
2181   defx  "DE"
2182   defb  10
2183   defx  "HL"
2184   defb  12
2185   defx  "IX"
2186   defb  #8C
2187   defx  "IY"
2188   defb  #CC
2189   defx  "AF"
2190   defb  14
2191   defx  "SP"
2192   defb  14
2193   ;
2194   defx  "NZ"
2195   defb  16
2196   defx  "Z"
2197   defb  17
2198   defx  "NC"
2199   defb  18
2200   defx  "PO"
2201   defb  20
2202   defx  "PE"
2203   defb  21
2204   defx  "P"
2205   defb  22
2206   defx  "M"
2207   defb  23
2208   ;
2209   defx  "(C"
2210   defb  #20
2211   ;
2212   defb  0
2215 LDOPS:
2216   defm  "I",0
2217     defx  "A"
2218   defb  #47
2219   defm  "R",0
2220     defx  "A"
2221   defb  #4F
2222   defm  "A",0
2223     defx  "I"
2224   defb  #57
2225   defm  "A",0
2226     defx  "R"
2227   defb  #5F
2228   defm  "(BC",0
2229     defx  "A"
2230   defb  2
2231   defm  "(DE",0
2232     defx  "A"
2233   defb  #12
2234   defm  "A",0
2235     defx  "(BC"
2236   defb  #0A
2237   defm  "A",0
2238     defx  "(DE"
2239   defb  #1A
2240   $IF @BZ80ASM_EXTLD
2241   defm  "BC",0
2242     defx  "DE"
2243   defb  #42+#80  ; #42,#4B  (+9)
2244   defm  "BC",0
2245     defx  "HL"
2246   defb  #44+#80  ; #44,#4D  (+9)
2247   defm  "DE",0
2248     defx  "BC"
2249   defb  #50+#80  ; #50,#59  (+9)
2250   defm  "DE",0
2251     defx  "HL"
2252   defb  #54+#80  ; #54,#5D  (+9)
2253   defm  "HL",0
2254     defx  "BC"
2255   defb  #60+#80  ; #60,#69  (+9)
2256   defm  "HL",0
2257     defx  "DE"
2258   defb  #62+#80  ; #62,#6B  (+9)
2259   $ENDIF
2260   ;
2261   defb  0
2263 csizest = $-csizest
2264 $printf "assembler tables size: %d", csizest
2266 asmsizest = $-asmsizest
2267 $printf "full assembler size: %d", asmsizest
2269 ; so they won't clutter symbol table
2270 csizest = -1
2271 asmsizest = -1
2273   ENDMODULE BZ80ASM