Tue Apr 21 17:01:22 1998 Alan Modra <alan@spri.levels.unisa.edu.au>

* config/tc-i386.c (check_prefix): New static function, split out
	from md_assemble.
	(struct _i386_insn): Add wait_prefix field.
	(md_assemble): Remove wait_prefix local variable.  Use
	check_prefix when adding a prefix.

	* config/tc-i386.c (current_templates): New static variable.
	(md_assemble): Remove current_templates local variable.
	(md_assemble, i386_operand): Improve error and warning messages in
	many places.  Add RESTORE_END_STRING in many places before error
	return.  Clarify some comments.

	* config/tc-i386.c (struct _i386_insn): Change seg field to a two
	element array.
	(md_assemble): Parse string instruction operands, looking for
	segment override prefixes.  Check for invalid segment prefixes on
	string instruction.
	(i386_operand): i.seg[] and max mem_operand changes for string
	insns.
	* config/tc-i386.h (EsSeg): Define.

	* config/tc-i386.h (regKludge): Define.
	(iclrKludge, imulKludge): Don't define.
	* config/tc-i386.c (md_assemble): Merge imulKludge and iclrKludge
	code.  Move ReverseRegRegmem fudges into Modrm case.  Reorder
	opcode_modifier checks to look for more common cases first.  Add
	default_seg for IsString case.
This commit is contained in:
Ian Lance Taylor 1998-04-21 21:08:39 +00:00
parent d0f44984d1
commit 4498e3d641
3 changed files with 369 additions and 218 deletions

View file

@ -1,3 +1,33 @@
Tue Apr 21 17:01:22 1998 Alan Modra <alan@spri.levels.unisa.edu.au>
* config/tc-i386.c (check_prefix): New static function, split out
from md_assemble.
(struct _i386_insn): Add wait_prefix field.
(md_assemble): Remove wait_prefix local variable. Use
check_prefix when adding a prefix.
* config/tc-i386.c (current_templates): New static variable.
(md_assemble): Remove current_templates local variable.
(md_assemble, i386_operand): Improve error and warning messages in
many places. Add RESTORE_END_STRING in many places before error
return. Clarify some comments.
* config/tc-i386.c (struct _i386_insn): Change seg field to a two
element array.
(md_assemble): Parse string instruction operands, looking for
segment override prefixes. Check for invalid segment prefixes on
string instruction.
(i386_operand): i.seg[] and max mem_operand changes for string
insns.
* config/tc-i386.h (EsSeg): Define.
* config/tc-i386.h (regKludge): Define.
(iclrKludge, imulKludge): Don't define.
* config/tc-i386.c (md_assemble): Merge imulKludge and iclrKludge
code. Move ReverseRegRegmem fudges into Modrm case. Reorder
opcode_modifier checks to look for more common cases first. Add
default_seg for IsString case.
Tue Apr 21 16:18:12 1998 Ian Lance Taylor <ian@cygnus.com> Tue Apr 21 16:18:12 1998 Ian Lance Taylor <ian@cygnus.com>
* configure.in: Call AM_PROG_LEX rather than AC_PROG_LEX and * configure.in: Call AM_PROG_LEX rather than AC_PROG_LEX and

View file

