blob: 3fb115cb6e3b31c723b0f5c9b7f702c5db4128c9 [file] [log] [blame]
/*---------------------------------------------------------------------------+
| get_address.c |
| |
| Get the effective address from an FPU instruction. |
| |
| Copyright (C) 1992,1993,1994 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
| |
+---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------+
| Note: |
| The file contains code which accesses user memory. |
| Emulator static data may change when user memory is accessed, due to |
| other processes using the emulator while swapping is in progress. |
+---------------------------------------------------------------------------*/
#include <linux/stddef.h>
#include <asm/segment.h>
#include "fpu_system.h"
#include "exception.h"
#include "fpu_emu.h"
static int reg_offset[] = {
offsetof(struct info,___eax),
offsetof(struct info,___ecx),
offsetof(struct info,___edx),
offsetof(struct info,___ebx),
offsetof(struct info,___esp),
offsetof(struct info,___ebp),
offsetof(struct info,___esi),
offsetof(struct info,___edi)
};
#define REG_(x) (*(long *)(reg_offset[(x)]+(char *) FPU_info))
static int reg_offset_vm86[] = {
offsetof(struct info,___cs),
offsetof(struct info,___vm86_ds),
offsetof(struct info,___vm86_es),
offsetof(struct info,___vm86_fs),
offsetof(struct info,___vm86_gs),
offsetof(struct info,___ss)
};
#define VM86_REG_(x) (*(unsigned short *) \
(reg_offset_vm86[((unsigned)x)]+(char *) FPU_info))
/* Decode the SIB byte. This function assumes mod != 0 */
static void *sib(int mod, unsigned long *fpu_eip)
{
unsigned char ss,index,base;
long offset;
RE_ENTRANT_CHECK_OFF;
FPU_code_verify_area(1);
base = get_fs_byte((char *) (*fpu_eip)); /* The SIB byte */
RE_ENTRANT_CHECK_ON;
(*fpu_eip)++;
ss = base >> 6;
index = (base >> 3) & 7;
base &= 7;
if ((mod == 0) && (base == 5))
offset = 0; /* No base register */
else
offset = REG_(base);
if (index == 4)
{
/* No index register */
/* A non-zero ss is illegal */
if ( ss )
EXCEPTION(EX_Invalid);
}
else
{
offset += (REG_(index)) << ss;
}
if (mod == 1)
{
/* 8 bit signed displacement */
RE_ENTRANT_CHECK_OFF;
FPU_code_verify_area(1);
offset += (signed char) get_fs_byte((char *) (*fpu_eip));
RE_ENTRANT_CHECK_ON;
(*fpu_eip)++;
}
else if (mod == 2 || base == 5) /* The second condition also has mod==0 */
{
/* 32 bit displacment */
RE_ENTRANT_CHECK_OFF;
FPU_code_verify_area(4);
offset += (signed) get_fs_long((unsigned long *) (*fpu_eip));
RE_ENTRANT_CHECK_ON;
(*fpu_eip) += 4;
}
return (void *) offset;
}
static unsigned long vm86_segment(unsigned char segment)
{
segment--;
#ifdef PARANOID
if ( segment > PREFIX_SS_ )
{
EXCEPTION(EX_INTERNAL|0x130);
math_abort(FPU_info,SIGSEGV);
}
#endif PARANOID
return (unsigned long)VM86_REG_(segment) << 4;
}
/*
MOD R/M byte: MOD == 3 has a special use for the FPU
SIB byte used iff R/M = 100b
7 6 5 4 3 2 1 0
..... ......... .........
MOD OPCODE(2) R/M
SIB byte
7 6 5 4 3 2 1 0
..... ......... .........
SS INDEX BASE
*/
void get_address(unsigned char FPU_modrm, unsigned long *fpu_eip,
fpu_addr_modes addr_modes)
{
unsigned char mod;
long *cpu_reg_ptr;
int offset = 0; /* Initialized just to stop compiler warnings. */
#ifndef PECULIAR_486
/* This is a reasonable place to do this */
FPU_data_selector = FPU_DS;
#endif PECULIAR_486
/* Memory accessed via the cs selector is write protected
in 32 bit protected mode. */
#define FPU_WRITE_BIT 0x10
if ( !addr_modes.vm86 && (FPU_modrm & FPU_WRITE_BIT)
&& (addr_modes.override.segment == PREFIX_CS_) )
{
math_abort(FPU_info,SIGSEGV);
}
mod = (FPU_modrm >> 6) & 3;
if (FPU_rm == 4 && mod != 3)
{
FPU_data_address = sib(mod, fpu_eip);
return;
}
cpu_reg_ptr = & REG_(FPU_rm);
switch (mod)
{
case 0:
if (FPU_rm == 5)
{
/* Special case: disp32 */
RE_ENTRANT_CHECK_OFF;
FPU_code_verify_area(4);
offset = get_fs_long((unsigned long *) (*fpu_eip));
(*fpu_eip) += 4;
RE_ENTRANT_CHECK_ON;
FPU_data_address = (void *) offset;
return;
}
else
{
FPU_data_address = (void *)*cpu_reg_ptr; /* Just return the contents
of the cpu register */
return;
}
case 1:
/* 8 bit signed displacement */
RE_ENTRANT_CHECK_OFF;
FPU_code_verify_area(1);
offset = (signed char) get_fs_byte((char *) (*fpu_eip));
RE_ENTRANT_CHECK_ON;
(*fpu_eip)++;
break;
case 2:
/* 32 bit displacement */
RE_ENTRANT_CHECK_OFF;
FPU_code_verify_area(4);
offset = (signed) get_fs_long((unsigned long *) (*fpu_eip));
(*fpu_eip) += 4;
RE_ENTRANT_CHECK_ON;
break;
case 3:
/* Not legal for the FPU */
EXCEPTION(EX_Invalid);
}
if ( addr_modes.vm86 )
{
offset += vm86_segment(addr_modes.override.segment);
}
FPU_data_address = offset + (char *)*cpu_reg_ptr;
}
void get_address_16(unsigned char FPU_modrm, unsigned long *fpu_eip,
fpu_addr_modes addr_modes)
{
unsigned char mod;
int offset = 0; /* Default used for mod == 0 */
#ifndef PECULIAR_486
/* This is a reasonable place to do this */
FPU_data_selector = FPU_DS;
#endif PECULIAR_486
/* Memory accessed via the cs selector is write protected
in 32 bit protected mode. */
#define FPU_WRITE_BIT 0x10
if ( !addr_modes.vm86 && (FPU_modrm & FPU_WRITE_BIT)
&& (addr_modes.override.segment == PREFIX_CS_) )
{
math_abort(FPU_info,SIGSEGV);
}
mod = (FPU_modrm >> 6) & 3;
switch (mod)
{
case 0:
if (FPU_rm == 6)
{
/* Special case: disp16 */
RE_ENTRANT_CHECK_OFF;
FPU_code_verify_area(2);
offset = (unsigned short)get_fs_word((unsigned short *) (*fpu_eip));
(*fpu_eip) += 2;
RE_ENTRANT_CHECK_ON;
goto add_segment;
}
break;
case 1:
/* 8 bit signed displacement */
RE_ENTRANT_CHECK_OFF;
FPU_code_verify_area(1);
offset = (signed char) get_fs_byte((signed char *) (*fpu_eip));
RE_ENTRANT_CHECK_ON;
(*fpu_eip)++;
break;
case 2:
/* 16 bit displacement */
RE_ENTRANT_CHECK_OFF;
FPU_code_verify_area(2);
offset = (unsigned) get_fs_word((unsigned short *) (*fpu_eip));
(*fpu_eip) += 2;
RE_ENTRANT_CHECK_ON;
break;
case 3:
/* Not legal for the FPU */
EXCEPTION(EX_Invalid);
break;
}
switch ( FPU_rm )
{
case 0:
offset += FPU_info->___ebx + FPU_info->___esi;
break;
case 1:
offset += FPU_info->___ebx + FPU_info->___edi;
break;
case 2:
offset += FPU_info->___ebp + FPU_info->___esi;
break;
case 3:
offset += FPU_info->___ebp + FPU_info->___edi;
break;
case 4:
offset += FPU_info->___esi;
break;
case 5:
offset += FPU_info->___edi;
break;
case 6:
offset += FPU_info->___ebp;
break;
case 7:
offset += FPU_info->___ebx;
break;
}
add_segment:
offset &= 0xffff;
if ( addr_modes.vm86 )
{
offset += vm86_segment(addr_modes.override.segment);
}
FPU_data_address = (void *)offset ;
}