| /**************************************************************************** |
| * |
| * 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 <common.h> |
| #include "x86emu/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) |
| { |
| 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 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_X86EMU_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_X86EMU_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_X86EMU_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_X86EMU_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_X86EMU_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_X86EMU_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_X86EMU_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_X86EMU_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_X86EMU_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_X86EMU_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_X86EMU_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_X86EMU_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); |
| } |