forked from KolibriOS/kolibrios
413 lines
11 KiB
C
413 lines
11 KiB
C
/***********************************************************************
|
|
*
|
|
* avra - Assembler for the Atmel AVR microcontroller series
|
|
*
|
|
* Copyright (C) 1998-2004 Jon Anders Haugum, Tobias Weber
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; see the file COPYING. If not, write to
|
|
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
* Boston, MA 02111-1307, USA.
|
|
*
|
|
*
|
|
* Authors of avra can be reached at:
|
|
* email: jonah@omegav.ntnu.no, tobiw@suprafluid.com
|
|
* www: http://sourceforge.net/projects/avra
|
|
*
|
|
*
|
|
* SourceForge.net: Detail:713798 Strings are not always correctly handled
|
|
* Change made by JEG 5-01-03
|
|
*
|
|
* global keyword is now .global to match common sytnax. TW 10-11-05
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <time.h>
|
|
|
|
#include "misc.h"
|
|
#include "avra.h"
|
|
#include "args.h"
|
|
|
|
|
|
/* Special fgets. Like fgets, but with better check for CR, LF and FF and without the ending \n char */
|
|
/* size must be >=2. No checks for s=NULL, size<2 or stream=NULL. B.A. */
|
|
char *fgets_new(struct prog_info *pi, char *s, int size, FILE *stream)
|
|
{
|
|
int c;
|
|
char *ptr=s;
|
|
do {
|
|
if((c=fgetc(stream))==EOF || IS_ENDLINE(c)) // Terminate at chr$ 10,12,13,0 and EOF
|
|
break;
|
|
/*
|
|
** concatenate lines terminated with \ only...
|
|
*/
|
|
if (c == '\\')
|
|
{
|
|
/* only newline and cr may follow... */
|
|
if((c=fgetc(stream))==EOF)
|
|
break;
|
|
|
|
if(!IS_ENDLINE(c)) // Terminate at chr$ 10,12,13,0 and EOF
|
|
{
|
|
*ptr++ = '\\'; // no concatenation, insert it
|
|
}
|
|
else
|
|
{
|
|
// mit be additional LF (DOS)
|
|
c=fgetc(stream);
|
|
if (IS_ENDLINE(c))
|
|
c=fgetc(stream);
|
|
|
|
if (c == EOF)
|
|
break;
|
|
}
|
|
}
|
|
|
|
*ptr++=c;
|
|
} while(--size);
|
|
if((c==EOF) && (ptr==s)) // EOF and no chars read -> that's all folks
|
|
return NULL;
|
|
if(!size) {
|
|
print_msg(pi, MSGTYPE_ERROR, "Line to long");
|
|
return NULL;
|
|
}
|
|
*ptr=0;
|
|
if(c==12) // Check for Formfeed (Bug [1462886])
|
|
print_msg(pi, MSGTYPE_WARNING, "Found Formfeed char. Please remove it.");
|
|
if(c==13) { // Check for CR LF sequence (DOS/ Windows line termination)
|
|
if((c=fgetc(stream)) != 10) {
|
|
ungetc(c,stream);
|
|
print_msg(pi, MSGTYPE_WARNING, "Found CR (0x0d) without LF (0x0a). Please add a LF.");
|
|
}
|
|
}
|
|
return s;
|
|
}
|
|
|
|
|
|
/*
|
|
* Parses given assembler file
|
|
*/
|
|
|
|
int parse_file(struct prog_info *pi, char *filename)
|
|
{
|
|
#if debug == 1
|
|
printf("parse_file\n");
|
|
#endif
|
|
int ok;
|
|
int loopok;
|
|
struct file_info *fi;
|
|
struct include_file *include_file;
|
|
ok = True;
|
|
if((fi=malloc(sizeof(struct file_info)))==NULL) {
|
|
print_msg(pi, MSGTYPE_OUT_OF_MEM,NULL);
|
|
return(False);
|
|
}
|
|
pi->fi = fi;
|
|
if(pi->pass == PASS_1) {
|
|
if((include_file = malloc(sizeof(struct include_file)))==NULL) {
|
|
print_msg(pi, MSGTYPE_OUT_OF_MEM, NULL);
|
|
free(fi);
|
|
return(False);
|
|
}
|
|
include_file->next = NULL;
|
|
if(pi->last_include_file) {
|
|
pi->last_include_file->next = include_file;
|
|
include_file->num = pi->last_include_file->num + 1;
|
|
} else {
|
|
pi->first_include_file = include_file;
|
|
include_file->num = 0;
|
|
}
|
|
pi->last_include_file = include_file;
|
|
if((include_file->name = malloc(strlen(filename) + 1))==NULL) {
|
|
print_msg(pi, MSGTYPE_OUT_OF_MEM, NULL);
|
|
free(fi);
|
|
return(False);
|
|
}
|
|
strcpy(include_file->name, filename);
|
|
} else { // PASS 2
|
|
for(include_file = pi->first_include_file; include_file; include_file = include_file->next) {
|
|
if(!strcmp(include_file->name, filename))
|
|
break;
|
|
}
|
|
}
|
|
if(!include_file) {
|
|
print_msg(pi, MSGTYPE_ERROR, "Internal assembler error");
|
|
free(fi);
|
|
return(False);
|
|
}
|
|
fi->include_file = include_file;
|
|
fi->line_number = 0;
|
|
fi->exit_file = False;
|
|
#if debug == 1
|
|
printf("Opening %s\n",filename);
|
|
#endif
|
|
if((fi->fp = fopen(filename, "r"))==NULL) {
|
|
perror(filename);
|
|
free(fi);
|
|
return(False);
|
|
}
|
|
loopok = True;
|
|
while(loopok && !fi->exit_file) {
|
|
if(fgets_new(pi,fi->buff, LINEBUFFER_LENGTH, fi->fp)) {
|
|
fi->line_number++;
|
|
pi->list_line = fi->buff;
|
|
ok = parse_line(pi, fi->buff);
|
|
#if debug == 1
|
|
printf("parse_line was %i\n", ok);
|
|
#endif
|
|
if(ok) {
|
|
if((pi->pass == PASS_2) && pi->list_line && pi->list_on)
|
|
fprintf(pi->list_file, " %s\n", pi->list_line);
|
|
if(pi->error_count >= pi->max_errors) {
|
|
print_msg(pi, MSGTYPE_MESSAGE, "Maximum error count reached. Exiting...");
|
|
loopok = False;
|
|
}
|
|
} else {
|
|
loopok = False;
|
|
}
|
|
} else {
|
|
loopok = False;
|
|
if(!feof(fi->fp)) {
|
|
ok = False;
|
|
perror(filename);
|
|
}
|
|
}
|
|
}
|
|
fclose(fi->fp);
|
|
free(fi);
|
|
return(ok);
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
*
|
|
* function parse_line
|
|
*
|
|
* Parses one line
|
|
*
|
|
****************************************************************************/
|
|
|
|
|
|
int parse_line(struct prog_info *pi, char *line)
|
|
{
|
|
char *ptr=NULL;
|
|
int k;
|
|
int flag=0, i;
|
|
int global_label = False;
|
|
char temp[LINEBUFFER_LENGTH];
|
|
struct label *label = NULL;
|
|
struct macro_call *macro_call;
|
|
|
|
while(IS_HOR_SPACE(*line)) line++; /* At first remove leading spaces / tabs */
|
|
if(IS_END_OR_COMMENT(*line)) /* Skip comment line or empty line */
|
|
return(True);
|
|
/* Filter out .stab debugging information */
|
|
/* .stabs sometimes contains colon : symbol - might be interpreted as label */
|
|
if(*line == '.') { /* minimal slowdown of existing code */
|
|
if(strncmp(temp,".stabs ",7) == 0 ) { /* compiler output is always lower case */
|
|
strcpy(temp,line); /* TODO : Do we need this temp variable ? Please check */
|
|
return parse_stabs( pi, temp );
|
|
}
|
|
if(strncmp(temp,".stabn ",7) == 0 ) {
|
|
strcpy(temp,line);
|
|
return parse_stabn( pi, temp );
|
|
}
|
|
}
|
|
/* Meta information translation */
|
|
ptr=line;
|
|
k=0;
|
|
while((ptr=strchr(ptr, '%')) != NULL) {
|
|
if(!strncmp(ptr, "%MINUTE%", 8) ) { /* Replacement always shorter than tag -> no length check */
|
|
k=strftime(ptr,3,"%M", localtime(&pi->time));
|
|
strcpy(ptr+k,ptr+8);
|
|
ptr+=k;
|
|
continue;
|
|
}
|
|
if(!strncmp(ptr, "%HOUR%", 6) ) {
|
|
k=strftime(ptr,3,"%H", localtime(&pi->time));
|
|
strcpy(ptr+k,ptr+6);
|
|
ptr+=k;
|
|
continue;
|
|
}
|
|
if(!strncmp(ptr, "%DAY%", 5) ) {
|
|
k=strftime(ptr,3,"%d", localtime(&pi->time));
|
|
strcpy(ptr+k,ptr+5);
|
|
ptr+=k;
|
|
continue;
|
|
}
|
|
if(!strncmp(ptr, "%MONTH%", 7) ) {
|
|
k=strftime(ptr,3,"%m", localtime(&pi->time));
|
|
strcpy(ptr+k,ptr+7);
|
|
ptr+=k;
|
|
continue;
|
|
}
|
|
if(!strncmp(ptr, "%YEAR%", 6) ) {
|
|
k=strftime(ptr,5,"%Y", localtime(&pi->time));
|
|
strcpy(ptr+k,ptr+6);
|
|
ptr+=k;
|
|
continue;
|
|
}
|
|
ptr++;
|
|
}
|
|
|
|
// if(pi->pass == PASS_2) // TODO : Test
|
|
// strcpy(pi->list_line, line);
|
|
|
|
strcpy(pi->fi->scratch,line);
|
|
|
|
for(i = 0; IS_LABEL(pi->fi->scratch[i]) || (pi->fi->scratch[i] == ':'); i++)
|
|
if(pi->fi->scratch[i] == ':') { /* it is a label */
|
|
pi->fi->scratch[i] = '\0';
|
|
if(pi->pass == PASS_1) {
|
|
for(macro_call = pi->macro_call; macro_call; macro_call = macro_call->prev_on_stack) {
|
|
for(label = pi->macro_call->first_label; label; label = label->next) {
|
|
if(!nocase_strcmp(label->name, &pi->fi->scratch[0])) {
|
|
print_msg(pi, MSGTYPE_ERROR, "Can't redefine local label %s", &pi->fi->scratch[0]);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if(test_label(pi,&pi->fi->scratch[0],"Can't redefine label %s")!=NULL)
|
|
break;
|
|
if(test_variable(pi,&pi->fi->scratch[0],"%s have already been defined as a .SET variable")!=NULL)
|
|
break;
|
|
if(test_constant(pi,&pi->fi->scratch[0],"%s has already been defined as a .EQU constant")!=NULL)
|
|
break;
|
|
label = malloc(sizeof(struct label));
|
|
if(!label) {
|
|
print_msg(pi, MSGTYPE_OUT_OF_MEM, NULL);
|
|
return(False);
|
|
}
|
|
label->next = NULL;
|
|
label->name = malloc(strlen(&pi->fi->scratch[0]) + 1);
|
|
if(!label->name) {
|
|
print_msg(pi, MSGTYPE_OUT_OF_MEM, NULL);
|
|
return(False);
|
|
}
|
|
strcpy(label->name, &pi->fi->scratch[0]);
|
|
switch(pi->segment) {
|
|
case SEGMENT_CODE:
|
|
label->value = pi->cseg_addr;
|
|
break;
|
|
case SEGMENT_DATA:
|
|
label->value = pi->dseg_addr;
|
|
break;
|
|
case SEGMENT_EEPROM:
|
|
label->value = pi->eseg_addr;
|
|
break;
|
|
}
|
|
if(pi->macro_call && !global_label) {
|
|
if(pi->macro_call->last_label)
|
|
pi->macro_call->last_label->next = label;
|
|
else
|
|
pi->macro_call->first_label = label;
|
|
pi->macro_call->last_label = label;
|
|
} else {
|
|
if(pi->last_label)
|
|
pi->last_label->next = label;
|
|
else
|
|
pi->first_label = label;
|
|
pi->last_label = label;
|
|
}
|
|
}
|
|
i++;
|
|
while(IS_HOR_SPACE(pi->fi->scratch[i]) && !IS_END_OR_COMMENT(pi->fi->scratch[i])) i++;
|
|
if(IS_END_OR_COMMENT(pi->fi->scratch[i])) {
|
|
if((pi->pass == PASS_2) && pi->list_on) { // Diff tilpassing
|
|
fprintf(pi->list_file, " %s\n", pi->list_line);
|
|
pi->list_line = NULL;
|
|
}
|
|
return(True);
|
|
}
|
|
strcpy(pi->fi->scratch, &pi->fi->scratch[i]);
|
|
break;
|
|
}
|
|
|
|
#if 0
|
|
if(pi->fi->scratch[0] == '.') {
|
|
#else
|
|
if((pi->fi->scratch[0] == '.') || (pi->fi->scratch[0] == '#')) {
|
|
#endif
|
|
pi->fi->label = label;
|
|
flag = parse_directive(pi);
|
|
if((pi->pass == PASS_2) && pi->list_on && pi->list_line) { // Diff tilpassing
|
|
fprintf(pi->list_file, " %s\n", pi->list_line);
|
|
pi->list_line = NULL;
|
|
}
|
|
return(flag);
|
|
} else {
|
|
return parse_mnemonic(pi);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Get the next token, and terminate the last one.
|
|
* Termination identifier is specified.
|
|
*/
|
|
|
|
char *get_next_token(char *data, int term)
|
|
{
|
|
int i = 0, j, anti_comma = False;
|
|
switch(term) {
|
|
case TERM_END:
|
|
// while(!IS_END_OR_COMMENT(data[i])) i++; Problems with 2. operand == ';'
|
|
while( ((data[i] != ',') || anti_comma) && !(((data[i] == ';') && !anti_comma) || IS_ENDLINE(data[i])) ) {
|
|
if((data[i] == '\'') || (data[i] == '"'))
|
|
anti_comma = anti_comma ? False : True;
|
|
i++;
|
|
}
|
|
break;
|
|
case TERM_SPACE:
|
|
while(!IS_HOR_SPACE(data[i]) && !IS_END_OR_COMMENT(data[i])) i++;
|
|
break;
|
|
case TERM_DASH:
|
|
while((data[i] != '-') && !IS_END_OR_COMMENT(data[i])) i++;
|
|
break;
|
|
case TERM_COLON:
|
|
while((data[i] != ':') && !IS_ENDLINE(data[i])) i++;
|
|
break;
|
|
case TERM_DOUBLEQUOTE:
|
|
while((data[i] != '"') && !IS_ENDLINE(data[i])) i++;
|
|
break;
|
|
case TERM_COMMA:
|
|
while(((data[i] != ',') || anti_comma) && !(((data[i] == ';') && !anti_comma) || IS_ENDLINE(data[i])) ) {
|
|
if((data[i] == '\'') || (data[i] == '"'))
|
|
anti_comma = anti_comma ? False : True;
|
|
i++;
|
|
}
|
|
break;
|
|
case TERM_EQUAL:
|
|
while((data[i] != '=') && !IS_END_OR_COMMENT(data[i])) i++;
|
|
break;
|
|
}
|
|
if(IS_END_OR_COMMENT(data[i])) {
|
|
data[i--] = '\0';
|
|
while(IS_HOR_SPACE(data[i])) data[i--] = '\0';
|
|
return(0);
|
|
}
|
|
j = i - 1;
|
|
while(IS_HOR_SPACE(data[j])) data[j--] = '\0';
|
|
data[i++] = '\0';
|
|
while(IS_HOR_SPACE(data[i]) && !IS_END_OR_COMMENT(data[i])) i++;
|
|
if(IS_END_OR_COMMENT(data[i]))
|
|
return(0);
|
|
return(&data[i]);
|
|
}
|
|
|
|
/* end of parser.c */
|
|
|