finished label manager interface; implemented sample label manager
[bz80asm.git] / bzasm80.zas
blob51b853c1a52a69a9f56431d2544d67fdc32329a6
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
27 IF !defined(BZ80ASM_ORGENTDISP)
28 BZ80ASM_ORGENTDISP equ 0
29 ENDIF
31   MODULE BZ80ASM
33 PC: defw #C000  ; logical program counter for the code
35 IF @BZ80ASM_ORGENTDISP
36 ; ORG/DISP sets this to its value
37 NEW_ORGENT: defw 0
38 ; ORG/ENT sets this to the corresponding type
39 ; it is reset at each call to ASSEM
40 ORGENT_NONE equ 0
41 ORGENT_ORG equ 1
42 ORGENT_DISP equ 2
43 ORGENT_ENT equ 3
44 ORGENT_TYPE: defw ORGENT_NONE
45 ENDIF
47 ; address of the routine that will be called if JR/DJNZ destination is too far away
48 ; you can simply RET from it if you want to ignore this error
49 ; note that stack is filled with garbage, so use `ASM_ERROR_EXIT` to bail out
50 ; this is not checked for zero, so if you will not init this, "CALL 0" will be executed
51 ; ret L to the byte that will be put on normal return
52 ASM_JR_TOO_FAR_CB: defw 0
54 ; on entry, ASSEM will store SP here
55 ; you can use this to reset the stack, and use RET to return to the caller
56 ; or simply use ASM_ERROR_EXIT (which will set carry too)
57 ASM_SP0: defw 0
59 ; ASSEM sets this if it hits mnemonics without a handler
60 ; you can add your own mnemonics to the assembler, and it
61 ; will set this to non-zero if it hits them
62 ; use this byte as decrementing counter, like this:
63 ;  jr    nc,no_error
64 ;  ld    a,(ASM_BAD_B)
65 ;  or    a
66 ;  jr    z,syntax_error
67 ;  ld    b,a
68 ;  djnz  not_first_one
69 ;    handle first instruction
70 ;  not_first_one:
71 ;  djnz  not_second_one
72 ;    handle second instruction
73 ;  ...and so on
74 ; ADDITIONALLY, this will be set to #FF on invalid token
75 ; this can be used to process labels (because otherwise
76 ; there is no way to tell if we got some bad operands, or
77 ; an unknown mnemonics)
78 ASM_BAD_B: defw 0
81 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
82 ;; expression parser variables
84 ;; set this to the address of error routine
85 ;; note that you cannot return from it, you HAVE to abort everything
86 ;; also note that machine stack is undefined, and SP should be set
87 ;; to some initial value
88 ;; "undefined" means that machine stack can contain alot of garbage,
89 ;; but will never be underflowed
91 ;; this function is called with error code in A
93 ;; you can load your own error code in A, and do:
94 ;;  jp  BZ80ASM.PARSE_EXPR_ERROR_A
95 EXPR_ERROR_CB: defw 0
97 ;; error codes
98 ;; expected number, but got something incomprehensible
99 EXPR_ERR_NUMBER_EXPECTED equ 1
100 ;; expected string, but got something incomprehensible
101 EXPR_ERR_STRING_EXPECTED equ 2
102 ;; expected ")", but got something strange
103 EXPR_ERR_RPAREN_EXPECTED equ 3
104 ;; expected ")", but got something strange
105 EXPR_ERR_DIVISION_BY_ZERO equ 4
107 ;; the asm never generates the following errors, but
108 ;; they may be useful for a main driver
110 ;; you can use this to dispatch "short jump destination is too far" error
111 ;; just do this in `ASM_JR_TOO_FAR_CB`:
112 ;;   ld  a,EXPR_ERR_JR_TOO_FAR
113 ;;   jp  BZ80ASM.PARSE_EXPR_ERROR_A
114 EXPR_ERR_JR_TOO_FAR equ 5
116 ;; this "unknown label" error can be used by label manager
117 EXPR_ERR_UNKNOWN_LABEL equ 6
118 ;; this error can be used by label manager if it cannot parse a label
119 EXPR_ERR_INVALID_LABEL_NAME equ 7
120 ;; this error can be used by label manager
121 EXPR_ERR_DUPLICATE_LABEL equ 8
123 ;; general "bad syntax" error, when you don't have anything better
124 EXPR_ERR_BAD_SYNTAX equ 9
126 ;; offset your own error codes with this
127 EXPR_ERR_USERDEF equ 10
130 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
131 ;; label manager callback
133 ;; find a label
134 ;; this is called from assembler to find an existing label.
135 ;; IY points to the first char of a label name.
136 ;; after parsing, IY should point right after the parsed name.
137 ;; all registers expect IY can be trashed (including IX).
139 ;; for forward referencing, label manager can create unknown
140 ;; labels, and set their value to (BZ80ASM.PC).
141 ;; it is important to set new labels to PC to avoid errors
142 ;; with short jumps if your "jr too far" error handler always
143 ;; bombs out. otherwise, you can set the label to anything
144 ;; you like.
146 ;; IN:
147 ;;   IY: text input buffer
148 ;; OUT:
149 ;;   IY: text input buffer after the label
150 ;;   HL: label value
151 ;;   CARRY FLAG: set if label wasn't found
152 ;;               expression parser will bomb out in this case
153 ;; on error, IY doesn't matter, because it will be restored
154 ;; by the calling code
155 GETLABEL_CB: defw 0
158 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
159 ; string terminator
160 CR equ 13
163 asmsizest = $
164 csizest = $
166 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
167 ;; ASSEMBLER:
168 ;; LANGUAGE-INDEPENDENT CONTROL SECTION:
169 ;;  Inputs:
170 ;;    IY: text buffer
171 ;;    IX: destination to put generated code at
172 ;;  Outputs:
173 ;;    A: delimiter
174 ;;    IY: delimiter position in text buffer
175 ;;    IX: points right after the generated code
176 ;;    carry set if syntax error (and A is undefined in this case)
177 ;;    others are dead (including extra register set and extra accum/flags)
179 ASSEM:
180   ; reset org/ent type
181   xor   a
182   IF @BZ80ASM_ORGENTDISP
183   ld    (ORGENT_TYPE),a
184   ENDIF
185   ld    (ASM_BAD_B),a
186   ld    (ASM_SP0),sp
187 ASSEM1:
188   call  SKIP
189   inc   iy
190   cp    ':'
191   jr    z,ASSEM1
192   ; this was used to terminate assembler section in BBC Basic
193   ;cp    ']'
194   ;ret   z
195   cp    CR
196   ret   z
197   dec   iy
198   push  ix
199   ;push  iy
200   call  ASMB
201   ;pop   bc
202   pop   de
203   ; exit if syntax error
204   ret   c
205   ; skip delimiters (but not terminators)
206   call  SKIP
207   ; exit with error flag if we're not at a terminator
208   scf
209   ret   nz
210   ; advance PC
211   push  ix
212   pop   hl
213   or    a
214   sbc   hl,de
215   ex    de,hl           ;DE= NO. OF BYTES
216   ld    hl,(PC)
217   add   hl,de
218   ld    (PC),hl         ;UPDATE PC
219   ; reset carry and Z
220   xor   a
221   ret
223 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
225 ;; jump here to restore stack, and exit with error (carry set)
227 ASM_ERROR_EXIT:
228   ld    sp,(ASM_SP0)
229   scf
230   ret
232 ASMB_BAD_MNEMO:
233   ld    a,255
234   ld    (ASM_BAD_B),a
235   ; carry is still set
236   ret
238 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
240 ;; PROCESSOR-SPECIFIC TRANSLATION SECTION:
242 ;; REGISTER USAGE: B - TYPE OF MOST RECENT OPERAND
243 ;;                 C - OPCODE BEING BUILT
244 ;;                 D - (IX) OR (IY) FLAG
245 ;;                 E - OFFSET FROM IX OR IY
246 ;;                HL - NUMERIC OPERAND VALUE
247 ;;                IX - CODE DESTINATION
248 ;;                IY - SOURCE TEXT POINTER
249 ;;    Inputs: A = initial character
250 ;;   Outputs: Carry set if syntax error.
252 ASMB:
253   call  SKIP
254   ret   z
255   ; this code seems to be for tokenized input
256   ; we don't have tokenizer
257   ;cp    TCALL
258   ;ld    c,0C4H
259   ;inc   iy
260   ;jp    z,GRPC
261   ;dec   iy
262   ld    hl,OPCODS
263   call  FIND
264   ; carry flag set on error
265   jr    c,ASMB_BAD_MNEMO
266   ; A contains token index
267   ; B contains token data byte
268   ld    c,b     ;ROOT OPCODE
269   ld    d,0     ;CLEAR IX/IY FLAG
271   ; now:
272   ; A contains token index
273   ; C contains token data byte
274   ; D contains IX/IY flag
276 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
278 ;; GROUP 0 - TRIVIAL CASES REQUIRING NO COMPUTATION
279 ;; GROUP 1 - AS GROUP 0 BUT WITH "ED" PREFIX
281   sub   OPC_COUNT_GROUPS_0_AND_1
282   jr    nc,GROUP2
283   cp    OPC_COUNT_GROUP_0-OPC_COUNT_GROUPS_0_AND_1
284   call  nc,EDX
285   jr    BYTE0
287 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
289 ;; GROUP 2 - BIT, RES, SET
290 ;; GROUP 3 - RLC, RRC, RL, RR, SLA, SRA, SRL
292 GROUP2:
293   sub   OPC_COUNT_GROUPS_2_AND_3
294   jr    nc,GROUP4
295   cp    OPC_COUNT_GROUP_2-OPC_COUNT_GROUPS_2_AND_3
296   call  c,CBIT
297   ret   c
298   call  REGLO
299   ret   c
300   call  CBX
301   jr    BYTE0
303 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
305 ;; GROUP 4 - PUSH, POP, EX (SP)
307 GROUP4:
308   sub   OPC_COUNT_GROUP_4
309   jr    nc,GROUP5
311   call  PAIR
312   ret   c
313   jr    BYTE0
315 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
317 ;; GROUP 5 - SUB, AND, XOR, OR, CP
318 ;; GROUP 6 - ADD, ADC, SBC
320 GROUP5:
321   sub   OPC_COUNT_GROUPS_5_AND_6
322   jr    nc,GROUP7
323   cp    OPC_COUNT_GROUP_5-OPC_COUNT_GROUPS_5_AND_6
324   ld    b,7
325   call  nc,OPND
326   ld    a,b
327   cp    7
328   jr    nz,G6HL
330   call  REGLO
331   ld    a,c
332   jr    nc,BIND1
333   xor   46H
334   call  BIND
335 DBX:
336   call  NUMBER
337   jr    VAL8
339 G6HL:
340   and   3FH
341   cp    12
342   scf
343   ret   nz
344   ld    a,c
345   cp    80H
346   ld    c,9
347   jr    z,G4
348   xor   1CH
349   rrca
350   ld    c,a
351   call  EDX
352   jr    G4
354 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
356 ;; GROUP 7 - INC, DEC
358 GROUP7:
359   sub   OPC_COUNT_GROUP_7
360   jr    nc,GROUP8
361   call  REGHI
362   ld    a,c
363 BIND1:
364   jp    nc,BIND
365   xor   64H
366   rlca
367   rlca
368   rlca
369   ld    c,a
370   call  PAIR1
371   ret   c
372 BYTE0:
373   ld    a,c
374   jr    BYTE2
376 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
378 ;; GROUP 8 - IN
379 ;; GROUP 9 - OUT
381 GROUP8:
382   sub   OPC_COUNT_GROUPS_8_AND_9
383   jr    nc,GROUPA
384   cp    OPC_COUNT_GROUP_8-OPC_COUNT_GROUPS_8_AND_9
385   call  z,CORN
386   ex    af,af'
387   call  REGHI
388   ret   c
389   ex    af,af'
390   call  c,CORN
391   inc   h
392   jr    z,BYTE0
393   ld    a,b
394   cp    7
395   scf
396   ret   nz
397   ld    a,c
398   xor   3
399   rlca
400   rlca
401   rlca
402   call  BYTE
403   jr    VAL8
405 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
407 ;; GROUP 10 - JR, DJNZ
409 GROUPA:
410   sub   OPC_COUNT_GROUP_10
411   jr    nc,GROUPB
412   cp    OPC_COUNT_GROUP_10_JRS-OPC_COUNT_GROUP_10
413   call  nz,COND
414   ld    a,c
415   jr    nc,GRPA
416   ld    a,18H
417 GRPA:
418   call  BYTE
419   call  NUMBER
420   ld    de,(PC)
421   inc   de
422   scf
423   sbc   hl,de
424   ld    a,l
425   rla
426   sbc   a,a
427   cp    h
428 TOOFAR:
429   call  nz,JR_TOO_FAR
430 VAL8:
431   ld    a,l
432   jr    BYTE2
434 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
436 ;; GROUP 11 - JP
438 GROUPB:
439   ld    b,a
440   jr    nz,GROUPC
441   call  COND
442   ld    a,c
443   jr    nc,GRPB
444   ld    a,b
445   and   3FH
446   cp    6
447   ld    a,0E9H
448   jr    z,BYTE2
449   ld    a,0C3H
450 GRPB:
451   call  BYTE
452   jr    ADDR
454 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
456 ;; GROUP 12 - CALL
458 GROUPC:
459   djnz  GROUPD
460 GRPC:
461   call  GRPE
462 ADDR:
463   call  NUMBER
464 VAL16:
465   call  VAL8
466   ld    a,h
467   jr    BYTE2
469 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
471 ;; GROUP 13 - RST
473 GROUPD:
474   djnz  GROUPE
475   call  NUMBER
476   and   c
477   or    h
478   jr    nz,TOOFAR
479   ld    a,l
480   or    c
481 BYTE2:
482   jr    BYTE1
484 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
486 ;; GROUP 14 - RET
488 GROUPE:
489   djnz  GROUPF
490 GRPE:
491   call  COND
492   ld    a,c
493   jr    nc,BYTE1
494   or    9
495   jr    BYTE1
497 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
499 ;; GROUP 15 - LD
501 GROUPF:
502   djnz  MISC
503   call  LDOP
504   jr    nc,LDA
505   call  REGHI
506   ex    af,af'
507   call  SKIP
508   cp    '('
509   jr    z,LDIN
510   ex    af,af'
511   jp    nc,G6
512   ld    c,1
513   call  PAIR1
514   ret   c
515   ld    a,14
516   cp    b
517   ld    b,a
518   call  z,PAIR
519   ld    a,b
520   and   3FH
521   cp    12
522   ld    a,c
523   jr    nz,GRPB
524   ld    a,0F9H
525   jr    BYTE1
526   ;
527 LDIN:
528   ex    af,af'
529   push  bc
530   call  nc,REGLO
531   ld    a,c
532   pop   bc
533   jr    nc,BIND
534   ld    c,0AH
535   call  PAIR1
536   call  LD16
537   jr    nc,GRPB
538   call  NUMBER
539   ld    c,2
540   call  PAIR
541   call  LD16
542   ret   c
543   call  BYTE
544   jr    VAL16
546 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
547 ;; misc. instructions
548 MISC:
549   jp    MISC_DEFB
552 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
554 ;; SUBROUTINES
557 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
559 JR_TOO_FAR:
560   ld    hl,(ASM_JR_TOO_FAR_CB)
561   jp    (hl)
563 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
565 LDA:
566   cp    4
567   call  c,EDX
568   ld    a,b
569 BYTE1:
570   jr    BYTE
572 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
574 LD16:
575   ld    a,b
576   jr    c,LD8
577   ld    a,b
578   and   3FH
579   cp    12
580   ld    a,c
581   ret   z
582   call  EDX
583   ld    a,c
584   or    43H
585   ret
587 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
589 LD8:
590   cp    7
591   scf
592   ret   nz
593   ld    a,c
594   or    30H
595   ret
597 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
599 CORN:
600   push  bc
601   call  OPND
602   bit   5,b
603   pop   bc
604   jr    z,NUMBER
605   ld    h,-1
606 EDX:
607   ld    a,0EDH
608   jr    BYTE
610 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
612 CBX:
613   ld    a,0CBH
614 BIND:
615   cp    76H
616   scf
617   ret   z               ;REJECT LD (HL),(HL)
618   call  BYTE
619   inc   d
620   ret   p
621   ld    a,e
622   jr    BYTE
624 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
626 OPND:
627   push  hl
628   ld    hl,OPRNDS
629   call  FIND
630   pop   hl
631   ret   c
632   bit   7,b
633   ret   z
634   bit   3,b
635   push  hl
636   call  z,OFFSET
637   ld    e,l
638   pop   hl
639   ld    a,0DDH
640   bit   6,b
641   jr    z,OP1
642   ld    a,0FDH
643 OP1:
644   or    a
645   inc   d
646   ld    d,a
647   ret   m
648 BYTE:
649   ld    (ix),a
650   inc   ix
651   or    a
652   ret
654 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
656 OFFSET:
657   ld    a,(iy)
658   cp    ')'
659   ld    hl,0
660   ret   z
662 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
663 ;; parse numeric expression
664 NUMBER:
665   call  SKIP
666   push  bc
667   push  de
668   push  ix
669   call  PARSE_INT_EXPR
670   ; HL: expression value
671   pop   ix
672   pop   de
673   pop   bc
674   ld    a,l
675   or    a
676   ret
678 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
680 REG:
681   call  OPND
682   ret   c
683   ld    a,b
684   and   3FH
685   cp    8
686   ccf
687   ret
689 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
691 REGLO:
692   call  REG
693   ret   c
694   jr    ORC
696 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
698 REGHI:
699   call  REG
700   ret   c
701   jr    SHL3
703 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
705 COND:
706   call  OPND
707   ret   c
708   ld    a,b
709   and   1FH
710   sub   16
711   jr    nc,SHL3
712   cp    -15
713   scf
714   ret   nz
715   ld    a,3
716   jr    SHL3
718 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
720 PAIR:
721   call  OPND
722   ret   c
723 PAIR1:
724   ld    a,b
725   and   0FH
726   sub   8
727   ret   c
728   jr    SHL3
730 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
732 CBIT:
733   call  NUMBER
734   cp    8
735   ccf
736   ret   c
737 SHL3:
738   rlca
739   rlca
740   rlca
741 ORC:
742   or    c
743   ld    c,a
744   ret
747 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
749 ;; common defb/defw code
750 ;;   carry set if we want words
752 COM_DEFBW:
753   push  af
754   push  ix
755   call  PARSE_INT_EXPR
756   pop   ix
757   ; HL: value
758   ld    (ix),l
759   inc   ix
760   pop   af
761   jr    nc,COM_DEFBW_BYTE
762   ld    (ix),h
763   inc   ix
764 COM_DEFBW_BYTE:
765   ex    af,af'
766   ; check for comma
767   call  SKIP
768   ret   z
769   ex    af,af'
770   jr    COM_DEFBW
772 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
774 ;; DEFB
776 MISC_DEFB:
777   djnz  MISC_DEFW
778 DEFB_LOOP:
779   or    a
780   jr    COM_DEFBW
782 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
784 ;; DEFW
786 MISC_DEFW:
787   djnz  MISC_DEFM
788 DEFW_LOOP:
789   scf
790   jr    COM_DEFBW
792 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
794 ;; DEFM
796 MISC_DEFM:
797   djnz  MISC_ORG
798 DEFM_LOOP:
799   push  ix
800   call  PARSE_STR_EXPR
801   pop   ix
802   ; HL: buffer address
803   ;  E: buffer length
804   xor   a
805   cp    e
806   jr    z,DEFM1_DONE_ONE
807 DEFM1:
808   ld    a,(hl)
809   inc   hl
810   call  BYTE
811   dec   e
812   jr    nz,DEFM1
813 DEFM1_DONE_ONE:
814   ; check for comma
815   call  SKIP
816   ret   z
817   jr    DEFM_LOOP
819 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
821 ;; ORG
823 MISC_ORG:
824   IF @BZ80ASM_ORGENTDISP
825   djnz  MISC_DISP
826   ld    a,ORGENT_ORG
827 COM_ORGENT:
828   ld    (ORGENT_TYPE),a
829   call  NUMBER
830   ld    (NEW_ORGENT),hl
831   or    a
832   ret
834 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
836 ;; DISP
838 MISC_DISP:
839   djnz  MISC_ENT
840   ld    a,ORGENT_DISP
841   jr    COM_ORGENT
843 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
845 ;; ENT
847 MISC_ENT:
848   djnz  MISC_WTF
849   ld    a,ORGENT_ENT
850   jr    COM_ORGENT
852 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
854 ;; error (the thing that should not be)
856 MISC_WTF:
857   ENDIF
858   ; set "unknown instruction index"
859   ; b may be zero here
860   ld    a,b
861   inc   a
862   ld    (ASM_BAD_B),a
863   scf
864   ret
867 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
868 ;; search the table for a token from the input buffer
869 ;; skips leading delimiters, and the token itself (if found)
870 ;; tokens in the table must have bit 7 set on the last char
871 ;; table ends with zero byte instead of first token char
872 ;; each token is followed by a data byte
873 ;; IN:
874 ;;   IY: text buffer
875 ;; OUT:
876 ;;   IY: new position in the text buffer
877 ;;    A: token number
878 ;;    B: token data byte
879 ;;   carry flag set on error (invalid char, or token not found)
880 ;;   on error, delimiters are still skipped
882 LDOP:
883   ld    hl,LDOPS
884 ; main table search entry point
885 FIND:
886   call  SKIP
887 EXIT:
888   ld    b,0
889   scf
890   ret   z
891   ; reject chars with high bit set
892   cp    128
893   ccf
894   ret   c
896 FIND0:
897   ; check the first char
898   ld    a,(hl)
899   or    a
900   jr    z,EXIT
901   xor   (iy)
902   and   %01011111   ; for case-insensitivity
903   jr    z,FIND2
904   ; first char is not matched, skip this token
905 FIND1:
906   bit   7,(hl)
907   inc   hl
908   jr    z,FIND1
909   ; skip token data byte
910   inc   hl
911   ; increment token counter
912   inc   b
913   jr    FIND0
915   ; first char matched, check the rest
916   ; A holds zero (due to xor/and)
917 FIND2:
918   push  iy
919 FIND3:
920   ; last token char?
921   bit   7,(hl)
922   inc   iy
923   inc   hl
924   jr    nz,FIND5
925   ; not the last token char
926   ; this compares (HL) with 0, because
927   ; A is guaranteed to hold zero here
928   cp    (hl)
929   ; zero byte in token means "skip delimiters"
930   ; it is used in some opcodes with fixed operands
931   call  z,SKIP0
932   ; load token char
933   ld    a,(hl)
934   ; compare with input char
935   xor   (iy)
936   and   %01011111   ; for case-insensitivity
937   jr    z,FIND3
939   ; alas, doesn't match
940 FIND4:
941   ; restore input stream pointer
942   pop   iy
943   ; and skip this token
944   jr    FIND1
946   ; we'll come here if we succesfully matched a token
947 FIND5:
948   ; if it isn't followed by a delimiter, '+' or '-', this is not a valid token
949   call  DELIM
950   call  nz,SIGN
951   jr    nz,FIND4
953   ; this token is valid
954 FIND6:
955   ; move token index to A
956   ld    a,b
957   ; load B with token data byte
958   ld    b,(hl)
959   ; drop original input stream position
960   pop   hl
961   ; we're done here
962   ; note that carry flag is guaranteed to be reset
963   ret
966 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
967 ;; used by FIND
968 SIGN:
969   cp    '+'
970   ret   z
971   cp    '-'
972   ret
974 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
975 ;; this entry point is used by FIND
976 SKIP0:
977   inc   hl
978 ;; this entry point is used to skip blanks and delimiters
979 ;; note that comma and right paren are considered blanks too
980 ;; as a consequence, operands may be delimited by spaces, or
981 ;; right parens, lol
982 ;; returns current char in A (and IY pointing to it)
983 ;; zero flag set if we hit a terminator
984 ;; zero flag reset if we hit a non-delimiter
985 SKIP:
986   ; delimiter or terminator?
987   call  DELIM
988   ret   nz
989   ; if this is terminator, still stop
990   call  TERM
991   ret   z
992   ; this is delimiter, skip it
993   inc   iy
994   jr    SKIP
996 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
997 ;; used by FIND and SKIP
998 ;; check if the current char is a delimiter or a terminator
999 ;; zero flag set on delimiter/terminator
1000 DELIM:
1001   ld    a,(iy)          ;ASSEMBLER DELIMITER
1002   cp    ' '
1003   ret   z
1004   cp    ','
1005   ret   z
1006   cp    ')'
1007   ret   z
1009 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1010 ;; entry point for SKIP
1011 TERM:
1012   cp    ';'             ;ASSEMBLER TERMINATOR
1013   ret   z
1014   ;cp    '\'
1015   ;ret   z
1017 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1018 ;; also used by assembler to check for command separator
1019 ;; the assembler itself does nothing with separators
1020 ASM_IS_SEP:
1021   cp    ':'             ;ASSEMBLER SEPARATOR
1022   ret   nc
1023   cp    CR
1024   ret
1026 csizest = $-csizest
1027 $printf "assembler size: %d", csizest
1030 csizest = $
1032 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1033 ;; parse integer number (without sign)
1034 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1036 ;; this is advanced number parser
1037 ;; it understands alot of suffixes and prefixes:
1038 ;;   $     -- lone "$" means "current PC"
1039 ;;   #nnnn -- hex
1040 ;;   $nnnn -- hex
1041 ;;   &nnnn -- hex
1042 ;;   %nnnn -- binary
1043 ;;   0Xnnn -- hex
1044 ;;   0Onnn -- octal
1045 ;;   0Bnnn -- binary
1046 ;;   nnnnH -- hex
1047 ;;   nnnnB -- binary
1048 ;;   nnnnO -- octal
1049 ;; everything is case-insensitive
1050 ;; you can separate digits with underscores
1051 ;; (i.e. "12_34_5" will work, underscores are simply ignored)
1054 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1056 ;; parse a number, push it onto the stack
1057 ;; understands prefixes and suffixes
1059 ;; IN:
1060 ;;   IY: text buffer
1061 ;; OUT:
1062 ;;   IY: text buffer after the expression
1063 ;;   HL: number
1064 ;;   carry flag reset
1065 ;;  OR: (cannot parse as a number)
1066 ;;   IY: unchanged
1067 ;;   carry flag set
1068 ;;  DE,AF,flags: dead
1070 PARSE_NUMBER:
1071   call  PARSER_SKIP_BLANKS
1072   scf
1073   ret   z
1074   push  iy          ; we will need to rollback on error
1075   ; A already contains a char, loaded by `PARSER_SKIP_BLANKS`
1076   cp    '#'
1077   jr    z,.hexprefix
1078   cp    '$'
1079   jr    z,.maybe_lone_dollar
1080   cp    '&'
1081   jr    z,.hexprefix
1082   cp    '%'
1083   jr    z,.binprefix
1084   ; no, leading zero doesn't mean "octal", this is stupid
1085   ; but we may have prefixes like "0x" and such
1086   cp    '0'
1087   jr    z,.maybe_zero_prefix
1088   ; check if we have a digit here
1089   call  PARSER_CONV_DIGIT
1090   jr    c,.not_a_number_carry_set
1091   cp    10
1092   ; nope, do not allow it, all numbers must start with a digit
1093   ;jr    nc,.must_be_hex_with_sfx
1094   ccf
1095   jr    c,.not_a_number_carry_set
1096 .do_normal_decimal:
1097   ; done with prefixes, try decimal number
1098   ; we'll switch to suffix checking on hex digit
1099   ld    hl,0        ; accumulator
1100 .decimal_loop:
1101   call  .getDigit
1102   jr    c,.decimal_done
1103   cp    10
1104   jr    nc,.must_be_hex_with_sfx
1105   ; HL=HL*10
1106   add   hl,hl
1107   ld    de,hl
1108   add   hl,hl
1109   add   hl,hl
1110   add   hl,de
1111   ; HL=HL+A
1112   ld    e,a
1113   ld    d,0
1114   add   hl,de
1115   ; next char
1116   inc   iy
1117   jr    .decimal_loop
1118 .decimal_done:
1119   ; check for suffix
1120   ld    a,(iy)
1121   and   %11011111   ; cheap uppercase
1122   cp    'H'
1123   jr    z,.must_be_hex_with_sfx
1124   cp    'B'
1125   jr    z,.bin_with_sfx
1126   cp    'O'
1127   jr    z,.oct_with_sfx
1128   ; no suffix, we're done
1130 .success:
1131   pop   de          ; drop iy
1132   ; reset carry flag
1133   or    a
1134   ret
1136 .not_a_number_carry_set:
1137   pop   iy
1138   ret
1140 .hexprefix:
1141   ; skip prefix
1142   inc   iy
1143   call  .parse_as_hex
1144 .after_prefix:
1145   jr    c,.not_a_number_carry_set
1146   jr    .success
1148 .maybe_lone_dollar:
1149   ; lone dollar means "PC"
1150   inc   iy
1151   call  .parse_as_hex
1152   ; the only case we may gen an error here is
1153   ; when our dollar isn't followed by a digit
1154   jr    nc,.success
1155   ; lone dollar is good too
1156   ; IY points right after the dollar here
1157   ld    hl,(BZ80ASM.PC)
1158   jr    .success
1160 .binprefix:
1161   ; skip prefix
1162   inc   iy
1163   call  .parse_as_bin
1164   jr    .after_prefix
1166 .maybe_binprefix:
1167   ; things like "0BEEFh" should be parsed as hex
1168   ; skip prefix
1169   inc   iy
1170   call  .parse_as_bin
1171   jr    c,.must_be_hex_with_sfx
1172   ; check for 'H'
1173   ld    a,(iy)
1174   and   %11011111   ; cheap uppercase
1175   cp    'H'
1176   jr    z,.must_be_hex_with_sfx
1177   jr    .success
1179 .octprefix:
1180   ; skip prefix
1181   inc   iy
1182   call  .parse_as_oct
1183   jr    .after_prefix
1185 .maybe_zero_prefix:
1186   ; check for '0x' and such
1187   ; skip '0'
1188   inc   iy
1189   ; load and prefix
1190   ; there's no need to skip it, as it will be
1191   ; skipped by the corresponding subroutine
1192   ld    a,(iy)
1193   ; so IY will point to the actual number
1194   and   %11011111   ; cheap uppercase
1195   cp    'X'
1196   jr    z,.hexprefix
1197   cp    'B'
1198   jr    z,.maybe_binprefix
1199   cp    'O'
1200   jr    z,.octprefix
1201   ; do not reparse '0', no need to backup
1202   jr    .do_normal_decimal
1204 .must_be_hex_with_sfx:
1205   ; reparse as hex, and check for suffix
1206   pop   iy
1207   push  iy
1208   call  .parse_as_hex
1209   jr    c,.not_a_number_carry_set
1210   ld    a,(iy)
1211   inc   iy
1212   and   %11011111   ; cheap uppercase
1213   cp    'H'
1214   jr    z,.success
1216 .bin_with_sfx:
1217   ; reparse as bin, skip suffix (it is guaranteed to be there)
1218   pop   iy
1219   push  iy
1220   call  .parse_as_bin
1221 .done_guaranteed_suffix:
1222   jr    c,.not_a_number_carry_set
1223   ; skip suffix
1224   inc   iy
1225   jr    .success
1227 .oct_with_sfx:
1228   ; reparse as bin, skip suffix (it is guaranteed to be there)
1229   pop   iy
1230   push  iy
1231   call  .parse_as_bin
1232   jr    .done_guaranteed_suffix
1234 .parse_as_hex:
1235   ld    hl,0        ; accumulator
1236   ; check first digit (as this is general parser)
1237   call  .getDigitNoUnder
1238   ret   c
1239 .parse_as_hex_loop:
1240   inc   iy
1241   add   hl,hl
1242   add   hl,hl
1243   add   hl,hl
1244   add   hl,hl
1245   ld    e,a
1246   ld    d,0
1247   add   hl,de
1248   call  .getDigit
1249   jr    nc,.parse_as_hex_loop
1250   ; clear carry flag (it is always set here)
1251   ccf
1252   ret
1254 .parse_as_bin:
1255   ld    hl,0        ; accumulator
1256   ; check first digit (as this is general parser)
1257   ld    a,(iy)
1258   call  .getDigitNoUnderBin
1259   ret   c
1260 .parse_as_bin_loop:
1261   inc   iy
1262   add   hl,hl
1263   ld    e,a
1264   ld    d,0
1265   add   hl,de
1266   call  .getBinDigit
1267   jr    nc,.parse_as_bin_loop
1268   ; clear carry flag (it is always set here)
1269   ccf
1270   ret
1272 .parse_as_oct:
1273   ld    hl,0        ; accumulator
1274   ; check first digit (as this is general parser)
1275   ld    a,(iy)
1276   call  .getDigitNoUnderOct
1277 .parse_as_oct_loop:
1278   inc   iy
1279   add   hl,hl
1280   add   hl,hl
1281   add   hl,hl
1282   ld    e,a
1283   ld    d,0
1284   add   hl,de
1285   call  .getOctDigit
1286   jr    nc,.parse_as_oct_loop
1287   ; clear carry flag (it is always set here)
1288   ccf
1289   ret
1292 .getDigit_inc:
1293   inc   iy
1294 .getDigit:
1295   ld    a,(iy)
1296   cp    '_'
1297   jr    z,.getDigit_inc
1298   jp    PARSER_CONV_DIGIT
1300   ; d: base
1301 .getDigitInBase:
1302   call  .getDigit
1303   ret   c
1304   cp    d
1305   ccf
1306   ret
1308 .getDigitNoUnder:
1309   ld    a,(iy)
1310   jp    PARSER_CONV_DIGIT
1312 .getDecDigit:
1313   ld    d,10
1314   jr    .getDigitInBase
1316 .getDigitNoUnderOct:
1317   ld    a,(iy)
1318 .getOctDigit:
1319   ld    d,8
1320   jr    .getDigitInBase
1322 .getDigitNoUnderBin:
1323   ld    a,(iy)
1324 .getBinDigit:
1325   ld    d,2
1326   jr    .getDigitInBase
1329 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1330 ;; converts 'A' to digit (assume hex)
1331 ;; carry set: not a digit char (and A is destroyed)
1333 PARSER_CONV_DIGIT:
1334   sub   '0'
1335   ret   c
1336   cp    10
1337   ccf
1338   ret   nc
1339   add   a,'0'
1340   and   %11011111   ; cheap uppercase
1341   sub   'A'-10
1342   ret   c
1343   cp    16
1344   ccf
1345   ret
1347 csizest = $-csizest
1348 $printf "assembler numparse size: %d", csizest
1351 csizest = $
1353 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1354 ;; math expression parser
1355 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1357 PARSE_EXPR_ERROR_A:
1358   ld    hl,(EXPR_ERROR_CB)
1359   jp    (hl)
1361 PARSE_EXPR_ERROR_INT:
1362   ld    a,EXPR_ERR_NUMBER_EXPECTED
1363   jr    PARSE_EXPR_ERROR_A
1365 PARSE_EXPR_ERROR_STR:
1366   ld    a,EXPR_ERR_STRING_EXPECTED
1367   jr    PARSE_EXPR_ERROR_A
1369 PARSE_EXPR_ERROR_0DIV:
1370   ld    a,EXPR_ERR_DIVISION_BY_ZERO
1371   jr    PARSE_EXPR_ERROR_A
1373 PARSE_EXPR_ERROR_RPAREN:
1374   ld    a,EXPR_ERR_RPAREN_EXPECTED
1375   jr    PARSE_EXPR_ERROR_A
1378 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1380 ;; parse an integer expression
1381 ;; IN:
1382 ;;   IY: text buffer
1383 ;; OUT:
1384 ;;   IY: text buffer after the expression
1385 ;;   HL: expression value
1386 ;;   everything other (including all alternate registers) is dead
1388 ;; priorities:
1389 ;;   unaries
1390 ;;   * / %
1391 ;;   + -
1392 ;;   << >>
1394 PARSE_INT_EXPR:
1395   call  PARSER_SKIP_BLANKS
1396   jp    z,PARSE_EXPR_ERROR_INT
1397   ; unary "+" or "-"?
1398   call  BZ80ASM.SIGN
1399   jr    nz,.doexpr
1400   push  af
1401   inc   iy
1402   call  .doexpr
1403   pop   af
1404   ; check for negate
1405   cp    '-'
1406   ret   nz
1407   ; negate HL
1408   or    a
1409   ld    de,0
1410   ex    de,hl
1411   sbc   hl,de
1412   ret
1414 .doexpr:
1415 ;; |
1416 .bitor:
1417   ; get first operand
1418   ; already done above
1419   call  .bitxor
1420 .bitor_next:
1421   ; check for operation
1422   call  PARSER_SKIP_BLANKS
1423   ret   z       ; exit on EOL
1424   cp    '|'
1425   ret   nz
1426   ; get second operand
1427   ld    bc,.bitxor
1428   call  .go_down_bc
1429   ld    a,l
1430   or    e
1431   ld    l,a
1432   ld    a,h
1433   or    d
1434   ld    h,a
1435   jr    .bitor_next
1437 ;; ^
1438 .bitxor:
1439   ; get first operand
1440   call  .bitand
1441 .bitxor_next:
1442   ; check for operation
1443   call  PARSER_SKIP_BLANKS
1444   ret   z       ; exit on EOL
1445   cp    '^'
1446   ret   nz
1447   ; get second operand
1448   ld    bc,.bitand
1449   call  .go_down_bc
1450   ld    a,l
1451   xor   e
1452   ld    l,a
1453   ld    a,h
1454   xor   d
1455   ld    h,a
1456   jr    .bitxor_next
1458 ;; &
1459 .bitand:
1460   ; get first operand
1461   call  .shlshr
1462 .bitand_next:
1463   ; check for operation
1464   call  PARSER_SKIP_BLANKS
1465   ret   z       ; exit on EOL
1466   cp    '&'
1467   ret   nz
1468   ; get second operand
1469   ld    bc,.shlshr
1470   call  .go_down_bc
1471   ld    a,l
1472   and   e
1473   ld    l,a
1474   ld    a,h
1475   and   d
1476   ld    h,a
1477   jr    .bitand_next
1479 ;; << >>
1480 .shlshr:
1481   call  .addsub
1482 .shlshr_next:
1483   ; check for operation
1484   call  PARSER_SKIP_BLANKS
1485   ret   z       ; exit on EOL
1486   ; (iy+0) and (iy+1) should be equal
1487   cp    (iy+1)
1488   ret   nz
1489   cp    '<'     ; %0011_1100
1490   jr    z,.doshift
1491   cp    '>'     ; %0011_1110
1492   ret   nz
1493 .doshift:
1494   ; get second operand
1495   inc   iy      ; skip operation part
1496   ld    bc,.addsub
1497   call  .go_down_bc
1498   ex    af,af'
1499   ; HL: number to shift
1500   ; DE: amount
1501   ld    a,d
1502   or    a
1503   jr    nz,.shift_too_far
1504   ld    a,e
1505   cp    16
1506   jr    nc,.shift_too_far
1507   ld    b,a
1508   ex    af,af'
1509   cp    '<'
1510   jr    z,.do_shl
1511   ; shr
1512 .do_shr_loop:
1513   srl   h
1514   rr    l
1515   djnz  .do_shr_loop
1516   jr    .shlshr_next
1517   ; shl
1518 .do_shl:
1519   ; shl
1520   sla   l
1521   rl    h
1522   djnz  .do_shl
1523   jr    .shlshr_next
1524 .shift_too_far:
1525   ld    hl,0
1526   jr    .shlshr_next
1528 ;; + -
1529 .addsub:
1530   ; get first operand
1531   call  .muldiv
1532 .addsub_next:
1533   ; check for operation
1534   call  PARSER_SKIP_BLANKS
1535   ret   z       ; exit on EOL
1536   cp    '+'     ; %0010_1011
1537   jr    z,.doaddsub
1538   cp    '-'     ; %0010_1101
1539   ret   nz
1540 .doaddsub:
1541   ; get second operand
1542   ld    bc,.muldiv
1543   call  .go_down_bc
1544   cp    '-'
1545   jr    z,.dosub
1546   add   hl,de
1547   jr    .addsub_next
1548 .dosub:
1549   sbc   hl,de
1550   jr    .addsub_next
1552 ;; * / %
1553 .muldiv:
1554   ; get first operand
1555   call  .term
1556 .muldiv_next:
1557   ; check for operation
1558   call  PARSER_SKIP_BLANKS
1559   ret   z       ; exit on EOL
1560   cp    '*'     ; %0010_1010
1561   jr    z,.domuldiv
1562   cp    '/'     ; %0010_1111
1563   jr    z,.domuldiv
1564   cp    '%'     ; %0010_0101
1565   ret   nz
1566 .domuldiv:
1567   ; get second operand
1568   ld    bc,.term
1569   call  .go_down_bc
1570   ld    bc,hl
1571   cp    '*'
1572   jr    z,.domul
1573   ex    af,af'  ; save operation
1574   ; div or mod
1575   ld    a,e
1576   or    d
1577   jp    z,PARSE_EXPR_ERROR_0DIV
1578   call  PARSER_UDIV_BC_DE
1579   ; was it div or mod?
1580   ex    af,af'
1581   cp    '%'
1582   jr    z,.muldiv_next  ; remainder already in hl
1583   ; division
1584   ld    hl,bc
1585   jr    .muldiv_next
1586 .domul:
1587   call  PARSER_UMUL_BC_DE
1588   jr    .muldiv_next
1590 ;; parse term, also process unaries and parens
1591 .term:
1592   call  PARSER_SKIP_BLANKS
1593   jp    z,PARSE_EXPR_ERROR_INT
1594   inc   iy      ; skip operation
1595   ld    c,')'
1596   cp    '('
1597   jr    z,.term_lparen
1598   cp    '['
1599   ld    c,']'
1600   jr    z,.term_lparen
1601   cp    '~'
1602   jr    z,.term_ubitnot
1603   ; this must be number
1604   dec   iy      ; undo skip
1605   call  PARSE_NUMBER
1606   ret   nc
1607   ; check for labels
1608   push  iy      ; for correct error position
1609   push  ix      ; the only register we care about
1610   ld    hl,.term_checklabel_ret
1611   push  hl
1612   ld    hl,(GETLABEL_CB)
1613   jp    (hl)
1614 .term_checklabel_ret:
1615   pop   ix
1616   pop   de
1617   ret   nc
1618   ; oops, error
1619   ; restore position for correct error reporting
1620   push  de
1621   pop   iy
1622   jp    PARSE_EXPR_ERROR_INT
1624   ;; "("
1625 .term_lparen:
1626   ;; C contains matching rparen
1627   push  bc
1628   call  PARSE_INT_EXPR
1629   call  PARSER_SKIP_BLANKS
1630   jp    z,PARSE_EXPR_ERROR_RPAREN
1631   pop   bc
1632   cp    c
1633   jp    nz,PARSE_EXPR_ERROR_RPAREN
1634   inc   iy
1635   ret
1637   ;; "~"
1638 .term_ubitnot:
1639   call  .term
1640   ld    a,h
1641   cpl
1642   ld    h,a
1643   ld    a,l
1644   cpl
1645   ld    l,a
1646   ret
1648   ;; call subroutine at BC
1649   ;; AF: preserved
1650   ;; HL: preserved
1651   ;; DE: subroutine result
1652   ;; i.e. HL is op0, DE is op1
1653 .go_down_bc:
1654   push  hl
1655   push  af      ; A holds operation
1656   inc   iy      ; skip operation
1657   ld    hl,.go_down_bc_ret
1658   push  hl
1659   push  bc
1660   ret
1661 .go_down_bc_ret:
1662   pop   af
1663   pop   de
1664   ex    de,hl
1665   ret
1668 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1670 ;; HL=BC*DE
1672 ;; BC,DE,A,flags: dead
1674 PARSER_UMUL_BC_DE:
1675   ; DEHL=BC*DE
1676   ld    hl,0
1677   ld    a,16
1678 .loop:
1679   add   hl,hl
1680   rl    e
1681   rl    d
1682   jr    nc,.skip
1683   add   hl,bc
1684   jr    nc,.skip
1685   inc   de
1686 .skip:
1687   dec   a
1688   jr    nz,.loop
1689   ret
1692 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1694 ;; performs BC/DE
1695 ;; OUT:
1696 ;;   BC: quotient
1697 ;;   HL: remainder
1699 ;; DE,A,flags: dead
1701 PARSER_UDIV_BC_DE:
1702   ld    hl,0
1703   ld    a,16
1704 .loop:
1705   sll   c
1706   rl    b
1707   adc   hl,hl
1708   sbc   hl,de
1709   jr    nc,.skip
1710   add   hl,de
1711   dec   c
1712 .skip:
1713   dec   a
1714   jr    nz,.loop
1715   ret
1718 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1720 ;; parse a string expression
1722 ;; IN:
1723 ;;   IY: text buffer
1724 ;; OUT:
1725 ;;   HL: string buffer start
1726 ;;    E: parsed string length
1727 ;;   everything other (including all alternate registers) is dead
1729 PARSE_STR_EXPR:
1730   call  PARSER_SKIP_BLANKS
1731   jp    z,PARSE_EXPR_ERROR_STR
1732   cp    34
1733   jr    z,.strok
1734   cp    39
1735   jp    nz,PARSE_EXPR_ERROR_STR
1736 .strok:
1737   ld    c,a  ; terminator
1738   inc   iy
1739   ; remember buffer start
1740   push  iy
1741 .strloop:
1742   ld    a,(iy)
1743   or    a
1744   jp    z,PARSE_EXPR_ERROR_STR
1745   cp    CR
1746   jp    z,PARSE_EXPR_ERROR_STR
1747   inc   iy
1748   cp    c
1749   jr    nz,.strloop
1750   ; done string parsing, calc length
1751   pop   hl  ; buffer start
1752   ex    de,hl
1753   push  iy
1754   pop   hl
1755   ; DE: buffer start
1756   ; HL: buffer end
1757   or    a
1758   sbc   hl,de
1759   ex    de,hl
1760   ret
1763 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1764 ;; skip blanks
1765 ;; returns current char in A
1766 ;; sets zero flag on EOL
1767 ;; IN:
1768 ;;   IY: text buffer
1769 ;; OUT:
1770 ;;   IY: text buffer at non-blank or EOL
1771 ;;    A: non-blank or EOL char
1772 ;;   zero flag is set on EOL
1774 PARSER_SKIP_BLANKS:
1775   ld    a,(iy)
1776   or    a
1777   ret   z
1778   cp    13
1779   ret   z
1780   inc   iy
1781   cp    33
1782   jr    c,PARSER_SKIP_BLANKS
1783   dec   iy
1784   ; reset zero flag
1785   or    a
1786   ret
1788 csizest = $-csizest
1789 $printf "assembler exprparse size: %d", csizest
1792 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1793 ;; various tables -- mnemonics, operands...
1795 csizest = $
1797 ;; number of "trivial" opcodes without any special processing
1798 OPC_COUNT_GROUPS_0_AND_1 equ 39
1799 ;; number of non-ED-prefixed instructions in "trivial"
1800 OPC_COUNT_GROUP_0 equ 15
1802 ;; total number of CB-prefixed instructions (GROUPS 2 and 3)
1803 OPC_COUNT_GROUPS_2_AND_3 equ 10
1804 ;; number of direct bit manipulation instructions in CB list (GROUP 2)
1805 OPC_COUNT_GROUP_2 equ 3
1807 ;; push, pop, ex (sp),rr
1808 OPC_COUNT_GROUP_4 equ 3
1810 ;; ALU with accum
1811 OPC_COUNT_GROUP_5 equ 5
1812 ;; ALU with accum
1813 OPC_COUNT_GROUP_6 equ 3
1814 ;; GROUPS 5 and 6
1815 OPC_COUNT_GROUPS_5_AND_6 equ OPC_COUNT_GROUP_5+OPC_COUNT_GROUP_6
1817 ;; INC/DEC
1818 OPC_COUNT_GROUP_7 equ 2
1820 ;; IN
1821 OPC_COUNT_GROUP_8 equ 1
1822 ;; OUT
1823 OPC_COUNT_GROUP_9 equ 1
1824 ;; GROUPS 8 and 9
1825 OPC_COUNT_GROUPS_8_AND_9 equ OPC_COUNT_GROUP_8+OPC_COUNT_GROUP_9
1827 ; JR,DJNZ
1828 OPC_COUNT_GROUP_10 equ 2
1829 OPC_COUNT_GROUP_10_JRS equ 1
1832 ;; WARNING! the assembler has some hard-coded mnemonics counts
1833 ;;          scattered across the code, so don't mess with the tables!
1834 ;;          i may document this in the future, but for now, leave it as it is.
1835 ;; mnemonics
1836 OPCODS:
1837   ; GROUPS 0 AND 1: "trivial" (39, OPC_COUNT_GROUPS_0_AND_1)
1838   ; GROUP 0: "trivial" one-byte (15, OPC_COUNT_GROUP_0)
1839   defx  "NOP"
1840   defb  0
1841   defx  "RLCA"
1842   defb  7
1843   defm  "EX",0,"AF",0
1844     defx "AF'"
1845   defb  8
1846   defx  "RRCA"
1847   defb  #0F
1848   defx  "RLA"
1849   defb  #17
1850   defx  "RRA"
1851   defb  #1F
1852   defx  "DAA"
1853   defb  #27
1854   defx  "CPL"
1855   defb  #2F
1856   defx  "SCF"
1857   defb  #37
1858   defx  "CCF"
1859   defb  #3F
1860   defx  "HALT"
1861   defb  #76
1862   defx  "EXX"
1863   defb  #D9
1864   defm  "EX",0,"DE",0
1865     defx "HL"
1866   defb  #EB
1867   defx  "DI"
1868   defb  #F3
1869   defx  "EI"
1870   defb  #FB
1871   ; GROUP 1: "trivial" ED-prefixed (24, OPC_COUNT_GROUPS_0_AND_1-OPC_COUNT_GROUP_0)
1872   defx  "NEG"
1873   defb  #44
1874   defm  "IM",0
1875     defx  "0"
1876   defb  #46
1877   defx  "RETN"
1878   defb  #45
1879   defx  "RETI"
1880   defb  #4D
1881   defm  "IM",0
1882     defx  "1"
1883   defb  #56
1884   defm  "IM",0
1885     defx  "2"
1886   defb  #5E
1887   defx  "RRD"
1888   defb  #67
1889   defx  "RLD"
1890   defb  #6F
1891   defx  "LDI"
1892   defb  #A0
1893   defx  "CPI"
1894   defb  #A1
1895   defx  "INI"
1896   defb  #A2
1897   defx  "OUTI"
1898   defb  #A3
1899   defx  "LDD"
1900   defb  #A8
1901   defx  "CPD"
1902   defb  #A9
1903   defx  "IND"
1904   defb  #AA
1905   defx  "OUTD"
1906   defb  #AB
1907   defx  "LDIR"
1908   defb  #B0
1909   defx  "CPIR"
1910   defb  #B1
1911   defx  "INIR"
1912   defb  #B2
1913   defx  "OTIR"
1914   defb  #B3
1915   defx  "LDDR"
1916   defb  #B8
1917   defx  "CPDR"
1918   defb  #B9
1919   defx  "INDR"
1920   defb  #BA
1921   defx  "OTDR"
1922   defb  #BB
1924   ; GROUPS 2 AND 3: CB-prefixed (10, OPC_COUNT_GROUPS_2_AND_3)
1925   ; GROUP 2: direct bit manipulation (3, OPC_COUNT_GROUP_2)
1926   defx  "BIT"
1927   defb  #40
1928   defx  "RES"
1929   defb  #80
1930   defx  "SET"
1931   defb  #C0
1932   ; GROUP 3: shifts (7, OPC_COUNT_GROUPS_2_AND_3-OPC_COUNT_GROUP_2)
1933   defx  "RLC"
1934   defb  0
1935   defx  "RRC"
1936   defb  8
1937   defx  "RL"
1938   defb  #10
1939   defx  "RR"
1940   defb  #18
1941   defx  "SLA"
1942   defb  #20
1943   defx  "SRA"
1944   defb  #28
1945   defx  "SRL"
1946   defb  #38
1948   ; GROUP 4: push,pop,ex (sp),rr (OPC_COUNT_GROUP_4)
1949   defx  "POP"
1950   defb  #C1
1951   defx  "PUSH"
1952   defb  #C5
1953   defm  "EX",0
1954     defx  "(SP"
1955   defb  #E3
1957   ; GROUP 5: ALU with accumulator (OPC_COUNT_GROUP_5)
1958   defx  "SUB"
1959   defb  #90
1960   defx  "AND"
1961   defb  #A0
1962   defx  "XOR"
1963   defb  #A8
1964   defx  "OR"
1965   defb  #B0
1966   defx  "CP"
1967   defb  #B8
1968   ;k8: for some reason i cannot remove those two
1969   ;defb  TAND
1970   ;defb  #A0
1971   ;defb  TOR
1972   ;defb  #B0
1974   ; GROUP 6: ALU with accumulator or HL (OPC_COUNT_GROUP_6)
1975   defx  "ADD"
1976   defb  #80
1977   defx  "ADC"
1978   defb  #88
1979   defx  "SBC"
1980   defb  #98
1982   ; GROUP 7: inc,dec (2, OPC_COUNT_GROUP_7)
1983   defx  "INC"
1984   defb  4
1985   defx  "DEC"
1986   defb  5
1988   ; GROUP 8: in (1, OPC_COUNT_GROUP_8)
1989   defx  "IN"
1990   defb  #40
1991   ; GROUP 9: out (1, OPC_COUNT_GROUP_9)
1992   defx  "OUT"
1993   defb  #41
1995   ; GROUP 10: jr,djnz (2, OPC_COUNT_GROUP_10)
1996   defx  "JR"
1997   defb  #20
1998   defx  "DJNZ"
1999   defb  #10
2001   ; GROUP 11: jp (strictly one)
2002   defx  "JP"
2003   defb  #C2
2005   ; GROUP 12: call (strictly one)
2006   defx  "CALL"
2007   defb  #C4
2009   ; GROUP 13: rst (strictly one)
2010   defx  "RST"
2011   defb  #C7
2013   ; GROUP 14: ret (strictly one)
2014   defx  "RET"
2015   defb  #C0
2017   ; GROUP 15: ld (strictly one)
2018   defx  "LD"
2019   defb  #40
2021   ; miscellaneous assembler instructions
2022   ; WARNING! the order matters!
2023   defx  "DEFB"
2024   defb  0
2025   ;
2026   defx  "DEFW"
2027   defb  0
2028   ;
2029   defx  "DEFM"
2030   defb  0
2032   IF @BZ80ASM_ORGENTDISP
2033   defx  "ORG"
2034   defb  0
2035   ;
2036   defx  "DISP"
2037   defb  0
2038   ;
2039   defx  "ENT"
2040   defb  0
2041   ENDIF
2042   ; softinclude user instructions
2043   include "?bzasm80_user_mnemonics.zas"
2045   ; no more
2046   defb  0
2048 ;; operands
2049 OPRNDS:
2050   defx  "B"
2051   defb  0
2052   defx  "C"
2053   defb  1
2054   defx  "D"
2055   defb  2
2056   defx  "E"
2057   defb  3
2058   defx  "H"
2059   defb  4
2060   defx  "L"
2061   defb  5
2062   defx  "(HL"
2063   defb  6
2064   defx  "A"
2065   defb  7
2066   defx  "(IX"
2067   defb  #86
2068   defx  "(IY"
2069   defb  #C6
2070   ;
2071   defx  "BC"
2072   defb  8
2073   defx  "DE"
2074   defb  10
2075   defx  "HL"
2076   defb  12
2077   defx  "IX"
2078   defb  #8C
2079   defx  "IY"
2080   defb  #CC
2081   defx  "AF"
2082   defb  14
2083   defx  "SP"
2084   defb  14
2085   ;
2086   defx  "NZ"
2087   defb  16
2088   defx  "Z"
2089   defb  17
2090   defx  "NC"
2091   defb  18
2092   defx  "PO"
2093   defb  20
2094   defx  "PE"
2095   defb  21
2096   defx  "P"
2097   defb  22
2098   defx  "M"
2099   defb  23
2100   ;
2101   defx  "(C"
2102   defb  #20
2103   ;
2104   defb  0
2107 LDOPS:
2108   defm  "I"
2109   defb  0
2110   defx  "A"
2111   defb  #47
2112   defm  "R"
2113   defb  0
2114   defx  "A"
2115   defb  #4F
2116   defm  "A"
2117   defb  0
2118   defx  "I"
2119   defb  #57
2120   defm  "A"
2121   defb  0
2122   defx  "R"
2123   defb  #5F
2124   defm  "(BC"
2125   defb  0
2126   defx  "A"
2127   defb  2
2128   defm  "(DE"
2129   defb  0
2130   defx  "A"
2131   defb  #12
2132   defm  "A"
2133   defb  0
2134   defx  "(BC"
2135   defb  #0A
2136   defm  "A"
2137   defb  0
2138   defx  "(DE"
2139   defb  #1A
2140   ;
2141   defb  0
2143 csizest = $-csizest
2144 $printf "assembler tables size: %d", csizest
2146 asmsizest = $-asmsizest
2147 $printf "full assembler size: %d", asmsizest
2149 ; so they won't clutter symbol table
2150 csizest = -1
2151 asmsizest = -1
2153   ENDMODULE BZ80ASM