Mercurial > hg-old > index.cgi
annotate src/lwasm.c @ 8:f1df096aa76f 1.1
Tagged 1.1 bugfix release
author | lost |
---|---|
date | Sun, 04 Jan 2009 05:46:07 +0000 |
parents | 34568fab6058 |
children | 05d4115b4860 |
rev | line source |
---|---|
0 | 1 /* |
4
34568fab6058
Fixed package to include all required files; also added copyright preamble to all source files
lost
parents:
0
diff
changeset
|
2 lwasm.c |
34568fab6058
Fixed package to include all required files; also added copyright preamble to all source files
lost
parents:
0
diff
changeset
|
3 Copyright © 2008 William Astle |
34568fab6058
Fixed package to include all required files; also added copyright preamble to all source files
lost
parents:
0
diff
changeset
|
4 |
34568fab6058
Fixed package to include all required files; also added copyright preamble to all source files
lost
parents:
0
diff
changeset
|
5 This file is part of LWASM. |
34568fab6058
Fixed package to include all required files; also added copyright preamble to all source files
lost
parents:
0
diff
changeset
|
6 |
34568fab6058
Fixed package to include all required files; also added copyright preamble to all source files
lost
parents:
0
diff
changeset
|
7 LWASM is free software: you can redistribute it and/or modify it under the |
34568fab6058
Fixed package to include all required files; also added copyright preamble to all source files
lost
parents:
0
diff
changeset
|
8 terms of the GNU General Public License as published by the Free Software |
34568fab6058
Fixed package to include all required files; also added copyright preamble to all source files
lost
parents:
0
diff
changeset
|
9 Foundation, either version 3 of the License, or (at your option) any later |
34568fab6058
Fixed package to include all required files; also added copyright preamble to all source files
lost
parents:
0
diff
changeset
|
10 version. |
34568fab6058
Fixed package to include all required files; also added copyright preamble to all source files
lost
parents:
0
diff
changeset
|
11 |
34568fab6058
Fixed package to include all required files; also added copyright preamble to all source files
lost
parents:
0
diff
changeset
|
12 This program is distributed in the hope that it will be useful, but WITHOUT |
34568fab6058
Fixed package to include all required files; also added copyright preamble to all source files
lost
parents:
0
diff
changeset
|
13 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
34568fab6058
Fixed package to include all required files; also added copyright preamble to all source files
lost
parents:
0
diff
changeset
|
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
34568fab6058
Fixed package to include all required files; also added copyright preamble to all source files
lost
parents:
0
diff
changeset
|
15 more details. |
34568fab6058
Fixed package to include all required files; also added copyright preamble to all source files
lost
parents:
0
diff
changeset
|
16 |
34568fab6058
Fixed package to include all required files; also added copyright preamble to all source files
lost
parents:
0
diff
changeset
|
17 You should have received a copy of the GNU General Public License along with |
34568fab6058
Fixed package to include all required files; also added copyright preamble to all source files
lost
parents:
0
diff
changeset
|
18 this program. If not, see <http://www.gnu.org/licenses/>. |
34568fab6058
Fixed package to include all required files; also added copyright preamble to all source files
lost
parents:
0
diff
changeset
|
19 |
34568fab6058
Fixed package to include all required files; also added copyright preamble to all source files
lost
parents:
0
diff
changeset
|
20 Contains the main code for lwasm |
34568fab6058
Fixed package to include all required files; also added copyright preamble to all source files
lost
parents:
0
diff
changeset
|
21 */ |
0 | 22 |
23 #include <ctype.h> | |
24 #include <errno.h> | |
25 #include <stdio.h> | |
26 #include <stdlib.h> | |
27 #include <string.h> | |
28 #define __lwasm_c_seen__ | |
29 #include "instab.h" | |
30 #include "lwasm.h" | |
31 | |
32 void lwasm_read_file(asmstate_t *as, char *fname); | |
33 extern int add_macro_line(asmstate_t *as, sourceline_t *cl, char *optr); | |
34 extern void expand_macro(asmstate_t *as, sourceline_t *cl, char **optr); | |
35 | |
36 #define debug(mess, ...) do { if (as->debug) { fprintf(stderr, "DEBUG: "); fprintf(stderr, (mess), ## __VA_ARGS__); } } while (0) | |
37 | |
38 void register_error(asmstate_t *as, sourceline_t *cl, int errcode) | |
39 { | |
40 errortab_t *e; | |
41 | |
42 e = malloc(sizeof(errortab_t)); | |
43 | |
44 e -> errnum = errcode; | |
45 e -> line = cl; | |
46 e -> next = cl -> errors; | |
47 cl -> errors = e; | |
48 | |
49 as -> errorcount++; | |
50 } | |
51 | |
52 int eval_expr(asmstate_t *as, sourceline_t *cl, char **optr, int *val); | |
53 | |
54 int eval_min(int v1, int v2, int v3, int v4) | |
55 { | |
56 if (v2 < v1) | |
57 v1 = v2; | |
58 if (v3 < v1) | |
59 v1 = v3; | |
60 if (v4 < v1) | |
61 v1 = v4; | |
62 return v1; | |
63 } | |
64 | |
65 int eval_max(int v1, int v2, int v3, int v4) | |
66 { | |
67 if (v2 > v1) | |
68 v1 = v2; | |
69 if (v3 > v1) | |
70 v1 = v3; | |
71 if (v4 > v1) | |
72 v1 = v4; | |
73 return v1; | |
74 } | |
75 | |
76 int lookupreg3(const char *rlist, char **str) | |
77 { | |
78 int rval = 0; | |
79 int f = 0; | |
80 const char *reglist = rlist; | |
81 | |
82 while (*reglist) | |
83 { | |
84 if (toupper(**str) == *reglist) | |
85 { | |
86 // first char matches | |
87 if (reglist[1] == ' ') | |
88 { | |
89 f = 1; | |
90 break; | |
91 } | |
92 if (toupper(*(*str + 1)) == reglist[1]) | |
93 { | |
94 // second char matches | |
95 if (reglist[2] == ' ') | |
96 { | |
97 f = 1; | |
98 break; | |
99 } | |
100 if (toupper(*(*str + 2)) == reglist[2]) | |
101 { | |
102 f = 1; | |
103 break; | |
104 } | |
105 } | |
106 } | |
107 reglist += 3; | |
108 rval++; | |
109 } | |
110 if (f == 0) | |
111 return -1; | |
112 | |
113 | |
114 reglist = rval * 3 + rlist; | |
115 if (reglist[1] == ' ') | |
116 (*str) += 1; | |
117 else if (reglist[2] == ' ') | |
118 (*str) += 2; | |
119 else | |
120 (*str)+=3; | |
121 return rval; | |
122 } | |
123 | |
124 | |
125 int lookupreg(const char *reglist, char **str) | |
126 { | |
127 int rval = 0; | |
128 while (*reglist) | |
129 { | |
130 if (toupper(**str) == *reglist) | |
131 { | |
132 // first char matches | |
133 if (reglist[1] == ' ' && !isalpha(*(*str + 1))) | |
134 break; | |
135 if (toupper(*(*str + 1)) == reglist[1]) | |
136 break; | |
137 } | |
138 reglist += 2; | |
139 rval++; | |
140 } | |
141 if (!*reglist) | |
142 return -1; | |
143 if (reglist[1] == ' ') | |
144 (*str)++; | |
145 else | |
146 (*str)+=2; | |
147 return rval; | |
148 } | |
149 | |
150 void addcodebyte(asmstate_t *as, sourceline_t *cl, int cb) | |
151 { | |
152 cl -> len += 1; | |
153 if (as -> passnum != 2) | |
154 return; | |
155 | |
156 if (cl -> numcodebytes >= cl -> codesize) | |
157 { | |
158 cl -> codebytes = realloc(cl -> codebytes, cl -> codesize + 32); | |
159 cl -> codesize += 32; | |
160 } | |
161 debug("EMIT: %02x\n", cb & 0xff); | |
162 cl -> codebytes[cl -> numcodebytes++] = cb & 0xFF; | |
163 } | |
164 | |
165 // parse a symble out of the line and return a pointer | |
166 // to a static pointer | |
167 // return NULL if not a symbol or a bad symbol | |
168 char *parse_symbol(asmstate_t *as, char **ptr) | |
169 { | |
170 static char *symptr = NULL; | |
171 char *tptr = *ptr; | |
172 int sl = 0; | |
173 | |
174 // symbol can start with _,a-z,A-Z | |
175 | |
176 if (!strchr(SYMCHAR_START, **ptr)) | |
177 return NULL; | |
178 | |
179 while (*tptr && !isspace(*tptr) && strchr(SYMCHAR, *tptr)) | |
180 { | |
181 tptr++; | |
182 sl++; | |
183 } | |
184 | |
185 symptr = realloc(symptr, sl + 1); | |
186 tptr = symptr; | |
187 while (sl) | |
188 { | |
189 *tptr++ = *(*ptr)++; | |
190 sl--; | |
191 } | |
192 *tptr = '\0'; | |
193 return symptr; | |
194 } | |
195 | |
196 // resolve an instruction | |
197 void resolve_insn(asmstate_t *as, sourceline_t *cl) | |
198 { | |
199 char *optr; | |
200 char opbuf[MAX_OP_LEN + 1]; | |
201 char *symbol = NULL; | |
202 int c; | |
203 | |
204 cl -> code_symloc = as -> addr; | |
205 | |
206 cl -> addrset = 0; | |
207 cl -> isequ = 0; | |
208 cl -> len = 0; | |
209 cl -> undef = 0; | |
210 | |
211 // only parse line on first pass | |
212 if (as -> passnum == 1) | |
213 { | |
214 optr = cl -> line; | |
215 if (!*optr || *optr == '*' || *optr == ';') | |
216 { | |
217 cl -> opcode = -1; | |
218 cl -> remainder = cl -> line; | |
219 return; | |
220 } | |
221 | |
222 if (!isspace(*optr)) | |
223 { | |
224 symbol = parse_symbol(as, &optr); | |
225 if (*optr && !isspace(*optr) && !(as -> inmacro)) | |
226 { | |
227 errorp1(ERR_BADSYM); | |
228 while (*optr && !isspace(*optr)) | |
229 optr++; | |
230 } | |
231 if (symbol) | |
232 { | |
233 cl -> symstr = strdup(symbol); | |
234 cl -> hassym = 1; | |
235 } | |
236 } | |
237 | |
238 while (isspace(*optr)) | |
239 optr++; | |
240 | |
241 // parse opcode | |
242 if (*optr && *optr != ';') | |
243 { | |
244 c = 0; | |
245 while (c < MAX_OP_LEN && *optr && !isspace(*optr)) | |
246 { | |
247 opbuf[c++] = *optr++; | |
248 } | |
249 opbuf[c] = '\0'; | |
250 if (*optr && !isspace(*optr) && !(as -> inmacro)) | |
251 { | |
252 errorp1(ERR_BADOP); | |
253 cl -> opcode = -1; | |
254 } | |
255 else | |
256 { | |
257 cl -> opcstr = strdup(opbuf); | |
258 for (c = 0; instab[c].opcode; c++) | |
259 { | |
260 if (!strcasecmp(opbuf, instab[c].opcode)) | |
261 break; | |
262 } | |
263 if (!instab[c].opcode && opbuf[0] == '*') | |
264 { | |
265 cl -> opcode = -1; | |
266 } | |
267 else if (!instab[c].opcode && !(as -> inmacro)) | |
268 { | |
269 cl -> opcode = -1; | |
270 | |
271 // look up macro | |
272 if (as -> macros) | |
273 { | |
274 macrotab_t *m; | |
275 | |
276 for (m = as -> macros; m; m = m -> next) | |
277 { | |
278 if (!strcmp(m -> name, opbuf)) | |
279 break; | |
280 } | |
281 if (m) | |
282 { | |
283 // we have a macro here | |
284 cl -> macro = m; | |
285 while (*optr && isspace(*optr)) | |
286 optr++; | |
287 expand_macro(as, cl, &optr); | |
288 return; | |
289 } | |
290 else | |
291 { | |
292 errorp1(ERR_BADOP); | |
293 } | |
294 } | |
295 else | |
296 { | |
297 errorp1(ERR_BADOP); | |
298 } | |
299 } | |
300 else | |
301 cl -> opcode = c; | |
302 } | |
303 } | |
304 else | |
305 cl -> opcode = -1; | |
306 | |
307 if (as -> inmacro && cl -> opcode >= 0 && instab[cl -> opcode].specialnum != SPECIAL_ENDM) | |
308 { | |
309 add_macro_line(as, cl, cl -> line); | |
310 cl -> opcode = -1; | |
311 cl -> remainder = cl -> line; | |
312 cl -> opcstr = NULL; | |
313 cl -> operstr = NULL; | |
314 cl -> symstr = NULL; | |
315 cl -> hassym = 0; | |
316 cl -> macrodef = 1; | |
317 return; | |
318 } | |
319 // parse operand | |
320 while (*optr && isspace(*optr)) | |
321 optr++; | |
322 | |
323 cl -> operstr = optr; | |
324 } | |
325 else | |
326 optr = cl -> operstr; | |
327 | |
328 if (as -> skipcond) | |
329 { | |
330 // if skipping a condition, need to skip a macro | |
331 if (cl -> opcode >= 0) | |
332 { | |
333 if (instab[cl -> opcode].specialnum == SPECIAL_MACRO) | |
334 { | |
335 as -> skipmacro = 1; | |
336 } | |
337 else if (instab[cl -> opcode].specialnum == SPECIAL_ENDM) | |
338 { | |
339 as -> skipmacro = 0; | |
340 } | |
341 else if (instab[cl -> opcode].specialnum == SPECIAL_COND && !(as -> skipmacro)) | |
342 { | |
343 as -> skipcount++; | |
344 } | |
345 else if (instab[cl -> opcode].specialnum == SPECIAL_ENDC && !(as -> skipmacro)) | |
346 { | |
347 as -> skipcount--; | |
348 if (as -> skipcount <= 0) | |
349 { | |
350 as -> skipcond = 0; | |
351 as -> noelse = 0; | |
352 } | |
353 } | |
354 else if (instab[cl -> opcode].specialnum == SPECIAL_ELSE && !(as -> skipmacro)) | |
355 { | |
356 if (as -> skipcount == 1) | |
357 { | |
358 as -> skipcount = 0; | |
359 as -> skipcond = 0; | |
360 as -> noelse = 1; | |
361 return; | |
362 } | |
363 } | |
364 } | |
365 if (as -> skipcond) | |
366 cl -> skipped = 1; | |
367 return; | |
368 } | |
369 | |
370 // do the code thing | |
371 // on pass 1, no code is generated | |
372 // on pass 2, code is generated using the "emit()" macro | |
373 if (cl -> opcode >= 0) | |
374 { | |
375 if (instab[cl -> opcode].opfn) | |
376 { | |
377 (*(instab[cl -> opcode].opfn))(as, cl, &optr); | |
378 if (as -> passnum == 1) | |
379 { | |
380 if (*optr) | |
381 { | |
382 char *t = optr; | |
383 char t2; | |
384 | |
385 t2 = *optr; | |
386 cl -> operstr = strdup(cl -> operstr); | |
387 *optr = t2; | |
388 while (*t && isspace(*t)) | |
389 t++; | |
390 cl -> remainder = strdup(t); | |
391 | |
392 } | |
393 cl -> remainder = optr; | |
394 } | |
395 } | |
396 else | |
397 { | |
398 errorp1(ERR_BADOP); | |
399 cl -> opcode = -1; | |
400 } | |
401 } | |
402 // address of the symbol may have been changed by a pseudo op | |
403 // so we couldn't register it above | |
404 // that means it may turn out to be a "forward ref" in pass 1 | |
405 if (cl -> hassym) | |
406 { | |
407 register_symbol(as, cl, cl -> symstr, cl -> code_symloc, cl -> isset ? SYMFLAG_SET : SYMFLAG_NONE); | |
408 } | |
409 | |
410 as -> addr += cl -> len; | |
411 } | |
412 | |
413 void generate_code(asmstate_t *as) | |
414 { | |
415 sourceline_t *cl; | |
416 | |
417 as -> addr = 0; | |
418 as -> dpval = 0; | |
419 as -> passnum = 2; | |
420 for (cl = as -> source_head; cl; cl = cl -> next) | |
421 { | |
422 resolve_insn(as, cl); | |
423 } | |
424 } | |
425 | |
426 void lwasm_read_file(asmstate_t *as, char *fname) | |
427 { | |
428 FILE *f; | |
429 int cline = 0; | |
430 sourceline_t *cl; | |
431 size_t bufflen; | |
432 char *buff = NULL; | |
433 int retval; | |
434 | |
435 as -> passnum = 1; | |
436 | |
437 f = fopen(fname, "r"); | |
438 if (!f) | |
439 { | |
440 fprintf(stderr, "Cannot open input file %s: %s\n", fname, strerror(errno)); | |
441 return; | |
442 } | |
443 | |
444 while (!feof(f)) | |
445 { | |
446 retval = getline(&buff, &bufflen, f); | |
447 debug(" read line (%s:%d): %s\n", fname, cline, buff); | |
448 if (retval < 0) | |
449 { | |
450 if (feof(f)) | |
451 break; | |
452 fprintf(stderr, "Error reading '%s': %s\n", fname, strerror(errno)); | |
453 exit(1); | |
454 } | |
455 if (strchr(buff, '\n')) | |
456 *strchr(buff, '\n') = '\0'; | |
457 if (strchr(buff, '\r')) | |
458 *strchr(buff, '\r') = '\0'; | |
459 cl = calloc(sizeof(sourceline_t), 1); | |
460 if (!cl) | |
461 { | |
462 perror("Malloc"); | |
463 exit(1); | |
464 } | |
465 | |
466 cl -> lineno = cline++; | |
467 cl -> sourcefile = fname; | |
468 cl -> opcode = -1; | |
469 cl -> addrmode = -1; | |
470 cl -> addr = as -> addr; | |
471 cl -> dpval = as -> dpval; | |
472 cl -> prev = as -> source_tail; | |
473 if (as -> source_tail) | |
474 as -> source_tail -> next = cl; | |
475 as -> source_tail = cl; | |
476 if (as -> source_head == NULL) | |
477 as -> source_head = cl; | |
478 cl -> line = strdup(buff); | |
479 | |
480 resolve_insn(as, cl); | |
481 | |
482 if (cl -> opcode >= 0 && instab[cl -> opcode].instype == INSTYPE_PSEUDO && instab[cl -> opcode].specialnum == SPECIAL_END) | |
483 break; | |
484 | |
485 *buff = '\0'; | |
486 | |
487 } | |
488 if (buff) | |
489 free(buff); | |
490 | |
491 fclose(f); | |
492 | |
493 return; | |
494 } | |
495 | |
496 /* | |
497 below this point is the expression evaluation package | |
498 | |
499 Supported binary operators: + - / * % | |
500 Supported unary operators: - | |
501 | |
502 <infix>: + | - | * | / | % | |
503 <unary>: - | |
504 <expr>: <term> <infix> <term> | |
505 <term>: <unary> <term> | |
506 <term>: ( <expr> ) | |
507 <term>: <symbol> | |
508 <term>: ' <char> | |
509 <term>: " <char> <char> | |
510 <term>: * | |
511 <term>: <number> | |
512 | |
513 <number>: <dec> | |
514 <number>: & <dec> | |
515 | |
516 <number>: $ <hex> | |
517 <number>: <hex> H | |
518 <number>: @ <oct> | |
519 <number>: <oct> O | |
520 <number>: <oct> Q | |
521 | |
522 <number>: % <bin> | |
523 <number>: <bin> B | |
524 | |
525 <bin>: 0 | 1 | |
526 <oct>: <bin> | 2 | 3 | 4 | 5 | 6 | 7 | |
527 <dec>: <oct> | 8 | 9 | |
528 <hex>: <dec> | A | B | C | D | E | F | |
529 | |
530 NOTE: hex values which start with a non-digit will need to be prefixed | |
531 by $ or have a 0 as the leading digit; hence: $CC or 0CCH otherwise the | |
532 assembler cannot tell the difference between CCH as a symbol or CCH as | |
533 the value $CC | |
534 | |
535 */ | |
536 | |
537 // will throw an error and return 0 in tval if there's a problem | |
538 // -1 is problem; cl -> undef set is undefined symbol | |
539 int eval_term(asmstate_t *as, sourceline_t *cl, char **optr, int *tval) | |
540 { | |
541 char tc; | |
542 int rval; | |
543 int binval; | |
544 int octval; | |
545 int decval; | |
546 int hexval; | |
547 int valtype; | |
548 int digval; | |
549 int bindone = 0; | |
550 | |
551 *tval = 0; | |
552 | |
553 beginagain: | |
554 tc = **optr; | |
555 if (tc == '+') | |
556 { | |
557 // unary +, ignored for symetry | |
558 (*optr)++; | |
559 goto beginagain; | |
560 } | |
561 | |
562 if (tc == '(') | |
563 { | |
564 (*optr)++; | |
565 rval = eval_expr(as, cl, optr, tval); | |
566 if (rval < 0) | |
567 return rval; | |
568 if (**optr != ')') | |
569 { | |
570 errorp1(ERR_BADEXPR); | |
571 return -1; | |
572 } | |
573 (*optr)++; | |
574 return 0; | |
575 } | |
576 | |
577 if (tc == '-') | |
578 { | |
579 (*optr)++; | |
580 rval = eval_term(as, cl, optr, tval); | |
581 if (rval < 0) | |
582 return rval; | |
583 *tval = -*tval; | |
584 return 0; | |
585 } | |
586 | |
587 // current address (of current instruction, not PC) | |
588 if (tc == '*') | |
589 { | |
590 *tval = cl -> addr; | |
591 (*optr)++; | |
592 return 0; | |
593 } | |
594 | |
595 if (strchr("abcdefghijklmnopqrstuvwxyz_", tolower(tc))) | |
596 { | |
597 // evaluate a symbol | |
598 char *symbuf; | |
599 | |
600 symbuf = parse_symbol(as, optr); | |
601 if (!symbuf) | |
602 { | |
603 errorp1(ERR_BADSYM); | |
604 *tval = 0; | |
605 return -1; | |
606 } | |
607 | |
608 debug(" looking up symbol: %s\n", symbuf); | |
609 *tval = lookup_symbol(as, symbuf); | |
610 | |
611 // if not found, flag forward ref | |
612 if (*tval == -1) | |
613 { | |
614 errorp2(ERR_UNDEF); | |
615 cl -> undef = 1; | |
616 *tval = 0; | |
617 return 0; | |
618 } | |
619 return 0; | |
620 } | |
621 | |
622 if (tc == '%') | |
623 { | |
624 // binary number | |
625 int v1 = 0; | |
626 (*optr)++; | |
627 while (strchr("01", **optr)) | |
628 { | |
629 v1 = v1 << 1 | ((*(*optr)++) - '0'); | |
630 } | |
631 *tval = v1; | |
632 return 0; | |
633 } | |
634 if (tc == '$') | |
635 { | |
636 // hex number | |
637 int v1 = 0; | |
638 (*optr)++; | |
639 debug("HEX CONST: %s\n", *optr); | |
640 while (**optr && strchr("01234567890ABCDEF", toupper(tc = **optr))) | |
641 { | |
642 debug("HEX 2: %02x\n", tc); | |
643 if (**optr >= 'A') | |
644 { | |
645 v1 = v1 << 4 | (toupper((*(*optr)++)) - 'A' + 10); | |
646 } | |
647 else | |
648 { | |
649 v1 = v1 << 4 | ((*(*optr)++) - '0'); | |
650 } | |
651 } | |
652 *tval = v1; | |
653 return 0; | |
654 } | |
655 if (tc == '@') | |
656 { | |
657 // octal number | |
658 int v1 = 0; | |
659 (*optr)++; | |
660 while (strchr("01234567", **optr)) | |
661 { | |
662 v1 = v1 << 3 | ((*(*optr)++) - '0'); | |
663 } | |
664 *tval = v1; | |
665 return 0; | |
666 } | |
667 if (tc == '&') | |
668 { | |
669 // decimal number | |
670 int v1 = 0; | |
671 (*optr)++; | |
672 while (strchr("0123456789", **optr)) | |
673 { | |
674 v1 = v1 * 10 + ((*(*optr)++) - '0'); | |
675 } | |
676 *tval = v1; | |
677 return 0; | |
678 } | |
679 if (tc == '\'') | |
680 { | |
681 (*optr)++; | |
682 if (!**optr) | |
683 { | |
684 errorp1(ERR_BADEXPR); | |
685 return -2; | |
686 } | |
687 *tval = *(*optr)++; | |
688 return 0; | |
689 } | |
690 if (tc == '"') | |
691 { | |
692 (*optr)++; | |
693 if (!**optr || !*(*optr + 1)) | |
694 { | |
695 errorp1(ERR_BADEXPR); | |
696 return -2; | |
697 } | |
698 *tval = *(*optr)++ << 8 | *(*optr)++; | |
699 return 0; | |
700 } | |
701 // end of string | |
702 if (tc == '\0') | |
703 { | |
704 // error if at EOS as we are looking for a term | |
705 errorp1(ERR_BADEXPR); | |
706 return -1; | |
707 } | |
708 | |
709 // we have a generic number here which may be decimal, hex, binary, or octal | |
710 // based on a suffix | |
711 | |
712 // possible data types are binary (1), octal (2), decimal(4), hex (8) | |
713 valtype = 15; | |
714 hexval = octval = decval = binval = 0; | |
715 while (1) | |
716 { | |
717 | |
718 // printf(" %c\n", **optr); | |
719 if (!**optr || !strchr("ABCDEFabcdefqhoQHO0123456789", **optr)) | |
720 { | |
721 // end of string, must be decimal or the end of a bin | |
722 if (bindone == 1) | |
723 { | |
724 *tval = binval; | |
725 return 0; | |
726 } | |
727 if (valtype & 4) | |
728 { | |
729 *tval = decval; | |
730 return 0; | |
731 } | |
732 else | |
733 { | |
734 errorp1(ERR_BADEXPR); | |
735 return -1; | |
736 } | |
737 } | |
738 tc = toupper(*(*optr)++); | |
739 | |
740 if (tc == 'H') | |
741 { | |
742 if (valtype & 8) | |
743 { | |
744 *tval = hexval; | |
745 return 0; | |
746 } | |
747 else | |
748 { | |
749 // syntax error | |
750 errorp1(ERR_BADEXPR); | |
751 return -1; | |
752 } | |
753 } | |
754 | |
755 if (tc == 'Q' || tc == 'O') | |
756 { | |
757 if (valtype && 2) | |
758 { | |
759 *tval = octval; | |
760 return 0; | |
761 } | |
762 else | |
763 { | |
764 errorp1(ERR_BADEXPR); | |
765 return -1; | |
766 } | |
767 } | |
768 | |
769 digval = tc - '0'; | |
770 if (digval > 9) | |
771 digval -= 7; | |
772 | |
773 // if it's not in the range of a hex digit, error out | |
774 if (tc < '0' || (tc > '9' && tc < 'A') || tc > 'F') | |
775 { | |
776 (*optr)--; | |
777 if (valtype & 4) | |
778 { | |
779 *tval = decval; | |
780 return 0; | |
781 } | |
782 // if we're in hex/bin mode and run to the end of the number | |
783 // we must have a binary constant or an error | |
784 // if the previous character is B, then we have binary | |
785 // else we have error since hex would require a terminating H | |
786 // which would be caught above | |
787 if (valtype == 8 && toupper(*(*optr)) == 'B') | |
788 { | |
789 *tval = binval; | |
790 return 0; | |
791 } | |
792 errorp1(ERR_BADEXPR); | |
793 return -1; | |
794 } | |
795 | |
796 // if we have any characters past the end of the B, it's not binary | |
797 if (bindone == 1) | |
798 bindone = 0; | |
799 if (tc == 'B') | |
800 bindone = 1; | |
801 if (digval > 1) | |
802 valtype &= 14; | |
803 else if (digval > 7) | |
804 valtype &= 13; | |
805 else if (digval > 9) | |
806 valtype &= 11; | |
807 | |
808 if (valtype & 8) | |
809 { | |
810 hexval = (hexval << 4) | digval; | |
811 } | |
812 if (valtype & 4) | |
813 { | |
814 decval = decval * 10 + digval; | |
815 } | |
816 if (valtype & 2) | |
817 { | |
818 octval = (octval << 3) | digval; | |
819 } | |
820 if (valtype & 1 && !bindone) | |
821 { | |
822 binval = (binval << 1) | digval; | |
823 } | |
824 | |
825 } | |
826 // can't get here from there | |
827 } | |
828 | |
829 // returns -1 if the expression cannot be parsed | |
830 // and returns -2 if there is an undefined symbol reference | |
831 // resulting value will be in *val; undefined symbols are parsed as | |
832 // value 0 but cl -> undef will be set. | |
833 int eval_expr(asmstate_t *as, sourceline_t *cl, char **optr, int *val) | |
834 { | |
835 int left; | |
836 int right; | |
837 char oper; | |
838 int rval; | |
839 | |
840 // by default, return 0 in val | |
841 *val = 0; | |
842 cl -> undef = 0; | |
843 | |
844 rval = eval_term(as, cl, optr, &left); | |
845 if (rval < 0) | |
846 return rval; | |
847 | |
848 nextop: | |
849 oper = **optr; | |
850 | |
851 // end of expr | |
852 if (isspace(oper) || oper == ',' || oper == '\0' || oper == ']' || oper == ')') | |
853 goto retleft; | |
854 | |
855 // unrecognized chars | |
856 if (!strchr("+-*/%", oper)) | |
857 goto retleft; | |
858 | |
859 (*optr)++; | |
860 | |
861 rval = eval_term(as, cl, optr, &right); | |
862 // propagate error | |
863 if (rval < 0) | |
864 return rval; | |
865 | |
866 // do the operation and put it in "left" | |
867 switch (oper) | |
868 { | |
869 case '+': | |
870 left += right; | |
871 break; | |
872 | |
873 case '-': | |
874 left -= right; | |
875 break; | |
876 | |
877 case '*': | |
878 left *= right; | |
879 break; | |
880 | |
881 case '/': | |
882 left /= right; | |
883 break; | |
884 | |
885 case '%': | |
886 left %= right; | |
887 break; | |
888 } | |
889 | |
890 goto nextop; | |
891 | |
892 retleft: | |
893 *val = left; | |
894 return 0; | |
895 } |