1150 lines
34 KiB
C
1150 lines
34 KiB
C
/****************************************************************************
|
|
*
|
|
* Realmode X86 Emulator Library
|
|
*
|
|
* Copyright (C) 1991-2004 SciTech Software, Inc.
|
|
* Copyright (C) David Mosberger-Tang
|
|
* Copyright (C) 1999 Egbert Eich
|
|
*
|
|
* ========================================================================
|
|
*
|
|
* Permission to use, copy, modify, distribute, and sell this software and
|
|
* its documentation for any purpose is hereby granted without fee,
|
|
* provided that the above copyright notice appear in all copies and that
|
|
* both that copyright notice and this permission notice appear in
|
|
* supporting documentation, and that the name of the authors not be used
|
|
* in advertising or publicity pertaining to distribution of the software
|
|
* without specific, written prior permission. The authors makes no
|
|
* representations about the suitability of this software for any purpose.
|
|
* It is provided "as is" without express or implied warranty.
|
|
*
|
|
* THE AUTHORS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
|
|
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
|
|
* EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
|
|
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
|
|
* USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
* PERFORMANCE OF THIS SOFTWARE.
|
|
*
|
|
* ========================================================================
|
|
*
|
|
* Language: ANSI C
|
|
* Environment: Any
|
|
* Developer: Kendall Bennett
|
|
*
|
|
* Description: This file includes subroutines which are related to
|
|
* instruction decoding and accessess of immediate data via IP. etc.
|
|
*
|
|
****************************************************************************/
|
|
|
|
#include "x86emui.h"
|
|
|
|
/*----------------------------- Implementation ----------------------------*/
|
|
|
|
/****************************************************************************
|
|
REMARKS:
|
|
Handles any pending asychronous interrupts.
|
|
****************************************************************************/
|
|
static void x86emu_intr_handle(void)
|
|
{
|
|
u8 intno;
|
|
|
|
if (M.x86.intr & INTR_SYNCH) {
|
|
intno = M.x86.intno;
|
|
if (_X86EMU_intrTab[intno]) {
|
|
(*_X86EMU_intrTab[intno])(intno);
|
|
} else {
|
|
push_word((u16)M.x86.R_FLG);
|
|
CLEAR_FLAG(F_IF);
|
|
CLEAR_FLAG(F_TF);
|
|
push_word(M.x86.R_CS);
|
|
M.x86.R_CS = mem_access_word(intno * 4 + 2);
|
|
push_word(M.x86.R_IP);
|
|
M.x86.R_IP = mem_access_word(intno * 4);
|
|
M.x86.intr = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
PARAMETERS:
|
|
intrnum - Interrupt number to raise
|
|
|
|
REMARKS:
|
|
Raise the specified interrupt to be handled before the execution of the
|
|
next instruction.
|
|
****************************************************************************/
|
|
void x86emu_intr_raise(
|
|
u8 intrnum)
|
|
{
|
|
printk("%s, rasing execption %x\n", __func__, intrnum);
|
|
x86emu_dump_regs();
|
|
M.x86.intno = intrnum;
|
|
M.x86.intr |= INTR_SYNCH;
|
|
}
|
|
|
|
/****************************************************************************
|
|
REMARKS:
|
|
Main execution loop for the emulator. We return from here when the system
|
|
halts, which is normally caused by a stack fault when we return from the
|
|
original real mode call.
|
|
****************************************************************************/
|
|
void X86EMU_exec(void)
|
|
{
|
|
u8 op1;
|
|
|
|
M.x86.intr = 0;
|
|
DB(x86emu_end_instr();)
|
|
|
|
for (;;) {
|
|
DB( if (CHECK_IP_FETCH())
|
|
x86emu_check_ip_access();)
|
|
/* If debugging, save the IP and CS values. */
|
|
SAVE_IP_CS(M.x86.R_CS, M.x86.R_IP);
|
|
INC_DECODED_INST_LEN(1);
|
|
if (M.x86.intr) {
|
|
if (M.x86.intr & INTR_HALTED) {
|
|
DB( if (M.x86.R_SP != 0) {
|
|
printk("halted\n");
|
|
X86EMU_trace_regs();
|
|
}
|
|
else {
|
|
if (M.x86.debug)
|
|
printk("Service completed successfully\n");
|
|
})
|
|
return;
|
|
}
|
|
if (((M.x86.intr & INTR_SYNCH) && (M.x86.intno == 0 || M.x86.intno == 2)) ||
|
|
!ACCESS_FLAG(F_IF)) {
|
|
x86emu_intr_handle();
|
|
}
|
|
}
|
|
op1 = (*sys_rdb)(((u32)M.x86.R_CS << 4) + (M.x86.R_IP++));
|
|
(*x86emu_optab[op1])(op1);
|
|
//if (M.x86.debug & DEBUG_EXIT) {
|
|
// M.x86.debug &= ~DEBUG_EXIT;
|
|
// return;
|
|
//}
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
REMARKS:
|
|
Halts the system by setting the halted system flag.
|
|
****************************************************************************/
|
|
void X86EMU_halt_sys(void)
|
|
{
|
|
M.x86.intr |= INTR_HALTED;
|
|
}
|
|
|
|
/****************************************************************************
|
|
PARAMETERS:
|
|
mod - Mod value from decoded byte
|
|
regh - Reg h value from decoded byte
|
|
regl - Reg l value from decoded byte
|
|
|
|
REMARKS:
|
|
Raise the specified interrupt to be handled before the execution of the
|
|
next instruction.
|
|
|
|
NOTE: Do not inline this function, as (*sys_rdb) is already inline!
|
|
****************************************************************************/
|
|
void fetch_decode_modrm(
|
|
int *mod,
|
|
int *regh,
|
|
int *regl)
|
|
{
|
|
int fetched;
|
|
|
|
DB( if (CHECK_IP_FETCH())
|
|
x86emu_check_ip_access();)
|
|
fetched = (*sys_rdb)(((u32)M.x86.R_CS << 4) + (M.x86.R_IP++));
|
|
INC_DECODED_INST_LEN(1);
|
|
*mod = (fetched >> 6) & 0x03;
|
|
*regh = (fetched >> 3) & 0x07;
|
|
*regl = (fetched >> 0) & 0x07;
|
|
}
|
|
|
|
/****************************************************************************
|
|
RETURNS:
|
|
Immediate byte value read from instruction queue
|
|
|
|
REMARKS:
|
|
This function returns the immediate byte from the instruction queue, and
|
|
moves the instruction pointer to the next value.
|
|
|
|
NOTE: Do not inline this function, as (*sys_rdb) is already inline!
|
|
****************************************************************************/
|
|
u8 fetch_byte_imm(void)
|
|
{
|
|
u8 fetched;
|
|
|
|
DB( if (CHECK_IP_FETCH())
|
|
x86emu_check_ip_access();)
|
|
fetched = (*sys_rdb)(((u32)M.x86.R_CS << 4) + (M.x86.R_IP++));
|
|
INC_DECODED_INST_LEN(1);
|
|
return fetched;
|
|
}
|
|
|
|
/****************************************************************************
|
|
RETURNS:
|
|
Immediate word value read from instruction queue
|
|
|
|
REMARKS:
|
|
This function returns the immediate byte from the instruction queue, and
|
|
moves the instruction pointer to the next value.
|
|
|
|
NOTE: Do not inline this function, as (*sys_rdw) is already inline!
|
|
****************************************************************************/
|
|
u16 fetch_word_imm(void)
|
|
{
|
|
u16 fetched;
|
|
|
|
DB( if (CHECK_IP_FETCH())
|
|
x86emu_check_ip_access();)
|
|
fetched = (*sys_rdw)(((u32)M.x86.R_CS << 4) + (M.x86.R_IP));
|
|
M.x86.R_IP += 2;
|
|
INC_DECODED_INST_LEN(2);
|
|
return fetched;
|
|
}
|
|
|
|
/****************************************************************************
|
|
RETURNS:
|
|
Immediate lone value read from instruction queue
|
|
|
|
REMARKS:
|
|
This function returns the immediate byte from the instruction queue, and
|
|
moves the instruction pointer to the next value.
|
|
|
|
NOTE: Do not inline this function, as (*sys_rdw) is already inline!
|
|
****************************************************************************/
|
|
u32 fetch_long_imm(void)
|
|
{
|
|
u32 fetched;
|
|
|
|
DB( if (CHECK_IP_FETCH())
|
|
x86emu_check_ip_access();)
|
|
fetched = (*sys_rdl)(((u32)M.x86.R_CS << 4) + (M.x86.R_IP));
|
|
M.x86.R_IP += 4;
|
|
INC_DECODED_INST_LEN(4);
|
|
return fetched;
|
|
}
|
|
|
|
/****************************************************************************
|
|
RETURNS:
|
|
Value of the default data segment
|
|
|
|
REMARKS:
|
|
Inline function that returns the default data segment for the current
|
|
instruction.
|
|
|
|
On the x86 processor, the default segment is not always DS if there is
|
|
no segment override. Address modes such as -3[BP] or 10[BP+SI] all refer to
|
|
addresses relative to SS (ie: on the stack). So, at the minimum, all
|
|
decodings of addressing modes would have to set/clear a bit describing
|
|
whether the access is relative to DS or SS. That is the function of the
|
|
cpu-state-varible M.x86.mode. There are several potential states:
|
|
|
|
repe prefix seen (handled elsewhere)
|
|
repne prefix seen (ditto)
|
|
|
|
cs segment override
|
|
ds segment override
|
|
es segment override
|
|
fs segment override
|
|
gs segment override
|
|
ss segment override
|
|
|
|
ds/ss select (in absense of override)
|
|
|
|
Each of the above 7 items are handled with a bit in the mode field.
|
|
****************************************************************************/
|
|
_INLINE u32 get_data_segment(void)
|
|
{
|
|
#define GET_SEGMENT(segment)
|
|
switch (M.x86.mode & SYSMODE_SEGMASK) {
|
|
case 0: /* default case: use ds register */
|
|
case SYSMODE_SEGOVR_DS:
|
|
case SYSMODE_SEGOVR_DS | SYSMODE_SEG_DS_SS:
|
|
return M.x86.R_DS;
|
|
case SYSMODE_SEG_DS_SS: /* non-overridden, use ss register */
|
|
return M.x86.R_SS;
|
|
case SYSMODE_SEGOVR_CS:
|
|
case SYSMODE_SEGOVR_CS | SYSMODE_SEG_DS_SS:
|
|
return M.x86.R_CS;
|
|
case SYSMODE_SEGOVR_ES:
|
|
case SYSMODE_SEGOVR_ES | SYSMODE_SEG_DS_SS:
|
|
return M.x86.R_ES;
|
|
case SYSMODE_SEGOVR_FS:
|
|
case SYSMODE_SEGOVR_FS | SYSMODE_SEG_DS_SS:
|
|
return M.x86.R_FS;
|
|
case SYSMODE_SEGOVR_GS:
|
|
case SYSMODE_SEGOVR_GS | SYSMODE_SEG_DS_SS:
|
|
return M.x86.R_GS;
|
|
case SYSMODE_SEGOVR_SS:
|
|
case SYSMODE_SEGOVR_SS | SYSMODE_SEG_DS_SS:
|
|
return M.x86.R_SS;
|
|
default:
|
|
#ifdef CONFIG_DEBUG
|
|
printk("error: should not happen: multiple overrides.\n");
|
|
#endif
|
|
HALT_SYS();
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
PARAMETERS:
|
|
offset - Offset to load data from
|
|
|
|
RETURNS:
|
|
Byte value read from the absolute memory location.
|
|
|
|
NOTE: Do not inline this function as (*sys_rdX) is already inline!
|
|
****************************************************************************/
|
|
u8 fetch_data_byte(
|
|
uint offset)
|
|
{
|
|
#ifdef CONFIG_DEBUG
|
|
if (CHECK_DATA_ACCESS())
|
|
x86emu_check_data_access((u16)get_data_segment(), offset);
|
|
#endif
|
|
return (*sys_rdb)((get_data_segment() << 4) + offset);
|
|
}
|
|
|
|
/****************************************************************************
|
|
PARAMETERS:
|
|
offset - Offset to load data from
|
|
|
|
RETURNS:
|
|
Word value read from the absolute memory location.
|
|
|
|
NOTE: Do not inline this function as (*sys_rdX) is already inline!
|
|
****************************************************************************/
|
|
u16 fetch_data_word(
|
|
uint offset)
|
|
{
|
|
#ifdef CONFIG_DEBUG
|
|
if (CHECK_DATA_ACCESS())
|
|
x86emu_check_data_access((u16)get_data_segment(), offset);
|
|
#endif
|
|
return (*sys_rdw)((get_data_segment() << 4) + offset);
|
|
}
|
|
|
|
/****************************************************************************
|
|
PARAMETERS:
|
|
offset - Offset to load data from
|
|
|
|
RETURNS:
|
|
Long value read from the absolute memory location.
|
|
|
|
NOTE: Do not inline this function as (*sys_rdX) is already inline!
|
|
****************************************************************************/
|
|
u32 fetch_data_long(
|
|
uint offset)
|
|
{
|
|
#ifdef CONFIG_DEBUG
|
|
if (CHECK_DATA_ACCESS())
|
|
x86emu_check_data_access((u16)get_data_segment(), offset);
|
|
#endif
|
|
return (*sys_rdl)((get_data_segment() << 4) + offset);
|
|
}
|
|
|
|
/****************************************************************************
|
|
PARAMETERS:
|
|
segment - Segment to load data from
|
|
offset - Offset to load data from
|
|
|
|
RETURNS:
|
|
Byte value read from the absolute memory location.
|
|
|
|
NOTE: Do not inline this function as (*sys_rdX) is already inline!
|
|
****************************************************************************/
|
|
u8 fetch_data_byte_abs(
|
|
uint segment,
|
|
uint offset)
|
|
{
|
|
#ifdef CONFIG_DEBUG
|
|
if (CHECK_DATA_ACCESS())
|
|
x86emu_check_data_access(segment, offset);
|
|
#endif
|
|
return (*sys_rdb)(((u32)segment << 4) + offset);
|
|
}
|
|
|
|
/****************************************************************************
|
|
PARAMETERS:
|
|
segment - Segment to load data from
|
|
offset - Offset to load data from
|
|
|
|
RETURNS:
|
|
Word value read from the absolute memory location.
|
|
|
|
NOTE: Do not inline this function as (*sys_rdX) is already inline!
|
|
****************************************************************************/
|
|
u16 fetch_data_word_abs(
|
|
uint segment,
|
|
uint offset)
|
|
{
|
|
#ifdef CONFIG_DEBUG
|
|
if (CHECK_DATA_ACCESS())
|
|
x86emu_check_data_access(segment, offset);
|
|
#endif
|
|
return (*sys_rdw)(((u32)segment << 4) + offset);
|
|
}
|
|
|
|
/****************************************************************************
|
|
PARAMETERS:
|
|
segment - Segment to load data from
|
|
offset - Offset to load data from
|
|
|
|
RETURNS:
|
|
Long value read from the absolute memory location.
|
|
|
|
NOTE: Do not inline this function as (*sys_rdX) is already inline!
|
|
****************************************************************************/
|
|
u32 fetch_data_long_abs(
|
|
uint segment,
|
|
uint offset)
|
|
{
|
|
#ifdef CONFIG_DEBUG
|
|
if (CHECK_DATA_ACCESS())
|
|
x86emu_check_data_access(segment, offset);
|
|
#endif
|
|
return (*sys_rdl)(((u32)segment << 4) + offset);
|
|
}
|
|
|
|
/****************************************************************************
|
|
PARAMETERS:
|
|
offset - Offset to store data at
|
|
val - Value to store
|
|
|
|
REMARKS:
|
|
Writes a word value to an segmented memory location. The segment used is
|
|
the current 'default' segment, which may have been overridden.
|
|
|
|
NOTE: Do not inline this function as (*sys_wrX) is already inline!
|
|
****************************************************************************/
|
|
void store_data_byte(
|
|
uint offset,
|
|
u8 val)
|
|
{
|
|
#ifdef CONFIG_DEBUG
|
|
if (CHECK_DATA_ACCESS())
|
|
x86emu_check_data_access((u16)get_data_segment(), offset);
|
|
#endif
|
|
(*sys_wrb)((get_data_segment() << 4) + offset, val);
|
|
}
|
|
|
|
/****************************************************************************
|
|
PARAMETERS:
|
|
offset - Offset to store data at
|
|
val - Value to store
|
|
|
|
REMARKS:
|
|
Writes a word value to an segmented memory location. The segment used is
|
|
the current 'default' segment, which may have been overridden.
|
|
|
|
NOTE: Do not inline this function as (*sys_wrX) is already inline!
|
|
****************************************************************************/
|
|
void store_data_word(
|
|
uint offset,
|
|
u16 val)
|
|
{
|
|
#ifdef CONFIG_DEBUG
|
|
if (CHECK_DATA_ACCESS())
|
|
x86emu_check_data_access((u16)get_data_segment(), offset);
|
|
#endif
|
|
(*sys_wrw)((get_data_segment() << 4) + offset, val);
|
|
}
|
|
|
|
/****************************************************************************
|
|
PARAMETERS:
|
|
offset - Offset to store data at
|
|
val - Value to store
|
|
|
|
REMARKS:
|
|
Writes a long value to an segmented memory location. The segment used is
|
|
the current 'default' segment, which may have been overridden.
|
|
|
|
NOTE: Do not inline this function as (*sys_wrX) is already inline!
|
|
****************************************************************************/
|
|
void store_data_long(
|
|
uint offset,
|
|
u32 val)
|
|
{
|
|
#ifdef CONFIG_DEBUG
|
|
if (CHECK_DATA_ACCESS())
|
|
x86emu_check_data_access((u16)get_data_segment(), offset);
|
|
#endif
|
|
(*sys_wrl)((get_data_segment() << 4) + offset, val);
|
|
}
|
|
|
|
/****************************************************************************
|
|
PARAMETERS:
|
|
segment - Segment to store data at
|
|
offset - Offset to store data at
|
|
val - Value to store
|
|
|
|
REMARKS:
|
|
Writes a byte value to an absolute memory location.
|
|
|
|
NOTE: Do not inline this function as (*sys_wrX) is already inline!
|
|
****************************************************************************/
|
|
void store_data_byte_abs(
|
|
uint segment,
|
|
uint offset,
|
|
u8 val)
|
|
{
|
|
#ifdef CONFIG_DEBUG
|
|
if (CHECK_DATA_ACCESS())
|
|
x86emu_check_data_access(segment, offset);
|
|
#endif
|
|
(*sys_wrb)(((u32)segment << 4) + offset, val);
|
|
}
|
|
|
|
/****************************************************************************
|
|
PARAMETERS:
|
|
segment - Segment to store data at
|
|
offset - Offset to store data at
|
|
val - Value to store
|
|
|
|
REMARKS:
|
|
Writes a word value to an absolute memory location.
|
|
|
|
NOTE: Do not inline this function as (*sys_wrX) is already inline!
|
|
****************************************************************************/
|
|
void store_data_word_abs(
|
|
uint segment,
|
|
uint offset,
|
|
u16 val)
|
|
{
|
|
#ifdef CONFIG_DEBUG
|
|
if (CHECK_DATA_ACCESS())
|
|
x86emu_check_data_access(segment, offset);
|
|
#endif
|
|
(*sys_wrw)(((u32)segment << 4) + offset, val);
|
|
}
|
|
|
|
/****************************************************************************
|
|
PARAMETERS:
|
|
segment - Segment to store data at
|
|
offset - Offset to store data at
|
|
val - Value to store
|
|
|
|
REMARKS:
|
|
Writes a long value to an absolute memory location.
|
|
|
|
NOTE: Do not inline this function as (*sys_wrX) is already inline!
|
|
****************************************************************************/
|
|
void store_data_long_abs(
|
|
uint segment,
|
|
uint offset,
|
|
u32 val)
|
|
{
|
|
#ifdef CONFIG_DEBUG
|
|
if (CHECK_DATA_ACCESS())
|
|
x86emu_check_data_access(segment, offset);
|
|
#endif
|
|
(*sys_wrl)(((u32)segment << 4) + offset, val);
|
|
}
|
|
|
|
/****************************************************************************
|
|
PARAMETERS:
|
|
reg - Register to decode
|
|
|
|
RETURNS:
|
|
Pointer to the appropriate register
|
|
|
|
REMARKS:
|
|
Return a pointer to the register given by the R/RM field of the
|
|
modrm byte, for byte operands. Also enables the decoding of instructions.
|
|
****************************************************************************/
|
|
u8* decode_rm_byte_register(
|
|
int reg)
|
|
{
|
|
switch (reg) {
|
|
case 0:
|
|
DECODE_PRINTF("AL");
|
|
return &M.x86.R_AL;
|
|
case 1:
|
|
DECODE_PRINTF("CL");
|
|
return &M.x86.R_CL;
|
|
case 2:
|
|
DECODE_PRINTF("DL");
|
|
return &M.x86.R_DL;
|
|
case 3:
|
|
DECODE_PRINTF("BL");
|
|
return &M.x86.R_BL;
|
|
case 4:
|
|
DECODE_PRINTF("AH");
|
|
return &M.x86.R_AH;
|
|
case 5:
|
|
DECODE_PRINTF("CH");
|
|
return &M.x86.R_CH;
|
|
case 6:
|
|
DECODE_PRINTF("DH");
|
|
return &M.x86.R_DH;
|
|
case 7:
|
|
DECODE_PRINTF("BH");
|
|
return &M.x86.R_BH;
|
|
}
|
|
HALT_SYS();
|
|
return NULL; /* NOT REACHED OR REACHED ON ERROR */
|
|
}
|
|
|
|
/****************************************************************************
|
|
PARAMETERS:
|
|
reg - Register to decode
|
|
|
|
RETURNS:
|
|
Pointer to the appropriate register
|
|
|
|
REMARKS:
|
|
Return a pointer to the register given by the R/RM field of the
|
|
modrm byte, for word operands. Also enables the decoding of instructions.
|
|
****************************************************************************/
|
|
u16* decode_rm_word_register(
|
|
int reg)
|
|
{
|
|
switch (reg) {
|
|
case 0:
|
|
DECODE_PRINTF("AX");
|
|
return &M.x86.R_AX;
|
|
case 1:
|
|
DECODE_PRINTF("CX");
|
|
return &M.x86.R_CX;
|
|
case 2:
|
|
DECODE_PRINTF("DX");
|
|
return &M.x86.R_DX;
|
|
case 3:
|
|
DECODE_PRINTF("BX");
|
|
return &M.x86.R_BX;
|
|
case 4:
|
|
DECODE_PRINTF("SP");
|
|
return &M.x86.R_SP;
|
|
case 5:
|
|
DECODE_PRINTF("BP");
|
|
return &M.x86.R_BP;
|
|
case 6:
|
|
DECODE_PRINTF("SI");
|
|
return &M.x86.R_SI;
|
|
case 7:
|
|
DECODE_PRINTF("DI");
|
|
return &M.x86.R_DI;
|
|
}
|
|
HALT_SYS();
|
|
return NULL; /* NOTREACHED OR REACHED ON ERROR */
|
|
}
|
|
|
|
/****************************************************************************
|
|
PARAMETERS:
|
|
reg - Register to decode
|
|
|
|
RETURNS:
|
|
Pointer to the appropriate register
|
|
|
|
REMARKS:
|
|
Return a pointer to the register given by the R/RM field of the
|
|
modrm byte, for dword operands. Also enables the decoding of instructions.
|
|
****************************************************************************/
|
|
u32* decode_rm_long_register(
|
|
int reg)
|
|
{
|
|
switch (reg) {
|
|
case 0:
|
|
DECODE_PRINTF("EAX");
|
|
return &M.x86.R_EAX;
|
|
case 1:
|
|
DECODE_PRINTF("ECX");
|
|
return &M.x86.R_ECX;
|
|
case 2:
|
|
DECODE_PRINTF("EDX");
|
|
return &M.x86.R_EDX;
|
|
case 3:
|
|
DECODE_PRINTF("EBX");
|
|
return &M.x86.R_EBX;
|
|
case 4:
|
|
DECODE_PRINTF("ESP");
|
|
return &M.x86.R_ESP;
|
|
case 5:
|
|
DECODE_PRINTF("EBP");
|
|
return &M.x86.R_EBP;
|
|
case 6:
|
|
DECODE_PRINTF("ESI");
|
|
return &M.x86.R_ESI;
|
|
case 7:
|
|
DECODE_PRINTF("EDI");
|
|
return &M.x86.R_EDI;
|
|
}
|
|
HALT_SYS();
|
|
return NULL; /* NOTREACHED OR REACHED ON ERROR */
|
|
}
|
|
|
|
/****************************************************************************
|
|
PARAMETERS:
|
|
reg - Register to decode
|
|
|
|
RETURNS:
|
|
Pointer to the appropriate register
|
|
|
|
REMARKS:
|
|
Return a pointer to the register given by the R/RM field of the
|
|
modrm byte, for word operands, modified from above for the weirdo
|
|
special case of segreg operands. Also enables the decoding of instructions.
|
|
****************************************************************************/
|
|
u16* decode_rm_seg_register(
|
|
int reg)
|
|
{
|
|
switch (reg) {
|
|
case 0:
|
|
DECODE_PRINTF("ES");
|
|
return &M.x86.R_ES;
|
|
case 1:
|
|
DECODE_PRINTF("CS");
|
|
return &M.x86.R_CS;
|
|
case 2:
|
|
DECODE_PRINTF("SS");
|
|
return &M.x86.R_SS;
|
|
case 3:
|
|
DECODE_PRINTF("DS");
|
|
return &M.x86.R_DS;
|
|
case 4:
|
|
DECODE_PRINTF("FS");
|
|
return &M.x86.R_FS;
|
|
case 5:
|
|
DECODE_PRINTF("GS");
|
|
return &M.x86.R_GS;
|
|
case 6:
|
|
case 7:
|
|
DECODE_PRINTF("ILLEGAL SEGREG");
|
|
break;
|
|
}
|
|
HALT_SYS();
|
|
return NULL; /* NOT REACHED OR REACHED ON ERROR */
|
|
}
|
|
|
|
/****************************************************************************
|
|
PARAMETERS:
|
|
scale - scale value of SIB byte
|
|
index - index value of SIB byte
|
|
|
|
RETURNS:
|
|
Value of scale * index
|
|
|
|
REMARKS:
|
|
Decodes scale/index of SIB byte and returns relevant offset part of
|
|
effective address.
|
|
****************************************************************************/
|
|
unsigned decode_sib_si(
|
|
int scale,
|
|
int index)
|
|
{
|
|
scale = 1 << scale;
|
|
if (scale > 1) {
|
|
DECODE_PRINTF2("[%d*", scale);
|
|
} else {
|
|
DECODE_PRINTF("[");
|
|
}
|
|
switch (index) {
|
|
case 0:
|
|
DECODE_PRINTF("EAX]");
|
|
return M.x86.R_EAX * index;
|
|
case 1:
|
|
DECODE_PRINTF("ECX]");
|
|
return M.x86.R_ECX * index;
|
|
case 2:
|
|
DECODE_PRINTF("EDX]");
|
|
return M.x86.R_EDX * index;
|
|
case 3:
|
|
DECODE_PRINTF("EBX]");
|
|
return M.x86.R_EBX * index;
|
|
case 4:
|
|
DECODE_PRINTF("0]");
|
|
return 0;
|
|
case 5:
|
|
DECODE_PRINTF("EBP]");
|
|
return M.x86.R_EBP * index;
|
|
case 6:
|
|
DECODE_PRINTF("ESI]");
|
|
return M.x86.R_ESI * index;
|
|
case 7:
|
|
DECODE_PRINTF("EDI]");
|
|
return M.x86.R_EDI * index;
|
|
}
|
|
HALT_SYS();
|
|
return 0; /* NOT REACHED OR REACHED ON ERROR */
|
|
}
|
|
|
|
/****************************************************************************
|
|
PARAMETERS:
|
|
mod - MOD value of preceding ModR/M byte
|
|
|
|
RETURNS:
|
|
Offset in memory for the address decoding
|
|
|
|
REMARKS:
|
|
Decodes SIB addressing byte and returns calculated effective address.
|
|
****************************************************************************/
|
|
unsigned decode_sib_address(
|
|
int mod)
|
|
{
|
|
int sib = fetch_byte_imm();
|
|
int ss = (sib >> 6) & 0x03;
|
|
int index = (sib >> 3) & 0x07;
|
|
int base = sib & 0x07;
|
|
int offset = 0;
|
|
int displacement;
|
|
|
|
switch (base) {
|
|
case 0:
|
|
DECODE_PRINTF("[EAX]");
|
|
offset = M.x86.R_EAX;
|
|
break;
|
|
case 1:
|
|
DECODE_PRINTF("[ECX]");
|
|
offset = M.x86.R_ECX;
|
|
break;
|
|
case 2:
|
|
DECODE_PRINTF("[EDX]");
|
|
offset = M.x86.R_EDX;
|
|
break;
|
|
case 3:
|
|
DECODE_PRINTF("[EBX]");
|
|
offset = M.x86.R_EBX;
|
|
break;
|
|
case 4:
|
|
DECODE_PRINTF("[ESP]");
|
|
offset = M.x86.R_ESP;
|
|
break;
|
|
case 5:
|
|
switch (mod) {
|
|
case 0:
|
|
displacement = (s32)fetch_long_imm();
|
|
DECODE_PRINTF2("[%d]", displacement);
|
|
offset = displacement;
|
|
break;
|
|
case 1:
|
|
displacement = (s8)fetch_byte_imm();
|
|
DECODE_PRINTF2("[%d][EBP]", displacement);
|
|
offset = M.x86.R_EBP + displacement;
|
|
break;
|
|
case 2:
|
|
displacement = (s32)fetch_long_imm();
|
|
DECODE_PRINTF2("[%d][EBP]", displacement);
|
|
offset = M.x86.R_EBP + displacement;
|
|
break;
|
|
default:
|
|
HALT_SYS();
|
|
}
|
|
DECODE_PRINTF("[EAX]");
|
|
offset = M.x86.R_EAX;
|
|
break;
|
|
case 6:
|
|
DECODE_PRINTF("[ESI]");
|
|
offset = M.x86.R_ESI;
|
|
break;
|
|
case 7:
|
|
DECODE_PRINTF("[EDI]");
|
|
offset = M.x86.R_EDI;
|
|
break;
|
|
default:
|
|
HALT_SYS();
|
|
}
|
|
offset += decode_sib_si(ss, index);
|
|
return offset;
|
|
}
|
|
|
|
/****************************************************************************
|
|
PARAMETERS:
|
|
rm - RM value to decode
|
|
|
|
RETURNS:
|
|
Offset in memory for the address decoding
|
|
|
|
REMARKS:
|
|
Return the offset given by mod=00 addressing. Also enables the
|
|
decoding of instructions.
|
|
|
|
NOTE: The code which specifies the corresponding segment (ds vs ss)
|
|
below in the case of [BP+..]. The assumption here is that at the
|
|
point that this subroutine is called, the bit corresponding to
|
|
SYSMODE_SEG_DS_SS will be zero. After every instruction
|
|
except the segment override instructions, this bit (as well
|
|
as any bits indicating segment overrides) will be clear. So
|
|
if a SS access is needed, set this bit. Otherwise, DS access
|
|
occurs (unless any of the segment override bits are set).
|
|
****************************************************************************/
|
|
unsigned decode_rm00_address(
|
|
int rm)
|
|
{
|
|
unsigned offset;
|
|
|
|
if (M.x86.mode & SYSMODE_PREFIX_ADDR) {
|
|
/* 32-bit addressing */
|
|
switch (rm) {
|
|
case 0:
|
|
DECODE_PRINTF("[EAX]");
|
|
return M.x86.R_EAX;
|
|
case 1:
|
|
DECODE_PRINTF("[ECX]");
|
|
return M.x86.R_ECX;
|
|
case 2:
|
|
DECODE_PRINTF("[EDX]");
|
|
return M.x86.R_EDX;
|
|
case 3:
|
|
DECODE_PRINTF("[EBX]");
|
|
return M.x86.R_EBX;
|
|
case 4:
|
|
return decode_sib_address(0);
|
|
case 5:
|
|
offset = fetch_long_imm();
|
|
DECODE_PRINTF2("[%08x]", offset);
|
|
return offset;
|
|
case 6:
|
|
DECODE_PRINTF("[ESI]");
|
|
return M.x86.R_ESI;
|
|
case 7:
|
|
DECODE_PRINTF("[EDI]");
|
|
return M.x86.R_EDI;
|
|
}
|
|
} else {
|
|
/* 16-bit addressing */
|
|
switch (rm) {
|
|
case 0:
|
|
DECODE_PRINTF("[BX+SI]");
|
|
return (M.x86.R_BX + M.x86.R_SI) & 0xffff;
|
|
case 1:
|
|
DECODE_PRINTF("[BX+DI]");
|
|
return (M.x86.R_BX + M.x86.R_DI) & 0xffff;
|
|
case 2:
|
|
DECODE_PRINTF("[BP+SI]");
|
|
M.x86.mode |= SYSMODE_SEG_DS_SS;
|
|
return (M.x86.R_BP + M.x86.R_SI) & 0xffff;
|
|
case 3:
|
|
DECODE_PRINTF("[BP+DI]");
|
|
M.x86.mode |= SYSMODE_SEG_DS_SS;
|
|
return (M.x86.R_BP + M.x86.R_DI) & 0xffff;
|
|
case 4:
|
|
DECODE_PRINTF("[SI]");
|
|
return M.x86.R_SI;
|
|
case 5:
|
|
DECODE_PRINTF("[DI]");
|
|
return M.x86.R_DI;
|
|
case 6:
|
|
offset = fetch_word_imm();
|
|
DECODE_PRINTF2("[%04x]", offset);
|
|
return offset;
|
|
case 7:
|
|
DECODE_PRINTF("[BX]");
|
|
return M.x86.R_BX;
|
|
}
|
|
}
|
|
HALT_SYS();
|
|
return 0;
|
|
}
|
|
|
|
/****************************************************************************
|
|
PARAMETERS:
|
|
rm - RM value to decode
|
|
|
|
RETURNS:
|
|
Offset in memory for the address decoding
|
|
|
|
REMARKS:
|
|
Return the offset given by mod=01 addressing. Also enables the
|
|
decoding of instructions.
|
|
****************************************************************************/
|
|
unsigned decode_rm01_address(
|
|
int rm)
|
|
{
|
|
int displacement;
|
|
|
|
if (M.x86.mode & SYSMODE_PREFIX_ADDR) {
|
|
/* 32-bit addressing */
|
|
if (rm != 4)
|
|
displacement = (s8)fetch_byte_imm();
|
|
else
|
|
displacement = 0;
|
|
|
|
switch (rm) {
|
|
case 0:
|
|
DECODE_PRINTF2("%d[EAX]", displacement);
|
|
return M.x86.R_EAX + displacement;
|
|
case 1:
|
|
DECODE_PRINTF2("%d[ECX]", displacement);
|
|
return M.x86.R_ECX + displacement;
|
|
case 2:
|
|
DECODE_PRINTF2("%d[EDX]", displacement);
|
|
return M.x86.R_EDX + displacement;
|
|
case 3:
|
|
DECODE_PRINTF2("%d[EBX]", displacement);
|
|
return M.x86.R_EBX + displacement;
|
|
case 4: {
|
|
int offset = decode_sib_address(1);
|
|
displacement = (s8)fetch_byte_imm();
|
|
DECODE_PRINTF2("[%d]", displacement);
|
|
return offset + displacement;
|
|
}
|
|
case 5:
|
|
DECODE_PRINTF2("%d[EBP]", displacement);
|
|
return M.x86.R_EBP + displacement;
|
|
case 6:
|
|
DECODE_PRINTF2("%d[ESI]", displacement);
|
|
return M.x86.R_ESI + displacement;
|
|
case 7:
|
|
DECODE_PRINTF2("%d[EDI]", displacement);
|
|
return M.x86.R_EDI + displacement;
|
|
}
|
|
} else {
|
|
/* 16-bit addressing */
|
|
displacement = (s8)fetch_byte_imm();
|
|
switch (rm) {
|
|
case 0:
|
|
DECODE_PRINTF2("%d[BX+SI]", displacement);
|
|
return (M.x86.R_BX + M.x86.R_SI + displacement) & 0xffff;
|
|
case 1:
|
|
DECODE_PRINTF2("%d[BX+DI]", displacement);
|
|
return (M.x86.R_BX + M.x86.R_DI + displacement) & 0xffff;
|
|
case 2:
|
|
DECODE_PRINTF2("%d[BP+SI]", displacement);
|
|
M.x86.mode |= SYSMODE_SEG_DS_SS;
|
|
return (M.x86.R_BP + M.x86.R_SI + displacement) & 0xffff;
|
|
case 3:
|
|
DECODE_PRINTF2("%d[BP+DI]", displacement);
|
|
M.x86.mode |= SYSMODE_SEG_DS_SS;
|
|
return (M.x86.R_BP + M.x86.R_DI + displacement) & 0xffff;
|
|
case 4:
|
|
DECODE_PRINTF2("%d[SI]", displacement);
|
|
return (M.x86.R_SI + displacement) & 0xffff;
|
|
case 5:
|
|
DECODE_PRINTF2("%d[DI]", displacement);
|
|
return (M.x86.R_DI + displacement) & 0xffff;
|
|
case 6:
|
|
DECODE_PRINTF2("%d[BP]", displacement);
|
|
M.x86.mode |= SYSMODE_SEG_DS_SS;
|
|
return (M.x86.R_BP + displacement) & 0xffff;
|
|
case 7:
|
|
DECODE_PRINTF2("%d[BX]", displacement);
|
|
return (M.x86.R_BX + displacement) & 0xffff;
|
|
}
|
|
}
|
|
HALT_SYS();
|
|
return 0; /* SHOULD NOT HAPPEN */
|
|
}
|
|
|
|
/****************************************************************************
|
|
PARAMETERS:
|
|
rm - RM value to decode
|
|
|
|
RETURNS:
|
|
Offset in memory for the address decoding
|
|
|
|
REMARKS:
|
|
Return the offset given by mod=10 addressing. Also enables the
|
|
decoding of instructions.
|
|
****************************************************************************/
|
|
unsigned decode_rm10_address(
|
|
int rm)
|
|
{
|
|
if (M.x86.mode & SYSMODE_PREFIX_ADDR) {
|
|
int displacement;
|
|
|
|
/* 32-bit addressing */
|
|
if (rm != 4)
|
|
displacement = (s32)fetch_long_imm();
|
|
else
|
|
displacement = 0;
|
|
|
|
switch (rm) {
|
|
case 0:
|
|
DECODE_PRINTF2("%d[EAX]", displacement);
|
|
return M.x86.R_EAX + displacement;
|
|
case 1:
|
|
DECODE_PRINTF2("%d[ECX]", displacement);
|
|
return M.x86.R_ECX + displacement;
|
|
case 2:
|
|
DECODE_PRINTF2("%d[EDX]", displacement);
|
|
return M.x86.R_EDX + displacement;
|
|
case 3:
|
|
DECODE_PRINTF2("%d[EBX]", displacement);
|
|
return M.x86.R_EBX + displacement;
|
|
case 4: {
|
|
int offset = decode_sib_address(2);
|
|
displacement = (s32)fetch_long_imm();
|
|
DECODE_PRINTF2("[%d]", displacement);
|
|
return offset + displacement;
|
|
}
|
|
case 5:
|
|
DECODE_PRINTF2("%d[EBP]", displacement);
|
|
return M.x86.R_EBP + displacement;
|
|
case 6:
|
|
DECODE_PRINTF2("%d[ESI]", displacement);
|
|
return M.x86.R_ESI + displacement;
|
|
case 7:
|
|
DECODE_PRINTF2("%d[EDI]", displacement);
|
|
return M.x86.R_EDI + displacement;
|
|
}
|
|
} else {
|
|
int displacement = (s16)fetch_word_imm();
|
|
|
|
/* 16-bit addressing */
|
|
switch (rm) {
|
|
case 0:
|
|
DECODE_PRINTF2("%d[BX+SI]", displacement);
|
|
return (M.x86.R_BX + M.x86.R_SI + displacement) & 0xffff;
|
|
case 1:
|
|
DECODE_PRINTF2("%d[BX+DI]", displacement);
|
|
return (M.x86.R_BX + M.x86.R_DI + displacement) & 0xffff;
|
|
case 2:
|
|
DECODE_PRINTF2("%d[BP+SI]", displacement);
|
|
M.x86.mode |= SYSMODE_SEG_DS_SS;
|
|
return (M.x86.R_BP + M.x86.R_SI + displacement) & 0xffff;
|
|
case 3:
|
|
DECODE_PRINTF2("%d[BP+DI]", displacement);
|
|
M.x86.mode |= SYSMODE_SEG_DS_SS;
|
|
return (M.x86.R_BP + M.x86.R_DI + displacement) & 0xffff;
|
|
case 4:
|
|
DECODE_PRINTF2("%d[SI]", displacement);
|
|
return (M.x86.R_SI + displacement) & 0xffff;
|
|
case 5:
|
|
DECODE_PRINTF2("%d[DI]", displacement);
|
|
return (M.x86.R_DI + displacement) & 0xffff;
|
|
case 6:
|
|
DECODE_PRINTF2("%d[BP]", displacement);
|
|
M.x86.mode |= SYSMODE_SEG_DS_SS;
|
|
return (M.x86.R_BP + displacement) & 0xffff;
|
|
case 7:
|
|
DECODE_PRINTF2("%d[BX]", displacement);
|
|
return (M.x86.R_BX + displacement) & 0xffff;
|
|
}
|
|
}
|
|
HALT_SYS();
|
|
return 0; /* SHOULD NOT HAPPEN */
|
|
}
|
|
|
|
|
|
/****************************************************************************
|
|
PARAMETERS:
|
|
mod - modifier
|
|
rm - RM value to decode
|
|
|
|
RETURNS:
|
|
Offset in memory for the address decoding, multiplexing calls to
|
|
the decode_rmXX_address functions
|
|
|
|
REMARKS:
|
|
Return the offset given by "mod" addressing.
|
|
****************************************************************************/
|
|
|
|
unsigned decode_rmXX_address(int mod, int rm)
|
|
{
|
|
if(mod == 0)
|
|
return decode_rm00_address(rm);
|
|
if(mod == 1)
|
|
return decode_rm01_address(rm);
|
|
return decode_rm10_address(rm);
|
|
}
|
|
|
|
|
|
|