This is the mail archive of the gas2@sourceware.cygnus.com mailing list for the gas2 project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

Patch: support for 16-bit i386 code.


I finally just decided to do this myself; wasn't too hard.
This patch creates '.code16' and '.code32' pseudo-opcodes for i386 gas,
which determine whether it writes 16-bit or 32-bit code.
When writing 16-bit code, it still only uses 32-bit addressing modes,
so the resulting code is suboptimal; but how much performance-critical
16-bit code is there these days anyway? :-)

BTW, this patch is to binutils-2.5.2.
Please acknowledge that you've received it
and when you expect it'll appear in a release.

Thanks!

*** include/opcode/old/i386.h	Fri Nov 25 07:11:50 1994
--- include/opcode/i386.h	Fri Nov 25 08:09:00 1994
***************
*** 36,43 ****
     conflict with the "movs" string move instruction.  Thus,
     {"movsb", 2, 0x0fbe, _, ReverseRegRegmem|Modrm, { Reg8|Mem,  Reg16|Reg32, 0} },
     is not kosher; we must seperate the two instructions. */
! {"movsbl", 2, 0x0fbe, _, ReverseRegRegmem|Modrm, { Reg8|Mem,  Reg32, 0} },
! {"movsbw", 2, 0x660fbe, _, ReverseRegRegmem|Modrm, { Reg8|Mem,  Reg16, 0} },
  {"movswl", 2, 0x0fbf, _, ReverseRegRegmem|Modrm, { Reg16|Mem, Reg32, 0} },
  
  /* move with zero extend */
--- 36,43 ----
     conflict with the "movs" string move instruction.  Thus,
     {"movsb", 2, 0x0fbe, _, ReverseRegRegmem|Modrm, { Reg8|Mem,  Reg16|Reg32, 0} },
     is not kosher; we must seperate the two instructions. */
! {"movsbl", 2, 0x0fbe, _, ReverseRegRegmem|Modrm|Data32, { Reg8|Mem,  Reg32, 0} },
! {"movsbw", 2, 0x0fbe, _, ReverseRegRegmem|Modrm|Data16, { Reg8|Mem,  Reg16, 0} },
  {"movswl", 2, 0x0fbf, _, ReverseRegRegmem|Modrm, { Reg16|Mem, Reg32, 0} },
  
  /* move with zero extend */
***************
*** 167,181 ****
  
  /* conversion insns */
  /* conversion:  intel naming */
! {"cbw", 0, 0x6698, _, NoModrm, { 0, 0, 0} },
! {"cwd", 0, 0x6699, _, NoModrm, { 0, 0, 0} },
! {"cwde", 0, 0x98, _, NoModrm, { 0, 0, 0} },
! {"cdq", 0, 0x99, _, NoModrm, { 0, 0, 0} },
  /*  att naming */
! {"cbtw", 0, 0x6698, _, NoModrm, { 0, 0, 0} },
! {"cwtl", 0, 0x98, _, NoModrm, { 0, 0, 0} },
! {"cwtd", 0, 0x6699, _, NoModrm, { 0, 0, 0} },
! {"cltd", 0, 0x99, _, NoModrm, { 0, 0, 0} },
  
  /* Warning! the mul/imul (opcode 0xf6) must only have 1 operand!  They are
     expanding 64-bit multiplies, and *cannot* be selected to accomplish
--- 167,181 ----
  
  /* conversion insns */
  /* conversion:  intel naming */
! {"cbw", 0, 0x98, _, NoModrm|Data16, { 0, 0, 0} },
! {"cwd", 0, 0x99, _, NoModrm|Data16, { 0, 0, 0} },
! {"cwde", 0, 0x98, _, NoModrm|Data32, { 0, 0, 0} },
! {"cdq", 0, 0x99, _, NoModrm|Data32, { 0, 0, 0} },
  /*  att naming */