@ -44,6 +44,7 @@ static int fits_in_unsigned_byte PARAMS ((long));
static int fits_in_unsigned_word PARAMS ((long)); static int fits_in_unsigned_word PARAMS ((long));
static int fits_in_signed_word PARAMS ((long)); static int fits_in_signed_word PARAMS ((long));
static int smallest_imm_type PARAMS ((long)); static int smallest_imm_type PARAMS ((long));
static int check_prefix PARAMS ((int));
static void set_16bit_code_flag PARAMS ((int)); static void set_16bit_code_flag PARAMS ((int));
#ifdef BFD_ASSEMBLER #ifdef BFD_ASSEMBLER
static bfd_reloc_code_real_type reloc static bfd_reloc_code_real_type reloc
@ -96,9 +97,9 @@ struct _i386_insn
reg_entry *index_reg; reg_entry *index_reg;
unsigned int log2_scale_factor; unsigned int log2_scale_factor;
/* SEG gives the seg_entry of this insn. It is equal to zero unless /* SEG gives the seg_entries of this insn. They are zero unless
an explicit segment override is given. */ explicit segment overrides are given. */
const seg_entry *seg; /* segment for memory operands (if given) */ const seg_entry *seg[2]; /* segments for memory operands (if given) */
/* PREFIX holds all the given prefix opcodes (usually null). /* PREFIX holds all the given prefix opcodes (usually null).
PREFIXES is the size of PREFIX. */ PREFIXES is the size of PREFIX. */
@ -106,7 +107,12 @@ struct _i386_insn
unsigned char prefix[MAX_PREFIXES]; unsigned char prefix[MAX_PREFIXES];
unsigned int prefixes; unsigned int prefixes;
/* RM and IB are the modrm byte and the base index byte where the /* Wait prefix needs to come before any other prefixes, so handle
it specially. wait_prefix will hold the opcode modifier flag
FWait if a wait prefix is given. */
int wait_prefix;
/* RM and BI are the modrm byte and the base index byte where the
addressing modes of this insn are encoded. */ addressing modes of this insn are encoded. */
modrm_byte rm; modrm_byte rm;
@ -179,10 +185,13 @@ static char *save_stack_p; /* stack pointer */
/* The instruction we're assembling. */ /* The instruction we're assembling. */
static i386_insn i; static i386_insn i;
/* Possible templates for current insn. */
static templates *current_templates;
/* Per instruction expressionS buffers: 2 displacements & 2 immediate max. */ /* Per instruction expressionS buffers: 2 displacements & 2 immediate max. */
static expressionS disp_expressions[2], im_expressions[2]; static expressionS disp_expressions[2], im_expressions[2];
/* pointers to ebp & esp entries in reg_hash hash table */ /* Pointers to ebp & esp entries in reg_hash hash table. */
static reg_entry *ebp, *esp; static reg_entry *ebp, *esp;
static int this_operand; /* current operand we are working on */ static int this_operand; /* current operand we are working on */
@ -232,8 +241,9 @@ const relax_typeS md_relax_table[] =
{1, 1, 0, 0}, {1, 1, 0, 0},
{1, 1, 0, 0}, {1, 1, 0, 0},
/* For now we don't use word displacement jumps; they may be /* For now we don't use word displacement jumps; they will not work
untrustworthy. */ for destination addresses > 0xFFFF, since they clear the upper 16
bits of %eip. */
{127 + 1, -128 + 1, 0, ENCODE_RELAX_STATE (COND_JUMP, DWORD)}, {127 + 1, -128 + 1, 0, ENCODE_RELAX_STATE (COND_JUMP, DWORD)},
/* word conditionals add 3 bytes to frag: /* word conditionals add 3 bytes to frag:
2 opcode prefix; 1 displacement bytes */ 2 opcode prefix; 1 displacement bytes */
@ -348,7 +358,7 @@ static reg_entry *parse_register PARAMS ((char *reg_string));
static void s_bss PARAMS ((int)); static void s_bss PARAMS ((int));
#endif #endif
symbolS *GOT_symbol; /* Pre-defined "__GLOBAL_OFFSET_TABLE" */ symbolS *GOT_symbol; /* Pre-defined "_GLOBAL_OFFSET_TABLE_" */
static INLINE unsigned long static INLINE unsigned long
mode_from_disp_size (t) mode_from_disp_size (t)
@ -422,6 +432,81 @@ smallest_imm_type (num)
: (Imm32)); : (Imm32));
} /* smallest_imm_type() */ } /* smallest_imm_type() */
static int
check_prefix (prefix)
int prefix;
{
int q;
for (q = 0; q < i.prefixes; q++)
{
switch (prefix)
{
case CS_PREFIX_OPCODE:
case DS_PREFIX_OPCODE:
case ES_PREFIX_OPCODE:
case FS_PREFIX_OPCODE:
case GS_PREFIX_OPCODE:
case SS_PREFIX_OPCODE:
switch (i.prefix[q])
{
case CS_PREFIX_OPCODE:
case DS_PREFIX_OPCODE:
case ES_PREFIX_OPCODE:
case FS_PREFIX_OPCODE:
case GS_PREFIX_OPCODE:
case SS_PREFIX_OPCODE:
as_bad ("same type of prefix used twice");
return 0;
}
break;
case REPNE:
case REPE:
switch (i.prefix[q])
{
case REPNE:
case REPE:
as_bad ("same type of prefix used twice");
return 0;
}
break;
case FWAIT_OPCODE:
if (i.wait_prefix != 0)
{
as_bad ("same type of prefix used twice");
return 0;
}
break;
default:
if (i.prefix[q] == prefix)
{
as_bad ("same type of prefix used twice");
return 0;
}
}
}
if (i.prefixes == MAX_PREFIXES && prefix != FWAIT_OPCODE)
{
char *p = "another"; /* paranoia */
for (q = 0;
q < sizeof (i386_prefixtab) / sizeof (i386_prefixtab[0]);
q++)
if (i386_prefixtab[q].prefix_code == prefix)
p = i386_prefixtab[q].prefix_name;
as_bad ("%d prefixes given and `%%%s' prefix gives too many",
MAX_PREFIXES, p);
return 0;
}
return 1;
}
static void static void
set_16bit_code_flag (new_16bit_code_flag) set_16bit_code_flag (new_16bit_code_flag)
int new_16bit_code_flag; int new_16bit_code_flag;
@ -736,10 +821,12 @@ type_names[] =
{ Imm1, "i1" }, { Imm1, "i1" },
{ Control, "control reg" }, { Control, "control reg" },
{ Test, "test reg" }, { Test, "test reg" },
{ Debug, "debug reg" },
{ FloatReg, "FReg" }, { FloatReg, "FReg" },
{ FloatAcc, "FAcc" }, { FloatAcc, "FAcc" },
{ JumpAbsolute, "Jump Absolute" }, { JumpAbsolute, "Jump Absolute" },
{ RegMMX, "rMMX" }, { RegMMX, "rMMX" },
{ EsSeg, "es" },
{ 0, "" } { 0, "" }
}; };
@ -840,9 +927,6 @@ md_assemble (line)
/* Count the size of the instruction generated. */ /* Count the size of the instruction generated. */
int insn_size = 0; int insn_size = 0;
/* Possible templates for current insn */
templates *current_templates = (templates *) 0;
int j; int j;
/* Initialize globals. */ /* Initialize globals. */
@ -861,8 +945,8 @@ md_assemble (line)
/* 1 if operand is pending after ','. */ /* 1 if operand is pending after ','. */
unsigned int expecting_operand = 0; unsigned int expecting_operand = 0;
/* 1 if we found a prefix only acceptable with string insns. */ /* Non-zero if we found a prefix only acceptable with string insns. */
unsigned int expecting_string_instruction = 0; const char *expecting_string_instruction = NULL;
/* Non-zero if operand parens not balanced. */ /* Non-zero if operand parens not balanced. */
unsigned int paren_not_balanced; unsigned int paren_not_balanced;
char *token_start = l; char *token_start = l;
@ -894,26 +978,26 @@ md_assemble (line)
prefix = (prefix_entry *) hash_find (prefix_hash, token_start); prefix = (prefix_entry *) hash_find (prefix_hash, token_start);
if (!prefix) if (!prefix)
{ {
as_bad ("no such opcode prefix ('%s')", token_start); as_bad ("no such opcode prefix `%s'", token_start);
RESTORE_END_STRING (l);
return; return;
} }
RESTORE_END_STRING (l); RESTORE_END_STRING (l);
/* check for repeated prefix */ /* check for repeated prefix */
for (q = 0; q < i.prefixes; q++) if (! check_prefix (prefix->prefix_code))
if (i.prefix[q] == prefix->prefix_code) return;
{ if (prefix->prefix_code == FWAIT_OPCODE)
as_bad ("same prefix used twice; you don't really want this!");
return;
}
if (i.prefixes == MAX_PREFIXES)
{ {
as_bad ("too many opcode prefixes"); i.wait_prefix = FWait;
return;
} }
i.prefix[i.prefixes++] = prefix->prefix_code; else
if (prefix->prefix_code == REPE || prefix->prefix_code == REPNE) {
expecting_string_instruction = 1; i.prefix[i.prefixes++] = prefix->prefix_code;
/* skip past PREFIX_SEPERATOR and reset token_start */ if (prefix->prefix_code == REPE
|| prefix->prefix_code == REPNE)
expecting_string_instruction = prefix->prefix_name;
}
/* Skip past PREFIX_SEPARATOR and reset token_start. */
token_start = ++l; token_start = ++l;
} }
} }
@ -921,6 +1005,7 @@ md_assemble (line)
if (token_start == l) if (token_start == l)
{ {
as_bad ("expecting opcode; got nothing"); as_bad ("expecting opcode; got nothing");
RESTORE_END_STRING (l);
return; return;
} }
@ -944,6 +1029,7 @@ md_assemble (line)
if (!current_templates) if (!current_templates)
{ {
as_bad ("no such 386 instruction: `%s'", token_start); as_bad ("no such 386 instruction: `%s'", token_start);
RESTORE_END_STRING (l);
return; return;
} }
} }
@ -951,21 +1037,15 @@ md_assemble (line)
/* check for rep/repne without a string instruction */ /* check for rep/repne without a string instruction */
if (expecting_string_instruction && if (expecting_string_instruction &&
!IS_STRING_INSTRUCTION (current_templates-> !(current_templates->start->opcode_modifier & IsString))
start->base_opcode))
{ {
as_bad ("expecting string instruction after rep/repne"); as_bad ("expecting string instruction after `%s'",
expecting_string_instruction);
return; return;
} }
/* There may be operands to parse. */ /* There may be operands to parse. */
if (*l != END_OF_INSN && if (*l != END_OF_INSN)
/* For string instructions, we ignore any operands if given. This
kludges, for example, 'rep/movsb %ds:(%esi), %es:(%edi)' where
the operands are always going to be the same, and are not really
encoded in machine code. */
!IS_STRING_INSTRUCTION (current_templates->
start->base_opcode))
{ {
/* parse operands */ /* parse operands */
do do
@ -1162,12 +1242,14 @@ md_assemble (line)
} /* for (t = ... */ } /* for (t = ... */
if (t == current_templates->end) if (t == current_templates->end)
{ /* we found no match */ { /* we found no match */
as_bad ("operands given don't match any known 386 instruction"); as_bad ("suffix or operands invalid for `%s'",
current_templates->start->name);
return; return;
} }
/* Copy the template we found (we may change it!). */ /* Copy the template we found. */
i.tm = *t; i.tm = *t;
i.tm.opcode_modifier |= i.wait_prefix;
if (found_reverse_match) if (found_reverse_match)
{ {
@ -1175,6 +1257,37 @@ md_assemble (line)
i.tm.operand_types[1] = t->operand_types[0]; i.tm.operand_types[1] = t->operand_types[0];
} }
/* Check string instruction segment overrides */
if ((i.tm.opcode_modifier & IsString) != 0 && i.mem_operands != 0)
{
int mem_op = (i.types[0] & Mem) ? 0 : 1;
if ((i.tm.operand_types[mem_op+0] & EsSeg) != 0)
{
if (i.seg[0] != (seg_entry *) 0 && i.seg[0] != (seg_entry *) &es)
{
as_bad ("`%s' %s operand must use `%%es' segment",
i.tm.name,
ordinal_names[mem_op+0]);
return;
}
/* There's only ever one segment override allowed per instruction.
This instruction possibly has a legal segment override on the
second operand, so copy the segment to where non-string
instructions store it, allowing common code. */
i.seg[0] = i.seg[1];
}
else if ((i.tm.operand_types[mem_op+1] & EsSeg) != 0)
{
if (i.seg[1] != (seg_entry *) 0 && i.seg[1] != (seg_entry *) &es)
{
as_bad ("`%s' %s operand must use `%%es' segment",
i.tm.name,
ordinal_names[mem_op+1]);
return;
}
}
}
/* If the matched instruction specifies an explicit opcode suffix, /* If the matched instruction specifies an explicit opcode suffix,
use it - and make sure none has already been specified. */ use it - and make sure none has already been specified. */
if (i.tm.opcode_modifier & (Data16|Data32)) if (i.tm.opcode_modifier & (Data16|Data32))
@ -1300,12 +1413,8 @@ md_assemble (line)
operand size prefix. */ operand size prefix. */
if ((i.suffix == WORD_OPCODE_SUFFIX) ^ flag_16bit_code) if ((i.suffix == WORD_OPCODE_SUFFIX) ^ flag_16bit_code)
{ {
if (i.prefixes == MAX_PREFIXES) if (! check_prefix (WORD_PREFIX_OPCODE))
{ return;
as_bad ("%d prefixes given and data size prefix gives too many prefixes",
MAX_PREFIXES);
return;
}
i.prefix[i.prefixes++] = WORD_PREFIX_OPCODE; i.prefix[i.prefixes++] = WORD_PREFIX_OPCODE;
} }
} }
@ -1327,41 +1436,19 @@ md_assemble (line)
found_reverse_match holds bit to set (different for int & found_reverse_match holds bit to set (different for int &
float insns). */ float insns). */
if (found_reverse_match) i.tm.base_opcode ^= found_reverse_match;
{
i.tm.base_opcode |= found_reverse_match;
}
/* The imul $imm, %reg instruction is converted into /* The imul $imm, %reg instruction is converted into
imul $imm, %reg, %reg. */ imul $imm, %reg, %reg, and the clr %reg instruction
if (i.tm.opcode_modifier & imulKludge) is converted into xor %reg, %reg. */
if (i.tm.opcode_modifier & regKludge)
{ {
/* Pretend we saw the 3 operand case. */ unsigned int first_reg_op = (i.types[0] & Reg) ? 0 : 1;
i.regs[2] = i.regs[1]; /* Pretend we saw the extra register operand. */
i.regs[first_reg_op+1] = i.regs[first_reg_op];
i.reg_operands = 2; i.reg_operands = 2;
} }
/* The clr %reg instruction is converted into xor %reg, %reg. */
if (i.tm.opcode_modifier & iclrKludge)
{
i.regs[1] = i.regs[0];
i.reg_operands = 2;
}
/* Certain instructions expect the destination to be in the i.rm.reg
field. This is by far the exceptional case. For these
instructions, if the source operand is a register, we must reverse
the i.rm.reg and i.rm.regmem fields. We accomplish this by faking
that the two register operands were given in the reverse order. */
if ((i.tm.opcode_modifier & ReverseRegRegmem) && i.reg_operands == 2)
{
unsigned int first_reg_operand = (i.types[0] & Reg) ? 0 : 1;
unsigned int second_reg_operand = first_reg_operand + 1;
reg_entry *tmp = i.regs[first_reg_operand];
i.regs[first_reg_operand] = i.regs[second_reg_operand];
i.regs[second_reg_operand] = tmp;
}
if (i.tm.opcode_modifier & ShortForm) if (i.tm.opcode_modifier & ShortForm)
{ {
/* The register or float register operand is in operand 0 or 1. */ /* The register or float register operand is in operand 0 or 1. */
@ -1377,32 +1464,6 @@ md_assemble (line)
i.suffix == DWORD_OPCODE_SUFFIX) i.suffix == DWORD_OPCODE_SUFFIX)
i.tm.base_opcode |= 0x8; i.tm.base_opcode |= 0x8;
} }
else if (i.tm.opcode_modifier & Seg2ShortForm)
{
if (i.tm.base_opcode == POP_SEG_SHORT && i.regs[0]->reg_num == 1)
{
as_bad ("you can't 'pop cs' on the 386.");
return;
}
i.tm.base_opcode |= (i.regs[0]->reg_num << 3);
}
else if (i.tm.opcode_modifier & Seg3ShortForm)
{
/* 'push %fs' is 0x0fa0; 'pop %fs' is 0x0fa1.
'push %gs' is 0x0fa8; 'pop %fs' is 0x0fa9.
So, only if i.regs[0]->reg_num == 5 (%gs) do we need
to change the opcode. */
if (i.regs[0]->reg_num == 5)
i.tm.base_opcode |= 0x08;
}
else if ((i.tm.base_opcode & ~DW) == MOV_AX_DISP32)
{
/* This is a special non-modrm instruction
that addresses memory with a 32-bit displacement mode anyway,
and thus requires an address-size prefix if in 16-bit mode. */
uses_mem_addrmode = 1;
default_seg = &ds;
}
else if (i.tm.opcode_modifier & Modrm) else if (i.tm.opcode_modifier & Modrm)
{ {
/* The opcode is completed (modulo i.tm.extension_opcode which /* The opcode is completed (modulo i.tm.extension_opcode which
@ -1425,10 +1486,25 @@ md_assemble (line)
| RegMMX)) | RegMMX))
? 0 : 1); ? 0 : 1);
dest = source + 1; dest = source + 1;
/* Certain instructions expect the destination to be
in the i.rm.reg field. This is by far the
exceptional case. For these instructions, if the
source operand is a register, we must reverse the
i.rm.reg and i.rm.regmem fields. We accomplish
this by pretending that the two register operands
were given in the reverse order. */
if (i.tm.opcode_modifier & ReverseRegRegmem)
{
reg_entry *tmp = i.regs[source];
i.regs[source] = i.regs[dest];
i.regs[dest] = tmp;
}
i.rm.mode = 3; i.rm.mode = 3;
/* We must be careful to make sure that all /* We must be careful to make sure that all
segment/control/test/debug/MMX registers go into segment/control/test/debug/MMX registers go into
the i.rm.reg field (despite the whether they are the i.rm.reg field (despite whether they are
source or destination operands). */ source or destination operands). */
if (i.regs[dest]->reg_type if (i.regs[dest]->reg_type
& (SReg2 | SReg3 | Control | Debug | Test | RegMMX)) & (SReg2 | SReg3 | Control | Debug | Test | RegMMX))
@ -1447,7 +1523,9 @@ md_assemble (line)
if (i.mem_operands) if (i.mem_operands)
{ {
unsigned int fake_zero_displacement = 0; unsigned int fake_zero_displacement = 0;
unsigned int op = (i.types[0] & Mem) ? 0 : ((i.types[1] & Mem) ? 1 : 2); unsigned int op = ((i.types[0] & Mem)
? 0
: (i.types[1] & Mem) ? 1 : 2);
/* Encode memory operand into modrm byte and base index /* Encode memory operand into modrm byte and base index
byte. */ byte. */
@ -1540,10 +1618,9 @@ md_assemble (line)
exp->X_op_symbol = (symbolS *) 0; exp->X_op_symbol = (symbolS *) 0;
} }
/* Find the default segment for the memory /* Find the default segment for the memory operand.
operand. Used to optimize out explicit segment Used to optimize out explicit segment specifications. */
specifications. */ if (i.seg[0])
if (i.seg && (t->opcode_modifier & LinearAddress) == 0)
{ {
unsigned int seg_index; unsigned int seg_index;
@ -1586,7 +1663,7 @@ md_assemble (line)
/* Now, if no memory operand has set i.rm.mode = 0, 1, 2 /* Now, if no memory operand has set i.rm.mode = 0, 1, 2
we must set it to 3 to indicate this is a register we must set it to 3 to indicate this is a register
operand int the regmem field */ operand in the regmem field. */
if (!i.mem_operands) if (!i.mem_operands)
i.rm.mode = 3; i.rm.mode = 3;
} }
@ -1599,18 +1676,46 @@ md_assemble (line)
if (i.rm.mode != 3) if (i.rm.mode != 3)
uses_mem_addrmode = 1; uses_mem_addrmode = 1;
} }
else if (i.tm.opcode_modifier & Seg2ShortForm)
{
if (i.tm.base_opcode == POP_SEG_SHORT && i.regs[0]->reg_num == 1)
{
as_bad ("you can't `pop %%cs' on the 386.");
return;
}
i.tm.base_opcode |= (i.regs[0]->reg_num << 3);
}
else if (i.tm.opcode_modifier & Seg3ShortForm)
{
/* 'push %fs' is 0x0fa0; 'pop %fs' is 0x0fa1.
'push %gs' is 0x0fa8; 'pop %fs' is 0x0fa9.
So, only if i.regs[0]->reg_num == 5 (%gs) do we need
to change the opcode. */
if (i.regs[0]->reg_num == 5)
i.tm.base_opcode |= 0x08;
}
else if ((i.tm.base_opcode & ~DW) == MOV_AX_DISP32)
{
/* This is a special non-modrm instruction
that addresses memory with a 32-bit displacement mode anyway,
and thus requires an address-size prefix if in 16-bit mode. */
uses_mem_addrmode = 1;
default_seg = &ds;
}
else if ((i.tm.opcode_modifier & IsString) != 0)
{
/* For the string instructions that allow a segment override
on one of their operands, the default segment is ds. */
default_seg = &ds;
}
/* GAS currently doesn't support 16-bit memory addressing modes at all, /* GAS currently doesn't support 16-bit memory addressing modes at all,
so if we're writing 16-bit code and using a memory addressing mode, so if we're writing 16-bit code and using a memory addressing mode,
always spew out an address size prefix. */ always spew out an address size prefix. */
if (uses_mem_addrmode && flag_16bit_code) if (uses_mem_addrmode && flag_16bit_code)
{ {
if (i.prefixes == MAX_PREFIXES) if (! check_prefix (ADDR_PREFIX_OPCODE))
{ return;
as_bad ("%d prefixes given and address size override gives too many prefixes",
MAX_PREFIXES);
return;
}
i.prefix[i.prefixes++] = ADDR_PREFIX_OPCODE; i.prefix[i.prefixes++] = ADDR_PREFIX_OPCODE;
} }
@ -1620,15 +1725,11 @@ md_assemble (line)
If we never figured out what the default segment is, If we never figured out what the default segment is,
then default_seg will be zero at this point, then default_seg will be zero at this point,
and the specified segment prefix will always be used. */ and the specified segment prefix will always be used. */
if ((i.seg) && (i.seg != default_seg)) if ((i.seg[0]) && (i.seg[0] != default_seg))
{ {
if (i.prefixes == MAX_PREFIXES) if (! check_prefix (i.seg[0]->seg_prefix))
{ return;
as_bad ("%d prefixes given and %s segment override gives too many prefixes", i.prefix[i.prefixes++] = i.seg[0]->seg_prefix;
MAX_PREFIXES, i.seg->seg_name);
return;
}
i.prefix[i.prefixes++] = i.seg->seg_prefix;
} }
} }
} }
@ -1649,6 +1750,9 @@ md_assemble (line)
{ {
unsigned long n = i.disps[0]->X_add_number; unsigned long n = i.disps[0]->X_add_number;
if (i.prefixes != 0)
as_warn ("skipping prefixes on this instruction");
if (i.disps[0]->X_op == O_constant) if (i.disps[0]->X_op == O_constant)
{ {
if (fits_in_signed_byte (n)) if (fits_in_signed_byte (n))
@ -1661,10 +1765,10 @@ md_assemble (line)
else else
{ /* It's an absolute word/dword displacement. */ { /* It's an absolute word/dword displacement. */
/* Use only 16-bit jumps for 16-bit code, /* Use 16-bit jumps only for 16-bit code,
because text segments are limited to 64K anyway; because text segments are limited to 64K anyway;
use only 32-bit jumps for 32-bit code, Use 32-bit jumps for 32-bit code, because they're faster,
because they're faster. */ and a 16-bit jump will clear the top 16 bits of %eip. */
int jmp_size = flag_16bit_code ? 2 : 4; int jmp_size = flag_16bit_code ? 2 : 4;
if (flag_16bit_code && !fits_in_signed_word (n)) if (flag_16bit_code && !fits_in_signed_word (n))
{ {
@ -1795,6 +1899,9 @@ md_assemble (line)
} }
else if (i.tm.opcode_modifier & JumpInterSegment) else if (i.tm.opcode_modifier & JumpInterSegment)
{ {
if (i.prefixes != 0)
as_warn ("skipping prefixes on this instruction");
if (flag_16bit_code) if (flag_16bit_code)
{ {
FRAG_APPEND_1_CHAR (WORD_PREFIX_OPCODE); FRAG_APPEND_1_CHAR (WORD_PREFIX_OPCODE);
@ -1818,7 +1925,16 @@ md_assemble (line)
/* Output normal instructions here. */ /* Output normal instructions here. */
unsigned char *q; unsigned char *q;
/* First the prefix bytes. */ /* Hack for fwait. It must come before any prefixes, as it
really is an instruction rather than a prefix. */
if ((i.tm.opcode_modifier & FWait) != 0)
{
p = frag_more (1);
insn_size += 1;
md_number_to_chars (p, (valueT) FWAIT_OPCODE, 1);
}
/* The prefix bytes. */
for (q = i.prefix; q < i.prefix + i.prefixes; q++) for (q = i.prefix; q < i.prefix + i.prefixes; q++)
{ {
p = frag_more (1); p = frag_more (1);
@ -2045,7 +2161,7 @@ i386_operand (operand_string)
register reg_entry *r; register reg_entry *r;
if (!(r = parse_register (op_string))) if (!(r = parse_register (op_string)))
{ {
as_bad ("bad register name ('%s')", op_string); as_bad ("bad register name `%s'", op_string);
return 0; return 0;
} }
/* Check for segment override, rather than segment register by /* Check for segment override, rather than segment register by
@ -2055,22 +2171,22 @@ i386_operand (operand_string)
switch (r->reg_num) switch (r->reg_num)
{ {
case 0: case 0:
i.seg = (seg_entry *) & es; i.seg[i.mem_operands] = (seg_entry *) & es;
break; break;
case 1: case 1:
i.seg = (seg_entry *) & cs; i.seg[i.mem_operands] = (seg_entry *) & cs;
break; break;
case 2: case 2:
i.seg = (seg_entry *) & ss; i.seg[i.mem_operands] = (seg_entry *) & ss;
break; break;
case 3: case 3:
i.seg = (seg_entry *) & ds; i.seg[i.mem_operands] = (seg_entry *) & ds;
break; break;
case 4: case 4:
i.seg = (seg_entry *) & fs; i.seg[i.mem_operands] = (seg_entry *) & fs;
break; break;
case 5: case 5:
i.seg = (seg_entry *) & gs; i.seg[i.mem_operands] = (seg_entry *) & gs;
break; break;
} }
op_string += 4; /* skip % <x> s : */ op_string += 4; /* skip % <x> s : */
@ -2078,7 +2194,7 @@ i386_operand (operand_string)
if (!is_digit_char (*op_string) && !is_identifier_char (*op_string) if (!is_digit_char (*op_string) && !is_identifier_char (*op_string)
&& *op_string != '(' && *op_string != ABSOLUTE_PREFIX) && *op_string != '(' && *op_string != ABSOLUTE_PREFIX)
{ {
as_bad ("bad memory operand after segment override"); as_bad ("bad memory operand `%s'", op_string);
return 0; return 0;
} }
/* Handle case of %es:*foo. */ /* Handle case of %es:*foo. */
@ -2118,7 +2234,7 @@ i386_operand (operand_string)
in certain cases. Oddly, the code in question turns out in certain cases. Oddly, the code in question turns out
to work correctly anyhow, so we make this just a warning to work correctly anyhow, so we make this just a warning
until those versions of gcc are obsolete. */ until those versions of gcc are obsolete. */
as_warn ("warning: unrecognized characters `%s' in expression", as_warn ("unrecognized characters `%s' in expression",
input_line_pointer); input_line_pointer);
} }
input_line_pointer = save_input_line_pointer; input_line_pointer = save_input_line_pointer;
@ -2126,7 +2242,7 @@ i386_operand (operand_string)
if (exp->X_op == O_absent) if (exp->X_op == O_absent)
{ {
/* missing or bad expr becomes absolute 0 */ /* missing or bad expr becomes absolute 0 */
as_bad ("missing or invalid immediate expression '%s' taken as 0", as_bad ("missing or invalid immediate expression `%s' taken as 0",
operand_string); operand_string);
exp->X_op = O_constant; exp->X_op = O_constant;
exp->X_add_number = 0; exp->X_add_number = 0;
@ -2182,9 +2298,12 @@ i386_operand (operand_string)
unsigned int found_base_index_form; unsigned int found_base_index_form;
do_memory_reference: do_memory_reference:
if (i.mem_operands == MAX_MEMORY_OPERANDS) if ((i.mem_operands == 1
&& (current_templates->start->opcode_modifier & IsString) == 0)
|| i.mem_operands == 2)
{ {
as_bad ("more than 1 memory reference in instruction"); as_bad ("too many memory references for `%s'",
current_templates->start->name);
return 0; return 0;
} }
i.mem_operands++; i.mem_operands++;
@ -2260,14 +2379,15 @@ i386_operand (operand_string)
base_string++; base_string++;
if (base_string == base_reg_name + 1) if (base_string == base_reg_name + 1)
{ {
as_bad ("can't find base register name after '(%c'", as_bad ("can't find base register name after `(%c'",
REGISTER_PREFIX); REGISTER_PREFIX);
return 0; return 0;
} }
END_STRING_AND_SAVE (base_string); END_STRING_AND_SAVE (base_string);
if (!(i.base_reg = parse_register (base_reg_name))) if (!(i.base_reg = parse_register (base_reg_name)))
{ {
as_bad ("bad base register name ('%s')", base_reg_name); as_bad ("bad base register name `%s'", base_reg_name);
RESTORE_END_STRING (base_string);
return 0; return 0;
} }
RESTORE_END_STRING (base_string); RESTORE_END_STRING (base_string);
@ -2278,7 +2398,7 @@ i386_operand (operand_string)
OR ')' ==> end. (scale factor = 1) */ OR ')' ==> end. (scale factor = 1) */
if (*base_string != ',' && *base_string != ')') if (*base_string != ',' && *base_string != ')')
{ {
as_bad ("expecting ',' or ')' after base register in `%s'", as_bad ("expecting `,' or `)' after base register in `%s'",
operand_string); operand_string);
return 0; return 0;
} }
@ -2291,7 +2411,8 @@ i386_operand (operand_string)
END_STRING_AND_SAVE (base_string); END_STRING_AND_SAVE (base_string);
if (!(i.index_reg = parse_register (index_reg_name))) if (!(i.index_reg = parse_register (index_reg_name)))
{ {
as_bad ("bad index register name ('%s')", index_reg_name); as_bad ("bad index register name `%s'", index_reg_name);
RESTORE_END_STRING (base_string);
return 0; return 0;
} }
RESTORE_END_STRING (base_string); RESTORE_END_STRING (base_string);
@ -2305,14 +2426,15 @@ i386_operand (operand_string)
base_string++; base_string++;
if (base_string == num_string) if (base_string == num_string)
{ {
as_bad ("can't find a scale factor after ','"); as_bad ("can't find a scale factor after `,'");
return 0; return 0;
} }
END_STRING_AND_SAVE (base_string); END_STRING_AND_SAVE (base_string);
/* We've got a scale factor. */ /* We've got a scale factor. */
if (!sscanf (num_string, "%d", &num)) if (!sscanf (num_string, "%d", &num))
{ {
as_bad ("can't parse scale factor from '%s'", num_string); as_bad ("can't parse scale factor from `%s'", num_string);
RESTORE_END_STRING (base_string);
return 0; return 0;
} }
RESTORE_END_STRING (base_string); RESTORE_END_STRING (base_string);
@ -2339,7 +2461,7 @@ i386_operand (operand_string)
{ {
if (!i.index_reg && *base_string == ',') if (!i.index_reg && *base_string == ',')
{ {
as_bad ("expecting index register or scale factor after ','; got '%c'", as_bad ("expecting index register or scale factor after `,'; got '%c'",
*(base_string + 1)); *(base_string + 1));
return 0; return 0;
} }
@ -2407,7 +2529,7 @@ i386_operand (operand_string)
*cp = '@'; *cp = '@';
} }
else else
as_bad ("Bad reloc specifier '%s' in expression", cp + 1); as_bad ("Bad reloc specifier `%s' in expression", cp + 1);
input_line_pointer = tmpbuf; input_line_pointer = tmpbuf;
} }
@ -2433,13 +2555,13 @@ i386_operand (operand_string)
#endif #endif
if (*input_line_pointer) if (*input_line_pointer)
as_bad ("Ignoring junk '%s' after expression", input_line_pointer); as_bad ("Ignoring junk `%s' after expression", input_line_pointer);
RESTORE_END_STRING (displacement_string_end); RESTORE_END_STRING (displacement_string_end);
input_line_pointer = save_input_line_pointer; input_line_pointer = save_input_line_pointer;
if (exp->X_op == O_absent) if (exp->X_op == O_absent)
{ {
/* missing expr becomes absolute 0 */ /* missing expr becomes absolute 0 */
as_bad ("missing or invalid displacement '%s' taken as 0", as_bad ("missing or invalid displacement `%s' taken as 0",
operand_string); operand_string);
i.types[this_operand] |= (Disp | Abs); i.types[this_operand] |= (Disp | Abs);
exp->X_op = O_constant; exp->X_op = O_constant;
@ -2493,13 +2615,13 @@ i386_operand (operand_string)
} }
if (i.index_reg && i.index_reg == esp) if (i.index_reg && i.index_reg == esp)
{ {
as_bad ("%s may not be used as an index register", esp->reg_name); as_bad ("`%%s' may not be used as an index register", esp->reg_name);
return 0; return 0;
} }
} }
else else
{ /* it's not a memory operand; argh! */ { /* it's not a memory operand; argh! */
as_bad ("invalid char %s begining %s operand '%s'", as_bad ("invalid char %s begining %s operand `%s'",
output_invalid (*op_string), ordinal_names[this_operand], output_invalid (*op_string), ordinal_names[this_operand],
op_string); op_string);
return 0; return 0;

View file

@ -157,7 +157,7 @@ extern int tc_coff_sizemachdep PARAMS ((fragS *frag));
#define MAX_OPERANDS 3 /* max operands per insn */ #define MAX_OPERANDS 3 /* max operands per insn */
#define MAX_PREFIXES 5 /* max prefixes per opcode */ #define MAX_PREFIXES 5 /* max prefixes per opcode */
#define MAX_IMMEDIATE_OPERANDS 2/* max immediates per insn */ #define MAX_IMMEDIATE_OPERANDS 2/* max immediates per insn */
#define MAX_MEMORY_OPERANDS 2 /* max memory ref per insn (lcall uses 2) */ #define MAX_MEMORY_OPERANDS 2 /* max memory refs per insn (string ops) */
/* we define the syntax here (modulo base,index,scale syntax) */ /* we define the syntax here (modulo base,index,scale syntax) */
#define REGISTER_PREFIX '%' #define REGISTER_PREFIX '%'
@ -198,52 +198,58 @@ extern int tc_coff_sizemachdep PARAMS ((fragS *frag));
Operands are classified so that we can match given operand types with Operands are classified so that we can match given operand types with
the opcode table in i386-opcode.h. the opcode table in i386-opcode.h.
*/ */
#define Unknown 0x0
/* register */ /* register */
#define Reg8 0x1 /* 8 bit reg */ #define Reg8 0x1 /* 8 bit reg */
#define Reg16 0x2 /* 16 bit reg */ #define Reg16 0x2 /* 16 bit reg */
#define Reg32 0x4 /* 32 bit reg */ #define Reg32 0x4 /* 32 bit reg */
#define Reg (Reg8|Reg16|Reg32) /* gen'l register */
#define WordReg (Reg16|Reg32) /* for push/pop operands */
/* immediate */ /* immediate */
#define Imm8 0x8 /* 8 bit immediate */ #define Imm8 0x8 /* 8 bit immediate */
#define Imm8S 0x10 /* 8 bit immediate sign extended */ #define Imm8S 0x10 /* 8 bit immediate sign extended */
#define Imm16 0x20 /* 16 bit immediate */ #define Imm16 0x20 /* 16 bit immediate */
#define Imm32 0x40 /* 32 bit immediate */ #define Imm32 0x40 /* 32 bit immediate */
#define Imm1 0x80 /* 1 bit immediate */ #define Imm1 0x80 /* 1 bit immediate */
#define ImmUnknown Imm32 /* for unknown expressions */
#define Imm (Imm8|Imm8S|Imm16|Imm32) /* gen'l immediate */
/* memory */ /* memory */
#define Disp8 0x200 /* 8 bit displacement (for jumps) */ #define BaseIndex 0x100
#define Disp16 0x400 /* 16 bit displacement */ /* Disp8,16,32 are used in different ways, depending on the
#define Disp32 0x800 /* 32 bit displacement */ instruction. For jumps, they specify the size of the PC relative
#define Disp (Disp8|Disp16|Disp32) /* General displacement */ displacement, for baseindex type instructions, they specify the
#define DispUnknown Disp32 /* for unknown size displacements */ size of the offset relative to the base register, and for memory
#define Mem8 0x1000 offset instructions such as `mov 1234,%al' they specify the size of
#define Mem16 0x2000 the offset relative to the segment base. */
#define Mem32 0x4000 #define Disp8 0x200 /* 8 bit displacement */
#define BaseIndex 0x8000 #define Disp16 0x400 /* 16 bit displacement */
#define Mem (Disp|Mem8|Mem16|Mem32|BaseIndex) /* General memory */ #define Disp32 0x800 /* 32 bit displacement */
#define WordMem (Mem16|Mem32|Disp|BaseIndex) /* Mem8,16,32 are used to limit the allowed sizes of memory operands */
#define ByteMem (Mem8|Disp|BaseIndex) #define Mem8 0x1000
#define Mem16 0x2000
#define Mem32 0x4000
/* specials */ /* specials */
#define InOutPortReg 0x10000 /* register to hold in/out port addr = dx */ #define InOutPortReg 0x10000 /* register to hold in/out port addr = dx */
#define ShiftCount 0x20000 /* register to hold shift cound = cl */ #define ShiftCount 0x20000 /* register to hold shift cound = cl */
#define Control 0x40000 /* Control register */ #define Control 0x40000 /* Control register */
#define Debug 0x80000 /* Debug register */ #define Debug 0x80000 /* Debug register */
#define Test 0x100000 /* Test register */ #define Test 0x100000 /* Test register */
#define FloatReg 0x200000 /* Float register */ #define FloatReg 0x200000 /* Float register */
#define FloatAcc 0x400000 /* Float stack top %st(0) */ #define FloatAcc 0x400000 /* Float stack top %st(0) */
#define SReg2 0x800000 /* 2 bit segment register */ #define SReg2 0x800000 /* 2 bit segment register */
#define SReg3 0x1000000 /* 3 bit segment register */ #define SReg3 0x1000000 /* 3 bit segment register */
#define Acc 0x2000000 /* Accumulator %al or %ax or %eax */ #define Acc 0x2000000 /* Accumulator %al or %ax or %eax */
#define ImplicitRegister (InOutPortReg|ShiftCount|Acc|FloatAcc)
#define JumpAbsolute 0x4000000 #define JumpAbsolute 0x4000000
#define Abs8 0x08000000 #define Abs8 0x8000000
#define Abs16 0x10000000 #define Abs16 0x10000000
#define Abs32 0x20000000 #define Abs32 0x20000000
#define Abs (Abs8|Abs16|Abs32) #define RegMMX 0x40000000 /* MMX register */
#define RegMMX 0x40000000 /* MMX register */ #define EsSeg 0x80000000 /* String insn operand with fixed es segment */
#define Reg (Reg8|Reg16|Reg32) /* gen'l register */
#define WordReg (Reg16|Reg32)
#define Imm (Imm8|Imm8S|Imm16|Imm32) /* gen'l immediate */
#define Disp (Disp8|Disp16|Disp32) /* General displacement */
#define Mem (Mem8|Mem16|Mem32|Disp|BaseIndex) /* General memory */
#define WordMem (Mem16|Mem32|Disp|BaseIndex)
#define ByteMem (Mem8|Disp|BaseIndex)
#define ImplicitRegister (InOutPortReg|ShiftCount|Acc|FloatAcc)
#define Abs (Abs8|Abs16|Abs32)
#define Byte (Reg8|Imm8|Imm8S) #define Byte (Reg8|Imm8|Imm8S)
#define Word (Reg16|Imm16) #define Word (Reg16|Imm16)
@ -274,35 +280,28 @@ typedef struct
unsigned int opcode_modifier; unsigned int opcode_modifier;
/* opcode_modifier bits: */ /* opcode_modifier bits: */
#define W 0x1 /* set if operands are words or dwords */ #define W 0x1 /* set if operands can be words or dwords
#define D 0x2 /* D = 0 if Reg --> Regmem; D = 1 if Regmem --> Reg */ encoded the canonical way: MUST BE 0x1 */
/* direction flag for floating insns: MUST BE 0x400 */ #define D 0x2 /* D = 0 if Reg --> Regmem;
#define FloatD 0x400 D = 1 if Regmem --> Reg: MUST BE 0x2 */
/* shorthand */ #define Modrm 0x4
#define DW (D|W) #define ReverseRegRegmem 0x8
#define ShortForm 0x10 /* register is in low 3 bits of opcode */ #define ShortForm 0x10 /* register is in low 3 bits of opcode */
#define ShortFormW 0x20 /* ShortForm and W bit is 0x8 */ #define ShortFormW 0x20 /* ShortForm and W bit is 0x8 */
#define Seg2ShortForm 0x40 /* encoding of load segment reg insns */ #define Seg2ShortForm 0x40 /* encoding of load segment reg insns */
#define Seg3ShortForm 0x80 /* fs/gs segment register insns. */ #define Seg3ShortForm 0x80 /* fs/gs segment register insns. */
#define Jump 0x100 /* special case for jump insns. */ #define Jump 0x100 /* special case for jump insns. */
#define JumpInterSegment 0x200 /* special case for intersegment leaps/calls */ #define JumpInterSegment 0x200 /* special case for intersegment leaps/calls */
/* 0x400 CANNOT BE USED since it's already used by FloatD above */ #define FloatD 0x400 /* direction for float insns: MUST BE 0x400 */
#define DONT_USE 0x400 #define JumpByte 0x800
#define NoModrm 0x800 #define JumpDword 0x1000
#define Modrm 0x1000 #define FWait 0x2000 /* instruction needs FWAIT */
#define imulKludge 0x2000 #define Data16 0x4000 /* needs data prefix if in 32-bit mode */
#define JumpByte 0x4000 #define Data32 0x8000 /* needs data prefix if in 16-bit mode */
#define JumpDword 0x8000 #define IsString 0x100000 /* quick test for string instructions */
#define ReverseRegRegmem 0x10000 #define regKludge 0x200000 /* fake an extra reg operand for clr, imul */
#define Data16 0x20000 /* needs data prefix if in 32-bit mode */
#define Data32 0x40000 /* needs data prefix if in 16-bit mode */
#define iclrKludge 0x80000 /* used to convert clr to xor */
#define LinearAddress 0x100000 /* uses linear address (no segment) */
/* (opcode_modifier & COMES_IN_ALL_SIZES) is true if the #define DW (D|W) /* shorthand */
instuction comes in byte, word, and dword sizes and is encoded into
machine code in the canonical way. */
#define COMES_IN_ALL_SIZES (W)
/* (opcode_modifier & COMES_IN_BOTH_DIRECTIONS) indicates that the /* (opcode_modifier & COMES_IN_BOTH_DIRECTIONS) indicates that the
source and destination operands can be reversed by setting either source and destination operands can be reversed by setting either