+ char *destp;
+ int num_opcodes = 1;
+ int i;
+ struct i860_it pseudo[3];
+
+ assert (str);
+
+ /* Assemble the instruction. */
+ i860_process_insn (str);
+
+ /* Check for expandable flag to produce pseudo-instructions. This
+ is an undesirable feature that should be avoided. */
+ if (the_insn.expand != 0
+ && ! (the_insn.fup & (OP_SEL_HA | OP_SEL_H | OP_SEL_L | OP_SEL_GOT
+ | OP_SEL_GOTOFF | OP_SEL_PLT)))
+ {
+ for (i = 0; i < 3; i++)
+ pseudo[i] = the_insn;
+
+ switch (the_insn.expand)
+ {
+
+ case E_DELAY:
+ num_opcodes = 1;
+ break;
+
+ case E_MOV:
+ if (the_insn.exp.X_add_symbol == NULL
+ && the_insn.exp.X_op_symbol == NULL
+ && (the_insn.exp.X_add_number < (1 << 15)
+ && the_insn.exp.X_add_number >= -(1 << 15)))
+ break;
+
+ /* Emit "or l%const,r0,ireg_dest". */
+ pseudo[0].opcode = (the_insn.opcode & 0x001f0000) | 0xe4000000;
+ pseudo[0].fup = (OP_IMM_S16 | OP_SEL_L);
+
+ /* Emit "orh h%const,ireg_dest,ireg_dest". */
+ pseudo[1].opcode = (the_insn.opcode & 0x03ffffff) | 0xec000000
+ | ((the_insn.opcode & 0x001f0000) << 5);
+ pseudo[1].fup = (OP_IMM_S16 | OP_SEL_H);
+
+ num_opcodes = 2;
+ break;
+
+ case E_ADDR:
+ if (the_insn.exp.X_add_symbol == NULL
+ && the_insn.exp.X_op_symbol == NULL
+ && (the_insn.exp.X_add_number < (1 << 15)
+ && the_insn.exp.X_add_number >= -(1 << 15)))
+ break;
+
+ /* Emit "orh ha%addr_expr,r0,r31". */
+ pseudo[0].opcode = 0xec000000 | (atmp << 16);
+ pseudo[0].fup = (OP_IMM_S16 | OP_SEL_HA);
+
+ /* Emit "l%addr_expr(r31),ireg_dest". We pick up the fixup
+ information from the original instruction. */
+ pseudo[1].opcode = (the_insn.opcode & ~0x03e00000) | (atmp << 21);
+ pseudo[1].fup = the_insn.fup | OP_SEL_L;
+
+ num_opcodes = 2;
+ break;
+
+ case E_U32:
+ if (the_insn.exp.X_add_symbol == NULL
+ && the_insn.exp.X_op_symbol == NULL
+ && (the_insn.exp.X_add_number < (1 << 16)
+ && the_insn.exp.X_add_number >= 0))
+ break;
+
+ /* Emit "$(opcode)h h%const,ireg_src2,r31". */
+ pseudo[0].opcode = (the_insn.opcode & 0xf3e0ffff) | 0x0c000000
+ | (atmp << 16);
+ pseudo[0].fup = (OP_IMM_S16 | OP_SEL_H);
+
+ /* Emit "$(opcode) l%const,r31,ireg_dest". */
+ pseudo[1].opcode = (the_insn.opcode & 0xf01f0000) | 0x04000000
+ | (atmp << 21);
+ pseudo[1].fup = (OP_IMM_S16 | OP_SEL_L);
+
+ num_opcodes = 2;
+ break;
+
+ case E_AND:
+ if (the_insn.exp.X_add_symbol == NULL
+ && the_insn.exp.X_op_symbol == NULL
+ && (the_insn.exp.X_add_number < (1 << 16)
+ && the_insn.exp.X_add_number >= 0))
+ break;
+
+ /* Emit "andnot h%const,ireg_src2,r31". */
+ pseudo[0].opcode = (the_insn.opcode & 0x03e0ffff) | 0xd4000000
+ | (atmp << 16);
+ pseudo[0].fup = (OP_IMM_S16 | OP_SEL_H);
+ pseudo[0].exp.X_add_number = -1 - the_insn.exp.X_add_number;
+
+ /* Emit "andnot l%const,r31,ireg_dest". */
+ pseudo[1].opcode = (the_insn.opcode & 0x001f0000) | 0xd4000000
+ | (atmp << 21);
+ pseudo[1].fup = (OP_IMM_S16 | OP_SEL_L);
+ pseudo[1].exp.X_add_number = -1 - the_insn.exp.X_add_number;
+
+ num_opcodes = 2;
+ break;
+
+ case E_S32:
+ if (the_insn.exp.X_add_symbol == NULL
+ && the_insn.exp.X_op_symbol == NULL
+ && (the_insn.exp.X_add_number < (1 << 15)
+ && the_insn.exp.X_add_number >= -(1 << 15)))
+ break;
+
+ /* Emit "orh h%const,r0,r31". */
+ pseudo[0].opcode = 0xec000000 | (atmp << 16);
+ pseudo[0].fup = (OP_IMM_S16 | OP_SEL_H);
+
+ /* Emit "or l%const,r31,r31". */
+ pseudo[1].opcode = 0xe4000000 | (atmp << 21) | (atmp << 16);
+ pseudo[1].fup = (OP_IMM_S16 | OP_SEL_L);
+
+ /* Emit "r31,ireg_src2,ireg_dest". */
+ pseudo[2].opcode = (the_insn.opcode & ~0x0400ffff) | (atmp << 11);
+ pseudo[2].fup = OP_IMM_S16;
+
+ num_opcodes = 3;
+ break;
+
+ default:
+ as_fatal (_("failed sanity check."));
+ }
+
+ the_insn = pseudo[0];
+
+ /* Warn if an opcode is expanded after a delayed branch. */
+ if (num_opcodes > 1 && last_expand == 1)
+ as_warn (_("Expanded opcode after delayed branch: `%s'"), str);
+
+ /* Warn if an opcode is expanded in dual mode. */
+ if (num_opcodes > 1 && dual_mode != DUAL_OFF)
+ as_warn (_("Expanded opcode in dual mode: `%s'"), str);
+
+ /* Notify if any expansions happen. */
+ if (target_warn_expand && num_opcodes > 1)
+ as_warn (_("An instruction was expanded (%s)"), str);
+ }
+
+ i = 0;
+ do
+ {
+ /* Output the opcode. Note that the i860 always reads instructions
+ as little-endian data. */
+ destp = frag_more (4);
+ number_to_chars_littleendian (destp, the_insn.opcode, 4);
+
+ /* Check for expanded opcode after branch or in dual mode. */
+ last_expand = the_insn.pcrel;
+
+ /* Output the symbol-dependent stuff. */
+ if (the_insn.fup != OP_NONE)
+ {
+ fixS *fix;
+ fix = fix_new_exp (frag_now,
+ destp - frag_now->fr_literal,
+ 4,
+ &the_insn.exp,
+ the_insn.pcrel,
+ the_insn.reloc);
+
+ /* Despite the odd name, this is a scratch field. We use
+ it to encode operand type information. */
+ fix->fx_addnumber = the_insn.fup;
+ }
+ the_insn = pseudo[++i];
+ }
+ while (--num_opcodes > 0);
+