-/*****************************************************************************
- parse_ldcont:
- Parse and replace a 'ldconst' pseudo-instruction with an appropriate
- i80960 instruction.
-
- Assumes the input consists of:
- arg[0] opcode mnemonic ('ldconst')
- arg[1] first operand (constant)
- arg[2] name of register to be loaded
-
- Replaces opcode and/or operands as appropriate.
-
- Returns the new number of arguments, or -1 on failure.
-
- *************************************************************************** */
-static
-int
-parse_ldconst (arg)
- char *arg[]; /* See above */
-{
- int n; /* Constant to be loaded */
- int shift; /* Shift count for "shlo" instruction */
- static char buf[5]; /* Literal for first operand */
- static char buf2[5]; /* Literal for second operand */
- expressionS e; /* Parsed expression */
-
-
- arg[3] = NULL; /* So we can tell at the end if it got used or not */
-
- parse_expr (arg[1], &e);
- switch (e.X_op)
- {
- default:
- /* We're dependent on one or more symbols -- use "lda" */
- arg[0] = "lda";
- break;
-
- case O_constant:
- /* Try the following mappings:
- * ldconst 0,<reg> ->mov 0,<reg>
- * ldconst 31,<reg> ->mov 31,<reg>
- * ldconst 32,<reg> ->addo 1,31,<reg>
- * ldconst 62,<reg> ->addo 31,31,<reg>
- * ldconst 64,<reg> ->shlo 8,3,<reg>
- * ldconst -1,<reg> ->subo 1,0,<reg>
- * ldconst -31,<reg>->subo 31,0,<reg>
- *
- * anthing else becomes:
- * lda xxx,<reg>
- */
- n = offs (e);
- if ((0 <= n) && (n <= 31))
- {
- arg[0] = "mov";
-
- }
- else if ((-31 <= n) && (n <= -1))
- {
- arg[0] = "subo";
- arg[3] = arg[2];
- sprintf (buf, "%d", -n);
- arg[1] = buf;
- arg[2] = "0";
-
- }
- else if ((32 <= n) && (n <= 62))
- {
- arg[0] = "addo";
- arg[3] = arg[2];
- arg[1] = "31";
- sprintf (buf, "%d", n - 31);
- arg[2] = buf;
-
- }
- else if ((shift = shift_ok (n)) != 0)
- {
- arg[0] = "shlo";
- arg[3] = arg[2];
- sprintf (buf, "%d", shift);
- arg[1] = buf;
- sprintf (buf2, "%d", n >> shift);
- arg[2] = buf2;
-
- }
- else
- {
- arg[0] = "lda";
- }
- break;
-
- case O_illegal:
- as_bad (_("invalid constant"));
- return -1;
- break;
- }
- return (arg[3] == 0) ? 2 : 3;
-}
-
-/*****************************************************************************
- parse_memop: parse a memory operand
-
- This routine is based on the observation that the 4 mode bits of the
- MEMB format, taken individually, have fairly consistent meaning:
-
- M3 (bit 13): 1 if displacement is present (D_BIT)
- M2 (bit 12): 1 for MEMB instructions (MEMB_BIT)
- M1 (bit 11): 1 if index is present (I_BIT)
- M0 (bit 10): 1 if abase is present (A_BIT)
-
- So we parse the memory operand and set bits in the mode as we find
- things. Then at the end, if we go to MEMB format, we need only set
- the MEMB bit (M2) and our mode is built for us.
-
- Unfortunately, I said "fairly consistent". The exceptions:
-
- DBIA
- 0100 Would seem illegal, but means "abase-only".
-
- 0101 Would seem to mean "abase-only" -- it means IP-relative.
- Must be converted to 0100.
-
- 0110 Would seem to mean "index-only", but is reserved.
- We turn on the D bit and provide a 0 displacement.
-
- The other thing to observe is that we parse from the right, peeling
- things * off as we go: first any index spec, then any abase, then
- the displacement.
-
- *************************************************************************** */
-static
-void
-parse_memop (memP, argP, optype)
- memS *memP; /* Where to put the results */
- char *argP; /* Text of the operand to be parsed */
- int optype; /* MEM1, MEM2, MEM4, MEM8, MEM12, or MEM16 */
-{
- char *indexP; /* Pointer to index specification with "[]" removed */
- char *p; /* Temp char pointer */
- char iprel_flag; /* True if this is an IP-relative operand */
- int regnum; /* Register number */
- /* Scale factor: 1,2,4,8, or 16. Later converted to internal format
- (0,1,2,3,4 respectively). */
- int scale;
- int mode; /* MEMB mode bits */
- int *intP; /* Pointer to register number */
-
- /* The following table contains the default scale factors for each
- type of memory instruction. It is accessed using (optype-MEM1)
- as an index -- thus it assumes the 'optype' constants are
- assigned consecutive values, in the order they appear in this
- table. */
- static const int def_scale[] =
- {
- 1, /* MEM1 */
- 2, /* MEM2 */
- 4, /* MEM4 */
- 8, /* MEM8 */
- -1, /* MEM12 -- no valid default */
- 16 /* MEM16 */
- };
-
-
- iprel_flag = mode = 0;
-
- /* Any index present? */
- indexP = get_ispec (argP);
- if (indexP)
- {
- p = strchr (indexP, '*');
- if (p == NULL)
- {
- /* No explicit scale -- use default for this instruction
- type and assembler mode. */
- if (flag_mri)
- scale = 1;
- else
- /* GNU960 compatibility */
- scale = def_scale[optype - MEM1];
- }
- else
- {
- *p++ = '\0'; /* Eliminate '*' */
-
- /* Now indexP->a '\0'-terminated register name,
- * and p->a scale factor.
- */
-
- if (!strcmp (p, "16"))
- {
- scale = 16;
- }
- else if (strchr ("1248", *p) && (p[1] == '\0'))
- {
- scale = *p - '0';
- }
- else
- {
- scale = -1;
- }
- }
-
- regnum = get_regnum (indexP); /* Get index reg. # */
- if (!IS_RG_REG (regnum))
- {
- as_bad (_("invalid index register"));
- return;
- }
-
- /* Convert scale to its binary encoding */
- switch (scale)
- {
- case 1:
- scale = 0 << 7;
- break;
- case 2:
- scale = 1 << 7;
- break;
- case 4:
- scale = 2 << 7;
- break;
- case 8:
- scale = 3 << 7;
- break;
- case 16:
- scale = 4 << 7;
- break;
- default:
- as_bad (_("invalid scale factor"));
- return;
- };
-
- memP->opcode |= scale | regnum; /* Set index bits in opcode */
- mode |= I_BIT; /* Found a valid index spec */
- }
-
- /* Any abase (Register Indirect) specification present? */
- if ((p = strrchr (argP, '(')) != NULL)
- {
- /* "(" is there -- does it start a legal abase spec? If not, it
- could be part of a displacement expression. */
- intP = (int *) hash_find (areg_hash, p);
- if (intP != NULL)
- {
- /* Got an abase here */
- regnum = *intP;
- *p = '\0'; /* discard register spec */
- if (regnum == IPREL)
- {
- /* We have to specialcase ip-rel mode */
- iprel_flag = 1;
- }
- else
- {
- memP->opcode |= regnum << 14;
- mode |= A_BIT;
- }
- }
- }
-
- /* Any expression present? */
- memP->e = argP;
- if (*argP != '\0')
- {
- mode |= D_BIT;
- }
-
- /* Special-case ip-relative addressing */
- if (iprel_flag)
- {
- if (mode & I_BIT)
- {
- syntax ();
- }
- else
- {
- memP->opcode |= 5 << 10; /* IP-relative mode */
- memP->disp = 32;
- }
- return;
- }
-
- /* Handle all other modes */
- switch (mode)
- {
- case D_BIT | A_BIT:
- /* Go with MEMA instruction format for now (grow to MEMB later
- if 12 bits is not enough for the displacement). MEMA format
- has a single mode bit: set it to indicate that abase is
- present. */
- memP->opcode |= MEMA_ABASE;
- memP->disp = 12;
- break;
-
- case D_BIT:
- /* Go with MEMA instruction format for now (grow to MEMB later
- if 12 bits is not enough for the displacement). */
- memP->disp = 12;
- break;
-
- case A_BIT:
- /* For some reason, the bit string for this mode is not
- consistent: it should be 0 (exclusive of the MEMB bit), so we
- set it "by hand" here. */
- memP->opcode |= MEMB_BIT;
- break;
-
- case A_BIT | I_BIT:
- /* set MEMB bit in mode, and OR in mode bits */
- memP->opcode |= mode | MEMB_BIT;
- break;
-
- case I_BIT:
- /* Treat missing displacement as displacement of 0. */
- mode |= D_BIT;
- /* Fall into next case. */
- case D_BIT | A_BIT | I_BIT:
- case D_BIT | I_BIT:
- /* set MEMB bit in mode, and OR in mode bits */
- memP->opcode |= mode | MEMB_BIT;
- memP->disp = 32;
- break;
-
- default:
- syntax ();
- break;
- }
-}
-
-/*****************************************************************************
- parse_po: parse machine-dependent pseudo-op
-
- This is a top-level routine for machine-dependent pseudo-ops. It slurps
- up the rest of the input line, breaks out the individual arguments,
- and dispatches them to the correct handler.
- *************************************************************************** */
-static
-void
-parse_po (po_num)
- int po_num; /* Pseudo-op number: currently S_LEAFPROC or S_SYSPROC */
-{
- /* Pointers operands, with no embedded whitespace.
- arg[0] unused, arg[1-3]->operands */
- char *args[4];
- int n_ops; /* Number of operands */
- char *p; /* Pointer to beginning of unparsed argument string */
- char eol; /* Character that indicated end of line */
-
- extern char is_end_of_line[];
-
- /* Advance input pointer to end of line. */
- p = input_line_pointer;
- while (!is_end_of_line[(unsigned char) *input_line_pointer])
- {
- input_line_pointer++;
- }
- eol = *input_line_pointer; /* Save end-of-line char */
- *input_line_pointer = '\0'; /* Terminate argument list */
-
- /* Parse out operands */
- n_ops = get_args (p, args);
- if (n_ops == -1)
- {
- return;
- }
-
- /* Dispatch to correct handler */
- switch (po_num)
- {
- case S_SYSPROC:
- s_sysproc (n_ops, args);
- break;
- case S_LEAFPROC:
- s_leafproc (n_ops, args);
- break;
- default:
- BAD_CASE (po_num);
- break;
- }
-
- /* Restore eol, so line numbers get updated correctly. Base
- assembler assumes we leave input pointer pointing at char
- following the eol. */
- *input_line_pointer++ = eol;
-}
-
-/*****************************************************************************
- parse_regop: parse a register operand.
-
- In case of illegal operand, issue a message and return some valid
- information so instruction processing can continue.
- *************************************************************************** */
-static
-void
-parse_regop (regopP, optext, opdesc)
- struct regop *regopP; /* Where to put description of register operand */
- char *optext; /* Text of operand */
- char opdesc; /* Descriptor byte: what's legal for this operand */
-{
- int n; /* Register number */
- expressionS e; /* Parsed expression */
-
- /* See if operand is a register */
- n = get_regnum (optext);
- if (n >= 0)
- {
- if (IS_RG_REG (n))
- {
- /* global or local register */
- if (!REG_ALIGN (opdesc, n))
- {
- as_bad (_("unaligned register"));
- }
- regopP->n = n;
- regopP->mode = 0;
- regopP->special = 0;
- return;
- }
- else if (IS_FP_REG (n) && FP_OK (opdesc))
- {
- /* Floating point register, and it's allowed */
- regopP->n = n - FP0;
- regopP->mode = 1;
- regopP->special = 0;
- return;
- }
- else if (IS_SF_REG (n) && SFR_OK (opdesc))
- {
- /* Special-function register, and it's allowed */
- regopP->n = n - SF0;
- regopP->mode = 0;
- regopP->special = 1;
- if (!targ_has_sfr (regopP->n))
- {
- as_bad (_("no such sfr in this architecture"));
- }
- return;
- }
- }
- else if (LIT_OK (opdesc))
- {
- /* How about a literal? */
- regopP->mode = 1;
- regopP->special = 0;
- if (FP_OK (opdesc))
- { /* floating point literal acceptable */
- /* Skip over 0f, 0d, or 0e prefix */
- if ((optext[0] == '0')
- && (optext[1] >= 'd')
- && (optext[1] <= 'f'))
- {
- optext += 2;
- }