1 /***********************************************************************
2 *
3 * AVRA - Assembler for the Atmel AVR microcontroller series
4 *
5 * Copyright (C) 1998-2020 The AVRA Authors
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; see the file COPYING. If not, write to
19 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, USA.
21 *
22 *
23 * Authors of AVRA can be reached at:
24 * email: jonah@omegav.ntnu.no, tobiw@suprafluid.com
25 * www: https://github.com/Ro5bert/avra
26 */
27
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <ctype.h>
32 #include <time.h>
33
34 #include "misc.h"
35 #include "avra.h"
36 #include "args.h"
37
38
39 /* Special fgets. Like fgets, but with better check for CR, LF and FF and without the ending \n char */
40 /* size must be >=2. No checks for s=NULL, size<2 or stream=NULL. B.A. */
41 char *
fgets_new(struct prog_info * pi,char * s,int size,FILE * stream)42 fgets_new(struct prog_info *pi, char *s, int size, FILE *stream)
43 {
44 int c;
45 char *ptr=s;
46 do {
47 if ((c=fgetc(stream))==EOF || IS_ENDLINE(c)) /* Terminate at chr$ 10,12,13,0 and EOF */
48 break;
49 /* concatenate lines terminated with \ only... */
50 if (c == '\\') {
51 /* only newline and cr may follow... */
52 if ((c=fgetc(stream))==EOF)
53 break;
54
55 if (!IS_ENDLINE(c)) { /* Terminate at chr$ 10,12,13,0 and EOF */
56 *ptr++ = '\\'; /* no concatenation, insert it */
57 } else {
58 /* mit be additional LF (DOS) */
59 c=fgetc(stream);
60 if (IS_ENDLINE(c))
61 c=fgetc(stream);
62
63 if (c == EOF)
64 break;
65 }
66 }
67
68 *ptr++=c;
69 } while (--size);
70 if ((c==EOF) && (ptr==s)) /* EOF and no chars read -> that's all folks */
71 return NULL;
72 if (!size) {
73 print_msg(pi, MSGTYPE_ERROR, "Line too long");
74 return NULL;
75 }
76 *ptr=0;
77 if (c==12) /* Check for Formfeed */
78 print_msg(pi, MSGTYPE_WARNING, "Found Formfeed char. Please remove it.");
79 if (c==13) { /* Check for CR LF sequence (DOS/ Windows line termination) */
80 if ((c=fgetc(stream)) != 10) {
81 ungetc(c,stream);
82 }
83 }
84 return s;
85 }
86
87
88 /* Parse given assembler file. */
89 int
parse_file(struct prog_info * pi,const char * filename)90 parse_file(struct prog_info *pi, const char *filename)
91 {
92 #if debug == 1
93 printf("parse_file\n");
94 #endif
95 int ok;
96 int loopok;
97 struct file_info *fi;
98 struct include_file *include_file;
99 ok = True;
100 if ((fi=malloc(sizeof(struct file_info)))==NULL) {
101 print_msg(pi, MSGTYPE_OUT_OF_MEM,NULL);
102 return (False);
103 }
104 pi->fi = fi;
105 if (pi->pass == PASS_1) {
106 if ((include_file = malloc(sizeof(struct include_file)))==NULL) {
107 print_msg(pi, MSGTYPE_OUT_OF_MEM, NULL);
108 free(fi);
109 return (False);
110 }
111 include_file->next = NULL;
112 if (pi->last_include_file) {
113 pi->last_include_file->next = include_file;
114 include_file->num = pi->last_include_file->num + 1;
115 } else {
116 pi->first_include_file = include_file;
117 include_file->num = 0;
118 }
119 pi->last_include_file = include_file;
120 if ((include_file->name = malloc(strlen(filename) + 1))==NULL) {
121 print_msg(pi, MSGTYPE_OUT_OF_MEM, NULL);
122 free(fi);
123 return (False);
124 }
125 strcpy(include_file->name, filename);
126 } else { /* PASS 2 */
127 for (include_file = pi->first_include_file; include_file; include_file = include_file->next) {
128 if (!strcmp(include_file->name, filename))
129 break;
130 }
131 }
132 if (!include_file) {
133 print_msg(pi, MSGTYPE_ERROR, "Internal assembler error");
134 free(fi);
135 return (False);
136 }
137 fi->include_file = include_file;
138 fi->line_number = 0;
139 fi->exit_file = False;
140 #if debug == 1
141 printf("Opening %s\n",filename);
142 #endif
143 if ((fi->fp = fopen(filename, "r"))==NULL) {
144 perror(filename);
145 free(fi);
146 return (False);
147 }
148 loopok = True;
149 while (loopok && !fi->exit_file) {
150 if (fgets_new(pi,fi->buff, LINEBUFFER_LENGTH, fi->fp)) {
151 fi->line_number++;
152 pi->list_line = fi->buff;
153 ok = parse_line(pi, fi->buff);
154 #if debug == 1
155 printf("parse_line was %i\n", ok);
156 #endif
157 if (ok) {
158 if ((pi->pass == PASS_2) && pi->list_line && pi->list_on)
159 fprintf(pi->list_file, " %s\n", pi->list_line);
160 if (pi->error_count >= pi->max_errors) {
161 print_msg(pi, MSGTYPE_MESSAGE, "Maximum error count reached. Exiting...");
162 loopok = False;
163 }
164 } else {
165 loopok = False;
166 }
167 } else {
168 loopok = False;
169 if (!feof(fi->fp)) {
170 ok = False;
171 perror(filename);
172 }
173 }
174 }
175 fclose(fi->fp);
176 free(fi);
177 return (ok);
178 }
179
180 /* Parse one line. */
181 int
parse_line(struct prog_info * pi,char * line)182 parse_line(struct prog_info *pi, char *line)
183 {
184 char *ptr=NULL;
185 int k;
186 int flag=0, i;
187 int global_label = False;
188 char temp[LINEBUFFER_LENGTH];
189 struct label *label = NULL;
190 struct macro_call *macro_call;
191 int len;
192
193 while (IS_HOR_SPACE(*line)) line++; /* At first remove leading spaces / tabs */
194 if (IS_END_OR_COMMENT(*line)) /* Skip comment line or empty line */
195 return (True);
196 /* Filter out .stab debugging information */
197 /* .stabs sometimes contains colon : symbol - might be interpreted as label */
198 if (*line == '.') { /* minimal slowdown of existing code */
199 if (strncmp(temp,".stabs ",7) == 0) { /* compiler output is always lower case */
200 strcpy(temp,line); /* TODO : Do we need this temp variable ? Please check */
201 return parse_stabs(pi, temp);
202 }
203 if (strncmp(temp,".stabn ",7) == 0) {
204 strcpy(temp,line);
205 return parse_stabn(pi, temp);
206 }
207 }
208 /* Meta information translation */
209 ptr=line;
210 k=0;
211 len = strlen(ptr);
212 while ((ptr=strchr(ptr, '%')) != NULL) {
213 if (!strncmp(ptr, "%MINUTE%", 8)) { /* Replacement always shorter than tag -> no length check */
214 k=strftime(ptr, 3, "%M", localtime(&pi->time));
215 memmove(ptr+k, ptr+8, len - (ptr+8 - line) + 1);
216 ptr += k;
217 len -= 8-k;
218 } else if (!strncmp(ptr, "%HOUR%", 6)) {
219 k=strftime(ptr, 3, "%H", localtime(&pi->time));
220 memmove(ptr+k, ptr+6, len - (ptr+6 - line) + 1);
221 ptr += k;
222 len -= 6-k;
223 } else if (!strncmp(ptr, "%DAY%", 5)) {
224 k=strftime(ptr, 3, "%d", localtime(&pi->time));
225 memmove(ptr+k, ptr+5, len - (ptr+5 - line) + 1);
226 ptr += k;
227 len -= 5-k;
228 } else if (!strncmp(ptr, "%MONTH%", 7)) {
229 k=strftime(ptr, 3, "%m", localtime(&pi->time));
230 memmove(ptr+k, ptr+7, len - (ptr+7 - line) + 1);
231 ptr += k;
232 len -= 7-k;
233 } else if (!strncmp(ptr, "%YEAR%", 6)) {
234 k=strftime(ptr, 5, "%Y", localtime(&pi->time));
235 memmove(ptr+k, ptr+6, len - (ptr+6 - line) + 1);
236 ptr += k;
237 len -= 6-k;
238 } else {
239 ptr++;
240 }
241 }
242
243 strcpy(pi->fi->scratch,line);
244
245 for (i = 0; IS_LABEL(pi->fi->scratch[i]) || (pi->fi->scratch[i] == ':'); i++)
246 if (pi->fi->scratch[i] == ':') { /* it is a label */
247 pi->fi->scratch[i] = '\0';
248 if (pi->pass == PASS_1) {
249 for (macro_call = pi->macro_call; macro_call; macro_call = macro_call->prev_on_stack) {
250 for (label = pi->macro_call->first_label; label; label = label->next) {
251 if (!nocase_strcmp(label->name, &pi->fi->scratch[0])) {
252 print_msg(pi, MSGTYPE_ERROR, "Can't redefine local label %s", &pi->fi->scratch[0]);
253 break;
254 }
255 }
256 }
257 if (test_label(pi,&pi->fi->scratch[0],"Can't redefine label %s")!=NULL)
258 break;
259 if (test_variable(pi,&pi->fi->scratch[0],"%s have already been defined as a .SET variable")!=NULL)
260 break;
261 if (test_constant(pi,&pi->fi->scratch[0],"%s has already been defined as a .EQU constant")!=NULL)
262 break;
263 label = malloc(sizeof(struct label));
264 if (!label) {
265 print_msg(pi, MSGTYPE_OUT_OF_MEM, NULL);
266 return (False);
267 }
268 label->next = NULL;
269 label->name = malloc(strlen(&pi->fi->scratch[0]) + 1);
270 if (!label->name) {
271 print_msg(pi, MSGTYPE_OUT_OF_MEM, NULL);
272 return (False);
273 }
274 strcpy(label->name, &pi->fi->scratch[0]);
275 label->value = pi->segment->addr;
276
277 if (pi->macro_call && !global_label) {
278 if (pi->macro_call->last_label)
279 pi->macro_call->last_label->next = label;
280 else
281 pi->macro_call->first_label = label;
282 pi->macro_call->last_label = label;
283 } else {
284 if (pi->last_label)
285 pi->last_label->next = label;
286 else
287 pi->first_label = label;
288 pi->last_label = label;
289 }
290 }
291 i++;
292 while (IS_HOR_SPACE(pi->fi->scratch[i]) && !IS_END_OR_COMMENT(pi->fi->scratch[i])) i++;
293 if (IS_END_OR_COMMENT(pi->fi->scratch[i])) {
294 if ((pi->pass == PASS_2) && pi->list_on) { /* Diff tilpassing */
295 fprintf(pi->list_file, " %s\n", pi->list_line);
296 pi->list_line = NULL;
297 }
298 return (True);
299 }
300 memmove(pi->fi->scratch, &pi->fi->scratch[i], strlen(&pi->fi->scratch[i])+1);
301 break;
302 }
303
304 if ((pi->fi->scratch[0] == '.') || (pi->fi->scratch[0] == '#')) {
305 pi->fi->label = label;
306 flag = parse_directive(pi);
307 if ((pi->pass == PASS_2) && pi->list_on && pi->list_line) { /* Diff tilpassing */
308 fprintf(pi->list_file, " %s\n", pi->list_line);
309 pi->list_line = NULL;
310 }
311 return (flag);
312 } else {
313 return parse_mnemonic(pi);
314 }
315 }
316
317
318 /* Get the next token, and terminate the last one.
319 * Termination identifier is specified. */
320 char *
get_next_token(char * data,int term)321 get_next_token(char *data, int term)
322 {
323 int i = 0, j, anti_comma = False;
324 /* XXX: this does the same thing for TERM_END and TERM_COMMA? */
325 switch (term) {
326 case TERM_END:
327 /* Skip to next comma or EOL or start of comment, taking into account
328 * the possibility for ',' or ';' to be inside quotes. */
329 while (((data[i] != ',') || anti_comma) && ((data[i] != ';') || anti_comma) && !IS_ENDLINE(data[i])) {
330 if ((data[i] == '\'') || (data[i] == '"'))
331 anti_comma = anti_comma ? False : True;
332 i++;
333 }
334 break;
335 case TERM_SPACE:
336 /* Skip to next horizontal space or EOL or start of comment. */
337 while (!IS_HOR_SPACE(data[i]) && !IS_END_OR_COMMENT(data[i])) i++;
338 break;
339 case TERM_DASH:
340 /* Skip to next dash or EOL or start of comment. */
341 while ((data[i] != '-') && !IS_END_OR_COMMENT(data[i])) i++;
342 break;
343 case TERM_COLON:
344 /* Skip to next colon or EOL. */
345 while ((data[i] != ':') && !IS_ENDLINE(data[i])) i++;
346 break;
347 case TERM_DOUBLEQUOTE:
348 /* Skip to next double quote or EOL. */
349 while ((data[i] != '"') && !IS_ENDLINE(data[i])) i++;
350 break;
351 case TERM_COMMA:
352 /* Skip to next comma or EOL or start of comment, taking into account
353 * the possibility for ',' or ';' to be inside quotes. */
354 while (((data[i] != ',') || anti_comma) && ((data[i] != ';') || anti_comma) && !IS_ENDLINE(data[i])) {
355 if ((data[i] == '\'') || (data[i] == '"'))
356 anti_comma = anti_comma ? False : True;
357 i++;
358 }
359 break;
360 case TERM_EQUAL:
361 /* Skip to next equals or EOL or start of comment. */
362 while ((data[i] != '=') && !IS_END_OR_COMMENT(data[i])) i++;
363 break;
364 }
365 /* If we hit EOL or a comment, return null. */
366 if (IS_END_OR_COMMENT(data[i])) {
367 /* Null-out the EOL/start of comment. */
368 data[i--] = '\0';
369 /* Null-out everything until the first non-horizontal whitespace
370 * character. */
371 while (IS_HOR_SPACE(data[i])) data[i--] = '\0';
372 return (0);
373 }
374 j = i - 1;
375 /* Null-out all horizontal whitespace before the terminator. */
376 while (IS_HOR_SPACE(data[j])) data[j--] = '\0';
377 /* Null-out the terminator itself. */
378 data[i++] = '\0';
379 /* Skip over all horizontal whitespace after the terminator. */
380 while (IS_HOR_SPACE(data[i]) && !IS_END_OR_COMMENT(data[i])) i++;
381 /* If we hit EOL or a comment, return null. */
382 if (IS_END_OR_COMMENT(data[i]))
383 return (0);
384 /* i should now be the index of the first non-whitespace character after
385 * the terminator. */
386 return (&data[i]);
387 }
388
389 /* end of parser.c */
390
391