picasm.c100644 423 0 102263 6131062217 11137 0ustar luigiwheel/* * picasm.c * * Freely distributable for non-commercial use (basically, * don't sell this program without my permission. You can * use it for developing commercial PIC applications, but * of course I am not responsible for any damages caused by * this assembler generating bad code or anything like that) * * Copyright 1995-1996 by Timo Rossi * email: trossi@jyu.fi * www: http://www.jyu.fi/~trossi/ * */ #include #include #include #include #include #include #include "picasm.h" int warnmode; static int total_line_count; static int errors, warnings; /* error & warning counts */ unsigned short list_flags; static FILE *list_fp; static int listing_on; static unsigned short list_loc, *list_ptr; static long list_val, list_len; 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]; struct patch *patch_list; /* * This PIC type table is not anywhere near complete... * (I don't have full data sheets of all the chips that I have * put in this table, so it may contain errors...) */ static struct pic_type pic_types[] = { { "16C52", 384, 0, 32, PIC12BIT|PIC_ID }, { "16C54", 512, 0, 32, PIC12BIT|PIC_ID }, { "16C55", 512, 0, 32, PIC12BIT|PIC_ID }, { "16C56", 1024, 0, 32, PIC12BIT|PIC_ID }, { "16C57", 2048, 0, 32, PIC12BIT|PIC_ID }, { "16C61", 1024, 0, 48, PIC14BIT|PIC_ID }, /* ID? */ { "16C64", 2048, 0, 128, PIC14BIT }, /* total RAM 128 bytes */ { "16C65", 4096, 0, 128, PIC14BIT }, /* total RAM 192 bytes */ { "16C71", 1024, 0, 48, PIC14BIT|PIC_ID }, { "16C72", 2048, 0, 128, PIC14BIT|PIC_ID }, /* total RAM 128 bytes */ { "16C74", 4096, 0, 128, PIC14BIT }, /* total RAM 192 bytes */ { "16C84", 1024, 64, 48, PIC14BIT|PIC_ID }, { NULL, 0, 0, 0, -1} /* end marker */ }; struct pic_type *pic_type; short instr_set; int prog_mem_size, reg_file_size; short code_generated; static unsigned short prog_mem[PROGMEM_MAX]; static unsigned short data_mem[DATAMEM_MAX]; static unsigned short config_fuses; static unsigned short pic_id[4]; int 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; /* Error handling */ /* * Show line number/line with error message */ static void err_line_ref(void) { struct inc_file *inc; if(current_file != NULL) { inc = current_file; if(inc->type != INC_FILE) { fprintf(stderr, "(Macro %s) ", inc->v.m.sym->name); while(inc != NULL && inc->type != INC_FILE) inc = inc->next; } fprintf(stderr, "File '%s' at line %d:\n", inc->v.f.fname, inc->linenum); fputs(line_buffer, stderr); if(line_buffer[0] != '\0' && line_buffer[strlen(line_buffer)-1] != '\n') fputc('\n', stderr); } } /* * Warning message */ void warning(char *fmt, ...) { va_list args; err_line_ref(); fputs("Warning: ", stderr); va_start(args, fmt); vfprintf(stderr, fmt, args); if(list_fp != NULL) { fputs("Warning: ", list_fp); vfprintf(list_fp, fmt, args); fputc('\n', list_fp); } fputc('\n', stderr); va_end(args); 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; err_line_ref(); fputs("Error: ", stderr); va_start(args, fmt); vfprintf(stderr, fmt, args); if(list_fp != NULL) { fputs("Error: ", list_fp); vfprintf(list_fp, fmt, args); fputc('\n', list_fp); } fputc('\n', stderr); va_end(args); errors++; if(lskip) error_lineskip(); } /* * Fatal error message */ void fatal_error(char *fmt, ...) { va_list args; err_line_ref(); fputs("Fatal error: ", stderr); va_start(args, fmt); vfprintf(stderr, fmt, args); fputc('\n', stderr); va_end(args); 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) { unsigned short *cp; int n; init_symtab(); /* initialize symbol table */ /* 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 = DATAMEM_MAX, cp = data_mem; n-- > 0; *cp++ = INVALID_DATA); /* initialize jump patch list */ 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; } /* * generate program code */ void gen_code(int val) { if(pic_type == NULL) 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\n", prog_location); if((list_flags & LIST_LOC) == 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 >= pic_type->datamem_size) fatal_error("Data EEPROM address out of range"); if(data_mem[edata_location] < 0x100) warning("Overlapping EEPROM data at 0x%x\n", edata_location); if((list_flags & LIST_LOC) == 0) { list_loc = edata_location; list_flags = LIST_LOC | LIST_EDATA; } list_len++; data_mem[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 */ unsigned short *data, /* pointer to word data */ int format) /* IHX8M or IHX16 */ { 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", *data & 0xff, (*data >> 8) & 0xff); break; case IHX16: fprintf(fp, "%02X%02X", (*data >> 8) & 0xff, *data & 0xff); break; } check += (*data & 0xff) + ((*data >> 8) & 0xff); data++; } /* write checksum, assumes 2-complement */ fprintf(fp, "%02X\n", (-check) & 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'", fname); /* 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(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; } } /* config fuses */ if(config_fuses != INVALID_CONFIG) { switch(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; } } if(pic_type->datamem_size > 0) /* data EEPROM */ { for(loc = 0;;) { while(loc < pic_type->datamem_size && data_mem[loc] >= 0x100) loc++; if(loc >= pic_type->datamem_size) break; reclen = 0; while(reclen < 8 && loc < pic_type->datamem_size && data_mem[loc] < 0x100) { loc++; reclen++; } write_hex_record(fp, reclen, 0x2100+loc-reclen, &data_mem[loc-reclen], format); } } fputs(":00000001FF\n", 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) fprintf(list_fp, "%c%04X ", ((list_flags & LIST_FORWARD) ? '?' : ' '), ((list_flags & LIST_PROG) ? prog_mem[list_loc] : ((list_flags & LIST_EDATA) ? data_mem[list_loc] : *list_ptr++))); else fputs(" ", list_fp); } fputs(line_buffer, list_fp); if(line_buffer[0] != '\0' && line_buffer[strlen(line_buffer)-1] != '\n') 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); fprintf(list_fp," %04X\n", ((list_flags & LIST_PROG) ? prog_mem[list_loc] : ((list_flags & LIST_EDATA) ? data_mem[list_loc] : *list_ptr++))); } } 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 as macro definition time * if inside a macro) */ static int handle_opt(void) { if(token_type != TOK_IDENTIFIER) { error(0, "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(0, "OPT syntax error"); return FAIL; } get_token(); return OK; } /* * Define a macro */ 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); 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 defintion 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(); } /* * Check Yes/No (or Disabled/Enabled or On/Off) strings for CONFIG * * returns: 0=no, 1=yes, -1=error * */ static int config_yes_no(char *s) { if(strcasecmp(s, "Yes") == 0 || strcasecmp(s, "On") == 0 || strcasecmp(s, "Enabled") == 0) return 1; else if(strcasecmp(s, "No") == 0 || strcasecmp(s, "Off") == 0 || strcasecmp(s, "Disabled") == 0) return 0; else return -1; /* error */ } /* * 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; do { skip_eol(); get_token(); write_listing_line(1); t = 0; if(token_type == TOK_IDENTIFIER) { t = 1; get_token(); if(token_type == TOK_COMMA) 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); if(token_type == TOK_EOF || token_type == KW_END) return -1; else return t; } /* * Add a patch pointing to the current location */ void add_patch(struct symbol *sym, int type) { struct patch *ptch; 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; patch_list = ptch; } /* * Generate code for an instruction with 8-bit literal data * allows forward references */ int gen_byte_c(int instr_code) { int t; long val; struct symbol *sym; t = 0; if(token_type == TOK_IDENTIFIER) { sym = lookup_symbol(token_string); if(sym == NULL || sym->type == SYM_FORWARD) { if(sym == NULL) { sym = add_symbol(token_string); sym->type = SYM_FORWARD; } add_patch(sym, PATCH8); get_token(); gen_code(instr_code); list_flags |= LIST_FORWARD; return OK; } } val = get_expression(); if(expr_error) return FAIL; if(val < -0x80 || val > 0xff) { #if 0 error(0, "8-bit literal out of range"); printf("got 0x%08x\n", val); return FAIL; #else warning("8-bit literal possibly out of range (0x%08x)\n", val); #endif } 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; } /* * The assembler itself */ static void assembler(char *fname) { static char symname[256]; struct symbol *sym; struct patch *ptch; int op, t; long val; char *cp; struct pic_type *pic; begin_include(fname); get_token(); while(token_type != TOK_EOF) { sym = NULL; if(token_type == TOK_IDENTIFIER) { strcpy(symname, token_string); t = line_buf_flag; sym = lookup_symbol(symname); if(sym != NULL && sym->type == SYM_MACRO) { /* skip whitespace */ while(tok_char != '\n' && isspace(tok_char)) read_src_char(); if(line_buf_ptr != NULL && (line_buf_ptr[-1] == 'm' || line_buf_ptr[-1] == 'M') && (line_buf_ptr[0] == 'a' || line_buf_ptr[0] == 'A') && (line_buf_ptr[1] == 'c' || line_buf_ptr[1] == 'C') && (line_buf_ptr[2] == 'r' || line_buf_ptr[2] == 'R') && (line_buf_ptr[3] == 'o' || line_buf_ptr[3] == 'O')) { 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'", sym->name); continue; } define_macro(symname); goto line_end; case KW_EQU: case TOK_EQUAL: if(sym != NULL) { if(sym->type != SYM_FORWARD) error(0, "Multiply defined symbol '%s'", sym->name); } else sym = add_symbol(symname); get_token(); sym->type = SYM_DEFINED; sym->v.value = get_expression(); if(expr_error) 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'", sym->name); else if(sym == NULL) sym = add_symbol(symname); get_token(); sym->type = SYM_SET; sym->v.value = get_expression(); if(expr_error) 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'", sym->name); if(sym == NULL) sym = add_symbol(symname); sym->type = SYM_DEFINED; sym->v.value = t; list_loc = t; list_flags = LIST_LOC; } break; } } /* 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)) != NULL && sym->type == SYM_MACRO) { expand_macro(sym); continue; } if(token_type == KW_END) break; op = token_type; get_token(); switch(op) { case KW_INCLUDE: if(token_type != TOK_STRCONST) { error(1, "Missing file name after INCLUDE"); continue; } strcpy(symname, token_string); get_token(); if(token_type != TOK_NEWLINE && token_type != TOK_EOF) error(0, "Extraneous characters after a valid source line"); begin_include(symname); write_listing_line(0); get_token(); continue; case KW_SET: case KW_EQU: case TOK_EQUAL: 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: /* * EXITM works now (version 0.97). Strange that * nobody noticed that it wasn't implemented * at all in previous versions. */ 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_IF: if(sym != NULL) error(0, "Label not allowed with IF"); val = get_expression(); 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_type == NULL) fatal_error("PIC device type not set"); org_val = -1; if((t = org_mode()) != O_NONE) { get_token(); O_Mode = t; switch(O_Mode) { case O_PROGRAM: t = prog_location; break; case O_REGFILE: t = reg_location; break; case O_EDATA: t = edata_location; break; } if(t < 0) error(0, "ORG value not set"); break; } val = get_expression(); if(expr_error) 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 = t; get_token(); } list_loc = org_val; list_flags = LIST_LOC; break; case KW_DS: val = get_expression(); if(expr_error) 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_size) fatal_error("Register file address out of range"); list_loc = reg_location; list_flags = LIST_LOC; reg_location += val; } break; case KW_EDATA: if(pic_type == NULL) fatal_error("PIC device type not set"); if(pic_type->datamem_size == 0) { error(1, "PIC%s does not have data EEPROM", pic_type->name); continue; } for(;;) { if(token_type == TOK_STRCONST) { for(cp = token_string; *cp != '\0'; cp++) gen_edata((int)((unsigned char)(*cp))); get_token(); } else { val = get_expression(); if(expr_error) continue; 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_type == NULL) fatal_error("PIC device type not set"); if(config_fuses != INVALID_CONFIG) { error(1, "Multiple CONFIG definitions"); continue; } for(;;) { if(token_type != TOK_IDENTIFIER) { cfg_error: error(1, "CONFIG syntax error"); continue; } strcpy(symname, token_string); get_token(); if(token_type != TOK_EQUAL) { error(1, "'=' expected"); continue; } get_token(); if(token_type != TOK_IDENTIFIER) goto cfg_error; if(strcasecmp(symname, "CP") == 0) { /* code protect */ if((t = config_yes_no(token_string)) < 0) goto cfg_error; switch(instr_set) { case PIC12BIT: config_fuses = (config_fuses & 0xff7) | (t ? 0 : 0x8); break; case PIC14BIT: /* set both protect fuses with 16C64/16C74 */ config_fuses = (config_fuses & (pic_type->regfile_size == 128 ? 0x3fcf : 0x3fef)) | (t ? 0 : 0x30); break; } } else if(strcasecmp(symname, "PWRT") == 0) { /* powerup timer */ if((t = config_yes_no(token_string)) < 0) goto cfg_error; switch(instr_set) { case PIC12BIT: error(0, "PIC%s config doesn't have the PWRTE bit", pic_type->name); break; case PIC14BIT: config_fuses = (config_fuses & 0x3ff7) | (t ? 8 : 0); break; } } else if(strcasecmp(symname, "WDT") == 0) { /* watchdog timer */ if((t = config_yes_no(token_string)) < 0) goto cfg_error; switch(instr_set) { case PIC12BIT: config_fuses = (config_fuses & 0xffb) | (t ? 4 : 0); break; case PIC14BIT: config_fuses = (config_fuses & 0x3ffb) | (t ? 4 : 0); break; } } else if(strcasecmp(symname, "OSC") == 0) { /* oscillator */ if(strcasecmp(token_string, "LP") == 0) config_fuses = (config_fuses & 0x3ffc); else if(strcasecmp(token_string, "XT") == 0) config_fuses = (config_fuses & 0x3ffc) | 1; else if(strcasecmp(token_string, "HS") == 0) config_fuses = (config_fuses & 0x3ffc) | 2; else if(strcasecmp(token_string, "RC") == 0) config_fuses = (config_fuses & 0x3ffc) | 3; else goto cfg_error; if(instr_set == PIC12BIT) config_fuses &= 0xfff; } else goto cfg_error; get_token(); if(token_type != TOK_COMMA) break; get_token(); } 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; } cp = token_string; if((token_string[0] == 'p' || token_string[0] == 'P') && (token_string[1] == 'i' || token_string[1] == 'I') && (token_string[2] == 'c' || token_string[2] == 'C')) cp += 3; for(pic = pic_types; pic->name != NULL; pic++) { if(strcasecmp(pic->name, cp) == 0) break; } if(pic->name == NULL) { error(1, "Invalid PIC device type"); continue; } if(pic_type != NULL && pic_type != pic) { error(1, "Duplicate DEVICE setting"); continue; } pic_type = pic; instr_set = pic_type->instr_flags & INSTRSET_MASK; prog_mem_size = pic_type->progmem_size; reg_file_size = pic_type->regfile_size; get_token(); break; /* PIC ID */ case KW_PICID: if(pic_type == NULL) fatal_error("PIC device type not set"); if(pic_id[0] != INVALID_ID) { error(1, "Multiple ID definitions"); continue; } if((pic_type->instr_flags & PIC_ID) == 0) { error(1, "ID not allowed with PIC%s", pic_type->name); continue; } for(t = 0;;) { val = get_expression(); if(expr_error) 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] = 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: switch(instr_set) { case PIC12BIT: t = assemble_12bit_mnemonic(op); break; case PIC14BIT: default: t = assemble_14bit_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"); 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(); /* * fix forward jumps/calls */ for(ptch = patch_list; ptch != NULL; ptch = ptch->next) { if(ptch->label->type == SYM_FORWARD) error(0, "Undefined label '%s'", ptch->label->name); else switch(ptch->type) { case PATCH8: if(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')", 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; } } } /* * main program */ int main(int argc, char *argv[]) { static char in_filename[256], out_filename[256], list_filename[256]; static int out_format = IHX8M; static int listing = 0; char *p; time_t ti; struct tm *tm; pic_type = NULL; out_filename[0] = '\0'; list_filename[0] = '\0'; warnmode = 0; while(argc > 1 && argv[1][0] == '-') { switch(argv[1][1]) { case 'o': if(argv[1][2] != '\0') strcpy(out_filename, &argv[1][2]); else { if(argc < 3) { fputs("-o option requires a file name\n", stderr); exit(EXIT_FAILURE); } strcpy(out_filename, argv[2]); argc--; argv++; } break; case 'i': case 'I': if(strcasecmp(&argv[1][1], "ihx8m") == 0) out_format = IHX8M; else if(strcasecmp(&argv[1][1], "ihx16") == 0) out_format = IHX16; else goto usage; break; case 'p': case 'P': if(!((argv[1][2] == 'i' || argv[1][2] == 'I') || (argv[1][3] == 'c' || argv[1][3] == 'C'))) goto usage; for(pic_type = pic_types; pic_type->name != NULL; pic_type++) { if(strcasecmp(pic_type->name, &argv[1][4]) == 0) break; } if(pic_type->name == NULL) { fprintf(stderr, "Invalid device type '%s'\n", &argv[1][1]); exit(EXIT_FAILURE); } instr_set = pic_type->instr_flags & INSTRSET_MASK; prog_mem_size = pic_type->progmem_size; reg_file_size = pic_type->regfile_size; break; case 'l': listing = 1; if(argv[1][2] != '\0') strcpy(list_filename, &argv[1][2]); break; case 'w': warnmode = 1; break; case 'v': fprintf(stderr, "12/14-bit PIC assembler " VERSION " -- Copyright 1995-1996 by Timo Rossi\n"); break; case '-': case '\0': argc--; argv++; goto opt_done; default: goto usage; } argc--; argv++; } opt_done: if(argc != 2) { usage: fputs("Usage: picasm [-o] [-l] [-ihx8m/ihx16]\n" " [-pic] [-w] \n", stderr); exit(EXIT_FAILURE); } strncpy(in_filename, argv[1], sizeof(in_filename)-1); if(strchr(in_filename, '.') == NULL) strcat(in_filename, ".asm"); if(out_filename[0] == '\0') { strcpy(out_filename, in_filename); if((p = strrchr(out_filename, '.')) != NULL) *p = '\0'; } if(strchr(out_filename, '.') == NULL) strcat(out_filename, ".hex"); init_assembler(); list_fp = NULL; if(listing) { if(list_filename[0] == '\0') { strcpy(list_filename, in_filename); if((p = strrchr(list_filename, '.')) != NULL) *p = '\0'; strcat(list_filename, ".lst"); } if((list_fp = fopen(list_filename, "w")) == NULL) fatal_error("Can't create listing file '%s'", list_filename); 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); if(errors == 0) { if(code_generated) write_output(out_filename, out_format); else fputs("No code generated\n", stderr); } else fprintf(stderr, "%d error%s found\n", errors, errors == 1 ? "" : "s"); if(warnings != 0) fprintf(stderr, "%d warning%s\n", warnings, warnings == 1 ? "" : "s"); if(list_fp) fclose(list_fp); exit(EXIT_SUCCESS); } token.c100644 423 0 36352 6125344262 10776 0ustar luigiwheel/* * picasm -- token.c * * Include handling, macro expansion, lexical analysis * */ #include #include #include #include #include "picasm.h" /* * keyword table for tokenizer * * this must be in sync with the token definitions in picasm.h */ static char Keyword_Table[] = { "include\0" "macro\0" "endm\0" "exitm\0" "if\0" "else\0" "endif\0" "equ\0" "set\0" "end\0" "org\0" "ds\0" "edata\0" "config\0" "picid\0" "device\0" "defined\0" "streq\0" "isstr\0" "chrval\0" "opt\0" /* 12/14-bit PIC instruction mnemonics */ "addlw\0" "addwf\0" "andlw\0" "andwf\0" "bcf\0" "bsf\0" "btfsc\0" "btfss\0" "call\0" "clrf\0" "clrw\0" "clrwdt\0" "comf\0" "decf\0" "decfsz\0" "goto\0" "incf\0" "incfsz\0" "iorlw\0" "iorwf\0" "movlw\0" "movf\0" "movwf\0" "nop\0" "option\0" "retfie\0" "retlw\0" "return\0" "rlf\0" "rrf\0" "sleep\0" "sublw\0" "subwf\0" "swapf\0" "tris\0" "xorlw\0" "xorwf\0" "\0" }; /* tokenizer definitions & variables */ int tok_char; int token_type, line_buf_flag; char token_string[TOKSIZE]; long token_int_val; /* * include file handling */ void begin_include(char *fname) { struct inc_file *p; p = mem_alloc(sizeof(struct inc_file)); p->type = INC_FILE; p->v.f.fname = mem_alloc(strlen(fname)+1); strcpy(p->v.f.fname, fname); p->linenum = 0; p->cond_nest_count = cond_nest_count; if((p->v.f.fp = fopen(p->v.f.fname, "r")) == NULL) { if(current_file == NULL) fatal_error("Can't open '%s'", p->v.f.fname); else { error(0, "Can't open include file '%s'", p->v.f.fname); free(p->v.f.fname); free(p); line_buf_ptr = NULL; tok_char = ' '; return; } } p->next = current_file; current_file = p; line_buf_ptr = NULL; tok_char = ' '; } /* * Move to previous level of include/macro */ void end_include(void) { struct inc_file *p; struct macro_arg *arg1, *arg2; if(current_file != NULL) { if(cond_nest_count != current_file->cond_nest_count) { error(0, "conditional assembly not terminated by ENDIF"); cond_nest_count = current_file->cond_nest_count; } p = current_file->next; if(current_file->type == INC_FILE) { fclose(current_file->v.f.fp); free(current_file->v.f.fname); } else { /* free macro arguments */ arg1 = current_file->v.m.args; while(arg1 != NULL) { arg2 = arg1->next; free(arg1); arg1 = arg2; } } free(current_file); current_file = p; } } /* * Expand a macro */ void expand_macro(struct symbol *sym) { struct inc_file *minc; struct macro_arg *arg; char *cp; int narg; int parcnt, d_char; write_listing_line(0); /* list the macro call line */ minc = mem_alloc(sizeof(struct inc_file)); minc->type = INC_MACRO; minc->v.m.sym = sym; minc->v.m.ml = sym->v.text; minc->linenum = 0; minc->cond_nest_count = cond_nest_count; minc->v.m.args = NULL; minc->v.m.uniq_id = unique_id_count++; arg = NULL; for(narg = 1;;narg++) { while(tok_char != '\n' && isspace(tok_char)) /* skip whitespace */ read_src_char(); if(tok_char == '\n' || tok_char == '\0' || tok_char == ';' || tok_char == EOF) break; cp = line_buf_ptr-1; /* * Macro parameters are separated by commas. However, strings and * character constants (using double and single quotes) * can be used even if they contain commas. Also commas * inside parenthesis (such as function parameter delimiters) * don't count as macro parameter separators. * */ parcnt = 0; /* parenthesis nesting count */ while(!isspace(tok_char) && tok_char != '\n' && tok_char != '\0' && tok_char != ';' && tok_char != EOF) { if(parcnt == 0 && tok_char == ',') break; if(tok_char == '(') parcnt++; else if(tok_char == ')') parcnt--; else if(tok_char == '"' || tok_char == '\'') { /* quoted string or character constant */ d_char = tok_char; do { read_src_char(); } while(tok_char != d_char && tok_char != '\n' && tok_char != '\0' && tok_char != EOF); if(tok_char != d_char) break; } read_src_char(); } if(narg >= 10) warning("Too many macro arguments (max. 9)"); if(arg == NULL) { arg = mem_alloc(sizeof(struct macro_arg) +(line_buf_ptr-cp-1)); minc->v.m.args = arg; } else { arg->next = mem_alloc(sizeof(struct macro_arg) +(line_buf_ptr-cp-1)); arg = arg->next; } strncpy(arg->text, cp, line_buf_ptr-cp-1); arg->text[line_buf_ptr-cp-1] = '\0'; arg->next = NULL; /* skip whitespace */ while(tok_char != '\n' && isspace(tok_char)) read_src_char(); if(tok_char != ',') break; read_src_char(); } if(tok_char != ';' && tok_char != '\n' && tok_char != '\0' && tok_char != EOF) error(0, "Extraneous characters after a valid source line"); minc->next = current_file; current_file = minc; line_buf_ptr = NULL; tok_char = ' '; get_token(); } /* * Read a character from source file. * Handles includes and macros. */ void read_src_char(void) { char *scp, *pcp, *dcp; int parm; struct macro_arg *arg; static char tmpbuf[12]; if(line_buf_ptr == NULL || *line_buf_ptr == '\0') { if(current_file == NULL) { tok_char = EOF; return; } getc1: if(current_file->type == INC_MACRO) { if(current_file->v.m.ml == NULL) { end_include(); goto getc1; } scp = current_file->v.m.ml->text; dcp = line_buffer; while(*scp != '\0' && dcp < &line_buffer[sizeof(line_buffer)]) { if(*scp == '\\') { scp++; if(*scp >= '1' && *scp <= '9') /* macro arg */ { parm = *scp - '1'; /* macro arg #, starting from 0 */ for(arg = current_file->v.m.args; arg != NULL && parm > 0; arg = arg->next, parm--); if(arg != NULL) { for(pcp = arg->text; *pcp != '\0' && dcp < &line_buffer[sizeof(line_buffer)];) *dcp++ = *pcp++; } scp++; } else if(*scp == '0' || *scp == '@') { sprintf(tmpbuf, "%03d", current_file->v.m.uniq_id); for(pcp = tmpbuf; *pcp != '\0' && dcp < &line_buffer[sizeof(line_buffer)];) *dcp++ = *pcp++; scp++; } else if(*scp == '#') /* number of arguments */ { for(parm = 0, arg = current_file->v.m.args; arg != NULL; arg = arg->next, parm++); sprintf(tmpbuf, "%d", parm); for(pcp = tmpbuf; *pcp != '\0' && dcp < &line_buffer[sizeof(line_buffer)];) *dcp++ = *pcp++; scp++; } else *dcp++ = *scp; } else *dcp++ = *scp++; } if(dcp == &line_buffer[sizeof(line_buffer)]) { error(0, "Line buffer overflow"); dcp--; } *dcp = '\0'; /* NUL-terminate the line */ current_file->v.m.ml = current_file->v.m.ml->next; } else { if(fgets(line_buffer, sizeof(line_buffer)-1, current_file->v.f.fp) == NULL) { if(current_file->next != NULL) { end_include(); goto getc1; } tok_char = EOF; return; } } current_file->linenum++; line_buf_ptr = line_buffer; } tok_char = ((unsigned char)(*line_buf_ptr++)); } /* * Lexical analyzer * Returns the next token from the source file */ void get_token(void) { int tp, base; char *cp; for(;;) { /* * skip spaces */ while(tok_char != '\n' && isspace(tok_char)) read_src_char(); if(tok_char == EOF) { token_type = TOK_EOF; token_string[0] = '\0'; return; } if(tok_char != ';') break; /* comment */ line_buf_ptr = NULL; tok_char = '\n'; } /* for(;;) */ /* * character constant (integer) * (does not currently handle the quote character) */ if(tok_char == '\'') { read_src_char(); token_string[0] = tok_char; read_src_char(); if(tok_char != '\'') goto invalid_token; read_src_char(); token_string[1] = '\0'; token_int_val = (long)((unsigned char)token_string[0]); token_type = TOK_INTCONST; return; } if(tok_char == '"') /* string constant (include filename) */ { read_src_char(); tp = 0; while(tp < TOKSIZE-1 && tok_char != '"' && tok_char != EOF) { token_string[tp++] = tok_char; read_src_char(); } if(tok_char != '\"') error(0, "String not terminated"); token_string[tp] = '\0'; read_src_char(); token_type = TOK_STRCONST; return; } /* * integer number */ if(isdigit(tok_char)) { token_type = TOK_INTCONST; token_string[0] = tok_char; tp = 1; read_src_char(); if(token_string[0] == '0') { if(tok_char == 'x' || tok_char == 'X') { /* hex number */ token_string[tp++] = tok_char; read_src_char(); while(tp < TOKSIZE-1 && isxdigit(tok_char)) { token_string[tp++] = tok_char; read_src_char(); } token_string[tp] = '\0'; token_int_val = strtoul(&token_string[2], NULL, 16); /* should put range check here */ return; } } while(tp < TOKSIZE-2 && isxdigit(tok_char)) { token_string[tp++] = tok_char; read_src_char(); } base = 10; switch(tok_char) { case 'H': /* hex */ case 'h': base = 16; /* hex */ token_string[tp++] = tok_char; read_src_char(); break; case 'O': /* octal */ case 'o': base = 8; /* octal */ token_string[tp++] = tok_char; read_src_char(); break; default: if(token_string[0] == '0' && (token_string[1] == 'b' || token_string[1] == 'B')) { token_string[tp] = '\0'; token_int_val = strtoul(&token_string[2], &cp, 2); if(cp != &token_string[tp]) error(0, "Invalid digit in a number"); /* should put range check here */ return; } else if(token_string[tp-1] == 'B' || token_string[tp-1] == 'b') { base = 2; } else { if(token_string[tp-1] != 'D' && token_string[tp-1] != 'd') token_string[tp++] = '\0'; } break; } token_string[tp] = '\0'; token_int_val = strtoul(token_string, &cp, base); if(cp != &token_string[tp-1]) error(0, "Invalid digit in a number"); /* should put range check here */ return; } /* * Handle B'10010100' binary etc. */ if((tok_char == 'b' || tok_char == 'B' || tok_char == 'd' || tok_char == 'D' || tok_char == 'h' || tok_char == 'H' || tok_char == 'o' || tok_char == 'O') && line_buf_ptr != NULL && *line_buf_ptr == '\'') { token_string[0] = tok_char; read_src_char(); token_string[1] = tok_char; read_src_char(); tp = 2; while(tp < TOKSIZE-1 && isxdigit(tok_char)) { token_string[tp++] = tok_char; read_src_char(); } if(tok_char != '\'') goto invalid_token; token_string[tp++] = tok_char; read_src_char(); token_string[tp] = '\0'; switch(token_string[0]) { case 'b': case 'B': base = 2; break; case 'o': case 'O': base = 8; break; case 'h': case 'H': base = 16; break; case 'd': case 'D': default: base = 10; break; } token_int_val = strtoul(&token_string[2], &cp, base); if(cp != &token_string[tp-1]) error(0, "Invalid digit in a number"); /* should put range check here */ token_type = TOK_INTCONST; return; } /* * keyword or identifier */ if(tok_char == '_' || isalpha(tok_char)) { line_buf_flag = (line_buf_ptr == &line_buffer[1]); token_string[0] = tok_char; tp = 1; read_src_char(); while(tp < TOKSIZE-1 && (tok_char == '_' || isalnum(tok_char))) { token_string[tp++] = tok_char; read_src_char(); } token_string[tp] = '\0'; token_type = FIRST_KW; cp = Keyword_Table; while(*cp) { if(strcasecmp(token_string, cp) == 0) return; while(*cp++); token_type++; } token_type = TOK_IDENTIFIER; return; } /* * non-numeric & non-alpha tokens */ switch(tok_char) { case '\n': case '\0': token_type = TOK_NEWLINE; strcpy(token_string, "\\n"); skip_eol(); return; case '<': token_string[0] = tok_char; read_src_char(); if(tok_char == '<') { token_string[1] = tok_char; token_string[2] = '\0'; token_type = TOK_LSHIFT; read_src_char(); return; } if(tok_char == '=') { token_string[1] = tok_char; token_string[2] = '\0'; token_type = TOK_LESS_EQ; read_src_char(); return; } if(tok_char == '>') { token_string[1] = tok_char; token_string[2] = '\0'; token_type = TOK_NOT_EQ; read_src_char(); return; } token_type = TOK_LESS; token_string[1] = '\0'; return; case '>': token_string[0] = tok_char; read_src_char(); if(tok_char == '>') { token_string[1] = tok_char; token_string[2] = '\0'; token_type = TOK_RSHIFT; read_src_char(); return; } if(tok_char == '=') { token_string[1] = tok_char; token_string[2] = '\0'; token_type = TOK_GT_EQ; read_src_char(); return; } token_string[1] = '\0'; token_type = TOK_GREATER; return; case '!': token_string[0] = tok_char; read_src_char(); if(tok_char != '=') goto invalid_token; token_string[1] = tok_char; token_string[2] = '\0'; read_src_char(); token_type = TOK_NOT_EQ; return; case '=': token_string[0] = tok_char; read_src_char(); if(tok_char == '=') { token_string[1] = tok_char; read_src_char(); token_string[2] = '\0'; token_type = TOK_EQ; return; } if(tok_char == '<') { token_string[1] = tok_char; read_src_char(); token_string[2] = '\0'; token_type = TOK_LESS_EQ; return; } if(tok_char == '>') { token_string[1] = tok_char; read_src_char(); token_string[2] = '\0'; token_type = TOK_GT_EQ; return; } token_string[1] = '\0'; token_type = TOK_EQUAL; return; case '\\': token_type = TOK_BACKSLASH; break; case ',': token_type = TOK_COMMA; break; case '.': token_type = TOK_PERIOD; break; case '$': token_type = TOK_DOLLAR; break; case '(': token_type = TOK_LEFTPAR; break; case ')': token_type = TOK_RIGHTPAR; break; case '+': token_type = TOK_PLUS; break; case '-': token_type = TOK_MINUS; break; case '&': token_type = TOK_BITAND; break; case '|': token_type = TOK_BITOR; break; case '^': token_type = TOK_BITXOR; break; case '~': token_type = TOK_BITNOT; break; case '*': token_type = TOK_ASTERISK; break; case '/': token_type = TOK_SLASH; break; case '%': token_type = TOK_PERCENT; break; case ':': token_type = TOK_COLON; break; case '[': token_type = TOK_LEFTBRAK; break; case ']': token_type = TOK_RIGHTBRAK; break; default: goto invalid_token; } token_string[0] = tok_char; token_string[1] = '\0'; read_src_char(); return; invalid_token: error(0, "Invalid token"); token_string[0] = '\0'; token_type = TOK_INVALID; } /* skip to the next line */ void skip_eol(void) { line_buf_ptr = NULL; tok_char = ' '; } symtab.c100644 423 0 2357 6115222036 11124 0ustar luigiwheel/* * picasm -- symtab.c * * symbol table handling * */ #include #include #include "picasm.h" #define HASH_TABLE_SIZE 127 static struct symbol *symbol_table[HASH_TABLE_SIZE]; /* * Compute a hash value from a string */ static unsigned int hash(char *str) { unsigned int h, a; h = 0; while((a = *str++) != '\0') { h = 17*h + a; } return h % HASH_TABLE_SIZE; } /* * Initialize symbol table */ void init_symtab(void) { struct symbol **sym; int n; for(sym = symbol_table, n = HASH_TABLE_SIZE; n-- > 0; *sym++ = NULL) ; } /* * Add a new symbol to the symbol table */ struct symbol * add_symbol(char *name) { struct symbol *sym; int i; if((sym = mem_alloc(sizeof(struct symbol) + strlen(name))) == NULL) return NULL; i = hash(name); sym->next = symbol_table[i]; symbol_table[i] = sym; strcpy(sym->name, name); /* the caller must fill the value, type and flags fields */ return sym; } /* * Try to find a symbol from the symbol table */ struct symbol * lookup_symbol(char *name) { struct symbol *sym; int i; i = hash(name); for(sym = symbol_table[i]; sym != NULL; sym = sym->next) { if(strcmp(sym->name, name) == 0) return sym; } return NULL; } expr.c100644 423 0 17100 6125345760 10626 0ustar luigiwheel/* * picasm -- expr.c * * expression parser * */ #include #include #include "picasm.h" int expr_error; /* expression error flag */ /* * expression parser, bottom level */ static long expr_element(void) { long val, tval; struct symbol *sym; static unsigned char strbuf[256]; switch(token_type) { case TOK_LEFTBRAK: get_token(); tval = 0; while(!expr_error && token_type != TOK_RIGHTBRAK) { val = get_expression(); if(val < 0 || val >= EXPR_NBITS) { error(1, "bit number out of range"); expr_error = 1; } tval |= (1 << val); } if(!expr_error) get_token(); return tval; case TOK_LEFTPAR: get_token(); val = get_expression(); if(!expr_error && token_type != TOK_RIGHTPAR) { error(1, "')' expected"); expr_error = 1; } if(!expr_error) get_token(); return val; case KW_DEFINED: get_token(); if(token_type != TOK_LEFTPAR) { error(1, "'(' expected"); expr_error = 1; return EXPR_FALSE; } get_token(); if(token_type != TOK_IDENTIFIER) { error(1, "Symbol expected"); expr_error = 1; return EXPR_FALSE; } if((sym = lookup_symbol(token_string)) == NULL) val = EXPR_FALSE; else if(sym->type == SYM_DEFINED || sym->type == SYM_SET) val = EXPR_TRUE; else val = EXPR_FALSE; get_token(); if(token_type != TOK_RIGHTPAR) { error(1, "')' expected"); expr_error = 1; return val; } get_token(); return val; case KW_STREQ: /* streq(arg1, arg2), return TRUE if strings are identical */ get_token(); if(token_type != TOK_LEFTPAR) { error(1, "'(' expected"); expr_error = 1; return EXPR_FALSE; } get_token(); if(token_type != TOK_STRCONST) { error(1, "Quoted string expected"); expr_error = 1; return EXPR_FALSE; } strcpy(strbuf, token_string); get_token(); if(token_type != TOK_COMMA) { error(1, "',' expected"); expr_error = 1; return EXPR_FALSE; } get_token(); if(token_type != TOK_STRCONST) { error(1, "Quoted string expected"); expr_error = 1; return EXPR_FALSE; } val = (strcmp(token_string, strbuf) == 0 ? EXPR_TRUE : EXPR_FALSE); get_token(); if(token_type != TOK_RIGHTPAR) { error(1, "')' expected"); expr_error = 1; return val; } get_token(); return val; case KW_ISSTR: /* isstr(arg), return TRUE if argument is a quoted string */ get_token(); if(token_type != TOK_LEFTPAR) { error(1, "'(' expected"); expr_error = 1; return EXPR_FALSE; } get_token(); if(token_type == TOK_STRCONST) { val = EXPR_TRUE; get_token(); } else if(token_type == TOK_RIGHTPAR) { get_token(); /* empty parameter list */ return EXPR_FALSE; } else { val = EXPR_FALSE; do { get_token(); } while(token_type != TOK_EOF && token_type != TOK_NEWLINE && token_type != TOK_COMMA && token_type != TOK_RIGHTPAR); } if(token_type != TOK_RIGHTPAR) { error(1, "')' expected"); expr_error = 1; return val; } get_token(); return val; case KW_CHRVAL: /* chrval(string, pos), return ascii code of character in string */ get_token(); if(token_type != TOK_LEFTPAR) { error(1, "'(' expected"); expr_error = 1; return -1; } get_token(); if(token_type != TOK_STRCONST) { error(1, "Quoted string expected"); expr_error = 1; return -1; } strcpy(strbuf, token_string); get_token(); if(token_type != TOK_COMMA) { error(1, "',' expected"); expr_error = 1; return -1; } get_token(); val = get_expression(); if(val < 0 || val >= strlen(strbuf)) val = -1; else val = strbuf[val]; if(token_type != TOK_RIGHTPAR) { error(1, "')' expected"); expr_error = 1; return val; } get_token(); return val; case TOK_DOLLAR: /* current location */ case TOK_PERIOD: switch(O_Mode) { case O_PROGRAM: val = prog_location; break; case O_REGFILE: val = reg_location; break; case O_EDATA: val = edata_location; break; case O_NONE: default: val = org_val; break; } if(val < 0) { error(1, "ORG value not set"); expr_error = 1; } get_token(); return val; case TOK_INTCONST: val = token_int_val; get_token(); return val; case TOK_IDENTIFIER: if((sym = lookup_symbol(token_string)) == NULL) { error(1, "Undefined symbol '%s'", token_string); expr_error = 1; } else { if(sym->type == SYM_MACRO) { error(1, "Invalid usage of macro name '%s'", token_string); expr_error = 1; } else if(sym->type != SYM_DEFINED && sym->type != SYM_SET) { error(1, "Undefined symbol '%s'", token_string); expr_error = 1; } get_token(); return sym->v.value; } break; default: error(1, "Expression syntax error"); expr_error = 1; break; } return 0; } /* expression parser, unary minus and bit-not */ static long expr_unary1(void) { int op; long val; if(!expr_error && (token_type == TOK_MINUS || token_type == TOK_BITNOT)) { op = token_type; get_token(); val = expr_element(); if(op == TOK_MINUS) return -val; else return ~val; } else return expr_element(); } /* expression, multiplication, division, bit-and */ static long expr_mul_div(void) { int op; long val1, val2; val1 = expr_unary1(); while(!expr_error && ((op = token_type) == TOK_ASTERISK || op == TOK_SLASH || op == TOK_PERCENT || op == TOK_BITAND)) { get_token(); val2 = expr_unary1(); switch(op) { case TOK_ASTERISK: val1 *= val2; break; case TOK_SLASH: case TOK_PERCENT: if(val2 == 0) { error(1, "Division by zero"); expr_error = 1; } else { if(op == TOK_SLASH) val1 /= val2; else val1 %= val2; } break; case TOK_BITAND: val1 &= val2; break; } } return val1; } /* expression. add, subtract, bit-or */ static long expr_add_sub(void) { int op; long val1, val2; val1 = expr_mul_div(); while(!expr_error && ((op = token_type) == TOK_PLUS || op == TOK_MINUS || op == TOK_BITOR || op == TOK_BITXOR || op == TOK_LSHIFT || op == TOK_RSHIFT)) { get_token(); val2 = expr_mul_div(); switch(op) { case TOK_PLUS: val1 += val2; break; case TOK_MINUS: val1 -= val2; break; case TOK_BITOR: val1 |= val2; break; case TOK_BITXOR: val1 ^= val2; break; case TOK_LSHIFT: val1 <<= val2; break; case TOK_RSHIFT: val1 >>= val2; break; } } return val1; } /* * the main expression parser entry point * * handles only compare operators directly */ long get_expression(void) { int op; long val1, val2; expr_error = 0; val1 = expr_add_sub(); while(!expr_error && ((op = token_type) == TOK_EQUAL || op == TOK_EQ || op == TOK_NOT_EQ || op == TOK_LESS || op == TOK_GREATER || op == TOK_LESS_EQ || op == TOK_GT_EQ)) { get_token(); val2 = expr_add_sub(); switch(op) { case TOK_EQUAL: case TOK_EQ: val1 = -(val1 == val2); break; case TOK_NOT_EQ: val1 = -(val1 != val2); break; case TOK_LESS: val1 = -(val1 < val2); break; case TOK_LESS_EQ: val1 = -(val1 <= val2); break; case TOK_GREATER: val1 = -(val1 > val2); break; case TOK_GT_EQ: val1 = -(val1 >= val2); break; } } return val1; } pic12bit.c100644 423 0 12034 6117444470 11265 0ustar luigiwheel/* * picasm -- pic12bit.c */ #include #include "picasm.h" /* * Assemble 12-bit PIC code * */ int assemble_12bit_mnemonic(int op) { int t; long val; struct symbol *sym; char *cp; switch(op) { case KW_ANDLW: case KW_IORLW: case KW_XORLW: val = get_expression(); if(expr_error) return FAIL; if(val < -0x80 || val > 0xff) error(1, "8-bit literal out of range"); val &= 0xff; /* this assumes 2-complement negative numbers */ switch(op) { case KW_ANDLW: gen_code(0xe00 | val); break; case KW_IORLW: gen_code(0xd00 | val); break; case KW_XORLW: gen_code(0xf00 | val); break; } break; case KW_MOVLW: if(gen_byte_c(0xc00) != OK) return FAIL; break; case KW_ADDWF: case KW_SUBWF: case KW_ANDWF: case KW_IORWF: case KW_XORWF: case KW_COMF: case KW_DECF: case KW_INCF: case KW_MOVF: case KW_DECFSZ: case KW_INCFSZ: case KW_RLF: case KW_RRF: case KW_SWAPF: val = get_expression(); if(expr_error) return FAIL; if(val < 0 || val > 0x1f) error(0, "Register file address out of range"); t = 1; if(token_type == TOK_COMMA) { get_token(); t = get_expression(); if(expr_error) return FAIL; } else { if(warnmode) warning("Destination speficier omitted"); } val = (val & 0x1f) | (t != 0 ? 0x20 : 0); switch(op) { case KW_ADDWF: gen_code(0x1c0 | val); break; case KW_SUBWF: gen_code(0x080 | val); break; case KW_ANDWF: gen_code(0x140 | val); break; case KW_IORWF: gen_code(0x100 | val); break; case KW_XORWF: gen_code(0x180 | val); break; case KW_COMF: gen_code(0x240 | val); break; case KW_DECF: gen_code(0x0c0 | val); break; case KW_INCF: gen_code(0x280 | val); break; case KW_MOVF: gen_code(0x200 | val); break; case KW_DECFSZ: gen_code(0x2c0 | val); break; case KW_INCFSZ: gen_code(0x3c0 | val); break; case KW_RLF: gen_code(0x340 | val); break; case KW_RRF: gen_code(0x300 | val); break; case KW_SWAPF: gen_code(0x380 | val); break; } break; case KW_CLRF: case KW_MOVWF: val = get_expression(); if(expr_error) return FAIL; if(val < 0 || val > 0x1f) error(0, "Register file address out of range"); switch(op) { case KW_CLRF: gen_code(0x060 | val); break; case KW_MOVWF: gen_code(0x020 | val); break; } break; case KW_BCF: case KW_BSF: case KW_BTFSC: case KW_BTFSS: val = get_expression(); if(expr_error) return FAIL; if(val < 0 || val > 0x1f) error(0, "Register file address out of range"); if(token_type != TOK_COMMA) { error(1, "',' expected"); return FAIL; } get_token(); t = get_expression(); if(expr_error) return FAIL; if(t < 0 || t > 7) { error(0, "Bit number out of range"); } val |= (t << 5); switch(op) { case KW_BCF: gen_code(0x400 | val); break; case KW_BSF: gen_code(0x500 | val); break; case KW_BTFSC: gen_code(0x600 | val); break; case KW_BTFSS: gen_code(0x700 | val); break; } break; case KW_CALL: case KW_GOTO: t = 0; if(token_type == TOK_IDENTIFIER) { sym = lookup_symbol(token_string); if(sym == NULL || sym->type == SYM_FORWARD) { if(sym == NULL) { sym = add_symbol(token_string); sym->type = SYM_FORWARD; } val = 0; add_patch(sym, (op == KW_CALL ? PATCH8 : PATCH9)); t = 1; get_token(); goto gen_goto_call; } } val = get_expression(); if(expr_error) return FAIL; if(val < 0 || val >= prog_mem_size) error(0, "GOTO/CALL address out of range"); if(op == KW_CALL && (val & 0x100) != 0) error(0, "CALL address in upper half of a page"); gen_goto_call: switch(op) { case KW_CALL: gen_code(0x900 | (val & 0xff)); break; case KW_GOTO: gen_code(0xa00 | (val & 0x1ff)); break; } if(t) list_flags |= LIST_FORWARD; break; case KW_TRIS: t = get_expression(); if(expr_error) return FAIL; if(t != 5 && t != 6 && t != 7) error(0, "Invalid register address for TRIS"); gen_code(0x000 | t); break; /* * RETLW allows multiple parameters/strings, for generating lookup tables */ case KW_RETLW: for(;;) { if(token_type == TOK_STRCONST) { for(cp = token_string; *cp != '\0'; cp++) gen_code(0x800 | (int)((unsigned char)(*cp))); get_token(); } else { if(gen_byte_c(0x800) != OK) return FAIL; } if(token_type != TOK_COMMA) break; get_token(); } break; case KW_NOP: gen_code(0x000); break; case KW_CLRW: gen_code(0x040); break; case KW_OPTION: gen_code(0x002); break; case KW_SLEEP: gen_code(0x003); break; case KW_CLRWDT: gen_code(0x004); break; case KW_RETURN: case KW_RETFIE: case KW_ADDLW: case KW_SUBLW: error(1, "Unimplemented instruction for PIC%s", pic_type->name); return FAIL; default: error(1, "Syntax error"); return FAIL; } return OK; } pic14bit.c100644 423 0 12162 6131061037 11257 0ustar luigiwheel/* * picasm -- pic14bit.c */ #include #include "picasm.h" /* * Assemble 14-bit PIC code */ int assemble_14bit_mnemonic(int op) { int t; long val; struct symbol *sym; char *cp; switch(op) { case KW_SUBLW: case KW_ANDLW: case KW_IORLW: case KW_XORLW: val = get_expression(); if(expr_error) return FAIL; if(val < -0x80 || val > 0xff) error(0, "8-bit literal out of range - XXX"); val &= 0xff; /* this assumes 2-complement negative numbers */ switch(op) { case KW_SUBLW: gen_code(0x3c00 | val); break; case KW_ANDLW: gen_code(0x3900 | val); break; case KW_IORLW: gen_code(0x3800 | val); break; case KW_XORLW: gen_code(0x3a00 | val); break; } break; case KW_ADDLW: if(gen_byte_c(0x3e00) != OK) return FAIL; break; case KW_MOVLW: if(gen_byte_c(0x3000) != OK) return FAIL; break; case KW_ADDWF: case KW_SUBWF: case KW_ANDWF: case KW_IORWF: case KW_XORWF: case KW_COMF: case KW_DECF: case KW_INCF: case KW_MOVF: case KW_DECFSZ: case KW_INCFSZ: case KW_RLF: case KW_RRF: case KW_SWAPF: val = get_expression(); if(expr_error) return FAIL; if(val < 0 || val > 0x7f) error(0, "Register file address out of range"); t = 1; if(token_type == TOK_COMMA) { get_token(); t = get_expression(); if(expr_error) return FAIL; } else { if(warnmode) warning("Destination speficier omitted"); } val = (val & 0x7f) | (t != 0 ? 0x80 : 0); switch(op) { case KW_ADDWF: gen_code(0x0700 | val); break; case KW_SUBWF: gen_code(0x0200 | val); break; case KW_ANDWF: gen_code(0x0500 | val); break; case KW_IORWF: gen_code(0x0400 | val); break; case KW_XORWF: gen_code(0x0600 | val); break; case KW_COMF: gen_code(0x0900 | val); break; case KW_DECF: gen_code(0x0300 | val); break; case KW_INCF: gen_code(0x0a00 | val); break; case KW_MOVF: gen_code(0x0800 | val); break; case KW_DECFSZ: gen_code(0x0b00 | val); break; case KW_INCFSZ: gen_code(0x0f00 | val); break; case KW_RLF: gen_code(0x0d00 | val); break; case KW_RRF: gen_code(0x0c00 | val); break; case KW_SWAPF: gen_code(0x0e00 | val); break; } break; case KW_CLRF: case KW_MOVWF: val = get_expression(); if(expr_error) return FAIL; if(val < 0 || val > 0x7f) error(0, "Register file address out of range"); switch(op) { case KW_CLRF: gen_code(0x0180 | val); break; case KW_MOVWF: gen_code(0x0080 | val); break; } break; case KW_BCF: case KW_BSF: case KW_BTFSC: case KW_BTFSS: val = get_expression(); if(expr_error) return FAIL; if(val < 0 || val > 0x7f) error(0, "Register file address out of range"); if(token_type != TOK_COMMA) { error(1, "',' expected"); return FAIL; } get_token(); t = get_expression(); if(expr_error) return FAIL; if(t < 0 || t > 7) { error(0, "Bit number out of range"); } val |= (t << 7); switch(op) { case KW_BCF: gen_code(0x1000 | val); break; case KW_BSF: gen_code(0x1400 | val); break; case KW_BTFSC: gen_code(0x1800 | val); break; case KW_BTFSS: gen_code(0x1c00 | val); break; } break; case KW_CALL: case KW_GOTO: t = 0; if(token_type == TOK_IDENTIFIER) { sym = lookup_symbol(token_string); if(sym == NULL || sym->type == SYM_FORWARD) { if(sym == NULL) { sym = add_symbol(token_string); sym->type = SYM_FORWARD; } val = 0; add_patch(sym, PATCH11); t = 1; get_token(); goto gen_goto_call; } } val = get_expression(); if(expr_error) return FAIL; if(val < 0 || val >= prog_mem_size) error(0, "GOTO/CALL address out of range"); gen_goto_call: val &= 0x7ff; switch(op) { case KW_CALL: gen_code(0x2000 | val); break; case KW_GOTO: gen_code(0x2800 | val); break; } if(t) list_flags |= LIST_FORWARD; break; case KW_TRIS: t = get_expression(); if(expr_error) return FAIL; if(t != 5 && t != 6) error(0, "Invalid register address for TRIS"); gen_code(0x0060 | t); if(warnmode) warning("TRIS instruction not recommended"); break; /* * RETLW allows multiple parameters/strings, for generating lookup tables */ case KW_RETLW: for(;;) { if(token_type == TOK_STRCONST) { for(cp = token_string; *cp != '\0'; cp++) gen_code(0x3400 | (int)((unsigned char)(*cp))); get_token(); } else { if(gen_byte_c(0x3400) != OK) return FAIL; } if(token_type != TOK_COMMA) break; get_token(); } break; case KW_NOP: gen_code(0x0000); break; case KW_CLRW: gen_code(0x0100); break; case KW_OPTION: gen_code(0x0062); if(warnmode) warning("OPTION instruction not recommended"); break; case KW_SLEEP: gen_code(0x0063); break; case KW_CLRWDT: gen_code(0x0064); break; case KW_RETFIE: gen_code(0x0009); break; case KW_RETURN: gen_code(0x0008); break; default: error(1, "Syntax error"); return FAIL; } return OK; } picasm.h100644 423 0 11433 6125765675 11147 0ustar luigiwheel/* * picasm.h */ #define VERSION "v1.0" #if defined(__SASC) || defined(__TURBOC__) #define strcasecmp stricmp #endif /* output formats */ enum { IHX8M, IHX16 }; /* org mode */ enum { O_NONE, O_PROGRAM, O_REGFILE, O_EDATA }; #define INVALID_INSTR 0xffff #define INVALID_DATA 0xffff #define INVALID_CONFIG 0xffff #define INVALID_ID 0xffff /* list flags */ #define LIST_LOC 1 #define LIST_PROG 2 #define LIST_EDATA 4 #define LIST_FORWARD 8 #define LIST_VAL 0x10 #define LIST_PTR 0x20 /* * structure for include files/macros */ struct inc_file { struct inc_file *next; union { struct { FILE *fp; char *fname; } f; /* file */ struct { struct symbol *sym; struct macro_line *ml; struct macro_arg *args; int uniq_id; } m; /* macro */ } v; int type; int linenum; int cond_nest_count; }; /* inc_file types */ enum { INC_FILE, INC_MACRO }; /* * structure to hold one macro line */ struct macro_line { struct macro_line *next; char text[1]; }; /* Macro argument */ struct macro_arg { struct macro_arg *next; char text[1]; }; /* * structure for patching forward jumps */ struct patch { struct patch *next; struct symbol *label; int location; int type; }; enum { PATCH11, /* 14-bit instr. set PICs */ PATCH9, /* 12-bit, goto */ PATCH8 /* 12-bit, call */ }; #define PROGMEM_MAX 4096 #define DATAMEM_MAX 64 /* * Definitions for different types of PIC processors */ #define INSTRSET_MASK 3 #define PIC12BIT 1 #define PIC14BIT 2 #define PIC_ID 8 struct pic_type { char *name; int progmem_size; int datamem_size; int regfile_size; /* without banking */ short instr_flags; }; #define TOKSIZE 256 struct symbol { struct symbol *next; union { long value; struct macro_line *text; } v; char type; char name[1]; }; /* symbol types */ enum { SYM_MACRO, SYM_FORWARD, SYM_SET, SYM_DEFINED }; /* * token codes */ /**/ enum { TOK_INVALID, TOK_EOF, TOK_NEWLINE, TOK_COLON, TOK_PERIOD, TOK_DOLLAR, TOK_COMMA, TOK_LEFTPAR, TOK_RIGHTPAR, TOK_LEFTBRAK, TOK_RIGHTBRAK, TOK_EQUAL, TOK_EQ, TOK_NOT_EQ, TOK_LESS, TOK_LESS_EQ, TOK_GREATER, TOK_GT_EQ, TOK_PLUS, TOK_MINUS, TOK_ASTERISK, TOK_SLASH, TOK_PERCENT, TOK_BITAND, TOK_BITOR, TOK_BITXOR, TOK_BITNOT, TOK_LSHIFT, TOK_RSHIFT, TOK_BACKSLASH, TOK_IDENTIFIER, TOK_INTCONST, TOK_STRCONST, /* used as file name with include, and in EDATA */ KW_INCLUDE, KW_MACRO, KW_ENDM, KW_EXITM, KW_IF, KW_ELSE, KW_ENDIF, KW_EQU, KW_SET, KW_END, KW_ORG, KW_DS, KW_EDATA, KW_CONFIG, KW_PICID, KW_DEVICE, KW_DEFINED, KW_STREQ, KW_ISSTR, KW_CHRVAL, KW_OPT, KW_ADDLW, KW_ADDWF, KW_ANDLW, KW_ANDWF, KW_BCF, KW_BSF, KW_BTFSC, KW_BTFSS, KW_CALL, KW_CLRF, KW_CLRW, KW_CLRWDT, KW_COMF, KW_DECF, KW_DECFSZ, KW_GOTO, KW_INCF, KW_INCFSZ, KW_IORLW, KW_IORWF, KW_MOVLW, KW_MOVF, KW_MOVWF, KW_NOP, KW_OPTION, KW_RETFIE, KW_RETLW, KW_RETURN, KW_RLF, KW_RRF, KW_SLEEP, KW_SUBLW, KW_SUBWF, KW_SWAPF, KW_TRIS, KW_XORLW, KW_XORWF, KW_END_POS /* end marker */ }; #define FIRST_KW KW_INCLUDE #define NUM_KEYWORDS (KW_END_POS-FIRST_KW) /* * truth values for boolean functions */ #define EXPR_FALSE (0) #define EXPR_TRUE (~0) /* number of bits in an expression value */ #define EXPR_NBITS 32 /* * Success/failure return codes for functions */ #define OK (0) #define FAIL (-1) /* * variable declarations */ /* picasm.c */ extern struct inc_file *current_file; extern char *line_buf_ptr; extern char line_buffer[256]; extern int unique_id_count; extern int cond_nest_count; extern int O_Mode; extern int prog_location, reg_location, edata_location, org_val; extern int warnmode; extern struct patch *patch_list; extern int prog_mem_size; extern unsigned short list_flags; extern struct pic_type *pic_type; /* token.c */ extern int token_type, line_buf_flag; extern char token_string[TOKSIZE]; extern long token_int_val; extern int tok_char; /* expr.c */ extern int expr_error; /* * function prototypes */ /* picasm.c */ void *mem_alloc(int size); void fatal_error(char *, ...), error(int, char *, ...), warning(char *, ...); void write_listing_line(int cond_flag); void gen_code(int val); void add_patch(struct symbol *sym, int type); int gen_byte_c(int instr_code); /* token.c */ void get_token(void), skip_eol(void); void expand_macro(struct symbol *sym); void begin_include(char *fname), end_include(void); void read_src_char(void); /* symtab.c */ void init_symtab(void); struct symbol *add_symbol(char *name); struct symbol *lookup_symbol(char *name); /* expr.c */ long get_expression(void); /* pic12bit.c */ int assemble_12bit_mnemonic(int op); /* pic14bit.c */ int assemble_14bit_mnemonic(int op); picasm.doc100644 423 0 15601 6125765706 11461 0ustar luigiwheel Assembler for Microchip PIC16Cxx microcontrollers ------------------------------------------------- version 1.0 Copyright 1995-1996 by Timo Rossi Freely distributable for non-commercial use (basically, don't sell this program without my permission. You can use it for developing commercial PIC applications, but of course I am not responsible for any damages caused by this assembler generating bad code or anything like that) Suggestions and bug reports are welcome. Timo Rossi email: trossi@jyu.fi www: http://www.jyu.fi/~trossi/ Code is (mostly) ANSI-C, and should compile with most platforms (It does assume that negative integers are stored in two's complement representation, and that 'short' variables are at least 16-bit long. And it uses the strcasecmp() or stricmp() function) Command line usage: picasm [-o] [-l] [-ihx8m] [-ihx16] [-pic] Options: -o Define output file name. Default is .hex -l Enable listing. Default listing file name is .lst -w Give more warnings (omitted W/F-flags, tris/option instructions on 14-bit PICs) -ihx8m IHX8M output format (default). -ihx16 IHX16 output format. -pic select PIC device (16c54, 16c55, 16c56, 16c57, 16c64, 16c71, 16c74, 16c84) This is a single-pass assembler, forward gotos/calls are patched at the end of the assembly (only single labels are accepted in that case, otherwise expressions can be used too) In versions 0.99c and later forward references can also be used with movlw, addlw and retlw (only the low 8 bits of an address are used) Expressions can have the following elements: (from highest precedence to the lowest) integer constants symbols () . (or $) current location defined(symbol) -- TRUE (-1) if symbol is defined else FALSE (0) streq("str1","str2") -- TRUE if strings are identical. isstr(arg) -- TRUE is argument is a quoted string chrval("str", pos) -- Returns ASCII character code from the string. position range is from 0 to string length-1. If position is out of range, returns -1. [expr1 expr2 ... exprn] -- the same as (1<> shift right = == equal != <> not equal < less than <= =< less or equal than > greater than >= => greater or equal than The compare operators return TRUE (-1) or FALSE (0) (they are useful with conditional assembly) Expressions are evaluated as 32-bit integers (or whatever size 'long' is). hex numbers: 0x, h'' or h (must begin with 0..9) octal numbers: o, o'' binary numbers: 0b, b'' or b decimal numbers without prefix or d'' Directives: (square brackets denote optional parameters)