/* * picasm.c * * Copyright 1995-2005 Timo Rossi, * See the file LICENSE for license terms. * * http://www.iki.fi/trossi/pic/ * */ #include #include #include #include #include #include #include #include "picasm.h" #include "util.h" #include "token.h" #include "symtab.h" int warnlevel; static int total_line_count; static int errors, warnings; /* error & warning counts */ unsigned short list_flags; static FILE *list_fp; int listing_on; static int list_loc; static pic_instr_t *list_ptr; long list_val; static long list_len; int default_radix; int cond_nest_count; int unique_id_count; /* the current source file/macro */ struct inc_file *current_file; /* Line buffer & pointer to it */ char *line_buf_ptr; char line_buffer[256]; static struct patch *global_patch_list; struct patch **local_patch_list_ptr; char pic_name[256]; instrset_t pic_instr_set; /* set the PIC_NONE before PIC type is defined */ int prog_mem_size = PROGMEM_MAX; static int data_eeprom_size = EEPROM_MAX; static int reg_file_limit = 0x100; short code_generated; static pic_instr_t prog_mem[PROGMEM_MAX]; static pic_instr_t data_eeprom[EEPROM_MAX]; static pic_instr_t pic_id[4]; pic_instr_t config_fuses; org_mode_t O_Mode; int prog_location; /* current address for program code */ int reg_location; /* current address for register file */ int edata_location; /* current address for data EEPROM */ int org_val; int local_level; static void err_out(char *str) { fputs(str, stderr); fputc('\n', stderr); } /* Error handling */ /* * Show line number/line with error message */ static void err_line_ref(void) { struct inc_file *inc; char outbuf[128]; int len; if(current_file != NULL) { inc = current_file; if(inc->type != INC_FILE) { p_snprintf(outbuf, sizeof outbuf, "(Macro %-.99s line %d)", inc->v.m.sym->name, inc->linenum); err_out(outbuf); while(inc != NULL && inc->type != INC_FILE) inc = inc->next; } p_snprintf(outbuf, sizeof outbuf, "%-.99s:%d:", inc->v.f.fname, inc->linenum); err_out(outbuf); len = strlen(line_buffer); if(len > 100) len = 100; strncpy(outbuf, line_buffer, len); outbuf[len] = '\0'; err_out(outbuf); } } /* * Warning message */ void warning(char *fmt, ...) { char outbuf[128]; va_list args; static const char warning_msg[] = "Warning: "; err_line_ref(); strcpy(outbuf, warning_msg); va_start(args, fmt); p_vsnprintf(outbuf + (sizeof warning_msg - 1), sizeof outbuf - (sizeof warning_msg - 1), fmt, args); va_end(args); err_out(outbuf); if(list_fp != NULL) fputs(outbuf, list_fp); warnings++; } /* * skip the end of line, in case of an error */ static void error_lineskip(void) { write_listing_line(0); skip_eol(); get_token(); } /* * Error message * call error_lineskip() if lskip is non-zero * */ void error(int lskip, char *fmt, ...) { va_list args; char outbuf[128]; static const char error_msg[] = "Error: "; err_line_ref(); strcpy(outbuf, error_msg); va_start(args, fmt); p_vsnprintf(outbuf + (sizeof error_msg - 1), sizeof outbuf - (sizeof error_msg - 1), fmt, args); va_end(args); err_out(outbuf); if(list_fp != NULL) { fputs(outbuf, list_fp); fputc('\n', list_fp); } if(++errors >= MAX_ERRORS) fatal_error("too many errors, aborting"); if(lskip) error_lineskip(); } /* * Fatal error message */ void fatal_error(char *fmt, ...) { va_list args; char outbuf[128]; static char const fatal_err_msg[] = "Fatal error: "; err_line_ref(); strcpy(outbuf, fatal_err_msg); va_start(args, fmt); p_vsnprintf(outbuf + (sizeof fatal_err_msg - 1), sizeof outbuf - (sizeof fatal_err_msg - 1), fmt, args); va_end(args); err_out(outbuf); exit(EXIT_FAILURE); } /* memory allocation */ void * mem_alloc(int size) { void *p; if((p = malloc(size)) == NULL) fatal_error("Out of memory"); return p; } /* * initialize the assembler */ static void init_assembler(void) { pic_instr_t *cp; int n; init_symtab(); /* initialize symbol table */ init_includes(); /* initialize include paths */ /* initialize program memory to invalid values */ for(n = PROGMEM_MAX, cp = prog_mem; n-- > 0; *cp++ = INVALID_INSTR) ; /* initialize data memory to invalid values */ for(n = EEPROM_MAX, cp = data_eeprom; n-- > 0; *cp++ = INVALID_DATA) ; /* initialize jump patch list */ global_patch_list = NULL; prog_location = -1; reg_location = -1; edata_location = -1; org_val = -1; current_file = NULL; config_fuses = INVALID_CONFIG; pic_id[0] = INVALID_ID; errors = warnings = 0; list_flags = 0; list_len = 0; listing_on = 1; cond_nest_count = 0; unique_id_count = 1; total_line_count = 0; code_generated = 0; local_level = 0; ifskip_mode = 0; default_radix = 10; } /* * generate program code */ void gen_code(int val) { if(pic_instr_set == PIC_NONE) fatal_error("PIC device type not set"); if(O_Mode == O_NONE) { O_Mode = O_PROGRAM; if(org_val < 0) { error(0, "ORG value not set"); prog_location = 0; return; } prog_location = org_val; } else if(O_Mode != O_PROGRAM) { error(0, "ORG mode conflict"); O_Mode = O_PROGRAM; return; } if(prog_location >= prog_mem_size) fatal_error("Code address out of range"); if(prog_mem[prog_location] != INVALID_INSTR) warning("Overlapping code at 0x%x", prog_location); if((list_flags & LIST_PROG) == 0) { list_loc = prog_location; list_flags = LIST_LOC | LIST_PROG; } list_len++; prog_mem[prog_location++] = val; code_generated = 1; } /* * Generate data for data EEPROM */ void gen_edata(int val) { if(O_Mode == O_NONE) { O_Mode = O_EDATA; if(org_val < 0) { error(0, "ORG value not set"); edata_location = 0; return; } edata_location = org_val; } else if(O_Mode != O_EDATA) { error(0, "ORG mode conflict"); O_Mode = O_EDATA; return; } if(edata_location >= data_eeprom_size) fatal_error("Data EEPROM address out of range"); if(data_eeprom[edata_location] < 0x100) warning("Overlapping EEPROM data at 0x%x", edata_location); if((list_flags & LIST_LOC) == 0) { list_loc = edata_location; list_flags = LIST_LOC | LIST_EDATA; } list_len++; data_eeprom[edata_location++] = val; code_generated = 1; } /* * Write one line of Intel-hex file */ static void write_hex_record(FILE *fp, int reclen, /* length (in words) */ int loc, /* address */ pic_instr_t *data, /* pointer to word data */ int format) /* IHX8M or IHX16 */ { unsigned int check = 0; switch(format) { case IHX8M: fprintf(fp, ":%02X%04X00", 2*reclen, 2*loc); check += ((2*loc) & 0xff) + (((2*loc) >> 8) & 0xff) + 2*reclen; break; case IHX16: fprintf(fp, ":%02X%04X00", reclen, loc); check += (loc & 0xff) + ((loc >> 8) & 0xff) + reclen; break; } while(reclen--) { switch(format) { case IHX8M: fprintf(fp, "%02X%02X", (int)(*data & 0xff), (int)((*data >> 8) & 0xff)); break; case IHX16: fprintf(fp, "%02X%02X", (int)((*data >> 8) & 0xff), (int)(*data & 0xff)); break; } check += (*data & 0xff) + ((*data >> 8) & 0xff); data++; } /* write checksum */ fprintf(fp, "%02X" HEX_EOL, (0x100 - (check & 0xff)) & 0xff); } /* * Write output file in ihx8m or ihx16-format * */ static void write_output(char *fname, int format) { int loc, reclen; FILE *fp; if((fp = fopen(fname, "w")) == NULL) fatal_error("Can't create file '%s': %s", fname, strerror(errno)); /* program */ for(loc = 0;;) { while(loc < prog_mem_size && prog_mem[loc] == INVALID_INSTR) loc++; if(loc >= prog_mem_size) break; reclen = 0; while(reclen < 8 && loc < prog_mem_size && prog_mem[loc] != INVALID_INSTR) { loc++; reclen++; } write_hex_record(fp, reclen, loc-reclen, &prog_mem[loc-reclen], format); } /* PIC ID */ if(pic_id[0] != INVALID_ID) { switch(pic_instr_set) { case PIC12BIT: write_hex_record(fp, 4, prog_mem_size, pic_id, format); break; case PIC14BIT: write_hex_record(fp, 4, 0x2000, pic_id, format); break; default: break; } } /* config fuses */ if(config_fuses != INVALID_CONFIG) { switch(pic_instr_set) { case PIC12BIT: write_hex_record(fp, 1, 0xfff, &config_fuses, format); break; case PIC14BIT: write_hex_record(fp, 1, 0x2007, &config_fuses, format); break; case PIC16BIT_NOMUL: case PIC16BIT: /* XXX Is this correct? */ write_hex_record(fp, 1, 0xfe00, &config_fuses, format); break; case PIC_NONE: fatal_error("internal error"); break; } } if(data_eeprom_size > 0) { /* data EEPROM */ for(loc = 0;;) { while(loc < data_eeprom_size && data_eeprom[loc] >= 0x100) loc++; if(loc >= data_eeprom_size) break; reclen = 0; while(reclen < 8 && loc < data_eeprom_size && data_eeprom[loc] < 0x100) { loc++; reclen++; } write_hex_record(fp, reclen, 0x2100+loc-reclen, &data_eeprom[loc-reclen], format); } } fputs(":00000001FF" HEX_EOL , fp); /* end record */ fclose(fp); } /* * Write one line to listing file (if listing is enabled) */ void write_listing_line(int cond_flag) { int i; if(list_fp != NULL && listing_on) { fprintf(list_fp, "%04d%c%c", ++total_line_count, (current_file != NULL && current_file->type == INC_MACRO ? '+' : ' '), (cond_flag ? '!' : ' ')); if(line_buffer[0] != '\0') { if(list_flags & LIST_VAL) { fprintf(list_fp, "%08lX ", list_val); } else { if(list_flags & LIST_LOC) fprintf(list_fp, "%04X", list_loc); else fputs(" ", list_fp); if((list_flags & (LIST_PROG|LIST_EDATA|LIST_PTR)) != 0) { fputc(((list_flags & LIST_FORWARD) ? '?' : ' '), list_fp); if(list_flags & LIST_PROG) { fprintf(list_fp, "%04X ", (unsigned short)prog_mem[list_loc]); } else if(list_flags & LIST_EDATA) { fprintf(list_fp, "%04X ", (unsigned short)data_eeprom[list_loc]); } else if(list_flags & LIST_PTR) { fprintf(list_fp, "%04X ", (unsigned short)(*list_ptr++)); } } else { fputs(" ", list_fp); } } fputs(line_buffer, list_fp); fputc('\n', list_fp); list_len--; for(i = 0; i < list_len; i++) { list_loc++; fprintf(list_fp, "%04d%c ", total_line_count, (current_file != NULL && current_file->type == INC_MACRO ? '+' : ' ')); if(list_flags & LIST_LOC) fprintf(list_fp, "%04X", list_loc); else fputs(" ", list_fp); if(list_flags & LIST_PROG) { fprintf(list_fp, " %04X\n", (unsigned short)prog_mem[list_loc]); } else if(list_flags & LIST_EDATA) { fprintf(list_fp, " %04X\n", (unsigned short) data_eeprom[list_loc]); } else if(list_flags & LIST_PTR) { fprintf(list_fp, " %04X\n", (unsigned short)(*list_ptr++)); } } } else { /* if(line_buffer[0] != '\0' ... */ fputc('\n', list_fp); } if(listing_on < 0) listing_on = 0; } list_flags = 0; list_len = 0; } /* * parse and handle OPT-directive * (this is special as it must be done at macro definition time * if inside a macro) */ static int handle_opt(void) { if(token_type != TOK_IDENTIFIER) { error(1, "OPT syntax error"); return FAIL; } /* * Note: when listing is turned off, 'listing_on' is set to -1 * here and the listing routine sets it to zero after listing * the line containing the 'opt nol'. */ if(strcasecmp(token_string, "nol") == 0 || strcasecmp(token_string, "nolist") == 0) { listing_on = -1; } else if(strcasecmp(token_string, "l") == 0 || strcasecmp(token_string, "list") == 0) { listing_on = 1; } else { error(1, "OPT syntax error"); return FAIL; } get_token(); return OK; } /* * Define a macro * * the check for multiple definitions of the macro name * has been done before calling this function. */ static void define_macro(char *name) { struct symbol *sym; struct macro_line *ml; int t; if(token_type != TOK_NEWLINE && token_type != TOK_EOF) error(0, "Extraneous characters after a valid source line"); skip_eol(); write_listing_line(0); sym = add_symbol(name, SYMTAB_GLOBAL); sym->type = SYM_MACRO; sym->v.text = NULL; ml = NULL; for(;;) { get_token(); /* read first token on next line */ t = 0; if(token_type == TOK_IDENTIFIER) { t = 1; get_token(); if(token_type == TOK_COLON) get_token(); } if(token_type == TOK_EOF || token_type == KW_END) fatal_error("Macro definition not terminated"); if(token_type == KW_MACRO) fatal_error("Nested macro definitions not allowed"); if(token_type == KW_ENDM) /* end macro definition */ break; /* OPT must be handled inside macros at definition time */ if(token_type == KW_OPT) { get_token(); handle_opt(); } else { if(ml == NULL) { ml = mem_alloc(sizeof(struct macro_line) +strlen(line_buffer)); sym->v.text = ml; } else { ml->next = mem_alloc(sizeof(struct macro_line) +strlen(line_buffer)); ml = ml->next; } strcpy(ml->text, line_buffer); ml->next = NULL; } write_listing_line(0); line_buf_ptr = NULL; tok_char = ' '; } if(t) error(0, "Label not allowed with ENDM"); get_token(); } /* * Skip subroutine used by the conditional assembly directives * return: -1=premature EOF, 0=ok, 1=label (not allowed) */ static int if_else_skip(void) { int t, ccount; ccount = 0; ifskip_mode++; do { skip_eol(); get_token(); write_listing_line(1); t = 0; if(token_type == TOK_IDENTIFIER) { t = 1; get_token(); if(token_type == TOK_COLON) get_token(); } if(token_type == KW_IF) { ccount++; } else if(token_type == KW_ENDIF) { if(ccount <= 0) break; ccount--; } else if(token_type == KW_ELSE && ccount <= 0) { break; } } while(token_type != TOK_EOF && token_type != KW_END); ifskip_mode--; return (token_type == TOK_EOF || token_type == KW_END) ? -1 : t; } /* * Add a patch pointing to the current location */ void add_patch(int tab, struct symbol *sym, patchtype_t type) { struct patch **patch_list_ptr, *ptch; patch_list_ptr = (tab == SYMTAB_GLOBAL ? &global_patch_list : local_patch_list_ptr); if(O_Mode == O_NONE) { O_Mode = O_PROGRAM; if(org_val < 0) { error(0, "ORG value not set"); prog_location = 0; return; } prog_location = org_val; } ptch = mem_alloc(sizeof(struct patch)); ptch->label = sym; ptch->type = type; ptch->location = prog_location; /* add a new patch to patch_list */ ptch->next = *patch_list_ptr; *patch_list_ptr = ptch; } /* * Apply the store patches and also free the patch list */ static void apply_patches(struct patch *patch_list) { struct patch *ptch, *p2; /* * fix forward jumps/calls */ for(ptch = patch_list; ptch != NULL; ptch = p2) { p2 = ptch->next; if(ptch->label->type == SYM_FORWARD) { error(0, "Undefined label '%s%s'", (patch_list == global_patch_list ? "" : "="), ptch->label->name); } else { switch(ptch->type) { case PATCH8: if(pic_instr_set == PIC12BIT && (ptch->label->v.value & 0x100) != 0 && (prog_mem[ptch->location] & 0xff00) == 0x900) error(0, "CALL address in upper half of a page (label '%s%s')", (patch_list == global_patch_list ? "" : "="), ptch->label->name); prog_mem[ptch->location] = (prog_mem[ptch->location] & 0xff00) | (ptch->label->v.value & 0xff); break; case PATCH9: prog_mem[ptch->location] = (prog_mem[ptch->location] & 0xfe00) | (ptch->label->v.value & 0x1ff); break; case PATCH11: prog_mem[ptch->location] = (prog_mem[ptch->location] & 0xf800) | (ptch->label->v.value & 0x7ff); break; case PATCH_UPPER_BYTE: prog_mem[ptch->location] = (prog_mem[ptch->location] & 0xf800) | ((ptch->label->v.value >> 8) & 0xff); break; } } mem_free(ptch); } } /* * Generate code for an instruction with 8-bit literal data * allows forward references */ int gen_byte_c(int instr_code) { int symtype; long val; struct symbol *sym; if(token_type == KW_HIBYTE) { get_token(); if(token_type != TOK_LEFTPAR) { error(1, "'(' expected"); return FAIL; } get_token(); if(token_type != TOK_IDENTIFIER && token_type != TOK_LOCAL_ID) { error(1, "Identifier expected"); return FAIL; } symtype = (token_type == TOK_IDENTIFIER ? SYMTAB_GLOBAL : SYMTAB_LOCAL); if(symtype == SYMTAB_LOCAL && local_level == 0) { error(1, "Local symbol outside a LOCAL block"); return FAIL; } sym = lookup_symbol(token_string, symtype); if(sym != NULL && sym->type != SYM_FORWARD) { if(sym->type != SYM_SET || sym->type != SYM_DEFINED) { error(1, "Invalid symbol type"); return FAIL; } gen_code(instr_code | ((sym->v.value >> 8) & 0xff)); } else { if(sym == NULL) { sym = add_symbol(token_string, symtype); sym->type = SYM_FORWARD; } add_patch(symtype, sym, PATCH_UPPER_BYTE); gen_code(instr_code); list_flags |= LIST_FORWARD; } get_token(); if(token_type != TOK_RIGHTPAR) { error(1, "')' expected"); return FAIL; } get_token(); return OK; } if(token_type == TOK_IDENTIFIER || token_type == TOK_LOCAL_ID) { symtype = (token_type == TOK_IDENTIFIER ? SYMTAB_GLOBAL : SYMTAB_LOCAL); if(symtype == SYMTAB_LOCAL && local_level == 0) { error(1, "Local symbol outside a LOCAL block"); return FAIL; } sym = lookup_symbol(token_string, symtype); if(sym == NULL || sym->type == SYM_FORWARD) { if(sym == NULL) { sym = add_symbol(token_string, symtype); sym->type = SYM_FORWARD; } add_patch(symtype, sym, PATCH8); get_token(); gen_code(instr_code); list_flags |= LIST_FORWARD; return OK; } } if(get_expression(&val) != OK) return FAIL; if(val < -0x80 || val > 0xff) { error(0, "8-bit literal out of range"); return FAIL; } gen_code(instr_code | (val & 0xff)); return OK; } /* * check if the current token is a valid ORG mode specifier * and return the mode (or O_NONE if not valid mode specifier) */ static int org_mode(void) { if(token_type == KW_EDATA) return O_EDATA; if(token_type == TOK_IDENTIFIER) { if(strcasecmp(token_string, "code") == 0) return O_PROGRAM; if(strcasecmp(token_string, "reg") == 0) return O_REGFILE; } return O_NONE; } /* * Device type selection: automatically include a device-specific * header file (which uses the set_pic_type directive and defines * the __do_config directive). */ static void do_device_include(char *devname) { char incname[256]; char *cp; int c; if(strncasecmp(devname, "PIC", 3) == 0) devname += 3; strcpy(incname, "_pic"); cp = &incname[4]; while(cp < &incname[sizeof(incname)-3]) { c = (unsigned char)(*devname); if(c == '\0') break; if(isupper(c)) c = tolower(c); *cp++ = c; devname++; } strcpy(cp, ".i"); get_token(); if(pic_instr_set != PIC_NONE) { /* * This error message would be misleading when the device type * has been set on the picasm command line, but because it can * only be done once from the command line, we can not get * this error condition in that case. */ error(1, "Duplicate DEVICE setting"); return; } begin_include(incname, 1, 1); } /* * The assembler itself */ static void assembler(char *fname, char *cmdline_pic_type) { static char symname[256]; struct symbol *sym; int op, t, symtype; long val; char *cp; sym = add_symbol("_PICASM", SYMTAB_GLOBAL); sym->type = SYM_DEFINED; sym->type2 = SYMT_CONSTANT; sym->v.value = I_VERSION; begin_include(fname, 0, 1); if(cmdline_pic_type != NULL) do_device_include(cmdline_pic_type); get_token(); while(token_type != TOK_EOF) { sym = NULL; if(token_type == TOK_IDENTIFIER || token_type == TOK_LOCAL_ID) { symtype = (token_type == TOK_IDENTIFIER ? SYMTAB_GLOBAL : SYMTAB_LOCAL); if(symtype == SYMTAB_LOCAL && local_level == 0) { error(1, "Local symbol outside a LOCAL block"); continue; } t = (line_buf_off == 0); p_strcpy(symname, token_string, sizeof symname); sym = lookup_symbol(symname, symtype); if(sym != NULL && sym->type == SYM_MACRO) { /* skip whitespace */ while(isspace(tok_char)) read_src_char(); if(line_buf_ptr != NULL && strncasecmp(line_buf_ptr-1, "macro", 5) == 0 && line_buf_ptr[4] != '.' && line_buf_ptr[4] != '_' && !isalnum((unsigned char)line_buf_ptr[4])) { error(1, "Multiple definition of macro '%s'", symname); continue; } expand_macro(sym); continue; } get_token(); switch(token_type) { case KW_MACRO: get_token(); if(sym != NULL) { error(1, "Multiply defined symbol '%s%s'", (symtype == SYMTAB_LOCAL ? "=" : ""), sym->name); continue; } define_macro(symname); goto line_end; case TOK_EQUAL: /* '=' can be used instead of EQU */ case KW_EQU: if(sym != NULL) { if(sym->type != SYM_FORWARD) error(0, "Multiply defined symbol '%s%s'", (symtype == SYMTAB_LOCAL ? "=" : ""), sym->name); } else { sym = add_symbol(symname, symtype); } get_token(); sym->type = SYM_DEFINED; sym->type2 = SYMT_CONSTANT; if(get_expression(&sym->v.value) != OK) continue; /* error_lineskip() done in expr.c */ list_val = sym->v.value; list_flags = LIST_VAL; goto line_end; case KW_SET: if(sym != NULL && sym->type != SYM_SET) { error(0, "Multiply defined symbol '%s%s'", (symtype == SYMTAB_LOCAL ? "=" : ""), sym->name); } else if(sym == NULL) { sym = add_symbol(symname, symtype); } get_token(); sym->type = SYM_SET; sym->type2 = SYMT_CONSTANT; if(get_expression(&sym->v.value) != OK) continue; list_val = sym->v.value; list_flags = LIST_VAL; goto line_end; case TOK_COLON: get_token(); goto do_label; default: if(t == 0) warning("Label not in the beginning of a line"); do_label: switch(O_Mode) { case O_PROGRAM: t = prog_location; break; case O_REGFILE: t = reg_location; break; case O_EDATA: t = edata_location; break; case O_NONE: t = org_val; break; } if(t < 0) { error(0, "ORG value not set"); } else { if(sym != NULL && sym->type != SYM_FORWARD) { error(0, "Multiply defined symbol '%s%s'", (symtype == SYMTAB_LOCAL ? "=" : ""), sym->name); } if(sym == NULL) { sym = add_symbol(symname, symtype); } sym->type = SYM_DEFINED; switch(O_Mode) { case O_PROGRAM: sym->type2 = SYMT_LABEL; break; case O_REGFILE: sym->type2 = SYMT_REGFILE; break; default: break; } sym->v.value = t; list_loc = t; list_flags = LIST_LOC; } break; } } /* if(token_type == TOK_IDENTIFIER... */ /* if this line has a label, 'sym' points to it */ if(token_type == TOK_NEWLINE) { write_listing_line(0); get_token(); continue; } if(token_type == TOK_IDENTIFIER && (sym = lookup_symbol(token_string, SYMTAB_GLOBAL)) != NULL && sym->type == SYM_MACRO) { expand_macro(sym); continue; } if(token_type == KW_END) break; if(token_type == KW_ERROR) { while(isspace((unsigned char)(*line_buf_ptr))) line_buf_ptr++; error(1, "%s", line_buf_ptr); continue; } op = token_type; get_token(); switch(op) { case KW_INCLUDE: if(token_type != TOK_STRCONST) { error(1, "Missing file name after INCLUDE"); continue; } p_strcpy(symname, token_string, sizeof symname); get_token(); if(token_type != TOK_NEWLINE && token_type != TOK_EOF) error(0, "Extraneous characters after a valid source line"); begin_include(symname, 1, 0); write_listing_line(0); get_token(); continue; case TOK_EQUAL: case KW_SET: case KW_EQU: if(sym == NULL) error(1, "SET/EQU without a label"); else error(1, "SET/EQU syntax error"); continue; case KW_MACRO: error(1, "MACRO without a macro name"); continue; case KW_ENDM: error(1, "ENDM not allowed outside a macro"); continue; case KW_EXITM: if(current_file == NULL || current_file->type != INC_MACRO) { error(1, "EXITM not allowed outside a macro"); continue; } cond_nest_count = current_file->cond_nest_count; end_include(); break; case KW_OPT: if(handle_opt() != OK) { error_lineskip(); continue; } break; case KW_LOCAL: add_local_symtab(); break; case KW_ENDLOCAL: if(local_level == 0) { error(1, "ENDLOCAL without LOCAL"); continue; } apply_patches(*local_patch_list_ptr); remove_local_symtab(); break; case KW_IF: if(sym != NULL) error(0, "Label not allowed with IF"); get_expression(&val); /*note: error status ignored*/ if(token_type != TOK_NEWLINE && token_type != TOK_EOF) error(0, "Extraneous characters after a valid source line"); if(val == 0) { write_listing_line(0); t=if_else_skip(); if(t == -1) fatal_error("Conditional not terminated"); else if(t == 1) error(0, "Label not allowed with %s", (token_type == KW_ELSE ? "ELSE" : "ENDIF")); if(token_type == KW_ELSE) cond_nest_count++; get_token(); goto line_end2; } else { cond_nest_count++; } break; case KW_ELSE: if(sym != NULL) error(0, "Label not allowed with %s", "ELSE"); if(current_file == NULL || cond_nest_count <= current_file->cond_nest_count) error(0, "ELSE without IF"); write_listing_line(0); t = if_else_skip(); if(t == -1) fatal_error("Conditional not terminated"); else if(t == 1) error(0, "Label not allowed with %s", (token_type == KW_ELSE ? "ELSE" : "ENDIF")); if(token_type == KW_ELSE) error(0, "Multiple ELSE statements with one IF"); cond_nest_count--; get_token(); goto line_end2; case KW_ENDIF: if(sym != NULL) error(0, "Label not allowed with %s", "ENDIF"); if(current_file == NULL || cond_nest_count <= current_file->cond_nest_count) error(0, "ENDIF without IF"); cond_nest_count--; break; case KW_ORG: if(pic_instr_set == PIC_NONE) fatal_error("PIC device type not set"); org_val = -1; if((t = org_mode()) != O_NONE) { get_token(); O_Mode = (org_mode_t)t; switch(O_Mode) { case O_PROGRAM: case O_NONE: /* shut up GCC warning, cannot really happen here */ org_val = prog_location; break; case O_REGFILE: org_val = reg_location; break; case O_EDATA: org_val = edata_location; break; } if(org_val < 0) { error(0, "ORG value not set"); O_Mode = O_NONE; } break; } if(get_expression(&val) != OK) continue; if(val < 0 || val >= prog_mem_size) { error(1, "ORG value out of range"); continue; } org_val = val; O_Mode = O_NONE; if(token_type == TOK_COMMA) { get_token(); if((t = org_mode()) == O_NONE) { error(0, "Invalid ORG mode"); } else { O_Mode = (org_mode_t)t; switch(O_Mode) { case O_PROGRAM: case O_NONE: /* shut up GCC warning, cannot really happen here */ prog_location = org_val; break; case O_REGFILE: reg_location = org_val; break; case O_EDATA: edata_location = org_val; break; } } get_token(); } list_loc = org_val; list_flags = LIST_LOC; break; case KW_DS: if(get_expression(&val) != OK) continue; if(O_Mode == O_NONE) { O_Mode = O_REGFILE; if(org_val < 0) { error(0, "ORG value not set"); reg_location = 0; } else { reg_location = org_val; } } if(O_Mode != O_REGFILE) { error(0, "ORG mode conflict"); } else { if(reg_location >= reg_file_limit) fatal_error("Register file address out of range"); list_loc = reg_location; list_flags = LIST_LOC; reg_location += val; } if(sym != NULL && sym->type2 == SYMT_UNKNOWN) sym->type2 = SYMT_REGFILE; break; case KW_CBLOCK: if(sym != NULL) error(0, "Label not allowed with CBLOCK"); if(token_type != TOK_NEWLINE && token_type != TOK_EOF) { if(get_expression(&val) != OK) { error_lineskip(); } else { reg_location = val; if(token_type != TOK_NEWLINE && token_type != TOK_EOF) error(0, "Extraneous characters after a valid source line"); skip_eol(); } } if(reg_location < 0) { error(0, "ORG/CBLOCK register address not set"); reg_location = 0; } /* not really implemented .... */ while(token_type != KW_ENDC) { list_loc = -1; while(token_type != TOK_NEWLINE) { if(token_type == TOK_EOF) { error(0, "Unexpected EOF inside a CBLOCK"); break; } if(token_type != TOK_IDENTIFIER) { error(1, "identifier expected"); goto out; } if(reg_location >= reg_file_limit) fatal_error("Register file address out of range"); if(list_loc < 0) list_loc = reg_location; sym = lookup_symbol(token_string, SYMTAB_GLOBAL); if(sym != NULL) { if(sym->type != SYM_FORWARD) { error(0, "Symbol type conflict (%s)", token_string); sym = NULL; } } else { sym = add_symbol(token_string, SYMTAB_GLOBAL); } if(sym != NULL) { sym->type = SYM_DEFINED; sym->type2 = SYMT_REGFILE; sym->v.value = reg_location; reg_location++; } get_token(); if(token_type == TOK_COMMA) get_token(); } list_flags = (list_loc >= 0 ? LIST_LOC : 0); write_listing_line(0); get_token(); /* skip the newline */ } get_token(); /* skip KW_ENDC token */ out: break; case KW_EDATA: if(pic_instr_set == PIC_NONE) fatal_error("PIC device type not set"); if(data_eeprom_size == 0) { error(1, "PIC%s does not have data EEPROM", pic_name); continue; } for(;;) { if(token_type == TOK_STRCONST) { for(cp = token_string; *cp != '\0'; cp++) gen_edata((int)((unsigned char)(*cp))); get_token(); } else { if(get_expression(&val) != OK) break; if(val < 0 || val > 0xff) { error(0, "Data EEPROM byte out of range"); } else { gen_edata(val); } } if(token_type != TOK_COMMA) break; get_token(); } break; case KW_CONFIG: if(pic_instr_set == PIC_NONE) fatal_error("PIC device type not set"); parse_config(); list_flags = LIST_PTR; list_ptr = &config_fuses; list_len = 1; break; /* Device type */ case KW_DEVICE: if(token_type != TOK_IDENTIFIER && token_type != TOK_STRCONST) { error(1, "DEVICE requires a device type"); continue; } do_device_include(token_string); break; case KW_SET_PIC_TYPE: if(pic_instr_set != PIC_NONE) fatal_error("PIC type already set"); if(token_type != TOK_STRCONST) fatal_error("Invalid set_pic_type parameters"); strncpy(pic_name, token_string, sizeof(pic_name)); pic_name[sizeof(pic_name)-1] = '\0'; get_token(); if(token_type != TOK_COMMA) fatal_error("Invalid set_pic_type parameters"); get_token(); if(token_type != TOK_STRCONST) fatal_error("Invalid set_pic_type parameters"); if(strcasecmp(token_string, "12-bit") == 0 || strcasecmp(token_string, "12bit") == 0) { pic_instr_set = PIC12BIT; } else if(strcasecmp(token_string, "14-bit") == 0 || strcasecmp(token_string, "14bit") == 0) { pic_instr_set = PIC14BIT; } else if(strcasecmp(token_string, "16-bit") == 0 || strcasecmp(token_string, "16bit") == 0) { pic_instr_set = PIC16BIT; } else if(strcasecmp(token_string, "16-bit-no-mul") == 0 || strcasecmp(token_string, "16bit_nomul") == 0) { pic_instr_set = PIC16BIT_NOMUL; } else { fatal_error("Invalid set_pic_type parameters"); } get_token(); cp = pic_name; if(strncasecmp(cp, "PIC", 3) == 0) cp += 3; p_snprintf(symname, sizeof symname, "__%s", cp); sym = add_symbol(symname, SYMTAB_GLOBAL); sym->type = SYM_DEFINED; sym->type2 = SYMT_CONSTANT; sym->v.value = 1; sym = lookup_symbol("__progmem_size", SYMTAB_GLOBAL); if(sym != NULL && (sym->type == SYM_SET || sym->type == SYM_DEFINED)) { if(sym->v.value < 0) fatal_error("Invalid __progmem_size"); prog_mem_size = sym->v.value; } sym = lookup_symbol("__data_eeprom_size", SYMTAB_GLOBAL); if(sym != NULL && (sym->type == SYM_SET || sym->type == SYM_DEFINED)) { if(sym->v.value < 0) fatal_error("Invalid __data_eeprom_size"); data_eeprom_size = sym->v.value; } break; /* PIC ID */ case KW_PICID: if(pic_instr_set == PIC_NONE) fatal_error("PIC device type not set"); if(pic_id[0] != INVALID_ID) { error(1, "Multiple ID definitions"); continue; } /* * should check if ID is really allowed for all PIC types. * Microchip 1994 databook specfically says that ID is not * implemented on PIC16C6x/74... but 1995/96 databook * has different information about that... * * But seems that the 16-bit PICs don't have an id * */ if(pic_instr_set == PIC16BIT) { error(1, "ID locations not supported on PIC%s", pic_name); continue; } for(t = 0;;) { if(get_expression(&val) != OK) continue; if(val < 0 || val > 0x3fff) { error(0, "PIC ID value out of range"); } else { if(t >= 4) { error(1, "PIC ID too long (max 4 bytes)"); continue; } pic_id[t] = (pic_instr_t)val; } t++; if(token_type != TOK_COMMA) break; get_token(); } if(t > 0) { list_flags = LIST_PTR; list_len = t; list_ptr = pic_id; while(t < 4) pic_id[t++] = 0x3fff; } break; /* mnemonics */ default: if(pic_instr_set == PIC_NONE) fatal_error("PIC device type not set"); switch(pic_instr_set) { case PIC12BIT: t = assemble_12bit_mnemonic(op); break; case PIC14BIT: default: t = assemble_14bit_mnemonic(op); break; case PIC16BIT_NOMUL: case PIC16BIT: t = assemble_16bit_mnemonic(op); break; } if(t != OK) continue; break; } line_end: write_listing_line(0); line_end2: if(token_type == TOK_EOF) continue; if(token_type != TOK_NEWLINE) { error(0, "Extraneous characters after a valid source line"); { #if 0 fprintf(stderr, "token_type==%d, token_string='%s'\n", token_type, token_string);/*XXX*/ #endif } skip_eol(); } get_token(); } /* while(token_type != TOK_EOF) */ /* * Close all open source files * (only really necessary if END has been used) */ while(current_file != NULL) end_include(); if(local_level > 0) error(0, "LOCAL not terminated with ENDLOCAL"); apply_patches(global_patch_list); } static void usage(void) { fputs("Usage: picasm " "[-o] [-l] [-s] [-ihx8m/ihx16] [-i]\n" " " "[-pic] [-d[=]] [-w[n]] \n", stderr); exit(EXIT_FAILURE); } /* * main program */ int main(int argc, char *argv[]) { static char in_filename[256], out_filename[256], list_filename[256], sym_filename[256]; static int out_format = IHX8M; int listing = 0, symdump = 0; char *p; time_t ti; struct tm *tm; char *cmdline_pic_type = NULL; pic_instr_set = PIC_NONE; out_filename[0] = '\0'; list_filename[0] = '\0'; sym_filename[0] = '\0'; warnlevel = 0; init_assembler(); while(argc > 1 && argv[1][0] == '-') { switch(argv[1][1]) { case 'o': /* output file name */ if(argv[1][2] != '\0') { p_strcpy(out_filename, &argv[1][2], sizeof out_filename); } else { if(argc < 3) { fputs("-o option requires a file name\n", stderr); exit(EXIT_FAILURE); } p_strcpy(out_filename, argv[2], sizeof out_filename); argc--; argv++; } break; case 'i': case 'I': /* output hex format (ihx8m/ihx16) */ if(strcasecmp(&argv[1][1], "ihx8m") == 0) { out_format = IHX8M; } else if(strcasecmp(&argv[1][1], "ihx16") == 0) { out_format = IHX16; } else { if(argv[1][2] != '\0') { add_include_path(&argv[1][2]); } else { if(argc < 3) usage(); add_include_path(argv[2]); argc--; argv++; } } break; case 'd': /* define symbols */ { char symname[256]; long val = 1; struct symbol *sym; p = &argv[1][2]; if(!isalpha((unsigned char)(*p)) && *p != '\0') { badsym: fputs("Bad symbol name (-d option)\n", stderr); exit(EXIT_FAILURE); } while(*p != '\0' && *p != '=') { if(!isalnum((unsigned char)(*p)) && *p != '_') goto badsym; if(p >= &argv[1][2] + 255) { fputs("Symbol too long (-d option)\n", stderr); exit(EXIT_FAILURE); } p++; } if(p == &argv[1][2]) goto badsym; memcpy(symname, &argv[1][2], p - &argv[1][2]); symname[p - &argv[1][2]] = '\0'; if(*p == '=') { char *ep; val = strtol(p+1, &ep, 0); if(*ep != '\0') { fputs("Invalid numeric value (-d option)\n", stderr); exit(EXIT_FAILURE); } } if(lookup_symbol(symname, SYMTAB_GLOBAL) != NULL) { fprintf(stderr, "Multiply defined symbol '%s'\n", symname); exit(EXIT_FAILURE); } sym = add_symbol(symname, SYMTAB_GLOBAL); sym->type = SYM_DEFINED; sym->type2 = SYMT_CONSTANT; sym->v.value = val; } break; case 'p': case 'P': /* PIC type */ if(!((argv[1][2] == 'i' || argv[1][2] == 'I') && (argv[1][3] == 'c' || argv[1][3] == 'C'))) usage(); cmdline_pic_type = &argv[1][1]; break; case 'l': /* listing/list filename */ listing = 1; if(argv[1][2] != '\0') p_strcpy(list_filename, &argv[1][2], sizeof list_filename); break; case 's': symdump++; break; case 'w': /* warning mode (gives some more warnings) */ if(argv[1][2] != '\0') { warnlevel = atoi(&argv[1][2]); } else { warnlevel = 1; } break; case 'v': /* version info */ fprintf(stderr, "12/14/16-bit PIC assembler " VERSION " -- Copyright 1995-2004 Timo Rossi\n"); break; case '-': /* end of option list */ case '\0': argc--; argv++; goto opt_done; default: usage(); } argc--; argv++; } opt_done: if(argc != 2) usage(); #ifdef BUILTIN_INCLUDE1 add_include_path(BUILTIN_INCLUDE1); #endif #ifdef BUILTIN_INCLUDE2 add_include_path(BUILTIN_INCLUDE2); #endif strncpy(in_filename, argv[1], sizeof(in_filename)-1); if(strchr(in_filename, '.') == NULL) p_strcat(in_filename, ".asm", sizeof in_filename); if(out_filename[0] == '\0') { p_strcpy(out_filename, in_filename, sizeof out_filename); if((p = strrchr(out_filename, '.')) != NULL) *p = '\0'; } if(strchr(out_filename, '.') == NULL) p_strcat(out_filename, ".hex", sizeof out_filename); list_fp = NULL; if(listing) { if(list_filename[0] == '\0') { p_strcpy(list_filename, in_filename, sizeof list_filename); if((p = strrchr(list_filename, '.')) != NULL) *p = '\0'; p_strcat(list_filename, ".lst", sizeof list_filename); } if((list_fp = fopen(list_filename, "w")) == NULL) fatal_error("Can't create listing file '%s': %s", list_filename, strerror(errno)); ti = time(NULL); tm = localtime(&ti); fprintf(list_fp, "** 12/14-bit PIC assembler " VERSION "\n"); fprintf(list_fp, "** %s assembled %s\n", in_filename, asctime(tm)); } assembler(in_filename, cmdline_pic_type); if(errors == 0) { if(pic_instr_set != PIC_NONE && code_generated) write_output(out_filename, out_format); else fputs("No code generated\n", stderr); } else { fprintf(stderr, "%d %s found\n", errors, errors == 1 ? "error" : "errors"); } if(warnings != 0) { fprintf(stderr, "%d %s\n", warnings, warnings == 1 ? "warning" : "warnings"); } if(list_fp) { if(symdump) dump_symtab(list_fp, symdump); fclose(list_fp); } return errors ? EXIT_FAILURE : EXIT_SUCCESS; }