[PATCH] x86emu: Correctly handle 0x66 prefix for some instructions

Julian Pidancet julian.pidancet at gmail.com
Mon Mar 5 07:47:06 PST 2012


Some instructions are not emulated correctly by x86emu when they
are prefixed by the 0x66 opcode.
I've identified problems in the emulation of these intructions: ret,
enter, leave, iret and some forms of call.

Most of the time, the problem is that these instructions should push or
pop 32-bit values to/from the stack, instead of 16bit, when they are
prefixed by the 0x66 special opcode.

The SeaBIOS project aims to produce a complete legacy BIOS
implementation as well as a VGA option ROM, entirely written in C and
using the GCC compiler.

In 16bit code produced by the GCC compiler, the 0x66 prefix is used
almost everywhere. This patch is necessary to allow the SeaBIOS VGA
option ROM to function with Xorg when using the vesa driver.

Signed-off-by: Julian Pidancet <julian.pidancet at gmail.com>
---
 hw/xfree86/x86emu/ops.c |  194 ++++++++++++++++++++++++++++++++++------------
 1 files changed, 143 insertions(+), 51 deletions(-)

diff --git a/hw/xfree86/x86emu/ops.c b/hw/xfree86/x86emu/ops.c
index 5d3cac1..440b8dc 100644
--- a/hw/xfree86/x86emu/ops.c
+++ b/hw/xfree86/x86emu/ops.c
@@ -8487,7 +8487,11 @@ static void x86emuOp_ret_near_IMM(u8 X86EMU_UNUSED(op1))
     DECODE_PRINTF2("%x\n", imm);
 	RETURN_TRACE("RET",M.x86.saved_cs,M.x86.saved_ip);
 	TRACE_AND_STEP();
-    M.x86.R_IP = pop_word();
+    if (M.x86.mode & SYSMODE_PREFIX_DATA) {
+        M.x86.R_EIP = pop_long();
+    } else {
+        M.x86.R_IP = pop_word();
+    }
     M.x86.R_SP += imm;
     DECODE_CLEAR_SEGOVR();
     END_OF_INSTR();
@@ -8503,7 +8507,11 @@ static void x86emuOp_ret_near(u8 X86EMU_UNUSED(op1))
     DECODE_PRINTF("RET\n");
 	RETURN_TRACE("RET",M.x86.saved_cs,M.x86.saved_ip);
 	TRACE_AND_STEP();
-    M.x86.R_IP = pop_word();
+    if (M.x86.mode & SYSMODE_PREFIX_DATA) {
+        M.x86.R_EIP = pop_long();
+    } else {
+        M.x86.R_IP = pop_word();
+    }
     DECODE_CLEAR_SEGOVR();
     END_OF_INSTR();
 }
@@ -8787,11 +8795,16 @@ static void x86emuOp_enter(u8 X86EMU_UNUSED(op1))
     frame_pointer = M.x86.R_SP;
     if (nesting > 0) {
         for (i = 1; i < nesting; i++) {
-            M.x86.R_BP -= 2;
-            push_word(fetch_data_word_abs(M.x86.R_SS, M.x86.R_BP));
+            if (M.x86.mode & SYSMODE_PREFIX_DATA) {
+                M.x86.R_EBP -= 4;
+                push_long(fetch_data_long_abs(M.x86.R_SS, M.x86.R_BP));
+            } else {
+                M.x86.R_BP -= 2;
+                push_word(fetch_data_word_abs(M.x86.R_SS, M.x86.R_BP));
             }
-        push_word(frame_pointer);
         }
+        push_word(frame_pointer);
+    }
     M.x86.R_BP = frame_pointer;
     M.x86.R_SP = (u16)(M.x86.R_SP - local);
     DECODE_CLEAR_SEGOVR();
@@ -8808,7 +8821,11 @@ static void x86emuOp_leave(u8 X86EMU_UNUSED(op1))
     DECODE_PRINTF("LEAVE\n");
     TRACE_AND_STEP();
     M.x86.R_SP = M.x86.R_BP;
-    M.x86.R_BP = pop_word();
+    if (M.x86.mode & SYSMODE_PREFIX_DATA) {
+        M.x86.R_EBP = pop_long();
+    } else {
+        M.x86.R_BP = pop_word();
+    }
     DECODE_CLEAR_SEGOVR();
     END_OF_INSTR();
 }
@@ -8827,8 +8844,13 @@ static void x86emuOp_ret_far_IMM(u8 X86EMU_UNUSED(op1))
     DECODE_PRINTF2("%x\n", imm);
 	RETURN_TRACE("RETF",M.x86.saved_cs,M.x86.saved_ip);
 	TRACE_AND_STEP();
