From: CSRG Date: Fri, 26 Oct 1990 05:14:08 +0000 (-0800) Subject: BSD 4_3_Net_2 development X-Git-Tag: BSD-4_3_Net_2~392 X-Git-Url: https://git.subgeniuskitty.com/unix-history/.git/commitdiff_plain/c00408595297a91a9dd89be001a80d83e6aac4ca BSD 4_3_Net_2 development Work on file usr/src/usr.bin/gas/config/i860.c Work on file usr/src/usr.bin/gas/config/ns32k.c Synthesized-from: CSRG/cd2/net.2 --- diff --git a/usr/src/usr.bin/gas/config/i860.c b/usr/src/usr.bin/gas/config/i860.c new file mode 100644 index 0000000000..9666943569 --- /dev/null +++ b/usr/src/usr.bin/gas/config/i860.c @@ -0,0 +1,1142 @@ +/* i860.c -- Assemble for the I860 + Copyright (C) 1989 Free Software Foundation, Inc. + +This file is part of GAS, the GNU Assembler. + +GAS 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 1, or (at your option) +any later version. + +GAS 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 GAS; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + + +#include +#include + +#include "i860-opcode.h" +#include "as.h" +#include "frags.h" +#include "struc-symbol.h" +#include "flonum.h" +#include "expr.h" +#include "hash.h" +#include "md.h" +#include "i860.h" /* position dependent redefine */ +#include "write.h" +#include "read.h" +#include "symbols.h" + +void md_begin(); +void md_end(); +void md_number_to_chars(); +void md_assemble(); +char *md_atof(); +void md_convert_frag(); +void md_create_short_jump(); +void md_create_long_jump(); +int md_estimate_size_before_relax(); +void md_number_to_imm(); +void md_number_to_disp(); +void md_number_to_field(); +void md_ri_to_chars(); +static void i860_ip(); +void emit_relocations(); + +const relax_typeS md_relax_table[] = { 0 }; + +/* handle of the OPCODE hash table */ +static struct hash_control *op_hash = NULL; + +static void s_dual(), s_enddual(); +static void s_atmp(); + +const pseudo_typeS +md_pseudo_table[] = { + { "dual", s_dual, 4 }, + { "enddual", s_enddual, 4 }, + { "atmp", s_atmp, 4 }, + { NULL, 0, 0 }, +}; + +int md_short_jump_size = 4; +int md_long_jump_size = 4; +int omagic = OMAGIC; /* Magic number for header */ + +/* This array holds the chars that always start a comment. If the + pre-processor is disabled, these aren't very useful */ +char comment_chars[] = "!/"; /* JF removed '|' from comment_chars */ + +/* This array holds the chars that only start a comment at the beginning of + a line. If the line seems to have the form '# 123 filename' + .line and .file directives will appear in the pre-processed output */ +/* Note that input_file.c hand checks for '#' at the beginning of the + first line of the input file. This is because the compiler outputs + #NO_APP at the beginning of its output. */ +/* Also note that '/*' will always start a comment */ +char line_comment_chars[] = "#/"; + +/* Chars that can be used to separate mant from exp in floating point nums */ +char EXP_CHARS[] = "eE"; + +/* Chars that mean this number is a floating point constant */ +/* As in 0f12.456 */ +/* or 0d1.2345e12 */ +char FLT_CHARS[] = "rRsSfFdDxXpP"; + +/* Also be aware that MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT may have to be + changed in read.c . Ideally it shouldn't have to know about it at all, + but nothing is ideal around here. + */ +int size_reloc_info = sizeof(struct relocation_info); + +static unsigned char octal[256]; +#define isoctal(c) octal[c] +static unsigned char toHex[256]; + +struct i860_it { + char *error; + unsigned long opcode; + struct nlist *nlistp; + expressionS exp; + int pcrel; + enum expand_type expand; + enum highlow_type highlow; + enum reloc_type reloc; +} the_insn; + +#ifdef __STDC__ +static void print_insn(struct i860_it *insn); +static int getExpression(char *str); +#else +static void print_insn(); +static int getExpression(); +#endif +static char *expr_end; +static char last_expand; /* error if expansion after branch */ + +enum dual +{ + DUAL_OFF = 0, DUAL_ON, DUAL_DDOT, DUAL_ONDDOT, +}; +static enum dual dual_mode = DUAL_OFF; /* dual-instruction mode */ + +static void +s_dual() /* floating point instructions have dual set */ +{ + dual_mode = DUAL_ON; +} + +static void +s_enddual() /* floating point instructions have dual set */ +{ + dual_mode = DUAL_OFF; +} + +static int atmp = 31; /* temporary register for pseudo's */ + +static void +s_atmp() +{ + register int temp; + if (strncmp(input_line_pointer, "sp", 2) == 0) { + input_line_pointer += 2; + atmp = 2; + } + else if (strncmp(input_line_pointer, "fp", 2) == 0) { + input_line_pointer += 2; + atmp = 3; + } + else if (strncmp(input_line_pointer, "r", 1) == 0) { + input_line_pointer += 1; + temp = get_absolute_expression(); + if (temp >= 0 && temp <= 31) + atmp = temp; + else + as_warn("Unknown temporary pseudo register"); + } + else { + as_warn("Unknown temporary pseudo register"); + } + demand_empty_rest_of_line(); + return; +} + +/* This function is called once, at assembler startup time. It should + set up all the tables, etc. that the MD part of the assembler will need. */ +void +md_begin() +{ + register char *retval = NULL; + int lose = 0; + register unsigned int i = 0; + + op_hash = hash_new(); + if (op_hash == NULL) + as_fatal("Virtual memory exhausted"); + + while (i < NUMOPCODES) + { + const char *name = i860_opcodes[i].name; + retval = hash_insert(op_hash, name, &i860_opcodes[i]); + if(retval != NULL && *retval != '\0') + { + fprintf (stderr, "internal error: can't hash `%s': %s\n", + i860_opcodes[i].name, retval); + lose = 1; + } + do + { + if (i860_opcodes[i].match & i860_opcodes[i].lose) + { + fprintf (stderr, "internal error: losing opcode: `%s' \"%s\"\n", + i860_opcodes[i].name, i860_opcodes[i].args); + lose = 1; + } + ++i; + } while (i < NUMOPCODES + && !strcmp(i860_opcodes[i].name, name)); + } + + if (lose) + as_fatal ("Broken assembler. No assembly attempted."); + + for (i = '0'; i < '8'; ++i) + octal[i] = 1; + for (i = '0'; i <= '9'; ++i) + toHex[i] = i - '0'; + for (i = 'a'; i <= 'f'; ++i) + toHex[i] = i + 10 - 'a'; + for (i = 'A'; i <= 'F'; ++i) + toHex[i] = i + 10 - 'A'; +} + +void +md_end() +{ + return; +} + +void +md_assemble(str) + char *str; +{ + char *toP; + int rsd; + int no_opcodes = 1; + int i; + struct i860_it pseudo[3]; + + assert(str); + i860_ip(str); + + /* check for expandable flag to produce pseudo-instructions */ + if (the_insn.expand != 0 && the_insn.highlow == NO_SPEC) { + for (i = 0; i < 3; i++) + pseudo[i] = the_insn; + + switch (the_insn.expand) { + + case E_DELAY: + no_opcodes = 1; + break; + + case E_MOV: + if (the_insn.exp.X_add_symbol == NULL && + the_insn.exp.X_subtract_symbol == NULL && + (the_insn.exp.X_add_number < (1 << 15) && + the_insn.exp.X_add_number >= -(1 << 15))) + break; + /* or l%const,r0,ireg_dest */ + pseudo[0].opcode = (the_insn.opcode & 0x001f0000) | 0xe4000000; + pseudo[0].highlow = PAIR; + /* orh h%const,ireg_dest,ireg_dest */ + pseudo[1].opcode = (the_insn.opcode & 0x03ffffff) | 0xec000000 | + ((the_insn.opcode & 0x001f0000) << 5); + pseudo[1].highlow = HIGH; + no_opcodes = 2; + break; + + case E_ADDR: + if (the_insn.exp.X_add_symbol == NULL && + the_insn.exp.X_subtract_symbol == NULL) + break; + /* orh ha%addr_expr,r0,r31 */ + pseudo[0].opcode = 0xec000000 | (atmp<<16); + pseudo[0].highlow = HIGHADJ; + pseudo[0].reloc = LOW0; /* must overwrite */ + /* l%addr_expr(r31),ireg_dest */ + pseudo[1].opcode = (the_insn.opcode & ~0x003e0000) | (atmp << 21); + pseudo[1].highlow = PAIR; + no_opcodes = 2; + break; + + case E_U32: /* 2nd version emulates Intel as, not doc. */ + if (the_insn.exp.X_add_symbol == NULL && + the_insn.exp.X_subtract_symbol == NULL && + (the_insn.exp.X_add_number < (1 << 16) && + the_insn.exp.X_add_number >= 0)) + break; + /* $(opcode)h h%const,ireg_src2,ireg_dest + pseudo[0].opcode = (the_insn.opcode & 0xf3ffffff) | 0x0c000000; */ + /* $(opcode)h h%const,ireg_src2,r31 */ + pseudo[0].opcode = (the_insn.opcode & 0xf3e0ffff) | 0x0c000000 | + (atmp << 16); + pseudo[0].highlow = HIGH; + /* $(opcode) l%const,ireg_dest,ireg_dest + pseudo[1].opcode = (the_insn.opcode & 0xf01f0000) | 0x04000000 | + ((the_insn.opcode & 0x001f0000) << 5); */ + /* $(opcode) l%const,r31,ireg_dest */ + pseudo[1].opcode = (the_insn.opcode & 0xf01f0000) | 0x04000000 | + (atmp << 21); + pseudo[1].highlow = PAIR; + no_opcodes = 2; + break; + + case E_AND: /* 2nd version emulates Intel as, not doc. */ + if (the_insn.exp.X_add_symbol == NULL && + the_insn.exp.X_subtract_symbol == NULL && + (the_insn.exp.X_add_number < (1 << 16) && + the_insn.exp.X_add_number >= 0)) + break; + /* andnot h%const,ireg_src2,ireg_dest + pseudo[0].opcode = (the_insn.opcode & 0x03ffffff) | 0xd4000000; */ + /* andnot h%const,ireg_src2,r31 */ + pseudo[0].opcode = (the_insn.opcode & 0x03e0ffff) | 0xd4000000 | + (atmp << 16); + pseudo[0].highlow = HIGH; + pseudo[0].exp.X_add_number = -1 - the_insn.exp.X_add_number; + /* andnot l%const,ireg_dest,ireg_dest + pseudo[1].opcode = (the_insn.opcode & 0x001f0000) | 0xd4000000 | + ((the_insn.opcode & 0x001f0000) << 5); */ + /* andnot l%const,r31,ireg_dest */ + pseudo[1].opcode = (the_insn.opcode & 0x001f0000) | 0xd4000000 | + (atmp << 21); + pseudo[1].highlow = PAIR; + pseudo[1].exp.X_add_number = -1 - the_insn.exp.X_add_number; + no_opcodes = 2; + break; + + case E_S32: + if (the_insn.exp.X_add_symbol == NULL && + the_insn.exp.X_subtract_symbol == NULL && + (the_insn.exp.X_add_number < (1 << 15) && + the_insn.exp.X_add_number >= -(1 << 15))) + break; + /* orh h%const,r0,r31 */ + pseudo[0].opcode = 0xec000000 | (atmp << 16); + pseudo[0].highlow = HIGH; + /* or l%const,r31,r31 */ + pseudo[1].opcode = 0xe4000000 | (atmp << 21) | (atmp << 16); + pseudo[1].highlow = PAIR; + /* r31,ireg_src2,ireg_dest */ + pseudo[2].opcode = (the_insn.opcode & ~0x0400ffff) | (atmp << 11); + pseudo[2].reloc = NO_RELOC; + no_opcodes = 3; + break; + + default: + abort(); + } + + the_insn = pseudo[0]; + /* check for expanded opcode after branch or in dual */ + if (no_opcodes > 1 && last_expand == TRUE) + as_warn("Expanded opcode after delayed branch: `%s'", str); + if (no_opcodes > 1 && dual_mode != DUAL_OFF) + as_warn("Expanded opcode in dual mode: `%s'", str); + } + + i = 0; + do { /* always produce at least one opcode */ + toP = frag_more(4); + /* put out the opcode */ + md_number_to_chars(toP, the_insn.opcode, 4); + + /* check for expanded opcode after branch or in dual */ + last_expand = the_insn.pcrel; + + /* put out the symbol-dependent stuff */ + if (the_insn.reloc != NO_RELOC) { + fix_new( + frag_now, /* which frag */ + (toP - frag_now->fr_literal), /* where */ + 4, /* size */ + the_insn.exp.X_add_symbol, + the_insn.exp.X_subtract_symbol, + the_insn.exp.X_add_number, + the_insn.pcrel, + /* merge bit fields into one argument */ + (int)(((the_insn.highlow & 0x3) << 4) | (the_insn.reloc & 0xf)) + ); + } + the_insn = pseudo[++i]; + } while (--no_opcodes > 0); + +} + +static void +i860_ip(str) + char *str; +{ + char *s; + const char *args; + char c; + unsigned long i; + struct i860_opcode *insn; + char *argsStart; + unsigned long opcode; + unsigned int mask; + int match = FALSE; + int comma = 0; + + + for (s = str; islower(*s) || *s == '.' || *s == '3'; ++s) + ; + switch (*s) { + + case '\0': + break; + + case ',': + comma = 1; + + /*FALLTHROUGH*/ + + case ' ': + *s++ = '\0'; + break; + + default: + as_warn("Unknown opcode: `%s'", str); + exit(1); + } + + if (strncmp(str, "d.", 2) == 0) { /* check for d. opcode prefix */ + if (dual_mode == DUAL_ON) + dual_mode = DUAL_ONDDOT; + else + dual_mode = DUAL_DDOT; + str += 2; + } + + if ((insn = (struct i860_opcode *) hash_find(op_hash, str)) == NULL) { + if (dual_mode == DUAL_DDOT || dual_mode == DUAL_ONDDOT) + str -= 2; + as_warn("Unknown opcode: `%s'", str); + return; + } + if (comma) { + *--s = ','; + } + argsStart = s; + for (;;) { + opcode = insn->match; + bzero(&the_insn, sizeof(the_insn)); + the_insn.reloc = NO_RELOC; + + /* + * Build the opcode, checking as we go to make + * sure that the operands match + */ + for (args = insn->args; ; ++args) { + switch (*args) { + + case '\0': /* end of args */ + if (*s == '\0') { + match = TRUE; + } + break; + + case '+': + case '(': /* these must match exactly */ + case ')': + case ',': + case ' ': + if (*s++ == *args) + continue; + break; + + case '#': /* must be at least one digit */ + if (isdigit(*s++)) { + while (isdigit(*s)) { + ++s; + } + continue; + } + break; + + case '1': /* next operand must be a register */ + case '2': + case 'd': + switch (*s) { + + case 'f': /* frame pointer */ + s++; + if (*s++ == 'p') { + mask = 0x3; + break; + } + goto error; + + case 's': /* stack pointer */ + s++; + if (*s++ == 'p') { + mask= 0x2; + break; + } + goto error; + + case 'r': /* any register */ + s++; + if (!isdigit(c = *s++)) { + goto error; + } + if (isdigit(*s)) { + if ((c = 10 * (c - '0') + (*s++ - '0')) >= 32) { + goto error; + } + } else { + c -= '0'; + } + mask= c; + break; + + default: /* not this opcode */ + goto error; + } + /* + * Got the register, now figure out where + * it goes in the opcode. + */ + switch (*args) { + + case '1': + opcode |= mask << 11; + continue; + + case '2': + opcode |= mask << 21; + continue; + + case 'd': + opcode |= mask << 16; + continue; + + } + break; + + case 'e': /* next operand is a floating point register */ + case 'f': + case 'g': + if (*s++ == 'f' && isdigit(*s)) { + mask = *s++; + if (isdigit(*s)) { + mask = 10 * (mask - '0') + (*s++ - '0'); + if (mask >= 32) { + break; + } + } else { + mask -= '0'; + } + switch (*args) { + + case 'e': + opcode |= mask << 11; + continue; + + case 'f': + opcode |= mask << 21; + continue; + + case 'g': + opcode |= mask << 16; + if (dual_mode != DUAL_OFF) + opcode |= (1 << 9); /* dual mode instruction */ + if (dual_mode == DUAL_DDOT) + dual_mode = DUAL_OFF; + if (dual_mode == DUAL_ONDDOT) + dual_mode = DUAL_ON; + if ((opcode & (1 << 10)) && (mask == ((opcode >> 11) & 0x1f))) + as_warn("Fsr1 equals fdest with Pipelining"); + continue; + } + } + break; + + case 'c': /* next operand must be a control register */ + if (strncmp(s, "fir", 3) == 0) { + opcode |= 0x0 << 21; + s += 3; + continue; + } + if (strncmp(s, "psr", 3) == 0) { + opcode |= 0x1 << 21; + s += 3; + continue; + } + if (strncmp(s, "dirbase", 7) == 0) { + opcode |= 0x2 << 21; + s += 7; + continue; + } + if (strncmp(s, "db", 2) == 0) { + opcode |= 0x3 << 21; + s += 2; + continue; + } + if (strncmp(s, "fsr", 3) == 0) { + opcode |= 0x4 << 21; + s += 3; + continue; + } + if (strncmp(s, "epsr", 4) == 0) { + opcode |= 0x5 << 21; + s += 4; + continue; + } + break; + + case '5': /* 5 bit immediate in src1 */ + bzero(&the_insn, sizeof(the_insn)); + if ( !getExpression(s)) { + s = expr_end; + if (the_insn.exp.X_add_number & ~0x1f) + as_warn("5-bit immediate too large"); + opcode |= (the_insn.exp.X_add_number & 0x1f) << 11; + bzero(&the_insn, sizeof(the_insn)); + the_insn.reloc = NO_RELOC; + continue; + } + break; + + case 'l': /* 26 bit immediate, relative branch */ + the_insn.reloc = BRADDR; + the_insn.pcrel = TRUE; + goto immediate; + + case 's': /* 16 bit immediate, split relative branch */ + /* upper 5 bits of offset in dest field */ + the_insn.pcrel = TRUE; + the_insn.reloc = SPLIT0; + goto immediate; + + case 'S': /* 16 bit immediate, split (st), aligned */ + if (opcode & (1 << 28)) + if (opcode & 0x1) + the_insn.reloc = SPLIT2; + else + the_insn.reloc = SPLIT1; + else + the_insn.reloc = SPLIT0; + goto immediate; + + case 'I': /* 16 bit immediate, aligned */ + if (opcode & (1 << 28)) + if (opcode & 0x1) + the_insn.reloc = LOW2; + else + the_insn.reloc = LOW1; + else + the_insn.reloc = LOW0; + goto immediate; + + case 'i': /* 16 bit immediate */ + the_insn.reloc = LOW0; + + /*FALLTHROUGH*/ + + immediate: + if(*s==' ') + s++; + if (strncmp(s, "ha%", 3) == 0) { + the_insn.highlow = HIGHADJ; + s += 3; + } else if (strncmp(s, "h%", 2) == 0) { + the_insn.highlow = HIGH; + s += 2; + } else if (strncmp(s, "l%", 2) == 0) { + the_insn.highlow = PAIR; + s += 2; + } + the_insn.expand = insn->expand; + + /* Note that if the getExpression() fails, we will still have + created U entries in the symbol table for the 'symbols' + in the input string. Try not to create U symbols for + registers, etc. */ + + if ( !getExpression(s)) { + s = expr_end; + continue; + } + break; + + default: + abort(); + } + break; + } + error: + if (match == FALSE) + { + /* Args don't match. */ + if (&insn[1] - i860_opcodes < NUMOPCODES + && !strcmp(insn->name, insn[1].name)) + { + ++insn; + s = argsStart; + continue; + } + else + { + as_warn("Illegal operands"); + return; + } + } + break; + } + + the_insn.opcode = opcode; + return; +} + +static int +getExpression(str) + char *str; +{ + char *save_in; + segT seg; + + save_in = input_line_pointer; + input_line_pointer = str; + switch (seg = expression(&the_insn.exp)) { + + case SEG_ABSOLUTE: + case SEG_TEXT: + case SEG_DATA: + case SEG_BSS: + case SEG_UNKNOWN: + case SEG_DIFFERENCE: + case SEG_BIG: + case SEG_NONE: + break; + + default: + the_insn.error = "bad segment"; + expr_end = input_line_pointer; + input_line_pointer=save_in; + return 1; + } + expr_end = input_line_pointer; + input_line_pointer = save_in; + return 0; +} + + +/* + This is identical to the md_atof in m68k.c. I think this is right, + but I'm not sure. + + Turn a string in 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 in *sizeP . An error message is returned, or NULL on OK. + */ + +/* Equal to MAX_PRECISION in atof-ieee.c */ +#define MAX_LITTLENUMS 6 + +char * +md_atof(type,litP,sizeP) + char type; + char *litP; + int *sizeP; +{ + int prec; + LITTLENUM_TYPE words[MAX_LITTLENUMS]; + LITTLENUM_TYPE *wordP; + char *t; + char *atof_ieee(); + + switch(type) { + + case 'f': + case 'F': + case 's': + case 'S': + prec = 2; + break; + + case 'd': + case 'D': + case 'r': + case 'R': + prec = 4; + break; + + case 'x': + case 'X': + prec = 6; + break; + + case 'p': + case 'P': + prec = 6; + break; + + default: + *sizeP=0; + return "Bad call to MD_ATOF()"; + } + t=atof_ieee(input_line_pointer,type,words); + if(t) + 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 ""; /* Someone should teach Dean about null pointers */ +} + +/* + * Write out big-endian. + */ +void +md_number_to_chars(buf,val,n) + char *buf; + long val; + int n; +{ + switch(n) { + + case 4: + *buf++ = val >> 24; + *buf++ = val >> 16; + case 2: + *buf++ = val >> 8; + case 1: + *buf = val; + break; + + default: + abort(); + } + return; +} + +void +md_number_to_imm(buf,val,n, fixP, seg_type) + char *buf; + long val; + int n; + fixS *fixP; + int seg_type; +{ + enum reloc_type reloc = fixP->fx_r_type & 0xf; + enum highlow_type highlow = (fixP->fx_r_type >> 4) & 0x3; + + assert(buf); + assert(n == 4); /* always on i860 */ + + switch(highlow) + { + + case HIGHADJ: /* adjusts the high-order 16-bits */ + if (val & (1 << 15)) + val += (1 << 16); + + /*FALLTHROUGH*/ + + case HIGH: /* selects the high-order 16-bits */ + val >>= 16; + break; + + case PAIR: /* selects the low-order 16-bits */ + val = val & 0xffff; + break; + + default: + break; + } + + switch(reloc) + { + + case BRADDR: /* br,call,bc,bc.t,bnc,bnc.t w/26-bit immediate */ + if (fixP->fx_pcrel != TRUE) + as_warn("26-bit branch w/o pc relative set: 0x%08x", val); + val >>= 2; /* align pcrel offset, see manual */ + + if (val >= (1 << 25) || val < -(1 << 25)) /* check for overflow */ + as_warn("26-bit branch offset overflow: 0x%08x", val); + buf[0] = (buf[0] & 0xfc) | ((val >> 24) & 0x3); + buf[1] = val >> 16; + buf[2] = val >> 8; + buf[3] = val; + break; + + case SPLIT2: /* 16 bit immediate, 4-byte aligned */ + if (val & 0x3) + as_warn("16-bit immediate 4-byte alignment error: 0x%08x", val); + val &= ~0x3; /* 4-byte align value */ + /*FALLTHROUGH*/ + case SPLIT1: /* 16 bit immediate, 2-byte aligned */ + if (val & 0x1) + as_warn("16-bit immediate 2-byte alignment error: 0x%08x", val); + val &= ~0x1; /* 2-byte align value */ + /*FALLTHROUGH*/ + case SPLIT0: /* st,bla,bte,btne w/16-bit immediate */ + if (fixP->fx_pcrel == TRUE) + val >>= 2; /* align pcrel offset, see manual */ + /* check for bounds */ + if (highlow != PAIR && (val >= (1 << 16) || val < -(1 << 15))) + as_warn("16-bit branch offset overflow: 0x%08x", val); + buf[1] = (buf[1] & ~0x1f) | ((val >> 11) & 0x1f); + buf[2] = (buf[2] & ~0x7) | ((val >> 8) & 0x7); + buf[3] |= val; /* perserve bottom opcode bits */ + break; + + case LOW4: /* fld,pfld,pst,flush 16-byte aligned */ + if (val & 0xf) + as_warn("16-bit immediate 16-byte alignment error: 0x%08x", val); + val &= ~0xf; /* 16-byte align value */ + /*FALLTHROUGH*/ + case LOW3: /* fld,pfld,pst,flush 8-byte aligned */ + if (val & 0x7) + as_warn("16-bit immediate 8-byte alignment error: 0x%08x", val); + val &= ~0x7; /* 8-byte align value */ + /*FALLTHROUGH*/ + case LOW2: /* 16 bit immediate, 4-byte aligned */ + if (val & 0x3) + as_warn("16-bit immediate 4-byte alignment error: 0x%08x", val); + val &= ~0x3; /* 4-byte align value */ + /*FALLTHROUGH*/ + case LOW1: /* 16 bit immediate, 2-byte aligned */ + if (val & 0x1) + as_warn("16-bit immediate 2-byte alignment error: 0x%08x", val); + val &= ~0x1; /* 2-byte align value */ + /*FALLTHROUGH*/ + case LOW0: /* 16 bit immediate, byte aligned */ + /* check for bounds */ + if (highlow != PAIR && (val >= (1 << 16) || val < -(1 << 15))) + as_warn("16-bit immediate overflow: 0x%08x", val); + buf[2] = val >> 8; + buf[3] |= val; /* perserve bottom opcode bits */ + break; + + case NO_RELOC: + default: + as_warn("bad relocation type: 0x%02x", reloc); + break; + } + return; +} + +/* should never be called for i860 */ +void +md_create_short_jump(ptr, from_addr, to_addr, frag, to_symbol) + char *ptr; + long from_addr, to_addr; +{ + fprintf(stderr, "i860_create_short_jmp\n"); + abort(); +} + +/* should never be called for i860 */ +void +md_number_to_disp(buf,val,n) + char *buf; + long val; +{ + fprintf(stderr, "md_number_to_disp\n"); + abort(); +} + +/* should never be called for i860 */ +void +md_number_to_field(buf,val,fix) + char *buf; + long val; + void *fix; +{ + fprintf(stderr, "i860_number_to_field\n"); + abort(); +} + +/* the bit-field entries in the relocation_info struct plays hell + with the byte-order problems of cross-assembly. So as a hack, + I added this mach. dependent ri twiddler. Ugly, but it gets + you there. -KWK */ +/* on i860: first 4 bytes are normal unsigned long address, next three + bytes are index, most sig. byte first. Byte 7 is broken up with + bit 7 as pcrel, bit 6 as extern, and the lower six bits as + relocation type (highlow 5-4). Next 4 bytes are long int addend. */ +/* Thanx and a tip of the hat to Michael Bloom, mb@ttidca.tti.com */ +void +md_ri_to_chars(ri_p, ri) + struct relocation_info *ri_p, ri; +{ +#if 0 + unsigned char the_bytes[sizeof(*ri_p)]; + + /* this is easy */ + md_number_to_chars(the_bytes, ri.r_address, sizeof(ri.r_address)); + /* now the fun stuff */ + the_bytes[4] = (ri.r_index >> 16) & 0x0ff; + the_bytes[5] = (ri.r_index >> 8) & 0x0ff; + the_bytes[6] = ri.r_index & 0x0ff; + the_bytes[7] = ((ri.r_extern << 7) & 0x80) | (0 & 0x60) | (ri.r_type & 0x1F); + /* Also easy */ + md_number_to_chars(&the_bytes[8], ri.r_addend, sizeof(ri.r_addend)); + /* now put it back where you found it, Junior... */ + bcopy (the_bytes, (char *)ri_p, sizeof(*ri_p)); +#endif +} + +/* should never be called for i860 */ +void +md_convert_frag(fragP) + register fragS *fragP; +{ + fprintf(stderr, "i860_convert_frag\n"); + abort(); +} + +/* should never be called for i860 */ +void +md_create_long_jump(ptr, from_addr, to_addr, frag, to_symbol) + char *ptr; + long from_addr, + to_addr; + fragS *frag; + symbolS *to_symbol; +{ + fprintf(stderr, "i860_create_long_jump\n"); + abort(); +} + +/* should never be called for i860 */ +int +md_estimate_size_before_relax(fragP, segtype) + register fragS *fragP; +{ + fprintf(stderr, "i860_estimate_size_before_relax\n"); + abort(); + return 0; +} + +/* for debugging only, must match enum reloc_type */ +static char *Reloc[] = { + "NO_RELOC", + "BRADDR", + "LOW0", + "LOW1", + "LOW2", + "LOW3", + "LOW4", + "SPLIT0", + "SPLIT1", + "SPLIT2", + "RELOC_32", +}; +static char *Highlow[] = { + "NO_SPEC", + "PAIR", + "HIGH", + "HIGHADJ", +}; +static void +print_insn(insn) + struct i860_it *insn; +{ + if (insn->error) { + fprintf(stderr, "ERROR: %s\n"); + } + fprintf(stderr, "opcode=0x%08x\t", insn->opcode); + fprintf(stderr, "expand=0x%08x\t", insn->expand); + fprintf(stderr, "reloc = %s\t", Reloc[insn->reloc]); + fprintf(stderr, "highlow = %s\n", Highlow[insn->highlow]); + fprintf(stderr, "exp = {\n"); + fprintf(stderr, "\t\tX_add_symbol = %s\n", + insn->exp.X_add_symbol ? + (insn->exp.X_add_symbol->sy_name ? + insn->exp.X_add_symbol->sy_name : "???") : "0"); + fprintf(stderr, "\t\tX_sub_symbol = %s\n", + insn->exp.X_subtract_symbol ? + (insn->exp.X_subtract_symbol->sy_name ? + insn->exp.X_subtract_symbol->sy_name : "???") : "0"); + fprintf(stderr, "\t\tX_add_number = %d\n", + insn->exp.X_add_number); + fprintf(stderr, "}\n"); + return; +} + +int +md_parse_option(argP,cntP,vecP) + char **argP; + int *cntP; + char ***vecP; +{ + return 1; +} + +/* + * I860 relocations are completely different, so it needs + * this machine dependent routine to emit them. + */ +void +emit_relocations(fixP, segment_address_in_file) + register fixS *fixP; + relax_addressT segment_address_in_file; +{ + struct reloc_info_i860 ri; + register symbolS *symbolP; + extern char *next_object_file_charP; + long add_number; + + bzero((char *) &ri, sizeof(ri)); + for (; fixP; fixP = fixP->fx_next) { + + if (fixP->fx_r_type & ~0x3f) { + fprintf(stderr, "fixP->fx_r_type = %d\n", fixP->fx_r_type); + abort(); + } + ri.r_pcrel = fixP->fx_pcrel; + ri.r_type = fixP->fx_r_type; + + if ((symbolP = fixP->fx_addsy) != NULL) { + ri.r_address = fixP->fx_frag->fr_address + + fixP->fx_where - segment_address_in_file; + if ((symbolP->sy_type & N_TYPE) == N_UNDF) { + ri.r_extern = 1; + ri.r_symbolnum = symbolP->sy_number; + } else { + ri.r_extern = 0; + ri.r_symbolnum = symbolP->sy_type & N_TYPE; + } + if (symbolP && symbolP->sy_frag) { + ri.r_addend = symbolP->sy_frag->fr_address; + } + ri.r_type = fixP->fx_r_type; + if (fixP->fx_pcrel) { + /* preserve actual offset vs. pc + 4 */ + ri.r_addend -= (ri.r_address + 4); + } else { + ri.r_addend = fixP->fx_addnumber; + } + + md_ri_to_chars((char *) &ri, ri); + append(&next_object_file_charP, (char *)& ri, sizeof(ri)); + } + } + return; +} + diff --git a/usr/src/usr.bin/gas/config/ns32k.c b/usr/src/usr.bin/gas/config/ns32k.c new file mode 100644 index 0000000000..8db917b243 --- /dev/null +++ b/usr/src/usr.bin/gas/config/ns32k.c @@ -0,0 +1,1768 @@ +/* ns32k.c -- Assemble on the National Semiconductor 32k series + Copyright (C) 1987 Free Software Foundation, Inc. + +This file is part of GAS, the GNU Assembler. + +GAS 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 1, or (at your option) +any later version. + +GAS 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 GAS; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#define NS32K +/*#define SHOW_NUM 1*/ /* uncomment for debugging */ + +#include +#include +#ifdef USG +#include +#else +#include +#endif +#include "ns32k-opcode.h" +#include "as.h" +#include "obstack.h" +#include "frags.h" +#include "struc-symbol.h" +#include "flonum.h" +#include "expr.h" +#include "md.h" +#include "hash.h" +#include "write.h" +#include "symbols.h" + +/* Macros */ +#define IIF_ENTRIES 13 /* number of entries in iif */ +#define PRIVATE_SIZE 256 /* size of my garbage memory */ +#define MAX_ARGS 4 +#define DEFAULT -1 /* addr_mode returns this value when plain constant or label is encountered */ + +#define IIF(ptr,a1,c1,e1,g1,i1,k1,m1,o1,q1,s1,u1) \ + iif.iifP[ptr].type= a1; \ + iif.iifP[ptr].size= c1; \ + iif.iifP[ptr].object= e1; \ + iif.iifP[ptr].object_adjust= g1; \ + iif.iifP[ptr].pcrel= i1; \ + iif.iifP[ptr].pcrel_adjust= k1; \ + iif.iifP[ptr].im_disp= m1; \ + iif.iifP[ptr].relax_substate= o1; \ + iif.iifP[ptr].bit_fixP= q1; \ + iif.iifP[ptr].addr_mode= s1; \ + iif.iifP[ptr].bsr= u1; + +#ifdef SEQUENT_COMPATABILITY +#define LINE_COMMENT_CHARS "|" +#define ABSOLUTE_PREFIX '@' +#define IMMEDIATE_PREFIX '#' +#endif + +#ifndef LINE_COMMENT_CHARS +#define LINE_COMMENT_CHARS "#" +#endif + +char comment_chars[] = "#"; +char line_comment_chars[] = LINE_COMMENT_CHARS; +#if !defined(ABSOLUTE_PREFIX) && !defined(IMMEDIATE_PREFIX) +#define ABSOLUTE_PREFIX '@' /* One or the other MUST be defined */ +#endif + +struct addr_mode { + char mode; /* addressing mode of operand (0-31) */ + char scaled_mode; /* mode combined with scaled mode */ + char scaled_reg; /* register used in scaled+1 (1-8) */ + char float_flag; /* set if R0..R7 was F0..F7 ie a floating-point-register */ + char am_size; /* estimated max size of general addr-mode parts*/ + char im_disp; /* if im_disp==1 we have a displacement */ + char pcrel; /* 1 if pcrel, this is really redundant info */ + char disp_suffix[2]; /* length of displacement(s), 0=undefined */ + char *disp[2]; /* pointer(s) at displacement(s) + or immediates(s) (ascii) */ + char index_byte; /* index byte */ +}; +typedef struct addr_mode addr_modeS; + + +char *freeptr,*freeptr_static; /* points at some number of free bytes */ +struct hash_control *inst_hash_handle; + +struct ns32k_opcode *desc; /* pointer at description of instruction */ +addr_modeS addr_modeP; +char EXP_CHARS[] = "eE"; +char FLT_CHARS[] = "fd"; /* we don't want to support lowercase, do we */ +long omagic = OMAGIC; +void md_number_to_disp(); +void md_number_to_imm(); +segT evaluate_expr(); +void fix_new_ns32k(); + +/* UPPERCASE denotes live names + * when an instruction is built, IIF is used as an intermidiate form to store + * the actual parts of the instruction. A ns32k machine instruction can + * be divided into a couple of sub PARTs. When an instruction is assembled + * the appropriate PART get an assignment. When an IIF has been completed it's + * converted to a FRAGment as specified in AS.H */ + +/* internal structs */ +struct option { + char *pattern; + unsigned long or; + unsigned long and; +}; + +typedef struct { + int type; /* how to interpret object */ + int size; /* Estimated max size of object */ + unsigned long object; /* binary data */ + int object_adjust; /* number added to object */ + int pcrel; /* True if object is pcrel */ + int pcrel_adjust; /* It's value reflects the length in bytes from the instruction start to the displacement */ + int im_disp; /* True if the object is a displacement */ + relax_substateT relax_substate; /* Initial relaxsubstate */ + bit_fixS *bit_fixP; /* Pointer at bit_fix struct */ + int addr_mode; /* What addrmode do we associate with this iif-entry */ + char bsr; /* Sequent hack */ +}iif_entryT; /* Internal Instruction Format */ +struct int_ins_form { + int instr_size; /* Max size of instruction in bytes. */ + iif_entryT iifP[IIF_ENTRIES+1]; +}; +struct int_ins_form iif; +expressionS exprP; +char *input_line_pointer; +/* description of the PARTs in IIF + *object[n]: + * 0 total length in bytes of entries in iif + * 1 opcode + * 2 index_byte_a + * 3 index_byte_b + * 4 disp_a_1 + * 5 disp_a_2 + * 6 disp_b_1 + * 7 disp_b_2 + * 8 imm_a + * 9 imm_b + * 10 implied1 + * 11 implied2 + * + * For every entry there is a datalength in bytes. This is stored in size[n]. + * 0, the objectlength is not explicitly given by the instruction + * and the operand is undefined. This is a case for relaxation. + * Reserve 4 bytes for the final object. + * + * 1, the entry contains one byte + * 2, the entry contains two bytes + * 3, the entry contains three bytes + * 4, the entry contains four bytes + * etc + * + * Furthermore, every entry has a data type identifier in type[n]. + * + * 0, the entry is void, ignore it. + * 1, the entry is a binary number. + * 2, the entry is a pointer at an expression. + * Where expression may be as simple as a single '1', + * and as complicated as foo-bar+12, + * foo and bar may be undefined but suffixed by :{b|w|d} to + * control the length of the object. + * + * 3, the entry is a pointer at a bignum struct + * + * + * The low-order-byte coresponds to low physical memory. + * Obviously a FRAGment must be created for each valid disp in PART whose + * datalength is undefined (to bad) . + * The case where just the expression is undefined is less severe and is + * handled by fix. Here the number of bytes in the objectfile is known. + * With this representation we simplify the assembly and separates the + * machine dependent/independent parts in a more clean way (said OE) + */ + +struct option opt1[]= /* restore, exit */ +{ + { "r0", 0x80, 0xff }, + { "r1", 0x40, 0xff }, + { "r2", 0x20, 0xff }, + { "r3", 0x10, 0xff }, + { "r4", 0x08, 0xff }, + { "r5", 0x04, 0xff }, + { "r6", 0x02, 0xff }, + { "r7", 0x01, 0xff }, + { 0 , 0x00, 0xff } +}; +struct option opt2[]= /* save, enter */ +{ + { "r0", 0x01, 0xff }, + { "r1", 0x02, 0xff }, + { "r2", 0x04, 0xff }, + { "r3", 0x08, 0xff }, + { "r4", 0x10, 0xff }, + { "r5", 0x20, 0xff }, + { "r6", 0x40, 0xff }, + { "r7", 0x80, 0xff }, + { 0 , 0x00, 0xff } +}; +struct option opt3[]= /* setcfg */ +{ + { "c", 0x8, 0xff }, + { "m", 0x4, 0xff }, + { "f", 0x2, 0xff }, + { "i", 0x1, 0xff }, + { 0 , 0x0, 0xff } +}; +struct option opt4[]= /* cinv */ +{ + { "a", 0x4, 0xff }, + { "i", 0x2, 0xff }, + { "d", 0x1, 0xff }, + { 0 , 0x0, 0xff } +}; +struct option opt5[]= /* string inst */ +{ + { "b", 0x2, 0xff }, + { "u", 0xc, 0xff }, + { "w", 0x4, 0xff }, + { 0 , 0x0, 0xff } +}; +struct option opt6[]= /* plain reg ext,cvtp etc */ +{ + { "r0", 0x00, 0xff }, + { "r1", 0x01, 0xff }, + { "r2", 0x02, 0xff }, + { "r3", 0x03, 0xff }, + { "r4", 0x04, 0xff }, + { "r5", 0x05, 0xff }, + { "r6", 0x06, 0xff }, + { "r7", 0x07, 0xff }, + { 0 , 0x00, 0xff } +}; + +#if !defined(NS32032) && !defined(NS32532) +#define NS32032 +#endif + +struct option cpureg_532[]= /* lpr spr */ +{ + { "us", 0x0, 0xff }, + { "dcr", 0x1, 0xff }, + { "bpc", 0x2, 0xff }, + { "dsr", 0x3, 0xff }, + { "car", 0x4, 0xff }, + { "fp", 0x8, 0xff }, + { "sp", 0x9, 0xff }, + { "sb", 0xa, 0xff }, + { "usp", 0xb, 0xff }, + { "cfg", 0xc, 0xff }, + { "psr", 0xd, 0xff }, + { "intbase", 0xe, 0xff }, + { "mod", 0xf, 0xff }, + { 0 , 0x00, 0xff } +}; +struct option mmureg_532[]= /* lmr smr */ +{ + { "mcr", 0x9, 0xff }, + { "msr", 0xa, 0xff }, + { "tear", 0xb, 0xff }, + { "ptb0", 0xc, 0xff }, + { "ptb1", 0xd, 0xff }, + { "ivar0", 0xe, 0xff }, + { "ivar1", 0xf, 0xff }, + { 0 , 0x0, 0xff } +}; + +struct option cpureg_032[]= /* lpr spr */ +{ + { "upsr", 0x0, 0xff }, + { "fp", 0x8, 0xff }, + { "sp", 0x9, 0xff }, + { "sb", 0xa, 0xff }, + { "psr", 0xd, 0xff }, + { "intbase", 0xe, 0xff }, + { "mod", 0xf, 0xff }, + { 0 , 0x0, 0xff } +}; +struct option mmureg_032[]= /* lmr smr */ +{ + { "bpr0", 0x0, 0xff }, + { "bpr1", 0x1, 0xff }, + { "pf0", 0x4, 0xff }, + { "pf1", 0x5, 0xff }, + { "sc", 0x8, 0xff }, + { "msr", 0xa, 0xff }, + { "bcnt", 0xb, 0xff }, + { "ptb0", 0xc, 0xff }, + { "ptb1", 0xd, 0xff }, + { "eia", 0xf, 0xff }, + { 0 , 0x0, 0xff } +}; + +#if defined(NS32532) +struct option *cpureg = cpureg_532; +struct option *mmureg = mmureg_532; +#else +struct option *cpureg = cpureg_032; +struct option *mmureg = mmureg_032; +#endif + + +const pseudo_typeS md_pseudo_table[]={ /* so far empty */ + { 0, 0, 0 } +}; + +#define IND(x,y) (((x)<<2)+(y)) + +/* those are index's to relax groups in md_relax_table + ie it must be multiplied by 4 to point at a group start. Viz IND(x,y) + Se function relax_segment in write.c for more info */ + +#define BRANCH 1 +#define PCREL 2 + +/* those are index's to entries in a relax group */ + +#define BYTE 0 +#define WORD 1 +#define DOUBLE 2 +#define UNDEF 3 +/* Those limits are calculated from the displacement start in memory. + The ns32k uses the begining of the instruction as displacement base. + This type of displacements could be handled here by moving the limit window + up or down. I choose to use an internal displacement base-adjust as there + are other routines that must consider this. Also, as we have two various + offset-adjusts in the ns32k (acb versus br/brs/jsr/bcond), two set of limits + would have had to be used. + Now we dont have to think about that. */ + + +const relax_typeS md_relax_table[]={ + { 1, 1, 0, 0 }, + { 1, 1, 0, 0 }, + { 1, 1, 0, 0 }, + { 1, 1, 0, 0 }, + + { (63), (-64), 1, IND(BRANCH,WORD) }, + { (8192), (-8192), 2, IND(BRANCH,DOUBLE) }, + { 0, 0, 4, 0 }, + { 1, 1, 0, 0 } +}; + +/* Array used to test if mode contains displacements. + Value is true if mode contains displacement. */ + +char disp_test[]={ 0,0,0,0,0,0,0,0, + 1,1,1,1,1,1,1,1, + 1,1,1,0,0,1,1,0, + 1,1,1,1,1,1,1,1 }; + +/* Array used to calculate max size of displacements */ + +char disp_size[]={ 4,1,2,0,4 }; + +/* Parses a general operand into an addressingmode struct + + in: pointer at operand in ascii form + pointer at addr_mode struct for result + the level of recursion. (always 0 or 1) + + out: data in addr_mode struct + */ +int addr_mode(operand,addr_modeP,recursive_level) + char *operand; + register addr_modeS *addr_modeP; +int recursive_level; +{ + register char *str; + register int i; + register int strl; + register int mode; + int j; + mode = DEFAULT; /* default */ + addr_modeP->scaled_mode=0; /* why not */ + addr_modeP->scaled_reg=0; /* if 0, not scaled index */ + addr_modeP->float_flag=0; + addr_modeP->am_size=0; + addr_modeP->im_disp=0; + addr_modeP->pcrel=0; /* not set in this function */ + addr_modeP->disp_suffix[0]=0; + addr_modeP->disp_suffix[1]=0; + addr_modeP->disp[0]=NULL; + addr_modeP->disp[1]=NULL; + str=operand; + if (str[0]==0) {return (0);} /* we don't want this */ + strl=strlen(str); + switch (str[0]) { + /* the following three case statements controls the mode-chars + this is the place to ed if you want to change them */ +#ifdef ABSOLUTE_PREFIX + case ABSOLUTE_PREFIX: + if (str[strl-1]==']') break; + addr_modeP->mode=21; /* absolute */ + addr_modeP->disp[0]=str+1; + return (-1); +#endif +#ifdef IMMEDIATE_PREFIX + case IMMEDIATE_PREFIX: + if (str[strl-1]==']') break; + addr_modeP->mode=20; /* immediate */ + addr_modeP->disp[0]=str+1; + return (-1); +#endif + case '.': + if (str[strl-1]!=']') { + switch (str[1]) { + case'-':case'+': + if (str[2]!='\000') { + addr_modeP->mode=27; /* pc-relativ */ + addr_modeP->disp[0]=str+2; + return (-1); + } + default: + as_warn("Invalid syntax in PC-relative addressing mode"); + return(0); + } + } + break; + case'e': + if (str[strl-1]!=']') { + if((!strncmp(str,"ext(",4)) && strl>7) { /* external */ + addr_modeP->disp[0]=str+4; + i=0; + j=2; + do { /* disp[0]'s termination point */ + j+=1; + if (str[j]=='(') i++; + if (str[j]==')') i--; + } while (jdisp[1]=str+j+2; + addr_modeP->mode=22; + return (-1); + } + } + break; + default:; + } + strl=strlen(str); + switch(strl) { + case 2: + switch (str[0]) { + case'f':addr_modeP->float_flag=1; + case'r': + if (str[1]>='0' && str[1]<'8') { + addr_modeP->mode=str[1]-'0'; + return (-1); + } + } + case 3: + if (!strncmp(str,"tos",3)) { + addr_modeP->mode=23; /* TopOfStack */ + return (-1); + } + default:; + } + if (strl>4) { + if (str[strl-1]==')') { + if (str[strl-2]==')') { + if (!strncmp(&str[strl-5],"(fp",3)) { + mode=16; /* Memory Relative */ + } + if (!strncmp(&str[strl-5],"(sp",3)) { + mode=17; + } + if (!strncmp(&str[strl-5],"(sb",3)) { + mode=18; + } + if (mode!=DEFAULT) { /* memory relative */ + addr_modeP->mode=mode; + j=strl-5; /* temp for end of disp[0] */ + i=0; + do { + strl-=1; + if (str[strl]==')') i++; + if (str[strl]=='(') i--; + } while (strl>-1 && i!=0); + if (i!=0) { + as_warn("Invalid syntax in Memory Relative addressing mode"); + return(0); + } + addr_modeP->disp[1]=str; + addr_modeP->disp[0]=str+strl+1; + str[j]='\000'; /* null terminate disp[0] */ + str[strl]='\000'; /* null terminate disp[1] */ + return (-1); + } + } + switch (str[strl-3]) { + case'r':case'R': + if (str[strl-2]>='0' && str[strl-2]<'8' && str[strl-4]=='(') { + addr_modeP->mode=str[strl-2]-'0'+8; + addr_modeP->disp[0]=str; + str[strl-4]=0; + return (-1); /* reg rel */ + } + default: + if (!strncmp(&str[strl-4],"(fp",3)) { + mode=24; + } + if (!strncmp(&str[strl-4],"(sp",3)) { + mode=25; + } + if (!strncmp(&str[strl-4],"(sb",3)) { + mode=26; + } + if (!strncmp(&str[strl-4],"(pc",3)) { + mode=27; + } + if (mode!=DEFAULT) { + addr_modeP->mode=mode; + addr_modeP->disp[0]=str; + str[strl-4]='\0'; + return (-1); /* memory space */ + } + } + } + /* no trailing ')' do we have a ']' ? */ + if (str[strl-1]==']') { + switch (str[strl-2]) { + case'b':mode=28;break; + case'w':mode=29;break; + case'd':mode=30;break; + case'q':mode=31;break; + default:; + as_warn("Invalid scaled-indexed mode, use (b,w,d,q)"); + if (str[strl-3]!=':' || str[strl-6]!='[' || + str[strl-5]=='r' || str[strl-4]<'0' || str[strl-4]>'7') { + as_warn("Syntax in scaled-indexed mode, use [Rn:m] where n=[0..7] m={b,w,d,q}"); + } + } /* scaled index */ + { + if (recursive_level>0) { + as_warn("Scaled-indexed addressing mode combined with scaled-index"); + return(0); + } + addr_modeP->am_size+=1; /* scaled index byte */ + j=str[strl-4]-'0'; /* store temporary */ + str[strl-6]='\000'; /* nullterminate for recursive call */ + i=addr_mode(str,addr_modeP,1); + if (!i || addr_modeP->mode==20) { + as_warn("Invalid or illegal addressing mode combined with scaled-index"); + return(0); + } + addr_modeP->scaled_mode=addr_modeP->mode; /* store the inferior mode */ + addr_modeP->mode=mode; + addr_modeP->scaled_reg=j+1; + return (-1); + } + } + } + addr_modeP->mode = DEFAULT; /* default to whatever */ + addr_modeP->disp[0]=str; + return (-1); +} + +/* ptr points at string + addr_modeP points at struct with result + This routine calls addr_mode to determine the general addr.mode of + the operand. When this is ready it parses the displacements for size + specifying suffixes and determines size of immediate mode via ns32k-opcode. + Also builds index bytes if needed. + */ +int get_addr_mode(ptr,addr_modeP) + char *ptr; + addr_modeS *addr_modeP; +{ + int tmp; + addr_mode(ptr,addr_modeP,0); + if (addr_modeP->mode == DEFAULT || addr_modeP->scaled_mode == -1) { + /* resolve ambigious operands, this shouldn't + be necessary if one uses standard NSC operand + syntax. But the sequent compiler doesn't!!! + This finds a proper addressinging mode if it + is implicitly stated. See ns32k-opcode.h */ + (void)evaluate_expr(&exprP,ptr); /* this call takes time Sigh! */ + if (addr_modeP->mode == DEFAULT) { + if (exprP.X_add_symbol || exprP.X_subtract_symbol) { + addr_modeP->mode=desc->default_model; /* we have a label */ + } else { + addr_modeP->mode=desc->default_modec; /* we have a constant */ + } + } else { + if (exprP.X_add_symbol || exprP.X_subtract_symbol) { + addr_modeP->scaled_mode=desc->default_model; + } else { + addr_modeP->scaled_mode=desc->default_modec; + } + } + /* must put this mess down in addr_mode to handle the scaled case better */ + } + /* It appears as the sequent compiler wants an absolute when we have a + label without @. Constants becomes immediates besides the addr case. + Think it does so with local labels too, not optimum, pcrel is better. + When I have time I will make gas check this and select pcrel when possible + Actually that is trivial. + */ + if (tmp=addr_modeP->scaled_reg) { /* build indexbyte */ + tmp--; /* remember regnumber comes incremented for flagpurpose */ + tmp|=addr_modeP->scaled_mode<<3; + addr_modeP->index_byte=(char)tmp; + addr_modeP->am_size+=1; + } + if (disp_test[addr_modeP->mode]) { /* there was a displacement, probe for length specifying suffix*/ + { + register char c; + register char suffix; + register char suffix_sub; + register int i; + register char *toP; + register char *fromP; + + addr_modeP->pcrel=0; + if (disp_test[addr_modeP->mode]) { /* there is a displacement */ + if (addr_modeP->mode==27 || addr_modeP->scaled_mode==27) { /* do we have pcrel. mode */ + addr_modeP->pcrel=1; + } + addr_modeP->im_disp=1; + for(i=0;i<2;i++) { + suffix_sub=suffix=0; + if (toP=addr_modeP->disp[i]) { /* suffix of expression, the largest size rules */ + fromP=toP; + while (c = *fromP++) { + *toP++=c; + if (c==':') { + switch (*fromP) { + case '\0': + as_warn("Premature end of suffix--Defaulting to d"); + suffix=4; + continue; + case 'b':suffix_sub=1;break; + case 'w':suffix_sub=2;break; + case 'd':suffix_sub=4;break; + default: + as_warn("Bad suffix after ':' use {b|w|d} Defaulting to d"); + suffix=4; + } + fromP++; + toP--; /* So we write over the ':' */ + if (suffixdisp_suffix[i]=suffix; + addr_modeP->am_size+=suffix ? suffix : 4; + } + } + } + } + } else { + if (addr_modeP->mode==20) { /* look in ns32k_opcode for size */ + addr_modeP->disp_suffix[0]=addr_modeP->am_size=desc->im_size; + addr_modeP->im_disp=0; + } + } + return addr_modeP->mode; +} + + +/* read an optionlist */ +void optlist(str,optionP,default_map) + char *str; /* the string to extract options from */ + struct option *optionP; /* how to search the string */ + unsigned long *default_map; /* default pattern and output */ +{ + register int i,j,k,strlen1,strlen2; + register char *patternP,*strP; + strlen1=strlen(str); + if (strlen1<1) { + as_fatal("Very short instr to option, ie you can't do it on a NULLstr"); + } + for (i=0;optionP[i].pattern!=0;i++) { + strlen2=strlen(optionP[i].pattern); + for (j=0;j3) as_fatal("Internal error check ns32k-opcode.h"); + pcrel=0; + pcrel_adjust=0; + tmp=0; + switch (operandsP[(loop<<1)+1]) { + case 'f': /* operand of sfsr turns out to be a nasty specialcase */ + opcode_bit_ptr-=5; + case 'F': /* 32 bit float general form */ + case 'L': /* 64 bit float */ + case 'Q': /* quad-word */ + case 'B': /* byte */ + case 'W': /* word */ + case 'D': /* double-word */ + case 'A': /* double-word gen-address-form ie no regs allowed */ + get_addr_mode(argv[i],&addr_modeP); + iif.instr_size+=addr_modeP.am_size; + if (opcode_bit_ptr==desc->opcode_size) b=4; else b=6; + for (j=b;j<(b+2);j++) { + if (addr_modeP.disp[j-b]) { + IIF(j, + 2, + addr_modeP.disp_suffix[j-b], + (unsigned long)addr_modeP.disp[j-b], + 0, + addr_modeP.pcrel, + iif.instr_size-addr_modeP.am_size, /* this aint used (now) */ + addr_modeP.im_disp, + IND(BRANCH,BYTE), + NULL, + addr_modeP.scaled_reg ? addr_modeP.scaled_mode:addr_modeP.mode, + 0); + } + } + opcode_bit_ptr-=5; + iif.iifP[1].object|=((long)addr_modeP.mode)<im_size); + argv[i]=freeptr; + freeptr=(char*)tmp; + pcrel-=1; /* make pcrel 0 inspite of what case 'p': wants */ + /* fall thru */ + case 'p': /* displacement - pc relative addressing */ + pcrel+=1; + /* fall thru */ + case 'd': /* displacement */ + iif.instr_size+=suffixP[i] ? suffixP[i] : 4; + IIF(12, 2, suffixP[i], (unsigned long)argv[i], 0, + pcrel, pcrel_adjust, 1, IND(BRANCH,BYTE), NULL,-1,0); + break; + case 'H': /* sequent-hack: the linker wants a bit set when bsr */ + pcrel=1; + iif.instr_size+=suffixP[i] ? suffixP[i] : 4; + IIF(12, 2, suffixP[i], (unsigned long)argv[i], 0, + pcrel, pcrel_adjust, 1, IND(BRANCH,BYTE), NULL,-1,1);break; + case 'q': /* quick */ + opcode_bit_ptr-=4; + IIF(11,2,42,(unsigned long)argv[i],0,0,0,0,0, + bit_fix_new(4,opcode_bit_ptr,-8,7,0,1,0),-1,0); + break; + case 'r': /* register number (3 bits) */ + list_search(argv[i],opt6,&tmp); + opcode_bit_ptr-=3; + iif.iifP[1].object|=tmp<operands) { + if (*lineptr++!='\0') { + sqr='['; + sep=','; + while (*lineptr!='\0') { + if (desc->operands[argc<<1]) { + suffix[argc]=0; + arg_type=desc->operands[(argc<<1)+1]; + switch (arg_type) { + case 'd': case 'b': case 'p': case 'H': /* the operand is supposed to be a displacement */ + /* Hackwarning: do not forget to update the 4 cases above when editing ns32k-opcode.h */ + suffix_separator=':'; + break; + default: + suffix_separator='\255'; /* if this char occurs we loose */ + } + suffix[argc]=0; /* 0 when no ':' is encountered */ + argv[argc]=freeptr; + *freeptr='\0'; + while ((c = *lineptr)!='\0' && c!=sep) { + if (c==sqr) { + if (sqr=='[') { + sqr=']';sep='\0'; + } else { + sqr='[';sep=','; + } + } + if (c==suffix_separator) { /* ':' - label/suffix separator */ + switch (lineptr[1]) { + case 'b':suffix[argc]=1;break; + case 'w':suffix[argc]=2;break; + case 'd':suffix[argc]=4;break; + default: as_warn("Bad suffix, defaulting to d"); + suffix[argc]=4; + if (lineptr[1]=='\0' || lineptr[1]==sep) { + lineptr+=1; + continue; + } + } + lineptr+=2; + continue; + } + *freeptr++=c; + lineptr++; + } + *freeptr++='\0'; + argc+=1; + if (*lineptr=='\0') continue; + lineptr+=1; + } else { + as_fatal("Too many operands passed to instruction"); + } + } + } + } + if (argc!=strlen(desc->operands)/2) { + if (strlen(desc->default_args)) { /* we can apply default, dont goof */ + if (parse(desc->default_args,1)!=1) { /* check error in default */ + as_fatal("Wrong numbers of operands in default, check ns32k-opcodes.h"); + } + } else { + as_fatal("Wrong number of operands"); + } + + } + for (i=0;iopcode_size/8; + IIF(1,1,iif.instr_size,desc->opcode_seed,0,0,0,0,0,0,-1,0); + + /* this call encodes operands to iif format */ + if (argc) { + encode_operand(argc, + argv, + &desc->operands[0], + &suffix[0], + desc->im_size, + desc->opcode_size); + } + return recursive_level; +} + + + /* Convert iif to fragments. + From this point we start to dribble with functions in other files than + this one.(Except hash.c) So, if it's possible to make an iif for an other + CPU, you don't need to know what frags, relax, obstacks, etc is in order + to port this assembler. You only need to know if it's possible to reduce + your cpu-instruction to iif-format (takes some work) and adopt the other + md_? parts according to given instructions + Note that iif was invented for the clean ns32k`s architecure. + */ +convert_iif() +{ + register int i,j; + fragS *inst_frag; + char *inst_offset,*inst_opcode; + char *memP; + segT segment; + int l,k; + register int rem_size; /* count the remaining bytes of instruction */ + register char type; + register char size = 0; + int size_so_far=0; /* used to calculate pcrel_adjust */ + + rem_size=iif.instr_size; + memP=frag_more(iif.instr_size); /* make sure we have enough bytes for instruction */ + inst_opcode=memP; + inst_offset=(char*)(memP-frag_now->fr_literal); + inst_frag=frag_now; + for (i=0;ifx_bit_base) { /* expand fx_bit_base to point at opcode */ + iif.iifP[i].bit_fixP->fx_bit_base=(long)inst_opcode; + } + case 8: /* bignum or doublefloat */ + bzero (memP,8); + case 1:case 2:case 3:case 4:/* the final size in objectmemory is known */ + j=(unsigned long)iif.iifP[i].bit_fixP; + switch (type) { + case 1: /* the object is pure binary */ + if (j || iif.iifP[i].pcrel) { + fix_new_ns32k(frag_now, + (long)(memP-frag_now->fr_literal), + size, + 0, + 0, + iif.iifP[i].object, + iif.iifP[i].pcrel, + (char)size_so_far, /*iif.iifP[i].pcrel_adjust,*/ + iif.iifP[i].im_disp, + j, + iif.iifP[i].bsr); /* sequent hack */ + } else { /* good, just put them bytes out */ + switch (iif.iifP[i].im_disp) { + case 0: + md_number_to_chars(memP,iif.iifP[i].object,size);break; + case 1: + md_number_to_disp(memP,iif.iifP[i].object,size);break; + default: as_fatal("iif convert internal pcrel/binary"); + } + } + memP+=size; + rem_size-=size; + break; + case 2: /* the object is a pointer at an expression, so unpack + it, note that bignums may result from the expression + */ + if ((segment=evaluate_expr(&exprP,(char*)iif.iifP[i].object))==SEG_BIG || size==8) { + if ((k=exprP.X_add_number)>0) { /* we have a bignum ie a quad */ + /* this can only happens in a long suffixed instruction */ + bzero(memP,size); /* size normally is 8 */ + if (k*2>size) as_warn("Bignum too big for long"); + if (k==3) memP+=2; + for (l=0;k>0;k--,l+=2) { + md_number_to_chars(memP+l,generic_bignum[l>>1],sizeof(LITTLENUM_TYPE)); + } + } else { /* flonum */ + LITTLENUM_TYPE words[4]; + + switch(size) { + case 4: + gen_to_words(words,2,8); + md_number_to_imm(memP ,(long)words[0],sizeof(LITTLENUM_TYPE)); + md_number_to_imm(memP+sizeof(LITTLENUM_TYPE),(long)words[1],sizeof(LITTLENUM_TYPE)); + break; + case 8: + gen_to_words(words,4,11); + md_number_to_imm(memP ,(long)words[0],sizeof(LITTLENUM_TYPE)); + md_number_to_imm(memP+sizeof(LITTLENUM_TYPE) ,(long)words[1],sizeof(LITTLENUM_TYPE)); + md_number_to_imm(memP+2*sizeof(LITTLENUM_TYPE),(long)words[2],sizeof(LITTLENUM_TYPE)); + md_number_to_imm(memP+3*sizeof(LITTLENUM_TYPE),(long)words[3],sizeof(LITTLENUM_TYPE)); + break; + } + } + memP+=size; + rem_size-=size; + break; + } + if (j || + exprP.X_add_symbol || + exprP.X_subtract_symbol || + iif.iifP[i].pcrel) { /* fixit */ + /* the expression was undefined due to an undefined label */ + /* create a fix so we can fix the object later */ + exprP.X_add_number+=iif.iifP[i].object_adjust; + fix_new_ns32k(frag_now, + (long)(memP-frag_now->fr_literal), + size, + exprP.X_add_symbol, + exprP.X_subtract_symbol, + exprP.X_add_number, + iif.iifP[i].pcrel, + (char)size_so_far, /*iif.iifP[i].pcrel_adjust,*/ + iif.iifP[i].im_disp, + j, + iif.iifP[i].bsr); /* sequent hack */ + + } else { /* good, just put them bytes out */ + switch (iif.iifP[i].im_disp) { + case 0: + md_number_to_imm(memP,exprP.X_add_number,size);break; + case 1: + md_number_to_disp(memP,exprP.X_add_number,size);break; + default: as_fatal("iif convert internal pcrel/pointer"); + } + } + memP+=size; + rem_size-=size; + break; + default: as_fatal("Internal logic error in iif.iifP[n].type"); + } + break; + case 0: /* To bad, the object may be undefined as far as its final + nsize in object memory is concerned. The size of the object + in objectmemory is not explicitly given. + If the object is defined its length can be determined and + a fix can replace the frag. + */ + { + int temp; + segment=evaluate_expr(&exprP,(char*)iif.iifP[i].object); + if ((exprP.X_add_symbol || exprP.X_subtract_symbol) && + !iif.iifP[i].pcrel) { /* OVE: hack, clamp to 4 bytes */ + size=4; /* we dont wan't to frag this, use 4 so it reaches */ + fix_new_ns32k(frag_now, + (long)(memP-frag_now->fr_literal), + size, + exprP.X_add_symbol, + exprP.X_subtract_symbol, + exprP.X_add_number, + 0, /* never iif.iifP[i].pcrel, */ + (char)size_so_far, /*iif.iifP[i].pcrel_adjust,*/ + 1, /* always iif.iifP[i].im_disp, */ + 0,0); + memP+=size; + rem_size-=4; + break; /* exit this absolute hack */ + } + + if (exprP.X_add_symbol || exprP.X_subtract_symbol) { /* frag it */ + if (exprP.X_subtract_symbol) { /* We cant relax this case */ + as_fatal("Can't relax difference"); + } + else { + /* at this stage we must undo some of the effect caused + by frag_more, ie we must make sure that frag_var causes + frag_new to creat a valid fix-size in the frag it`s closing + */ + temp = -(rem_size-4); + obstack_blank_fast(&frags,temp); + /* we rewind none, some or all of the requested size we + requested by the first frag_more for this iif chunk. + Note: that we allocate 4 bytes to an object we NOT YET + know the size of, thus rem_size-4. + */ + (void)frag_variant(rs_machine_dependent, + 4, + 0, + IND(BRANCH,UNDEF), /* expecting the worst */ + exprP.X_add_symbol, + exprP.X_add_number, + (char*)inst_opcode, + (char)size_so_far, /*iif.iifP[i].pcrel_adjust);*/ + iif.iifP[i].bsr); /* sequent linker hack */ + rem_size-=4; + if (rem_size>0) { + memP=frag_more(rem_size); + } + } + } + else {/* Double work, this is done in md_number_to_disp */ + exprP.X_add_number; + if (-64<=exprP.X_add_number && exprP.X_add_number<=63) { + size=1; + } else { + if (-8192<=exprP.X_add_number && exprP.X_add_number<=8191) { + size=2; + } else { + if (-0x1f000000<=exprP.X_add_number && + exprP.X_add_number<=0x1fffffff) + /* if (-0x40000000<=exprP.X_add_number && + exprP.X_add_number<=0x3fffffff) */ + { + size=4; + } else { + as_warn("Displacement to large for :d"); + size=4; + } + } + } + /* rewind the bytes not used */ + temp = -(4-size); + md_number_to_disp(memP,exprP.X_add_number,size); + obstack_blank_fast(&frags,temp); + memP+=size; + rem_size-=4; /* we allocated this amount */ + } + } + break; + default: + as_fatal("Internal logic error in iif.iifP[].type"); + } + size_so_far+=size; + size=0; + } + } +} + +void md_assemble(line) +char *line; +{ + freeptr=freeptr_static; + parse(line,0); /* explode line to more fix form in iif */ + convert_iif(); /* convert iif to frags, fix's etc */ +#ifdef SHOW_NUM + printf(" \t\t\t%s\n",line); +#endif +} + + +void md_begin() +{ + /* build a hashtable of the instructions */ + register struct ns32k_opcode *ptr; + register char *stat; + inst_hash_handle=hash_new(); + for (ptr=ns32k_opcodes;ptrname,(char*)ptr))) { + as_fatal("Can't hash %s: %s",ptr->name,stat); /*fatal*/ + exit(0); + } + } + freeptr_static=(char*)malloc(PRIVATE_SIZE); /* some private space please! */ +} + + +void +md_end() +{ + free(freeptr_static); +} + +/* Must be equal to MAX_PRECISON in atof-ieee.c */ +#define MAX_LITTLENUMS 6 + +/* Turn the string pointed to by litP into a floating point constant of type + type, and emit the appropriate bytes. The number of LITTLENUMS emitted + is stored in *sizeP . An error message is returned, or NULL on OK. + */ +char * +md_atof(type,litP,sizeP) +char type; +char *litP; +int *sizeP; +{ + int prec; + LITTLENUM_TYPE words[MAX_LITTLENUMS]; + LITTLENUM_TYPE *wordP; + char *t; + char *atof_ieee(); + + switch(type) { + case 'f': + prec = 2; + break; + + case 'd': + prec = 4; + break; + default: + *sizeP=0; + return "Bad call to MD_ATOF()"; + } + t=atof_ieee(input_line_pointer,type,words); + if(t) + input_line_pointer=t; + + *sizeP=prec * sizeof(LITTLENUM_TYPE); + for(wordP=words+prec;prec--;) { + md_number_to_chars(litP,(long)(*--wordP),sizeof(LITTLENUM_TYPE)); + litP+=sizeof(LITTLENUM_TYPE); + } + return ""; /* Someone should teach Dean about null pointers */ +} + +/* Convert number to chars in correct order */ + +void +md_number_to_chars (buf, value, nbytes) + char *buf; + long int value; + int nbytes; +{ + while (nbytes--) + { +#ifdef SHOW_NUM + printf("%x ",value & 0xff); +#endif + *buf++ = value; /* Lint wants & MASK_CHAR. */ + value >>= BITS_PER_CHAR; + } +} +/* Convert number to chars in correct order */ + + + +/* This is a variant of md_numbers_to_chars. The reason for its' existence + is the fact that ns32k uses Huffman coded displacements. This implies + that the bit order is reversed in displacements and that they are prefixed + with a size-tag. + + binary: msb -> lsb 0xxxxxxx byte + 10xxxxxx xxxxxxxx word + 11xxxxxx xxxxxxxx xxxxxxxx xxxxxxxx double word + + This must be taken care of and we do it here! + */ +void md_number_to_disp(buf,val,n) + char *buf; + long val; + char n; +{ + switch(n) { + case 1: + if (val < -64 || val > 63) + as_warn("Byte displacement out of range. line number not valid"); + val&=0x7f; +#ifdef SHOW_NUM + printf("%x ",val & 0xff); +#endif + *buf++=val; + break; + case 2: + if (val < -8192 || val > 8191) + as_warn("Word displacement out of range. line number not valid"); + val&=0x3fff; + val|=0x8000; +#ifdef SHOW_NUM + printf("%x ",val>>8 & 0xff); +#endif + *buf++=(val>>8); +#ifdef SHOW_NUM + printf("%x ",val & 0xff); +#endif + *buf++=val; + break; + case 4: + if (val < -0x1f000000 || val >= 0x20000000) + /* if (val < -0x20000000 || val >= 0x20000000) */ + as_warn("Double word displacement out of range"); + val|=0xc0000000; +#ifdef SHOW_NUM + printf("%x ",val>>24 & 0xff); +#endif + *buf++=(val>>24); +#ifdef SHOW_NUM + printf("%x ",val>>16 & 0xff); +#endif + *buf++=(val>>16); +#ifdef SHOW_NUM + printf("%x ",val>>8 & 0xff); +#endif + *buf++=(val>>8); +#ifdef SHOW_NUM + printf("%x ",val & 0xff); +#endif + *buf++=val; + break; + default: + as_fatal("Internal logic error"); + } +} +void md_number_to_imm(buf,val,n) + char *buf; + long val; + char n; +{ + switch(n) { + case 1: +#ifdef SHOW_NUM + printf("%x ",val & 0xff); +#endif + *buf++=val; + break; + case 2: +#ifdef SHOW_NUM + printf("%x ",val>>8 & 0xff); +#endif + *buf++=(val>>8); +#ifdef SHOW_NUM + printf("%x ",val & 0xff); +#endif + *buf++=val; + break; + case 4: +#ifdef SHOW_NUM + printf("%x ",val>>24 & 0xff); +#endif + *buf++=(val>>24); +#ifdef SHOW_NUM + printf("%x ",val>>16 & 0xff); +#endif + *buf++=(val>>16); +#ifdef SHOW_NUM + printf("%x ",val>>8 & 0xff); +#endif + *buf++=(val>>8); +#ifdef SHOW_NUM + printf("%x ",val & 0xff); +#endif + *buf++=val; + break; + default: + as_fatal("Internal logic error"); + } +} +/* the bit-field entries in the relocation_info struct plays hell + with the byte-order problems of cross-assembly. So as a hack, + I added this mach. dependent ri twiddler. Ugly, but it gets + you there. -KWK */ +/* OVE: on a ns32k the twiddling continues at an even deeper level + here we have to distinguish between displacements and immediates. + + The sequent has a bit for this. It also has a bit for relocobjects that + points at the target for a bsr (BranchSubRoutine) !?!?!?! + + Using [] is importable but fast!! still, its rather funny :-) + This md_ri.... is tailored for sequent. + */ + +void md_ri_to_chars(ri_p, ri) + struct relocation_info *ri_p, ri; +{ + if (ri.r_bsr) {ri.r_pcrel=0;} /* sequent seems to want this */ + md_number_to_chars((char*)ri_p, ri.r_address, sizeof(ri.r_address)); + md_number_to_chars((char*)ri_p+4, + (long)(ri.r_symbolnum ) | + (long)(ri.r_pcrel << 24 ) | + (long)(ri.r_length << 25 ) | + (long)(ri.r_extern << 27 ) | + (long)(ri.r_bsr << 28 ) | + (long)(ri.r_disp << 29 ), + 4); + /* the first and second md_number_to_chars never overlaps (32bit cpu case) */ +} + +/* fast bitfiddling support */ +/* mask used to zero bitfield before oring in the true field */ + +static unsigned long l_mask[]={ 0xffffffff, 0xfffffffe, 0xfffffffc, 0xfffffff8, + 0xfffffff0, 0xffffffe0, 0xffffffc0, 0xffffff80, + 0xffffff00, 0xfffffe00, 0xfffffc00, 0xfffff800, + 0xfffff000, 0xffffe000, 0xffffc000, 0xffff8000, + 0xffff0000, 0xfffe0000, 0xfffc0000, 0xfff80000, + 0xfff00000, 0xffe00000, 0xffc00000, 0xff800000, + 0xff000000, 0xfe000000, 0xfc000000, 0xf8000000, + 0xf0000000, 0xe0000000, 0xc0000000, 0x80000000, + }; +static unsigned long r_mask[]={ 0x00000000, 0x00000001, 0x00000003, 0x00000007, + 0x0000000f, 0x0000001f, 0x0000003f, 0x0000007f, + 0x000000ff, 0x000001ff, 0x000003ff, 0x000007ff, + 0x00000fff, 0x00001fff, 0x00003fff, 0x00007fff, + 0x0000ffff, 0x0001ffff, 0x0003ffff, 0x0007ffff, + 0x000fffff, 0x001fffff, 0x003fffff, 0x007fffff, + 0x00ffffff, 0x01ffffff, 0x03ffffff, 0x07ffffff, + 0x0fffffff, 0x1fffffff, 0x3fffffff, 0x7fffffff, + }; +#define MASK_BITS 31 +/* Insert bitfield described by field_ptr and val at buf + This routine is written for modification of the first 4 bytes pointed + to by buf, to yield speed. + The ifdef stuff is for selection between a ns32k-dependent routine + and a general version. (My advice: use the general version!) + */ + +void md_number_to_field(buf,val,field_ptr) + register char *buf; + register long val; + register bit_fixS *field_ptr; +{ + register unsigned long object; + register unsigned long mask; +/* define ENDIAN on a ns32k machine */ +#ifdef ENDIAN + register unsigned long *mem_ptr; +#else + register char *mem_ptr; +#endif + if (field_ptr->fx_bit_min<=val && val<=field_ptr->fx_bit_max) { +#ifdef ENDIAN + if (field_ptr->fx_bit_base) { /* override buf */ + mem_ptr=(unsigned long*)field_ptr->fx_bit_base; + } else { + mem_ptr=(unsigned long*)buf; + } +#else + if (field_ptr->fx_bit_base) { /* override buf */ + mem_ptr=(char*)field_ptr->fx_bit_base; + } else { + mem_ptr=buf; + } +#endif + mem_ptr+=field_ptr->fx_bit_base_adj; +#ifdef ENDIAN /* we have a nice ns32k machine with lowbyte at low-physical mem */ + object = *mem_ptr; /* get some bytes */ +#else /* OVE Goof! the machine is a m68k or dito */ + /* That takes more byte fiddling */ + object=0; + object|=mem_ptr[3] & 0xff; + object<<=8; + object|=mem_ptr[2] & 0xff; + object<<=8; + object|=mem_ptr[1] & 0xff; + object<<=8; + object|=mem_ptr[0] & 0xff; +#endif + mask=0; + mask|=(r_mask[field_ptr->fx_bit_offset]); + mask|=(l_mask[field_ptr->fx_bit_offset+field_ptr->fx_bit_size]); + object&=mask; + val+=field_ptr->fx_bit_add; + object|=((val<fx_bit_offset) & (mask ^ 0xffffffff)); +#ifdef ENDIAN + *mem_ptr=object; +#else + mem_ptr[0]=(char)object; + object>>=8; + mem_ptr[1]=(char)object; + object>>=8; + mem_ptr[2]=(char)object; + object>>=8; + mem_ptr[3]=(char)object; +#endif + } else { + as_warn("Bit field out of range"); + } +} + +/* Convert a relaxed displacement to dito in final output */ + +void + md_convert_frag(fragP) +register fragS *fragP; +{ + long disp; + long ext; + + /* Address in gas core of the place to store the displacement. */ + register char *buffer_address = fragP -> fr_fix + fragP -> fr_literal; + /* Address in object code of the displacement. */ + register int object_address = fragP -> fr_fix + fragP -> fr_address; + + know(fragP->fr_symbol); + + /* The displacement of the address, from current location. */ + disp = (fragP->fr_symbol->sy_value + fragP->fr_offset) - object_address; + disp+= fragP->fr_pcrel_adjust; + + switch(fragP->fr_subtype) { + case IND(BRANCH,BYTE): + ext=1; + break; + case IND(BRANCH,WORD): + ext=2; + break; + case IND(BRANCH,DOUBLE): + ext=4; + break; + } + if(ext) { + md_number_to_disp(buffer_address,(long)disp,(int)ext); + fragP->fr_fix+=ext; + } +} + + + +/* This function returns the estimated size a variable object will occupy, + one can say that we tries to guess the size of the objects before we + actually know it */ + +md_estimate_size_before_relax(fragP,segtype) + register fragS *fragP; +{ + int old_fix; + old_fix=fragP->fr_fix; + switch(fragP->fr_subtype) { + case IND(BRANCH,UNDEF): + if((fragP->fr_symbol->sy_type&N_TYPE)==segtype) { + /* the symbol has been assigned a value */ + fragP->fr_subtype=IND(BRANCH,BYTE); + } else { + /* we don't relax symbols defined in an other segment + the thing to do is to assume the object will occupy 4 bytes */ + fix_new_ns32k(fragP, + (int)(fragP->fr_fix), + 4, + fragP->fr_symbol, + (symbolS *)0, + fragP->fr_offset, + 1, + fragP->fr_pcrel_adjust, + 1, + 0, + fragP->fr_bsr); /*sequent hack */ + fragP->fr_fix+=4; + /* fragP->fr_opcode[1]=0xff; */ + frag_wane(fragP); + break; + } + case IND(BRANCH,BYTE): + fragP->fr_var+=1; + break; + default: + break; + } + return fragP->fr_var + fragP->fr_fix - old_fix; +} + +int md_short_jump_size = 3; +int md_long_jump_size = 5; + +void +md_create_short_jump(ptr,from_addr,to_addr,frag,to_symbol) +char *ptr; +long from_addr, + to_addr; +fragS *frag; +symbolS *to_symbol; +{ + long offset; + + offset = to_addr - from_addr; + md_number_to_chars(ptr, (long)0xEA ,1); + md_number_to_disp(ptr+1,(long)offset,2); +} + +void +md_create_long_jump(ptr,from_addr,to_addr,frag,to_symbol) +char *ptr; +long from_addr, + to_addr; +fragS *frag; +symbolS *to_symbol; +{ + long offset; + + offset= to_addr - from_addr; + md_number_to_chars(ptr, (long)0xEA, 2); + md_number_to_disp(ptr+2,(long)offset,4); +} + +/* JF this is a new function to parse machine-dep options */ +int +md_parse_option(argP,cntP,vecP) +char **argP; +int *cntP; +char ***vecP; +{ + switch(**argP) { + case 'm': + (*argP)++; + + if(!strcmp(*argP,"32032")) { + cpureg = cpureg_032; + mmureg = mmureg_032; + } else if(!strcmp(*argP, "32532")) { + cpureg = cpureg_532; + mmureg = mmureg_532; + } else + as_warn("Unknown -m option ignored"); + + while(**argP) + (*argP)++; + break; + + default: + return 0; + } + return 1; +} + +/* + * bit_fix_new() + * + * Create a bit_fixS in obstack 'notes'. + * This struct is used to profile the normal fix. If the bit_fixP is a + * valid pointer (not NULL) the bit_fix data will be used to format the fix. + */ +bit_fixS * +bit_fix_new (size,offset,min,max,add,base_type,base_adj) + char size; /* Length of bitfield */ + char offset; /* Bit offset to bitfield */ + long base_type; /* 0 or 1, if 1 it's exploded to opcode ptr */ + long base_adj; + long min; /* Signextended min for bitfield */ + long max; /* Signextended max for bitfield */ + long add; /* Add mask, used for huffman prefix */ +{ + register bit_fixS * bit_fixP; + + bit_fixP = (bit_fixS *)obstack_alloc(¬es,sizeof(bit_fixS)); + + bit_fixP -> fx_bit_size = size; + bit_fixP -> fx_bit_offset = offset; + bit_fixP -> fx_bit_base = base_type; + bit_fixP -> fx_bit_base_adj = base_adj; + bit_fixP -> fx_bit_max = max; + bit_fixP -> fx_bit_min = min; + bit_fixP -> fx_bit_add = add; + + return bit_fixP; +} + +void +fix_new_ns32k (frag, where, size, add_symbol, sub_symbol, offset, pcrel, + pcrel_adjust, im_disp, bit_fixP, bsr) + fragS * frag; /* Which frag? */ + int where; /* Where in that frag? */ + short int size; /* 1, 2 or 4 usually. */ + symbolS * add_symbol; /* X_add_symbol. */ + symbolS * sub_symbol; /* X_subtract_symbol. */ + long int offset; /* X_add_number. */ + int pcrel; /* TRUE if PC-relative relocation. */ + char pcrel_adjust; /* not zero if adjustment of pcrel offset is needed */ + char im_disp; /* true if the value to write is a displacement */ + bit_fixS *bit_fixP; /* pointer at struct of bit_fix's, ignored if NULL */ + char bsr; /* sequent-linker-hack: 1 when relocobject is a bsr */ + +{ + register fixS * fixP; + + fixP = (fixS *)obstack_alloc(¬es,sizeof(fixS)); + fixP -> fx_frag = frag; + fixP -> fx_where = where; + fixP -> fx_size = size; + fixP -> fx_addsy = add_symbol; + fixP -> fx_subsy = sub_symbol; + fixP -> fx_offset = offset; + fixP -> fx_pcrel = pcrel; + fixP -> fx_pcrel_adjust = pcrel_adjust; + fixP -> fx_im_disp = im_disp; + fixP -> fx_bit_fixP = bit_fixP; + fixP -> fx_bsr = bsr; + fixP -> fx_next = * seg_fix_rootP; + + * seg_fix_rootP = fixP; +}