blob: 86ae753a1a20a9d772e707a957944bd74749a38a [file] [log] [blame]
/*---------------------------------------------------------------------------+
| get_address.c |
| |
| Get the effective address from an FPU instruction. |
| |
| Copyright (C) 1992,1993 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail apm233m@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))
/* Decode the SIB byte. This function assumes mod != 0 */
static void *sib(int mod)
{
unsigned char ss,index,base;
long offset;
RE_ENTRANT_CHECK_OFF
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
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
offset += (signed) get_fs_long((unsigned long *) FPU_EIP);
RE_ENTRANT_CHECK_ON
FPU_EIP += 4;
}
return (void *) offset;
}
/*
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 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
mod = (FPU_modrm >> 6) & 3;
if (FPU_rm == 4 && mod != 3)
{
FPU_data_address = sib(mod);
return;
}
cpu_reg_ptr = & REG_(FPU_rm);
switch (mod)
{
case 0:
if (FPU_rm == 5)
{
/* Special case: disp32 */
RE_ENTRANT_CHECK_OFF
offset = get_fs_long((unsigned long *) FPU_EIP);
RE_ENTRANT_CHECK_ON
FPU_EIP += 4;
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
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
offset = (signed) get_fs_long((unsigned long *) FPU_EIP);
RE_ENTRANT_CHECK_ON
FPU_EIP += 4;
break;
case 3:
/* Not legal for the FPU */
EXCEPTION(EX_Invalid);
}
FPU_data_address = offset + (char *)*cpu_reg_ptr;
}