/*
Copyright Notice
================
BOCHS is Copyright 1994 by Kevin P. Lawton.

BOCHS is shareware for PERSONAL USE only.

For more information, read the file 'LICENSE' included in the bochs
distribution.  If you don't have access to this file, or have questions
regarding the licensing policy, the author may be contacted via:

    US Mail:  Kevin Lawton
              528 Lexington St.
              Waltham, MA 02154

    EMail:    bochs@tiac.net
*/





#include <stdio.h>
#include "bx_bochs.h"



/* the default segment to use for a one-byte modrm with mod==00b
   and rm==i
 */
bx_segment_reg_t *bx_seg_reg_mod00_rm[8] = {
  &bx_cpu.ds,
  &bx_cpu.ds,
  &bx_cpu.ds,
  &bx_cpu.ds,
  &bx_cpu.ds, /* this entry should never be accessed, escape to 2-byte */
  &bx_cpu.ds,
  &bx_cpu.ds,
  &bx_cpu.ds
  };

/* the default segment to use for a one-byte modrm with mod==01b
   and rm==i
 */
bx_segment_reg_t *bx_seg_reg_mod01_rm[8] = {
  &bx_cpu.ds,
  &bx_cpu.ds,
  &bx_cpu.ds,
  &bx_cpu.ds,
  &bx_cpu.ds, /* this entry should never be accessed, escape to 2-byte */
  &bx_cpu.ss,
  &bx_cpu.ds,
  &bx_cpu.ds
  };

/* the default segment to use for a one-byte modrm with mod==10b
   and rm==i
 */
bx_segment_reg_t *bx_seg_reg_mod10_rm[8] = {
  &bx_cpu.ds,
  &bx_cpu.ds,
  &bx_cpu.ds,
  &bx_cpu.ds,
  &bx_cpu.ds, /* this entry should never be accessed, escape to 2-byte */
  &bx_cpu.ss,
  &bx_cpu.ds,
  &bx_cpu.ds
  };



/* the default segment to use for a two-byte modrm with mod==00b
   and base==i
 */
bx_segment_reg_t *bx_seg_reg_mod00_base[8] = {
  &bx_cpu.ds,
  &bx_cpu.ds,
  &bx_cpu.ds,
  &bx_cpu.ds,
  &bx_cpu.ss,
  &bx_cpu.ds,
  &bx_cpu.ds,
  &bx_cpu.ds
  };

/* the default segment to use for a two-byte modrm with mod==01b
   and base==i
 */
bx_segment_reg_t *bx_seg_reg_mod01_base[8] = {
  &bx_cpu.ds,
  &bx_cpu.ds,
  &bx_cpu.ds,
  &bx_cpu.ds,
  &bx_cpu.ss,
  &bx_cpu.ss,
  &bx_cpu.ds,
  &bx_cpu.ds
  };

/* the default segment to use for a two-byte modrm with mod==10b
   and base==i
 */
bx_segment_reg_t *bx_seg_reg_mod10_base[8] = {
  &bx_cpu.ds,
  &bx_cpu.ds,
  &bx_cpu.ds,
  &bx_cpu.ds,
  &bx_cpu.ss,
  &bx_cpu.ss,
  &bx_cpu.ds,
  &bx_cpu.ds
  };

bx_segment_reg_t *bx_seg_reg_16bit_mode[8] = {
  &bx_cpu.ds,
  &bx_cpu.ds,
  &bx_cpu.ss,
  &bx_cpu.ss,
  &bx_cpu.ds,
  &bx_cpu.ds,
  &bx_cpu.ss,
  &bx_cpu.ds
  };

extern bx_segment_reg_t *bx_segment_override;

/* bx_decode_exgx() will be rewritten using a switch statements
   to boost performance ... */

  void