! {"cbtw", 0, 0x98, _, NoModrm|Data16, { 0, 0, 0} },
! {"cwtl", 0, 0x98, _, NoModrm|Data32, { 0, 0, 0} },
! {"cwtd", 0, 0x99, _, NoModrm|Data16, { 0, 0, 0} },
! {"cltd", 0, 0x99, _, NoModrm|Data32, { 0, 0, 0} },
  
  /* Warning! the mul/imul (opcode 0xf6) must only have 1 operand!  They are
     expanding 64-bit multiplies, and *cannot* be selected to accomplish
***************
*** 321,334 ****
  {"jnle", 1, 0x7f, _, Jump, { Disp, 0, 0} },
  {"jg", 1, 0x7f, _, Jump, { Disp, 0, 0} },
  
  /* these turn into pseudo operations when disp is larger than 8 bits */
  #define IS_JUMP_ON_CX_ZERO(o) \
    (o == 0x66e3)
  #define IS_JUMP_ON_ECX_ZERO(o) \
    (o == 0xe3)
  
! {"jcxz", 1, 0x66e3, _, JumpByte, { Disp, 0, 0} },
! {"jecxz", 1, 0xe3, _, JumpByte, { Disp, 0, 0} },
  
  #define IS_LOOP_ECX_TIMES(o) \
    (o == 0xe2 || o == 0xe1 || o == 0xe0)
--- 321,339 ----
  {"jnle", 1, 0x7f, _, Jump, { Disp, 0, 0} },
  {"jg", 1, 0x7f, _, Jump, { Disp, 0, 0} },
  
+ #if 0  /* XXX where are these macros used?
+ 	  To get them working again, they need to take
+ 	  an entire template as the parameter,
+ 	  and check for Data16/Data32 flags.  */
  /* these turn into pseudo operations when disp is larger than 8 bits */
  #define IS_JUMP_ON_CX_ZERO(o) \
    (o == 0x66e3)
  #define IS_JUMP_ON_ECX_ZERO(o) \
    (o == 0xe3)
+ #endif
  
! {"jcxz", 1, 0xe3, _, JumpByte|Data16, { Disp, 0, 0} },
! {"jecxz", 1, 0xe3, _, JumpByte|Data32, { Disp, 0, 0} },
  
  #define IS_LOOP_ECX_TIMES(o) \
    (o == 0xe2 || o == 0xe1 || o == 0xe0)
***************
*** 432,439 ****
  /* i386sl (and i486sl?) only */
  {"rsm", 0, 0x0faa, _, NoModrm,{ 0, 0, 0} },
  
! {"boundl", 2, 0x62, _, Modrm, { Reg32, Mem, 0} },
! {"boundw", 2, 0x6662, _, Modrm, { Reg16, Mem, 0} },
  
  {"hlt", 0, 0xf4, _, NoModrm, { 0, 0, 0} },
  {"wait", 0, 0x9b, _, NoModrm, { 0, 0, 0} },
--- 437,444 ----
  /* i386sl (and i486sl?) only */
  {"rsm", 0, 0x0faa, _, NoModrm,{ 0, 0, 0} },
  
! {"boundl", 2, 0x62, _, Modrm|Data32, { Reg32, Mem, 0} },
! {"boundw", 2, 0x62, _, Modrm|Data16, { Reg16, Mem, 0} },
  
  {"hlt", 0, 0xf4, _, NoModrm, { 0, 0, 0} },
  {"wait", 0, 0x9b, _, NoModrm, { 0, 0, 0} },
