This is m98k.c in view mode; [Download] [Up]
#include <ctype.h> #include <string.h> #include <mach-o/m98k/reloc.h> #include "m98k-opcode.h" #include "as.h" #include "flonum.h" #include "expr.h" #include "hash.h" #include "read.h" #include "md.h" #include "obstack.h" #include "symbols.h" #include "messages.h" #include "atof-ieee.h" #include "input-scrub.h" #include "sections.h" /* * These are the default cputype and cpusubtype for the m98k architecture. */ const cpu_type_t md_cputype = CPU_TYPE_MC98000; cpu_subtype_t md_cpusubtype = CPU_SUBTYPE_MC98000_ALL; /* This is the byte sex for the m98k architecture */ const enum byte_sex md_target_byte_sex = BIG_ENDIAN_BYTE_SEX; /* These characters start a comment anywhere on the line */ const char md_comment_chars[] = ";"; /* These characters only start a comment at the beginning of a line */ const char md_line_comment_chars[] = "#"; /* * These characters can be used to separate mantissa decimal digits from * exponent decimal digits in floating point numbers. */ const char md_EXP_CHARS[] = "eE"; /* * The characters after a leading 0 that means this number is a floating point * constant as in 0f123.456 or 0d1.234E-12 (see md_EXP_CHARS above). */ const char md_FLT_CHARS[] = "dDfF"; /* * This is the machine dependent pseudo opcode table for this target machine. */ static void s_reg( int reg); const pseudo_typeS md_pseudo_table[] = { {"greg", s_reg, 'r' }, {0} /* end of table marker */ }; #define RT(x) (((x) >> 21) & 0x1f) #define RA(x) (((x) >> 16) & 0x1f) struct m98k_insn { unsigned long opcode; expressionS exp; enum reloc_type_m98k reloc; long pcrel; long pcrel_reloc; }; /* * The pointer to the opcode hash table built by md_begin() and used by * md_assemble() to look up opcodes. */ static struct hash_control *op_hash = NULL; /* * These aid in the printing of better error messages for parameter syntax * errors when there is only one mnemonic in the tables. */ static unsigned long error_param_count = 0; static char *error_param_message = NULL; /* * These are name names of the known special registers and the numbers assigned * to them. */ struct special_register { unsigned long number; char *name; }; static const struct special_register special_registers[] = { { 0, "mq" }, /* 601 only */ { 1, "xer" }, { 4, "rtcu" }, { 5, "rtcl" }, { 8, "lr" }, { 9, "ctr" }, { 18, "dsisr" }, { 19, "dar" }, { 22, "dec" }, { 25, "sdr1" }, { 26, "srr0" }, { 27, "srr1" }, { 272, "sprg0" }, { 273, "sprg1" }, { 274, "sprg2" }, { 275, "sprg3" }, { 280, "asr" }, { 281, "rtcd" }, { 282, "rtci" }, { 287, "pvr" }, { 528, "ibat0u" }, { 529, "ibat0l" }, { 530, "ibat1u" }, { 531, "ibat1l" }, { 532, "ibat2u" }, { 533, "ibat2l" }, { 534, "ibat3u" }, { 535, "ibat3l" }, { 528, "bat0u" }, /* 601 only */ { 529, "bat0l" }, /* 601 only */ { 530, "bat1u" }, /* 601 only */ { 531, "bat1l" }, /* 601 only */ { 532, "bat2u" }, /* 601 only */ { 533, "bat2l" }, /* 601 only */ { 534, "bat3u" }, /* 601 only */ { 535, "bat3l" }, /* 601 only */ { 536, "dbat0u" }, { 537, "dbat0l" }, { 538, "dbat1u" }, { 539, "dbat1l" }, { 540, "dbat2u" }, { 541, "dbat2l" }, { 542, "dbat3u" }, { 543, "dbat3l" }, { 1008,"hid0" }, /* 601 only */ { 1009,"hid1" }, /* 601 only */ { 1010,"hid2" }, /* 601 only */ { 1013,"hid5" }, /* 601 only */ { 1013,"dabr" }, /* 601 only */ { 1022,"fpecr" }, { 1023,"pid" }, /* 601 only */ { 0, "" } /* end of table marker */ }; /* * These are name names of the condition field special registers and the * numbers assigned to them. */ struct condition_symbol { unsigned long value; char *name; }; static const struct condition_symbol condition_symbols[] = { { 0, "lt" }, /* less than */ { 1, "gt" }, /* greater than */ { 2, "eq" }, /* equal */ { 3, "so" }, /* summary overflow */ { 3, "un" }, /* unordered */ { 0, "" } /* end of table marker */ }; struct CR_field { unsigned long value; char *name; }; static const struct CR_field CR_fields[] = { { 0, "cr0" }, /* CR field 0 */ { 4, "cr1" }, /* CR field 1 */ { 8, "cr2" }, /* CR field 2 */ { 12, "cr3" }, /* CR field 3 */ { 16, "cr4" }, /* CR field 4 */ { 20, "cr5" }, /* CR field 5 */ { 24, "cr6" }, /* CR field 6 */ { 28, "cr7" }, /* CR field 7 */ { 0, "" } /* end of table marker */ }; /* * These are built in macros because they are trivial to implement as macros * which otherwise be less obvious to do special entries for them. */ struct macros { char *name; char *body; }; static const struct macros m98k_macros[] = { { "extldi\n", "rldicr $0,$1,$3,$2-1\n" }, { "extldi.\n", "rldicr. $0,$1,$3,$2-1\n" }, { "extrdi\n", "rldicl $0,$1,$2+$3,64-$2\n" }, { "extrdi.\n", "rldicl. $0,$1,$2+$3,64-$2\n" }, { "insrdi\n", "rldimi $0,$1,64-($3+$2),$3\n" }, { "insrdi.\n", "rldimi. $0,$1,64-($3+$2),$3\n" }, { "rotldi\n", "rldicl $0,$1,$2,0\n" }, { "rotldi.\n", "rldicl. $0,$1,$2,0\n" }, { "rotrdi\n", "rldicl $0,$1,64-$2,0\n" }, { "rotrdi.\n", "rldicl. $0,$1,64-$2,0\n" }, { "rotld\n", "rldcl $0,$1,$2,0\n" }, { "rotld.\n", "rldcl. $0,$1,$2,0\n" }, { "sldi\n", "rldicr $0,$1,$2,63-$2\n" }, { "sldi.\n", "rldicr. $0,$1,$2,63-$2\n" }, { "srdi\n", "rldicl $0,$1,64-$2,$2\n" }, { "srdi.\n", "rldicl. $0,$1,64-$2,$2\n" }, { "clrldi\n", "rldicl $0,$1,0,$2\n" }, { "clrldi.\n", "rldicl. $0,$1,0,$2\n" }, { "clrrdi\n", "rldicl $0,$1,0,63-$2\n" }, { "clrrdi.\n", "rldicl. $0,$1,0,63-$2\n" }, { "clrlsldi\n","rldic $0,$1,$3,$2-$3\n" }, { "clrlsldi.\n","rldic. $0,$1,$3,$2-$3\n" }, { "extlwi\n", "rlwinm $0,$1,$3,0,$2-1\n" }, { "extlwi.\n", "rlwinm. $0,$1,$3,0,$2-1\n" }, { "extrwi\n", "rlwinm $0,$1,$2+$3,32-$2,31\n" }, { "extrwi.\n", "rlwinm. $0,$1,$2+$3,32-$2,31\n" }, { "inslwi\n", "rlwimi $0,$1,32-$3,$3,($3+$2)-1\n" }, { "inslwi.\n", "rlwimi. $0,$1,32-$3,$3,($3+$2)-1\n" }, { "insrwi\n", "rlwimi $0,$1,32-($3+$2),$3,($3+$2)-1\n" }, { "insrwi.\n", "rlwimi. $0,$1,32-($3+$2),$3,($3+$2)-1\n" }, { "rotlwi\n", "rlwinm $0,$1,$2,0,31\n" }, { "rotlwi.\n", "rlwinm. $0,$1,$2,0,31\n" }, { "rotrwi\n", "rlwinm $0,$1,32-$2,0,31\n" }, { "rotrwi.\n", "rlwinm. $0,$1,32-$2,0,31\n" }, { "rotlw\n", "rlwnm $0,$1,$2,0,31\n" }, { "rotlw.\n", "rlwnm. $0,$1,$2,0,31\n" }, { "slwi\n", "rlwinm $0,$1,$2,0,31-$2\n" }, { "slwi.\n", "rlwinm. $0,$1,$2,0,31-$2\n" }, { "srwi\n", "rlwinm $0,$1,32-$2,$2,31\n" }, { "srwi.\n", "rlwinm. $0,$1,32-$2,$2,31\n" }, { "clrlwi\n", "rlwinm $0,$1,0,$2,31\n" }, { "clrlwi.\n", "rlwinm. $0,$1,0,$2,31\n" }, { "clrrwi\n", "rlwinm $0,$1,0,0,31-$2\n" }, { "clrrwi.\n", "rlwinm. $0,$1,0,0,31-$2\n" }, { "clrlslwi\n","rlwinm $0,$1,$3,$2-$3,31-$2\n" }, { "clrlslwi.\n","rlwinm. $0,$1,$3,$2-$3,31-$2\n" }, { "mtxer\n", "mtspr 1,$0\n"}, { "mfxer\n", "mfspr $0,1\n"}, { "mtlr\n", "mtspr 8,$0\n"}, { "mflr\n", "mfspr $0,8\n"}, { "mtctr\n", "mtspr 9,$0\n"}, { "mfctr\n", "mfspr $0,9\n"}, { "mtdsisr\n", "mtspr 18,$0\n"}, { "mfdsisr\n", "mfspr $0,18\n"}, { "mtdar\n", "mtspr 19,$0\n"}, { "mfdar\n", "mfspr $0,19\n"}, { "mtdec\n", "mtspr 22,$0\n"}, { "mfdec\n", "mfspr $0,22\n"}, { "mtsdr1\n", "mtspr 25,$0\n"}, { "mfsdr1\n", "mfspr $0,25\n"}, { "mtsrr0\n", "mtspr 26,$0\n"}, { "mfsrr0\n", "mfspr $0,26\n"}, { "mtsrr1\n", "mtspr 27,$0\n"}, { "mfsrr1\n", "mfspr $0,27\n"}, { "mtsprg\n", "mtspr 272+$0,$1\n"}, { "mfsprg\n", "mfspr $0,272+$1\n"}, { "mtasr\n", "mtspr 280,$0\n"}, { "mfasr\n", "mfspr $0,280\n"}, { "mtrtcd\n", "mtspr 281,$0\n"}, { "mfrtcd\n", "mfspr $0,281\n"}, { "mtrtci\n", "mtspr 282,$0\n"}, { "mfrtci\n", "mfspr $0,282\n"}, { "mfpvr\n", "mfspr $0,287\n"}, { "mtibatu\n", "mtspr 528+2*$0,$1\n"}, { "mfibatu\n", "mfspr $0,528+2*$1\n"}, { "mtibatl\n", "mtspr 529+2*$0,$1\n"}, { "mfibatl\n", "mfspr $0,529+2*$1\n"}, { "mtdbatu\n", "mtspr 536+2*$0,$1\n"}, { "mfdbatu\n", "mfspr $0,536+2*$1\n"}, { "mtdbatl\n", "mtspr 537+2*$0,$1\n"}, { "mfdbatl\n", "mfspr $0,537+2*$1\n"}, { "mtbatu\n", "mtspr 528+2*$0,$1\n"}, { "mfbatu\n", "mfspr $0,528+2*$1\n"}, { "mtbatl\n", "mtspr 529+2*$0,$1\n"}, { "mfbatl\n", "mfspr $0,529+2*$1\n"}, { "subi\n", "addi $0,$1,-($2)\n"}, { "subis\n", "addis $0,$1,-($2)\n"}, { "subic\n", "addic $0,$1,-($2)\n"}, { "subic.\n", "addic. $0,$1,-($2)\n"}, { "", "" } /* end of table marker */ }; static int calcop( struct m98k_opcode *format, char *param, struct m98k_insn *insn, char *op, char prediction); static char *parse_branch( char *param, struct m98k_insn *insn, struct m98k_opcode *format, int parcnt); static char *parse_displacement( char *param, struct m98k_insn *insn, struct m98k_opcode *format, int parcnt); static char *parse_immediate( char *param, struct m98k_insn *insn, struct m98k_opcode *format, int parcnt); static char *parse_reg( char *reg_name, char *param, struct m98k_insn *insn, struct m98k_opcode *format, unsigned long parcnt); static char *parse_spreg( char *param, struct m98k_insn *insn, struct m98k_opcode *format, unsigned long parcnt); static char *parse_bcnd( char *param, struct m98k_insn *insn, struct m98k_opcode *format, unsigned long parcnt); static char *parse_crf( char *param, struct m98k_insn *insn, struct m98k_opcode *format, unsigned long parcnt); static char *parse_num( char *param, struct m98k_insn *insn, struct m98k_opcode *format, unsigned long parcnt, long max_width_zero); static char *parse_sh( char *param, struct m98k_insn *insn, struct m98k_opcode *format, unsigned long parcnt); static char *parse_mb( char *param, struct m98k_insn *insn, struct m98k_opcode *format, unsigned long parcnt); /* * md_begin() is called from main() in as.c before assembly begins. It is used * to allow target machine dependent initialization. */ void md_begin(void) { unsigned long i; char *name, *retval; /* initialize the opcode hash table */ op_hash = hash_new(); if(op_hash == NULL) as_fatal("Could not initialize the opcode hash table"); /* loop until you see the end of the list */ i = 0; while(*m98k_opcodes[i].name){ name = m98k_opcodes[i].name; /* hash each mnemonic and record its position */ retval = hash_insert(op_hash, name, (char *)&m98k_opcodes[i]); if(retval != NULL && *retval != '\0') as_fatal("Can't hash instruction '%s':%s", m98k_opcodes[i].name, retval); /* skip to next unique mnemonic or end of list */ for(i++; strcmp(m98k_opcodes[i].name, name) == 0; i++) ; } /* * Load the builtin macros for extended mnemonics for rotate and * shift mnemonics. */ for(i = 0; *m98k_macros[i].name != '\0'; i++){ input_line_pointer = m98k_macros[i].name; s_macro(0); add_to_macro_definition(m98k_macros[i].body); s_endmacro(0); } } /* * md_end() is called from main() in as.c after assembly ends. It is used * to allow target machine dependent clean up. */ void md_end(void) { } /* * md_parse_option() is called from main() in as.c to parse target machine * dependent command line options. This routine returns 0 if it is passed an * option that is not recognized non-zero otherwise. */ int md_parse_option( char **argP, int *cntP, char ***vecP) { return(0); } /* * s_reg() is used to implement ".greg symbol,exp" and ".xreg symbol,exp" * which set symbol to 1 or 0 depending on if the expression is a general * register or extended register respectfully. These are intended for use in * macros. */ static void s_reg( int reg) { char *name, *end_name, delim; symbolS *symbolP; unsigned long n_value, val; if( * input_line_pointer == '"') name = input_line_pointer + 1; else name = input_line_pointer; delim = get_symbol_end(); end_name = input_line_pointer; *end_name = delim; SKIP_WHITESPACE(); if ( * input_line_pointer != ',' ) { *end_name = 0; as_warn("Expected comma after name \"%s\"", name); *end_name = delim; ignore_rest_of_line(); return; } input_line_pointer ++; *end_name = 0; SKIP_WHITESPACE(); n_value = 0; if (*input_line_pointer == reg || *input_line_pointer == toupper(reg)){ input_line_pointer++; if(isdigit(*input_line_pointer)){ val = 0; while (isdigit(*input_line_pointer)){ if ((val = val * 10 + *input_line_pointer++ - '0') > 31) break; } SKIP_WHITESPACE(); if(val <= 31 && (*input_line_pointer == '\n' || *input_line_pointer == '@')) n_value = 1; } } symbolP = symbol_find_or_make (name); symbolP -> sy_type = N_ABS; symbolP -> sy_other = 0; /* NO_SECT */ symbolP -> sy_value = n_value; symbolP -> sy_frag = & zero_address_frag; *end_name = delim; totally_ignore_line(); } /* * md_assemble() is passed a pointer to a string that should be a assembly * statement for the target machine. */ void md_assemble( char *op) { char *param, *thisfrag, prediction; struct m98k_opcode *format; struct m98k_insn insn; unsigned long retry; /* * Pick up the instruction and any trailing branch prediction character * (a trailing '+' or '-' on the instruction). */ prediction = '\0'; for(param = op; !isspace(*param) && *param != '\0' ; param++) prediction = *param; if(prediction == '+' || prediction == '-') param[-1] = '\0'; else prediction = '\0'; if(*param != '\0') *param++ = '\0'; /* try to find the instruction in the hash table */ if((format = (struct m98k_opcode *)hash_find(op_hash, op)) == NULL){ as_warn("Invalid mnemonic '%s'", op); return; } /* try parsing this instruction into insn */ retry = 0; error_param_count = 0; error_param_message = NULL; while(calcop(format, param, &insn, op, prediction) == 0){ /* if it doesn't parse try the next instruction */ if(strcmp(format->name, format[1].name) == 0){ format++; retry = 1; } else{ if(retry == 0){ if(error_param_message != NULL) as_warn(error_param_message, error_param_count + 1); else as_warn("Parameter syntax error (parameter %lu)", error_param_count + 1); } else as_warn("Parameter syntax error"); return; } } /* * Check for invalid forms of instructions. For the following * instructions: lbzu, lbzux, lhzu, lhzux, lhau, lhaux, lwzu, lwzux, * lwaux, ldu, ldux * if RA == 0 or RA == RT the instruction form is invalid. */ if((insn.opcode & 0xfc000000) == 0x8c000000 || /* lbzu */ (insn.opcode & 0xfc0007fe) == 0x7c0000ee || /* lbzux */ (insn.opcode & 0xfc000000) == 0xa4000000 || /* lhzu */ (insn.opcode & 0xfc0007fe) == 0x7c00026e || /* lbzux */ (insn.opcode & 0xfc000000) == 0xac000000 || /* lhau */ (insn.opcode & 0xfc0007fe) == 0x7c0002ee || /* lhaux */ (insn.opcode & 0xfc000000) == 0x84000000 || /* lwzu */ (insn.opcode & 0xfc0007fe) == 0x7c00006e || /* lwzux */ (insn.opcode & 0xfc0007fe) == 0x7c0002ea || /* lwaux */ (insn.opcode & 0xfc000000) == 0xe8000000 || /* ldu */ (insn.opcode & 0xfc0007fe) == 0x7c00006a){ /* ldux */ if(RA(insn.opcode) == 0) as_warn("Invalid form of the instruction (RA must not be 0)"); if(RA(insn.opcode) == RT(insn.opcode)) as_warn("Invalid form of the instruction (RA must not the same " "as RT)"); } /* * For the following instructions: stbu, stbux, sthu, sthux, stwu, * stwux, stdu, stdux, lfsu, lfsux, lfdu, lfdux, stfsu, stfsux, stfdu, * stfdux * if RA == 0 the instruction form is invalid. */ if((insn.opcode & 0xfc000000) == 0x9c000000 || /* stbu */ (insn.opcode & 0xfc0007fe) == 0x7c0001ee || /* stbux */ (insn.opcode & 0xfc000000) == 0xb4000000 || /* sthu */ (insn.opcode & 0xfc0007fe) == 0x7c00036e || /* sthux */ (insn.opcode & 0xfc000000) == 0x94000000 || /* stwu */ (insn.opcode & 0xfc0007fe) == 0x7c00016e || /* stwux */ (insn.opcode & 0xfc000003) == 0xf8000001 || /* stdu */ (insn.opcode & 0xfc0007fe) == 0x7c00016a || /* stdux */ (insn.opcode & 0xfc000000) == 0xc4000000 || /* lfsu */ (insn.opcode & 0xfc0007fe) == 0x7c00046e || /* lfsux */ (insn.opcode & 0xfc000000) == 0xcc000000 || /* lfdu */ (insn.opcode & 0xfc0007fe) == 0x7c0004ee || /* lfdux */ (insn.opcode & 0xfc000000) == 0xd4000000 || /* stfsu */ (insn.opcode & 0xfc0007fe) == 0x7c00056e || /* stfsux */ (insn.opcode & 0xfc000000) == 0xdc000000 || /* stfdu */ (insn.opcode & 0xfc0007fe) == 0x7c0005ee){ /* stfdux */ if(RA(insn.opcode) == 0) as_warn("Invalid form of the instruction (RA must not be 0)"); } /* * For the following instructions: lmw, lmd, lswi, lswx * if RA is in the range of registers to be loaded or RT == RA == 0 * the instruction form is invalid. WHAT does this mean? */ if((insn.opcode & 0xfc000000) == 0xb8000000 || /* lmw */ (insn.opcode & 0xfc000003) == 0xe8000003 || /* lmw */ (insn.opcode & 0xfc0007fe) == 0x7c0004aa || /* lswi */ (insn.opcode & 0xfc0007fe) == 0x7c00042a){ /* lswx */ } /* grow the current frag and plop in the opcode */ thisfrag = frag_more(4); md_number_to_chars(thisfrag, insn.opcode, 4); /* * If the -g flag is present generate a line number stab for the * instruction. * * See the detailed comments about stabs in read_a_source_file() for a * description of what is going on here. */ if(flagseen['g'] && frchain_now->frch_nsect == text_nsect){ (void)symbol_new( "", 68 /* N_SLINE */, text_nsect, logical_input_line /* n_desc, line number */, obstack_next_free(&frags) - frag_now->fr_literal, frag_now); } /* if this instruction requires labels mark it for later */ switch(insn.reloc){ case NO_RELOC: break; case M98K_RELOC_HI16: case M98K_RELOC_LO16: case M98K_RELOC_HA16: case M98K_RELOC_LO14: fix_new(frag_now, thisfrag - frag_now->fr_literal, 4, insn.exp.X_add_symbol, insn.exp.X_subtract_symbol, insn.exp.X_add_number, 0, 0, insn.reloc); break; case M98K_RELOC_BR14: fix_new(frag_now, thisfrag - frag_now->fr_literal, 4, insn.exp.X_add_symbol, insn.exp.X_subtract_symbol, insn.exp.X_add_number, insn.pcrel, insn.pcrel_reloc, insn.reloc); break; case M98K_RELOC_BR24: fix_new(frag_now, thisfrag - frag_now->fr_literal, 4, insn.exp.X_add_symbol, insn.exp.X_subtract_symbol, insn.exp.X_add_number, insn.pcrel, insn.pcrel_reloc, insn.reloc); break; default: as_warn("Unknown relocation type"); break; } } static int calcop( struct m98k_opcode *format, char *param, struct m98k_insn *insn, char *op, char prediction) { unsigned long parcnt; /* initial the passed structure */ memset(insn, '\0', sizeof(struct m98k_insn)); insn->opcode = format->opcode; insn->reloc = NO_RELOC; /* parse all parameters */ for(parcnt = 0; parcnt < 5 && format->ops[parcnt].type != NONE; parcnt++){ error_param_count = parcnt; switch(format->ops[parcnt].type){ case PCREL: case BADDR: param = parse_branch(param, insn, format, parcnt); break; case D: case DS: param = parse_displacement(param, insn, format, parcnt); break; case SI: case UI: param = parse_immediate(param, insn, format, parcnt); break; case GREG: case G0REG: param = parse_reg("r", param, insn, format, parcnt); break; case FREG: param = parse_reg("f", param, insn, format, parcnt); break; case SGREG: param = parse_reg("sr", param, insn, format, parcnt); break; case SPREG: param = parse_spreg(param, insn, format, parcnt); break; case BCND: param = parse_bcnd(param, insn, format, parcnt); break; case CRF: case CRFONLY: param = parse_crf(param, insn, format, parcnt); break; case NUM: param = parse_num(param, insn, format, parcnt, 0); break; case NUM0: param = parse_num(param, insn, format, parcnt, 1); break; case sh: param = parse_sh(param, insn, format, parcnt); break; case mb: param = parse_mb(param, insn, format, parcnt); break; default: as_fatal("Unknown parameter type"); } /* see if parser failed or not */ if (param == NULL) return(0); } if(format->ops[0].type == NONE && *param != '\0'){ error_param_message = "too many parameters"; return(0); } if(IS_BRANCH_CONDITIONAL(insn->opcode)){ if(prediction != '\0'){ /* * Set the Y_BIT assuming the displacement is non-negitive. * If the displacement is negitive then the Y_BIT is flipped * in md_number_to_imm(). */ if(prediction == '+') insn->opcode |= Y_BIT; else{ /* prediction == '-' */ if((insn->opcode & Y_BIT) != 0) as_warn("branch prediction ('-') ignored (specified " "operand has prediction bit set)"); else insn->opcode &= ~(Y_BIT); } } } else{ if(prediction != '\0') as_warn("branch prediction ignored (instruction is not a " "conditional branch)"); } return(1); } static char * parse_displacement( char *param, struct m98k_insn *insn, struct m98k_opcode *format, int parcnt) { unsigned long val; char *end, *saveptr, *saveparam; segT seg; if(parcnt != 1 || format->ops[2].type != G0REG) as_fatal("internal error, bad table entry for instruction %s " "(displacement operand not second operand or general " "register not third operand)", format->name); /* * There must be "(rX)" (where X is a number between 0-31) or "(0)" * at the end of the parameter string. To know out where the * displacement expression ends determine the begining the "(rX)" * by looking for the last '(' in the string. The parsing of this * trailing string will be done in another routine. */ end = strrchr(param, '('); if(end == NULL) return(NULL); *end = '\0'; /* * The expression may have one of the following: hi16(exp), ha16(exp), * or lo16(exp) around the expression which determines the relocation * type. */ if(strncmp(param,"hi16(",5) == 0){ insn->reloc = M98K_RELOC_HI16; param += 5; } else if(strncmp(param,"ha16(",5) == 0){ insn->reloc = M98K_RELOC_HA16; param += 5; } else if(strncmp(param,"lo16(",5) == 0){ if(format->ops[parcnt].type == DS) insn->reloc = M98K_RELOC_LO14; else insn->reloc = M98K_RELOC_LO16; param += 5; } saveptr = input_line_pointer; input_line_pointer = param; seg = expression(&insn->exp); try_to_make_absolute(&insn->exp); seg = insn->exp.X_seg; saveparam = input_line_pointer; input_line_pointer = saveptr; *end = '('; if(insn->reloc != NO_RELOC){ if(*saveparam != ')' || ++saveparam != end) return(NULL); } else{ if(saveparam != end) return(NULL); val = insn->exp.X_add_number; if(seg != SEG_ABSOLUTE){ error_param_message = "Parameter error: expression must be " "absolute (parameter %lu)"; return(NULL); } if(val & 0x8000){ if((val & 0xffff0000) != 0xffff0000){ error_param_message = "Parameter error: expression out of " "range (parameter %lu)"; return(NULL); } val = val & 0xffff; } else{ if((val & 0xffff0000) != 0){ error_param_message = "Parameter error: expression out of " "range (parameter %lu)"; return(NULL); } } if(format->ops[parcnt].type == DS){ if((val & 0x3) != 0){ error_param_message = "Parameter error: expression must be " "a multiple of 4 (parameter %lu)"; return(NULL); } val >>= 2; } insn->opcode |= val << format->ops[parcnt].offset; } return(saveparam); } static char * parse_immediate( char *param, struct m98k_insn *insn, struct m98k_opcode *format, int parcnt) { unsigned long val; char *saveptr, *saveparam; segT seg; /* * The expression may have one of the following: hi16(exp), ha16(exp), * or lo16(exp) around the expression which determines the relocation * type. */ if(strncmp(param,"hi16(",5) == 0){ insn->reloc = M98K_RELOC_HI16; param += 5; } else if(strncmp(param,"ha16(",5) == 0){ insn->reloc = M98K_RELOC_HA16; param += 5; } else if(strncmp(param,"lo16(",5) == 0){ if(format->ops[parcnt].type == DS) insn->reloc = M98K_RELOC_LO14; else insn->reloc = M98K_RELOC_LO16; param += 5; } saveptr = input_line_pointer; input_line_pointer = param; seg = expression(&insn->exp); try_to_make_absolute(&insn->exp); seg = insn->exp.X_seg; saveparam = input_line_pointer; input_line_pointer = saveptr; if(insn->reloc != NO_RELOC){ if(*saveparam != ')') return(NULL); saveparam++; if(*saveparam == '\0'){ if(parcnt == 4 || format->ops[parcnt+1].type == NONE) return(saveparam); else return(NULL); } else if(*saveparam == ','){ if(parcnt != 4 && format->ops[parcnt+1].type != NONE) return(saveparam+1); else return(NULL); } else return(NULL); } else{ val = insn->exp.X_add_number; if(seg != SEG_ABSOLUTE){ error_param_message = "Parameter error: expression must be " "absolute (parameter %lu)"; return(NULL); } if(format->ops[parcnt].type == SI){ if(val & 0x8000){ if((val & 0xffff0000) != 0xffff0000){ error_param_message = "Parameter error: expression out " "of range (parameter %lu)"; return(NULL); } val = val & 0xffff; } else{ if((val & 0xffff0000) != 0){ error_param_message = "Parameter error: expression out " "of range (parameter %lu)"; return(NULL); } } } else{ if((val & 0xffff0000) != 0){ error_param_message = "Parameter error: expression out " "of range (parameter %lu)"; return(NULL); } } if(*saveparam == '\0'){ if(parcnt == 4 || format->ops[parcnt+1].type == NONE){ insn->opcode |= val << format->ops[parcnt].offset; return(saveparam); } else return(NULL); } else if(*saveparam == ','){ if(parcnt != 4 && format->ops[parcnt+1].type != NONE){ insn->opcode |= val << format->ops[parcnt].offset; return(saveparam+1); } else return(NULL); } else return(NULL); } return(saveparam); } static char * parse_branch( char *param, struct m98k_insn *insn, struct m98k_opcode *format, int parcnt) { char *saveptr, *saveparam; segT seg; saveptr = input_line_pointer; input_line_pointer = param; seg = expression(&insn->exp); try_to_make_absolute(&insn->exp); seg = insn->exp.X_seg; saveparam = input_line_pointer; input_line_pointer = saveptr; insn->pcrel = 0; insn->pcrel_reloc = 0; if(format->ops[parcnt].type == PCREL){ /* * The NeXT linker has the ability to scatter blocks of * sections between labels. This requires that brances to * labels that survive to the link phase must be able to * be relocated. */ if(insn->exp.X_add_symbol != NULL && (insn->exp.X_add_symbol->sy_name[0] != 'L' || flagseen ['L'])) insn->pcrel_reloc = 1; else insn->pcrel_reloc = 0; insn->pcrel = 1; } switch(format->ops[parcnt].width){ case 14: insn->reloc = M98K_RELOC_BR14; break; case 24: insn->reloc = M98K_RELOC_BR24; break; default: as_fatal("Unknown branch instruction width %d", format->ops[parcnt].width); break; } return(saveparam); } static char * parse_reg( char *reg_name, char *param, struct m98k_insn *insn, struct m98k_opcode *format, unsigned long parcnt) { unsigned long val, d; d = 0; if(*param == '(' && parcnt == 2 && (format->ops[1].type == D || format->ops[1].type == DS)){ d = 1; param++; } if(format->ops[parcnt].type == G0REG && *param == '0'){ val = 0; param++; } else{ val = 0; while(*reg_name){ if(*param++ != *reg_name++) return(NULL); } if(!isdigit(*param)) return(NULL); while(isdigit(*param)) if((val = val * 10 + *param++ - '0') >= 1 << format->ops[parcnt].width) return(NULL); if(format->ops[parcnt].type == G0REG && val == 0){ error_param_message = "Parameter error: r0 not allowed " "for parameter %lu (code as 0 not r0)"; return(NULL); } } if(*param == '\0'){ if(parcnt == 4 || format->ops[parcnt+1].type == NONE){ insn->opcode |= val << format->ops[parcnt].offset; return(param); } else return(NULL); } else if(*param == ','){ if(parcnt != 4 && format->ops[parcnt+1].type != NONE){ insn->opcode |= val << format->ops[parcnt].offset; return(param+1); } else return(NULL); } else if(d == 1 && *param == ')' && param[1] == '\0'){ if(parcnt == 4 || format->ops[parcnt+1].type == NONE){ insn->opcode |= val << format->ops[parcnt].offset; return(++param); } else return(NULL); } return(NULL); } static char * parse_spreg( char *param, struct m98k_insn *insn, struct m98k_opcode *format, unsigned long parcnt) { int val; unsigned long i; char *saveptr, save_c; expressionS exp; segT seg; saveptr = input_line_pointer; input_line_pointer = param; while(*param != ',' && *param != '\0') param++; save_c = *param; *param = '\0'; seg = SEG_ABSOLUTE; val = 0; for(i = 0; *special_registers[i].name != '\0'; i++){ if(strcmp(input_line_pointer, special_registers[i].name) == 0){ val = special_registers[i].number; break; } } if(*special_registers[i].name == '\0'){ seg = expression(&exp); try_to_make_absolute(&exp); seg = exp.X_seg; val = exp.X_add_number; } *param = save_c; input_line_pointer = saveptr; if(seg != SEG_ABSOLUTE){ error_param_message = "Parameter error: expression must be " "absolute (parameter %lu)"; return(NULL); } if(val > 1024 || val < 0){ error_param_message = "Parameter error: expression out " "of range (parameter %lu)"; return(NULL); } val = ((val & 0x1f) << 5) | ((val >> 5) & 0x1f); if(*param == '\0'){ if(parcnt == 4 || format->ops[parcnt+1].type == NONE){ insn->opcode |= val << format->ops[parcnt].offset; return(param); } else return(NULL); } else if(*param == ','){ if(parcnt != 4 && format->ops[parcnt+1].type != NONE){ insn->opcode |= val << format->ops[parcnt].offset; return(param+1); } else return(NULL); } return(NULL); } static char * parse_bcnd( char *param, struct m98k_insn *insn, struct m98k_opcode *format, unsigned long parcnt) { int val; unsigned long i, j; char *saveptr, save_c, *plus, save_plus; expressionS exp; segT seg; saveptr = input_line_pointer; input_line_pointer = param; while(*param != ',' && *param != '\0') param++; save_c = *param; *param = '\0'; /* * look for "[CR_field+]condition_symbol". */ val = -1; for(plus = input_line_pointer; *plus != '+' && *plus != '\0'; plus++) ; if(*plus == '+'){ save_plus = *plus; *plus = '\0'; for(i = 0; *CR_fields[i].name != '\0'; i++) if(strcmp(input_line_pointer, CR_fields[i].name) == 0) break; *plus = save_plus; if(*CR_fields[i].name != '\0'){ for(j = 0; *condition_symbols[j].name != '\0'; j++) if(strcmp(plus+1, condition_symbols[j].name) == 0) break; if(*condition_symbols[j].name != '\0'){ val = CR_fields[i].value + condition_symbols[j].value; } } } else{ for(i = 0; *condition_symbols[i].name != '\0'; i++) if(strcmp(input_line_pointer, condition_symbols[i].name) == 0) break; if(*condition_symbols[i].name != '\0') val = condition_symbols[i].value; } if(val == -1){ seg = expression(&exp); try_to_make_absolute(&exp); seg = exp.X_seg; val = exp.X_add_number; if(seg != SEG_ABSOLUTE){ error_param_message = "Parameter error: expression must be " "absolute (parameter %lu)"; *param = save_c; input_line_pointer = saveptr; return(NULL); } if(val >= (1 << format->ops[parcnt].width) || val < 0){ error_param_message = "Parameter error: expression out " "of range (parameter %lu)"; *param = save_c; input_line_pointer = saveptr; return(NULL); } } *param = save_c; input_line_pointer = saveptr; if(*param == '\0'){ if(parcnt == 4 || format->ops[parcnt+1].type == NONE){ insn->opcode |= val << format->ops[parcnt].offset; return(param); } else return(NULL); } else if(*param == ','){ if(parcnt != 4 && format->ops[parcnt+1].type != NONE){ insn->opcode |= val << format->ops[parcnt].offset; return(param+1); } else return(NULL); } return(NULL); } static char * parse_crf( char *param, struct m98k_insn *insn, struct m98k_opcode *format, unsigned long parcnt) { int val; unsigned long i; char *saveptr, save_c; expressionS exp; segT seg; saveptr = input_line_pointer; input_line_pointer = param; while(*param != ',' && *param != '\0') param++; save_c = *param; *param = '\0'; val = -1; for(i = 0; *CR_fields[i].name != '\0'; i++){ if(strcmp(input_line_pointer, CR_fields[i].name) == 0){ val = CR_fields[i].value; break; } } if(val == -1){ if(format->ops[parcnt].type == CRFONLY){ *param = save_c; input_line_pointer = saveptr; return(NULL); } seg = expression(&exp); try_to_make_absolute(&exp); seg = exp.X_seg; val = exp.X_add_number; if(seg != SEG_ABSOLUTE){ error_param_message = "Parameter error: expression must be " "absolute (parameter %lu)"; *param = save_c; input_line_pointer = saveptr; return(NULL); } if(val >= (1 << format->ops[parcnt].width) || val < 0){ error_param_message = "Parameter error: expression out " "of range (parameter %lu)"; *param = save_c; input_line_pointer = saveptr; return(NULL); } } *param = save_c; input_line_pointer = saveptr; if(*param == '\0'){ if(parcnt == 4 || format->ops[parcnt+1].type == NONE){ insn->opcode |= val << format->ops[parcnt].offset; return(param); } else return(NULL); } else if(*param == ','){ if(parcnt != 4 && format->ops[parcnt+1].type != NONE){ insn->opcode |= val << format->ops[parcnt].offset; return(param+1); } else return(NULL); } return(NULL); } static char * parse_num( char *param, struct m98k_insn *insn, struct m98k_opcode *format, unsigned long parcnt, long max_width_zero) { int val; char *saveptr, save_c; expressionS exp; segT seg; saveptr = input_line_pointer; input_line_pointer = param; while(*param != ',' && *param != '\0') param++; save_c = *param; *param = '\0'; seg = expression(&exp); try_to_make_absolute(&exp); seg = exp.X_seg; *param = save_c; input_line_pointer = saveptr; val = exp.X_add_number; if(seg != SEG_ABSOLUTE){ error_param_message = "Parameter error: expression must be " "absolute (parameter %lu)"; return(NULL); } if(max_width_zero){ if(val == (1 << format->ops[parcnt].width)) val = 0; } if(val >= (1 << format->ops[parcnt].width) || val < 0){ error_param_message = "Parameter error: expression out " "of range (parameter %lu)"; return(NULL); } if(*param == '\0'){ if(parcnt == 4 || format->ops[parcnt+1].type == NONE){ insn->opcode |= val << format->ops[parcnt].offset; return(param); } else return(NULL); } else if(*param == ','){ if(parcnt != 4 && format->ops[parcnt+1].type != NONE){ insn->opcode |= val << format->ops[parcnt].offset; return(param+1); } else return(NULL); } return(NULL); } static char * parse_sh( char *param, struct m98k_insn *insn, struct m98k_opcode *format, unsigned long parcnt) { int val; char *saveptr, save_c; expressionS exp; segT seg; saveptr = input_line_pointer; input_line_pointer = param; while(*param != ',' && *param != '\0') param++; save_c = *param; *param = '\0'; seg = expression(&exp); try_to_make_absolute(&exp); seg = exp.X_seg; *param = save_c; input_line_pointer = saveptr; val = exp.X_add_number; if(seg != SEG_ABSOLUTE){ error_param_message = "Parameter error: expression must be " "absolute (parameter %lu)"; return(NULL); } if(val == 64) val = 0; if(val >= 64 || val < 0){ error_param_message = "Parameter error: expression out " "of range (parameter %lu)"; return(NULL); } if(*param == '\0'){ if(parcnt == 4 || format->ops[parcnt+1].type == NONE){ insn->opcode |= (val & 0x1f) << 11; insn->opcode |= ((val >> 5) & 0x1) << 1; return(param); } else return(NULL); } else if(*param == ','){ if(parcnt != 4 && format->ops[parcnt+1].type != NONE){ insn->opcode |= (val & 0x1f) << 11; insn->opcode |= ((val >> 5) & 0x1) << 1; return(param+1); } else return(NULL); } return(NULL); } static char * parse_mb( char *param, struct m98k_insn *insn, struct m98k_opcode *format, unsigned long parcnt) { int val; char *saveptr, save_c; expressionS exp; segT seg; saveptr = input_line_pointer; input_line_pointer = param; while(*param != ',' && *param != '\0') param++; save_c = *param; *param = '\0'; seg = expression(&exp); try_to_make_absolute(&exp); seg = exp.X_seg; *param = save_c; input_line_pointer = saveptr; val = exp.X_add_number; if(seg != SEG_ABSOLUTE){ error_param_message = "Parameter error: expression must be " "absolute (parameter %lu)"; return(NULL); } if(val > 64 || val < 0){ error_param_message = "Parameter error: expression out " "of range (parameter %lu)"; return(NULL); } if(*param == '\0'){ if(parcnt == 4 || format->ops[parcnt+1].type == NONE){ insn->opcode |= (val & 0x1f) << 6; insn->opcode |= ((val >> 5) & 0x1) << 5; return(param); } else return(NULL); } else if(*param == ','){ if(parcnt != 4 && format->ops[parcnt+1].type != NONE){ insn->opcode |= (val & 0x1f) << 6; insn->opcode |= ((val >> 5) & 0x1) << 5; return(param+1); } else return(NULL); } return(NULL); } /* * md_number_to_chars() is the target machine dependent routine that puts out * a binary value of size 4, 2, or 1 bytes into the specified buffer. This is * done in the target machine's byte sex. In this case the byte order is * big endian. */ void md_number_to_chars( char *buf, long val, int nbytes) { switch(nbytes){ case 4: *buf++ = val >> 24; *buf++ = val >> 16; case 2: *buf++ = val >> 8; case 1: *buf = val; break; default: abort(); } } /* * md_number_to_imm() is the target machine dependent routine that puts out * a binary value of size 4, 2, or 1 bytes into the specified buffer with * reguard to a possible relocation entry (the fixP->fx_r_type field in the fixS * structure pointed to by fixP) for the section with the ordinal nsect. This * is done in the target machine's byte sex using it's relocation types. * In this case the byte order is big endian. */ void md_number_to_imm( unsigned char *buf, long val, int nbytes, fixS *fixP, int nsect) { unsigned long opcode; if(fixP->fx_r_type == NO_RELOC || fixP->fx_r_type == M98K_RELOC_VANILLA){ switch(nbytes){ case 4: *buf++ = val >> 24; *buf++ = val >> 16; case 2: *buf++ = val >> 8; case 1: *buf = val; break; default: abort(); } return; } switch(fixP->fx_r_type){ case M98K_RELOC_HI16: buf[2] = val >> 24; buf[3] = val >> 16; break; case M98K_RELOC_LO16: buf[2] = val >> 8; buf[3] = val; break; case M98K_RELOC_HA16: val += 0x00008000; buf[2] = val >> 24; buf[3] = val >> 16; break; case M98K_RELOC_LO14: buf[2] = val >> 8; buf[3] = val & 0xfc; break; case M98K_RELOC_BR14: if((val & 0x00008000) != 0){ opcode = buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3]; if(((opcode) & 0x03e00000) != 0x02800000){ opcode ^= Y_BIT; buf[0] = opcode >> 24; buf[1] = opcode >> 16; buf[2] = opcode >> 8; buf[3] = opcode; } } if(fixP->fx_pcrel) val += 4; buf[2] = val >> 8; buf[3] |= val & 0xfc; break; case M98K_RELOC_BR24: if(fixP->fx_pcrel) val += 4; buf[0] |= (val >> 24) & 0x03; buf[1] = val >> 16; buf[2] = val >> 8; buf[3] |= val & 0xfc; break; default: as_warn("Bad relocation type"); break; } } /* * md_atof() turns a string pointed to by input_line_pointer into a floating * point constant of type type, and store the appropriate bytes in *litP. * The number of LITTLENUMS emitted is stored indirectly through *sizeP. * An error message is returned, or a string containg only a '\0' for OK. * For this machine only IEEE single and IEEE double floating-point formats * are allowed. */ char * md_atof( int type, char *litP, int *sizeP) { int prec; LITTLENUM_TYPE words[6]; LITTLENUM_TYPE *wordP; char *t; switch(type){ case 'f': case 'F': case 's': case 'S': prec = 2; break; case 'd': case 'D': case 'r': case 'R': prec = 4; break; default: *sizeP = 0; return("Bad call to MD_ATOF()"); } t = atof_ieee(input_line_pointer, type, words); if(t != NULL) input_line_pointer = t; *sizeP = prec * sizeof(LITTLENUM_TYPE); for(wordP = words; prec--; ){ md_number_to_chars(litP, (long)(*wordP++), sizeof(LITTLENUM_TYPE)); litP += sizeof(LITTLENUM_TYPE); } return ""; /* OK */ } int md_estimate_size_before_relax( fragS *fragP, int segment_type) { as_warn("Relaxation should never occur"); return(sizeof(long)); } const relax_typeS md_relax_table[] = {0}; void md_convert_frag( fragS *fragP) { as_warn("Relaxation should never occur"); }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.