-    M.x86.R_IP = pop_word();
-    M.x86.R_CS = pop_word();
+    if (M.x86.mode & SYSMODE_PREFIX_DATA) {
+        M.x86.R_IP = pop_long();
+        M.x86.R_CS = pop_long() & 0xffff;
+    } else {
+        M.x86.R_IP = pop_word();
+        M.x86.R_CS = pop_word();
+    }
     M.x86.R_SP += imm;
     DECODE_CLEAR_SEGOVR();
     END_OF_INSTR();
@@ -8844,8 +8866,13 @@ static void x86emuOp_ret_far(u8 X86EMU_UNUSED(op1))
     DECODE_PRINTF("RETF\n");
 	RETURN_TRACE("RETF",M.x86.saved_cs,M.x86.saved_ip);
 	TRACE_AND_STEP();
-    M.x86.R_IP = pop_word();
-    M.x86.R_CS = pop_word();
+    if (M.x86.mode & SYSMODE_PREFIX_DATA) {
+        M.x86.R_IP = pop_long();
+        M.x86.R_CS = pop_long() & 0xffff;
+    } else {
+        M.x86.R_IP = pop_word();
+        M.x86.R_CS = pop_word();
+    }
     DECODE_CLEAR_SEGOVR();
     END_OF_INSTR();
 }
@@ -8939,9 +8966,15 @@ static void x86emuOp_iret(u8 X86EMU_UNUSED(op1))
 
     TRACE_AND_STEP();
 
-    M.x86.R_IP = pop_word();
-    M.x86.R_CS = pop_word();
-    M.x86.R_FLG = pop_word();
+    if (M.x86.mode & SYSMODE_PREFIX_DATA) {
+        M.x86.R_IP = pop_long();
+        M.x86.R_CS = pop_long() & 0xffff;
+        M.x86.R_FLG = pop_long();
+    } else {
+        M.x86.R_IP = pop_word();
+        M.x86.R_CS = pop_word();
+        M.x86.R_FLG = pop_word();
+    }
     DECODE_CLEAR_SEGOVR();
     END_OF_INSTR();
 }
@@ -11168,19 +11201,36 @@ static void x86emuOp_opcFF_word_RM(u8 X86EMU_UNUSED(op1))
             }
             break;
         case 2:         /* call word ptr ... */
-            destval = fetch_data_word(destoffset);
-            TRACE_AND_STEP();
-            push_word(M.x86.R_IP);
-            M.x86.R_IP = destval;
+            if (M.x86.mode & SYSMODE_PREFIX_DATA) {
+                destval = fetch_data_long(destoffset);
+                TRACE_AND_STEP();
+                push_long(M.x86.R_EIP);
+                M.x86.R_EIP = destval;
+            } else {
+                destval = fetch_data_word(destoffset);
+                TRACE_AND_STEP();
+                push_word(M.x86.R_IP);
+                M.x86.R_IP = destval;
+            }
             break;
         case 3:         /* call far ptr ... */
-            destval = fetch_data_word(destoffset);
-            destval2 = fetch_data_word(destoffset + 2);
-            TRACE_AND_STEP();
-            push_word(M.x86.R_CS);
-            M.x86.R_CS = destval2;
-            push_word(M.x86.R_IP);
-            M.x86.R_IP = destval;
+            if (M.x86.mode & SYSMODE_PREFIX_DATA) {
+                destval = fetch_data_long(destoffset);
+                destval2 = fetch_data_word(destoffset + 4);
+                TRACE_AND_STEP();
+                push_long(M.x86.R_CS);
+                M.x86.R_CS = destval2;
+                push_long(M.x86.R_EIP);
+                M.x86.R_EIP = destval;
+            } else {
+                destval = fetch_data_word(destoffset);
+                destval2 = fetch_data_word(destoffset + 2);
+                TRACE_AND_STEP();
+                push_word(M.x86.R_CS);
+                M.x86.R_CS = destval2;
+                push_word(M.x86.R_IP);
+                M.x86.R_IP = destval;
+            }
             break;
         case 4:         /* jmp word ptr ... */
             destval = fetch_data_word(destoffset);
@@ -11250,19 +11300,36 @@ static void x86emuOp_opcFF_word_RM(u8 X86EMU_UNUSED(op1))
             }
             break;
         case 2:         /* call word ptr ... */
-            destval = fetch_data_word(destoffset);
-            TRACE_AND_STEP();
-            push_word(M.x86.R_IP);
-            M.x86.R_IP = destval;
+            if (M.x86.mode & SYSMODE_PREFIX_DATA) {
+                destval = fetch_data_long(destoffset);
+                TRACE_AND_STEP();
+                push_long(M.x86.R_EIP);
+                M.x86.R_EIP = destval;
+            } else {
+                destval = fetch_data_word(destoffset);
+                TRACE_AND_STEP();
+                push_word(M.x86.R_IP);
+                M.x86.R_IP = destval;
+            }
             break;
         case 3:         /* call far ptr ... */
