4x8 font fix
[bz80asm.git] / parser_number.zas
blob2104a6aa7a95a530ccef5e3b74bc153a8f9da6b0
1 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2 ;; parse integer number (without sign)
3 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
5 ;; this is advanced number parser
6 ;; it understands alot of suffixes and prefixes:
7 ;;   $     -- lone "$" means "current PC"
8 ;;   #nnnn -- hex
9 ;;   $nnnn -- hex
10 ;;   &nnnn -- hex
11 ;;   %nnnn -- binary
12 ;;   0Xnnn -- hex
13 ;;   0Onnn -- octal
14 ;;   0Bnnn -- binary
15 ;;   nnnnH -- hex
16 ;;   nnnnB -- binary
17 ;;   nnnnO -- octal
18 ;; everything is case-insensitive
19 ;; you can separate digits with underscores
20 ;; (i.e. "12_34_5" will work, underscores are simply ignored)
23 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
25 ;; parse a number, push it onto the stack
26 ;; understands prefixes and suffixes
28 ;; IN:
29 ;;   IY: text buffer
30 ;; OUT:
31 ;;   IY: text buffer after the expression
32 ;;   HL: number
33 ;;   carry flag reset
34 ;;  OR: (cannot parse as a number)
35 ;;   IY: unchanged
36 ;;   carry flag set
37 ;;  DE,AF,flags: dead
39 PARSE_NUMBER:
40   call  PARSER_SKIP_BLANKS
41   scf
42   ret   z
43   push  iy          ; we will need to rollback on error
44   ; A already contains a char, loaded by `PARSER_SKIP_BLANKS`
45   cp    '#'
46   jr    z,.hexprefix
47   cp    '$'
48   jr    z,.maybe_lone_dollar
49   cp    '&'
50   jr    z,.hexprefix
51   cp    '%'
52   jr    z,.binprefix
53   ; no, leading zero doesn't mean "octal", this is stupid
54   ; but we may have prefixes like "0x" and such
55   cp    '0'
56   jr    z,.maybe_zero_prefix
57   ; check if we have a digit here
58   call  PARSER_CONV_DIGIT
59   jr    c,.not_a_number_carry_set
60   cp    10
61   ; nope, do not allow it, all numbers must start with a digit
62   ;jr    nc,.must_be_hex_with_sfx
63   ccf
64   jr    c,.not_a_number_carry_set
65 .do_normal_decimal:
66   ; done with prefixes, try decimal number
67   ; we'll switch to suffix checking on hex digit
68   ld    hl,0        ; accumulator
69 .decimal_loop:
70   call  .getDigit
71   jr    c,.decimal_done
72   cp    10
73   jr    nc,.must_be_hex_with_sfx
74   ; HL=HL*10
75   add   hl,hl
76   ld    de,hl
77   add   hl,hl
78   add   hl,hl
79   add   hl,de
80   ; HL=HL+A
81   ld    e,a
82   ld    d,0
83   add   hl,de
84   ; next char
85   inc   iy
86   jr    .decimal_loop
87 .decimal_done:
88   ; check for suffix
89   ld    a,(iy)
90   and   %11011111   ; cheap uppercase
91   cp    'H'
92   jr    z,.must_be_hex_with_sfx
93   cp    'B'
94   jr    z,.bin_with_sfx
95   cp    'O'
96   jr    z,.oct_with_sfx
97   ; no suffix, we're done
99 .success:
100   pop   de          ; drop iy
101   ; reset carry flag
102   or    a
103   ret
105 .not_a_number_carry_set:
106   pop   iy
107   ret
109 .hexprefix:
110   ; skip prefix
111   inc   iy
112   call  .parse_as_hex
113 .after_prefix:
114   jr    c,.not_a_number_carry_set
115   jr    .success
117 .maybe_lone_dollar:
118   ; lone dollar means "PC"
119   inc   iy
120   call  .parse_as_hex
121   ; the only case we may gen an error here is
122   ; when our dollar isn't followed by a digit
123   jr    nc,.success
124   ; lone dollar is good too
125   ; IY points right after the dollar here
126   ld    hl,(BZ80ASM.PC)
127   jr    .success
129 .binprefix:
130   ; skip prefix
131   inc   iy
132   call  .parse_as_bin
133   jr    .after_prefix
135 .maybe_binprefix:
136   ; things like "0BEEFh" should be parsed as hex
137   ; skip prefix
138   inc   iy
139   call  .parse_as_bin
140   jr    c,.must_be_hex_with_sfx
141   ; check for 'H'
142   ld    a,(iy)
143   and   %11011111   ; cheap uppercase
144   cp    'H'
145   jr    z,.must_be_hex_with_sfx
146   jr    .success
148 .octprefix:
149   ; skip prefix
150   inc   iy
151   call  .parse_as_oct
152   jr    .after_prefix
154 .maybe_zero_prefix:
155   ; check for '0x' and such
156   ; skip '0'
157   inc   iy
158   ; load and prefix
159   ; there's no need to skip it, as it will be
160   ; skipped by the corresponding subroutine
161   ld    a,(iy)
162   ; so IY will point to the actual number
163   and   %11011111   ; cheap uppercase
164   cp    'X'
165   jr    z,.hexprefix
166   cp    'B'
167   jr    z,.maybe_binprefix
168   cp    'O'
169   jr    z,.octprefix
170   ; do not reparse '0', no need to backup
171   jr    .do_normal_decimal
173 .must_be_hex_with_sfx:
174   ; reparse as hex, and check for suffix
175   pop   iy
176   push  iy
177   call  .parse_as_hex
178   jr    c,.not_a_number_carry_set
179   ld    a,(iy)
180   inc   iy
181   and   %11011111   ; cheap uppercase
182   cp    'H'
183   jr    z,.success
185 .bin_with_sfx:
186   ; reparse as bin, skip suffix (it is guaranteed to be there)
187   pop   iy
188   push  iy
189   call  .parse_as_bin
190 .done_guaranteed_suffix:
191   jr    c,.not_a_number_carry_set
192   ; skip suffix
193   inc   iy
194   jr    .success
196 .oct_with_sfx:
197   ; reparse as bin, skip suffix (it is guaranteed to be there)
198   pop   iy
199   push  iy
200   call  .parse_as_bin
201   jr    .done_guaranteed_suffix
203 .parse_as_hex:
204   ld    hl,0        ; accumulator
205   ; check first digit (as this is general parser)
206   call  .getDigitNoUnder
207   ret   c
208 .parse_as_hex_loop:
209   inc   iy
210   add   hl,hl
211   add   hl,hl
212   add   hl,hl
213   add   hl,hl
214   ld    e,a
215   ld    d,0
216   add   hl,de
217   call  .getDigit
218   jr    nc,.parse_as_hex_loop
219   ; clear carry flag (it is always set here)
220   ccf
221   ret
223 .parse_as_bin:
224   ld    hl,0        ; accumulator
225   ; check first digit (as this is general parser)
226   ld    a,(iy)
227   call  .getDigitNoUnderBin
228   ret   c
229 .parse_as_bin_loop:
230   inc   iy
231   add   hl,hl
232   ld    e,a
233   ld    d,0
234   add   hl,de
235   call  .getBinDigit
236   jr    nc,.parse_as_bin_loop
237   ; clear carry flag (it is always set here)
238   ccf
239   ret
241 .parse_as_oct:
242   ld    hl,0        ; accumulator
243   ; check first digit (as this is general parser)
244   ld    a,(iy)
245   call  .getDigitNoUnderOct
246 .parse_as_oct_loop:
247   inc   iy
248   add   hl,hl
249   add   hl,hl
250   add   hl,hl
251   ld    e,a
252   ld    d,0
253   add   hl,de
254   call  .getOctDigit
255   jr    nc,.parse_as_oct_loop
256   ; clear carry flag (it is always set here)
257   ccf
258   ret
261 .getDigit_inc:
262   inc   iy
263 .getDigit:
264   ld    a,(iy)
265   cp    '_'
266   jr    z,.getDigit_inc
267   jp    PARSER_CONV_DIGIT
269   ; d: base
270 .getDigitInBase:
271   call  .getDigit
272   ret   c
273   cp    d
274   ccf
275   ret
277 .getDigitNoUnder:
278   ld    a,(iy)
279   jp    PARSER_CONV_DIGIT
281 .getDecDigit:
282   ld    d,10
283   jr    .getDigitInBase
285 .getDigitNoUnderOct:
286   ld    a,(iy)
287 .getOctDigit:
288   ld    d,8
289   jr    .getDigitInBase
291 .getDigitNoUnderBin:
292   ld    a,(iy)
293 .getBinDigit:
294   ld    d,2
295   jr    .getDigitInBase
298 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
299 ;; converts 'A' to digit (assume hex)
300 ;; carry set: not a digit char (and A is destroyed)
302 PARSER_CONV_DIGIT:
303   sub   '0'
304   ret   c
305   cp    10
306   ccf
307   ret   nc
308   add   a,'0'
309   and   %11011111   ; cheap uppercase
310   sub   'A'-10
311   ret   c
312   cp    16
313   ccf
314   ret