From 4498e3d641de103d13590ab9b687591c0b08c0fe Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Tue, 21 Apr 1998 21:08:39 +0000 Subject: [PATCH] Tue Apr 21 17:01:22 1998 Alan Modra * 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. --- gas/ChangeLog | 30 ++++ gas/config/tc-i386.c | 420 ++++++++++++++++++++++++++++--------------- gas/config/tc-i386.h | 137 +++++++------- 3 files changed, 369 insertions(+), 218 deletions(-) diff --git a/gas/ChangeLog b/gas/ChangeLog index c422f7d185..f89e2c7db5 100644 --- a/gas/ChangeLog +++ b/gas/ChangeLog @@ -1,3 +1,33 @@ +Tue Apr 21 17:01:22 1998 Alan Modra + + * 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 * configure.in: Call AM_PROG_LEX rather than AC_PROG_LEX and diff --git a/gas/config/tc-i386.c b/gas/config/tc-i386.c index 427d2c6c9f..10c97f55ca 100644 --- a/gas/config/tc-i386.c +++ b/gas/config/tc-i386.c @@ -44,6 +44,7 @@ static int fits_in_unsigned_byte PARAMS ((long)); static int fits_in_unsigned_word PARAMS ((long)); static int fits_in_signed_word PARAMS ((long)); static int smallest_imm_type PARAMS ((long)); +static int check_prefix PARAMS ((int)); static void set_16bit_code_flag PARAMS ((int)); #ifdef BFD_ASSEMBLER static bfd_reloc_code_real_type reloc @@ -96,9 +97,9 @@ struct _i386_insn reg_entry *index_reg; unsigned int log2_scale_factor; - /* SEG gives the seg_entry of this insn. It is equal to zero unless - an explicit segment override is given. */ - const seg_entry *seg; /* segment for memory operands (if given) */ + /* SEG gives the seg_entries of this insn. They are zero unless + explicit segment overrides are given. */ + const seg_entry *seg[2]; /* segments for memory operands (if given) */ /* PREFIX holds all the given prefix opcodes (usually null). PREFIXES is the size of PREFIX. */ @@ -106,7 +107,12 @@ struct _i386_insn unsigned char prefix[MAX_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. */ modrm_byte rm; @@ -179,10 +185,13 @@ static char *save_stack_p; /* stack pointer */ /* The instruction we're assembling. */ static i386_insn i; +/* Possible templates for current insn. */ +static templates *current_templates; + /* Per instruction expressionS buffers: 2 displacements & 2 immediate max. */ 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 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}, - /* For now we don't use word displacement jumps; they may be - untrustworthy. */ + /* For now we don't use word displacement jumps; they will not work + for destination addresses > 0xFFFF, since they clear the upper 16 + bits of %eip. */ {127 + 1, -128 + 1, 0, ENCODE_RELAX_STATE (COND_JUMP, DWORD)}, /* word conditionals add 3 bytes to frag: 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)); #endif -symbolS *GOT_symbol; /* Pre-defined "__GLOBAL_OFFSET_TABLE" */ +symbolS *GOT_symbol; /* Pre-defined "_GLOBAL_OFFSET_TABLE_" */ static INLINE unsigned long mode_from_disp_size (t) @@ -422,6 +432,81 @@ smallest_imm_type (num) : (Imm32)); } /* 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 set_16bit_code_flag (new_16bit_code_flag) int new_16bit_code_flag; @@ -736,10 +821,12 @@ type_names[] = { Imm1, "i1" }, { Control, "control reg" }, { Test, "test reg" }, + { Debug, "debug reg" }, { FloatReg, "FReg" }, { FloatAcc, "FAcc" }, { JumpAbsolute, "Jump Absolute" }, { RegMMX, "rMMX" }, + { EsSeg, "es" }, { 0, "" } }; @@ -840,9 +927,6 @@ md_assemble (line) /* Count the size of the instruction generated. */ int insn_size = 0; - /* Possible templates for current insn */ - templates *current_templates = (templates *) 0; - int j; /* Initialize globals. */ @@ -861,8 +945,8 @@ md_assemble (line) /* 1 if operand is pending after ','. */ unsigned int expecting_operand = 0; - /* 1 if we found a prefix only acceptable with string insns. */ - unsigned int expecting_string_instruction = 0; + /* Non-zero if we found a prefix only acceptable with string insns. */ + const char *expecting_string_instruction = NULL; /* Non-zero if operand parens not balanced. */ unsigned int paren_not_balanced; char *token_start = l; @@ -894,26 +978,26 @@ md_assemble (line) prefix = (prefix_entry *) hash_find (prefix_hash, token_start); 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; } RESTORE_END_STRING (l); /* check for repeated prefix */ - for (q = 0; q < i.prefixes; q++) - if (i.prefix[q] == prefix->prefix_code) - { - as_bad ("same prefix used twice; you don't really want this!"); - return; - } - if (i.prefixes == MAX_PREFIXES) + if (! check_prefix (prefix->prefix_code)) + return; + if (prefix->prefix_code == FWAIT_OPCODE) { - as_bad ("too many opcode prefixes"); - return; + i.wait_prefix = FWait; } - i.prefix[i.prefixes++] = prefix->prefix_code; - if (prefix->prefix_code == REPE || prefix->prefix_code == REPNE) - expecting_string_instruction = 1; - /* skip past PREFIX_SEPERATOR and reset token_start */ + else + { + i.prefix[i.prefixes++] = prefix->prefix_code; + 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; } } @@ -921,6 +1005,7 @@ md_assemble (line) if (token_start == l) { as_bad ("expecting opcode; got nothing"); + RESTORE_END_STRING (l); return; } @@ -944,6 +1029,7 @@ md_assemble (line) if (!current_templates) { as_bad ("no such 386 instruction: `%s'", token_start); + RESTORE_END_STRING (l); return; } } @@ -951,21 +1037,15 @@ md_assemble (line) /* check for rep/repne without a string instruction */ if (expecting_string_instruction && - !IS_STRING_INSTRUCTION (current_templates-> - start->base_opcode)) + !(current_templates->start->opcode_modifier & IsString)) { - as_bad ("expecting string instruction after rep/repne"); + as_bad ("expecting string instruction after `%s'", + expecting_string_instruction); return; } /* There may be operands to parse. */ - 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)) + if (*l != END_OF_INSN) { /* parse operands */ do @@ -1162,12 +1242,14 @@ md_assemble (line) } /* for (t = ... */ if (t == current_templates->end) { /* 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; } - /* Copy the template we found (we may change it!). */ + /* Copy the template we found. */ i.tm = *t; + i.tm.opcode_modifier |= i.wait_prefix; if (found_reverse_match) { @@ -1175,6 +1257,37 @@ md_assemble (line) 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, use it - and make sure none has already been specified. */ if (i.tm.opcode_modifier & (Data16|Data32)) @@ -1300,12 +1413,8 @@ md_assemble (line) operand size prefix. */ if ((i.suffix == WORD_OPCODE_SUFFIX) ^ flag_16bit_code) { - if (i.prefixes == MAX_PREFIXES) - { - as_bad ("%d prefixes given and data size prefix gives too many prefixes", - MAX_PREFIXES); - return; - } + if (! check_prefix (WORD_PREFIX_OPCODE)) + return; i.prefix[i.prefixes++] = WORD_PREFIX_OPCODE; } } @@ -1327,41 +1436,19 @@ md_assemble (line) found_reverse_match holds bit to set (different for int & 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 - imul $imm, %reg, %reg. */ - if (i.tm.opcode_modifier & imulKludge) + imul $imm, %reg, %reg, and the clr %reg instruction + is converted into xor %reg, %reg. */ + if (i.tm.opcode_modifier & regKludge) { - /* Pretend we saw the 3 operand case. */ - i.regs[2] = i.regs[1]; + unsigned int first_reg_op = (i.types[0] & Reg) ? 0 : 1; + /* Pretend we saw the extra register operand. */ + i.regs[first_reg_op+1] = i.regs[first_reg_op]; 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) { /* 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.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) { /* The opcode is completed (modulo i.tm.extension_opcode which @@ -1425,10 +1486,25 @@ md_assemble (line) | RegMMX)) ? 0 : 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; /* We must be careful to make sure that all 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). */ if (i.regs[dest]->reg_type & (SReg2 | SReg3 | Control | Debug | Test | RegMMX)) @@ -1447,7 +1523,9 @@ md_assemble (line) if (i.mem_operands) { 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 byte. */ @@ -1540,10 +1618,9 @@ md_assemble (line) exp->X_op_symbol = (symbolS *) 0; } - /* Find the default segment for the memory - operand. Used to optimize out explicit segment - specifications. */ - if (i.seg && (t->opcode_modifier & LinearAddress) == 0) + /* Find the default segment for the memory operand. + Used to optimize out explicit segment specifications. */ + if (i.seg[0]) { unsigned int seg_index; @@ -1586,7 +1663,7 @@ md_assemble (line) /* 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 - operand int the regmem field */ + operand in the regmem field. */ if (!i.mem_operands) i.rm.mode = 3; } @@ -1599,18 +1676,46 @@ md_assemble (line) if (i.rm.mode != 3) 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, so if we're writing 16-bit code and using a memory addressing mode, always spew out an address size prefix. */ if (uses_mem_addrmode && flag_16bit_code) { - if (i.prefixes == MAX_PREFIXES) - { - as_bad ("%d prefixes given and address size override gives too many prefixes", - MAX_PREFIXES); - return; - } + if (! check_prefix (ADDR_PREFIX_OPCODE)) + return; i.prefix[i.prefixes++] = ADDR_PREFIX_OPCODE; } @@ -1620,15 +1725,11 @@ md_assemble (line) If we never figured out what the default segment is, then default_seg will be zero at this point, 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) - { - as_bad ("%d prefixes given and %s segment override gives too many prefixes", - MAX_PREFIXES, i.seg->seg_name); - return; - } - i.prefix[i.prefixes++] = i.seg->seg_prefix; + if (! check_prefix (i.seg[0]->seg_prefix)) + return; + i.prefix[i.prefixes++] = i.seg[0]->seg_prefix; } } } @@ -1649,6 +1750,9 @@ md_assemble (line) { 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 (fits_in_signed_byte (n)) @@ -1661,10 +1765,10 @@ md_assemble (line) else { /* 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; - use only 32-bit jumps for 32-bit code, - because they're faster. */ + Use 32-bit jumps for 32-bit code, 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; if (flag_16bit_code && !fits_in_signed_word (n)) { @@ -1795,6 +1899,9 @@ md_assemble (line) } else if (i.tm.opcode_modifier & JumpInterSegment) { + if (i.prefixes != 0) + as_warn ("skipping prefixes on this instruction"); + if (flag_16bit_code) { FRAG_APPEND_1_CHAR (WORD_PREFIX_OPCODE); @@ -1818,7 +1925,16 @@ md_assemble (line) /* Output normal instructions here. */ 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++) { p = frag_more (1); @@ -2045,7 +2161,7 @@ i386_operand (operand_string) register reg_entry *r; if (!(r = parse_register (op_string))) { - as_bad ("bad register name ('%s')", op_string); + as_bad ("bad register name `%s'", op_string); return 0; } /* Check for segment override, rather than segment register by @@ -2055,22 +2171,22 @@ i386_operand (operand_string) switch (r->reg_num) { case 0: - i.seg = (seg_entry *) & es; + i.seg[i.mem_operands] = (seg_entry *) & es; break; case 1: - i.seg = (seg_entry *) & cs; + i.seg[i.mem_operands] = (seg_entry *) & cs; break; case 2: - i.seg = (seg_entry *) & ss; + i.seg[i.mem_operands] = (seg_entry *) & ss; break; case 3: - i.seg = (seg_entry *) & ds; + i.seg[i.mem_operands] = (seg_entry *) & ds; break; case 4: - i.seg = (seg_entry *) & fs; + i.seg[i.mem_operands] = (seg_entry *) & fs; break; case 5: - i.seg = (seg_entry *) & gs; + i.seg[i.mem_operands] = (seg_entry *) & gs; break; } op_string += 4; /* skip % s : */ @@ -2078,7 +2194,7 @@ i386_operand (operand_string) if (!is_digit_char (*op_string) && !is_identifier_char (*op_string) && *op_string != '(' && *op_string != ABSOLUTE_PREFIX) { - as_bad ("bad memory operand after segment override"); + as_bad ("bad memory operand `%s'", op_string); return 0; } /* Handle case of %es:*foo. */ @@ -2118,7 +2234,7 @@ i386_operand (operand_string) in certain cases. Oddly, the code in question turns out to work correctly anyhow, so we make this just a warning 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 = save_input_line_pointer; @@ -2126,7 +2242,7 @@ i386_operand (operand_string) if (exp->X_op == O_absent) { /* 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); exp->X_op = O_constant; exp->X_add_number = 0; @@ -2182,9 +2298,12 @@ i386_operand (operand_string) unsigned int found_base_index_form; 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; } i.mem_operands++; @@ -2260,14 +2379,15 @@ i386_operand (operand_string) base_string++; 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); return 0; } END_STRING_AND_SAVE (base_string); 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; } RESTORE_END_STRING (base_string); @@ -2278,7 +2398,7 @@ i386_operand (operand_string) OR ')' ==> end. (scale factor = 1) */ 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); return 0; } @@ -2291,7 +2411,8 @@ i386_operand (operand_string) END_STRING_AND_SAVE (base_string); 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; } RESTORE_END_STRING (base_string); @@ -2305,14 +2426,15 @@ i386_operand (operand_string) base_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; } END_STRING_AND_SAVE (base_string); /* We've got a scale factor. */ 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; } RESTORE_END_STRING (base_string); @@ -2339,7 +2461,7 @@ i386_operand (operand_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)); return 0; } @@ -2407,7 +2529,7 @@ i386_operand (operand_string) *cp = '@'; } 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; } @@ -2433,13 +2555,13 @@ i386_operand (operand_string) #endif 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); input_line_pointer = save_input_line_pointer; if (exp->X_op == O_absent) { /* 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); i.types[this_operand] |= (Disp | Abs); exp->X_op = O_constant; @@ -2493,13 +2615,13 @@ i386_operand (operand_string) } 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; } } else { /* 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], op_string); return 0; diff --git a/gas/config/tc-i386.h b/gas/config/tc-i386.h index 2fc36055a3..6d4f6b448b 100644 --- a/gas/config/tc-i386.h +++ b/gas/config/tc-i386.h @@ -157,7 +157,7 @@ extern int tc_coff_sizemachdep PARAMS ((fragS *frag)); #define MAX_OPERANDS 3 /* max operands per insn */ #define MAX_PREFIXES 5 /* max prefixes per opcode */ #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) */ #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 the opcode table in i386-opcode.h. */ -#define Unknown 0x0 /* register */ -#define Reg8 0x1 /* 8 bit reg */ -#define Reg16 0x2 /* 16 bit reg */ -#define Reg32 0x4 /* 32 bit reg */ -#define Reg (Reg8|Reg16|Reg32) /* gen'l register */ -#define WordReg (Reg16|Reg32) /* for push/pop operands */ +#define Reg8 0x1 /* 8 bit reg */ +#define Reg16 0x2 /* 16 bit reg */ +#define Reg32 0x4 /* 32 bit reg */ /* immediate */ -#define Imm8 0x8 /* 8 bit immediate */ -#define Imm8S 0x10 /* 8 bit immediate sign extended */ -#define Imm16 0x20 /* 16 bit immediate */ -#define Imm32 0x40 /* 32 bit immediate */ -#define Imm1 0x80 /* 1 bit immediate */ -#define ImmUnknown Imm32 /* for unknown expressions */ -#define Imm (Imm8|Imm8S|Imm16|Imm32) /* gen'l immediate */ +#define Imm8 0x8 /* 8 bit immediate */ +#define Imm8S 0x10 /* 8 bit immediate sign extended */ +#define Imm16 0x20 /* 16 bit immediate */ +#define Imm32 0x40 /* 32 bit immediate */ +#define Imm1 0x80 /* 1 bit immediate */ /* memory */ -#define Disp8 0x200 /* 8 bit displacement (for jumps) */ -#define Disp16 0x400 /* 16 bit displacement */ -#define Disp32 0x800 /* 32 bit displacement */ -#define Disp (Disp8|Disp16|Disp32) /* General displacement */ -#define DispUnknown Disp32 /* for unknown size displacements */ -#define Mem8 0x1000 -#define Mem16 0x2000 -#define Mem32 0x4000 -#define BaseIndex 0x8000 -#define Mem (Disp|Mem8|Mem16|Mem32|BaseIndex) /* General memory */ -#define WordMem (Mem16|Mem32|Disp|BaseIndex) -#define ByteMem (Mem8|Disp|BaseIndex) +#define BaseIndex 0x100 +/* Disp8,16,32 are used in different ways, depending on the + instruction. For jumps, they specify the size of the PC relative + displacement, for baseindex type instructions, they specify the + size of the offset relative to the base register, and for memory + offset instructions such as `mov 1234,%al' they specify the size of + the offset relative to the segment base. */ +#define Disp8 0x200 /* 8 bit displacement */ +#define Disp16 0x400 /* 16 bit displacement */ +#define Disp32 0x800 /* 32 bit displacement */ +/* Mem8,16,32 are used to limit the allowed sizes of memory operands */ +#define Mem8 0x1000 +#define Mem16 0x2000 +#define Mem32 0x4000 /* specials */ -#define InOutPortReg 0x10000 /* register to hold in/out port addr = dx */ -#define ShiftCount 0x20000 /* register to hold shift cound = cl */ -#define Control 0x40000 /* Control register */ -#define Debug 0x80000 /* Debug register */ -#define Test 0x100000 /* Test register */ -#define FloatReg 0x200000 /* Float register */ -#define FloatAcc 0x400000 /* Float stack top %st(0) */ -#define SReg2 0x800000 /* 2 bit segment register */ -#define SReg3 0x1000000 /* 3 bit segment register */ -#define Acc 0x2000000 /* Accumulator %al or %ax or %eax */ -#define ImplicitRegister (InOutPortReg|ShiftCount|Acc|FloatAcc) +#define InOutPortReg 0x10000 /* register to hold in/out port addr = dx */ +#define ShiftCount 0x20000 /* register to hold shift cound = cl */ +#define Control 0x40000 /* Control register */ +#define Debug 0x80000 /* Debug register */ +#define Test 0x100000 /* Test register */ +#define FloatReg 0x200000 /* Float register */ +#define FloatAcc 0x400000 /* Float stack top %st(0) */ +#define SReg2 0x800000 /* 2 bit segment register */ +#define SReg3 0x1000000 /* 3 bit segment register */ +#define Acc 0x2000000 /* Accumulator %al or %ax or %eax */ #define JumpAbsolute 0x4000000 -#define Abs8 0x08000000 -#define Abs16 0x10000000 -#define Abs32 0x20000000 -#define Abs (Abs8|Abs16|Abs32) -#define RegMMX 0x40000000 /* MMX register */ +#define Abs8 0x8000000 +#define Abs16 0x10000000 +#define Abs32 0x20000000 +#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 Word (Reg16|Imm16) @@ -274,35 +280,28 @@ typedef struct unsigned int opcode_modifier; /* opcode_modifier bits: */ -#define W 0x1 /* set if operands are words or dwords */ -#define D 0x2 /* D = 0 if Reg --> Regmem; D = 1 if Regmem --> Reg */ - /* direction flag for floating insns: MUST BE 0x400 */ -#define FloatD 0x400 - /* shorthand */ -#define DW (D|W) -#define ShortForm 0x10 /* register is in low 3 bits of opcode */ -#define ShortFormW 0x20 /* ShortForm and W bit is 0x8 */ -#define Seg2ShortForm 0x40 /* encoding of load segment reg insns */ -#define Seg3ShortForm 0x80 /* fs/gs segment register insns. */ -#define Jump 0x100 /* special case for jump insns. */ +#define W 0x1 /* set if operands can be words or dwords + encoded the canonical way: MUST BE 0x1 */ +#define D 0x2 /* D = 0 if Reg --> Regmem; + D = 1 if Regmem --> Reg: MUST BE 0x2 */ +#define Modrm 0x4 +#define ReverseRegRegmem 0x8 +#define ShortForm 0x10 /* register is in low 3 bits of opcode */ +#define ShortFormW 0x20 /* ShortForm and W bit is 0x8 */ +#define Seg2ShortForm 0x40 /* encoding of load segment reg insns */ +#define Seg3ShortForm 0x80 /* fs/gs segment register insns. */ +#define Jump 0x100 /* special case for jump insns. */ #define JumpInterSegment 0x200 /* special case for intersegment leaps/calls */ - /* 0x400 CANNOT BE USED since it's already used by FloatD above */ -#define DONT_USE 0x400 -#define NoModrm 0x800 -#define Modrm 0x1000 -#define imulKludge 0x2000 -#define JumpByte 0x4000 -#define JumpDword 0x8000 -#define ReverseRegRegmem 0x10000 -#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) */ +#define FloatD 0x400 /* direction for float insns: MUST BE 0x400 */ +#define JumpByte 0x800 +#define JumpDword 0x1000 +#define FWait 0x2000 /* instruction needs FWAIT */ +#define Data16 0x4000 /* needs data prefix if in 32-bit mode */ +#define Data32 0x8000 /* needs data prefix if in 16-bit mode */ +#define IsString 0x100000 /* quick test for string instructions */ +#define regKludge 0x200000 /* fake an extra reg operand for clr, imul */ - /* (opcode_modifier & COMES_IN_ALL_SIZES) is true if the - instuction comes in byte, word, and dword sizes and is encoded into - machine code in the canonical way. */ -#define COMES_IN_ALL_SIZES (W) +#define DW (D|W) /* shorthand */ /* (opcode_modifier & COMES_IN_BOTH_DIRECTIONS) indicates that the source and destination operands can be reversed by setting either