/* Implements directives, pseudo-ops and processor opcodes Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 James Bowman, Craig Franklin This file is part of gputils. gputils is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. gputils is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with gputils; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "stdhdr.h" #include "libgputils.h" #include "gpasm.h" #include "directive.h" #include "evaluate.h" #include "parse.h" #include "processor.h" #include "lst.h" #include "macro.h" #include "coff.h" #include "gperror.h" #include "special.h" /* Forward declarations */ void execute_body(struct macro_head *h); #define ATTRIB_COND 1 /************************************************************************/ /* create flags that control the behavior of the table macros like data, * dt,de, and dw */ #define DEFAULT_LIT_MASK 0xffff #define ENDIAN_SWAP (1<<31) #define PACKING_BYTES (1<<30) #define SPLIT_PACK (1<<29) struct sllist { struct pnode *p; struct sllist *next; }; extern struct pnode *mk_constant(int value); /************************************************************************/ int _16bit_core = 0; /* The 16bit core is handled differently in * some instances. */ int _17cxx_core = 0; /* 17cxx is different from 18cxx */ /************************************************************************/ /* Write a word into the memory image at the current location */ static void emit(unsigned int value) { /* only write the program data to memory on the second pass */ if (state.pass == 2) { if ((state.mode == relocatable) && (state.obj.section == NULL)) { gperror(GPE_WRONG_SECTION, NULL); } if (_17cxx_core && (state.org > 0xffff)) { gperror(GPE_ADDROVF, NULL); } if(value > state.device.core_size) { gpmessage(GPM_RANGE,NULL); value &= state.device.core_size; } if (i_memory_get(state.i_memory, state.org) & MEM_USED_MASK) { gperror(GPE_ADDROVR, NULL); } i_memory_put(state.i_memory, state.org, MEM_USED_MASK | value); } state.org++; } int endian_swap_word(int x) { return ( ( (x>>8) & 0xff) | ( (x<<8) & 0xff00)); } /* Write a word to memory unless we're packing successive bytes into a word * * The static variable 'packed_hi_lo' acts both as a flag and a shift amount. * when it's zero, the next byte to be emitted is packed into the low byte * of the word. When it is 8, the next byte is packed into high byte of the * word. This variable is used in conjunction with the data( ) routine. * * The static variable 'packed_byte' is the word into which two bytes are * packed. When two bytes are packed, then the word is emitted. */ int packed_hi_lo = 0; static int packed_byte = 0; static void emit_packed(unsigned int value, unsigned int mode) { if(value > 255) { gpwarning(GPW_RANGE,NULL); value &= 0xff; } packed_byte |= (value << packed_hi_lo); if(packed_hi_lo) { if(mode & ENDIAN_SWAP) packed_byte = endian_swap_word(packed_byte); emit( packed_byte); packed_hi_lo = packed_byte = 0; } else packed_hi_lo = 8; } static int off_or_on(struct pnode *p) { int had_error = 0, ret = 0; if (p->tag != symbol) had_error = 1; else if (strcasecmp(p->value.symbol, "OFF") == 0) ret = 0; else if (strcasecmp(p->value.symbol, "ON") == 0) ret = 1; else had_error = 1; if (had_error) gperror(GPE_EXPECTED, "Expected \"ON\" or \"OFF\"."); return ret; } static void data(struct sllist *L, int flavor, int lit_mask) { if (L) { struct sllist *list,*previous; int value; list = L->next; while(list) { value = reloc_evaluate(list->p, RELOCT_ALL); if(lit_mask & PACKING_BYTES) emit_packed(value | flavor, lit_mask); else { if((value > lit_mask) || (value < 0)) { gpwarning(GPW_RANGE,NULL); } if(lit_mask & SPLIT_PACK) { emit(value & 0xff); emit((value >> 8) & 0xff); } else { emit((value & lit_mask) | flavor); } } previous = list; list = list->next; free(previous); } } if(packed_hi_lo) emit_packed(flavor, lit_mask); } /* If we convert to glib, we can use the built in library call g_slist_append */ struct sllist *sllist_append(struct sllist *list, struct pnode *p) { struct sllist *new; new = malloc(sizeof(*new)); new->p = p; new->next = NULL; list->next = new; return(new); } /* convert an expression list which may consist of strings, constants, labels, * etc. into a singly linked list of integers. * This is called by the db,dw,dt,de, and data directives. * * pnode *L - A pointer to a doubly-linked list containing all of the * expressions. * sllist *list - A pointer to a singly-linked list into which we will place * the integer values of the expressions. * packing_strings - A flag indicating that char strings should be packed two to * a word. Note that this applies to only the da, dw, and data * directives. The db directive does the byte packing later. */ static void simplify_data(struct pnode *L, struct sllist *list, int packing_strings) { if (L) { struct pnode *p; unsigned int v = 0; unsigned int shift; int value; p = HEAD(L); if (p->tag == string) { char *pc = p->value.string; if(packing_strings == 2) { shift = 7; } else { shift = 8; } while (*pc) { pc = convert_escape_chars(pc, &value); if(packing_strings) { if(v>= (1<<31) ) { v = (v<= 1<<31 ) { v = v <mode = in_then; new->prev = state.astack; if (state.astack == NULL) new->prev_enabled = 1; else new->prev_enabled = state.astack->enabled && state.astack->prev_enabled; new->enabled = 0; /* Only the default */ state.astack = new; } /* Checking that a macro definition's parameters are correct */ static int macro_parms_simple(struct pnode *parms) { if (parms == NULL) { return 1; } else if (HEAD(parms)->tag != symbol) { gperror(GPE_ILLEGAL_ARGU, NULL); return 0; } else { return (macro_parms_simple(TAIL(parms))); } } static int list_symbol_member(struct pnode *M, struct pnode *L) { if (L == NULL) { return 0; } else if (STRCMP(M->value.symbol, HEAD(L)->value.symbol) == 0) { char buf[BUFSIZ]; snprintf(buf, sizeof(buf), "Duplicate macro parameter (%s).", HEAD(L)->value.symbol); gperror(GPE_UNKNOWN, buf); return 1; } else { return list_symbol_member(M, TAIL(L)); } } static int macro_parms_unique(struct pnode *parms) { if (parms == NULL) return 1; else return (!list_symbol_member(HEAD(parms), TAIL(parms)) && macro_parms_unique(TAIL(parms))); } static int macro_parms_ok(struct pnode *parms) { return (macro_parms_simple(parms) && macro_parms_unique(parms)); } typedef gpasmVal opfunc(gpasmVal r, char *name, int arity, struct pnode *parms); /************************************************************************/ static gpasmVal do_badram(gpasmVal r, char *name, int arity, struct pnode *parms) { struct pnode *p; state.lst.line.linetype = dir; for (; parms != NULL; parms = TAIL(parms)) { p = HEAD(parms); if ((p->tag == binop) && (p->value.binop.op == '-')) { int start, end; if (can_evaluate(p->value.binop.p0) && can_evaluate(p->value.binop.p1)) { start = evaluate(p->value.binop.p0); end = evaluate(p->value.binop.p1); if ((end < start) || (start < 0) || (MAX_RAM <= end)) { gpwarning(GPW_INVALID_RAM, NULL); } else { for (; start <= end; start++) state.badram[start] = 1; } } } else { if (can_evaluate(p)) { int loc; loc = evaluate(p); if ((loc < 0) || (MAX_RAM <= loc)) { gpwarning(GPW_INVALID_RAM, NULL); } else state.badram[loc] = 1; } } } return r; } static gpasmVal do_badrom(gpasmVal r, char *name, int arity, struct pnode *parms) { state.lst.line.linetype = dir; /* FIXME: implement this directive */ gpwarning(GPW_UNKNOWN, "gpasm doesn't support the badrom directive yet"); return r; } static void set_bankisel(int address) { if (state.device.class == PROC_CLASS_PIC14) { if (address < 0x100) { /* bcf 0x3, 0x7 */ emit(0x1383); } else { /* bsf 0x3, 0x7 */ emit(0x1783); } } else { /* movlb bank */ emit(0xb800 | gp_processor_check_bank(state.device.class, address)); } } static gpasmVal do_bankisel(gpasmVal r, char *name, int arity, struct pnode *parms) { struct pnode *p; int num_reloc; if ((state.device.class != PROC_CLASS_PIC14) && (state.device.class != PROC_CLASS_PIC16)) { gpmessage(GPM_EXTPAGE, NULL); return r; } if (enforce_arity(arity, 1)) { p = HEAD(parms); if (state.mode == absolute) { set_bankisel(maybe_evaluate(p)); } else { num_reloc = count_reloc(p); if (num_reloc == 0) { /* it is an absolute address, generate the bankisel but no relocation */ set_bankisel(maybe_evaluate(p)); } else if (num_reloc != 1) { gperror(GPE_UNRESOLVABLE, NULL); } else { reloc_evaluate(p, RELOCT_IBANKSEL); emit(0); } } } return r; } static gpasmVal do_banksel(gpasmVal r, char *name, int arity, struct pnode *parms) { struct pnode *p; int address; int bank; int num_reloc; if (enforce_arity(arity, 1)) { p = HEAD(parms); if (state.mode == absolute) { address = maybe_evaluate(p); bank = gp_processor_check_bank(state.device.class, address); state.org += gp_processor_set_bank(state.device.class, state.processor_info->num_banks, bank, state.i_memory, state.org); } else { num_reloc = count_reloc(p); if (num_reloc == 0) { /* it is an absolute address, generate the banksel but no relocation */ address = maybe_evaluate(p); bank = gp_processor_check_bank(state.device.class, address); state.org += gp_processor_set_bank(state.device.class, state.processor_info->num_banks, bank, state.i_memory, state.org); } else if (num_reloc != 1) { gperror(GPE_UNRESOLVABLE, NULL); } else if (state.device.class == PROC_CLASS_PIC16) { reloc_evaluate(p, RELOCT_BANKSEL); emit(0); } else if (state.device.class == PROC_CLASS_PIC16E) { reloc_evaluate(p, RELOCT_BANKSEL); emit(0); } else { switch (state.processor_info->num_banks) { case 2: reloc_evaluate(p, RELOCT_BANKSEL); emit(0); break; case 4: reloc_evaluate(p, RELOCT_BANKSEL); emit(0); emit(0); break; } } } } return r; } static gpasmVal do_code(gpasmVal r, char *name, int arity, struct pnode *parms) { struct pnode *p; state.lst.line.linetype = sec; state.next_state = state_section; if (state.mode == absolute) { gperror(GPE_OBJECT_ONLY, NULL); } else { switch (arity) { case 0: /* new relocatable section */ strncpy(state.obj.new_sec_name, ".code", sizeof(state.obj.new_sec_name)); state.obj.new_sec_addr = 0; state.obj.new_sec_flags = STYP_TEXT; break; case 1: /* new absolute section */ p = HEAD(parms); strncpy(state.obj.new_sec_name, ".code", sizeof(state.obj.new_sec_name)); state.obj.new_sec_addr = maybe_evaluate(p) >> _16bit_core; state.obj.new_sec_flags = STYP_TEXT | STYP_ABS; break; default: enforce_arity(arity, 1); } } return r; } static gpasmVal do_constant(gpasmVal r, char *name, int arity, struct pnode *parms) { struct pnode *p; int value; state.lst.line.linetype = dir; for (; parms; parms = TAIL(parms)) { p = HEAD(parms); if ((p->tag == binop) && (p->value.binop.op == '=')) { if (enforce_simple(p->value.binop.p0)) { char *lhs; /* fetch the symbol */ lhs = p->value.binop.p0->value.symbol; /* constants must be assigned a value at declaration */ value = maybe_evaluate(p->value.binop.p1); /* put the symbol and value in the table*/ set_global(lhs, value, PERMANENT, gvt_constant); } } else { gperror(GPE_ILLEGAL_ARGU, NULL); } } return r; } /*------------------------------------------------------------------------- * * configuration memory * * In addition to memory for storing instructions, each pic has memory for * storing configuration data (e.g. code protection, wdt enable, etc.). Each * family of the pic microcontrollers treats this memory slightly different. * * do_config( ) - Called by the parser when a __CONFIG assembler directive * is encountered. * */ static gpasmVal do_config(gpasmVal r, char *name, int arity, struct pnode *parms) { struct pnode *p; int ca; int value; state.lst.line.linetype = config; switch(arity) { case 1: if(_16bit_core) { gpwarning(GPW_EXPECTED,"18cxxx devices should specify __CONFIG address"); ca = CONFIG1L; } else { ca = state.device.config_address; } p = HEAD(parms); break; case 2: ca = maybe_evaluate(HEAD(parms)); p = HEAD(TAIL(parms)); break; default: enforce_arity(arity,2); return r; } state.lst.config_address = ca; if (state.mode == relocatable) { if ((!state.found_devid) && ((ca == DEVID1) || (ca == DEVID2))) { coff_new_section(".devid", ca >> _16bit_core, STYP_ABS | STYP_TEXT); state.found_devid = true; } else if (!state.found_config) { coff_new_section(".config", ca >> _16bit_core, STYP_ABS | STYP_TEXT); state.found_config = true; } } if ((can_evaluate(p)) && (state.pass == 2)) { value = evaluate(p); if(_16bit_core) { int curval = i_memory_get(state.c_memory, ca>>1); int mask = 0xff <<((ca&1) ? 0 : 8); if(value > 0xff) { gpwarning(GPW_RANGE,0); } /* If the config address is even, then this byte goes in LSB position */ value = (value & 0xff) << ((ca&1) ? 8 : 0) | MEM_USED_MASK; if(curval & MEM_USED_MASK) curval &= mask; else curval |= mask; i_memory_put(state.c_memory, ca>>1, curval | value); } else { if(value > state.device.core_size) { gpmessage(GPM_RANGE,NULL); value &= state.device.core_size; } if (i_memory_get(state.c_memory, ca) & MEM_USED_MASK) { gperror(GPE_ADDROVR, NULL); } i_memory_put(state.c_memory, ca, MEM_USED_MASK | value); /* FIXME: need line_number? this one will be wrong coff_linenum(1) */ } /* force the section to end */ state.obj.section = NULL; } return r; } /*------------------------------------------------------------------------- * do_da - The 'da' directive. Generates a number representing two * 7 bit ascii characters. It can be used in place of the DATA * directive for 14 bit cores to pack two characters into one * word. */ static gpasmVal do_da(gpasmVal r, char *name, int arity, struct pnode *parms) { struct sllist list; list.next = NULL; if(_16bit_core || state.device.core_size == CORE_12BIT_MASK) { simplify_data(parms, &list, 1); } else { simplify_data(parms, &list, 2); } data(&list, 0, DEFAULT_LIT_MASK); return r; } /*------------------------------------------------------------------------- * do_data - The 'data' directive. Initialize one or more words of program * memory with data. On all families except the pic18cxxx, the * first character is in the most significant byte of the word. */ static gpasmVal do_data(gpasmVal r, char *name, int arity, struct pnode *parms) { struct sllist list; list.next = NULL; simplify_data(parms, &list, 1); if ((state.mode == relocatable) && !(SECTION_FLAGS & STYP_TEXT)) { /* This is a data memory not program */ state.lst.line.linetype = res; /* data memory is byte sized so split the data */ data(&list, 0, SPLIT_PACK); } else { data(&list, 0, DEFAULT_LIT_MASK); } return r; } /*------------------------------------------------------------------------- * do_db - Reserve program memory words with packed 8-bit values. On the * 18cxxx families, dw and db are the same. For the 12 and 14 bit * cores, the upper bits are masked (e.g. the 14-bit core can only * store 14bits at a given program memory address, so the upper 2 * in a db directive are meaningless. */ static gpasmVal do_db(gpasmVal r, char *name, int arity, struct pnode *parms) { struct sllist list; list.next = NULL; simplify_data(parms, &list,0); if ((state.mode == relocatable) && !(SECTION_FLAGS & STYP_TEXT)) { /* This is a data memory not program */ state.lst.line.linetype = res; /* only valid in initialized data sections */ if (SECTION_FLAGS & STYP_BSS) gperror(GPE_WRONG_SECTION, NULL); data(&list, 0, 0xff); } else { if(_16bit_core) { data(&list, 0, PACKING_BYTES); } else { data(&list, 0, ENDIAN_SWAP | PACKING_BYTES); } } return r; } static gpasmVal do_de(gpasmVal r, char *name, int arity, struct pnode *parms) { struct sllist list; list.next = NULL; simplify_data(parms, &list,0); if(_16bit_core) { data(&list, 0, PACKING_BYTES); } else { data(&list, 0, 0xff); } return r; } static gpasmVal do_def(gpasmVal r, char *name, int arity, struct pnode *parms) { struct pnode *p; char *symbol_name = NULL; gp_symbol_type *coff_symbol = NULL; int shift = 0; int eval; int value = 0; gp_boolean new_class = false; int coff_class = C_NULL; gp_boolean new_type = false; int coff_type = T_NULL; enum gpasmValTypes type = gvt_debug; state.lst.line.linetype = dir; if (state.mode == absolute) { gperror(GPE_OBJECT_ONLY, NULL); } else { if (arity < 2) { enforce_arity(arity, 2); return r; } /* the first argument is the symbol name */ p = HEAD(parms); if (enforce_simple(p)) { symbol_name = p->value.symbol; } else { return r; } parms = TAIL(parms); if ((SECTION_FLAGS & STYP_TEXT) && _16bit_core) { shift = 1; } /* update the properties */ for (; parms; parms = TAIL(parms)) { p = HEAD(parms); if ((p->tag == binop) && (p->value.binop.op == '=')) { if (enforce_simple(p->value.binop.p0)) { char *lhs; lhs = p->value.binop.p0->value.symbol; if (strcasecmp(lhs, "value") == 0) { value = maybe_evaluate(p->value.binop.p1); } else if (strcasecmp(lhs, "size") == 0) { state.org += (maybe_evaluate(p->value.binop.p1) >> shift); } else if (strcasecmp(lhs, "type") == 0) { eval = maybe_evaluate(p->value.binop.p1); if ((eval < 0) || (eval > 0xffff)) { gperror(GPE_RANGE, NULL); } else { new_type = true; coff_type = eval; } } else if (strcasecmp(lhs, "class") == 0) { eval = maybe_evaluate(p->value.binop.p1); if ((eval < -128) || (eval > 127)) { gperror(GPE_RANGE, NULL); } else { new_class = true; coff_class = eval; } } else { gperror(GPE_ILLEGAL_ARGU, NULL); } } } else { if (enforce_simple(p)) { if (strcasecmp(p->value.symbol, "absolute") == 0) { type = gvt_absolute; value = 0; } else if (strcasecmp(p->value.symbol, "debug") == 0) { type = gvt_debug; value = 0; } else if (strcasecmp(p->value.symbol, "extern") == 0) { type = gvt_extern; value = 0; } else if (strcasecmp(p->value.symbol, "global") == 0) { type = gvt_global; value = state.org << shift; } else if (strcasecmp(p->value.symbol, "static") == 0) { type = gvt_static; value = state.org << shift; } else { gperror(GPE_ILLEGAL_ARGU, NULL); } } } } } set_global(symbol_name, value, PERMANENT, type); /* update the symbol with the values */ if ((state.pass == 2) && (new_class || new_type)) { coff_symbol = gp_coffgen_findsymbol(state.obj.object, symbol_name); assert(coff_symbol != NULL); if (new_class) coff_symbol->class = coff_class; if (new_type) coff_symbol->type = coff_type; } return r; } static gpasmVal do_dim(gpasmVal r, char *name, int arity, struct pnode *parms) { struct pnode *p; char *symbol_name = NULL; gp_symbol_type *coff_symbol = NULL; int number_symbols; gp_aux_type *aux_list; struct sllist first_list; struct sllist *list = &first_list; struct sllist *previous; int i; int value; state.lst.line.linetype = dir; if (state.mode == absolute) { gperror(GPE_OBJECT_ONLY, NULL); } else { if (arity < 3) { enforce_arity(arity, 1); return r; } /* the first argument is the symbol name */ p = HEAD(parms); if (enforce_simple(p)) { /* lookup the symbol */ symbol_name = p->value.symbol; coff_symbol = gp_coffgen_findsymbol(state.obj.object, symbol_name); if (coff_symbol == NULL) { gperror(GPE_NOSYM, NULL); return r; } } else { return r; } parms = TAIL(parms); /* the second argument must be the number of aux symbols */ p = HEAD(parms); number_symbols = maybe_evaluate(p); if ((number_symbols < 0) || (number_symbols > 127)) { gperror(GPE_UNKNOWN, "number of auxiliary symbols must be less then 128 and positive"); return r; } state.obj.symbol_num += number_symbols; parms = TAIL(parms); /* create the symbols */ aux_list = gp_coffgen_blockaux(number_symbols); coff_symbol->num_auxsym = number_symbols; coff_symbol->aux_list = aux_list; /* convert the arguments into a list of values */ list->next = NULL; simplify_data(parms, list, 0); /* write the data to the auxiliary symbols */ list = list->next; i = 0; while(list) { value = maybe_evaluate(list->p); if (value & (~0xff)) { gperror(GPE_RANGE, NULL); return r; } if (aux_list == NULL) { gperror(GPE_UNKNOWN, "insufficent number of auxiliary symbols"); return r; } if (i == SYMBOL_SIZE) { i = 0; aux_list = aux_list->next; } else { aux_list->_aux_symbol.data[i++] = value; } previous = list; list = list->next; free(previous); } } return r; } static gpasmVal do_direct(gpasmVal r, char *name, int arity, struct pnode *parms) { struct pnode *p; int value; unsigned char direct_command = 0; char *direct_string = NULL; state.lst.line.linetype = dir; if (state.mode == absolute) { gperror(GPE_OBJECT_ONLY, NULL); } else if (enforce_arity(arity, 2)) { p = HEAD(parms); value = maybe_evaluate(p); if ((value < 0) || (value > 255)) { gperror(GPE_RANGE, NULL); } else { direct_command = value; } p= HEAD(TAIL(parms)); if (p->tag == string) { if (strlen(p->value.string) < 255) { direct_string = convert_escaped_char(p->value.string,'"'); } else { gperror(GPE_UNKNOWN, "string must be less than 255 bytes long"); } } else { gperror(GPE_ILLEGAL_ARGU, NULL); } if (direct_string == NULL) { return r; } if (SECTION_FLAGS & STYP_TEXT) { coff_add_directsym(direct_command, direct_string); } else { gperror(GPE_WRONG_SECTION, NULL); } } return r; } static gpasmVal do_dt(gpasmVal r, char *name, int arity, struct pnode *parms) { struct symbol *s; struct insn *i; struct sllist list; s = get_symbol(state.stBuiltin, "RETLW"); assert(s != NULL); /* Every PIC has a RETLW instruction */ i = get_symbol_annotation(s); list.next = NULL; simplify_data(parms, &list, 0); data(&list, i->opcode, 0xff); return r; } /*------------------------------------------------------------------------- * do_dw - The 'dw' directive. On all families except for the p18cxxx, the * dw directive is the same as the 'data' directive. For the p18cxxx * it's the same as the 'db' directive. (That's strange, but it's * also the way mpasm does it). */ static gpasmVal do_dw(gpasmVal r, char *name, int arity, struct pnode *parms) { struct sllist list; list.next = NULL; simplify_data(parms, &list, 1); if ((state.mode == relocatable) && !(SECTION_FLAGS & STYP_TEXT)) { /* This is a data memory not program */ state.lst.line.linetype = res; /* only valid in initialized data sections */ if (SECTION_FLAGS & STYP_BSS) gperror(GPE_WRONG_SECTION, NULL); /* data memory is byte sized so split the data */ data(&list, 0, SPLIT_PACK); } else { data(&list, 0, DEFAULT_LIT_MASK); } return r; } static gpasmVal do_else(gpasmVal r, char *name, int arity, struct pnode *parms) { state.lst.line.linetype = dir; if (state.astack == NULL) gperror(GPE_ILLEGAL_COND, NULL); else if ((state.astack->mode != in_then)) gperror(GPE_ILLEGAL_COND, NULL); else state.astack->enabled = !state.astack->enabled; return r; } static gpasmVal do_endif(gpasmVal r, char *name, int arity, struct pnode *parms) { state.lst.line.linetype = dir; if (state.astack == NULL) { gperror(GPE_ILLEGAL_COND, "Illegal condition (ENDIF)."); } else if ((state.astack->mode != in_then) && (state.astack->mode != in_else)) { gperror(GPE_ILLEGAL_COND, "Illegal condition (ENDIF)."); } else { struct amode *old; old = state.astack; state.astack = state.astack->prev; free(old); } return r; } static gpasmVal do_endm(gpasmVal r, char *name, int arity, struct pnode *parms) { assert(!state.mac_head); state.lst.line.linetype = dir; if (state.mac_prev == NULL) gperror(GPE_UNMATCHED_ENDM, NULL); else state.mac_prev = NULL; state.mac_body = NULL; return r; } static gpasmVal do_endw(gpasmVal r, char *name, int arity, struct pnode *parms) { state.lst.line.linetype = dir; assert(!state.mac_head); if (state.mac_prev == NULL) { gperror(GPE_ILLEGAL_COND, "Illegal condition (ENDW)."); } else if (maybe_evaluate(state.while_head->parms)) { state.next_state = state_while; state.next_buffer.macro = state.while_head; } state.mac_body = NULL; state.mac_prev = NULL; state.while_head = NULL; return r; } static gpasmVal do_eof(gpasmVal r, char *name, int arity, struct pnode *parms) { state.lst.line.linetype = dir; if (state.mode == absolute) { gperror(GPE_OBJECT_ONLY, NULL); } else if (enforce_arity(arity, 0)) { if (state.debug_info) { coff_add_eofsym(); } else { gpwarning(GPW_UNKNOWN, "directive ignored when debug info is disabled"); } } return r; } static gpasmVal do_equ(gpasmVal r, char *name, int arity, struct pnode *parms) { struct pnode *p; state.lst.line.linetype = equ; if (enforce_arity(arity, 1)) { p = HEAD(parms); if (can_evaluate(p)) r = evaluate(p); } return r; } static gpasmVal do_error(gpasmVal r, char *name, int arity, struct pnode *parms) { struct pnode *p; state.lst.line.linetype = dir; if (enforce_arity(arity, 1)) { p = HEAD(parms); if (p->tag == string) { gperror(GPE_USER, p->value.string); } else { gperror(GPE_ILLEGAL_ARGU, NULL); } } return r; } /************************************************************************ * do_errlvl - parse the ERRORLEVEL directive * * If the file gpasm is assembling contains a ERRORLEVEL directive, then scan * the comma delimited list of options in *parms * * Inputs: * gpasmVal r - not used, but is returned * char *name - not used, but contains the directive name 'list'. * int arity - not used, but should contain '1' * struct pnode *parms - a linked list of the parsed parameters * * * ************************************************************************/ static gpasmVal do_errlvl(gpasmVal r, char *name, int arity, struct pnode *parms) { struct pnode *p; state.lst.line.linetype = dir; if (state.pass == 2) { for (; parms; parms = TAIL(parms)) { p = HEAD(parms); if (p->tag == unop) { gpasmVal value = evaluate(p->value.unop.p0); if (p->value.unop.op == '-') { add_code(-value); } else if (p->value.unop.op == '+') { add_code(value); } else { gperror(GPE_ILLEGAL_ARGU, "Expected 0, 1, 2, +|-"); } } else if (p->tag == constant) { select_errorlevel(p->value.constant); } else { gperror(GPE_ILLEGAL_ARGU, "Expected 0, 1, 2, +|-"); } } } return r; } static gpasmVal do_exitm(gpasmVal r, char *name, int arity, struct pnode *parms) { state.lst.line.linetype = dir; if (enforce_arity(arity, 0)) { if (state.stGlobal == state.stTop) { gperror(GPE_UNKNOWN, "Attempt to use \"exitm\" outside of macro"); } else { state.next_state = state_exitmacro; } } return r; } static gpasmVal do_expand(gpasmVal r, char *name, int arity, struct pnode *parms) { state.lst.line.linetype = dir; if (state.cmd_line.macro_expand) { gpmessage(GPM_SUPLIN, NULL); } else { if (enforce_arity(arity, 0)) { state.lst.expand = true; } } return r; } static gpasmVal do_extern(gpasmVal r, char *name, int arity, struct pnode *parms) { char *p; state.lst.line.linetype = dir; if (state.mode == absolute) { gperror(GPE_OBJECT_ONLY, NULL); } else { for (; parms; parms = TAIL(parms)) { p = maybe_evaluate_concat(HEAD(parms)); if (p) { set_global(p, 0, PERMANENT, gvt_extern); } } } return r; } static gpasmVal do_file(gpasmVal r, char *name, int arity, struct pnode *parms) { struct pnode *p; state.lst.line.linetype = dir; if (state.mode == absolute) { gperror(GPE_OBJECT_ONLY, NULL); } else if (enforce_arity(arity, 1)) { if (state.debug_info) { p = HEAD(parms); if (p->tag == string) { state.obj.debug_file = coff_add_filesym(p->value.string, 0); } else { gperror(GPE_ILLEGAL_ARGU, NULL); } } else { gpwarning(GPW_UNKNOWN, "directive ignored when debug info is disabled"); } } return r; } /* Filling constants is handled here. Filling instructions is handled in the parser. */ static gpasmVal do_fill(gpasmVal r, char *name, int arity, struct pnode *parms) { struct pnode *h; int number; int i; if (enforce_arity(arity, 2)) { h = HEAD(parms); number = eval_fill_number(HEAD(TAIL(parms))); for (i = 1; i <= number ; i += 1) { /* we must evaluate each loop, because some symbols change (i.e. $) */ emit(maybe_evaluate(h)); } } return r; } static gpasmVal do_global(gpasmVal r, char *name, int arity, struct pnode *parms) { char *p; char buf[BUFSIZ]; struct symbol *s; struct variable *var; state.lst.line.linetype = dir; if (state.mode == absolute) { gperror(GPE_OBJECT_ONLY, NULL); } else { for (; parms; parms = TAIL(parms)) { p = maybe_evaluate_concat(HEAD(parms)); if (p) { s = get_symbol(state.stTop, p); if (s == NULL) { snprintf(buf, sizeof(buf), "Symbol not previously defined (%s).", p); gperror(GPE_NOSYM, buf); } else { var = get_symbol_annotation(s); if (var == NULL) { snprintf(buf, sizeof(buf), "Symbol not assigned a value (%s).", p); gpwarning(GPW_UNKNOWN, buf); } else { if ((var->previous_type == gvt_address) || (var->previous_type == gvt_global) || (var->previous_type == gvt_static)) { /* make the symbol global */ var->type = gvt_global; } else if (var->previous_type == gvt_extern) { gperror(GPE_DUPLAB, NULL); } else { snprintf(buf, sizeof(buf), "Operand must be an address label (%s).", p); gperror(GPE_MUST_BE_LABEL, buf); } } } } else { gperror(GPE_ILLEGAL_ARGU, NULL); } } } return r; } static gpasmVal do_idata(gpasmVal r, char *name, int arity, struct pnode *parms) { struct pnode *p; state.lst.line.linetype = sec; state.next_state = state_section; if (state.mode == absolute) { gperror(GPE_OBJECT_ONLY, NULL); } else { switch (arity) { case 0: /* new relocatable section */ strncpy(state.obj.new_sec_name, ".idata", sizeof(state.obj.new_sec_name)); state.obj.new_sec_addr = 0; state.obj.new_sec_flags = STYP_DATA; break; case 1: /* new absolute section */ p = HEAD(parms); strncpy(state.obj.new_sec_name, ".idata", sizeof(state.obj.new_sec_name)); state.obj.new_sec_addr = maybe_evaluate(p) >> _16bit_core; state.obj.new_sec_flags = STYP_DATA | STYP_ABS; break; default: enforce_arity(arity, 1); } } return r; } static gpasmVal do_ident(gpasmVal r, char *name, int arity, struct pnode *parms) { struct pnode *p; state.lst.line.linetype = dir; if (state.mode == absolute) { gperror(GPE_OBJECT_ONLY, NULL); } else if (enforce_arity(arity, 1)) { p = HEAD(parms); if (p->tag == string) { coff_add_identsym(p->value.string); } else { gperror(GPE_ILLEGAL_ARGU, NULL); } } return r; } static gpasmVal do_idlocs(gpasmVal r, char *name, int arity, struct pnode *parms) { int value; int idreg; int curvalue; int mask; if (_17cxx_core) { gperror(GPE_ILLEGAL_DIR, NULL); return r; } if (_16bit_core) { if (enforce_arity(arity,2)) { idreg = maybe_evaluate(HEAD(parms)); value = maybe_evaluate(HEAD(TAIL(parms))); } else { gperror(GPW_EXPECTED,"18cxxx devices should specify __IDLOC address"); return r; } } else { if (enforce_arity(arity,1)) { idreg = state.device.id_location; value = maybe_evaluate(HEAD(parms)); } else { return r; } } if ((state.mode == relocatable) && (!state.found_idlocs)) { coff_new_section(".idlocs", idreg >> _16bit_core, STYP_ABS | STYP_TEXT); state.found_idlocs = true; } state.lst.config_address = idreg; state.device.id_location = idreg; if (state.pass == 2) { if (_16bit_core) { state.lst.line.linetype = config; if (idreg > IDLOC7 || idreg < IDLOC0) { gperror(GPE_RANGE,NULL); } else { if(value > 0xff) { gpwarning(GPW_RANGE, NULL); } curvalue = i_memory_get(state.c_memory, idreg>>1); mask = 0xff <<((idreg&1) ? 0 : 8); /* If the address is even, then this byte goes in LSB position */ value = (value & 0xff) << ((idreg&1) ? 8 : 0) | MEM_USED_MASK; if(curvalue & MEM_USED_MASK) curvalue &= mask; else curvalue |= mask; i_memory_put(state.c_memory, idreg>>1, curvalue | value); } } else { state.lst.line.linetype = idlocs; if (value > 0xffff) { gpmessage(GPM_IDLOC, NULL); value &= 0xffff; } if (i_memory_get(state.c_memory, idreg) & MEM_USED_MASK) { gperror(GPE_ADDROVR, NULL); } i_memory_put(state.c_memory, idreg, ((value & 0xf000) >> 12) | MEM_USED_MASK); i_memory_put(state.c_memory, idreg + 1, ((value & 0x0f00) >> 8) | MEM_USED_MASK); i_memory_put(state.c_memory, idreg + 2, ((value & 0x00f0) >> 4) | MEM_USED_MASK); i_memory_put(state.c_memory, idreg + 3, (value & 0x000f) | MEM_USED_MASK); } } return r; } static gpasmVal do_if(gpasmVal r, char *name, int arity, struct pnode *parms) { struct pnode *p; state.lst.line.linetype = dir; enter_if(); /* Only evaluate the conditional if it matters... */ if (state.astack->prev_enabled) { if (enforce_arity(arity, 1)) { p = HEAD(parms); state.astack->enabled = maybe_evaluate(p); } } return r; } static gpasmVal do_ifdef(gpasmVal r, char *name, int arity, struct pnode *parms) { struct pnode *p; state.lst.line.linetype = dir; enter_if(); /* Only evaluate the conditional if it matters... */ if (state.astack->prev_enabled) { if (enforce_arity(arity, 1)) { p = HEAD(parms); if (p->tag != symbol) { gperror(GPE_ILLEGAL_LABEL, NULL); } else { if ((get_symbol(state.stDefines, p->value.symbol)) || (get_symbol(state.stTop, p->value.symbol))) state.astack->enabled = 1; } } } return r; } static gpasmVal do_ifndef(gpasmVal r, char *name, int arity, struct pnode *parms) { struct pnode *p; state.lst.line.linetype = dir; enter_if(); /* Only evaluate the conditional if it matters... */ if (state.astack->prev_enabled) { if (enforce_arity(arity, 1)) { p = HEAD(parms); if (p->tag != symbol) { gperror(GPE_ILLEGAL_LABEL, NULL); } else { if ((!get_symbol(state.stDefines, p->value.symbol)) && (!get_symbol(state.stTop, p->value.symbol))) state.astack->enabled = 1; } } } return r; } static gpasmVal do_include(gpasmVal r, char *name, int arity, struct pnode *parms) { struct pnode *p; state.lst.line.linetype = dir; if (enforce_arity(arity, 1)) { p = HEAD(parms); if (p->tag == string) { state.next_state = state_include; state.next_buffer.file = strdup(p->value.string); } else { gperror(GPE_ILLEGAL_ARGU, NULL); } } return r; } static gpasmVal do_line(gpasmVal r, char *name, int arity, struct pnode *parms) { struct pnode *p; state.lst.line.linetype = dir; if (state.mode == absolute) { gperror(GPE_OBJECT_ONLY, NULL); } else if (enforce_arity(arity, 1)) { if (state.debug_info) { p = HEAD(parms); state.obj.debug_line = maybe_evaluate(p); } else { gpwarning(GPW_UNKNOWN, "directive ignored when debug info is disabled"); } } return r; } /************************************************************************ * do_list - parse the LIST directive * * If the file gpasm is assembling contains a LIST directive, then scan * and parse will call do_list and pass the comma delimited list of LIST * options in *parms * * Inputs: * gpasmVal r - not used, but is returned * char *name - not used, but contains the directive name 'list'. * int arity - not used, but should contain '1' * struct pnode *parms - a linked list of the parsed parameters * * * ************************************************************************/ static gpasmVal do_list(gpasmVal r, char *name, int arity, struct pnode *parms) { struct pnode *p; state.lst.enabled = true; state.lst.line.linetype = dir; for (; parms; parms = TAIL(parms)) { p = HEAD(parms); if ((p->tag == binop) && (p->value.binop.op == '=')) { if (enforce_simple(p->value.binop.p0)) { char *lhs; lhs = p->value.binop.p0->value.symbol; if (strcasecmp(lhs, "b") == 0) { int b; b = maybe_evaluate(p->value.binop.p1); if (b != 0) state.lst.tabstop = b; } else if (strcasecmp(lhs, "c") == 0) { ; /* Ignore this for now: column width not used */ } else if (strcasecmp(lhs, "f") == 0) { if (enforce_simple(p->value.binop.p1)) select_hexformat(p->value.binop.p1->value.symbol); } else if (strcasecmp(lhs, "l") == 0) { ; /* Ignore this for now: page length */ } else if (strcasecmp(lhs, "mm") == 0) { state.lst.memorymap = off_or_on(p->value.binop.p1); } else if (strcasecmp(lhs, "n") == 0) { if (can_evaluate(p->value.binop.p1)) { int number = evaluate(p->value.binop.p1); if ((number > 6) || (number == 0)) { state.lst.linesperpage = number; } else { gperror(GPE_RANGE, NULL); } } } else if (strcasecmp(lhs, "p") == 0) { if (enforce_simple(p->value.binop.p1)) select_processor(p->value.binop.p1->value.symbol); } else if (strcasecmp(lhs, "pe") == 0) { state.extended_pic16e = true; if (enforce_simple(p->value.binop.p1)) select_processor(p->value.binop.p1->value.symbol); } else if (strcasecmp(lhs, "r") == 0) { if (enforce_simple(p->value.binop.p1)) select_radix(p->value.binop.p1->value.symbol); } else if (strcasecmp(lhs, "st") == 0) { state.lst.symboltable = off_or_on(p->value.binop.p1); } else if (strcasecmp(lhs, "t") == 0) { ; /* Ignore this for now: always wrap long list lines */ } else if (strcasecmp(lhs, "w") == 0) { select_errorlevel(maybe_evaluate(p->value.binop.p1)); } else if (strcasecmp(lhs, "x") == 0) { if (enforce_simple(p->value.binop.p1)) select_expand(p->value.binop.p1->value.symbol); } else { gperror(GPE_ILLEGAL_ARGU, NULL); } } } else { if (enforce_simple(p)) { if (strcasecmp(p->value.symbol, "free") == 0) { ; /* Ignore this directive */ } else if (strcasecmp(p->value.symbol, "fixed") == 0) { ; /* Ignore this directive */ } else if (strcasecmp(p->value.symbol, "nowrap") == 0) { ; /* Ignore this directive */ } else if (strcasecmp(p->value.symbol, "wrap") == 0) { ; /* Ignore this directive */ } else { gperror(GPE_ILLEGAL_ARGU, NULL); } } } } /* The .list symbol is only added to the COFF file if its only action is to turn on the listing */ if (arity == 0) coff_add_listsym(); return r; } static gpasmVal do_local(gpasmVal r, char *name, int arity, struct pnode *parms) { struct pnode *p; state.lst.line.linetype = dir; /* like variable except it is put in TOP instead of GLOBAL */ if (state.stGlobal == state.stTop) { gperror(GPE_UNKNOWN, "Attempt to use \"local\" outside of macro"); } else { for (; parms; parms = TAIL(parms)) { p = HEAD(parms); if ((p->tag == binop) && (p->value.binop.op == '=')) { if (enforce_simple(p->value.binop.p0)) { char *lhs; gpasmVal value; /* fetch the symbol */ lhs = p->value.binop.p0->value.symbol; value = maybe_evaluate(p->value.binop.p1); /* put the symbol and value in the TOP table*/ add_symbol(state.stTop, lhs); set_global(lhs, value, TEMPORARY, gvt_constant); } } else if (p->tag == symbol) { /* put the symbol in the Top table */ add_symbol(state.stTop, p->value.symbol); } else { gperror(GPE_ILLEGAL_ARGU, NULL); } } } return r; } static gpasmVal do_noexpand(gpasmVal r, char *name, int arity, struct pnode *parms) { state.lst.line.linetype = dir; if (state.cmd_line.macro_expand) { gpmessage(GPM_SUPLIN, NULL); } else { if (enforce_arity(arity, 0)) { state.lst.expand = false; } } return r; } static gpasmVal do_nolist(gpasmVal r, char *name, int arity, struct pnode *parms) { state.lst.line.linetype = dir; if (!state.lst.force) state.lst.enabled = false; coff_add_nolistsym(); return r; } static gpasmVal do_maxram(gpasmVal r, char *name, int arity, struct pnode *parms) { struct pnode *p; state.lst.line.linetype = dir; if (enforce_arity(arity, 1)) { p = HEAD(parms); if (can_evaluate(p)) state.maxram = evaluate(p); } return r; } static gpasmVal do_maxrom(gpasmVal r, char *name, int arity, struct pnode *parms) { state.lst.line.linetype = dir; /* FIXME: implement this directive */ gpwarning(GPW_UNKNOWN, "gpasm doesn't support the maxrom directive yet"); return r; } static gpasmVal do_macro(gpasmVal r, char *name, int arity, struct pnode *parms) { struct macro_head *head = malloc(sizeof(*head)); head->parms = parms; head->body = NULL; head->defined = 0; /* Record data for the list, cod, and coff files */ head->line_number = state.src->line_number; head->file_symbol = state.src->file_symbol; head->src_name = strdup(state.src->name); state.lst.line.linetype = dir; if (macro_parms_ok(parms)) state.mac_head = head; state.mac_prev = &(head->body); state.mac_body = NULL; return r; } static gpasmVal do_messg(gpasmVal r, char *name, int arity, struct pnode *parms) { struct pnode *p; state.lst.line.linetype = dir; if (enforce_arity(arity, 1)) { p = HEAD(parms); if (p->tag == string) { gpmessage(GPM_USER, p->value.string); } else { gperror(GPE_ILLEGAL_ARGU, NULL); } } return r; } static gpasmVal do_org(gpasmVal r, char *name, int arity, struct pnode *parms) { struct pnode *p; state.lst.line.linetype = org; if (enforce_arity(arity, 1)) { p = HEAD(parms); if (can_evaluate(p)) { r = evaluate(p) >> _16bit_core; if (state.mode == absolute) { state.org = r; } else { /* Default section name, this will be overwritten if a label is present. */ snprintf(state.obj.new_sec_name, sizeof(state.obj.new_sec_name), ".org_%x", r); state.obj.new_sec_addr = r; state.obj.new_sec_flags = STYP_TEXT | STYP_ABS; state.lst.line.linetype = sec; state.next_state = state_section; } } } return r; } static gpasmVal do_page(gpasmVal r, char *name, int arity, struct pnode *parms) { state.lst.line.linetype = dir; if (enforce_arity(arity, 0)) lst_throw(); return r; } static gpasmVal do_pagesel(gpasmVal r, char *name, int arity, struct pnode *parms) { struct pnode *p; int address; int page; int num_reloc; if ((state.device.class == PROC_CLASS_EEPROM8) || (state.device.class == PROC_CLASS_PIC16E)) { /* do nothing */ return r; } else if (state.device.class == PROC_CLASS_PIC16) { gpmessage(GPM_W_MODIFIED, NULL); } if (enforce_arity(arity, 1)) { p = HEAD(parms); if (state.mode == absolute) { address = maybe_evaluate(p); page = gp_processor_check_page(state.device.class, address); state.org += gp_processor_set_page(state.device.class, state.processor_info->num_pages, page, state.i_memory, state.org); } else { num_reloc = count_reloc(p); if (num_reloc == 0) { /* it is an absolute address, generate the pagesel but no relocation */ address = maybe_evaluate(p); page = gp_processor_check_page(state.device.class, address); state.org += gp_processor_set_page(state.device.class, state.processor_info->num_pages, page, state.i_memory, state.org); } else if (num_reloc != 1) { gperror(GPE_ILLEGAL_LABEL, NULL); } else if (state.device.class == PROC_CLASS_PIC16) { reloc_evaluate(p, RELOCT_PAGESEL_WREG); emit(0); emit(0); } else { switch (state.processor_info->num_pages) { case 2: reloc_evaluate(p, RELOCT_PAGESEL_BITS); emit(0); break; case 4: reloc_evaluate(p, RELOCT_PAGESEL_BITS); emit(0); emit(0); break; } } } } return r; } static gpasmVal do_processor(gpasmVal r, char *name, int arity, struct pnode *parms) { state.lst.line.linetype = dir; if (enforce_arity(arity, 1)) { struct pnode *p = HEAD(parms); if (enforce_simple(p)) select_processor(p->value.symbol); } return r; } static gpasmVal do_radix(gpasmVal r, char *name, int arity, struct pnode *parms) { struct pnode *p; state.lst.line.linetype = dir; if (enforce_arity(arity, 1)) { p = HEAD(parms); if (enforce_simple(p)) { select_radix(p->value.symbol); } } return r; } static gpasmVal do_res(gpasmVal r, char *name, int arity, struct pnode *parms) { struct pnode *p; int count; int i; if (enforce_arity(arity, 1)) { p = HEAD(parms); if (can_evaluate(p)) { count = evaluate(p); r = state.org; if (state.mode == absolute) { state.lst.line.linetype = equ; state.org += (count >> _16bit_core); } else { state.lst.line.linetype = res; if (SECTION_FLAGS & STYP_TEXT) count >>= _16bit_core; for (i = 0; i < count; i++) { if (SECTION_FLAGS & STYP_TEXT) { /* For some reason program memory is filled with a different value. */ emit(state.device.core_size); } else { emit(0); } } } } } return r; } static gpasmVal do_set(gpasmVal r, char *name, int arity, struct pnode *parms) { struct pnode *p; state.lst.line.linetype = set; if (enforce_arity(arity, 1)) { p = HEAD(parms); if (can_evaluate(p)) r = evaluate(p); } return r; } static gpasmVal do_space(gpasmVal r, char *name, int arity, struct pnode *parms) { struct pnode *p; state.lst.line.linetype = dir; if (state.pass == 2) { switch (arity) { case 0: /* do nothing */ break; case 1: p = HEAD(parms); if (can_evaluate(p)) { int i; for (i = evaluate(p); i > 0; i--) lst_line(""); } break; default: enforce_arity(arity, 1); } } return r; } static gpasmVal do_type(gpasmVal r, char *name, int arity, struct pnode *parms) { struct pnode *p; char *symbol_name = NULL; gp_symbol_type *coff_symbol = NULL; int value; state.lst.line.linetype = dir; if (state.mode == absolute) { gperror(GPE_OBJECT_ONLY, NULL); } else if (enforce_arity(arity, 2)) { /* the first argument is the symbol name */ p = HEAD(parms); if (enforce_simple(p)) { symbol_name = p->value.symbol; coff_symbol = gp_coffgen_findsymbol(state.obj.object, symbol_name); if (coff_symbol == NULL) { gperror(GPE_NOSYM, NULL); } else { p = HEAD(TAIL(parms)); value = maybe_evaluate(p); if ((value < 0) || (value > 0xffff)) { gperror(GPE_RANGE, NULL); } else { coff_symbol->type = value; } } } } return r; } static gpasmVal do_udata(gpasmVal r, char *name, int arity, struct pnode *parms) { struct pnode *p; state.lst.line.linetype = sec; state.next_state = state_section; if (state.mode == absolute) { gperror(GPE_OBJECT_ONLY, NULL); } else { switch (arity) { case 0: /* new relocatable section */ strncpy(state.obj.new_sec_name, ".udata", sizeof(state.obj.new_sec_name)); state.obj.new_sec_addr = 0; state.obj.new_sec_flags = STYP_BSS; break; case 1: /* new absolute section */ p = HEAD(parms); strncpy(state.obj.new_sec_name, ".udata", sizeof(state.obj.new_sec_name)); state.obj.new_sec_addr = maybe_evaluate(p); state.obj.new_sec_flags = STYP_BSS | STYP_ABS; break; default: enforce_arity(arity, 1); } } return r; } static gpasmVal do_udata_acs(gpasmVal r, char *name, int arity, struct pnode *parms) { struct pnode *p; state.lst.line.linetype = sec; state.next_state = state_section; if (state.mode == absolute) { gperror(GPE_OBJECT_ONLY, NULL); } else { switch (arity) { case 0: /* new relocatable section */ strncpy(state.obj.new_sec_name, ".udata_acs", sizeof(state.obj.new_sec_name)); state.obj.new_sec_addr = 0; state.obj.new_sec_flags = STYP_BSS | STYP_ACCESS; break; case 1: /* new absolute section */ p = HEAD(parms); strncpy(state.obj.new_sec_name, ".udata_acs", sizeof(state.obj.new_sec_name)); state.obj.new_sec_addr = maybe_evaluate(p); state.obj.new_sec_flags = STYP_BSS | STYP_ABS | STYP_ACCESS; break; default: enforce_arity(arity, 1); } } return r; } static gpasmVal do_udata_ovr(gpasmVal r, char *name, int arity, struct pnode *parms) { struct pnode *p; state.lst.line.linetype = sec; state.next_state = state_section; if (state.mode == absolute) { gperror(GPE_OBJECT_ONLY, NULL); } else { switch (arity) { case 0: /* new relocatable section */ strncpy(state.obj.new_sec_name, ".udata_ovr", sizeof(state.obj.new_sec_name)); state.obj.new_sec_addr = 0; state.obj.new_sec_flags = STYP_BSS | STYP_OVERLAY; break; case 1: /* new absolute section */ p = HEAD(parms); strncpy(state.obj.new_sec_name, ".udata_ovr", sizeof(state.obj.new_sec_name)); state.obj.new_sec_addr = maybe_evaluate(p); state.obj.new_sec_flags = STYP_BSS | STYP_ABS | STYP_OVERLAY; break; default: enforce_arity(arity, 1); } } return r; } static gpasmVal do_udata_shr(gpasmVal r, char *name, int arity, struct pnode *parms) { struct pnode *p; state.lst.line.linetype = sec; state.next_state = state_section; if (state.mode == absolute) { gperror(GPE_OBJECT_ONLY, NULL); } else { switch (arity) { case 0: /* new relocatable section */ strncpy(state.obj.new_sec_name, ".udata_shr", sizeof(state.obj.new_sec_name)); state.obj.new_sec_addr = 0; state.obj.new_sec_flags = STYP_BSS | STYP_SHARED; break; case 1: /* new absolute section */ p = HEAD(parms); strncpy(state.obj.new_sec_name, ".udata_shr", sizeof(state.obj.new_sec_name)); state.obj.new_sec_addr = maybe_evaluate(p); state.obj.new_sec_flags = STYP_BSS | STYP_ABS | STYP_SHARED; break; default: enforce_arity(arity, 1); } } return r; } static gpasmVal do_undefine(gpasmVal r, char *name, int arity, struct pnode *parms) { struct pnode *p; state.lst.line.linetype = dir; if (enforce_arity(arity, 1)) { p = HEAD(parms); if (p->tag == symbol) { if (remove_symbol(state.stDefines, p->value.symbol) == 0) gpwarning(GPW_NOT_DEFINED, NULL); } else { gperror(GPE_ILLEGAL_ARGU, NULL); } } return r; } static gpasmVal do_variable(gpasmVal r, char *name, int arity, struct pnode *parms) { struct pnode *p; int value; state.lst.line.linetype = dir; for (; parms; parms = TAIL(parms)) { p = HEAD(parms); if ((p->tag == binop) && (p->value.binop.op == '=')) { if (enforce_simple(p->value.binop.p0)) { char *lhs; /* fetch the symbol */ lhs = p->value.binop.p0->value.symbol; value = maybe_evaluate(p->value.binop.p1); /* put the symbol and value in the table*/ set_global(lhs, value, TEMPORARY, gvt_constant); } } else if (p->tag == symbol) { /* put the symbol with a 0 value in the table*/ set_global(p->value.symbol, 0, TEMPORARY, gvt_constant); } else { gperror(GPE_ILLEGAL_ARGU, NULL); } } return r; } static gpasmVal do_while(gpasmVal r, char *name, int arity, struct pnode *parms) { struct macro_head *head = malloc(sizeof(*head)); struct pnode *p; if (state.src->type == src_while) { state.pass = 2; /* Ensure error actually gets displayed */ gperror(GPE_UNKNOWN, "gpasm does not yet support nested while loops"); exit (1); } state.lst.line.linetype = dir; if (enforce_arity(arity, 1)) { p = HEAD(parms); head->parms = p; } else { head->parms = NULL; } head->body = NULL; /* Record data for the list, cod, and coff files */ head->line_number = state.src->line_number; head->file_symbol = state.src->file_symbol; head->src_name = strdup(state.src->name); /* DON'T set up state.mac_head; this isn't a macro head. */ state.while_head = head; state.mac_prev = &(head->body); state.mac_body = NULL; return r; } int asm_enabled(void) { return ((state.astack == NULL) || (state.astack->enabled && state.astack->prev_enabled)); } /* Check that a register file address is ok */ void file_ok(unsigned int file) { /* don't check address, the linker takes care of it */ if (state.mode == relocatable) return; if ((0 > file) || (file > state.maxram) || (state.badram[file])) { gpwarning(GPW_INVALID_RAM, NULL); } /* Issue bank message if necessary */ switch (state.device.class) { case PROC_CLASS_EEPROM8: /* do nothing */ break; case PROC_CLASS_GENERIC: case PROC_CLASS_PIC12: case PROC_CLASS_SX: if (file & (~0x1f)) gpmessage(GPM_BANK, NULL); break; case PROC_CLASS_PIC14: if (file & (~0x7f)) gpmessage(GPM_BANK, NULL); break; case PROC_CLASS_PIC16: if (file & (~0xff)) gpmessage(GPM_BANK, NULL); break; case PROC_CLASS_PIC16E: /* do nothing */ break; default: assert(0); } return; } static void emit_check(int insn, int argument, int mask) { int test = argument; if (test < 0) test = -test; /* If there are bits that shouldn't be set then issue a warning */ if (test & (~mask)) { gpwarning(GPW_RANGE, NULL); } emit(insn | (argument & mask)); return; } /* For relative branches, issue a warning if the absolute value of argument is greater than range */ static void emit_check_relative(int insn, int argument, int mask, int range) { char full_message[BUFSIZ]; /* If the branch is too far then issue an error */ if ((argument > range) || (argument < -(range+1))) { snprintf(full_message, sizeof(full_message), "Argument out of range (%d not between %d and %d)\n", argument, -(range+1), range); gperror(GPE_RANGE, full_message); } emit(insn | (argument & mask)); return; } static int check_flag(int flag) { if ((flag != 0) && (flag != 1)) gpwarning(GPW_RANGE, NULL); return flag & 0x1; } gpasmVal do_insn(char *name, struct pnode *parms) { struct symbol *s; int arity; struct pnode *p; int file; /* register file address, if applicable */ gpasmVal r; /* Return value */ r = state.org; arity = list_length(parms); s = get_symbol(state.stBuiltin, name); if (s) { struct insn *i; i = get_symbol_annotation(s); /* Interpret the instruction if assembly is enabled, or if it's a conditional. */ if (asm_enabled() || (i->attribs & ATTRIB_COND)) { state.lst.line.linetype = insn; switch (i->class) { case INSN_CLASS_LIT3_BANK: if (enforce_arity(arity, 1)) { p = HEAD(parms); emit_check(i->opcode, (reloc_evaluate(p, RELOCT_F) >> 5), 0x07); } break; case INSN_CLASS_LIT3_PAGE: if (enforce_arity(arity, 1)) { p = HEAD(parms); emit_check(i->opcode, (reloc_evaluate(p, RELOCT_F) >> 9), 0x07); } break; case INSN_CLASS_LIT1: { int s = 0; switch (arity) { case 1: s = check_flag(reloc_evaluate(HEAD(parms), RELOCT_F)); case 0: emit(i->opcode | s); break; default: enforce_arity(arity, 1); } } break; case INSN_CLASS_LIT4: if (enforce_arity(arity, 1)) { p = HEAD(parms); emit_check(i->opcode, reloc_evaluate(p, RELOCT_F), 0x0f); } break; case INSN_CLASS_LIT4S: if (enforce_arity(arity, 1)) { p = HEAD(parms); emit_check(i->opcode, (reloc_evaluate(p, RELOCT_MOVLR) << 4), 0xf0); } break; case INSN_CLASS_LIT6: if (enforce_arity(arity, 1)) { p = HEAD(parms); /* The literal cannot be a relocatable address */ emit_check(i->opcode, maybe_evaluate(p), 0x3f); } break; case INSN_CLASS_LIT8: if (enforce_arity(arity, 1)) { p = HEAD(parms); if (strcasecmp(i->name, "movlb") == 0) { emit_check(i->opcode, reloc_evaluate(p, RELOCT_MOVLB), 0xf); } else { emit_check(i->opcode, reloc_evaluate(p, RELOCT_LOW), 0xff); } } break; case INSN_CLASS_LIT8C12: if (enforce_arity(arity, 1)) { int value; p = HEAD(parms); value = reloc_evaluate(p, RELOCT_CALL); /* PC is 11 bits. mpasm checks the maximum device address. */ if (value & (~0x7ff)) gperror(GPE_RANGE, NULL); if ((value & 0x600) != (state.org & 0x600)) gpmessage(GPM_PAGE, NULL); if (value & 0x100) gperror(GPE_BAD_CALL_ADDR, NULL); emit(i->opcode | (value & 0xff)); } break; case INSN_CLASS_LIT8C16: if (enforce_arity(arity, 1)) { int value; p = HEAD(parms); value = reloc_evaluate(p, RELOCT_LOW); /* PC is 16 bits. mpasm checks the maximum device address. */ if (value & (~0xffff)) gperror(GPE_RANGE, NULL); emit(i->opcode | (value & 0xff)); } break; case INSN_CLASS_LIT9: if (enforce_arity(arity, 1)) { int value; p = HEAD(parms); value = reloc_evaluate(p, RELOCT_GOTO); /* PC is 11 bits. mpasm checks the maximum device address. */ if (value & (~0x7ff)) gperror(GPE_RANGE, NULL); if ((value & 0x600) != (state.org & 0x600)) gpmessage(GPM_PAGE, NULL); emit(i->opcode | (value & 0x1ff)); } break; case INSN_CLASS_LIT11: if (enforce_arity(arity, 1)) { int value; p = HEAD(parms); if (strcasecmp(i->name, "goto") == 0) { value = reloc_evaluate(p, RELOCT_GOTO); } else { value = reloc_evaluate(p, RELOCT_CALL); } /* PC is 13 bits. mpasm checks the maximum device address. */ if (value & (~0x1fff)) gperror(GPE_RANGE, NULL); if ((value & 0x1800) != (state.org & 0x1800)) gpmessage(GPM_PAGE, NULL); emit(i->opcode | (value & 0x7ff)); } break; case INSN_CLASS_LIT13: if (enforce_arity(arity, 1)) { int value; p = HEAD(parms); if (strcasecmp(i->name, "goto") == 0) { value = reloc_evaluate(p, RELOCT_GOTO); } else { value = reloc_evaluate(p, RELOCT_CALL); } /* PC is 16 bits. mpasm checks the maximum device address. */ if (value & (~0xffff)) gperror(GPE_RANGE, NULL); if ((value & 0xe000) != (state.org & 0xe000)) gpmessage(GPM_PAGE, NULL); emit(i->opcode | (value & 0x1fff)); } break; case INSN_CLASS_LITFSR: if (enforce_arity(arity, 2)) { int value; int fsr; p = HEAD(parms); fsr = maybe_evaluate(p); if ((fsr < 0) || (fsr > 2)) gperror(GPE_RANGE, NULL); p = HEAD(TAIL(parms)); /* the offset cannot be a relocatable address */ value = maybe_evaluate(p); if (value & (~0x3f)) gperror(GPE_RANGE, NULL); emit(i->opcode | ((fsr & 0x3) << 6) | (value & 0x3f)); } break; case INSN_CLASS_RBRA8: if (enforce_arity(arity, 1)) { int offset; p = HEAD(parms); if (count_reloc(p) == 0) { offset = maybe_evaluate(p) - ((state.org + 1)<<_16bit_core); offset >>= _16bit_core; } else { offset = reloc_evaluate(p, RELOCT_CONDBRA); } /* The offset for the relative branch must be between -127 <= offset <= 127. */ emit_check_relative(i->opcode, offset, 0xff, 127); } break; case INSN_CLASS_RBRA11: if (enforce_arity(arity, 1)) { int offset; p = HEAD(parms); if (count_reloc(p) == 0) { offset = maybe_evaluate(p) - ((state.org + 1)<<_16bit_core); offset >>= _16bit_core; } else { offset = reloc_evaluate(p, RELOCT_BRA); } emit_check_relative(i->opcode, offset, 0x7ff, 0x3ff); } break; case INSN_CLASS_LIT20: if (enforce_arity(arity, 1)) { int dest; p = HEAD(parms); dest = reloc_evaluate(p, RELOCT_GOTO) >> _16bit_core; emit(i->opcode | (dest & 0xff)); reloc_evaluate(p, RELOCT_GOTO2); /* add the second relocation */ emit_check(0xf000, dest>>8, 0xfff); } break; case INSN_CLASS_CALL20: { int dest; int s = 0; /* By default, fast push is not used */ struct pnode *p2; /* second parameter */ p = HEAD(parms); switch (arity) { case 2: p2 = HEAD(TAIL(parms)); /* Allow "s" for fast push */ if ((p2->tag == symbol) && (strcasecmp(p2->value.symbol, "s") == 0)) s = 1; else s = check_flag(maybe_evaluate(p2)); break; case 1: s = 0; break; default: enforce_arity(arity, 2); } dest = reloc_evaluate(p, RELOCT_CALL) >> _16bit_core; emit(i->opcode | (s<<8) | (dest & 0xff)); reloc_evaluate(p, RELOCT_CALL2); /* add the second relocation */ emit_check(0xf000, (dest>>8), 0xfff); } break; case INSN_CLASS_FLIT12: { int k; if (enforce_arity(arity, 2)) { p = HEAD(parms); file = maybe_evaluate(p); if(file > 3) gperror(GPE_UNKNOWN, "FSR is out of range"); p = HEAD(TAIL(parms)); k = reloc_evaluate(p, RELOCT_LFSR1); emit_check(i->opcode | ((file & 3) << 4), (k>>8), 0xf); reloc_evaluate(p, RELOCT_LFSR2); /* add the second relocation */ emit(0xf000 | (k & 0xff)); } } break; case INSN_CLASS_FF: if (enforce_arity(arity, 2)) { int dest = maybe_evaluate(HEAD(TAIL(parms))); /* destination can't be PCL, TOSU, TOSH, TOSL */ if ((dest == 0xff9) || (dest == 0xfff) || (dest == 0xffe) || (dest == 0xffd)) { gperror(GPE_UNKNOWN, "Invalid destination"); } emit_check(i->opcode, reloc_evaluate(HEAD(parms), RELOCT_FF1), 0xfff); emit_check(0xf000, reloc_evaluate(HEAD(TAIL(parms)), RELOCT_FF2), 0xfff); } break; case INSN_CLASS_FP: if (enforce_arity(arity, 2)) { int reg=0; file=reloc_evaluate(HEAD(parms), RELOCT_F); reg=reloc_evaluate(HEAD(TAIL(parms)), RELOCT_P); file_ok(file); if (reg & ~0xf1f) { gpwarning(GPW_RANGE, NULL); } emit(i->opcode | ( (reg & 0x1f) << 8) | (file & 0xff) ); } break; case INSN_CLASS_PF: if (enforce_arity(arity, 2)) { int reg=0; file=reloc_evaluate(HEAD(TAIL(parms)), RELOCT_F); reg=reloc_evaluate(HEAD(parms), RELOCT_P); file_ok(file); if (reg & ~0xf1f) { gpwarning(GPW_RANGE, NULL); } emit(i->opcode | ( (reg & 0x1f) << 8) | (file & 0xff) ); } break; case INSN_CLASS_SF: if (enforce_arity(arity, 2)) { int source; int dest; p = HEAD(parms); if (p->tag != offset) gperror(GPE_MISSING_BRACKET, NULL); source = maybe_evaluate(p); p = HEAD(TAIL(parms)); dest = maybe_evaluate(p); /* destination can't be PCL, TOSU, TOSH, TOSL */ if ((dest == 0xff9) || (dest == 0xfff) || (dest == 0xffe) || (dest == 0xffd)) { gperror(GPE_UNKNOWN, "Invalid destination"); } emit_check(i->opcode, source, 0x7f); emit_check(0xf000, reloc_evaluate(HEAD(TAIL(parms)), RELOCT_FF2), 0xfff); } break; case INSN_CLASS_SS: if (enforce_arity(arity, 2)) { int source; int dest; p = HEAD(parms); if (p->tag != offset) gperror(GPE_MISSING_BRACKET, NULL); source = maybe_evaluate(p); p = HEAD(TAIL(parms)); if (p->tag != offset) gperror(GPE_MISSING_BRACKET, NULL); dest = maybe_evaluate(p); emit_check(i->opcode, source, 0x7f); emit_check(0xf000, dest, 0x7f); } break; case INSN_CLASS_OPF5: if (enforce_arity(arity, 1)) { p = HEAD(parms); if (strcasecmp(i->name, "tris") == 0) { file = reloc_evaluate(p, RELOCT_TRIS); } else { file = reloc_evaluate(p, RELOCT_F); } file_ok(file); emit(i->opcode | (file & 0x1f)); } break; case INSN_CLASS_OPWF5: { int d = 1; /* Default destination of 1 (file) */ struct pnode *p2; /* second parameter */ if(arity == 0) { enforce_arity(arity, 2); break; } p = HEAD(parms); switch (arity) { case 2: p2 = HEAD(TAIL(parms)); /* Allow "w" and "f" as destinations */ if ((p2->tag == symbol) && (strcasecmp(p2->value.symbol, "f") == 0)) d = 1; else if ((p2->tag == symbol) && (strcasecmp(p2->value.symbol, "w") == 0)) d = 0; else d = check_flag(maybe_evaluate(p2)); break; case 1: d = 1; gpmessage(GPM_NOF,NULL); break; default: enforce_arity(arity, 2); } file = reloc_evaluate(p, RELOCT_F); file_ok(file); emit(i->opcode | (d << 5) | (file & 0x1f)); } break; case INSN_CLASS_B5: { struct pnode *f, *b; int bit; if (enforce_arity(arity, 2)) { f = HEAD(parms); b = HEAD(TAIL(parms)); file = reloc_evaluate(f, RELOCT_F); bit = maybe_evaluate(b); if (!((0 <= bit) && (bit <= 7))) gpwarning(GPW_RANGE, NULL); file_ok(file); emit(i->opcode | ((bit & 7) << 5) |(file & 0x1f)); } } break; case INSN_CLASS_B8: { struct pnode *f, *b; int bit; if (enforce_arity(arity, 2)) { f = HEAD(parms); b = HEAD(TAIL(parms)); file = reloc_evaluate(f, RELOCT_F); bit = maybe_evaluate(b); if (!((0 <= bit) && (bit <= 7))) gpwarning(GPW_RANGE, NULL); file_ok(file); emit(i->opcode | ((bit & 7) << 8) | (file & 0xff)); } } break; case INSN_CLASS_OPF7: if (enforce_arity(arity, 1)) { p = HEAD(parms); if (strcasecmp(i->name, "tris") == 0) { gpwarning(GPW_NOT_RECOMMENDED, NULL); file = reloc_evaluate(p, RELOCT_TRIS); } else { file = reloc_evaluate(p, RELOCT_F); } file_ok(file); emit(i->opcode | (file & 0x7f)); } break; case INSN_CLASS_OPF8: if (enforce_arity(arity, 1)) { p = HEAD(parms); file = reloc_evaluate(p, RELOCT_F); file_ok(file); emit(i->opcode | (file & 0xff)); } break; case INSN_CLASS_OPWF7: { int d = 1; /* Default destination of 1 (file) */ struct pnode *p2; /* second parameter */ if(arity == 0) { enforce_arity(arity, 2); break; } p = HEAD(parms); switch (arity) { case 2: p2 = HEAD(TAIL(parms)); /* Allow "w" and "f" as destinations */ if ((p2->tag == symbol) && (strcasecmp(p2->value.symbol, "f") == 0)) d = 1; else if ((p2->tag == symbol) && (strcasecmp(p2->value.symbol, "w") == 0)) d = 0; else d = check_flag(maybe_evaluate(p2)); break; case 1: d = 1; gpmessage(GPM_NOF,NULL); break; default: enforce_arity(arity, 2); } file = reloc_evaluate(p, RELOCT_F); file_ok(file); emit(i->opcode | (d << 7) | (file & 0x7f)); } break; case INSN_CLASS_OPWF8: { int d = 1; /* Default destination of 1 (file) */ struct pnode *p2; /* second parameter */ if(arity == 0) { enforce_arity(arity, 2); break; } p = HEAD(parms); switch (arity) { case 2: p2 = HEAD(TAIL(parms)); /* Allow "w" and "f" as destinations */ if ((p2->tag == symbol) && (strcasecmp(p2->value.symbol, "f") == 0)) d = 1; else if ((p2->tag == symbol) && (strcasecmp(p2->value.symbol, "w") == 0)) d = 0; else d = check_flag(maybe_evaluate(p2)); break; case 1: d = 1; gpmessage(GPM_NOF,NULL); break; default: enforce_arity(arity, 2); } file = reloc_evaluate(p, RELOCT_F); file_ok(file); emit(i->opcode | (d << 8) | (file & 0xff)); } break; case INSN_CLASS_B7: { struct pnode *f, *b; int bit; if (enforce_arity(arity, 2)) { f = HEAD(parms); b = HEAD(TAIL(parms)); file = reloc_evaluate(f, RELOCT_F); bit = maybe_evaluate(b); if (!((0 <= bit) && (bit <= 7))) gpwarning(GPW_RANGE, NULL); file_ok(file); emit(i->opcode | ((bit & 7) << 7) | (file & 0x7f)); } } break; case INSN_CLASS_OPFA8: { int a = 0; struct pnode *p2; /* second parameter */ if(arity == 0) { enforce_arity(arity, 2); break; } p = HEAD(parms); file = reloc_evaluate(p, RELOCT_F); file_ok(file); /* add relocation for the access bit, if necessary */ reloc_evaluate(p, RELOCT_ACCESS); /* Default access (use the BSR unless access is to special registers) */ /* If extended instructions are enabled, access bit should default to 1 for low-end */ /* of Access Memory unless the file is explicitly an offset (e.g. [foo]) */ if ((state.extended_pic16e == true) && (file <= 0x5f)) { if (p->tag == offset) { a = 0; } else { a = 1; } } else if ((file < state.device.bsr_boundary) || (file >= (0xf00 + state.device.bsr_boundary))) { a = 0; } else { a = 1; } switch (arity) { case 2: p2 = HEAD(TAIL(parms)); /* Allow "w" and "f" as destinations */ if ((p2->tag == symbol) && (strcasecmp(p2->value.symbol, "b") == 0)) a = 1; else a = check_flag(maybe_evaluate(p2)); break; case 1: /* use default a */ break; default: enforce_arity(arity, 2); } emit(i->opcode | (a << 8) | (file & 0xff)); } break; case INSN_CLASS_BA8: { struct pnode *f, *b,*par; int bit,a=0; if ((arity != 2) && (arity != 3)) { enforce_arity(arity, 3); break; } f = HEAD(parms); file = reloc_evaluate(f, RELOCT_F); if (arity == 3) { par = HEAD(TAIL(TAIL(parms))); if ((par->tag == symbol) && (strcasecmp(par->value.symbol, "b") == 0)) a = 1; else a = check_flag(maybe_evaluate(par)); } else { /* Default access (use the BSR unless access is to special registers) */ if ((file < state.device.bsr_boundary) || (file >= (0xf00 + state.device.bsr_boundary))) { a = 0; } else { a = 1; } } /* add relocation for the access bit, if necessary */ reloc_evaluate(f, RELOCT_ACCESS); b = HEAD(TAIL(parms)); bit = maybe_evaluate(b); if (!((0 <= bit) && (bit <= 7))) gpwarning(GPW_RANGE, NULL); file_ok(file); emit(i->opcode | ( a << 8) | ((bit & 7) << 9) | (file & 0xff)); } break; case INSN_CLASS_OPWFA8: { int d = 1; /* Default destination of 1 (file) */ int a = 0; struct pnode *par; /* second parameter */ if(arity == 0) { enforce_arity(arity, 2); break; } p = HEAD(parms); file = reloc_evaluate(p, RELOCT_F); file_ok(file); /* add relocation for the access bit, if necessary */ reloc_evaluate(p, RELOCT_ACCESS); /* Default access (use the BSR unless access is to special registers) */ /* If extended instructions are enabled, access bit should default to 1 for low-end */ /* of Access Memory */ if ((state.extended_pic16e == true) && (file <= 0x5f)) { if (p->tag == offset) { a = 0; } else { a = 1; } } else if ((file < state.device.bsr_boundary) || (file >= (0xf00 + state.device.bsr_boundary))) { a = 0; } else { a = 1; } switch (arity) { case 3: par = HEAD(TAIL(TAIL(parms))); if ((par->tag == symbol) && (strcasecmp(par->value.symbol, "b") == 0)) a = 1; else a = check_flag(maybe_evaluate(par)); /* fall through */ case 2: par = HEAD(TAIL(parms)); /* Allow "w" and "f" as destinations */ if ((par->tag == symbol) && (strcasecmp(par->value.symbol, "f") == 0)) d = 1; else if ((par->tag == symbol) && (strcasecmp(par->value.symbol, "w") == 0)) d = 0; else d = check_flag(maybe_evaluate(par)); break; case 1: /* use default a and d */ gpmessage(GPM_NOF,NULL); break; default: enforce_arity(arity, 3); } emit(i->opcode | (d << 9) | (a << 8) | (file & 0xff)); } break; case INSN_CLASS_IMPLICIT: if (arity != 0) { gpwarning(GPW_EXTRANEOUS, NULL); } if ((strcasecmp(i->name, "option") == 0) && (state.device.core_size != CORE_12BIT_MASK)){ gpwarning(GPW_NOT_RECOMMENDED, NULL); } emit(i->opcode); break; case INSN_CLASS_TBL: if (enforce_arity(arity, 1)) { p = HEAD(parms); switch(maybe_evaluate(p)) { case TBL_NO_CHANGE: emit(i->opcode); break; case TBL_POST_INC: emit(i->opcode | 1); break; case TBL_POST_DEC: emit(i->opcode | 2); break; case TBL_PRE_INC: emit(i->opcode | 3); break; default: gperror(GPE_ILLEGAL_ARGU, NULL); } } break; case INSN_CLASS_TBL2: if (enforce_arity(arity, 2)) { int t=0; /* read low byte by default */ struct pnode *p2; /* second parameter */ /* "0" (lower byte) and "1" (upper byte) */ p = HEAD(parms); t = check_flag(maybe_evaluate(p)); p2 = HEAD(TAIL(parms)); file = reloc_evaluate(p2, RELOCT_F); file_ok(file); emit(i->opcode | (t << 9) | (file & 0xff)); } break; case INSN_CLASS_TBL3: if (enforce_arity(arity, 3)) { int inc=0,t=0; struct pnode *p2; /* second parameter */ struct pnode *p3; /* third parameter */ /* "0" (lower byte) and "1" (upper byte) */ p = HEAD(parms); t = check_flag(maybe_evaluate(p)); /* "0" (no change) and "1" (postincrement) */ p2 = HEAD(TAIL(parms)); inc = check_flag(maybe_evaluate(p2)); p3 = HEAD(TAIL(TAIL(parms))); file = reloc_evaluate(p3, RELOCT_F); file_ok(file); emit(i->opcode | (t << 9) | (inc << 8) | (file & 0xff)); } break; case INSN_CLASS_FUNC: r = (*(opfunc*)i->opcode)(r, name, arity, parms); break; } } } else { s = get_symbol(state.stMacros, name); if (s) { struct macro_head *h = get_symbol_annotation(s); /* Found the macro: execute it */ if (asm_enabled()) { if ((h->defined != 1) && (state.pass == 2)) { gperror(GPE_UNKNOWN, "Forward references to macros are not allowed."); } else { setup_macro(h, arity, parms); } } } else { if (asm_enabled()) { if (state.processor_chosen == 0){ gperror(GPE_UNDEF_PROC, NULL); } else { char mesg[80]; snprintf(mesg, sizeof(mesg), "Unknown opcode \"%.40s\"", name); gperror(GPE_UNKNOWN, mesg); } } } } return r; } /************************************************************************/ /* There are several groups of operations that we handle here. First is op_0: the instructions that can happen before the processor type is known. Second is op_1, the set of instructions that are common to all processors, third is processor-family specific: op_XXX */ /* Note that instructions within each group are sorted alphabetically */ struct insn op_0[] = { { "code", 0, (long int)do_code, INSN_CLASS_FUNC, 0 }, { "constant", 0, (long int)do_constant, INSN_CLASS_FUNC, 0 }, { "else", 0, (long int)do_else, INSN_CLASS_FUNC, ATTRIB_COND }, { "endif", 0, (long int)do_endif, INSN_CLASS_FUNC, ATTRIB_COND }, { "endm", 0, (long int)do_endm, INSN_CLASS_FUNC, 0 }, { "endw", 0, (long int)do_endw, INSN_CLASS_FUNC, 0 }, { "equ", 0, (long int)do_equ, INSN_CLASS_FUNC, 0 }, { "error", 0, (long int)do_error, INSN_CLASS_FUNC, 0 }, { "exitm", 0, (long int)do_exitm, INSN_CLASS_FUNC, 0 }, { "expand", 0, (long int)do_expand, INSN_CLASS_FUNC, 0 }, { "extern", 0, (long int)do_extern, INSN_CLASS_FUNC, 0 }, { "errorlevel", 0, (long int)do_errlvl, INSN_CLASS_FUNC, 0 }, { "global", 0, (long int)do_global, INSN_CLASS_FUNC, 0 }, { "idata", 0, (long int)do_idata, INSN_CLASS_FUNC, 0 }, { "if", 0, (long int)do_if, INSN_CLASS_FUNC, ATTRIB_COND }, { "ifdef", 0, (long int)do_ifdef, INSN_CLASS_FUNC, ATTRIB_COND }, { "ifndef", 0, (long int)do_ifndef, INSN_CLASS_FUNC, ATTRIB_COND }, { "include", 0, (long int)do_include, INSN_CLASS_FUNC, 0 }, { "list", 0, (long int)do_list, INSN_CLASS_FUNC, 0 }, { "local", 0, (long int)do_local, INSN_CLASS_FUNC, 0 }, { "macro", 0, (long int)do_macro, INSN_CLASS_FUNC, 0 }, { "messg", 0, (long int)do_messg, INSN_CLASS_FUNC, 0 }, { "noexpand", 0, (long int)do_noexpand, INSN_CLASS_FUNC, 0 }, { "nolist", 0, (long int)do_nolist, INSN_CLASS_FUNC, 0 }, { "page", 0, (long int)do_page, INSN_CLASS_FUNC, 0 }, { "processor", 0, (long int)do_processor, INSN_CLASS_FUNC, 0 }, { "radix", 0, (long int)do_radix, INSN_CLASS_FUNC, 0 }, { "set", 0, (long int)do_set, INSN_CLASS_FUNC, 0 }, { "space", 0, (long int)do_space, INSN_CLASS_FUNC, 0 }, { "udata", 0, (long int)do_udata, INSN_CLASS_FUNC, 0 }, { "udata_acs", 0, (long int)do_udata_acs, INSN_CLASS_FUNC, 0 }, { "udata_ovr", 0, (long int)do_udata_ovr, INSN_CLASS_FUNC, 0 }, { "udata_shr", 0, (long int)do_udata_shr, INSN_CLASS_FUNC, 0 }, { "variable", 0, (long int)do_variable, INSN_CLASS_FUNC, 0 }, { "while", 0, (long int)do_while, INSN_CLASS_FUNC, 0 }, { ".def", 0, (long int)do_def, INSN_CLASS_FUNC, 0 }, { ".dim", 0, (long int)do_dim, INSN_CLASS_FUNC, 0 }, { ".direct", 0, (long int)do_direct, INSN_CLASS_FUNC, 0 }, { ".eof", 0, (long int)do_eof, INSN_CLASS_FUNC, 0 }, { ".file", 0, (long int)do_file, INSN_CLASS_FUNC, 0 }, { ".ident", 0, (long int)do_ident, INSN_CLASS_FUNC, 0 }, { ".line", 0, (long int)do_line, INSN_CLASS_FUNC, 0 }, { ".set", 0, (long int)do_set, INSN_CLASS_FUNC, 0 }, { ".type", 0, (long int)do_type, INSN_CLASS_FUNC, 0 }, { "#if", 0, (long int)do_if, INSN_CLASS_FUNC, ATTRIB_COND }, { "#else", 0, (long int)do_else, INSN_CLASS_FUNC, ATTRIB_COND }, { "#endif", 0, (long int)do_endif, INSN_CLASS_FUNC, ATTRIB_COND }, { "#ifdef", 0, (long int)do_ifdef, INSN_CLASS_FUNC, ATTRIB_COND }, { "#ifndef", 0, (long int)do_ifndef, INSN_CLASS_FUNC, ATTRIB_COND }, { "#undefine", 0, (long int)do_undefine, INSN_CLASS_FUNC, 0 } }; const int num_op_0 = TABLE_SIZE(op_0); struct insn op_1[] = { { "__badram", 0, (long int)do_badram, INSN_CLASS_FUNC, 0 }, { "__badrom", 0, (long int)do_badrom, INSN_CLASS_FUNC, 0 }, { "__config", 0, (long int)do_config, INSN_CLASS_FUNC, 0 }, { "__fuses", 0, (long int)do_config, INSN_CLASS_FUNC, 0 }, { "__idlocs", 0, (long int)do_idlocs, INSN_CLASS_FUNC, 0 }, { "__maxram", 0, (long int)do_maxram, INSN_CLASS_FUNC, 0 }, { "__maxrom", 0, (long int)do_maxrom, INSN_CLASS_FUNC, 0 }, { "bankisel", 0, (long int)do_bankisel, INSN_CLASS_FUNC, 0 }, { "banksel", 0, (long int)do_banksel, INSN_CLASS_FUNC, 0 }, { "data", 0, (long int)do_data, INSN_CLASS_FUNC, 0 }, { "da", 0, (long int)do_da, INSN_CLASS_FUNC, 0 }, { "db", 0, (long int)do_db, INSN_CLASS_FUNC, 0 }, { "de", 0, (long int)do_de, INSN_CLASS_FUNC, 0 }, { "dt", 0, (long int)do_dt, INSN_CLASS_FUNC, 0 }, { "dw", 0, (long int)do_dw, INSN_CLASS_FUNC, 0 }, { "fill", 0, (long int)do_fill, INSN_CLASS_FUNC, 0 }, { "org", 0, (long int)do_org, INSN_CLASS_FUNC, 0 }, { "pagesel", 0, (long int)do_pagesel, INSN_CLASS_FUNC, 0 }, { "res", 0, (long int)do_res, INSN_CLASS_FUNC, 0 } }; const int num_op_1 = TABLE_SIZE(op_1); void opcode_init(int stage) { int i; int count = 0; struct insn *base = NULL; switch (stage) { case 0: base = op_0; count = num_op_0; break; case 1: base = op_1; count = num_op_1; break; case 2: state.device.class = gp_processor_class(state.processor); switch (state.device.class) { case PROC_CLASS_EEPROM8: base = 0; count = 0; state.device.core_size = CORE_8BIT_MASK; state.device.config_address = 0; state.device.id_location = 0; break; case PROC_CLASS_GENERIC: base = 0; count = 0; state.device.core_size = CORE_12BIT_MASK; state.device.config_address = CONFIG_ADDRESS_12; state.device.id_location = IDLOC_ADDRESS_12; break; case PROC_CLASS_PIC12: base = op_12c5xx; count = num_op_12c5xx; state.device.core_size = CORE_12BIT_MASK; state.device.config_address = CONFIG_ADDRESS_12; state.device.id_location = IDLOC_ADDRESS_12; break; case PROC_CLASS_SX: base = op_sx; count = num_op_sx; state.device.core_size = CORE_12BIT_MASK; state.device.config_address = CONFIG_ADDRESS_12; state.device.id_location = IDLOC_ADDRESS_12; /* page instruction conflicts with the page directive */ remove_symbol(state.stBuiltin, "page"); break; case PROC_CLASS_PIC14: base = op_16cxx; count = num_op_16cxx; state.device.core_size = CORE_14BIT_MASK; state.device.config_address = CONFIG_ADDRESS_14; state.device.id_location = IDLOC_ADDRESS_14; break; case PROC_CLASS_PIC16: base = op_17cxx; count = num_op_17cxx; state.device.core_size = CORE_16BIT_MASK; _17cxx_core = 1; state.device.config_address = CONFIG_17CXX; break; case PROC_CLASS_PIC16E: base = op_18cxx; count = num_op_18cxx; state.device.core_size = CORE_16BIT_MASK; _16bit_core = 1; state.c_memory_base = CONFIG1L; state.device.config_address = CONFIG1L; state.device.bsr_boundary = gp_processor_bsr_boundary(state.processor); /* The 16_bit core special macros are encoded directly into the * symbol table like regular instructions. */ for (i = 0; i < num_op_18cxx_sp; i++) annotate_symbol( add_symbol(state.stBuiltin, op_18cxx_sp[i].name), &op_18cxx_sp[i]); if (state.extended_pic16e) { /* Some 18xx devices have an extended instruction set. */ for (i = 0; i < num_op_18cxx_ext; i++) annotate_symbol( add_symbol(state.stBuiltin, op_18cxx_ext[i].name), &op_18cxx_ext[i]); } break; default: assert(0); } break; case 3: /* add 12 and 14 bit special macros */ base = special; count = num_op_special; break; default: assert(0); } for (i = 0; i < count; i++) annotate_symbol(add_symbol(state.stBuiltin, base[i].name), &base[i]); /* Special Case, Some instructions not available on 17c42 devices */ if (state.processor == pic17c42) { remove_symbol(state.stBuiltin, "MULWF"); remove_symbol(state.stBuiltin, "MOVLR"); remove_symbol(state.stBuiltin, "MULLW"); } } /************************************************************************/ void begin_cblock(struct pnode *c) { if (asm_enabled()) { state.cblock = maybe_evaluate(c); } } void cblock_expr(struct pnode *s) { if ((asm_enabled()) && (can_evaluate_concatenation(s))) { set_global(evaluate_concatenation(s), state.cblock, PERMANENT, gvt_cblock); state.cblock++; } } void cblock_expr_incr(struct pnode *s, struct pnode *incr) { if ((asm_enabled()) && (can_evaluate_concatenation(s))) { set_global(evaluate_concatenation(s), state.cblock, PERMANENT, gvt_cblock); state.cblock += maybe_evaluate(incr); } }