bx_decode_exgx(Bit32u *reg_addr, Bit32u *mod_rm_addr,
    int *mod_rm_type, bx_segment_reg_t **mod_rm_seg_reg)
{
  Bit8u  modrm, mod, ttt, rm, sib, ss, index, base;
  Bit32u displ, index_reg_val, base_reg_val;

  Bit8u  displ8;
  Bit16u displ16;
  Bit16u base16, index16;


  if (bx_32bit_addrsize) {
    /* use 32bit addressing modes.  orthogonal base & index registers,
       scaling available, etc. */
    modrm = bx_fetch_next_byte();
    mod = modrm >> 6;
    ttt = (modrm >> 3) & 0x07;
    rm = modrm & 0x07;

    *reg_addr = ttt;
    if (mod == 3) { /* mod, reg, reg */
      *mod_rm_addr = rm;
      *mod_rm_type = BX_REGISTER_REF;
      }
    else { /* mod != 3 */
      *mod_rm_type = BX_MEMORY_REF;
      if (rm != 4) { /* rm != 100b, no s-i-b byte */
	/* one byte modrm */
	switch (mod) {
	  case 0:
	    if (bx_segment_override)
	      *mod_rm_seg_reg = bx_segment_override;
	    else
	      *mod_rm_seg_reg = bx_seg_reg_mod00_rm[rm];
	    if (rm == 5) { /* no reg, 32-bit displacement */
	      *mod_rm_addr = bx_fetch_next_dword();
	      }
	    else {
	      /* else reg indirect, no displacement */
	      BX_READ_32BIT_REG(*mod_rm_addr, rm);
	      }
	    break;
	  case 1:
	    if (bx_segment_override)
	      *mod_rm_seg_reg = bx_segment_override;
	    else
	      *mod_rm_seg_reg = bx_seg_reg_mod01_rm[rm];
	    /* reg, 8-bit displacement, sign extend */
	    displ = bx_fetch_next_byte();
	    BX_READ_32BIT_REG(*mod_rm_addr, rm);
	    *mod_rm_addr += BX_U_TO_S_BYTE(displ);
	    break;
	  case 2:
	    if (bx_segment_override)
	      *mod_rm_seg_reg = bx_segment_override;
	    else
	      *mod_rm_seg_reg = bx_seg_reg_mod10_rm[rm];
	    /* reg, 32-bit displacement */
	    displ = bx_fetch_next_dword();
	    BX_READ_32BIT_REG(*mod_rm_addr, rm);
	    *mod_rm_addr += BX_U_TO_S_32BIT(displ);
	    break;
	  } /* switch (mod) */
	} /* if (rm != 4) */
      else { /* rm == 4, s-i-b byte follows */
	sib = bx_fetch_next_byte();
	ss = sib >> 6;
	index = (sib >> 3) & 0x07;
	base = sib & 0x07;
	/*fprintf(stderr, "ss=%d, index=%d, base=%d\n", ss, index, base);*/
	switch (mod) {
	  case 0:
	    if (bx_segment_override)
	      *mod_rm_seg_reg = bx_segment_override;
	    else
	      *mod_rm_seg_reg = bx_seg_reg_mod00_base[base];
	    if (base != 5) { /* base != 101b, no displacement */
	      BX_READ_32BIT_REG(*mod_rm_addr, base);
	      /* index == 100b means no scale & index */
	      if (index != 4) {
		BX_READ_32BIT_REG(index_reg_val, index);
		index_reg_val <<= ss;
		*mod_rm_addr += index_reg_val;
		}
	      }
	    else { /* base == 101b (no base), 32-bit displacement */
	      displ = bx_fetch_next_dword();
	      *mod_rm_addr = displ;
	      if (index != 4) {
		BX_READ_32BIT_REG(index_reg_val, index);
		index_reg_val <<= ss;
		*mod_rm_addr += index_reg_val;
		}
	      }
	    break;
	  case 1:
	    if (bx_segment_override)
	      *mod_rm_seg_reg = bx_segment_override;
	    else
	      *mod_rm_seg_reg = bx_seg_reg_mod01_base[base];
	    displ = bx_fetch_next_byte();
	    BX_READ_32BIT_REG(base_reg_val, base);
	    BX_READ_32BIT_REG(index_reg_val, index);
	    *mod_rm_addr = displ + base_reg_val + (index_reg_val << ss);
	    break;
	  case 2:
	    if (bx_segment_override)
	      *mod_rm_seg_reg = bx_segment_override;
	    else
	      *mod_rm_seg_reg = bx_seg_reg_mod10_base[base];
	    displ = bx_fetch_next_dword();
	    BX_READ_32BIT_REG(base_reg_val, base);
	    BX_READ_32BIT_REG(index_reg_val, index);
	    *mod_rm_addr = displ + base_reg_val + (index_reg_val << ss);
	    break;
	  }
	} /* s-i-b byte follows */
      } /* if (mod != 3) */
    /*fprintf(stderr, "mod=%d, ttt=%d, rm=%d, ss=%d, index=%d, base=%d, displ=%d\n", mod, ttt, rm, ss, index, base, displ);*/
    }

  else {
    /* 16 bit addressing modes. */
    modrm = bx_fetch_next_byte();
    mod = modrm >> 6;
    ttt = (modrm >> 3) & 0x07;
    rm = modrm & 0x07;
    
    *reg_addr = ttt;
    switch (mod) {
      case 3: /* mod, reg, reg */
       *mod_rm_addr = rm;
       *mod_rm_type = BX_REGISTER_REF;
       break;
      case 0:
	*mod_rm_type = BX_MEMORY_REF;
        if (rm != 6) { /* no displacement */
          BX_READ_16BIT_BASE_REG(base16, rm);
	  BX_READ_16BIT_INDEX_REG(index16, rm);
	  *mod_rm_addr = (Bit16u) (base16 + index16);
	  if (bx_segment_override)
	    *mod_rm_seg_reg = bx_segment_override;
	  else
	    *mod_rm_seg_reg = bx_seg_reg_16bit_mode[rm];
          }
        else {
          *mod_rm_addr = (Bit16u) bx_fetch_next_word();
	  if (bx_segment_override)
	    *mod_rm_seg_reg = bx_segment_override;
	  else
	    *mod_rm_seg_reg = &bx_cpu.ds;
          }
	break;
      case 1:
	*mod_rm_type = BX_MEMORY_REF;
	displ8 = bx_fetch_next_byte();
	BX_READ_16BIT_BASE_REG(base16, rm);
	BX_READ_16BIT_INDEX_REG(index16, rm);
	*mod_rm_addr = (Bit16u) (base16 + index16 + (Bit8s) displ8);
	if (bx_segment_override)
	  *mod_rm_seg_reg = bx_segment_override;
	else
	  *mod_rm_seg_reg = bx_seg_reg_16bit_mode[rm];
	break;
      case 2:
	*mod_rm_type = BX_MEMORY_REF;
	displ16 = bx_fetch_next_word();
	BX_READ_16BIT_BASE_REG(base16, rm);
	BX_READ_16BIT_INDEX_REG(index16, rm);
	*mod_rm_addr = (Bit16u) (base16 + index16 + displ16);
	if (bx_segment_override)
	  *mod_rm_seg_reg = bx_segment_override;
	else
	  *mod_rm_seg_reg = bx_seg_reg_16bit_mode[rm];
	break;
      } /* switch (mod) ... */
    } /* else ... */
}

