#include <stdio.h>
#include <limits.h>
#include "as.h"
#include "subsegs.h"
#include "bfd.h"
#include "elf/mmix.h"
#include "opcode/mmix.h"
#include "safe-ctype.h"
#include "dwarf2dbg.h"
#include "obstack.h"
enum mmix_fixup_action
{
mmix_fixup_byte,
mmix_fixup_register,
mmix_fixup_register_or_adjust_for_byte
};
static int get_spec_regno PARAMS ((char *));
static int get_operands PARAMS ((int, char *, expressionS[]));
static int get_putget_operands
PARAMS ((struct mmix_opcode *, char *, expressionS[]));
static void s_prefix PARAMS ((int));
static void s_greg PARAMS ((int));
static void s_loc PARAMS ((int));
static void s_bspec PARAMS ((int));
static void s_espec PARAMS ((int));
static void mmix_s_local PARAMS ((int));
static void mmix_greg_internal PARAMS ((char *));
static void mmix_set_geta_branch_offset PARAMS ((char *, offsetT value));
static void mmix_set_jmp_offset PARAMS ((char *, offsetT));
static void mmix_fill_nops PARAMS ((char *, int));
static int cmp_greg_symbol_fixes PARAMS ((const PTR, const PTR));
static int cmp_greg_val_greg_symbol_fixes
PARAMS ((const PTR p1, const PTR p2));
static void mmix_handle_rest_of_empty_line PARAMS ((void));
static void mmix_discard_rest_of_line PARAMS ((void));
static void mmix_byte PARAMS ((void));
static void mmix_cons PARAMS ((int));
#define MAGIC_FB_BACKWARD_CHAR '\003'
#define MAGIC_FB_FORWARD_CHAR '\004'
#define COPY_FR_WHERE_TO_FX(FRAG, FIX) \
do \
{ \
(FIX)->fx_file = (FRAG)->fr_file; \
(FIX)->fx_line = (FRAG)->fr_line; \
} \
while (0)
const char *md_shortopts = "x";
static int current_fb_label = -1;
static char *pending_label = NULL;
static bfd_vma lowest_text_loc = (bfd_vma) -1;
static int text_has_contents = 0;
static int last_alignment = 0;
static int want_unaligned = 0;
static bfd_vma lowest_data_loc = (bfd_vma) -1;
static int data_has_contents = 0;
fragS *mmix_opcode_frag = NULL;
static int n_of_raw_gregs = 0;
static struct
{
char *label;
expressionS exp;
} mmix_raw_gregs[MAX_GREGS];
static fixS *mmix_gregs[MAX_GREGS];
static int n_of_cooked_gregs = 0;
static asection *real_reg_section;
struct mmix_symbol_gregs
{
int n_gregs;
struct mmix_symbol_greg_fixes
{
fixS *fix;
offsetT offs;
} greg_fixes[MAX_GREGS];
};
static int label_without_colon_this_line = 1;
static int expand_op = 1;
static int warn_on_expansion = 1;
static int merge_gregs = 1;
static int allocate_undefined_gregs_in_linker = 0;
static int predefined_syms = 1;
static int equated_spec_regs = 1;
int mmix_gnu_syntax = 0;
int mmix_globalize_symbols = 0;
int pushj_stubs = 1;
int mmix_next_semicolon_is_eoln = 1;
static int doing_bspec = 0;
static char *bspec_file;
static unsigned int bspec_line;
struct option md_longopts[] =
{
#define OPTION_RELAX (OPTION_MD_BASE)
#define OPTION_NOEXPAND (OPTION_RELAX + 1)
#define OPTION_NOMERGEGREG (OPTION_NOEXPAND + 1)
#define OPTION_NOSYMS (OPTION_NOMERGEGREG + 1)
#define OPTION_GNU_SYNTAX (OPTION_NOSYMS + 1)
#define OPTION_GLOBALIZE_SYMBOLS (OPTION_GNU_SYNTAX + 1)
#define OPTION_FIXED_SPEC_REGS (OPTION_GLOBALIZE_SYMBOLS + 1)
#define OPTION_LINKER_ALLOCATED_GREGS (OPTION_FIXED_SPEC_REGS + 1)
#define OPTION_NOPUSHJSTUBS (OPTION_LINKER_ALLOCATED_GREGS + 1)
{"linkrelax", no_argument, NULL, OPTION_RELAX},
{"no-expand", no_argument, NULL, OPTION_NOEXPAND},
{"no-merge-gregs", no_argument, NULL, OPTION_NOMERGEGREG},
{"no-predefined-syms", no_argument, NULL, OPTION_NOSYMS},
{"gnu-syntax", no_argument, NULL, OPTION_GNU_SYNTAX},
{"globalize-symbols", no_argument, NULL, OPTION_GLOBALIZE_SYMBOLS},
{"fixed-special-register-names", no_argument, NULL,
OPTION_FIXED_SPEC_REGS},
{"linker-allocated-gregs", no_argument, NULL,
OPTION_LINKER_ALLOCATED_GREGS},
{"no-pushj-stubs", no_argument, NULL, OPTION_NOPUSHJSTUBS},
{"no-stubs", no_argument, NULL, OPTION_NOPUSHJSTUBS},
{NULL, no_argument, NULL, 0}
};
size_t md_longopts_size = sizeof (md_longopts);
static struct hash_control *mmix_opcode_hash;
char *mmix_current_prefix;
struct obstack mmix_sym_obstack;
#define STATE_GETA (1)
#define STATE_BCC (2)
#define STATE_PUSHJ (3)
#define STATE_JMP (4)
#define STATE_GREG (5)
#define STATE_PUSHJSTUB (6)
#define STATE_LENGTH_MASK (1)
#define STATE_ZERO (0)
#define STATE_MAX (1)
#define STATE_UNDF STATE_MAX
#define STATE_GREG_UNDF ENCODE_RELAX (STATE_GREG, STATE_ZERO)
#define STATE_GREG_DEF ENCODE_RELAX (STATE_GREG, STATE_MAX)
#define GETA_0F (65536 * 4 - 8)
#define GETA_0B (-65536 * 4 - 4)
#define GETA_MAX_LEN 4 * 4
#define GETA_3F 0
#define GETA_3B 0
#define BCC_0F GETA_0F
#define BCC_0B GETA_0B
#define BCC_MAX_LEN 6 * 4
#define BCC_5F GETA_3F
#define BCC_5B GETA_3B
#define PUSHJ_0F GETA_0F
#define PUSHJ_0B GETA_0B
#define PUSHJ_MAX_LEN 5 * 4
#define PUSHJ_4F GETA_3F
#define PUSHJ_4B GETA_3B
#define PUSHJSTUB_MAX ((offsetT) (((addressT) -1) >> 1))
#define PUSHJSTUB_MIN (-PUSHJSTUB_MAX - 1)
#define JMP_0F (65536 * 256 * 4 - 8)
#define JMP_0B (-65536 * 256 * 4 - 4)
#define JMP_MAX_LEN 5 * 4
#define JMP_4F 0
#define JMP_4B 0
#define RELAX_ENCODE_SHIFT 1
#define ENCODE_RELAX(what, length) (((what) << RELAX_ENCODE_SHIFT) + (length))
const relax_typeS mmix_relax_table[] =
{
{1, 1, 0, 0},
{1, 1, 0, 0},
{GETA_0F, GETA_0B, 0, ENCODE_RELAX (STATE_GETA, STATE_MAX)},
{GETA_3F, GETA_3B,
GETA_MAX_LEN - 4, 0},
{BCC_0F, BCC_0B, 0, ENCODE_RELAX (STATE_BCC, STATE_MAX)},
{BCC_5F, BCC_5B,
BCC_MAX_LEN - 4, 0},
{PUSHJ_0F, PUSHJ_0B, 0, ENCODE_RELAX (STATE_PUSHJSTUB, STATE_ZERO)},
{PUSHJ_4F, PUSHJ_4B,
PUSHJ_MAX_LEN - 4, 0},
{JMP_0F, JMP_0B, 0, ENCODE_RELAX (STATE_JMP, STATE_MAX)},
{JMP_4F, JMP_4B,
JMP_MAX_LEN - 4, 0},
{0, 0, 0, 0}, {0, 0, 0, 0},
{PUSHJSTUB_MAX, PUSHJSTUB_MIN,
0, ENCODE_RELAX (STATE_PUSHJ, STATE_MAX)},
{0, 0, PUSHJ_MAX_LEN, 0}
};
const pseudo_typeS md_pseudo_table[] =
{
{"greg", s_greg, 0},
{"bspec", s_bspec, 1},
{"espec", s_espec, 1},
{"local", mmix_s_local, 1},
{NULL, 0, 0}
};
const char mmix_comment_chars[] = "%!";
const char mmix_symbol_chars[] = ":@";
const char line_comment_chars[] = "*#";
const char line_separator_chars[] = ";";
const char mmix_exp_chars[] = "eE";
const char mmix_flt_chars[] = "rf";
static void
mmix_set_geta_branch_offset (opcodep, value)
char *opcodep;
offsetT value;
{
if (value < 0)
{
value += 65536 * 4;
opcodep[0] |= 1;
}
value /= 4;
md_number_to_chars (opcodep + 2, value, 2);
}
static void
mmix_set_jmp_offset (opcodep, value)
char *opcodep;
offsetT value;
{
if (value < 0)
{
value += 65536 * 256 * 4;
opcodep[0] |= 1;
}
value /= 4;
md_number_to_chars (opcodep + 1, value, 3);
}
static void
mmix_fill_nops (opcodep, n)
char *opcodep;
int n;
{
int i;
for (i = 0; i < n; i++)
md_number_to_chars (opcodep + i * 4, SWYM_INSN_BYTE << 24, 4);
}
int
mmix_current_location (fn, exp)
void (*fn) PARAMS ((expressionS *));
expressionS *exp;
{
(*fn) (exp);
return 1;
}
static int
get_operands (max_operands, s, exp)
int max_operands;
char *s;
expressionS exp[];
{
char *p = s;
int numexp = 0;
int nextchar = ',';
while (nextchar == ',')
{
while (*p == ' ' || *p == '\t')
p++;
if (*p == 0 || *p == '\n' || *p == '\r')
{
break;
}
else if (numexp == max_operands)
{
as_bad (_("invalid operands"));
mmix_discard_rest_of_line ();
return 0;
}
input_line_pointer = p;
expression (&exp[numexp]);
if (exp[numexp].X_op == O_illegal)
{
as_bad (_("invalid operands"));
}
else if (exp[numexp].X_op == O_absent)
{
as_bad (_("missing operand"));
}
numexp++;
p = input_line_pointer;
while (*p == ' ' || *p == '\t')
p++;
nextchar = *p++;
}
if (nextchar != ',')
{
mmix_handle_rest_of_empty_line ();
input_line_pointer--;
}
exp[numexp].X_op = O_illegal;
return (numexp);
}
static int
get_spec_regno (name)
char *name;
{
int i;
if (name == NULL)
return -1;
if (*name == ':')
name++;
for (i = 0; mmix_spec_regs[i].name != NULL; i++)
if (strcmp (name, mmix_spec_regs[i].name) == 0)
return mmix_spec_regs[i].number;
return -1;
}
static int
get_putget_operands (insn, operands, exp)
struct mmix_opcode *insn;
char *operands;
expressionS exp[];
{
expressionS *expp_reg;
expressionS *expp_sreg;
char *sregp = NULL;
char *sregend = operands;
char *p = operands;
char c = *sregend;
int regno;
while (*p == ' ' || *p == '\t')
p++;
input_line_pointer = p;
exp[0].X_op = O_illegal;
exp[1].X_op = O_illegal;
if (insn->operands == mmix_operands_get)
{
expp_reg = &exp[0];
expp_sreg = &exp[1];
expression (expp_reg);
p = input_line_pointer;
while (*p == ' ' || *p == '\t')
p++;
if (*p == ',')
{
p++;
while (*p == ' ' || *p == '\t')
p++;
sregp = p;
input_line_pointer = sregp;
c = get_symbol_end ();
sregend = input_line_pointer;
}
}
else
{
expp_sreg = &exp[0];
expp_reg = &exp[1];
sregp = p;
c = get_symbol_end ();
sregend = p = input_line_pointer;
*p = c;
while (*p == ' ' || *p == '\t')
p++;
if (*p == ',')
{
p++;
while (*p == ' ' || *p == '\t')
p++;
input_line_pointer = p;
expression (expp_reg);
}
*sregend = 0;
}
regno = get_spec_regno (sregp);
*sregend = c;
if (expp_reg->X_op != O_illegal
&& expp_reg->X_op != O_absent
&& regno != -1)
{
expp_sreg->X_op = O_register;
expp_sreg->X_add_number = regno + 256;
}
return 2;
}
int
md_parse_option (c, arg)
int c;
char *arg ATTRIBUTE_UNUSED;
{
switch (c)
{
case 'x':
warn_on_expansion = 0;
allocate_undefined_gregs_in_linker = 1;
break;
case OPTION_RELAX:
linkrelax = 1;
break;
case OPTION_NOEXPAND:
expand_op = 0;
break;
case OPTION_NOMERGEGREG:
merge_gregs = 0;
break;
case OPTION_NOSYMS:
predefined_syms = 0;
equated_spec_regs = 0;
break;
case OPTION_GNU_SYNTAX:
mmix_gnu_syntax = 1;
label_without_colon_this_line = 0;
break;
case OPTION_GLOBALIZE_SYMBOLS:
mmix_globalize_symbols = 1;
break;
case OPTION_FIXED_SPEC_REGS:
equated_spec_regs = 0;
break;
case OPTION_LINKER_ALLOCATED_GREGS:
allocate_undefined_gregs_in_linker = 1;
break;
case OPTION_NOPUSHJSTUBS:
pushj_stubs = 0;
break;
default:
return 0;
}
return 1;
}
void
md_show_usage (stream)
FILE * stream;
{
fprintf (stream, _(" MMIX-specific command line options:\n"));
fprintf (stream, _("\
-fixed-special-register-names\n\
Allow only the original special register names.\n"));
fprintf (stream, _("\
-globalize-symbols Make all symbols global.\n"));
fprintf (stream, _("\
-gnu-syntax Turn off mmixal syntax compatibility.\n"));
fprintf (stream, _("\
-relax Create linker relaxable code.\n"));
fprintf (stream, _("\
-no-predefined-syms Do not provide mmixal built-in constants.\n\
Implies -fixed-special-register-names.\n"));
fprintf (stream, _("\
-no-expand Do not expand GETA, branches, PUSHJ or JUMP\n\
into multiple instructions.\n"));
fprintf (stream, _("\
-no-merge-gregs Do not merge GREG definitions with nearby values.\n"));
fprintf (stream, _("\
-linker-allocated-gregs If there's no suitable GREG definition for the\
operands of an instruction, let the linker resolve.\n"));
fprintf (stream, _("\
-x Do not warn when an operand to GETA, a branch,\n\
PUSHJ or JUMP is not known to be within range.\n\
The linker will catch any errors. Implies\n\
-linker-allocated-gregs."));
}
static void
mmix_discard_rest_of_line ()
{
while (*input_line_pointer
&& (! is_end_of_line[(unsigned char) *input_line_pointer]
|| TC_EOL_IN_INSN (input_line_pointer)))
input_line_pointer++;
}
static void
mmix_handle_rest_of_empty_line ()
{
if (mmix_gnu_syntax)
demand_empty_rest_of_line ();
else
{
mmix_discard_rest_of_line ();
input_line_pointer++;
}
}
void
mmix_md_begin ()
{
int i;
const struct mmix_opcode *opcode;
obstack_begin (&mmix_sym_obstack, 0);
lex_type[':'] = (LEX_NAME | LEX_BEGIN_NAME);
mmix_opcode_hash = hash_new ();
real_reg_section
= bfd_make_section_old_way (stdoutput, MMIX_REG_SECTION_NAME);
for (opcode = mmix_opcodes; opcode->name; opcode++)
hash_insert (mmix_opcode_hash, opcode->name, (char *) opcode);
for (i = 0; i < 256; i++)
{
char buf[5];
sprintf (buf, "$%d", i);
symbol_table_insert (symbol_new (buf, reg_section, i,
&zero_address_frag));
}
if (predefined_syms)
{
for (i = 0; mmix_spec_regs[i].name != NULL; i++)
symbol_table_insert (symbol_new (mmix_spec_regs[i].name,
reg_section,
mmix_spec_regs[i].number + 256,
&zero_address_frag));
symbol_table_insert (symbol_new ("ROUND_CURRENT", reg_section, 512,
&zero_address_frag));
symbol_table_insert (symbol_new ("ROUND_OFF", reg_section, 512 + 1,
&zero_address_frag));
symbol_table_insert (symbol_new ("ROUND_UP", reg_section, 512 + 2,
&zero_address_frag));
symbol_table_insert (symbol_new ("ROUND_DOWN", reg_section, 512 + 3,
&zero_address_frag));
symbol_table_insert (symbol_new ("ROUND_NEAR", reg_section, 512 + 4,
&zero_address_frag));
}
}
void
md_assemble (str)
char *str;
{
char *operands = str;
char modified_char = 0;
struct mmix_opcode *instruction;
fragS *opc_fragP = NULL;
int max_operands = 3;
char *opcodep = NULL;
expressionS exp[4];
int n_operands = 0;
for (operands = str;
is_part_of_name (*operands);
++operands)
;
if (ISSPACE (*operands))
{
modified_char = *operands;
*operands++ = '\0';
}
instruction = (struct mmix_opcode *) hash_find (mmix_opcode_hash, str);
if (instruction == NULL)
{
as_bad (_("unknown opcode: `%s'"), str);
pending_label = NULL;
return;
}
if (modified_char != 0)
operands[-1] = modified_char;
input_line_pointer = operands;
if (instruction->type == mmix_type_pseudo)
{
switch (instruction->operands)
{
case mmix_operands_loc:
case mmix_operands_byte:
case mmix_operands_prefix:
case mmix_operands_local:
case mmix_operands_bspec:
case mmix_operands_espec:
if (current_fb_label >= 0)
colon (fb_label_name (current_fb_label, 1));
else if (pending_label != NULL)
{
colon (pending_label);
pending_label = NULL;
}
break;
default:
break;
}
switch (instruction->operands)
{
case mmix_operands_loc:
s_loc (0);
break;
case mmix_operands_byte:
mmix_byte ();
break;
case mmix_operands_wyde:
mmix_cons (2);
break;
case mmix_operands_tetra:
mmix_cons (4);
break;
case mmix_operands_octa:
mmix_cons (8);
break;
case mmix_operands_prefix:
s_prefix (0);
break;
case mmix_operands_local:
mmix_s_local (0);
break;
case mmix_operands_bspec:
s_bspec (0);
break;
case mmix_operands_espec:
s_espec (0);
break;
default:
BAD_CASE (instruction->operands);
}
input_line_pointer--;
if (current_fb_label >= 0)
{
fb_label_instance_inc (current_fb_label);
current_fb_label = -1;
}
if (instruction->operands != mmix_operands_loc)
want_unaligned = 0;
return;
}
if (now_seg == data_section)
{
if (lowest_data_loc != (bfd_vma) -1 && (lowest_data_loc & 3) != 0)
{
if (data_has_contents)
as_bad (_("specified location wasn't TETRA-aligned"));
else if (want_unaligned)
as_bad (_("unaligned data at an absolute location is not supported"));
lowest_data_loc &= ~(bfd_vma) 3;
lowest_data_loc += 4;
}
data_has_contents = 1;
}
else if (now_seg == text_section)
{
if (lowest_text_loc != (bfd_vma) -1 && (lowest_text_loc & 3) != 0)
{
if (text_has_contents)
as_bad (_("specified location wasn't TETRA-aligned"));
else if (want_unaligned)
as_bad (_("unaligned data at an absolute location is not supported"));
lowest_text_loc &= ~(bfd_vma) 3;
lowest_text_loc += 4;
}
text_has_contents = 1;
}
if (last_alignment < 2 && ! want_unaligned)
{
frag_align (2, 0, 0);
record_alignment (now_seg, 2);
last_alignment = 2;
}
else
want_unaligned = 0;
if (pending_label != NULL)
{
colon (pending_label);
pending_label = NULL;
}
switch (instruction->operands)
{
case mmix_operands_reg_yz:
case mmix_operands_pop:
case mmix_operands_regaddr:
case mmix_operands_pushj:
case mmix_operands_get:
case mmix_operands_put:
case mmix_operands_set:
case mmix_operands_save:
case mmix_operands_unsave:
max_operands = 2;
break;
case mmix_operands_sync:
case mmix_operands_jmp:
case mmix_operands_resume:
max_operands = 1;
break;
default:
break;
}
if (! equated_spec_regs
&& (instruction->operands == mmix_operands_get
|| instruction->operands == mmix_operands_put))
n_operands = get_putget_operands (instruction, operands, exp);
else
n_operands = get_operands (max_operands, operands, exp);
if (current_fb_label >= 0)
{
fb_label_instance_inc (current_fb_label);
colon (fb_label_name (current_fb_label, 0));
current_fb_label = -1;
}
opcodep = frag_more (4);
mmix_opcode_frag = opc_fragP = frag_now;
frag_now->fr_opcode = opcodep;
if (OUTPUT_FLAVOR == bfd_target_elf_flavour)
dwarf2_emit_insn (4);
md_number_to_chars (opcodep, instruction->match, 4);
switch (instruction->operands)
{
case mmix_operands_jmp:
if (n_operands == 0 && ! mmix_gnu_syntax)
break;
if (n_operands != 1 || exp[0].X_op == O_register)
{
as_bad (_("invalid operand to opcode %s: `%s'"),
instruction->name, operands);
return;
}
if (expand_op)
frag_var (rs_machine_dependent, 4 * 4, 0,
ENCODE_RELAX (STATE_JMP, STATE_UNDF),
exp[0].X_add_symbol,
exp[0].X_add_number,
opcodep);
else
fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal, 4,
exp + 0, 1, BFD_RELOC_MMIX_ADDR27);
break;
case mmix_operands_pushj:
if (n_operands != 2
|| ((exp[0].X_op == O_constant || exp[0].X_op == O_register)
&& (exp[0].X_add_number > 255 || exp[0].X_add_number < 0)))
{
as_bad (_("invalid operands to opcode %s: `%s'"),
instruction->name, operands);
return;
}
if (exp[0].X_op == O_register || exp[0].X_op == O_constant)
opcodep[1] = exp[0].X_add_number;
else
fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 1,
1, exp + 0, 0, BFD_RELOC_MMIX_REG_OR_BYTE);
if (expand_op)
frag_var (rs_machine_dependent, PUSHJ_MAX_LEN - 4, 0,
ENCODE_RELAX (STATE_PUSHJ, STATE_UNDF),
exp[1].X_add_symbol,
exp[1].X_add_number,
opcodep);
else
fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal, 4,
exp + 1, 1, BFD_RELOC_MMIX_ADDR19);
break;
case mmix_operands_regaddr:
if (n_operands != 2 || exp[1].X_op == O_register)
{
as_bad (_("invalid operands to opcode %s: `%s'"),
instruction->name, operands);
return;
}
if (! expand_op)
fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal, 4,
exp + 1, 1, BFD_RELOC_MMIX_ADDR19);
else if (instruction->type == mmix_type_condbranch)
frag_var (rs_machine_dependent, BCC_MAX_LEN - 4, 0,
ENCODE_RELAX (STATE_BCC, STATE_UNDF),
exp[1].X_add_symbol,
exp[1].X_add_number,
opcodep);
else
frag_var (rs_machine_dependent, GETA_MAX_LEN - 4, 0,
ENCODE_RELAX (STATE_GETA, STATE_UNDF),
exp[1].X_add_symbol,
exp[1].X_add_number,
opcodep);
break;
default:
break;
}
switch (instruction->operands)
{
case mmix_operands_regs:
if (n_operands != 3 || exp[2].X_op == O_constant)
{
as_bad (_("invalid operands to opcode %s: `%s'"),
instruction->name, operands);
return;
}
case mmix_operands_regs_z:
if (n_operands != 3)
{
as_bad (_("invalid operands to opcode %s: `%s'"),
instruction->name, operands);
return;
}
case mmix_operands_reg_yz:
case mmix_operands_roundregs_z:
case mmix_operands_roundregs:
case mmix_operands_regs_z_opt:
case mmix_operands_neg:
case mmix_operands_regaddr:
case mmix_operands_get:
case mmix_operands_set:
case mmix_operands_save:
if (n_operands < 1
|| (exp[0].X_op == O_register && exp[0].X_add_number > 255))
{
as_bad (_("invalid operands to opcode %s: `%s'"),
instruction->name, operands);
return;
}
if (exp[0].X_op == O_register)
opcodep[1] = exp[0].X_add_number;
else
fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 1,
1, exp + 0, 0, BFD_RELOC_MMIX_REG);
break;
default:
;
}
switch (instruction->operands)
{
case mmix_operands_pushgo:
if (n_operands < 2
|| ((exp[0].X_op == O_constant || exp[0].X_op == O_register)
&& (exp[0].X_add_number > 255 || exp[0].X_add_number < 0)))
{
as_bad (_("invalid operands to opcode %s: `%s'"),
instruction->name, operands);
return;
}
else if (exp[0].X_op == O_constant || exp[0].X_op == O_register)
opcodep[1] = exp[0].X_add_number;
else
fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 1,
1, exp + 0, 0, BFD_RELOC_MMIX_REG_OR_BYTE);
break;
case mmix_operands_pop:
if ((n_operands == 0 || n_operands == 1) && ! mmix_gnu_syntax)
break;
case mmix_operands_x_regs_z:
if (n_operands < 1
|| (exp[0].X_op == O_constant
&& (exp[0].X_add_number > 255
|| exp[0].X_add_number < 0)))
{
as_bad (_("invalid operands to opcode %s: `%s'"),
instruction->name, operands);
return;
}
if (exp[0].X_op == O_constant)
opcodep[1] = exp[0].X_add_number;
else
fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 1,
1, exp + 0, 0, BFD_RELOC_8);
default:
;
}
switch (instruction->operands)
{
case mmix_operands_set:
if (n_operands != 2
|| (exp[1].X_op == O_constant
&& (exp[1].X_add_number > 0xffff || exp[1].X_add_number < 0)))
{
as_bad (_("invalid operands to opcode %s: `%s'"),
instruction->name, operands);
return;
}
if (exp[1].X_op == O_constant)
{
md_number_to_chars (opcodep, SETL_INSN_BYTE, 1);
opcodep[2] = (exp[1].X_add_number >> 8) & 255;
opcodep[3] = exp[1].X_add_number & 255;
break;
}
case mmix_operands_x_regs_z:
case mmix_operands_regs:
case mmix_operands_regs_z:
case mmix_operands_pushgo:
case mmix_operands_regs_z_opt:
if ((n_operands != 2 && n_operands != 3)
|| (exp[1].X_op == O_register && exp[1].X_add_number > 255)
|| (n_operands == 3
&& ((exp[2].X_op == O_register
&& exp[2].X_add_number > 255
&& mmix_gnu_syntax)
|| (exp[2].X_op == O_constant
&& (exp[2].X_add_number > 255
|| exp[2].X_add_number < 0)))))
{
as_bad (_("invalid operands to opcode %s: `%s'"),
instruction->name, operands);
return;
}
if (n_operands == 2)
{
symbolS *sym;
opcodep[0] |= IMM_OFFSET_BIT;
if (exp[1].X_op == O_register && exp[1].X_add_number <= 255)
{
opcodep[2] = exp[1].X_add_number;
break;
}
if ((instruction->operands != mmix_operands_regs_z_opt
&& instruction->operands != mmix_operands_x_regs_z
&& instruction->operands != mmix_operands_pushgo)
|| (instruction->type != mmix_type_memaccess_octa
&& instruction->type != mmix_type_memaccess_tetra
&& instruction->type != mmix_type_memaccess_wyde
&& instruction->type != mmix_type_memaccess_byte
&& instruction->type != mmix_type_memaccess_block
&& instruction->type != mmix_type_jsr
&& instruction->type != mmix_type_branch))
{
fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 2,
1, exp + 1, 0, BFD_RELOC_MMIX_REG);
break;
}
sym = make_expr_symbol (exp + 1);
fix_new (opc_fragP, opcodep - opc_fragP->fr_literal + 2,
8, sym, 0, 0, BFD_RELOC_MMIX_BASE_PLUS_OFFSET);
break;
}
if (exp[1].X_op == O_register)
opcodep[2] = exp[1].X_add_number;
else
fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 2,
1, exp + 1, 0, BFD_RELOC_MMIX_REG);
if (exp[2].X_op == O_register && exp[2].X_add_number <= 255)
opcodep[3] = exp[2].X_add_number;
else if (exp[2].X_op == O_constant
|| (exp[2].X_op == O_register && exp[2].X_add_number > 255))
{
opcodep[3] = exp[2].X_add_number;
opcodep[0] |= IMM_OFFSET_BIT;
}
else
fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 3,
1, exp + 2, 0,
(instruction->operands == mmix_operands_set
|| instruction->operands == mmix_operands_regs)
? BFD_RELOC_MMIX_REG : BFD_RELOC_MMIX_REG_OR_BYTE);
break;
case mmix_operands_pop:
if (n_operands == 0 && ! mmix_gnu_syntax)
break;
if (n_operands == 1 && ! mmix_gnu_syntax)
goto a_single_24_bit_number_operand;
case mmix_operands_reg_yz:
if (n_operands != 2
|| exp[1].X_op == O_register
|| (exp[1].X_op == O_constant
&& (exp[1].X_add_number > 0xffff || exp[1].X_add_number < 0)))
{
as_bad (_("invalid operands to opcode %s: `%s'"),
instruction->name, operands);
return;
}
if (exp[1].X_op == O_constant)
{
opcodep[2] = (exp[1].X_add_number >> 8) & 255;
opcodep[3] = exp[1].X_add_number & 255;
}
else
fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 2,
2, exp + 1, 0, BFD_RELOC_16);
break;
case mmix_operands_jmp:
break;
case mmix_operands_roundregs:
if ((n_operands == 3 && exp[2].X_op == O_constant)
|| (n_operands == 2 && exp[1].X_op == O_constant))
{
as_bad (_("invalid operands to opcode %s: `%s'"),
instruction->name, operands);
return;
}
case mmix_operands_roundregs_z:
{
int op2no = n_operands - 1;
if ((n_operands != 2 && n_operands != 3)
|| ((exp[op2no].X_op == O_register
&& exp[op2no].X_add_number > 255)
|| (exp[op2no].X_op == O_constant
&& (exp[op2no].X_add_number > 255
|| exp[op2no].X_add_number < 0)))
|| (n_operands == 3
&& ((exp[1].X_op == O_register
&& exp[1].X_add_number < 512)
|| (exp[1].X_op == O_constant
&& exp[1].X_add_number < 0
&& exp[1].X_add_number > 4)
|| (exp[1].X_op != O_register
&& exp[1].X_op != O_constant))))
{
as_bad (_("invalid operands to opcode %s: `%s'"),
instruction->name, operands);
return;
}
if (n_operands == 3)
opcodep[2] = exp[1].X_add_number & 255;
if (exp[op2no].X_op == O_register)
opcodep[3] = exp[op2no].X_add_number;
else if (exp[op2no].X_op == O_constant)
{
opcodep[3] = exp[op2no].X_add_number;
opcodep[0] |= IMM_OFFSET_BIT;
}
else
fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 3,
1, exp + op2no, 0,
instruction->operands == mmix_operands_roundregs
? BFD_RELOC_MMIX_REG
: BFD_RELOC_MMIX_REG_OR_BYTE);
break;
}
case mmix_operands_sync:
a_single_24_bit_number_operand:
if (n_operands != 1
|| exp[0].X_op == O_register
|| (exp[0].X_op == O_constant
&& (exp[0].X_add_number > 0xffffff || exp[0].X_add_number < 0)))
{
as_bad (_("invalid operands to opcode %s: `%s'"),
instruction->name, operands);
return;
}
if (exp[0].X_op == O_constant)
{
opcodep[1] = (exp[0].X_add_number >> 16) & 255;
opcodep[2] = (exp[0].X_add_number >> 8) & 255;
opcodep[3] = exp[0].X_add_number & 255;
}
else
fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 1,
3, exp + 0, 0, BFD_RELOC_24);
break;
case mmix_operands_neg:
if ((n_operands != 3 && n_operands != 2)
|| (n_operands == 3 && exp[1].X_op == O_register)
|| ((exp[1].X_op == O_constant || exp[1].X_op == O_register)
&& (exp[1].X_add_number > 255 || exp[1].X_add_number < 0))
|| (n_operands == 3
&& ((exp[2].X_op == O_register && exp[2].X_add_number > 255)
|| (exp[2].X_op == O_constant
&& (exp[2].X_add_number > 255
|| exp[2].X_add_number < 0)))))
{
as_bad (_("invalid operands to opcode %s: `%s'"),
instruction->name, operands);
return;
}
if (n_operands == 2)
{
if (exp[1].X_op == O_register)
opcodep[3] = exp[1].X_add_number;
else if (exp[1].X_op == O_constant)
{
opcodep[3] = exp[1].X_add_number;
opcodep[0] |= IMM_OFFSET_BIT;
}
else
fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 3,
1, exp + 1, 0, BFD_RELOC_MMIX_REG_OR_BYTE);
break;
}
if (exp[1].X_op == O_constant)
opcodep[2] = exp[1].X_add_number;
else
fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 2,
1, exp + 1, 0, BFD_RELOC_8);
if (exp[2].X_op == O_register)
opcodep[3] = exp[2].X_add_number;
else if (exp[2].X_op == O_constant)
{
opcodep[3] = exp[2].X_add_number;
opcodep[0] |= IMM_OFFSET_BIT;
}
else
fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 3,
1, exp + 2, 0, BFD_RELOC_MMIX_REG_OR_BYTE);
break;
case mmix_operands_regaddr:
break;
case mmix_operands_get:
if (n_operands != 2
|| (exp[1].X_op == O_register
&& (exp[1].X_add_number < 256 || exp[1].X_add_number >= 512))
|| (exp[1].X_op == O_constant
&& (exp[1].X_add_number < 0 || exp[1].X_add_number > 256))
|| (exp[1].X_op != O_constant && exp[1].X_op != O_register))
{
as_bad (_("invalid operands to opcode %s: `%s'"),
instruction->name, operands);
return;
}
opcodep[3] = exp[1].X_add_number - 256;
break;
case mmix_operands_put:
if (n_operands != 2
|| (exp[0].X_op == O_register
&& (exp[0].X_add_number < 256 || exp[0].X_add_number >= 512))
|| (exp[0].X_op == O_constant
&& (exp[0].X_add_number < 0 || exp[0].X_add_number > 256))
|| (exp[0].X_op != O_constant && exp[0].X_op != O_register))
{
as_bad (_("invalid operands to opcode %s: `%s'"),
instruction->name, operands);
return;
}
opcodep[1] = exp[0].X_add_number - 256;
if (exp[1].X_op == O_register)
opcodep[3] = exp[1].X_add_number;
else if (exp[1].X_op == O_constant)
{
opcodep[3] = exp[1].X_add_number;
opcodep[0] |= IMM_OFFSET_BIT;
}
else
fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 3,
1, exp + 1, 0, BFD_RELOC_MMIX_REG_OR_BYTE);
break;
case mmix_operands_save:
if (n_operands != 2
|| exp[1].X_op != O_constant
|| exp[1].X_add_number != 0)
{
as_bad (_("invalid operands to opcode %s: `%s'"),
instruction->name, operands);
return;
}
break;
case mmix_operands_unsave:
if (n_operands < 2 && ! mmix_gnu_syntax)
{
if (n_operands == 1)
{
if (exp[0].X_op == O_register)
opcodep[3] = exp[0].X_add_number;
else
fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 3,
1, exp, 0, BFD_RELOC_MMIX_REG);
}
break;
}
if (n_operands != 2
|| exp[0].X_op != O_constant
|| exp[0].X_add_number != 0
|| exp[1].X_op == O_constant
|| (exp[1].X_op == O_register
&& exp[1].X_add_number > 255))
{
as_bad (_("invalid operands to opcode %s: `%s'"),
instruction->name, operands);
return;
}
if (exp[1].X_op == O_register)
opcodep[3] = exp[1].X_add_number;
else
fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 3,
1, exp + 1, 0, BFD_RELOC_MMIX_REG);
break;
case mmix_operands_xyz_opt:
if (n_operands == 0 && ! mmix_gnu_syntax)
;
else if (n_operands == 1 && exp[0].X_op != O_register)
{
if (exp[0].X_op == O_constant)
{
if (exp[0].X_add_number > 255*255*255
|| exp[0].X_add_number < 0)
{
as_bad (_("invalid operands to opcode %s: `%s'"),
instruction->name, operands);
return;
}
else
{
opcodep[1] = (exp[0].X_add_number >> 16) & 255;
opcodep[2] = (exp[0].X_add_number >> 8) & 255;
opcodep[3] = exp[0].X_add_number & 255;
}
}
else
fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 1,
3, exp, 0, BFD_RELOC_24);
}
else if (n_operands == 2
&& exp[0].X_op != O_register
&& exp[1].X_op != O_register)
{
if (exp[0].X_op == O_constant)
{
if (exp[0].X_add_number > 255
|| exp[0].X_add_number < 0)
{
as_bad (_("invalid operands to opcode %s: `%s'"),
instruction->name, operands);
return;
}
else
opcodep[1] = exp[0].X_add_number & 255;
}
else
fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 1,
1, exp, 0, BFD_RELOC_8);
if (exp[1].X_op == O_constant)
{
if (exp[1].X_add_number > 255*255
|| exp[1].X_add_number < 0)
{
as_bad (_("invalid operands to opcode %s: `%s'"),
instruction->name, operands);
return;
}
else
{
opcodep[2] = (exp[1].X_add_number >> 8) & 255;
opcodep[3] = exp[1].X_add_number & 255;
}
}
else
fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 2,
2, exp + 1, 0, BFD_RELOC_16);
}
else if (n_operands == 3
&& exp[0].X_op != O_register
&& exp[1].X_op != O_register
&& exp[2].X_op != O_register)
{
if (exp[0].X_op == O_constant)
{
if (exp[0].X_add_number > 255
|| exp[0].X_add_number < 0)
{
as_bad (_("invalid operands to opcode %s: `%s'"),
instruction->name, operands);
return;
}
else
opcodep[1] = exp[0].X_add_number & 255;
}
else
fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 1,
1, exp, 0, BFD_RELOC_8);
if (exp[1].X_op == O_constant)
{
if (exp[1].X_add_number > 255
|| exp[1].X_add_number < 0)
{
as_bad (_("invalid operands to opcode %s: `%s'"),
instruction->name, operands);
return;
}
else
opcodep[2] = exp[1].X_add_number & 255;
}
else
fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 2,
1, exp + 1, 0, BFD_RELOC_8);
if (exp[2].X_op == O_constant)
{
if (exp[2].X_add_number > 255
|| exp[2].X_add_number < 0)
{
as_bad (_("invalid operands to opcode %s: `%s'"),
instruction->name, operands);
return;
}
else
opcodep[3] = exp[2].X_add_number & 255;
}
else
fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 3,
1, exp + 2, 0, BFD_RELOC_8);
}
else if (n_operands <= 3
&& (strcmp (instruction->name, "trip") == 0
|| strcmp (instruction->name, "trap") == 0))
{
if (n_operands == 3)
{
if (exp[0].X_op == O_register)
opcodep[1] = exp[0].X_add_number;
else
fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 1,
1, exp, 0, BFD_RELOC_MMIX_REG_OR_BYTE);
if (exp[1].X_op == O_register)
opcodep[2] = exp[1].X_add_number;
else
fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 2,
1, exp + 1, 0, BFD_RELOC_MMIX_REG_OR_BYTE);
if (exp[2].X_op == O_register)
opcodep[3] = exp[2].X_add_number;
else
fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 3,
1, exp + 2, 0, BFD_RELOC_MMIX_REG_OR_BYTE);
}
else if (n_operands == 2)
{
if (exp[0].X_op == O_register)
opcodep[2] = exp[0].X_add_number;
else
fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 2,
1, exp, 0, BFD_RELOC_MMIX_REG_OR_BYTE);
if (exp[1].X_op == O_register)
opcodep[3] = exp[1].X_add_number;
else
fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 3,
1, exp + 1, 0, BFD_RELOC_MMIX_REG_OR_BYTE);
}
else
{
as_bad (_("unsupported operands to %s: `%s'"),
instruction->name, operands);
return;
}
}
else
{
as_bad (_("invalid operands to opcode %s: `%s'"),
instruction->name, operands);
return;
}
break;
case mmix_operands_resume:
if (n_operands == 0 && ! mmix_gnu_syntax)
break;
if (n_operands != 1
|| exp[0].X_op == O_register
|| (exp[0].X_op == O_constant
&& (exp[0].X_add_number < 0
|| exp[0].X_add_number > 255)))
{
as_bad (_("invalid operands to opcode %s: `%s'"),
instruction->name, operands);
return;
}
if (exp[0].X_op == O_constant)
opcodep[3] = exp[0].X_add_number;
else
fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 3,
1, exp + 0, 0, BFD_RELOC_8);
break;
case mmix_operands_pushj:
break;
default:
BAD_CASE (instruction->operands);
}
}
int
mmix_assemble_return_nonzero (str)
char *str;
{
int last_error_count = had_errors ();
char *s2 = str;
char c;
while (ISALNUM (*s2))
{
if (ISUPPER ((unsigned char) *s2))
*s2 = TOLOWER (*s2);
s2++;
}
for (s2 = str; *s2 && *s2 != '\n'; s2++)
;
c = *s2;
*s2 = 0;
md_assemble (str);
*s2 = c;
return had_errors () == last_error_count;
}
static void
s_prefix (unused)
int unused ATTRIBUTE_UNUSED;
{
char *p;
int c;
SKIP_WHITESPACE ();
p = input_line_pointer;
c = get_symbol_end ();
if (*p == ':' && p[1] == 0)
mmix_current_prefix = NULL;
else
{
obstack_grow (&mmix_sym_obstack, p, strlen (p) + 1);
p = obstack_finish (&mmix_sym_obstack);
if (mmix_current_prefix != NULL || *p == ':')
p = mmix_prefix_name (p);
mmix_current_prefix = p;
}
*input_line_pointer = c;
mmix_handle_rest_of_empty_line ();
}
char *
mmix_prefix_name (shortname)
char *shortname;
{
if (*shortname == ':')
return shortname + 1;
if (mmix_current_prefix == NULL)
as_fatal (_("internal: mmix_prefix_name but empty prefix"));
if (*shortname == '$')
return shortname;
obstack_grow (&mmix_sym_obstack, mmix_current_prefix,
strlen (mmix_current_prefix));
obstack_grow (&mmix_sym_obstack, shortname, strlen (shortname) + 1);
return obstack_finish (&mmix_sym_obstack);
}
static void
mmix_greg_internal (label)
char *label;
{
expressionS *expP = &mmix_raw_gregs[n_of_raw_gregs].exp;
expression (expP);
if (expP->X_op == O_absent)
{
expP->X_op = O_constant;
expP->X_add_number = 0;
expP->X_unsigned = 0;
expP->X_add_symbol = NULL;
expP->X_op_symbol = NULL;
}
mmix_raw_gregs[n_of_raw_gregs].label
= mmix_current_prefix == NULL ? label : mmix_prefix_name (label);
if (n_of_raw_gregs == MAX_GREGS - 1)
as_bad (_("too many GREG registers allocated (max %d)"), MAX_GREGS);
else
n_of_raw_gregs++;
mmix_handle_rest_of_empty_line ();
}
static void
s_greg (unused)
int unused ATTRIBUTE_UNUSED;
{
char *p;
char c;
p = input_line_pointer;
c = get_symbol_end ();
if (! is_end_of_line[(unsigned char) c])
input_line_pointer++;
if (*p)
{
obstack_grow (&mmix_sym_obstack, p, strlen (p) + 1);
mmix_greg_internal (obstack_finish (&mmix_sym_obstack));
}
else
mmix_greg_internal (NULL);
}
static void
s_bspec (unused)
int unused ATTRIBUTE_UNUSED;
{
asection *expsec;
asection *sec;
char secname[sizeof (MMIX_OTHER_SPEC_SECTION_PREFIX) + 20]
= MMIX_OTHER_SPEC_SECTION_PREFIX;
expressionS exp;
int n;
expsec = expression (&exp);
mmix_handle_rest_of_empty_line ();
if (doing_bspec)
{
as_bad (_("BSPEC already active. Nesting is not supported."));
return;
}
if (exp.X_op != O_constant
|| expsec != absolute_section
|| exp.X_add_number < 0
|| exp.X_add_number > 65535)
{
as_bad (_("invalid BSPEC expression"));
exp.X_add_number = 0;
}
n = (int) exp.X_add_number;
sprintf (secname + strlen (MMIX_OTHER_SPEC_SECTION_PREFIX), "%d", n);
sec = bfd_get_section_by_name (stdoutput, secname);
if (sec == NULL)
{
char *newsecname = xstrdup (secname);
sec = bfd_make_section (stdoutput, newsecname);
if (sec == NULL)
as_fatal (_("can't create section %s"), newsecname);
if (!bfd_set_section_flags (stdoutput, sec,
bfd_get_section_flags (stdoutput, sec)
| SEC_READONLY))
as_fatal (_("can't set section flags for section %s"), newsecname);
}
obj_elf_section_change_hook ();
subseg_set (sec, 0);
as_where (&bspec_file, &bspec_line);
doing_bspec = 1;
}
static void
s_espec (unused)
int unused ATTRIBUTE_UNUSED;
{
if (! doing_bspec)
{
as_bad (_("ESPEC without preceding BSPEC"));
return;
}
mmix_handle_rest_of_empty_line ();
doing_bspec = 0;
obj_elf_previous (0);
}
static void
mmix_s_local (unused)
int unused ATTRIBUTE_UNUSED;
{
expressionS exp;
expression (&exp);
if (exp.X_op == O_absent)
{
as_bad (_("missing local expression"));
return;
}
else if (exp.X_op == O_register)
{
exp.X_op = O_constant;
}
fix_new_exp (frag_now, 0, 0, &exp, 0, BFD_RELOC_MMIX_LOCAL);
mmix_handle_rest_of_empty_line ();
}
int
md_estimate_size_before_relax (fragP, segment)
fragS *fragP;
segT segment;
{
int length;
#define HANDLE_RELAXABLE(state) \
case ENCODE_RELAX (state, STATE_UNDF): \
if (fragP->fr_symbol != NULL \
&& S_GET_SEGMENT (fragP->fr_symbol) == segment \
&& !S_IS_WEAK (fragP->fr_symbol)) \
{ \
\
fragP->fr_subtype \
= ENCODE_RELAX (state, STATE_ZERO); \
} \
break;
switch (fragP->fr_subtype)
{
HANDLE_RELAXABLE (STATE_GETA);
HANDLE_RELAXABLE (STATE_BCC);
HANDLE_RELAXABLE (STATE_JMP);
case ENCODE_RELAX (STATE_PUSHJ, STATE_UNDF):
if (fragP->fr_symbol != NULL
&& S_GET_SEGMENT (fragP->fr_symbol) == segment
&& !S_IS_WEAK (fragP->fr_symbol))
fragP->fr_subtype = ENCODE_RELAX (STATE_PUSHJ, STATE_ZERO);
else if (pushj_stubs)
fragP->fr_subtype = ENCODE_RELAX (STATE_PUSHJSTUB, STATE_ZERO);
case ENCODE_RELAX (STATE_PUSHJ, STATE_ZERO):
case ENCODE_RELAX (STATE_PUSHJSTUB, STATE_ZERO):
seg_info (segment)->tc_segment_info_data.last_stubfrag = fragP;
break;
case ENCODE_RELAX (STATE_GETA, STATE_ZERO):
case ENCODE_RELAX (STATE_BCC, STATE_ZERO):
case ENCODE_RELAX (STATE_JMP, STATE_ZERO):
break;
case STATE_GREG_DEF:
length = fragP->tc_frag_data != NULL ? 0 : 8;
fragP->fr_var = length;
return length;
break;
default:
BAD_CASE (fragP->fr_subtype);
}
length = mmix_relax_table[fragP->fr_subtype].rlx_length;
fragP->fr_var = length;
return length;
}
char *
md_atof (type, litP, sizeP)
int type;
char *litP;
int *sizeP;
{
int prec;
LITTLENUM_TYPE words[4];
char *t;
int i;
switch (type)
{
case 'f':
case 'r':
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 * 2;
for (i = 0; i < prec; i++)
{
md_number_to_chars (litP, (valueT) words[i], 2);
litP += 2;
}
return NULL;
}
void
md_convert_frag (abfd, sec, fragP)
bfd *abfd ATTRIBUTE_UNUSED;
segT sec ATTRIBUTE_UNUSED;
fragS *fragP;
{
char *var_partp;
char *opcodep;
int var_part_size = 0;
symbolS *symbolP;
unsigned long var_part_offset;
fragS *opc_fragP = fragP->tc_frag_data;
fixS *tmpfixP;
bfd_vma target_address;
bfd_vma opcode_address;
know (fragP->fr_type == rs_machine_dependent);
var_part_offset = fragP->fr_fix;
var_partp = fragP->fr_literal + var_part_offset;
opcodep = fragP->fr_opcode;
symbolP = fragP->fr_symbol;
target_address
= ((symbolP ? S_GET_VALUE (symbolP) : 0) + fragP->fr_offset);
opcode_address = fragP->fr_address + fragP->fr_fix - 4;
switch (fragP->fr_subtype)
{
case ENCODE_RELAX (STATE_PUSHJSTUB, STATE_ZERO):
mmix_set_geta_branch_offset (opcodep, 0);
tmpfixP = fix_new (opc_fragP, opcodep - opc_fragP->fr_literal, 8,
fragP->fr_symbol, fragP->fr_offset, 1,
BFD_RELOC_MMIX_PUSHJ_STUBBABLE);
COPY_FR_WHERE_TO_FX (fragP, tmpfixP);
var_part_size = 0;
break;
case ENCODE_RELAX (STATE_GETA, STATE_ZERO):
case ENCODE_RELAX (STATE_BCC, STATE_ZERO):
case ENCODE_RELAX (STATE_PUSHJ, STATE_ZERO):
mmix_set_geta_branch_offset (opcodep, target_address - opcode_address);
if (linkrelax)
{
tmpfixP
= fix_new (opc_fragP, opcodep - opc_fragP->fr_literal, 4,
fragP->fr_symbol, fragP->fr_offset, 1,
BFD_RELOC_MMIX_ADDR19);
COPY_FR_WHERE_TO_FX (fragP, tmpfixP);
}
var_part_size = 0;
break;
case ENCODE_RELAX (STATE_JMP, STATE_ZERO):
mmix_set_jmp_offset (opcodep, target_address - opcode_address);
if (linkrelax)
{
tmpfixP
= fix_new (opc_fragP, opcodep - opc_fragP->fr_literal, 4,
fragP->fr_symbol, fragP->fr_offset, 1,
BFD_RELOC_MMIX_ADDR27);
COPY_FR_WHERE_TO_FX (fragP, tmpfixP);
}
var_part_size = 0;
break;
case STATE_GREG_DEF:
if (fragP->tc_frag_data == NULL)
{
md_number_to_chars (var_partp, 0, 8);
tmpfixP
= fix_new (fragP, var_partp - fragP->fr_literal, 8,
fragP->fr_symbol, fragP->fr_offset, 0, BFD_RELOC_64);
COPY_FR_WHERE_TO_FX (fragP, tmpfixP);
mmix_gregs[n_of_cooked_gregs++] = tmpfixP;
var_part_size = 8;
}
else
var_part_size = 0;
break;
#define HANDLE_MAX_RELOC(state, reloc) \
case ENCODE_RELAX (state, STATE_MAX): \
var_part_size \
= mmix_relax_table[ENCODE_RELAX (state, STATE_MAX)].rlx_length; \
mmix_fill_nops (var_partp, var_part_size / 4); \
if (warn_on_expansion) \
as_warn_where (fragP->fr_file, fragP->fr_line, \
_("operand out of range, instruction expanded")); \
tmpfixP = fix_new (fragP, var_partp - fragP->fr_literal - 4, 8, \
fragP->fr_symbol, fragP->fr_offset, 1, reloc); \
COPY_FR_WHERE_TO_FX (fragP, tmpfixP); \
break
HANDLE_MAX_RELOC (STATE_GETA, BFD_RELOC_MMIX_GETA);
HANDLE_MAX_RELOC (STATE_BCC, BFD_RELOC_MMIX_CBRANCH);
HANDLE_MAX_RELOC (STATE_PUSHJ, BFD_RELOC_MMIX_PUSHJ);
HANDLE_MAX_RELOC (STATE_JMP, BFD_RELOC_MMIX_JMP);
default:
BAD_CASE (fragP->fr_subtype);
break;
}
fragP->fr_fix += var_part_size;
fragP->fr_var = 0;
}
void
md_apply_fix3 (fixP, valP, segment)
fixS * fixP;
valueT * valP;
segT segment;
{
char *buf = fixP->fx_where + fixP->fx_frag->fr_literal;
offsetT val = (offsetT) * valP;
segT symsec
= (fixP->fx_addsy == NULL
? absolute_section : S_GET_SEGMENT (fixP->fx_addsy));
if (fixP->fx_addsy != NULL
&& (! S_IS_DEFINED (fixP->fx_addsy)
|| S_IS_WEAK (fixP->fx_addsy)
|| (fixP->fx_pcrel && symsec != segment)
|| (! fixP->fx_pcrel
&& symsec != absolute_section
&& ((fixP->fx_r_type != BFD_RELOC_MMIX_REG
&& fixP->fx_r_type != BFD_RELOC_MMIX_REG_OR_BYTE)
|| symsec != reg_section))))
{
fixP->fx_done = 0;
return;
}
else if (fixP->fx_r_type == BFD_RELOC_MMIX_LOCAL
|| fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
|| fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
{
fixP->fx_done = 0;
return;
}
else
fixP->fx_done = 1;
switch (fixP->fx_r_type)
{
case BFD_RELOC_64:
case BFD_RELOC_32:
case BFD_RELOC_24:
case BFD_RELOC_16:
case BFD_RELOC_8:
case BFD_RELOC_64_PCREL:
case BFD_RELOC_32_PCREL:
case BFD_RELOC_24_PCREL:
case BFD_RELOC_16_PCREL:
case BFD_RELOC_8_PCREL:
md_number_to_chars (buf, val, fixP->fx_size);
break;
case BFD_RELOC_MMIX_ADDR19:
if (expand_op)
{
BAD_CASE (fixP->fx_r_type);
break;
}
case BFD_RELOC_MMIX_GETA:
case BFD_RELOC_MMIX_CBRANCH:
case BFD_RELOC_MMIX_PUSHJ:
case BFD_RELOC_MMIX_PUSHJ_STUBBABLE:
if (val < -(((offsetT) 1 << 19)/2)
|| val >= ((offsetT) 1 << 19)/2 - 1
|| (val & 3) != 0)
{
if (warn_on_expansion)
as_warn_where (fixP->fx_file, fixP->fx_line,
_("operand out of range"));
fixP->fx_done = 0;
val = 0;
}
mmix_set_geta_branch_offset (buf, val);
break;
case BFD_RELOC_MMIX_ADDR27:
if (expand_op)
{
BAD_CASE (fixP->fx_r_type);
break;
}
case BFD_RELOC_MMIX_JMP:
if (val < -(((offsetT) 1 << 27)/2)
|| val >= ((offsetT) 1 << 27)/2 - 1
|| (val & 3) != 0)
{
if (warn_on_expansion)
as_warn_where (fixP->fx_file, fixP->fx_line,
_("operand out of range"));
fixP->fx_done = 0;
val = 0;
}
mmix_set_jmp_offset (buf, val);
break;
case BFD_RELOC_MMIX_REG_OR_BYTE:
if (fixP->fx_addsy != NULL
&& (S_GET_SEGMENT (fixP->fx_addsy) != reg_section
|| S_GET_VALUE (fixP->fx_addsy) > 255)
&& S_GET_SEGMENT (fixP->fx_addsy) != absolute_section)
{
as_bad_where (fixP->fx_file, fixP->fx_line,
_("invalid operands"));
fixP->fx_done = 1;
}
buf[0] = val;
if ((fixP->fx_where & 3) == 3
&& (fixP->fx_addsy == NULL
|| S_GET_SEGMENT (fixP->fx_addsy) == absolute_section))
buf[-3] |= IMM_OFFSET_BIT;
break;
case BFD_RELOC_MMIX_REG:
if (fixP->fx_addsy == NULL
|| S_GET_SEGMENT (fixP->fx_addsy) != reg_section
|| S_GET_VALUE (fixP->fx_addsy) > 255)
{
as_bad_where (fixP->fx_file, fixP->fx_line,
_("invalid operands"));
fixP->fx_done = 1;
}
*buf = val;
break;
case BFD_RELOC_MMIX_BASE_PLUS_OFFSET:
fixP->fx_done = 0;
return;
case BFD_RELOC_MMIX_PUSHJ_1:
case BFD_RELOC_MMIX_PUSHJ_2:
case BFD_RELOC_MMIX_PUSHJ_3:
case BFD_RELOC_MMIX_CBRANCH_J:
case BFD_RELOC_MMIX_CBRANCH_1:
case BFD_RELOC_MMIX_CBRANCH_2:
case BFD_RELOC_MMIX_CBRANCH_3:
case BFD_RELOC_MMIX_GETA_1:
case BFD_RELOC_MMIX_GETA_2:
case BFD_RELOC_MMIX_GETA_3:
case BFD_RELOC_MMIX_JMP_1:
case BFD_RELOC_MMIX_JMP_2:
case BFD_RELOC_MMIX_JMP_3:
default:
BAD_CASE (fixP->fx_r_type);
break;
}
if (fixP->fx_done)
fixP->fx_offset = val;
}
static int
cmp_greg_val_greg_symbol_fixes (p1, p2)
const PTR p1;
const PTR p2;
{
offsetT val1 = *(offsetT *) p1;
offsetT val2 = ((struct mmix_symbol_greg_fixes *) p2)->offs;
if (val1 >= val2 && val1 < val2 + 255)
return 0;
if (val1 > val2)
return 1;
return -1;
}
arelent *
tc_gen_reloc (section, fixP)
asection *section ATTRIBUTE_UNUSED;
fixS *fixP;
{
bfd_signed_vma val
= fixP->fx_offset
+ (fixP->fx_addsy != NULL
&& !S_IS_WEAK (fixP->fx_addsy)
&& !S_IS_COMMON (fixP->fx_addsy)
? S_GET_VALUE (fixP->fx_addsy) : 0);
arelent *relP;
bfd_reloc_code_real_type code = BFD_RELOC_NONE;
char *buf = fixP->fx_where + fixP->fx_frag->fr_literal;
symbolS *addsy = fixP->fx_addsy;
asection *addsec = addsy == NULL ? NULL : S_GET_SEGMENT (addsy);
asymbol *baddsy = addsy != NULL ? symbol_get_bfdsym (addsy) : NULL;
bfd_vma addend
= val - (baddsy == NULL || S_IS_COMMON (addsy) || S_IS_WEAK (addsy)
? 0 : bfd_asymbol_value (baddsy));
if (bfd_section_size (section->owner, section) == 0)
as_bad_where
(fixP->fx_file, fixP->fx_line,
fixP->fx_r_type == BFD_RELOC_MMIX_LOCAL
? _("directive LOCAL must be placed in code or data")
: _("internal confusion: relocation in a section without contents"));
switch (fixP->fx_r_type)
{
case BFD_RELOC_64:
case BFD_RELOC_32:
case BFD_RELOC_24:
case BFD_RELOC_16:
case BFD_RELOC_8:
code = fixP->fx_r_type;
if (addsy == NULL || bfd_is_abs_section (addsec))
{
md_number_to_chars (buf, val, fixP->fx_size);
return NULL;
}
break;
case BFD_RELOC_64_PCREL:
case BFD_RELOC_32_PCREL:
case BFD_RELOC_24_PCREL:
case BFD_RELOC_16_PCREL:
case BFD_RELOC_8_PCREL:
case BFD_RELOC_MMIX_LOCAL:
case BFD_RELOC_VTABLE_INHERIT:
case BFD_RELOC_VTABLE_ENTRY:
case BFD_RELOC_MMIX_GETA:
case BFD_RELOC_MMIX_GETA_1:
case BFD_RELOC_MMIX_GETA_2:
case BFD_RELOC_MMIX_GETA_3:
case BFD_RELOC_MMIX_CBRANCH:
case BFD_RELOC_MMIX_CBRANCH_J:
case BFD_RELOC_MMIX_CBRANCH_1:
case BFD_RELOC_MMIX_CBRANCH_2:
case BFD_RELOC_MMIX_CBRANCH_3:
case BFD_RELOC_MMIX_PUSHJ:
case BFD_RELOC_MMIX_PUSHJ_1:
case BFD_RELOC_MMIX_PUSHJ_2:
case BFD_RELOC_MMIX_PUSHJ_3:
case BFD_RELOC_MMIX_PUSHJ_STUBBABLE:
case BFD_RELOC_MMIX_JMP:
case BFD_RELOC_MMIX_JMP_1:
case BFD_RELOC_MMIX_JMP_2:
case BFD_RELOC_MMIX_JMP_3:
case BFD_RELOC_MMIX_ADDR19:
case BFD_RELOC_MMIX_ADDR27:
code = fixP->fx_r_type;
break;
case BFD_RELOC_MMIX_REG_OR_BYTE:
if (addsy != NULL
&& (bfd_is_und_section (addsec)
|| strcmp (bfd_get_section_name (addsec->owner, addsec),
MMIX_REG_CONTENTS_SECTION_NAME) == 0))
{
code = fixP->fx_r_type;
break;
}
if (addsy != NULL
&& (S_GET_SEGMENT (addsy) != real_reg_section
|| val > 255
|| val < 0)
&& ! bfd_is_abs_section (addsec))
goto badop;
if ((fixP->fx_where & 3) == 3
&& (addsy == NULL || bfd_is_abs_section (addsec)))
buf[-3] |= IMM_OFFSET_BIT;
buf[0] = val;
return NULL;
case BFD_RELOC_MMIX_BASE_PLUS_OFFSET:
if (addsy != NULL
&& strcmp (bfd_get_section_name (addsec->owner, addsec),
MMIX_REG_CONTENTS_SECTION_NAME) == 0)
{
code = BFD_RELOC_MMIX_REG;
break;
}
if (addsy == NULL || addsec != real_reg_section)
{
struct mmix_symbol_gregs *gregs;
struct mmix_symbol_greg_fixes *fix;
if (S_IS_DEFINED (addsy)
&& !bfd_is_com_section (addsec)
&& !S_IS_WEAK (addsy))
{
if (! symbol_section_p (addsy) && ! bfd_is_abs_section (addsec))
as_fatal (_("internal: BFD_RELOC_MMIX_BASE_PLUS_OFFSET not resolved to section"));
if (lowest_data_loc != (bfd_vma) -1
&& (bfd_vma) val + 256 > lowest_data_loc
&& bfd_is_abs_section (addsec))
{
val -= (offsetT) lowest_data_loc;
addsy = section_symbol (data_section);
}
else if (lowest_text_loc != (bfd_vma) -1
&& (bfd_vma) val + 256 > lowest_text_loc
&& bfd_is_abs_section (addsec))
{
val -= (offsetT) lowest_text_loc;
addsy = section_symbol (text_section);
}
}
gregs = *symbol_get_tc (addsy);
if (gregs == NULL
|| (fix = bsearch (&val, gregs->greg_fixes, gregs->n_gregs,
sizeof (gregs->greg_fixes[0]),
cmp_greg_val_greg_symbol_fixes)) == NULL
|| fix->offs > val
|| fix->offs + 255 < val)
{
if (allocate_undefined_gregs_in_linker)
{
code = fixP->fx_r_type;
break;
}
else
as_bad_where (fixP->fx_file, fixP->fx_line,
_("no suitable GREG definition for operands"));
return NULL;
}
else
{
buf[1] = val - fix->offs;
code = BFD_RELOC_MMIX_REG;
baddsy
= (bfd_get_section_by_name (stdoutput,
MMIX_REG_CONTENTS_SECTION_NAME)
->symbol);
addend = fix->fix->fx_frag->fr_address + fix->fix->fx_where;
}
}
else if (S_GET_VALUE (addsy) > 255)
as_bad_where (fixP->fx_file, fixP->fx_line,
_("invalid operands"));
else
{
*buf = val;
return NULL;
}
break;
case BFD_RELOC_MMIX_REG:
if (addsy != NULL
&& (bfd_is_und_section (addsec)
|| strcmp (bfd_get_section_name (addsec->owner, addsec),
MMIX_REG_CONTENTS_SECTION_NAME) == 0))
{
code = fixP->fx_r_type;
break;
}
if (addsy != NULL
&& (addsec != real_reg_section
|| val > 255
|| val < 0)
&& ! bfd_is_und_section (addsec))
;
else
{
buf[0] = val;
return NULL;
}
badop:
default:
as_bad_where
(fixP->fx_file, fixP->fx_line,
_("operands were not reducible at assembly-time"));
fixP->fx_addsy = NULL;
return NULL;
}
relP = (arelent *) xmalloc (sizeof (arelent));
assert (relP != 0);
relP->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
*relP->sym_ptr_ptr = baddsy;
relP->address = fixP->fx_frag->fr_address + fixP->fx_where;
relP->addend = addend;
relP->howto = bfd_reloc_type_lookup (stdoutput, code);
if (! relP->howto)
{
const char *name;
name = S_GET_NAME (addsy);
if (name == NULL)
name = _("<unknown>");
as_fatal (_("cannot generate relocation type for symbol %s, code %s"),
name, bfd_get_reloc_code_name (code));
}
return relP;
}
void
mmix_handle_mmixal ()
{
char *s0 = input_line_pointer;
char *s;
char *label = NULL;
char c;
if (pending_label != NULL)
as_fatal (_("internal: unhandled label %s"), pending_label);
if (mmix_gnu_syntax)
return;
if (input_line_pointer[0] == '.')
{
label_without_colon_this_line = 0;
return;
}
while (1)
{
if (*s0 == 0 || is_end_of_line[(unsigned int) *s0])
return;
if (! ISSPACE (*s0))
break;
s0++;
}
if (ISDIGIT (input_line_pointer[0])
&& input_line_pointer[1] == 'H'
&& ISSPACE (input_line_pointer[2]))
{
char *s;
current_fb_label = input_line_pointer[0] - '0';
input_line_pointer[1] = input_line_pointer[-1];
input_line_pointer += 2;
s = input_line_pointer;
while (*s && ISSPACE (*s) && ! is_end_of_line[(unsigned int) *s])
s++;
if (is_end_of_line[(unsigned int) *s])
{
char *name;
unsigned int line;
as_where (&name, &line);
as_bad_where (name, line + 1,
_("[0-9]H labels may not appear alone on a line"));
current_fb_label = -1;
}
if (*s == '.')
{
char *name;
unsigned int line;
as_where (&name, &line);
as_bad_where (name, line + 1,
_("[0-9]H labels do not mix with dot-pseudos"));
current_fb_label = -1;
}
}
else
{
current_fb_label = -1;
if (is_name_beginner (input_line_pointer[0]))
label = input_line_pointer;
}
s0 = input_line_pointer;
while (*s0 && is_part_of_name (*s0))
s0++;
if (s0 != input_line_pointer && s0[-1] == ':'
&& (s0 - 2 != input_line_pointer
|| ! ISDIGIT (s0[-2])))
s0[-1] = ' ';
else if (label != NULL)
{
int len_0 = s0 - label + 1;
obstack_grow (&mmix_sym_obstack, label, len_0);
pending_label = obstack_finish (&mmix_sym_obstack);
pending_label[len_0 - 1] = 0;
}
while (*s0 && ISSPACE (*s0) && ! is_end_of_line[(unsigned int) *s0])
s0++;
if (pending_label != NULL && is_end_of_line[(unsigned int) *s0])
pending_label = NULL;
s = s0;
while (*s)
{
c = *s++;
if (is_end_of_line[(unsigned int) c])
break;
if (c == MAGIC_FB_BACKWARD_CHAR || c == MAGIC_FB_FORWARD_CHAR)
as_bad (_("invalid characters in input"));
}
s = s0;
while (*s
&& ! ISSPACE (*s)
&& *s != ';'
&& ! is_end_of_line[(unsigned int) *s])
s++;
while (*s
&& ISSPACE (*s)
&& *s != ';'
&& ! is_end_of_line[(unsigned int) *s])
s++;
while ((c = *s) != 0
&& ! ISSPACE (c)
&& c != ';'
&& ! is_end_of_line[(unsigned int) c])
{
if (c == '"')
{
s++;
while (*s
&& *s != '"'
&& (! is_end_of_line[(unsigned int) *s] || *s == ';'))
s++;
if (*s == '"')
s++;
}
else if (ISDIGIT (c))
{
if ((s[1] != 'B' && s[1] != 'F')
|| is_part_of_name (s[-1])
|| is_part_of_name (s[2]))
s++;
else
{
s[0] = (s[1] == 'B'
? MAGIC_FB_BACKWARD_CHAR : MAGIC_FB_FORWARD_CHAR);
s[1] = c;
}
}
else
s++;
}
while (*s
&& ISSPACE (*s)
&& *s != ';'
&& !is_end_of_line[(unsigned int) *s])
s++;
mmix_next_semicolon_is_eoln = (*s == ';');
s = s0;
if (s[0] == 'I' && s[1] == 'S' && ISSPACE (s[2]))
{
*s = '=';
s[1] = ' ';
input_line_pointer = s;
bump_line_counters ();
if (current_fb_label >= 0)
{
char *fb_name;
fb_name = fb_label_name (current_fb_label, 1);
obstack_grow (&mmix_sym_obstack, fb_name, strlen (fb_name) + 1);
equals (obstack_finish (&mmix_sym_obstack), 0);
fb_label_instance_inc (current_fb_label);
current_fb_label = -1;
}
else
{
if (pending_label == NULL)
as_bad (_("empty label field for IS"));
else
equals (pending_label, 0);
pending_label = NULL;
}
mmix_handle_rest_of_empty_line ();
input_line_pointer--;
input_line_pointer[-1] = ' ';
}
else if (s[0] == 'G'
&& s[1] == 'R'
&& strncmp (s, "GREG", 4) == 0
&& (ISSPACE (s[4]) || is_end_of_line[(unsigned char) s[4]]))
{
input_line_pointer = s + 4;
bump_line_counters ();
if (current_fb_label >= 0)
{
char *fb_name;
fb_name = fb_label_name (current_fb_label, 1);
obstack_1grow (&mmix_sym_obstack, ':');
obstack_grow (&mmix_sym_obstack, fb_name, strlen (fb_name) + 1);
mmix_greg_internal (obstack_finish (&mmix_sym_obstack));
fb_label_instance_inc (current_fb_label);
current_fb_label = -1;
}
else
mmix_greg_internal (pending_label);
input_line_pointer--;
input_line_pointer[-1] = ' ';
pending_label = NULL;
}
else if (pending_label != NULL)
{
input_line_pointer += strlen (pending_label);
input_line_pointer[-1] = '\n';
}
}
void
mmix_fb_label (expP)
expressionS *expP;
{
symbolS *sym;
char *fb_internal_name;
if (mmix_gnu_syntax
|| (input_line_pointer[0] != MAGIC_FB_BACKWARD_CHAR
&& input_line_pointer[0] != MAGIC_FB_FORWARD_CHAR))
return;
fb_internal_name
= fb_label_name (input_line_pointer[1] - '0',
(input_line_pointer[0] == MAGIC_FB_FORWARD_CHAR ? 1 : 0)
+ ((input_line_pointer[1] - '0' == current_fb_label
&& input_line_pointer[0] == MAGIC_FB_FORWARD_CHAR)
? 1 : 0));
input_line_pointer += 2;
sym = symbol_find_or_make (fb_internal_name);
if (S_GET_SEGMENT (sym) == absolute_section)
{
expP->X_op = O_constant;
expP->X_add_number = S_GET_VALUE (sym);
}
else
{
expP->X_op = O_symbol;
expP->X_add_symbol = sym;
expP->X_add_number = 0;
}
}
int
mmix_force_relocation (fixP)
fixS *fixP;
{
if (fixP->fx_r_type == BFD_RELOC_MMIX_LOCAL
|| fixP->fx_r_type == BFD_RELOC_MMIX_BASE_PLUS_OFFSET)
return 1;
if (linkrelax)
return 1;
if (fixP->fx_pcrel)
return 1;
return generic_force_reloc (fixP);
}
long
md_pcrel_from_section (fixP, sec)
fixS * fixP;
segT sec;
{
if (fixP->fx_addsy != (symbolS *) NULL
&& (! S_IS_DEFINED (fixP->fx_addsy)
|| S_GET_SEGMENT (fixP->fx_addsy) != sec))
{
return 0;
}
return (fixP->fx_frag->fr_address + fixP->fx_where);
}
void
mmix_adjust_symtab ()
{
symbolS *sym;
symbolS *regsec = section_symbol (reg_section);
for (sym = symbol_rootP; sym != NULL; sym = symbol_next (sym))
if (S_GET_SEGMENT (sym) == reg_section)
{
if (sym == regsec)
{
if (S_IS_EXTERN (sym) || symbol_used_in_reloc_p (sym))
abort ();
symbol_remove (sym, &symbol_rootP, &symbol_lastP);
}
else
S_SET_SEGMENT (sym, real_reg_section);
}
}
int
mmix_label_without_colon_this_line ()
{
int retval = label_without_colon_this_line;
if (! mmix_gnu_syntax)
label_without_colon_this_line = 1;
return retval;
}
long
mmix_md_relax_frag (seg, fragP, stretch)
segT seg;
fragS *fragP;
long stretch;
{
switch (fragP->fr_subtype)
{
case STATE_GREG_DEF:
return 0;
case ENCODE_RELAX (STATE_PUSHJ, STATE_ZERO):
{
offsetT aim;
addressT target;
addressT address;
symbolS *symbolP;
target = fragP->fr_offset;
address = fragP->fr_address;
symbolP = fragP->fr_symbol;
if (symbolP)
{
fragS *sym_frag;
sym_frag = symbol_get_frag (symbolP);
know (S_GET_SEGMENT (symbolP) != absolute_section
|| sym_frag == &zero_address_frag);
target += S_GET_VALUE (symbolP);
if (stretch != 0
&& sym_frag->relax_marker != fragP->relax_marker
&& S_GET_SEGMENT (symbolP) == seg)
target += stretch;
}
aim = target - address - fragP->fr_fix;
if (aim >= PUSHJ_0B && aim <= PUSHJ_0F)
{
segment_info_type *seginfo = seg_info (seg);
if (fragP == seginfo->tc_segment_info_data.last_stubfrag)
seginfo->tc_segment_info_data.nstubs = 0;
return 0;
}
fragP->fr_subtype = ENCODE_RELAX (STATE_PUSHJSTUB, STATE_ZERO);
}
case ENCODE_RELAX (STATE_PUSHJSTUB, STATE_ZERO):
{
segment_info_type *seginfo = seg_info (seg);
fragS *lastfrag = seginfo->frchainP->frch_last;
relax_substateT prev_type = fragP->fr_subtype;
know (lastfrag->fr_type == rs_fill
&& lastfrag->fr_fix == 0
&& lastfrag->fr_var == 0);
if ((lastfrag->fr_address
+ stretch
+ PUSHJ_MAX_LEN * seginfo->tc_segment_info_data.nstubs)
- (fragP->fr_address + fragP->fr_fix)
> GETA_0F
|| !pushj_stubs)
fragP->fr_subtype = mmix_relax_table[prev_type].rlx_more;
else
seginfo->tc_segment_info_data.nstubs++;
if (fragP == seginfo->tc_segment_info_data.last_stubfrag)
seginfo->tc_segment_info_data.nstubs = 0;
return
(mmix_relax_table[fragP->fr_subtype].rlx_length
- mmix_relax_table[prev_type].rlx_length);
}
case ENCODE_RELAX (STATE_PUSHJ, STATE_MAX):
{
segment_info_type *seginfo = seg_info (seg);
if (fragP == seginfo->tc_segment_info_data.last_stubfrag)
seginfo->tc_segment_info_data.nstubs = 0;
return 0;
}
default:
return relax_frag (seg, fragP, stretch);
case STATE_GREG_UNDF:
BAD_CASE (fragP->fr_subtype);
}
as_fatal (_("internal: unexpected relax type %d:%d"),
fragP->fr_type, fragP->fr_subtype);
return 0;
}
void
mmix_md_end ()
{
fragS *fragP;
symbolS *mainsym;
int i;
fragS *mmix_reg_contents_frags = NULL;
mmix_current_prefix = NULL;
if (doing_bspec)
as_bad_where (bspec_file, bspec_line, _("BSPEC without ESPEC."));
if (text_has_contents && lowest_text_loc != (bfd_vma) -1)
{
symbolS *symbolP;
char locsymbol[sizeof (":") - 1
+ sizeof (MMIX_LOC_SECTION_START_SYMBOL_PREFIX) - 1
+ sizeof (".text")];
sprintf (locsymbol, ":%s%s", MMIX_LOC_SECTION_START_SYMBOL_PREFIX,
".text");
symbolP
= symbol_new (locsymbol, absolute_section, lowest_text_loc,
&zero_address_frag);
S_SET_EXTERNAL (symbolP);
}
if (data_has_contents && lowest_data_loc != (bfd_vma) -1)
{
symbolS *symbolP;
char locsymbol[sizeof (":") - 1
+ sizeof (MMIX_LOC_SECTION_START_SYMBOL_PREFIX) - 1
+ sizeof (".data")];
sprintf (locsymbol, ":%s%s", MMIX_LOC_SECTION_START_SYMBOL_PREFIX,
".data");
symbolP
= symbol_new (locsymbol, absolute_section, lowest_data_loc,
&zero_address_frag);
S_SET_EXTERNAL (symbolP);
}
mainsym = symbol_find (MMIX_START_SYMBOL_NAME);
if (mainsym != NULL && ! mmix_gnu_syntax)
{
symbol_get_bfdsym (mainsym)->flags |= BSF_FUNCTION;
S_SET_EXTERNAL (mainsym);
}
if (n_of_raw_gregs != 0)
{
segT this_segment = now_seg;
subsegT this_subsegment = now_subseg;
asection *regsec
= bfd_make_section_old_way (stdoutput,
MMIX_REG_CONTENTS_SECTION_NAME);
subseg_set (regsec, 0);
mmix_opcode_frag = NULL;
frag_grow (0);
mmix_reg_contents_frags = frag_now;
for (i = n_of_raw_gregs - 1; i >= 0; i--)
{
if (mmix_raw_gregs[i].label != NULL)
colon (mmix_raw_gregs[i].label);
frag_var (rs_machine_dependent, 8, 0, STATE_GREG_UNDF,
make_expr_symbol (&mmix_raw_gregs[i].exp), 0, NULL);
}
subseg_set (this_segment, this_subsegment);
}
for (fragP = mmix_reg_contents_frags; fragP != NULL; fragP = fragP->fr_next)
{
fragS **fpp;
fragS *fp = NULL;
fragS *osymfrag;
offsetT osymval;
expressionS *oexpP;
symbolS *symbolP = fragP->fr_symbol;
if (fragP->fr_type != rs_machine_dependent
|| fragP->fr_subtype != STATE_GREG_UNDF)
continue;
fragP->fr_subtype = STATE_GREG_DEF;
if (! merge_gregs)
continue;
osymval = (offsetT) S_GET_VALUE (symbolP);
osymfrag = symbol_get_frag (symbolP);
if (! S_IS_DEFINED (symbolP))
continue;
oexpP = symbol_get_value_expression (fragP->fr_symbol);
if (oexpP->X_op == O_constant && osymval == 0)
continue;
for (fpp = &fragP->fr_next; *fpp != NULL; fpp = &fpp[0]->fr_next)
{
fp = *fpp;
if (fp->fr_type != rs_machine_dependent
|| fp->fr_subtype != STATE_GREG_UNDF)
continue;
if ((offsetT) S_GET_VALUE (fp->fr_symbol) == osymval
&& symbol_get_frag (fp->fr_symbol) == osymfrag)
{
*fpp = fp->fr_next;
fp->fr_next = fragP->fr_next;
fragP->fr_next = fp;
break;
}
}
if (*fpp != NULL)
fragP->tc_frag_data = fp;
}
}
static int
cmp_greg_symbol_fixes (parg, qarg)
const PTR parg;
const PTR qarg;
{
const struct mmix_symbol_greg_fixes *p
= (const struct mmix_symbol_greg_fixes *) parg;
const struct mmix_symbol_greg_fixes *q
= (const struct mmix_symbol_greg_fixes *) qarg;
return p->offs > q->offs ? 1 : p->offs < q->offs ? -1 : 0;
}
void
mmix_frob_file ()
{
int i;
struct mmix_symbol_gregs *all_greg_symbols[MAX_GREGS];
int n_greg_symbols = 0;
for (i = 0; i < n_of_cooked_gregs; i++)
{
offsetT offs;
symbolS *sym;
struct mmix_symbol_gregs *gregs;
fixS *fixP;
fixP = mmix_gregs[i];
know (fixP->fx_r_type == BFD_RELOC_64);
if (fixP->fx_subsy != NULL)
{
as_bad_where (fixP->fx_file, fixP->fx_line,
_("GREG expression too complicated"));
continue;
}
sym = fixP->fx_addsy;
offs = (offsetT) fixP->fx_offset;
if (S_IS_DEFINED (sym)
&& !bfd_is_com_section (S_GET_SEGMENT (sym))
&& !S_IS_WEAK (sym))
{
if (! symbol_section_p (sym)
&& ! bfd_is_abs_section (S_GET_SEGMENT (sym)))
as_fatal (_("internal: GREG expression not resolved to section"));
offs += S_GET_VALUE (sym);
}
if (lowest_data_loc != (bfd_vma) -1
&& (bfd_vma) offs + 256 > lowest_data_loc
&& bfd_is_abs_section (S_GET_SEGMENT (sym)))
{
offs -= (offsetT) lowest_data_loc;
sym = section_symbol (data_section);
}
else if (lowest_text_loc != (bfd_vma) -1
&& (bfd_vma) offs + 256 > lowest_text_loc
&& bfd_is_abs_section (S_GET_SEGMENT (sym)))
{
offs -= (offsetT) lowest_text_loc;
sym = section_symbol (text_section);
}
gregs = *symbol_get_tc (sym);
if (gregs == NULL)
{
gregs = xmalloc (sizeof (*gregs));
gregs->n_gregs = 0;
symbol_set_tc (sym, &gregs);
all_greg_symbols[n_greg_symbols++] = gregs;
}
gregs->greg_fixes[gregs->n_gregs].fix = fixP;
gregs->greg_fixes[gregs->n_gregs++].offs = offs;
}
for (i = 0; i < n_greg_symbols; i++)
qsort (all_greg_symbols[i]->greg_fixes, all_greg_symbols[i]->n_gregs,
sizeof (all_greg_symbols[i]->greg_fixes[0]), cmp_greg_symbol_fixes);
if (real_reg_section != NULL)
{
asection **secpp;
if (bfd_get_section_flags (stdoutput, real_reg_section) & SEC_HAS_CONTENTS)
as_fatal (_("register section has contents\n"));
for (secpp = &stdoutput->sections;
*secpp != real_reg_section;
secpp = &(*secpp)->next)
;
bfd_section_list_remove (stdoutput, secpp);
--stdoutput->section_count;
}
}
int
mmix_parse_predefined_name (name, expP)
char *name;
expressionS *expP;
{
char *canon_name;
char *handler_charp;
const char handler_chars[] = "DVWIOUZX";
symbolS *symp;
if (! predefined_syms)
return 0;
canon_name = tc_canonicalize_symbol_name (name);
if (canon_name[1] == '_'
&& strcmp (canon_name + 2, "Handler") == 0
&& (handler_charp = strchr (handler_chars, *canon_name)) != NULL)
{
symp = symbol_find (name);
if (symp == NULL)
symp = symbol_new (name, text_section,
0x10 * (handler_charp + 1 - handler_chars),
&zero_address_frag);
}
else
{
unsigned int i;
static const struct
{
const char *name;
valueT val;
} predefined_abs_syms[] =
{
{"Data_Segment", (valueT) 0x20 << 56},
{"Pool_Segment", (valueT) 0x40 << 56},
{"Stack_Segment", (valueT) 0x60 << 56},
{"StdIn", 0},
{"StdOut", 1},
{"StdErr", 2},
{"TextRead", 0},
{"TextWrite", 1},
{"BinaryRead", 2},
{"BinaryWrite", 3},
{"BinaryReadWrite", 4},
{"Halt", 0},
{"Fopen", 1},
{"Fclose", 2},
{"Fread", 3},
{"Fgets", 4},
{"Fgetws", 5},
{"Fwrite", 6},
{"Fputs", 7},
{"Fputws", 8},
{"Fseek", 9},
{"Ftell", 10},
{"D_BIT", 0x80},
{"V_BIT", 0x40},
{"W_BIT", 0x20},
{"I_BIT", 0x10},
{"O_BIT", 0x08},
{"U_BIT", 0x04},
{"Z_BIT", 0x02},
{"X_BIT", 0x01},
{"Inf", 0x7ff00000}
};
symp = symbol_find (name);
if (symp != NULL)
return 0;
for (i = 0;
i < sizeof (predefined_abs_syms) / sizeof (predefined_abs_syms[0]);
i++)
if (strcmp (canon_name, predefined_abs_syms[i].name) == 0)
{
symbol_table_insert (symbol_new (predefined_abs_syms[i].name,
absolute_section,
predefined_abs_syms[i].val,
&zero_address_frag));
return 0;
}
return 0;
}
expP->X_op = O_symbol;
expP->X_add_number = 0;
expP->X_add_symbol = symp;
expP->X_op_symbol = NULL;
return 1;
}
void
mmix_md_elf_section_change_hook ()
{
if (doing_bspec)
as_bad (_("section change from within a BSPEC/ESPEC pair is not supported"));
last_alignment = bfd_get_section_alignment (now_seg->owner, now_seg);
want_unaligned = 0;
}
static void
s_loc (ignore)
int ignore ATTRIBUTE_UNUSED;
{
segT section;
expressionS exp;
char *p;
symbolS *sym;
offsetT off;
if (doing_bspec)
{
as_bad (_("directive LOC from within a BSPEC/ESPEC pair is not supported"));
return;
}
section = expression (&exp);
if (exp.X_op == O_illegal
|| exp.X_op == O_absent
|| exp.X_op == O_big
|| section == undefined_section)
{
as_bad (_("invalid LOC expression"));
return;
}
if (section == absolute_section)
{
if (exp.X_add_number < ((offsetT) 0x20 << 56))
{
section = text_section;
if (exp.X_add_number < 0
|| exp.X_add_number < (offsetT) lowest_text_loc)
{
as_bad (_("LOC expression stepping backwards is not supported"));
exp.X_op = O_absent;
}
else
{
if (text_has_contents && lowest_text_loc == (bfd_vma) -1)
lowest_text_loc = 0;
if (lowest_text_loc == (bfd_vma) -1)
{
lowest_text_loc = exp.X_add_number;
exp.X_op = O_absent;
}
else
exp.X_add_number -= lowest_text_loc;
}
}
else
{
section = data_section;
if (exp.X_add_number < (offsetT) lowest_data_loc)
{
as_bad (_("LOC expression stepping backwards is not supported"));
exp.X_op = O_absent;
}
else
{
if (data_has_contents && lowest_data_loc == (bfd_vma) -1)
lowest_data_loc = (bfd_vma) 0x20 << 56;
if (lowest_data_loc == (bfd_vma) -1)
{
lowest_data_loc = exp.X_add_number;
exp.X_op = O_absent;
}
else
exp.X_add_number -= lowest_data_loc;
}
}
}
if (section != now_seg)
{
obj_elf_section_change_hook ();
subseg_set (section, 0);
md_elf_section_change_hook ();
}
if (exp.X_op != O_absent)
{
if (exp.X_op != O_constant && exp.X_op != O_symbol)
{
sym = make_expr_symbol (&exp);
off = 0;
}
else
{
sym = exp.X_add_symbol;
off = exp.X_add_number;
}
p = frag_var (rs_org, 1, 1, (relax_substateT) 0, sym, off, (char *) 0);
*p = 0;
}
mmix_handle_rest_of_empty_line ();
}
static void
mmix_byte ()
{
unsigned int c;
char *start;
if (now_seg == text_section)
text_has_contents = 1;
else if (now_seg == data_section)
data_has_contents = 1;
do
{
SKIP_WHITESPACE ();
switch (*input_line_pointer)
{
case '\"':
++input_line_pointer;
start = input_line_pointer;
while (is_a_char (c = next_char_of_string ()))
{
FRAG_APPEND_1_CHAR (c);
}
if (input_line_pointer[-1] != '\"')
{
as_bad (_("unterminated string"));
mmix_discard_rest_of_line ();
return;
}
break;
default:
{
expressionS exp;
segT expseg = expression (&exp);
if ((expseg != absolute_section && expseg != reg_section)
|| (exp.X_op != O_constant
&& (exp.X_op != O_register
|| exp.X_add_number <= 255)))
{
as_bad (_("BYTE expression not a pure number"));
mmix_discard_rest_of_line ();
return;
}
else if ((exp.X_add_number > 255 && exp.X_op != O_register)
|| exp.X_add_number < 0)
{
as_bad (_("BYTE expression not in the range 0..255"));
mmix_discard_rest_of_line ();
return;
}
FRAG_APPEND_1_CHAR (exp.X_add_number);
}
break;
}
SKIP_WHITESPACE ();
c = *input_line_pointer++;
}
while (c == ',');
input_line_pointer--;
if (mmix_gnu_syntax)
demand_empty_rest_of_line ();
else
{
mmix_discard_rest_of_line ();
input_line_pointer++;
}
last_alignment = 0;
}
static void
mmix_cons (nbytes)
int nbytes;
{
expressionS exp;
char *start;
if (now_seg == text_section)
{
if (lowest_text_loc != (bfd_vma) -1
&& (lowest_text_loc & (nbytes - 1)) != 0)
{
if (text_has_contents)
as_bad (_("data item with alignment larger than location"));
else if (want_unaligned)
as_bad (_("unaligned data at an absolute location is not supported"));
lowest_text_loc &= ~((bfd_vma) nbytes - 1);
lowest_text_loc += (bfd_vma) nbytes;
}
text_has_contents = 1;
}
else if (now_seg == data_section)
{
if (lowest_data_loc != (bfd_vma) -1
&& (lowest_data_loc & (nbytes - 1)) != 0)
{
if (data_has_contents)
as_bad (_("data item with alignment larger than location"));
else if (want_unaligned)
as_bad (_("unaligned data at an absolute location is not supported"));
lowest_data_loc &= ~((bfd_vma) nbytes - 1);
lowest_data_loc += (bfd_vma) nbytes;
}
data_has_contents = 1;
}
if (! want_unaligned)
{
last_alignment = nbytes == 2 ? 1 : (nbytes == 4 ? 2 : 3);
frag_align (last_alignment, 0, 0);
record_alignment (now_seg, last_alignment);
}
if (current_fb_label >= 0)
colon (fb_label_name (current_fb_label, 1));
else if (pending_label != NULL)
{
colon (pending_label);
pending_label = NULL;
}
SKIP_WHITESPACE ();
if (is_end_of_line[(unsigned int) *input_line_pointer])
{
exp.X_op = O_constant;
exp.X_add_number = 0;
exp.X_unsigned = 0;
exp.X_add_symbol = NULL;
exp.X_op_symbol = NULL;
emit_expr (&exp, (unsigned int) nbytes);
}
else
do
{
unsigned int c;
switch (*input_line_pointer)
{
case '\"':
++input_line_pointer;
start = input_line_pointer;
while (is_a_char (c = next_char_of_string ()))
{
exp.X_op = O_constant;
exp.X_add_number = c;
exp.X_unsigned = 1;
emit_expr (&exp, (unsigned int) nbytes);
}
if (input_line_pointer[-1] != '\"')
{
as_bad (_("unterminated string"));
mmix_discard_rest_of_line ();
return;
}
break;
default:
{
expression (&exp);
emit_expr (&exp, (unsigned int) nbytes);
SKIP_WHITESPACE ();
}
break;
}
}
while (*input_line_pointer++ == ',');
input_line_pointer--;
mmix_handle_rest_of_empty_line ();
}
void
mmix_md_do_align (n, fill, len, max)
int n;
char *fill ATTRIBUTE_UNUSED;
int len ATTRIBUTE_UNUSED;
int max ATTRIBUTE_UNUSED;
{
last_alignment = n;
want_unaligned = n == 0;
}