***************
*** 808,813 ****
--- 813,819 ----
  };
  
  static const prefix_entry i386_prefixtab[] = {
+ #define ADDR_PREFIX_OPCODE 0x67
    { "addr16", 0x67 },		/* address size prefix ==> 16bit addressing
  				 * (How is this useful?) */
  #define WORD_PREFIX_OPCODE 0x66
*** gas/config/old/tc-i386.h	Fri Nov 25 06:34:23 1994
--- gas/config/tc-i386.h	Fri Nov 25 08:06:26 1994
***************
*** 125,131 ****
  #define tc_headers_hook(a)		{;}	/* not used */
  
  #define MAX_OPERANDS 3		/* max operands per insn */
! #define MAX_PREFIXES 4		/* 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) */
  
--- 125,131 ----
  #define tc_headers_hook(a)		{;}	/* not used */
  
  #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) */
  
***************
*** 263,268 ****
--- 263,270 ----
  #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 */
  
    /* (opcode_modifier & COMES_IN_ALL_SIZES) is true if the
       instuction comes in byte, word, and dword sizes and is encoded into
*** gas/config/old/tc-i386.c	Fri Nov 25 06:34:23 1994
--- gas/config/tc-i386.c	Fri Nov 25 08:37:02 1994
***************
*** 174,179 ****
--- 174,181 ----
  
  static int flag_do_long_jump;	/* FIXME what does this do? */
  
+ static int flag_16bit_code;	/* 1 if we're writing 16-bit code, 0 if 32-bit */
+ 
  /* Interface to relax_segment.
     There are 2 relax states for 386 jump insns: one for conditional &
     one for unconditional jumps.  This is because the these two types
***************
*** 318,323 ****
--- 320,331 ----
  	  : (Imm32));
  }				/* smallest_imm_type() */
  
+ void set_16bit_code_flag(new_16bit_code_flag)
+ 	int new_16bit_code_flag;
+ {
+   flag_16bit_code = new_16bit_code_flag;
+ }
+ 
  const pseudo_typeS md_pseudo_table[] =
  {
  #ifndef I386COFF
***************
*** 334,339 ****
--- 342,349 ----
    {"value", cons, 2},
    {"noopt", s_ignore, 0},
    {"optim", s_ignore, 0},
+   {"code16", set_16bit_code_flag, 1},
+   {"code32", set_16bit_code_flag, 0},
    {0, 0, 0}
  };
  
***************
*** 1034,1039 ****
--- 1044,1064 ----
      i.tm = *t;
      t = &i.tm;			/* alter new copy of template */
  
+     /* If the matched instruction specifies an explicit opcode suffix,
+        use it - and make sure none has already been specified.  */
+     if (t->opcode_modifier & (Data16|Data32))
+       {
+ 	if (i.suffix)
+ 	  {
+ 	    as_bad ("extraneous opcode suffix given");
+ 	    return;
+ 	  }
+ 	if (t->opcode_modifier & Data16)
+ 	  i.suffix = WORD_OPCODE_SUFFIX;
+ 	else
+ 	  i.suffix = DWORD_OPCODE_SUFFIX;
+       }
+ 
      /* If there's no opcode suffix we try to invent one based on register
         operands. */
      if (!i.suffix && i.reg_operands)
***************
*** 1117,1123 ****
  	  t->base_opcode |= W;
  	/* Now select between word & dword operations via the
  				   operand size prefix. */
! 	if (i.suffix == WORD_OPCODE_SUFFIX)
  	  {
  	    if (i.prefixes == MAX_PREFIXES)
  	      {
--- 1142,1148 ----
  	  t->base_opcode |= W;
  	/* Now select between word & dword operations via the
  				   operand size prefix. */
! 	if ((i.suffix == WORD_OPCODE_SUFFIX) ^ flag_16bit_code)
  	  {
  	    if (i.prefixes == MAX_PREFIXES)
  	      {
***************
*** 1383,1388 ****
--- 1408,1427 ----
  		if (t->extension_opcode != None)
  		  i.rm.reg = t->extension_opcode;
  	      }
+ 
+ 	    /* 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 ((i.rm.mode != 3) && flag_16bit_code)
+ 	      {
+ 		if (i.prefixes == MAX_PREFIXES)
+ 		  {
+ 		    as_bad ("%d prefixes given and address size override gives too many prefixes",
+ 		    	MAX_PREFIXES);
+ 		    return;
+ 		  }
+ 		i.prefix[i.prefixes++] = ADDR_PREFIX_OPCODE;
+ 	      }
  	  }
        }
    }
***************
*** 1470,1475 ****
--- 1509,1527 ----
        {
  	int size = (t->opcode_modifier & JumpByte) ? 1 : 4;
  	unsigned long n = i.disps[0]->X_add_number;
+ 	unsigned char *q;
+ 
+ 	/* The jcx/jecx instruction might need a data size prefix.
+ 	   XXX would it be OK just to output all the prefixes?  */
+ 	for (q = i.prefix; q < i.prefix + i.prefixes; q++)
+ 	  {
+ 	    if (*q == WORD_PREFIX_OPCODE)
+ 	      {
+ 	        p = frag_more (1);
+ 	        insn_size += 1;
+ 	        md_number_to_chars (p, (valueT) *q, 1);
+ 	      }
+ 	  }
  
  	if (fits_in_unsigned_byte (t->base_opcode))
  	  {
				Bryan
---
Bryan Ford	baford@cs.utah.edu	University of Utah, CSS
`finger baford@schirf.cs.utah.edu' for PGP key and other info.