example driver: show current pass number
[bz80asm.git] / labman.zas
blob1a6e0f619175f0ddaa8dece3c0deccf01517f477
1 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2 ;; label manager
3 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
6 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
7 ;;
8 ;; reset all labels, reinit label manager
9 ;; HL,AF: dead
11 LABMAN_INIT:
12   xor   a
13   ld    hl,(LABMAN_FIRST_LABEL)
14   ld    (hl),a
15   ld    (LABMAN_PASS),a
16   ret
19 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
21 ;; increment pass counter, perform pass bookkeeping
22 ;; calling this more than once is UB
23 ;; returns current pass in A
24 ;; HL, flags: dead
26 LABMAN_ADVANCE_PASS:
27   ld    hl,LABMAN_PASS
28   inc   (hl)
29   ld    a,(hl)
30   ret
33 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
34 LABMAN_PASS: defb 0
37 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
38 ;; data buffer format:
40 ;; label-name  ; end of table if the first byte is zero
41 ;; defw value
42 ;; defb flags
44 ;; name has highest bit of the last char set
45 ;; flags are used for forward references
46 ;; #00: fully resolved label
47 ;; #FF: forward-referenced label (it was used, but the value is unknown yet)
48 ;; at the end of the first pass, any labels with #FF are undefined
49 ;; on the second pass, no new labels are created
52 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
54 ;; find a label
56 ;; IN:
57 ;;   IY: text input buffer
58 ;; OUT:
59 ;;   IY: text input buffer after the label
60 ;;   HL: points to label data area
61 ;;   CARRY RESET
62 ;;  OR:
63 ;;   IY: original
64 ;;   HL: points after the last label
65 ;;   CARRY SET
66 ;; AF is dead, other registers are preserved
68 labman_find:
69   ld    (.saved_iy),iy
70   ld    hl,(LABMAN_FIRST_LABEL)
71 .labman_loop:
72   ld    a,(hl)
73   or    a
74   jr    z,.not_found
75   ; check name
76   ld    iy,0    ; patched above
77 .saved_iy equ $-2
78 .check_loop:
79   ld    a,(hl)
80   and   #7F
81   cp    (iy)
82   jr    nz,.skip_label
83   inc   iy
84   bit   7,(hl)
85   inc   hl
86   jr    z,.check_loop
87   jr    .check_test_iy_end
88   ; check failed, skip label
89 .skip_label:
90   ld    a,(hl)
91   inc   hl
92   or    a
93   jr    nz,.skip_label
94 .skip_label_data_area:
95   ; skip value
96   inc   hl
97   inc   hl
98   ; skip pass flag
99   inc   hl
100   jr    .labman_loop
102 .check_test_iy_end:
103   ld    a,(iy)
104   call  isIdChar
105   ; if ok, carry is set (non-id char)
106   ; inverse it for correct return result
107   ccf
108   ret   nc
109   jr    .skip_label_data_area
111 .not_found:
112   ld    iy,(.saved_iy)
113   scf
114   ret
117 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
119 ;;  this is called by the driver to define a new label
121 ;; IN:
122 ;;   IY: text input buffer
123 ;; OUT:
124 ;;   IY: text input buffer after the label
125 ;;   CARRY SET on error
127 LABMAN_DEFINE_LABEL:
128   ld    a,0
129   ld    (LABMAN_NEW_LABEL_FLAG),a
130   ld    (.orig_tib),iy
131   call  labman_find
132   jr    c,LABMAN_FIND_LABEL_create_new_label
133   ; existing label: check if it is a forward reference
134   inc   hl
135   inc   hl
136   ld    a,(hl)
137   inc   a
138   jr    z,.fwd_label
139   ; on second and other passes, this is not an error
140   ld    a,(LABMAN_PASS)
141   or    a
142   jr    nz,.skip_label
143   ld    a,BZ80ASM.EXPR_ERR_DUPLICATE_LABEL
144   jp    BZ80ASM.PARSE_EXPR_ERROR_A
145 .fwd_label:
146   ; reset "forward" flag
147   ld    (hl),a
148   ; there should be no unresolved labels on the second pass
149   ld    a,(LABMAN_PASS)
150   or    a
151   jr    nz,.broken_code
152   ; set value (PC)
153   dec   hl
154   dec   hl
155   ld    de,(BZ80ASM.PC)
156   ld    (hl),e
157   inc   hl
158   ld    (hl),d
159   ret
160 .broken_code:
161   ld    iy,0    ; patched above
162 .orig_tib equ $-2
163   call  .print_label
164   ld    a,13
165   call  EMIT
166   ld    a,BZ80ASM.EXPR_ERR_UNRESOLVED_LABEL
167   jp    BZ80ASM.PARSE_EXPR_ERROR_A
168 .skip_label:
169   ; assume that label name is good
170   ld    a,(iy)
171   inc   iy
172   call  isIdChar
173   jr    nc,.skip_label
174   ccf   ; reset carry
175   ret
177 .print_label:
178   ld    a,(iy)
179   call  EMIT
180   ld    a,(iy)
181   inc   iy
182   call  isIdChar
183   jr    nc,.print_label
184   ret
187 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
189 ;; find a label
190 ;; see full documentation in "bz80asm.zas"
192 ;; IN:
193 ;;   IY: text input buffer
194 ;; OUT:
195 ;;   IY: text input buffer after the label
196 ;;   HL: label value
197 ;;   CARRY FLAG: set if label wasn't found
198 ;;               expression parser will bomb out in this case
200 LABMAN_FIND_LABEL:
201   ld    a,#FF
202   ld    (LABMAN_NEW_LABEL_FLAG),a
204   call  labman_find
205   jr    c,.create_new_label
206   ; old label, return value
207   ld    e,(hl)
208   inc   hl
209   ld    d,(hl)
210   ex    de,hl
211   ret
213 @LABMAN_FIND_LABEL_create_new_label:
214 .create_new_label:
215   ; allowed only on the first pass
216   ld    a,(LABMAN_PASS)
217   or    a
218   jr    nz,.broken_code
219   ld    a,(iy)
220   call  isAlpha
221   jr    nc,.good_start
222   cp    '_'
223   jr    nc,.good_start
224   cp    '.'
225   scf
226   ret   nz
227   ld    a,EXPR_ERR_NO_LOCAL_LABELS_YET
228   jp    BZ80ASM.PARSE_EXPR_ERROR_A
229 .broken_code:
230   ld    a,BZ80ASM.EXPR_ERR_BAD_SYNTAX
231   jp    BZ80ASM.PARSE_EXPR_ERROR_A
233 .good_start:
234   push  hl      ; we'll use this later to print a name
235   ; HL points to the memory where our new label should be put
236   ; copy label name there
237 .idloop:
238   ld    a,(iy)
239   call  isIdChar
240   jr    c,.idend
241   ld    (hl),a
242   inc   hl
243   inc   iy
244   jr    .idloop
245 .idend:
246   ; mark name end
247   dec   hl
248   set   7,(hl)
249   ; move to data area
250   inc   hl
251   ; assign new value
252   ld    de,(BZ80ASM.PC)
253   ld    (hl),e
254   inc   hl
255   ld    (hl),d
256   inc   hl
257   ; set "forward reference" flag
258   ld    (hl),#FF
259 @LABMAN_NEW_LABEL_FLAG equ $-1
260   ; put zero to mark new label area end
261   inc   hl
262   ld    (hl),0
264   ; now print new label
265   ld    hl,msg_newlabel
266   call  printstr
267   pop   hl
268 .prloop:
269   ld    a,(hl)
270   and   #7F
271   call  EMIT
272   bit   7,(hl)
273   inc   hl
274   jr    z,.prloop
275   ld    a,'>'
276   call  EMIT
277   ld    a,13
278   call  EMIT
280   ; return new value
281   ld    hl,(BZ80ASM.PC)
282   ; reset carry (just to be sure)
283   or    a
284   ret
286 msg_newlabel: defx "NEWLABEL:<"
289 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
290 ;; check if A is an alpha char
291 ;; carry set: not an alpha char
293 isAlpha:
294   cp    'a'
295   jr    c,.notlower
296   cp    'z'+1
297   jr    nc,.notlower
298   or    a
299   ret
300 .notlower:
301   cp    'A'
302   ret   c
303   cp    'Z'+1
304   ccf
305   ret
307 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
308 ;; check if A is a decimal digit
309 ;; carry set: not a digit char
311 isDigit:
312   cp    '0'
313   ret   c
314   cp    '9'+1
315   ccf
316   ret
318 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
319 ;; check if A is a valid identifier char (including digits)
320 ;; carry set: not an id char
322 isIdChar:
323   call  isAlpha
324   ret   nc
325   call  isDigit
326   ret   nc
327   cp    '_'
328   jr    z,.goodchar
329   cp    '$'
330   jr    z,.goodchar
331   scf
332   ret
333 .goodchar:
334   or    a
335   ret