/*********************************************************************** * * 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 */ #include #include #include #include #include "misc.h" #include "args.h" #include "avra.h" #include "device.h" enum { DIRECTIVE_BYTE = 0, DIRECTIVE_CSEG, DIRECTIVE_CSEGSIZE, DIRECTIVE_DB, DIRECTIVE_DEF, DIRECTIVE_DEVICE, DIRECTIVE_DSEG, DIRECTIVE_DW, DIRECTIVE_ENDM, DIRECTIVE_ENDMACRO, DIRECTIVE_EQU, DIRECTIVE_ESEG, DIRECTIVE_EXIT, DIRECTIVE_INCLUDE, DIRECTIVE_LIST, DIRECTIVE_LISTMAC, DIRECTIVE_MACRO, DIRECTIVE_NOLIST, DIRECTIVE_ORG, DIRECTIVE_SET, DIRECTIVE_DEFINE, DIRECTIVE_UNDEF, DIRECTIVE_IFDEF, DIRECTIVE_IFNDEF, DIRECTIVE_IF, DIRECTIVE_ELSE, DIRECTIVE_ELIF, DIRECTIVE_ENDIF, DIRECTIVE_MESSAGE, DIRECTIVE_WARNING, DIRECTIVE_ERROR, DIRECTIVE_COUNT }; char *directive_list[] = { "BYTE", "CSEG", "CSEGSIZE", "DB", "DEF", "DEVICE", "DSEG", "DW", "ENDM", "ENDMACRO", "EQU", "ESEG", "EXIT", "INCLUDE", "LIST", "LISTMAC", "MACRO", "NOLIST", "ORG", "SET", "DEFINE", "UNDEF", "IFDEF", "IFNDEF", "IF", "ELSE", "ELIF", "ENDIF", "MESSAGE", "WARNING", "ERROR" }; int parse_directive(struct prog_info *pi, int pass) { int directive; int ok = True; int i; char *next, *data; struct file_info *fi_bak; struct label *label; struct def *def; struct data_list *includedir; next = get_next_token(pi->fi->scratch, TERM_SPACE); for(i = 0; pi->fi->scratch[i] != '\0'; i++) { pi->fi->scratch[i] = toupper(pi->fi->scratch[i]); } directive = get_directive_type(pi->fi->scratch + 1); if(directive == -1) { print_msg(pi, MSGTYPE_ERROR, "Unknown directive: %s", pi->fi->scratch); return(True); } switch(directive) { case DIRECTIVE_BYTE: if(!next) { print_msg(pi, MSGTYPE_ERROR, ".BYTE needs a size operand"); return(True); } if(pi->segment != SEGMENT_DATA) print_msg(pi, MSGTYPE_ERROR, ".BYTE directive can only be used in data segment (.DSEG)"); get_next_token(next, TERM_END); if(!get_expr(pi, next, &i)) return(False); if((pass == PASS_2) && pi->list_line && pi->list_on) { fprintf(pi->list_file, "D:%06x %s", pi->dseg_addr, pi->list_line); pi->list_line = NULL; } pi->dseg_addr += i; if(pass == PASS_1) pi->dseg_count += i; break; case DIRECTIVE_CSEG: pi->segment = SEGMENT_CODE; break; case DIRECTIVE_CSEGSIZE: break; case DIRECTIVE_DB: if((pass == PASS_2) && pi->list_line && pi->list_on) { fprintf(pi->list_file, " %s", pi->list_line); pi->list_line = NULL; } return(parse_db(pi, next, pass)); break; case DIRECTIVE_DEF: if(!next) { print_msg(pi, MSGTYPE_ERROR, ".DEF needs an operand"); return(True); } data = get_next_token(next, TERM_EQUAL); if(!(data && (tolower(data[0]) == 'r') && isdigit(data[1]))) { print_msg(pi, MSGTYPE_ERROR, "%s needs a register (e.g. .def BZZZT = r16)", next); return(True); } i = atoi(&data[1]); if(i > 31) print_msg(pi, MSGTYPE_ERROR, "R%d is not a valid register", i); for(def = pi->first_def; def; def = def->next) if(!nocase_strcmp(def->name, next)) { def->reg = i; return(True); } def = malloc(sizeof(struct def)); if(!def) { print_msg(pi, MSGTYPE_OUT_OF_MEM, NULL); return(False); } def->next = NULL; if(pi->last_def) pi->last_def->next = def; else pi->first_def = def; pi->last_def = def; def->name = malloc(strlen(next) + 1); if(!def->name) { print_msg(pi, MSGTYPE_OUT_OF_MEM, NULL); return(False); } strcpy(def->name, next); def->reg = i; break; case DIRECTIVE_DEVICE: if(pass == PASS_2) return(True); if(!next) { print_msg(pi, MSGTYPE_ERROR, ".DEVICE needs an operand"); return(True); } get_next_token(next, TERM_END); pi->device = get_device(pi,next); if(!pi->device) print_msg(pi, MSGTYPE_ERROR, "Unknown device: %s", next); break; case DIRECTIVE_DSEG: pi->segment = SEGMENT_DATA; break; case DIRECTIVE_DW: if(pi->segment == SEGMENT_DATA) { print_msg(pi, MSGTYPE_ERROR, "Can't use .DW directive in data segment (.DSEG)"); return(True); } while(next) { data = get_next_token(next, TERM_COMMA); if(pass == PASS_2) { if(!get_expr(pi, next, &i)) return(False); if((i < -32768) || (i > 65535)) print_msg(pi, MSGTYPE_WARNING, "Value %d is out of range (-32768 <= k <= 65535). Will be masked", i); } if(pi->segment == SEGMENT_EEPROM) { if(pass == PASS_2) { write_ee_byte(pi, pi->eseg_addr, (unsigned char)i); write_ee_byte(pi, pi->eseg_addr + 1, (unsigned char)(i >> 8)); } pi->eseg_addr += 2; if(pass == PASS_1) pi->eseg_count += 2; } else { if((pass == PASS_2) && pi->hfi) write_prog_word(pi, pi->cseg_addr, i); pi->cseg_addr++; if(pass == PASS_1) pi->cseg_count++; } next = data; } break; case DIRECTIVE_ENDM: case DIRECTIVE_ENDMACRO: print_msg(pi, MSGTYPE_ERROR, "No .MACRO found before .ENDMACRO"); break; case DIRECTIVE_EQU: if(!next) { print_msg(pi, MSGTYPE_ERROR, ".EQU needs an operand"); return(True); } data = get_next_token(next, TERM_EQUAL); if(!data) { print_msg(pi, MSGTYPE_ERROR, "%s needs an expression (e.g. .EQU BZZZT = 0x2a)", next); return(True); } get_next_token(data, TERM_END); if(!get_expr(pi, data, &i)) return(False); for(label = pi->first_label; label; label = label->next) { if(!nocase_strcmp(label->name, next)) { print_msg(pi, MSGTYPE_ERROR, "%s have already been defined as a label", next); return(True); } } for(label = pi->first_variable; label; label = label->next) if(!nocase_strcmp(label->name, next)) { print_msg(pi, MSGTYPE_ERROR, "%s have already been defined as a .SET variable", next); return(True); } for(label = pi->first_constant; label; label = label->next) if(!nocase_strcmp(label->name, next)) { print_msg(pi, MSGTYPE_ERROR, "Can't redefine constant %s, use .SET instead", next); return(True); } label = malloc(sizeof(struct label)); if(!label) { print_msg(pi, MSGTYPE_OUT_OF_MEM, NULL); return(False); } label->next = NULL; if(pi->last_constant) pi->last_constant->next = label; else pi->first_constant = label; pi->last_constant = label; label->name = malloc(strlen(next) + 1); if(!label->name) { print_msg(pi, MSGTYPE_OUT_OF_MEM, NULL); return(False); } strcpy(label->name, next); label->value = i; if((pass == PASS_2) && pi->list_line && pi->list_on) { fprintf(pi->list_file, " %s", pi->list_line); pi->list_line = NULL; } break; case DIRECTIVE_ESEG: pi->segment = SEGMENT_EEPROM; break; case DIRECTIVE_EXIT: pi->fi->exit_file = True; break; case DIRECTIVE_INCLUDE: if(!next) { print_msg(pi, MSGTYPE_ERROR, "Nothing to include"); return(True); } next = term_string(pi, next); if((pass == PASS_2) && pi->list_line && pi->list_on) { fprintf(pi->list_file, " %s", pi->list_line); pi->list_line = NULL; } // Test if include is in local directory ok = test_include(next); data = NULL; if(!ok) for(includedir = GET_ARG(pi->args, ARG_INCLUDEDIR); includedir && !ok; includedir = includedir->next) { i = strlen(includedir->data); if(data) free(data); data = malloc(i + strlen(next) + 2); if(!data) { print_msg(pi, MSGTYPE_OUT_OF_MEM, NULL); return(False); } strcpy(data, includedir->data); if((data[i - 1] != '\\') && (data[i - 1] != '/')) data[i++] = '/'; strcpy(&data[i], next); ok = test_include(data); } if(ok) { fi_bak = pi->fi; ok = parse_file(pi, data ? data : next, pass); pi->fi = fi_bak; } else print_msg(pi, MSGTYPE_ERROR, "cannot find include file: %s", next); if(data) free(data); break; case DIRECTIVE_LIST: if(pass == PASS_2) if(pi->list_file) pi->list_on = True; break; case DIRECTIVE_LISTMAC: if(pass == PASS_2) SET_ARG(pi->args, ARG_LISTMAC, True); break; case DIRECTIVE_MACRO: return(read_macro(pi, next, pass)); break; case DIRECTIVE_NOLIST: if(pass == PASS_2) pi->list_on = False; break; case DIRECTIVE_ORG: if(!next) { print_msg(pi, MSGTYPE_ERROR, ".ORG needs an operand"); return(True); } get_next_token(next, TERM_END); if(!get_expr(pi, next, &i)) return(False); switch(pi->segment) { case SEGMENT_CODE: pi->cseg_addr = i; break; case SEGMENT_DATA: pi->dseg_addr = i; break; case SEGMENT_EEPROM: pi->eseg_addr = i; } if(pi->fi->label) pi->fi->label->value = i; if((pass == PASS_2) && pi->list_line && pi->list_on) { fprintf(pi->list_file, " %s", pi->list_line); pi->list_line = NULL; } break; case DIRECTIVE_SET: if(!next) { print_msg(pi, MSGTYPE_ERROR, ".SET needs an operand"); return(True); } data = get_next_token(next, TERM_EQUAL); if(!data) { print_msg(pi, MSGTYPE_ERROR, "%s needs an expression (e.g. .SET BZZZT = 0x2a)", next); return(True); } get_next_token(data, TERM_END); if(!get_expr(pi, data, &i)) return(False); for(label = pi->first_label; label; label = label->next) if(!nocase_strcmp(label->name, next)) { print_msg(pi, MSGTYPE_ERROR, "%s have already been defined as a label", next); return(True); } for(label = pi->first_constant; label; label = label->next) if(!nocase_strcmp(label->name, next)) { print_msg(pi, MSGTYPE_ERROR, "%s have already been defined as a .EQU constant", next); return(True); } for(label = pi->first_variable; label; label = label->next) if(!nocase_strcmp(label->name, next)) { label->value = i; return(True); } label = malloc(sizeof(struct label)); if(!label) { print_msg(pi, MSGTYPE_OUT_OF_MEM, NULL); return(False); } label->next = NULL; if(pi->last_variable) pi->last_variable->next = label; else pi->first_variable = label; pi->last_variable = label; label->name = malloc(strlen(next) + 1); if(!label->name) { print_msg(pi, MSGTYPE_OUT_OF_MEM, NULL); return(False); } strcpy(label->name, next); label->value = i; break; case DIRECTIVE_DEFINE: if(!next) { print_msg(pi, MSGTYPE_ERROR, ".DEFINE needs an operand"); return(True); } data = get_next_token(next, TERM_SPACE); if(data) { get_next_token(data, TERM_END); if(!get_expr(pi, data, &i)) return(False); } else i = 1; for(label = pi->first_label; label; label = label->next) if(!nocase_strcmp(label->name, next)) { print_msg(pi, MSGTYPE_ERROR, "%s have already been defined as a label", next); return(True); } for(label = pi->first_variable; label; label = label->next) if(!nocase_strcmp(label->name, next)) { print_msg(pi, MSGTYPE_ERROR, "%s have already been defined as a .SET variable", next); return(True); } for(label = pi->first_constant; label; label = label->next) if(!nocase_strcmp(label->name, next)) { print_msg(pi, MSGTYPE_ERROR, "Can't redefine constant %s, use .SET instead", next); return(True); } label = malloc(sizeof(struct label)); if(!label) { print_msg(pi, MSGTYPE_OUT_OF_MEM, NULL); return(False); } label->next = NULL; if(pi->last_constant) pi->last_constant->next = label; else pi->first_constant = label; pi->last_constant = label; label->name = malloc(strlen(next) + 1); if(!label->name) { print_msg(pi, MSGTYPE_OUT_OF_MEM, NULL); return(False); } strcpy(label->name, next); label->value = i; if((pass == PASS_2) && pi->list_line && pi->list_on) { fprintf(pi->list_file, " %s", pi->list_line); pi->list_line = NULL; } break; case DIRECTIVE_UNDEF: // TODO break; case DIRECTIVE_IFDEF: if(!next) { print_msg(pi, MSGTYPE_ERROR, ".IFDEF needs an operand"); return(True); } get_next_token(next, TERM_END); if(get_symbol(pi, next, NULL)) pi->conditional_depth++; else { if(!spool_conditional(pi, False)) return(False); } break; case DIRECTIVE_IFNDEF: if(!next) { print_msg(pi, MSGTYPE_ERROR, ".IFNDEF needs an operand"); return(True); } get_next_token(next, TERM_END); if(get_symbol(pi, next, NULL)) { if(!spool_conditional(pi, False)) return(False); } else pi->conditional_depth++; break; case DIRECTIVE_IF: if(!next) { print_msg(pi, MSGTYPE_ERROR, ".IF needs an expression"); return(True); } get_next_token(next, TERM_END); if(!get_expr(pi, next, &i)) return(False); if(i) pi->conditional_depth++; else { if(!spool_conditional(pi, False)) return(False); } break; case DIRECTIVE_ELSE: case DIRECTIVE_ELIF: if(!spool_conditional(pi, True)) return(False); break; case DIRECTIVE_ENDIF: if(pi->conditional_depth == 0) print_msg(pi, MSGTYPE_ERROR, "Too many .ENDIF"); else pi->conditional_depth--; break; case DIRECTIVE_MESSAGE: if(pass == PASS_1) return(True); if(!next) { print_msg(pi, MSGTYPE_ERROR, "No message string supplied"); return(True); } next = term_string(pi, next); print_msg(pi, MSGTYPE_MESSAGE, next); break; case DIRECTIVE_WARNING: if(pass == PASS_1) return(True); if(!next) { print_msg(pi, MSGTYPE_ERROR, "No warning string supplied"); return(True); } next = term_string(pi, next); print_msg(pi, MSGTYPE_WARNING, next); break; case DIRECTIVE_ERROR: next = term_string(pi, next); print_msg(pi, MSGTYPE_ERROR, next); pi->error_count = pi->max_errors; if(pass == PASS_1) return(True); if(!next) { print_msg(pi, MSGTYPE_ERROR, "No error string supplied"); return(True); } break; } return(ok); } int get_directive_type(char *directive) { int i; for(i = 0; i < DIRECTIVE_COUNT; i++) { if(!strcmp(directive, directive_list[i])) return(i); } return(-1); } char *term_string(struct prog_info *pi, char *string) { int i; if(string[0] != '\"') { print_msg(pi, MSGTYPE_ERROR, "String must be enclosed in \"-signs"); } else { string++; } for(i = 0; (string[i] != '\"') && !((string[i] == 10) || (string[i] == 13) || (string[i] == '\0')); i++); if((string[i] == 10) || (string[i] == 13) || (string[i] == '\0')) { print_msg(pi, MSGTYPE_ERROR, "String is missing a closing \"-sign"); } string[i] = '\0'; return(string); } int parse_db(struct prog_info *pi, char *next, int pass) { int i; int count; char *data; char prev; if(pi->segment == SEGMENT_DATA) { print_msg(pi, MSGTYPE_ERROR, "Can't use .DB directive in data segment (.DSEG)."); return(True); } count = 0; if(pass == PASS_2 && pi->list_on) { if(pi->segment == SEGMENT_EEPROM) fprintf(pi->list_file, "E:%06X ", pi->eseg_addr); if(pi->segment == SEGMENT_CODE) fprintf(pi->list_file, "C:%06X ", pi->cseg_addr); } while(next) { data = get_next_token(next, TERM_COMMA); if(next[0] == '\"') { next = term_string(pi, next); while(*next != '\0') { count++; write_db(pi, *next, &prev, count, pass); if(pass == PASS_2 && pi->list_on) fprintf(pi->list_file, "%02X", *next); next++; } } else { if(pass == PASS_2) { if(!get_expr(pi, next, &i)) return(False); if((i < -128) || (i > 255)) print_msg(pi, MSGTYPE_WARNING, "Value %d is out of range (-128 <= k <= 255). Will be masked", i); if(pi->list_on) fprintf(pi->list_file, "%02X", (char)i); } count++; write_db(pi, (char)i, &prev, count, pass); } next = data; } if(pi->segment == SEGMENT_CODE) { if((count % 2) == 1) { if(pass == PASS_2) { if(pi->list_on) fprintf(pi->list_file, "00 ; zero byte added"); write_prog_word(pi, pi->cseg_addr, prev & 0xFF); print_msg(pi, MSGTYPE_WARNING, "A .DB segment with an odd number of bytes is detected. A zero byte is added."); } pi->cseg_addr++; if(pass == PASS_1) { pi->cseg_count++; } } } if(pass == PASS_2 && pi->list_on) { fprintf(pi->list_file, "\n"); pi->list_line = NULL; } return(True); } void write_db(struct prog_info *pi, char byte, char *prev, int count, int pass) { if(pi->segment == SEGMENT_EEPROM) { if(pass == PASS_2) { write_ee_byte(pi, pi->eseg_addr, byte); } pi->eseg_addr++; if(pass == PASS_1) { pi->eseg_count++; } } else { /* pi->segment == SEGMENT_CODE */ if((count % 2) == 0) { if(pass == PASS_2) { write_prog_word(pi, pi->cseg_addr, (byte << 8) | (*prev & 0xff)); } pi->cseg_addr++; if(pass == PASS_1) { pi->cseg_count++; } } else { *prev = byte; } } } int spool_conditional(struct prog_info *pi, int only_endif) { int current_depth = 0, do_next; if(pi->macro_line) { while((pi->macro_line = pi->macro_line->next)) { pi->macro_call->line_index++; if(check_conditional(pi, pi->macro_line->line, ¤t_depth, &do_next, only_endif)) { if(!do_next) return(True); } else return(False); } print_msg(pi, MSGTYPE_ERROR, "Found no closing .ENDIF in macro"); } else { while(fgets(pi->fi->buff, LINEBUFFER_LENGTH, pi->fi->fp)) { pi->fi->line_number++; if(check_conditional(pi, pi->fi->buff, ¤t_depth, &do_next, only_endif)) { if(!do_next) return(True); } else return(False); } if(feof(pi->fi->fp)) { print_msg(pi, MSGTYPE_ERROR, "Found no closing .ENDIF"); return(True); } else { perror(pi->fi->include_file->name); return(False); } } return(True); } int check_conditional(struct prog_info *pi, char *pbuff, int *current_depth, int *do_next, int only_endif) { int i = 0; char *next; char linebuff[LINEBUFFER_LENGTH]; strcpy(linebuff, pbuff); /* avoid cutting of the end of .elif line */ *do_next = False; while(IS_HOR_SPACE(linebuff[i]) && !IS_END(linebuff[i])) i++; if(linebuff[i] == '.') { i++; if(!nocase_strncmp(&linebuff[i], "if", 2)) (*current_depth)++; else if(!nocase_strncmp(&linebuff[i], "endif", 5)) { if(*current_depth == 0) return(True); (*current_depth)--; } else if(!only_endif && (*current_depth == 0)) { if(!nocase_strncmp(&linebuff[i], "else", 4)) { pi->conditional_depth++; return(True); } else if(!nocase_strncmp(&linebuff[i], "elif", 4)) { next = get_next_token(&linebuff[i], TERM_SPACE); if(!next) { print_msg(pi, MSGTYPE_ERROR, ".ELIF needs an operand"); return(True); } get_next_token(next, TERM_END); if(!get_expr(pi, next, &i)) return(False); if(i) pi->conditional_depth++; else { if(!spool_conditional(pi, False)) return(False); } return(True); } } } *do_next = True; return(True); } int test_include(const char *filename) { FILE *fp; fp = fopen(filename, "r"); if(fp) { fclose(fp); return(True); } else return(False); } /* end of directiv.c */