-            destval = fetch_data_word(destoffset);
-            destval2 = fetch_data_word(destoffset + 2);
-            TRACE_AND_STEP();
-            push_word(M.x86.R_CS);
-            M.x86.R_CS = destval2;
-            push_word(M.x86.R_IP);
-            M.x86.R_IP = destval;
+            if (M.x86.mode & SYSMODE_PREFIX_DATA) {
+                destval = fetch_data_long(destoffset);
+                destval2 = fetch_data_word(destoffset + 4);
+                TRACE_AND_STEP();
+                push_long(M.x86.R_CS);
+                M.x86.R_CS = destval2;
+                push_long(M.x86.R_EIP);
+                M.x86.R_EIP = destval;
+            } else {
+                destval = fetch_data_word(destoffset);
+                destval2 = fetch_data_word(destoffset + 2);
+                TRACE_AND_STEP();
+                push_word(M.x86.R_CS);
+                M.x86.R_CS = destval2;
+                push_word(M.x86.R_IP);
+                M.x86.R_IP = destval;
+            }
             break;
         case 4:         /* jmp word ptr ... */
             destval = fetch_data_word(destoffset);
@@ -11332,19 +11399,36 @@ static void x86emuOp_opcFF_word_RM(u8 X86EMU_UNUSED(op1))
             }
             break;
         case 2:         /* call word ptr ... */
-            destval = fetch_data_word(destoffset);
-            TRACE_AND_STEP();
-            push_word(M.x86.R_IP);
-            M.x86.R_IP = destval;
+            if (M.x86.mode & SYSMODE_PREFIX_DATA) {
+                destval = fetch_data_long(destoffset);
+                TRACE_AND_STEP();
+                push_long(M.x86.R_EIP);
+                M.x86.R_EIP = destval;
+            } else {
+                destval = fetch_data_word(destoffset);
+                TRACE_AND_STEP();
+                push_word(M.x86.R_IP);
+                M.x86.R_IP = destval;
+            }
             break;
         case 3:         /* call far ptr ... */
-            destval = fetch_data_word(destoffset);
-            destval2 = fetch_data_word(destoffset + 2);
-            TRACE_AND_STEP();
-            push_word(M.x86.R_CS);
-            M.x86.R_CS = destval2;
-            push_word(M.x86.R_IP);
-            M.x86.R_IP = destval;
+            if (M.x86.mode & SYSMODE_PREFIX_DATA) {
+                destval = fetch_data_long(destoffset);
+                destval2 = fetch_data_word(destoffset + 4);
+                TRACE_AND_STEP();
+                push_long(M.x86.R_CS);
+                M.x86.R_CS = destval2;
+                push_long(M.x86.R_EIP);
+                M.x86.R_EIP = destval;
+            } else {
+                destval = fetch_data_word(destoffset);
+                destval2 = fetch_data_word(destoffset + 2);
+                TRACE_AND_STEP();
+                push_word(M.x86.R_CS);
+                M.x86.R_CS = destval2;
+                push_word(M.x86.R_IP);
+                M.x86.R_IP = destval;
+            }
             break;
         case 4:         /* jmp word ptr ... */
             destval = fetch_data_word(destoffset);
@@ -11412,11 +11496,19 @@ static void x86emuOp_opcFF_word_RM(u8 X86EMU_UNUSED(op1))
             }
             break;
         case 2:         /* call word ptr ... */
-            destreg = DECODE_RM_WORD_REGISTER(rl);
-            DECODE_PRINTF("\n");
-            TRACE_AND_STEP();
-            push_word(M.x86.R_IP);
-            M.x86.R_IP = *destreg;
+            if (M.x86.mode & SYSMODE_PREFIX_DATA) {
+                destreg = DECODE_RM_LONG_REGISTER(rl);
+                DECODE_PRINTF("\n");
+                TRACE_AND_STEP();
+                push_long(M.x86.R_EIP);
+                M.x86.R_EIP = *destreg;
+            } else {
+                destreg = DECODE_RM_WORD_REGISTER(rl);
+                DECODE_PRINTF("\n");
+                TRACE_AND_STEP();
+                push_word(M.x86.R_IP);
+                M.x86.R_IP = *destreg;
+            }
             break;
         case 3:         /* jmp far ptr ... */
             DECODE_PRINTF("OPERATION UNDEFINED 0XFF \n");
-- 
Julian Pidancet




More information about the xorg mailing list