0,0 → 1,560 |
/*********************************************************************** |
* |
* 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 |
*/ |
|
/* |
* In append_type: added generic register names support |
* Alexey Pavluchenko, 16.Nov.2005 |
*/ |
|
#include <stdio.h> |
#include <stdlib.h> |
#include <string.h> |
#include <ctype.h> |
|
#include "misc.h" |
#include "args.h" |
#include "avra.h" |
#include "device.h" |
|
/* Only Windows LIBC does support itoa, so we add this |
function for other systems here manually. Thank you |
Peter Hettkamp for your work. */ |
|
#ifndef WIN32 |
char * itoa(int num, char *str, const int number_format) |
{ |
int num1 = num; |
int num_chars = 0; |
int pos; |
|
while (num1>0) |
{ |
num_chars++; |
num1 /= number_format; |
} |
|
if (num_chars == 0) num_chars = 1; |
|
str[num_chars] = 0; |
|
for (pos = num_chars-1; pos>=0; pos--) |
{ |
int cur_char = num % number_format; |
|
if (cur_char < 10) /* Insert number */ |
{ |
str[pos] = cur_char + '0'; |
} |
else |
{ |
str[pos] = cur_char-10 + 'A'; |
} |
|
num /= number_format; |
} |
return(str); |
} |
#endif |
|
|
int read_macro(struct prog_info *pi, char *name) |
{ |
int loopok; |
int i; |
int start; |
struct macro *macro; |
struct macro_line *macro_line; |
struct macro_line **last_macro_line = NULL; |
struct macro_label *macro_label; |
|
if(pi->pass == PASS_1) { |
if(!name) { |
print_msg(pi, MSGTYPE_ERROR, "missing macro name"); |
return(True); |
} |
get_next_token(name, TERM_END); |
|
for(i = 0; !IS_END_OR_COMMENT(name[i]); i++) { |
if(!IS_LABEL(name[i])) { |
print_msg(pi, MSGTYPE_ERROR, "illegal characters used in macro name '%s'",name); |
return(False); |
} |
} |
|
macro = calloc(1, sizeof(struct macro)); |
if(!macro) { |
print_msg(pi, MSGTYPE_OUT_OF_MEM, NULL); |
return(False); |
} |
|
if(pi->last_macro) |
pi->last_macro->next = macro; |
else |
pi->first_macro = macro; |
pi->last_macro = macro; |
macro->name = malloc(strlen(name) + 1); |
if(!macro->name) { |
print_msg(pi, MSGTYPE_OUT_OF_MEM, NULL); |
return(False); |
} |
strcpy(macro->name, name); |
macro->include_file = pi->fi->include_file; |
macro->first_line_number = pi->fi->line_number; |
last_macro_line = ¯o->first_macro_line; |
} |
else { /* pi->pass == PASS_2 */ |
if(pi->list_line && pi->list_on) { |
fprintf(pi->list_file, " %s\n", pi->list_line); |
pi->list_line = NULL; |
} |
// reset macro label running numbers |
get_next_token(name, TERM_END); |
macro = get_macro(pi, name); |
if (!macro) { |
print_msg(pi, MSGTYPE_ERROR, "macro inconsistency in '%s'", name); |
return(True); |
} |
for(macro_label = macro->first_label; macro_label; macro_label = macro_label->next) { |
macro_label->running_number = 0; |
} |
} |
|
loopok = True; |
while(loopok) { |
if(fgets_new(pi,pi->fi->buff, LINEBUFFER_LENGTH, pi->fi->fp)) { |
pi->fi->line_number++; |
i = 0; |
while(IS_HOR_SPACE(pi->fi->buff[i]) && !IS_END_OR_COMMENT(pi->fi->buff[i])) i++; |
if(pi->fi->buff[i] == '.') { |
i++; |
if(!nocase_strncmp(&pi->fi->buff[i], "endm", 4)) |
loopok = False; |
if(!nocase_strncmp(&pi->fi->buff[i], "endmacro", 8)) |
loopok = False; |
} |
if(pi->pass == PASS_1) { |
if(loopok) { |
i = 0; /* find start of line */ |
while(IS_HOR_SPACE(pi->fi->buff[i]) && !IS_END_OR_COMMENT(pi->fi->buff[i])) { |
i++; |
} |
start = i; |
/* find end of line */ |
while(!IS_END_OR_COMMENT(pi->fi->buff[i]) && (IS_LABEL(pi->fi->buff[i]) || pi->fi->buff[i] == ':')) { |
i++; |
} |
if(pi->fi->buff[i-1] == ':' && (pi->fi->buff[i-2] == '%' |
&& (IS_HOR_SPACE(pi->fi->buff[i]) || IS_END_OR_COMMENT(pi->fi->buff[i])))) { |
if(macro->first_label) { |
for(macro_label = macro->first_label; macro_label->next; macro_label=macro_label->next){} |
macro_label->next = calloc(1,sizeof(struct macro_label)); |
macro_label = macro_label->next; |
} |
else { |
macro_label = calloc(1,sizeof(struct macro_label)); |
macro->first_label = macro_label; |
} |
macro_label->label = malloc(strlen(&pi->fi->buff[start])+1); |
pi->fi->buff[i-1] = '\0'; |
strcpy(macro_label->label, &pi->fi->buff[start]); |
pi->fi->buff[i-1] = ':'; |
macro_label->running_number = 0; |
} |
|
macro_line = calloc(1, sizeof(struct macro_line)); |
if(!macro_line) { |
print_msg(pi, MSGTYPE_OUT_OF_MEM, NULL); |
return(False); |
} |
*last_macro_line = macro_line; |
last_macro_line = ¯o_line->next; |
macro_line->line = malloc(strlen(pi->fi->buff) + 1); |
if(!macro_line->line) { |
print_msg(pi, MSGTYPE_OUT_OF_MEM, NULL); |
return(False); |
} |
strcpy(macro_line->line, &pi->fi->buff[start]); |
} |
} |
else if(pi->fi->buff && pi->list_file && pi->list_on) { |
if(pi->fi->buff[i] == ';') |
fprintf(pi->list_file, " %s\n", pi->fi->buff); |
else |
fprintf(pi->list_file, " %s\n", pi->fi->buff); |
} |
} |
else { |
if(feof(pi->fi->fp)) { |
print_msg(pi, MSGTYPE_ERROR, "Found no closing .ENDMACRO"); |
return(True); |
} |
else { |
perror(pi->fi->include_file->name); |
return(False); |
} |
} |
} |
return(True); |
} |
|
|
struct macro *get_macro(struct prog_info *pi, char *name) |
{ |
struct macro *macro; |
|
for(macro = pi->first_macro; macro; macro = macro->next) |
if(!nocase_strcmp(macro->name, name)) |
return(macro); |
return(NULL); |
} |
|
void append_type(struct prog_info *pi, char *name, int c, char *value) |
{ |
int p, l; |
struct def *def; |
|
p = strlen(name); |
name[p++] = '_'; |
|
if(c == 0) |
{ |
name[p++] = 'v'; |
name[p] = '\0'; |
return; |
} |
|
l = strlen(value); |
if ((l==2 || l==3) && (tolower(value[0])=='r') && isdigit(value[1]) && (l==3?isdigit(value[2]):1) && (atoi(&value[1])<32)) |
{ |
itoa((c*8),&name[p],10); |
return; |
} |
|
|
for(def = pi->first_def; def; def = def->next) |
if(!nocase_strcmp(def->name, value)) |
{ |
itoa((c*8),&name[p],10); |
return; |
} |
|
name[p++] = 'i'; |
name[p] = '\0'; |
} |
|
|
/********************************************************* |
* This routine replaces the macro call with mnemonics. * |
*********************************************************/ |
|
int expand_macro(struct prog_info *pi, struct macro *macro, char *rest_line) |
{ |
int ok = True, macro_arg_count = 0, off, a, b = 0, c, i = 0, j = 0; |
char *line = NULL; |
char *temp; |
char *macro_args[MAX_MACRO_ARGS]; |
char tmp[7]; |
char buff[LINEBUFFER_LENGTH]; |
char arg = False; |
char *nmn; //string buffer for 'n'ew 'm'acro 'n'ame |
struct macro_line *old_macro_line; |
struct macro_call *macro_call; |
struct macro_label *macro_label; |
|
if(rest_line) { |
//we reserve some extra space for extended macro parameters |
line = malloc(strlen(rest_line) + 20); |
if(!line) { |
print_msg(pi, MSGTYPE_OUT_OF_MEM, NULL); |
return(False); |
} |
|
/* exchange amca word 'src' with YH:YL and 'dst' with ZH:ZL */ |
for(c = 0, a = strlen(rest_line); c < a; c++) { |
switch (tolower(rest_line[c])) { |
case 's': |
if(IS_SEPARATOR(rest_line[c-1]) && (rest_line[c+1] == 'r') && (rest_line[c+2] == 'c') && IS_SEPARATOR(rest_line[c+3])) { |
strcpy(&line[b],"YH:YL"); |
b += 5; |
c += 2; |
} |
else { |
line[b++] = rest_line[c]; |
} |
break; |
case 'd': |
if(IS_SEPARATOR(rest_line[c-1]) && (rest_line[c+1] == 's') && (rest_line[c+2] == 't') && IS_SEPARATOR(rest_line[c+3])) { |
strcpy(&line[b],"ZH:ZL"); |
b += 5; |
c += 2; |
} |
else { |
line[b++] = rest_line[c]; |
} |
break; |
// case ';': |
// break; |
default: |
line[b++] = rest_line[c]; |
} |
} |
strcpy(&line[b],"\n"); /* set CR/LF at the end of the line */ |
|
|
/* here we split up the macro arguments into "macro_args" |
* Extended macro code interpreter added by TW 2002 |
*/ |
|
temp = line; |
/* test for advanced parameters */ |
if( temp[0] == '[' ) { // there must be "[" " then "]", else it is garbage |
if(!strchr(temp, ']')) { |
print_msg(pi, MSGTYPE_ERROR, "found no closing ']'"); |
return(False); |
} |
|
// Okay now we are within the advanced code interpreter |
|
temp++; // = &temp[1]; // skip the first bracket |
nmn = malloc(LINEBUFFER_LENGTH); |
if(!nmn) { |
print_msg(pi, MSGTYPE_OUT_OF_MEM, NULL); |
return(False); |
} |
strcpy(nmn,macro->name); // create a new macro name buffer |
c = 1; // byte counter |
arg = True; // loop flag |
|
while(arg) { |
while(IS_HOR_SPACE(temp[0])) { //skip leading spaces |
temp++; // = &temp[1]; |
} |
off = 0; // pointer offset |
do { |
switch(temp[off]) { //test current character code |
case ':': |
temp[off] = '\0'; |
if(off > 0) { |
c++; |
macro_args[macro_arg_count++] = temp; |
} |
else { |
print_msg(pi, MSGTYPE_ERROR, "missing register before ':'",nmn); |
return(False); |
} |
break; |
case ']': |
arg = False; |
case ',': |
a = off; |
do temp[a--] = '\0'; while( IS_HOR_SPACE(temp[a]) ); |
if(off > 0) { |
macro_args[macro_arg_count++] = temp; |
append_type(pi, nmn, c, temp); |
c = 1; |
} |
else { |
append_type(pi, nmn, 0, temp); |
c = 1; |
} |
break; |
|
default: |
off++; |
} |
} |
while(temp[off] != '\0'); |
|
if(arg) temp = &temp[off+1]; |
else break; |
} |
|
macro = get_macro(pi,nmn); |
if(macro == NULL) { |
print_msg(pi, MSGTYPE_ERROR, "Macro %s is not defined !",nmn); |
return(False); |
} |
free(nmn); |
} |
/* or else, we handle the macro as normal macro */ |
else { |
line = malloc(strlen(rest_line) + 1); |
if(!line) { |
print_msg(pi, MSGTYPE_OUT_OF_MEM, NULL); |
return(False); |
} |
strcpy(line, rest_line); |
temp = line; |
while(temp) { |
macro_args[macro_arg_count++] = temp; |
temp = get_next_token(temp, TERM_COMMA); |
} |
} |
} |
|
if(pi->pass == PASS_1) { |
macro_call = calloc(1, sizeof(struct macro_call)); |
if(!macro_call) { |
print_msg(pi, MSGTYPE_OUT_OF_MEM, NULL); |
return(False); |
} |
if(pi->last_macro_call) |
pi->last_macro_call->next = macro_call; |
else |
pi->first_macro_call = macro_call; |
|
pi->last_macro_call = macro_call; |
macro_call->line_number = pi->fi->line_number; |
macro_call->include_file = pi->fi->include_file; |
macro_call->macro = macro; |
macro_call->prev_on_stack = pi->macro_call; |
|
if(macro_call->prev_on_stack) { |
macro_call->nest_level = macro_call->prev_on_stack->nest_level + 1; |
macro_call->prev_line_index = macro_call->prev_on_stack->line_index; |
} |
} |
else { |
for(macro_call = pi->first_macro_call; macro_call; macro_call = macro_call->next) { |
if((macro_call->include_file->num == pi->fi->include_file->num) && (macro_call->line_number == pi->fi->line_number)) { |
if(pi->macro_call) { |
/* Find correct macro_call when using recursion and nesting */ |
if(macro_call->prev_on_stack == pi->macro_call) |
if((macro_call->nest_level == (pi->macro_call->nest_level + 1)) && (macro_call->prev_line_index == pi->macro_call->line_index)) |
break; |
} |
else break; |
} |
} |
if(pi->list_line && pi->list_on) { |
fprintf(pi->list_file, "C:%06x + %s\n", pi->cseg_addr, pi->list_line); |
pi->list_line = NULL; |
} |
} |
|
macro_call->line_index = 0; |
pi->macro_call = macro_call; |
old_macro_line = pi->macro_line; |
|
//printf("\nconvert macro: '%s'\n",macro->name); |
|
for(pi->macro_line = macro->first_macro_line; pi->macro_line && ok; pi->macro_line = pi->macro_line->next) { |
macro_call->line_index++; |
if(GET_ARG(pi->args, ARG_LISTMAC)) |
pi->list_line = buff; |
else |
pi->list_line = NULL; |
|
/* here we change jumps/calls within macro that corresponds to macro labels. |
Only in case there is an entry in macro_label list */ |
|
strcpy(buff,"\0"); |
macro_label = get_macro_label(pi->macro_line->line,macro); |
if(macro_label) { |
/* test if the right macro label has been found */ |
temp = strstr(pi->macro_line->line,macro_label->label); |
c = strlen(macro_label->label); |
if(temp[c] == ':') { /* it is a label definition */ |
macro_label->running_number++; |
strncpy(buff, macro_label->label, c - 1); |
buff[c - 1] = 0; |
i = strlen(buff) + 2; /* we set the process indeafter label */ |
/* add running number to it */ |
strcpy(&buff[c-1],itoa(macro_label->running_number, tmp, 10)); |
strcat(buff, ":\0"); |
} |
else if(IS_HOR_SPACE(temp[c]) || IS_END_OR_COMMENT(temp[c])) { /* it is a jump to a macro defined label */ |
strcpy(buff,pi->macro_line->line); |
temp = strstr(buff, macro_label->label); |
i = temp - buff + strlen(macro_label->label); |
strncpy(temp, macro_label->label, c - 1); |
strcpy(&temp[c-1], itoa(macro_label->running_number, tmp, 10)); |
} |
} |
else { |
i = 0; |
} |
|
/* here we check every character of current line */ |
for(j = i; pi->macro_line->line[i] != '\0'; i++) { |
/* check for register place holders */ |
if(pi->macro_line->line[i] == '@') { |
i++; |
if(!isdigit(pi->macro_line->line[i])) |
print_msg(pi, MSGTYPE_ERROR, "@ must be followed by a number"); |
else if((pi->macro_line->line[i] - '0') >= macro_arg_count) |
print_msg(pi, MSGTYPE_ERROR, "Missing macro argument (for @%c)", pi->macro_line->line[i]); |
else { |
/* and replace them with given registers */ |
strcat(&buff[j], macro_args[pi->macro_line->line[i] - '0']); |
j += strlen(macro_args[pi->macro_line->line[i] - '0']); |
} |
} |
else if (pi->macro_line->line[i] == ';') { |
strncat(buff, "\n", 1); |
break; |
} |
else { |
strncat(buff, &pi->macro_line->line[i], 1); |
} |
} |
|
ok = parse_line(pi, buff); |
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..."); |
ok = False; |
break; |
} |
} |
} |
|
pi->macro_line = old_macro_line; |
pi->macro_call = macro_call->prev_on_stack; |
if(rest_line) |
free(line); |
return(ok); |
} |
|
struct macro_label *get_macro_label(char *line, struct macro *macro) |
{ |
char *temp ; |
struct macro_label *macro_label; |
|
for(macro_label = macro->first_label; macro_label; macro_label = macro_label->next) { |
temp = strstr(line,macro_label->label); |
if(temp) { |
return macro_label; |
} |
} |
return NULL; |
} |
|
/* end of macro.c */ |
|