-
Notifications
You must be signed in to change notification settings - Fork 2
/
a85eval.c
472 lines (373 loc) · 11.8 KB
/
a85eval.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
/*
HEADER: CUG267;
TITLE: 8085 Cross-Assembler (Portable);
FILENAME: A85EVAL.C;
VERSION: 0.1;
DATE: 08/27/1988;
SEE-ALSO: A85.H;
AUTHORS: William C. Colley III;
*/
/*
8085 Cross-Assembler in Portable C
Copyright (c) 1985,1987 William C. Colley, III
Revision History:
Ver Date Description
0.0 AUG 1987 Derived from version 3.4 of my portable 6800/6801
cross-assembler. WCC3.
0.1 AUG 1988 Fixed a bug in the command line parser that puts it
into a VERY long loop if the user types a command line
like "A85 FILE.ASM -L". WCC3 per Alex Cameron.
DEC 2013 fixed as per fixed a68eval.c for lcc-win32 HRJ
This file contains the assembler's expression evaluator and lexical analyzer.
The lexical analyzer chops the input character stream up into discrete tokens
that are processed by the expression analyzer and the line assembler. The
expression analyzer processes the token stream into unsigned results of
arithmetic expressions.
*/
/* Get global goodies: */
#include "a85.h"
#include <ctype.h>
#include <string.h>
/* from A18eval.c HRJ */
/* local prototypes HRJ*/
static unsigned eval(unsigned);
static void exp_error(char);
void unlex(void);
TOKEN *lex(void);
static void make_number(unsigned);
int popc(void);
void pushc(char);
int isalph(char); /* was isalph(int) HRJ */
static int isnum(char), ischar(char), ishex(char);
static int isalpnum(char c);
/* external prototypes HRJ*/
void error(char);
void pops(char *), trash(void);
OPCODE *find_operator(char *);
SYMBOL *find_symbol(char *);
void asm_line(void);
void lclose(void), lopen(char *), lputs(void);
void hclose(void), hopen(char *), hputc(unsigned);
void error(char), fatal_error(char *), warning(char *);
void hseek(unsigned);
void unlex(void);
/* above from A68eval.c HRJ */
/* Get access to global mailboxes defined in A85.C: */
extern char lline[]; //HRJ was line[] in A85.c
extern int filesp, forwd, pass;
extern unsigned pc;
extern FILE *filestk[], *source;
extern TOKEN token;
/* Expression analysis routine. The token stream from the lexical */
/* analyzer is processed as an arithmetic expression and reduced to an */
/* unsigned value. If an error occurs during the evaluation, the */
/* global flag forwd is set to indicate to the line assembler that it */
/* should not base certain decisions on the result of the evaluation. */
static int bad;
unsigned expr()
{
SCRATCH unsigned u;
// unsigned eval();
bad = FALSE;
u = eval(START);
return bad ? 0 : u;
}
static unsigned eval(pre)
unsigned pre;
{
register unsigned op, u, v;
// TOKEN *lex();
// void exp_error(), unlex();
for (;;) {
u = op = lex() -> valu;
switch (token.attr & TYPE) {
case REG: exp_error('S'); break;
case SEP: // HRJ in a68eval.c if (pre != START) unlex();
case EOL: unlex(); exp_error('E'); return (u); /* return had no value HRJ*/
case OPR: if (!(token.attr & UNARY)) { exp_error('E'); break; }
// HRJ was u = eval((op == '+' || op == '-') ?
// (unsigned) UOP1 : token.attr & PREC);
u = (op == '*' ? pc :
eval((op == '+' || op == '-') ?
(unsigned) UOP1 : token.attr & PREC)); //HRJ from a68eval.c
switch (op) {
case '-': u = word(0-u); break; /* had (-u) HRJ */
case NOT: u ^= 0xffff; break;
case HIGH: u = high(u); break;
case LOW: u = low(u); break;
}
case VAL:
case STR: for (;;) {
op = lex() -> valu;
switch (token.attr & TYPE) {
case REG: exp_error('S'); break;
case SEP: //HRJ in a68eval.c if (pre != START) unlex();
case EOL: if (pre == LPREN) exp_error('(');
unlex(); return u; //HRJ no unlex() in a68
case STR:
case VAL: exp_error('E'); break;
case OPR: if (!(token.attr & BINARY)) {
exp_error('E'); break;
}
if ((token.attr & PREC) >= pre) {
unlex(); return u;
}
if (op != ')')
v = eval(token.attr & PREC);
switch (op) {
case '+': u += v; break;
case '-': u -= v; break;
case '*': u *= v; break;
case '/': u /= v; break;
case MOD: u %= v; break;
case AND: u &= v; break;
case OR: u |= v; break;
case XOR: u ^= v; break;
case '<': u = u < v; break;
case LE: u = u <= v; break;
case '=': u = u == v; break;
case GE: u = u >= v; break;
case '>': u = u > v; break;
case NE: u = u != v; break;
case SHL: if (v > 15)
exp_error('E');
else u <<= v;
break;
case SHR: if (v > 15)
exp_error('E');
else u >>= v;
break;
case ')': if (pre == LPREN)
return u;
exp_error('(');
break;
}
clamp(u);
break;
}
}
break;
}
}
}
static void exp_error(char c)
{
forwd = bad = TRUE; error(c);
}
/* Lexical analyzer. The source input character stream is chopped up */
/* into its component parts and the pieces are evaluated. Symbols are */
/* looked up, operators are looked up, etc. Everything gets reduced */
/* to an attribute word, a numeric value, and (possibly) a string */
/* value. */
static int oldt = FALSE;
static int quote = FALSE;
TOKEN *lex(void)
{
SCRATCH char c, *p; // HRJ d not needed
SCRATCH unsigned b;
SCRATCH OPCODE *o;
SCRATCH SYMBOL *s;
/* OPCODE *find_operator();
SYMBOL *find_symbol();
void exp_error(), make_number(), pops(), pushc(), trash(); */
if (oldt) { oldt = FALSE; return &token; }
trash();
if (isalph(c = popc())) {
pushc(c); pops(token.sval);
if (!strcmp(token.sval,"$")) {
token.attr = VAL;
token.valu = pc;
}
else if ((o = find_operator(token.sval))) {
token.attr = o -> attr;
token.valu = o -> valu;
}
else {
token.attr = VAL; token.valu = 0;
if ((s = find_symbol(token.sval))) {
token.valu = s -> valu;
if (pass == 2 && s -> attr & FORWD) forwd = TRUE;
}
else exp_error('U');
}
}
else if (isnum(c)) {
pushc(c); pops(token.sval);
for (p = token.sval; *p; ++p);
switch (toupper(*--p)) {
case 'B': b = 2; break;
case 'O':
case 'Q': b = 8; break;
default: ++p;
case 'D': b = 10; break;
case 'H': b = 16; break;
}
*p = '\0'; make_number(b);
}
else switch (c) {
//HRJ a68 has %, @, $, # cases
case '(': token.attr = UNARY + LPREN + OPR;
goto opr1;
case ')': token.attr = BINARY + RPREN + OPR;
goto opr1;
case '+': token.attr = BINARY + UNARY + ADDIT + OPR;
goto opr1;
case '-': token.attr = BINARY + UNARY + ADDIT + OPR;
goto opr1;
case '*': token.attr = BINARY + UNARY + MULT + OPR;
goto opr1;
case '/': token.attr = BINARY + MULT + OPR;
opr1: token.valu = c;
break;
case '<': token.valu = c;
if ((c = popc()) == '=') token.valu = LE;
else if (c == '>') token.valu = NE;
else pushc(c);
goto opr2;
case '=': token.valu = c;
if ((c = popc()) == '<') token.valu = LE;
else if (c == '>') token.valu = GE;
else pushc(c);
goto opr2;
case '>': token.valu = c;
if ((c = popc()) == '<') token.valu = NE;
else if (c == '=') token.valu = GE;
else pushc(c);
opr2: token.attr = BINARY + RELAT + OPR;
break;
case '\'':
case '"': quote = TRUE; token.attr = STR;
//HRJ following different in a68eval.c
// for (p = token.sval; ; ++p) {
// if ((d = popc()) == '\n') { exp_error('"'); break; }
// if ((*p = d) == c && (d=popc()) != c) break;
// }
// pushc(d); //HRJ end of difference
//HRJ I think this is a problem because popc() is evoked twice per FOR loop
// but only pushc() once after breaking loop. May miss newline.
//HRJ replaced above with following from a68eval.c
for (p = token.sval; (*p = popc()) != c; ++p)
if (*p == '\n') { exp_error('"'); break; }
//HRJ end of replacement. This fixed problem where expressions like
// DB<tab>"B";<crlf> or DB<tab>"B"<tab>;comment were "E" errors
*p = '\0'; quote = FALSE;
if ((token.valu = token.sval[0]) && token.sval[1])
token.valu = (token.valu << 8) + token.sval[1];
break;
case ',': token.attr = SEP;
break;
case '\n': token.attr = EOL;
break;
}
return &token;
}
static void make_number(base)
unsigned base;
{
SCRATCH char *p;
SCRATCH unsigned d;
/* void exp_error(); */
token.attr = VAL;
token.valu = 0;
for (p = token.sval; *p; ++p) {
d = toupper(*p) - (isnum(*p) ? '0' : 'A' - 10);
token.valu = token.valu * base + d;
if (!ishex(*p) || d >= base) { exp_error('D'); break; }
}
clamp(token.valu);
return;
}
int isalph(char c) // HRJ slightly different from a68eval.c
{
return (c >= '@' && c <= '~') || (c >= '#' && c <= '&') ||
c == '!' || c == '.' || c == ':' || c == '?';
}
static int isnum(char c)
{
return c >= '0' && c <= '9';
}
static int ishex(char c)
{
return isnum(c) || ((c = toupper(c)) >= 'A' && c <= 'F');
}
static int isalpnum(char c) //HRJ changed to avoid ctype.h conflict
{
return isalph(c) || isnum(c);
}
/* Push back the current token into the input stream. One level of */
/* pushback is supported. */
void unlex()
{
oldt = TRUE;
return;
}
/* Get an alphanumeric string into the string value part of the */
/* current token. Leading blank space is trashed. */
void pops(s)
char *s;
{
// void pushc(), trash();
trash();
for (; isalpnum(*s = popc()); ++s);
pushc(*s); *s = '\0';
return;
}
/* Trash blank space and push back the character following it. */
void trash()
{
SCRATCH char c;
// void pushc();
while ((c = popc()) == ' ');
pushc(c);
return;
}
/* Get character from input stream. This routine does a number of */
/* other things while it's passing back characters. All control */
/* characters except \t and \n are ignored. \t is mapped into ' '. */
/* Semicolon is mapped to \n. In addition, a copy of all input is set */
/* up in a line buffer for the benefit of the listing. */
static int oldc, eol;
static char *lptr;
int popc(void)
{
SCRATCH int c;
if (oldc) { c = oldc; oldc = '\0'; return c; }
if (eol) return '\n';
for (;;) {
if ((c = getc(source)) != EOF && (c &= 0377) == ';' && !quote) {
do *lptr++ = c;
while ((c = getc(source)) != EOF && (c &= 0377) != '\n');
}
if (c == EOF) c = '\n';
// HRJ could try if (c == '!' && !quote) { lptr ="!\n\0"
// to treat ! as instruction seperator, force scan to break line
// but too hard to force scanner to deal with opr!opr
if ((*lptr++ = c) >= ' ' && c <= '~') return c;
if (c == '\n') { eol = TRUE; *lptr = '\0'; return '\n'; }
if (c == '\t') return quote ? '\t' : ' ';
}
}
/* Push character back onto input stream. Only one level of push-back */
/* supported. \0 cannot be pushed back, but nobody would want to. */
void pushc(char c)
{
oldc = c;
return;
}
/* Begin new line of source input. This routine returns non-zero if */
/* EOF has been reached on the main source file, zero otherwise. */
int newline()
{
// void fatal_error();
oldc = '\0'; lptr = lline;
oldt = eol = FALSE;
while (feof(source)) {
if (ferror(source)) fatal_error(ASMREAD);
if (filesp) {
fclose(source);
source = filestk[--filesp];
}
else return TRUE;
}
return FALSE;
}