/[gxemul]/trunk/src/cpu_x86.c
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Diff of /trunk/src/cpu_x86.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 6 by dpavlin, Mon Oct 8 16:18:11 2007 UTC revision 12 by dpavlin, Mon Oct 8 16:18:38 2007 UTC
# Line 25  Line 25 
25   *  SUCH DAMAGE.   *  SUCH DAMAGE.
26   *   *
27   *   *
28   *  $Id: cpu_x86.c,v 1.161 2005/05/31 06:20:38 debug Exp $   *  $Id: cpu_x86.c,v 1.169 2005/08/14 23:44:22 debug Exp $
29   *   *
30   *  x86 (and amd64) CPU emulation.   *  x86 (and amd64) CPU emulation.
31   *   *
# Line 69  int x86_cpu_family_init(struct cpu_famil Line 69  int x86_cpu_family_init(struct cpu_famil
69  #include "memory.h"  #include "memory.h"
70  #include "symbol.h"  #include "symbol.h"
71    
72    #define DYNTRANS_DUALMODE_32
73  extern volatile int single_step;  #define DYNTRANS_32
74  extern int old_show_trace_tree;    #define DYNTRANS_VARIABLE_INSTRUCTION_LENGTH
75  extern int old_instruction_trace;  #include "tmp_x86_head.c"
 extern int old_quiet_mode;  
 extern int quiet_mode;  
76    
77    
78  static struct x86_model models[] = x86_models;  static struct x86_model models[] = x86_models;
# Line 92  static char *cond_names[N_X86_CONDS] = x Line 90  static char *cond_names[N_X86_CONDS] = x
90   *   *
91   *  Create a new x86 cpu object.   *  Create a new x86 cpu object.
92   */   */
93  struct cpu *x86_cpu_new(struct memory *mem, struct machine *machine,  int x86_cpu_new(struct cpu *cpu, struct memory *mem, struct machine *machine,
94          int cpu_id, char *cpu_type_name)          int cpu_id, char *cpu_type_name)
95  {  {
96          int i = 0;          int i = 0;
         struct cpu *cpu;  
   
         if (cpu_type_name == NULL)  
                 return NULL;  
97    
98          /*  Try to find a match:  */          /*  Try to find a match:  */
99          while (models[i].model_number != 0) {          while (models[i].model_number != 0) {
# Line 109  struct cpu *x86_cpu_new(struct memory *m Line 103  struct cpu *x86_cpu_new(struct memory *m
103          }          }
104    
105          if (models[i].name == NULL)          if (models[i].name == NULL)
106                  return NULL;                  return 0;
   
         cpu = malloc(sizeof(struct cpu));  
         if (cpu == NULL) {  
                 fprintf(stderr, "out of memory\n");  
                 exit(1);  
         }  
107    
108          memset(cpu, 0, sizeof(struct cpu));          cpu->memory_rw  = x86_memory_rw;
109          cpu->memory_rw          = x86_memory_rw;          cpu->byte_order = EMUL_LITTLE_ENDIAN;
         cpu->name               = cpu_type_name;  
         cpu->mem                = mem;  
         cpu->machine            = machine;  
         cpu->cpu_id             = cpu_id;  
         cpu->byte_order         = EMUL_LITTLE_ENDIAN;  
         cpu->bootstrap_cpu_flag = 0;  
         cpu->running            = 0;  
110    
111          cpu->cd.x86.model = models[i];          cpu->cd.x86.model = models[i];
112    
# Line 158  struct cpu *x86_cpu_new(struct memory *m Line 139  struct cpu *x86_cpu_new(struct memory *m
139                  debug("%s", cpu->name);                  debug("%s", cpu->name);
140          }          }
141    
142          return cpu;          return 1;
143  }  }
144    
145    
# Line 475  void x86_cpu_register_match(struct machi Line 456  void x86_cpu_register_match(struct machi
456  }  }
457    
458    
459    /*
460     *  x86_cpu_show_full_statistics():
461     *
462     *  Show detailed statistics on opcode usage on each cpu.
463     */
464    void x86_cpu_show_full_statistics(struct machine *m)
465    {
466            fatal("x86_cpu_show_full_statistics(): TODO\n");
467    }
468    
469    
470    /*
471     *  x86_cpu_tlbdump():
472     *
473     *  Called from the debugger to dump the TLB in a readable format.
474     *  x is the cpu number to dump, or -1 to dump all CPUs.
475     *
476     *  If rawflag is nonzero, then the TLB contents isn't formated nicely,
477     *  just dumped.
478     */
479    void x86_cpu_tlbdump(struct machine *m, int x, int rawflag)
480    {
481            fatal("ppc_cpu_tlbdump(): TODO\n");
482    }
483    
484    
485  /*  Macro which modifies the lower part of a value, or the entire value,  /*  Macro which modifies the lower part of a value, or the entire value,
486      depending on 'mode':  */      depending on 'mode':  */
487  #define modify(old,new) (                                       \  #define modify(old,new) (                                       \
# Line 1350  static int modrm(struct cpu *cpu, int wr Line 1357  static int modrm(struct cpu *cpu, int wr
1357          case 3:          case 3:
1358                  if (flags & MODRM_EIGHTBIT) {                  if (flags & MODRM_EIGHTBIT) {
1359                          if (disasm) {                          if (disasm) {
1360                                  strcpy(modrm_rm, reg_names_bytes[rm]);                                  strlcpy(modrm_rm, reg_names_bytes[rm],
1361                                        sizeof(modrm_rm));
1362                          } else {                          } else {
1363                                  switch (writeflag) {                                  switch (writeflag) {
1364                                  case MODRM_WRITE_RM:                                  case MODRM_WRITE_RM:
# Line 1375  static int modrm(struct cpu *cpu, int wr Line 1383  static int modrm(struct cpu *cpu, int wr
1383                  } else {                  } else {
1384                          if (disasm) {                          if (disasm) {
1385                                  if (mode == 16 || flags & MODRM_RM_16BIT)                                  if (mode == 16 || flags & MODRM_RM_16BIT)
1386                                          strcpy(modrm_rm, reg_names[rm]);                                          strlcpy(modrm_rm, reg_names[rm],
1387                                                sizeof(modrm_rm));
1388                                  else                                  else
1389                                          sprintf(modrm_rm, "%s%s", e,                                          sprintf(modrm_rm, "%s%s", e,
1390                                              reg_names[rm]);                                              reg_names[rm]);
# Line 1415  static int modrm(struct cpu *cpu, int wr Line 1424  static int modrm(struct cpu *cpu, int wr
1424    
1425          if (flags & MODRM_EIGHTBIT && !(flags & MODRM_R_NONEIGHTBIT)) {          if (flags & MODRM_EIGHTBIT && !(flags & MODRM_R_NONEIGHTBIT)) {
1426                  if (disasm) {                  if (disasm) {
1427                          strcpy(modrm_r, reg_names_bytes[r]);                          strlcpy(modrm_r, reg_names_bytes[r],
1428                                sizeof(modrm_r));
1429                  } else {                  } else {
1430                          switch (writeflag) {                          switch (writeflag) {
1431                          case MODRM_WRITE_R:                          case MODRM_WRITE_R:
# Line 1437  static int modrm(struct cpu *cpu, int wr Line 1447  static int modrm(struct cpu *cpu, int wr
1447          } else {          } else {
1448                  if (disasm) {                  if (disasm) {
1449                          if (flags & MODRM_SEG)                          if (flags & MODRM_SEG)
1450                                  strcpy(modrm_r, seg_names[r]);                                  strlcpy(modrm_r, seg_names[r],
1451                                        sizeof(modrm_r));
1452                          else if (flags & MODRM_CR)                          else if (flags & MODRM_CR)
1453                                  sprintf(modrm_r, "cr%i", r);                                  sprintf(modrm_r, "cr%i", r);
1454                          else if (flags & MODRM_DR)                          else if (flags & MODRM_DR)
# Line 1447  static int modrm(struct cpu *cpu, int wr Line 1458  static int modrm(struct cpu *cpu, int wr
1458                                          sprintf(modrm_r, "%s%s", e,                                          sprintf(modrm_r, "%s%s", e,
1459                                              reg_names[r]);                                              reg_names[r]);
1460                                  else                                  else
1461                                          strcpy(modrm_r, reg_names[r]);                                          strlcpy(modrm_r, reg_names[r],
1462                                                sizeof(modrm_r));
1463                          }                          }
1464                  } else {                  } else {
1465                          switch (writeflag) {                          switch (writeflag) {
# Line 2431  int x86_cpu_disassemble_instr(struct cpu Line 2443  int x86_cpu_disassemble_instr(struct cpu
2443  }  }
2444    
2445    
2446    
2447  /*  /*
2448   *  x86_cpuid():   *  x86_cpuid():
2449   *   *
# Line 2510  static void x86_cpuid(struct cpu *cpu) Line 2523  static void x86_cpuid(struct cpu *cpu)
2523  }  }
2524    
2525    
 #define TRANSLATE_ADDRESS       translate_address_x86  
 #include "memory_x86.c"  
 #undef TRANSLATE_ADDRESS  
   
   
 #define MEMORY_RW       x86_memory_rw  
 #define MEM_X86  
 #include "memory_rw.c"  
 #undef MEM_X86  
 #undef MEMORY_RW  
   
   
2526  /*  /*
2527   *  x86_push():   *  x86_push():
2528   */   */
# Line 3144  cpu->machine->md.pc.pic2->irr, cpu->mach Line 3145  cpu->machine->md.pc.pic2->irr, cpu->mach
3145  }  }
3146    
3147    
3148  /*  #define TRANSLATE_ADDRESS       translate_address_x86
3149   *  x86_cpu_run_instr():  #include "memory_x86.c"
3150   *  #undef TRANSLATE_ADDRESS
  *  Execute one instruction on a specific CPU.  
  *  
  *  Return value is the number of instructions executed during this call,    
  *  0 if no instruction was executed.  
  */  
 int x86_cpu_run_instr(struct emul *emul, struct cpu *cpu)  
 {  
         int i, r, rep = 0, op, len, mode, omode, mode67;  
         int nprefixbytes = 0, success, longmode;  
         uint32_t imm, imm2;  
         unsigned char buf[16];  
         unsigned char *instr = buf, *instr_orig, *really_orig_instr;  
         uint64_t newpc = cpu->pc;  
         uint64_t tmp, op1, op2;  
         int trap_flag_was_set = cpu->cd.x86.rflags & X86_FLAGS_TF;  
   
         /*  Check PC against breakpoints:  */  
         if (!single_step)  
                 for (i=0; i<cpu->machine->n_breakpoints; i++)  
                         if (cpu->pc == cpu->machine->breakpoint_addr[i]) {  
                                 fatal("Breakpoint reached, 0x%04x:0x%llx\n",  
                                     cpu->cd.x86.s[X86_S_CS],  
                                    (long long)cpu->pc);  
                                 single_step = 1;  
                                 return 0;  
                         }  
   
         if (!cpu->cd.x86.descr_cache[X86_S_CS].valid) {  
                 fatal("x86_cpu_run_instr(): Invalid CS descriptor?\n");  
                 cpu->running = 0;  
                 return 0;  
         }  
   
         longmode = cpu->cd.x86.efer & X86_EFER_LME;  
         mode = cpu->cd.x86.descr_cache[X86_S_CS].default_op_size;  
         omode = mode;  
         if (mode != 16 && mode != 32) {  
                 fatal("x86_cpu_run_instr(): Invalid CS default op size, %i\n",  
                     mode);  
                 cpu->running = 0;  
                 return 0;  
         }  
   
         if (cpu->cd.x86.interrupt_asserted &&  
             cpu->cd.x86.rflags & X86_FLAGS_IF) {  
                 if (cause_interrupt(cpu))  
                         return 0;  
         }  
   
         /*  16-bit BIOS emulation:  */  
         if (mode == 16 && ((newpc + (cpu->cd.x86.s[X86_S_CS] << 4)) & 0xff000)  
             == 0xf8000 && cpu->machine->prom_emulation) {  
                 int addr = (newpc + (cpu->cd.x86.s[X86_S_CS] << 4)) & 0xfff;  
                 if (cpu->machine->instruction_trace)  
                         debug("(PC BIOS emulation, int 0x%02x)\n",  
                             addr >> 4);  
                 pc_bios_emul(cpu);  
                 /*  Approximately equivalent to 500 instructions.  */  
                 return 500;  
         }  
   
         if (cpu->cd.x86.halted) {  
                 if (!(cpu->cd.x86.rflags & X86_FLAGS_IF)) {  
                         fatal("[ Halting with interrupts disabled. ]\n");  
                         cpu->running = 0;  
                 }  
                 /*  Treating this as more than one instruction makes us  
                     wait less for devices.  */  
                 return 1000;  
         }  
   
         /*  Read an instruction from memory:  */  
         cpu->cd.x86.cursegment = X86_S_CS;  
         cpu->cd.x86.seg_override = 0;  
   
         r = cpu->memory_rw(cpu, cpu->mem, cpu->pc, &buf[0], sizeof(buf),  
             MEM_READ, CACHE_INSTRUCTION);  
         if (!r) {  
                 /*  This could happen if, for example, there was an  
                     exception while we tried to read the instruction.  */  
                 return 0;  
         }  
   
         really_orig_instr = instr;      /*  Used to display an error message  
                                             for unimplemented instructions.  */  
   
         if (cpu->machine->instruction_trace)  
                 x86_cpu_disassemble_instr(cpu, instr, 1 | omode, 0, 0);  
   
         /*  For debugging:  */  
         if (instr[0] == 0 && instr[1] == 0 && instr[2] == 0 && instr[3] == 0) {  
                 fatal("WARNING: Running in nothingness?\n");  
                 cpu->running = 0;  
                 return 0;  
         }  
   
         /*  All instructions are at least one byte long :-)  */  
         newpc ++;  
   
         /*  Default is to use the data segment, or the stack segment:  */  
         cpu->cd.x86.cursegment = X86_S_DS;  
         mode67 = mode;  
   
         /*  Any prefix?  */  
         for (;;) {  
                 if (longmode && (instr[0] & 0xf0) == 0x40) {  
                         fatal("TODO: REX byte 0x%02x\n", instr[0]);  
                         cpu->running = 0;  
                 } else if (instr[0] == 0x66) {  
                         if (mode == 16)  
                                 mode = 32;  
                         else  
                                 mode = 16;  
                 } else if (instr[0] == 0x67) {  
                         if (mode67 == 16)  
                                 mode67 = 32;  
                         else  
                                 mode67 = 16;  
                 } else if (instr[0] == 0x26) {  
                         cpu->cd.x86.cursegment = X86_S_ES;  
                         cpu->cd.x86.seg_override = 1;  
                 } else if (instr[0] == 0x2e) {  
                         cpu->cd.x86.cursegment = X86_S_CS;  
                         cpu->cd.x86.seg_override = 1;  
                 } else if (instr[0] == 0x36) {  
                         cpu->cd.x86.cursegment = X86_S_SS;  
                         cpu->cd.x86.seg_override = 1;  
                 } else if (instr[0] == 0x3e) {  
                         cpu->cd.x86.cursegment = X86_S_DS;  
                         cpu->cd.x86.seg_override = 1;  
                 } else if (instr[0] == 0x64) {  
                         cpu->cd.x86.cursegment = X86_S_FS;  
                         cpu->cd.x86.seg_override = 1;  
                 } else if (instr[0] == 0x65) {  
                         cpu->cd.x86.cursegment = X86_S_GS;  
                         cpu->cd.x86.seg_override = 1;  
                 } else if (instr[0] == 0xf0) {  
                         /*  lock  */  
                 } else if (instr[0] == 0xf2) {  
                         rep = REP_REPNE;  
                 } else if (instr[0] == 0xf3) {  
                         rep = REP_REP;  
                 } else  
                         break;  
                 instr ++;  
                 newpc ++;  
                 if (++nprefixbytes > 4) {  
                         fatal("x86: too many prefix bytes at ");  
                         print_csip(cpu); fatal("\n");  
                         cpu->running = 0;  
                         return 0;  
                 }  
         }  
   
         op = instr[0];  
         instr ++;  
   
         if ((op & 0xf0) <= 0x30 && (op & 7) <= 5) {  
                 success = 1;  
                 instr_orig = instr;  
                 switch (op & 7) {  
                 case 4: imm = read_imm(&instr, &newpc, 8);  
                         op1 = cpu->cd.x86.r[X86_R_AX] & 0xff;  
                         op2 = (signed char)imm;  
                         mode = 8;  
                         break;  
                 case 5: imm = read_imm(&instr, &newpc, mode);  
                         op1 = cpu->cd.x86.r[X86_R_AX]; op2 = imm;  
                         break;  
                 default:success = modrm(cpu, MODRM_READ, mode, mode67,  
                             op&1? 0 : MODRM_EIGHTBIT, &instr, &newpc,&op1,&op2);  
                         if (!success)  
                                 return 0;  
                 }  
   
                 if ((op & 6) == 2) {  
                         uint64_t tmp = op1; op1 = op2; op2 = tmp;  
                 }  
   
                 /*  printf("op1=0x%x op2=0x%x => ", (int)op1, (int)op2);  */  
   
                 switch (mode) {  
                 case 16: op1 &= 0xffff; op2 &= 0xffff; break;  
                 case 32: op1 &= 0xffffffffULL; op2 &= 0xffffffffULL; break;  
                 }  
   
                 switch (op & 0x38) {  
                 case 0x00:      x86_calc_flags(cpu, op1, op2, !(op & 1)? 8 :  
                                     mode, CALCFLAGS_OP_ADD);  
                                 op1 = op1 + op2;  
                                 break;  
                 case 0x08:      op1 = op1 | op2; break;  
                 case 0x10:      tmp = op2;  
                                 if (cpu->cd.x86.rflags & X86_FLAGS_CF)  
                                         tmp ++;  
                                 x86_calc_flags(cpu, op1, tmp, !(op & 1)? 8 :  
                                     mode, CALCFLAGS_OP_ADD);  
                                 op1 = op1 + tmp;  
                                 break;  
                 case 0x18:      tmp = op2;  
                                 if (cpu->cd.x86.rflags & X86_FLAGS_CF)  
                                         tmp ++;  
                                 x86_calc_flags(cpu, op1, tmp, !(op & 1)? 8 :  
                                     mode, CALCFLAGS_OP_SUB);  
                                 op1 = op1 - tmp;  
                                 break;  
                 case 0x20:      op1 = op1 & op2; break;  
                 case 0x28:      x86_calc_flags(cpu, op1, op2, !(op & 1)? 8 :  
                                     mode, CALCFLAGS_OP_SUB);  
                                 op1 = op1 - op2; break;  
                 case 0x30:      op1 = op1 ^ op2; break;  
                 case 0x38:      x86_calc_flags(cpu, op1, op2, !(op & 1)? 8 :  
                                     mode, CALCFLAGS_OP_SUB);  
                                 break;  
                 default:  
                         fatal("not yet\n");  
                         exit(1);  
                 }  
   
                 switch (mode) {  
                 case 16: op1 &= 0xffff; op2 &= 0xffff; break;  
                 case 32: op1 &= 0xffffffffULL; op2 &= 0xffffffffULL; break;  
                 }  
   
                 /*  NOTE: Manual cmp for "sbb, "sub" and "cmp" instructions.  */  
                 if ((op & 0x38) != 0x38 && (op & 0x38) != 0x28 &&  
                     (op & 0x38) != 0x18 && (op & 0x38) != 0x00 &&  
                     (op & 0x38) != 0x10)  
                         x86_calc_flags(cpu, op1, 0, !(op & 1)? 8 : mode,  
                             CALCFLAGS_OP_XOR);  
   
                 /*  "and","or","xor" always clears CF and OF:  */  
                 if ((op & 0x38) == 0x08 || (op & 0x38) == 0x20 ||  
                     (op & 0x38) == 0x30) {  
                         cpu->cd.x86.rflags &= ~X86_FLAGS_CF;  
                         cpu->cd.x86.rflags &= ~X86_FLAGS_OF;  
                 }  
   
                 /*  printf("op1=0x%x op2=0x%x\n", (int)op1, (int)op2);  */  
   
                 if ((op & 6) == 2) {  
                         uint64_t tmp = op1; op1 = op2; op2 = tmp;  
                 }  
   
                 /*  Write back the result: (for all cases except CMP)  */  
                 if ((op & 0x38) != 0x38) {  
                         switch (op & 7) {  
                         case 4: cpu->cd.x86.r[X86_R_AX] = (cpu->cd.x86.r[  
                                     X86_R_AX] & ~0xff) | (op1 & 0xff);  
                                 break;  
                         case 5: cpu->cd.x86.r[X86_R_AX] = modify(cpu->  
                                     cd.x86.r[X86_R_AX], op1);  
                                 break;  
                         default:success = modrm(cpu, (op & 6) == 2?  
                                     MODRM_WRITE_R : MODRM_WRITE_RM, mode,  
                                     mode67, op&1? 0 : MODRM_EIGHTBIT,  
                                     &instr_orig, NULL, &op1, &op2);  
                                 if (!success)  
                                         return 0;  
                         }  
                 }  
         } else if ((op & 0xf0) < 0x20 && (op & 7) == 6) {  
                 success = x86_push(cpu, cpu->cd.x86.s[op / 8], mode);  
                 if (!success)  
                         return 0;  
         } else if (op == 0x0f && cpu->cd.x86.model.model_number ==  
             X86_MODEL_8086) {  
                 uint64_t tmp;  
                 fatal("WARNING: pop cs\n");  
                 if (!x86_pop(cpu, &tmp, mode))  
                         return 0;  
                 reload_segment_descriptor(cpu, X86_S_CS, tmp, &newpc);  
         } else if (op == 0x0f) {  
                 uint64_t tmp;  
                 unsigned char *instr_orig_2;  
                 int signflag, i;  
                 imm = read_imm(&instr, &newpc, 8);  
                 if (imm >= 0x40 && imm <= 0x4f) {       /*  CMOVxx  */  
                         op = imm & 0xf;  
                         if (!modrm(cpu, MODRM_READ, mode, mode67,  
                             0, &instr, &newpc, &op1, &op2))  
                                 return 0;  
                         success = x86_condition(cpu, op);  
                         if (success) {  
                                 if (!modrm(cpu, MODRM_WRITE_R, mode, mode67,  
                                     MODRM_EIGHTBIT, &instr_orig, NULL,  
                                     &op2, &op1))  
                                         return 0;  
                         }  
                 } else if (imm >= 0x80 && imm <= 0x8f) {  
                         /*  conditional near jump  */  
                         op = imm & 0xf;  
                         imm = read_imm(&instr, &newpc, mode);  
                         success = x86_condition(cpu, op);  
                         if (success)  
                                 newpc += imm;  
                 } else if (imm >= 0x90 && imm <= 0x9f) {  
                         instr_orig = instr;  
                         if (!modrm(cpu, MODRM_READ, mode, mode67,  
                             MODRM_EIGHTBIT, &instr, &newpc, &op1, &op2))  
                                 return 0;  
                         op1 = x86_condition(cpu, imm & 0xf);  
                         if (!modrm(cpu, MODRM_WRITE_RM, mode, mode67,  
                             MODRM_EIGHTBIT, &instr_orig, NULL, &op1, &op2))  
                                 return 0;  
                 } else {  
                         int subop;  
                         switch (imm) {  
                         case 0x00:  
                                 subop = (*instr >> 3) & 0x7;  
                                 switch (subop) {  
                                 case 1: /*  str  */  
                                         /*  TODO: Check Prot.mode?  */  
                                         op1 = cpu->cd.x86.tr;  
                                         if (!modrm(cpu, MODRM_WRITE_RM, 16,  
                                             mode67, 0, &instr, &newpc, &op1,  
                                             &op2))  
                                                 return 0;  
                                         break;  
                                 case 2: /*  lldt  */  
                                         /*  TODO: Check cpl? and Prot.mode  */  
                                         if (!modrm(cpu, MODRM_READ, 16, mode67,  
                                             0, &instr, &newpc, &op1, &op2))  
                                                 return 0;  
                                         reload_segment_descriptor(cpu,  
                                             RELOAD_LDTR, op1, &newpc);  
                                         break;  
                                 case 3: /*  ltr  */  
                                         /*  TODO: Check cpl=0 and Prot.mode  */  
                                         if (!modrm(cpu, MODRM_READ, 16, mode67,  
                                             0, &instr, &newpc, &op1, &op2))  
                                                 return 0;  
                                         reload_segment_descriptor(cpu,  
                                             RELOAD_TR, op1, &newpc);  
                                         break;  
                                 default:fatal("UNIMPLEMENTED 0x%02x,0x%02x"  
                                             ",0x%02x\n", op, imm, *instr);  
                                         quiet_mode = 0;  
                                         x86_cpu_disassemble_instr(cpu,  
                                             really_orig_instr, 1 | omode, 0, 0);  
                                         cpu->running = 0;  
                                 }  
                                 break;  
                         case 0x01:  
                                 subop = (*instr >> 3) & 0x7;  
                                 switch (subop) {  
                                 case 0: /*  sgdt  */  
                                 case 1: /*  sidt  */  
                                 case 2: /*  lgdt  */  
                                 case 3: /*  lidt  */  
                                         instr_orig = instr;  
                                         if (!modrm(cpu, MODRM_READ, mode,  
                                             mode67, MODRM_JUST_GET_ADDR, &instr,  
                                             &newpc, &op1, &op2))  
                                                 return 0;  
                                         /*  TODO/NOTE: how about errors?  */  
                                         if (subop >= 2) {  
                                                 x86_load(cpu, op1, &tmp, 2);  
                                                 x86_load(cpu, op1 + 2, &op2, 4);  
                                                 if (mode == 16)  
                                                         op2 &= 0x00ffffffULL;  
                                         }  
                                         switch (subop) {  
                                         case 0: tmp = cpu->cd.x86.gdtr_limit;  
                                                 op2 = cpu->cd.x86.gdtr;  
                                                 break;  
                                         case 1: tmp = cpu->cd.x86.idtr_limit;  
                                                 op2 = cpu->cd.x86.idtr;  
                                                 break;  
                                         case 2: cpu->cd.x86.gdtr_limit =  
                                                     tmp & 0xffff;  
                                                 cpu->cd.x86.gdtr = op2;  
                                                 break;  
                                         case 3: cpu->cd.x86.idtr_limit =  
                                                     tmp & 0xffff;  
                                                 cpu->cd.x86.idtr = op2;  
                                                 break;  
                                         }  
                                         if (subop < 2) {  
                                                 if (mode == 16)  
                                                         op2 &= 0x00ffffffULL;  
                                                 x86_store(cpu, op1, tmp, 2);  
                                                 x86_store(cpu, op1+2, op2, 4);  
                                         }  
                                         break;  
                                 case 4: /*  smsw  */  
                                 case 6: /*  lmsw  */  
                                         instr_orig = instr;  
                                         if (!modrm(cpu, MODRM_READ, 16, mode67,  
                                             0, &instr, &newpc, &op1, &op2))  
                                                 return 0;  
                                         if (((*instr_orig >> 3) & 0x7) == 4) {  
                                                 op1 = cpu->cd.x86.cr[0] &0xffff;  
                                                 if (!modrm(cpu, MODRM_WRITE_RM,  
                                                     16, mode67, 0, &instr_orig,  
                                                     NULL, &op1, &op2))  
                                                         return 0;  
                                         } else {  
                                                 /*  lmsw cannot be used to  
                                                     clear bit 0:  */  
                                                 op1 |= (cpu->cd.x86.cr[0] &  
                                                     X86_CR0_PE);  
                                                 x86_write_cr(cpu, 0,  
                                                     (cpu->cd.x86.cr[0] & ~0xf)  
                                                     | (op1 & 0xf));  
                                         }  
                                         break;  
                                 case 7: /*  invlpg  */  
                                         modrm(cpu, MODRM_READ, mode,  
                                             mode67, MODRM_JUST_GET_ADDR, &instr,  
                                             &newpc, &op1, &op2);  
                                         /*  TODO  */  
                                         break;  
                                 default:fatal("UNIMPLEMENTED 0x%02x,0x%02x"  
                                             ",0x%02x\n", op, imm, *instr);  
                                         quiet_mode = 0;  
                                         x86_cpu_disassemble_instr(cpu,  
                                             really_orig_instr, 1 | omode, 0, 0);  
                                         cpu->running = 0;  
                                 }  
                                 break;  
                         case 0x06:      /*  CLTS  */  
                                 cpu->cd.x86.cr[0] &= ~X86_CR0_TS;  
                                 break;  
                         case 0x08:      /*  INVD  */  
                                 /*  TODO  */  
                                 break;  
                         case 0x09:      /*  WBINVD  */  
                                 /*  TODO  */  
                                 break;  
                         case 0x0b:      /*  Reserved  */  
                                 x86_interrupt(cpu, 6, 0);  
                                 return 1;  
                         case 0x20:      /*  MOV r/m,CRx  */  
                         case 0x21:      /*  MOV r/m,DRx: TODO: is this right? */  
                                 instr_orig = instr;  
                                 if (!modrm(cpu, MODRM_READ, 32, mode67,  
                                     imm==0x20? MODRM_CR : MODRM_DR, &instr,  
                                     &newpc, &op1, &op2))  
                                         return 0;  
                                 op1 = op2;  
                                 if (!modrm(cpu, MODRM_WRITE_RM, 32, mode67,  
                                     imm==0x20? MODRM_CR : MODRM_DR, &instr_orig,  
                                     NULL, &op1, &op2))  
                                         return 0;  
                                 break;  
                         case 0x22:      /*  MOV CRx,r/m  */  
                         case 0x23:      /*  MOV DRx,r/m  */  
                                 instr_orig = instr;  
                                 if (!modrm(cpu, MODRM_READ, 32, mode67,  
                                     imm==0x22? MODRM_CR : MODRM_DR, &instr,  
                                     &newpc, &op1, &op2))  
                                         return 0;  
                                 op2 = op1;  
                                 if (!modrm(cpu, MODRM_WRITE_R, 32, mode67,  
                                     imm==0x22? MODRM_CR : MODRM_DR, &instr_orig,  
                                     NULL, &op1, &op2))  
                                         return 0;  
                                 break;  
                         case 0x30:      /*  WRMSR  */  
                         case 0x32:      /*  RDMSR  */  
                                 x86_msr(cpu, imm==0x30? 1 : 0);  
                                 break;  
                         case 0x31:      /*  RDTSC  */  
                                 if (cpu->cd.x86.model.model_number <  
                                     X86_MODEL_PENTIUM)  
                                         fatal("WARNING: rdtsc usually requires"  
                                             " a Pentium. continuing anyway\n");  
                                 if (cpu->cd.x86.cr[4] & X86_CR4_TSD)  
                                         fatal("WARNING: time stamp disable:"  
                                             " TODO\n");  
                                 cpu->cd.x86.r[X86_R_DX] = cpu->cd.x86.tsc >> 32;  
                                 cpu->cd.x86.r[X86_R_AX] = cpu->cd.x86.tsc  
                                     & 0xffffffff;  
                                 /*  TODO: make this better  */  
                                 cpu->cd.x86.tsc += 1000;  
                                 break;  
                         case 0xa0:  
                                 if (!x86_push(cpu, cpu->cd.x86.s[X86_S_FS],  
                                     mode))  
                                         return 0;  
                                 break;  
                         case 0xa1:  
                                 if (!x86_pop(cpu, &tmp, mode))  
                                         return 0;  
                                 reload_segment_descriptor(cpu, X86_S_FS,  
                                     tmp, &newpc);  
                                 break;  
                         case 0xa2:  
                                 if (!(cpu->cd.x86.rflags & X86_FLAGS_ID))  
                                         fatal("TODO: ID bit off in flags,"  
                                             " but CPUID attempted?\n");  
                                 x86_cpuid(cpu);  
                                 break;  
                         case 0xa4:  
                         case 0xa5:  
                         case 0xac:  
                         case 0xad:  
                                 instr_orig = instr;  
                                 if (!modrm(cpu, MODRM_READ, mode, mode67,  
                                     0, &instr, &newpc, &op1, &op2))  
                                         return 0;  
                                 if (imm & 1)  
                                         imm2 = cpu->cd.x86.r[X86_R_CX];  
                                 else  
                                         imm2 = read_imm(&instr, &newpc, 8);  
                                 imm2 &= 31;  
                                 if (imm <= 0xa5) {      /*  SHLD  */  
                                         if (mode == 16) {  
                                                 op1 <<= 16;  
                                                 op1 |= (op2 & 0xffff);  
                                         } else {  
                                                 op1 <<= 32;  
                                                 op1 |= (op2 & 0xffffffff);  
                                         }  
                                         x86_shiftrotate(cpu, &op1, 4, imm2,  
                                             mode == 64? 64 : (mode * 2));  
                                         op1 >>= (mode==16? 16 : 32);  
                                 } else {                /*  SHRD  */  
                                         if (mode == 16) {  
                                                 op2 <<= 16;  
                                                 op1 = (op1 & 0xffff) | op2;  
                                         } else {  
                                                 op2 <<= 32;  
                                                 op1 = (op1 & 0xffffffff) | op2;  
                                         }  
                                         x86_shiftrotate(cpu, &op1, 5, imm2,  
                                             mode == 64? 64 : (mode * 2));  
                                         op1 &= (mode==16? 0xffff : 0xffffffff);  
                                 }  
                                 if (!modrm(cpu, MODRM_WRITE_RM, mode, mode67,  
                                     0, &instr_orig, NULL, &op1, &op2))  
                                         return 0;  
                                 break;  
                         case 0xa8:  
                                 if (!x86_push(cpu, cpu->cd.x86.s[X86_S_GS],  
                                     mode))  
                                         return 0;  
                                 break;  
                         case 0xa9:  
                                 if (!x86_pop(cpu, &tmp, mode))  
                                         return 0;  
                                 reload_segment_descriptor(cpu, X86_S_GS,  
                                     tmp, &newpc);  
                                 break;  
                         case 0xa3:              /*  BT  */  
                         case 0xab:              /*  BTS  */  
                         case 0xb3:              /*  BTR  */  
                         case 0xbb:              /*  BTC  */  
                                 instr_orig = instr;  
                                 if (!modrm(cpu, MODRM_READ, mode, mode67,  
                                     0, &instr, &newpc, &op1, &op2))  
                                         return 0;  
                                 imm2 = op2 & 31;  
                                 if (mode == 16)  
                                         imm2 &= 15;  
                                 cpu->cd.x86.rflags &= ~X86_FLAGS_CF;  
                                 if (op1 & ((uint64_t)1 << imm2))  
                                         cpu->cd.x86.rflags |=  
                                             X86_FLAGS_CF;  
                                 switch (imm) {  
                                 case 0xab:  
                                         op1 |= ((uint64_t)1 << imm2);  
                                         break;  
                                 case 0xb3:  
                                         op1 &= ~((uint64_t)1 << imm2);  
                                         break;  
                                 case 0xbb:  
                                         op1 ^= ((uint64_t)1 << imm2);  
                                         break;  
                                 }  
                                 if (imm != 0xa3) {  
                                         if (!modrm(cpu, MODRM_WRITE_RM, mode,  
                                             mode67, 0, &instr_orig, NULL,  
                                             &op1, &op2))  
                                                 return 0;  
                                 }  
                                 break;  
                         case 0xaf:      /*  imul r16/32, rm16/32  */  
                                 instr_orig = instr;  
                                 if (!modrm(cpu, MODRM_READ, mode, mode67,  
                                     0, &instr, &newpc, &op1, &op2))  
                                         return 0;  
                                 cpu->cd.x86.rflags &= X86_FLAGS_CF;  
                                 cpu->cd.x86.rflags &= X86_FLAGS_OF;  
                                 if (mode == 16) {  
                                         op2 = (int16_t)op1 * (int16_t)op2;  
                                         if (op2 >= 0x10000)  
                                                 cpu->cd.x86.rflags |=  
                                                     X86_FLAGS_CF | X86_FLAGS_OF;  
                                 } else {  
                                         op2 = (int32_t)op1 * (int32_t)op2;  
                                         if (op2 >= 0x100000000ULL)  
                                                 cpu->cd.x86.rflags |=  
                                                     X86_FLAGS_CF | X86_FLAGS_OF;  
                                 }  
                                 if (!modrm(cpu, MODRM_WRITE_R, mode, mode67,  
                                     0, &instr_orig, NULL, &op1, &op2))  
                                         return 0;  
                                 break;  
                         case 0xb0:  
                         case 0xb1:      /*  CMPXCHG  */  
                                 instr_orig = instr;  
                                 if (!modrm(cpu, MODRM_READ, mode, mode67,  
                                     imm == 0xb0? MODRM_EIGHTBIT : 0,  
                                     &instr, &newpc, &op1, &op2))  
                                         return 0;  
                                 x86_calc_flags(cpu, op1, cpu->cd.x86.r[  
                                     X86_R_AX], imm == 0xb0? 8 : mode,  
                                     CALCFLAGS_OP_SUB);  
                                 if (cpu->cd.x86.rflags & X86_FLAGS_ZF) {  
                                         if (!modrm(cpu, MODRM_WRITE_RM, mode,  
                                             mode67, imm == 0xb0?  
                                             MODRM_EIGHTBIT : 0,  
                                             &instr_orig, NULL, &op2, &op1))  
                                                 return 0;  
                                 } else {  
                                         if (imm == 0xb0)  
                                                 cpu->cd.x86.r[X86_R_AX] =  
                                                     (cpu->cd.x86.r[X86_R_AX] &  
                                                     ~0xff) | (op1 & 0xff);  
                                         else if (mode == 16)  
                                                 cpu->cd.x86.r[X86_R_AX] =  
                                                     (cpu->cd.x86.r[X86_R_AX] &  
                                                     ~0xffff) | (op1 & 0xffff);  
                                         else    /*  32 bit  */  
                                                 cpu->cd.x86.r[X86_R_AX] = op1;  
                                 }  
                                 break;  
                         case 0xb2:      /*  LSS  */  
                         case 0xb4:      /*  LFS  */  
                         case 0xb5:      /*  LGS  */  
                                 instr_orig = instr;  
                                 if (!modrm(cpu, MODRM_READ, mode, mode67,  
                                     MODRM_JUST_GET_ADDR, &instr, &newpc,  
                                     &op1, &op2))  
                                         return 0;  
                                 /*  op1 is the address to load from  */  
                                 if (!x86_load(cpu, op1, &tmp, mode/8))  
                                         return 0;  
                                 op2 = tmp;  
                                 if (!x86_load(cpu, op1 + mode/8, &tmp, 2))  
                                         return 0;  
                                 reload_segment_descriptor(cpu, imm==0xb2?  
                                     X86_S_SS:(imm==0xb4?X86_S_FS:X86_S_GS),  
                                     tmp, &newpc);  
                                 if (!modrm(cpu, MODRM_WRITE_R, mode, mode67,  
                                     0, &instr_orig, NULL, &op1, &op2))  
                                         return 0;  
                                 break;  
                         case 0xb6:  
                         case 0xb7:      /*  movzx  */  
                         case 0xbe:  
                         case 0xbf:      /*  movsx  */  
                                 instr_orig = instr;  
                                 if (!modrm(cpu, MODRM_READ, mode, mode67,  
                                     (imm&1)==0? (MODRM_EIGHTBIT |  
                                     MODRM_R_NONEIGHTBIT) : MODRM_RM_16BIT,  
                                     &instr, &newpc, &op1, &op2))  
                                         return 0;  
                                 signflag = 0;  
                                 if (imm >= 0xbe)  
                                         signflag = 1;  
                                 op2 = op1;  
                                 if (imm & 1) {          /*  r32 = r16  */  
                                         op2 &= 0xffff;  
                                         if (signflag && op2 & 0x8000)  
                                                 op2 |= 0xffff0000ULL;  
                                 } else {                /*  r(mode) = r8  */  
                                         op2 &= 0xff;  
                                         if (signflag && op2 & 0x80)  
                                                 op2 |= 0xffffff00ULL;  
                                 }  
                                 if (!modrm(cpu, MODRM_WRITE_R, mode, mode67,  
                                     (imm&1)==0? (MODRM_EIGHTBIT |  
                                     MODRM_R_NONEIGHTBIT) : MODRM_RM_16BIT,  
                                     &instr_orig, NULL, &op1, &op2))  
                                         return 0;  
                                 break;  
                         case 0xba:  
                                 subop = (*instr >> 3) & 0x7;  
                                 switch (subop) {  
                                 case 4: /*  BT  */  
                                 case 5: /*  BTS  */  
                                 case 6: /*  BTR  */  
                                 case 7: /*  BTC  */  
                                         instr_orig = instr;  
                                         if (!modrm(cpu, MODRM_READ, mode,  
                                             mode67, 0, &instr, &newpc, &op1,  
                                             &op2))  
                                                 return 0;  
                                         imm = read_imm(&instr, &newpc, 8);  
                                         imm &= 31;  
                                         if (mode == 16)  
                                                 imm &= 15;  
                                         cpu->cd.x86.rflags &= ~X86_FLAGS_CF;  
                                         if (op1 & ((uint64_t)1 << imm))  
                                                 cpu->cd.x86.rflags |=  
                                                     X86_FLAGS_CF;  
                                         switch (subop) {  
                                         case 5: op1 |= ((uint64_t)1 << imm);  
                                                 break;  
                                         case 6: op1 &= ~((uint64_t)1 << imm);  
                                                 break;  
                                         case 7: op1 ^= ((uint64_t)1 << imm);  
                                                 break;  
                                         }  
                                         if (subop != 4) {  
                                                 if (!modrm(cpu, MODRM_WRITE_RM,  
                                                     mode, mode67, 0,  
                                                     &instr_orig, NULL,  
                                                     &op1, &op2))  
                                                         return 0;  
                                         }  
                                         break;  
                                 default:fatal("UNIMPLEMENTED 0x%02x,0x%02x"  
                                             ",0x%02x\n", op, imm, *instr);  
                                         quiet_mode = 0;  
                                         x86_cpu_disassemble_instr(cpu,  
                                             really_orig_instr, 1|omode, 0, 0);  
                                         cpu->running = 0;  
                                 }  
                                 break;  
                         case 0xbc:      /*  bsf  */  
                         case 0xbd:      /*  bsr  */  
                                 instr_orig = instr;  
                                 if (!modrm(cpu, MODRM_READ, mode, mode67,  
                                     0, &instr, &newpc, &op1, &op2))  
                                         return 0;  
                                 cpu->cd.x86.rflags &= ~X86_FLAGS_ZF;  
                                 if (op1 == 0)  
                                         cpu->cd.x86.rflags |= X86_FLAGS_ZF;  
                                 i = mode - 1;  
                                 if (imm == 0xbc)  
                                         i = 0;  
                                 for (;;) {  
                                         if (op1 & ((uint64_t)1<<i)) {  
                                                 op2 = i;  
                                                 break;  
                                         }  
                                         if (imm == 0xbc) {  
                                                 if (++i >= mode)  
                                                         break;  
                                         } else {  
                                                 if (--i < 0)  
                                                         break;  
                                         }  
                                 }  
                                 if (!modrm(cpu, MODRM_WRITE_R, mode, mode67,  
                                     0, &instr_orig, NULL, &op1, &op2))  
                                         return 0;  
                                 break;  
                         case 0xc0:      /*  xadd  */  
                         case 0xc1:  
                                 instr_orig = instr_orig_2 = instr;  
                                 if (!modrm(cpu, MODRM_READ, mode, mode67,  
                                     imm == 0xc0? MODRM_EIGHTBIT : 0,  
                                     &instr, &newpc, &op1, &op2))  
                                         return 0;  
                                 tmp = op1; op1 = op2; op2 = tmp;  
                                 x86_calc_flags(cpu, op1, op2, imm==0xc0?  
                                     8 : mode, CALCFLAGS_OP_ADD);  
                                 op1 += op2;  
                                 if (!modrm(cpu, MODRM_WRITE_RM, mode, mode67,  
                                     imm == 0xc0? MODRM_EIGHTBIT : 0,  
                                     &instr_orig, NULL, &op1, &op2))  
                                         return 0;  
                                 if (!modrm(cpu, MODRM_WRITE_R, mode, mode67,  
                                     imm == 0xc0? MODRM_EIGHTBIT : 0,  
                                     &instr_orig_2, NULL, &op1, &op2))  
                                         return 0;  
                                 break;  
                         case 0xc7:  
                                 subop = (*instr >> 3) & 0x7;  
                                 switch (subop) {  
                                 case 1: /*  CMPXCHG8B  */  
                                         if (!modrm(cpu, MODRM_READ, mode,  
                                             mode67, MODRM_JUST_GET_ADDR, &instr,  
                                             &newpc, &op1, &op2))  
                                                 return 0;  
                                         if (!x86_load(cpu, op1, &tmp, 8))  
                                                 return 0;  
                                         cpu->cd.x86.rflags &= ~X86_FLAGS_ZF;  
                                         if ((tmp >> 32) == (0xffffffffULL &  
                                             cpu->cd.x86.r[X86_R_DX]) && (tmp  
                                             & 0xffffffffULL) == (0xffffffffULL &  
                                             cpu->cd.x86.r[X86_R_AX])) {  
                                                 cpu->cd.x86.rflags |=  
                                                     X86_FLAGS_ZF;  
                                                 tmp = ((cpu->cd.x86.r[X86_R_CX]  
                                                     & 0xffffffffULL) << 32) |  
                                                     (cpu->cd.x86.r[X86_R_BX] &  
                                                     0xffffffffULL);  
                                                 if (!x86_store(cpu, op1, tmp,8))  
                                                         return 0;  
                                         } else {  
                                                 cpu->cd.x86.r[X86_R_DX] =  
                                                     tmp >> 32;  
                                                 cpu->cd.x86.r[X86_R_AX] =  
                                                     tmp & 0xffffffffULL;  
                                         }  
                                         break;  
                                 default:fatal("UNIMPLEMENTED 0x%02x,0x%02x"  
                                             ",0x%02x\n", op, imm, *instr);  
                                         quiet_mode = 0;  
                                         x86_cpu_disassemble_instr(cpu,  
                                             really_orig_instr, 1|omode, 0, 0);  
                                         cpu->running = 0;  
                                 }  
                                 break;  
                         default:fatal("TODO: 0x0f,0x%02x\n", imm);  
                                 quiet_mode = 0;  
                                 x86_cpu_disassemble_instr(cpu,  
                                     really_orig_instr, 1|omode, 0, 0);  
                                 cpu->running = 0;  
                         }  
                 }  
         } else if ((op & 0xf0) < 0x20 && (op & 7) == 7) {  
                 uint64_t tmp;  
                 success = x86_pop(cpu, &tmp, mode);  
                 if (!success)  
                         return 0;  
                 reload_segment_descriptor(cpu, op/8, tmp, &newpc);  
         } else if (op == 0x27) {                        /*  DAA  */  
                 int a = (cpu->cd.x86.r[X86_R_AX] >> 4) & 0xf;  
                 int b = cpu->cd.x86.r[X86_R_AX] & 0xf;  
                 if (b > 9) {  
                         b -= 10;  
                         a ++;  
                 } else if (cpu->cd.x86.rflags & X86_FLAGS_AF)  
                         b += 6;  
                 cpu->cd.x86.rflags &= ~X86_FLAGS_CF;  
                 cpu->cd.x86.rflags &= ~X86_FLAGS_AF;  
                 if (a*10 + b >= 100) {  
                         cpu->cd.x86.rflags |= X86_FLAGS_CF;  
                         cpu->cd.x86.rflags |= X86_FLAGS_AF;  
                         a %= 10;  
                 }  
                 cpu->cd.x86.r[X86_R_AX] &= ~0xff;  
                 cpu->cd.x86.r[X86_R_AX] |= ((a*16 + b) & 0xff);  
         } else if (op == 0x2f) {                        /*  DAS  */  
                 int tmp_al = cpu->cd.x86.r[X86_R_AX] & 0xff;  
                 if ((tmp_al & 0xf) > 9 || cpu->cd.x86.rflags & X86_FLAGS_AF) {  
                         cpu->cd.x86.r[X86_R_AX] &= ~0xff;  
                         cpu->cd.x86.r[X86_R_AX] |= ((tmp_al - 6) & 0xff);  
                         cpu->cd.x86.rflags |= X86_FLAGS_AF;  
                 } else  
                         cpu->cd.x86.rflags &= ~X86_FLAGS_AF;  
                 if (tmp_al > 0x9f || cpu->cd.x86.rflags & X86_FLAGS_CF) {  
                         cpu->cd.x86.r[X86_R_AX] &= ~0xff;  
                         cpu->cd.x86.r[X86_R_AX] |= ((tmp_al - 0x60) & 0xff);  
                         cpu->cd.x86.rflags |= X86_FLAGS_CF;  
                 } else  
                         cpu->cd.x86.rflags &= ~X86_FLAGS_CF;  
                 x86_calc_flags(cpu, cpu->cd.x86.r[X86_R_AX] & 0xff,  
                     0, 8, CALCFLAGS_OP_XOR);  
         } else if (op == 0x37) {                        /*  AAA  */  
                 int b = cpu->cd.x86.r[X86_R_AX] & 0xf;  
                 if (b > 9) {  
                         cpu->cd.x86.r[X86_R_AX] = (cpu->cd.x86.r[X86_R_AX]  
                             & ~0xff00) | ((cpu->cd.x86.r[X86_R_AX] &  
                             0xff00) + 0x100);  
                         cpu->cd.x86.rflags |= X86_FLAGS_CF | X86_FLAGS_AF;  
                 } else {  
                         cpu->cd.x86.rflags &= ~(X86_FLAGS_CF | X86_FLAGS_AF);  
                 }  
                 cpu->cd.x86.r[X86_R_AX] &= ~0xf0;  
         } else if (op >= 0x40 && op <= 0x4f) {  
                 int old_cf = cpu->cd.x86.rflags & X86_FLAGS_CF;  
                 if (op < 0x48) {  
                         x86_calc_flags(cpu, cpu->cd.x86.r[op & 7], 1, mode,  
                             CALCFLAGS_OP_ADD);  
                         cpu->cd.x86.r[op & 7] = modify(cpu->cd.x86.r[op & 7],  
                             cpu->cd.x86.r[op & 7] + 1);  
                 } else {  
                         x86_calc_flags(cpu, cpu->cd.x86.r[op & 7], 1, mode,  
                             CALCFLAGS_OP_SUB);  
                         if (mode == 16)  
                                 cpu->cd.x86.r[op & 7] = modify(cpu->cd.x86.r[op  
                                     & 7], (cpu->cd.x86.r[op & 7] & 0xffff) - 1);  
                         else {  
                                 cpu->cd.x86.r[op & 7] --;  
                                 cpu->cd.x86.r[op & 7] &= 0xffffffffULL;  
                         }  
                 }  
                 /*  preserve CF:  */  
                 cpu->cd.x86.rflags &= ~X86_FLAGS_CF;  
                 cpu->cd.x86.rflags |= old_cf;  
         } else if (op >= 0x50 && op <= 0x57) {  
                 if (!x86_push(cpu, cpu->cd.x86.r[op & 7], mode))  
                         return 0;  
         } else if (op >= 0x58 && op <= 0x5f) {  
                 if (!x86_pop(cpu, &tmp, mode))  
                         return 0;  
                 if (mode == 16)  
                         cpu->cd.x86.r[op & 7] = (cpu->cd.x86.r[op & 7] &  
                             ~0xffff) | (tmp & 0xffff);  
                 else  
                         cpu->cd.x86.r[op & 7] = tmp;  
         } else if (op == 0x60) {                /*  PUSHA/PUSHAD  */  
                 uint64_t r[8];  
                 int i;  
                 for (i=0; i<8; i++)  
                         r[i] = cpu->cd.x86.r[i];  
                 for (i=0; i<8; i++)  
                         if (!x86_push(cpu, r[i], mode)) {  
                                 fatal("TODO: failed pusha\n");  
                                 cpu->running = 0;  
                                 return 0;  
                         }  
         } else if (op == 0x61) {                /*  POPA/POPAD  */  
                 uint64_t r[8];  
                 int i;  
                 for (i=7; i>=0; i--)  
                         if (!x86_pop(cpu, &r[i], mode)) {  
                                 fatal("TODO: failed popa\n");  
                                 cpu->running = 0;  
                                 return 0;  
                         }  
                 for (i=0; i<8; i++)  
                         if (i != X86_R_SP) {  
                                 if (mode == 16)  
                                         cpu->cd.x86.r[i] = (cpu->cd.x86.r[i]  
                                             & ~0xffff) | (r[i] & 0xffff);  
                                 else  
                                         cpu->cd.x86.r[i] = r[i];  
                         }  
         } else if (op == 0x68) {                /*  PUSH imm16/32  */  
                 uint64_t imm = read_imm(&instr, &newpc, mode);  
                 if (!x86_push(cpu, imm, mode))  
                         return 0;  
         } else if (op == 0x69 || op == 0x6b) {  
                 instr_orig = instr;  
                 if (!modrm(cpu, MODRM_READ, mode, mode67, 0, &instr,  
                     &newpc, &op1, &op2))  
                         return 0;  
                 if (op == 0x69)  
                         imm = read_imm(&instr, &newpc, mode);  
                 else  
                         imm = (signed char)read_imm(&instr, &newpc, 8);  
                 op2 = op1 * imm;  
                 /*  TODO: overflow!  */  
                 if (!modrm(cpu, MODRM_WRITE_R, mode, mode67, 0,  
                     &instr_orig, NULL, &op1, &op2))  
                         return 0;  
         } else if (op == 0x6a) {                /*  PUSH imm8  */  
                 uint64_t imm = (signed char)read_imm(&instr, &newpc, 8);  
                 if (!x86_push(cpu, imm, mode))  
                         return 0;  
         } else if ((op & 0xf0) == 0x70) {  
                 imm = read_imm(&instr, &newpc, 8);  
                 success = x86_condition(cpu, op);  
                 if (success)  
                         newpc = modify(newpc, newpc + (signed char)imm);  
         } else if (op == 0x80 || op == 0x81) {  /*  add/and r/m, imm  */  
                 instr_orig = instr;  
                 if (!modrm(cpu, MODRM_READ, mode, mode67, op == 0x80?  
                     MODRM_EIGHTBIT : 0, &instr, &newpc, &op1, &op2))  
                         return 0;  
                 imm = read_imm(&instr, &newpc, op==0x80? 8 : mode);  
                 switch ((*instr_orig >> 3) & 0x7) {  
                 case 0: x86_calc_flags(cpu, op1, imm, op==0x80? 8 : mode,  
                             CALCFLAGS_OP_ADD);  
                         op1 += imm;  
                         break;  
                 case 1: op1 |= imm; break;  
                 case 2: tmp = imm + (cpu->cd.x86.rflags & X86_FLAGS_CF? 1 : 0);  
                         x86_calc_flags(cpu, op1, tmp, op==0x80? 8 : mode,  
                             CALCFLAGS_OP_ADD);  
                         op1 += tmp;  
                         break;  
                 case 3: tmp = imm + (cpu->cd.x86.rflags & X86_FLAGS_CF? 1 : 0);  
                         x86_calc_flags(cpu, op1, tmp, op==0x80? 8 : mode,  
                             CALCFLAGS_OP_SUB);  
                         op1 -= tmp;  
                         break;  
                 case 4: op1 &= imm; break;  
                 case 5: x86_calc_flags(cpu, op1, imm, op==0x80? 8 : mode,  
                             CALCFLAGS_OP_SUB);  
                         op1 -= imm; break;  
                 case 6: op1 ^= imm; break;  
                 case 7: x86_calc_flags(cpu, op1, imm, op==0x80? 8 : mode,  
                             CALCFLAGS_OP_SUB); /* cmp */  
                         break;  
                 }  
   
                 if (((*instr_orig >> 3) & 0x7) != 7) {  
                         if (((*instr_orig >> 3) & 0x7) != 0 &&  
                             ((*instr_orig >> 3) & 0x7) != 2 &&  
                             ((*instr_orig >> 3) & 0x7) != 3 &&  
                             ((*instr_orig >> 3) & 0x7) != 5)  
                                 x86_calc_flags(cpu, op1, 0, op==0x80? 8 : mode,  
                                     CALCFLAGS_OP_XOR);  
   
                         /*  "and","or","xor" always clears CF and OF:  */  
                         if (((*instr_orig >> 3) & 0x7) == 1 ||  
                             ((*instr_orig >> 3) & 0x7) == 4 ||  
                             ((*instr_orig >> 3) & 0x7) == 6) {  
                                 cpu->cd.x86.rflags &= ~X86_FLAGS_CF;  
                                 cpu->cd.x86.rflags &= ~X86_FLAGS_OF;  
                         }  
   
                         if (!modrm(cpu, MODRM_WRITE_RM, mode, mode67,  
                             op == 0x80? MODRM_EIGHTBIT : 0, &instr_orig,  
                             NULL, &op1, &op2))  
                                 return 0;  
                 }  
         } else if (op == 0x83) {        /*  add/and r/m1632, imm8  */  
                 instr_orig = instr;  
                 if (!modrm(cpu, MODRM_READ, mode, mode67, 0, &instr,  
                     &newpc, &op1, &op2))  
                         return 0;  
                 imm = read_imm(&instr, &newpc, 8);  
                 switch ((*instr_orig >> 3) & 0x7) {  
                 case 0: x86_calc_flags(cpu, op1, (signed char)imm,  
                             mode, CALCFLAGS_OP_ADD);  
                         op1 += (signed char)imm;  
                         break;  
                 case 1: op1 |= (signed char)imm; break;  
                 case 2: tmp = (signed char)imm +  
                             (cpu->cd.x86.rflags & X86_FLAGS_CF? 1 : 0);  
                         x86_calc_flags(cpu, op1, tmp, mode, CALCFLAGS_OP_ADD);  
                         op1 += tmp;  
                         break;  
                 case 3: tmp = (signed char)imm +  
                             (cpu->cd.x86.rflags & X86_FLAGS_CF? 1 : 0);  
                         x86_calc_flags(cpu, op1, tmp, mode, CALCFLAGS_OP_SUB);  
                         op1 -= tmp;  
                         break;  
                 case 4: op1 &= (signed char)imm; break;  
                 case 5: x86_calc_flags(cpu, op1, (signed char)imm, mode,  
                             CALCFLAGS_OP_SUB);  
                         op1 -= (signed char)imm; break;  
                 case 6: op1 ^= (signed char)imm; break;  
                 case 7: x86_calc_flags(cpu, op1, (signed char)imm, mode,  
                             CALCFLAGS_OP_SUB);  
                         break;  
                 }  
                 if (((*instr_orig >> 3) & 0x7) != 7) {  
                         if (((*instr_orig >> 3) & 0x7) != 0 &&  
                             ((*instr_orig >> 3) & 0x7) != 2 &&  
                             ((*instr_orig >> 3) & 0x7) != 3 &&  
                             ((*instr_orig >> 3) & 0x7) != 5)  
                                 x86_calc_flags(cpu, op1, 0, mode,  
                                     CALCFLAGS_OP_XOR);  
   
                         /*  "and","or","xor" always clears CF and OF:  */  
                         if (((*instr_orig >> 3) & 0x7) == 1 ||  
                             ((*instr_orig >> 3) & 0x7) == 4 ||  
                             ((*instr_orig >> 3) & 0x7) == 6) {  
                                 cpu->cd.x86.rflags &= ~X86_FLAGS_CF;  
                                 cpu->cd.x86.rflags &= ~X86_FLAGS_OF;  
                         }  
                         if (!modrm(cpu, MODRM_WRITE_RM, mode,  
                             mode67, 0, &instr_orig, NULL, &op1, &op2))  
                                 return 0;  
                 }  
         } else if (op == 0x84 || op == 0x85) {          /*  TEST  */  
                 success = modrm(cpu, MODRM_READ, mode, mode67, op == 0x84?  
                     MODRM_EIGHTBIT : 0, &instr, &newpc, &op1, &op2);  
                 if (!success)  
                         return 0;  
                 op1 &= op2;  
                 x86_calc_flags(cpu, op1, 0, op==0x84? 8 : mode,  
                     CALCFLAGS_OP_XOR);  
                 cpu->cd.x86.rflags &= ~X86_FLAGS_CF;  
                 cpu->cd.x86.rflags &= ~X86_FLAGS_OF;  
         } else if (op >= 0x86 && op <= 0x87) {          /*  XCHG  */  
                 void *orig2 = instr_orig = instr;  
                 success = modrm(cpu, MODRM_READ, mode, mode67, op&1? 0 :  
                     MODRM_EIGHTBIT, &instr, &newpc, &op1, &op2);  
                 if (!success)  
                         return 0;  
                 /*  Note: Update the r/m first, because it may be dependant  
                     on original register values :-)  */  
                 success = modrm(cpu, MODRM_WRITE_RM, mode, mode67,  
                     op == 0x86? MODRM_EIGHTBIT : 0, &instr_orig,  
                     NULL, &op2, &op1);  
                 instr_orig = orig2;  
                 success = modrm(cpu, MODRM_WRITE_R, mode, mode67,  
                     op == 0x86? MODRM_EIGHTBIT : 0, &instr_orig,  
                     NULL, &op2, &op1);  
                 if (!success)  
                         return 0;  
         } else if (op >= 0x88 && op <= 0x8b) {          /*  MOV  */  
                 instr_orig = instr;  
                 success = modrm(cpu, MODRM_READ, mode, mode67, (op & 1) == 0?  
                     MODRM_EIGHTBIT : 0, &instr, &newpc, &op1, &op2);  
                 if (!success)  
                         return 0;  
                 if (op < 0x8a) {  
                         success = modrm(cpu, MODRM_WRITE_RM, mode, mode67,  
                             op == 0x88? MODRM_EIGHTBIT : 0, &instr_orig,  
                             NULL, &op2, &op1);  
                 } else {  
                         success = modrm(cpu, MODRM_WRITE_R, mode, mode67,  
                             op == 0x8a? MODRM_EIGHTBIT : 0, &instr_orig,  
                             NULL, &op2, &op1);  
                 }  
                 if (!success)  
                         return 0;  
         } else if (op == 0x8c || op == 0x8e) {          /*  MOV seg  */  
                 instr_orig = instr;  
                 if (!modrm(cpu, MODRM_READ, 16, mode67, MODRM_SEG,  
                     &instr, &newpc, &op1, &op2))  
                         return 0;  
                 if (op == 0x8c) {  
                         if (!modrm(cpu, MODRM_WRITE_RM, 16, mode67, MODRM_SEG,  
                             &instr_orig, NULL, &op2, &op1))  
                                 return 0;  
                 } else {  
                         reload_segment_descriptor(cpu, (*instr_orig >> 3) & 7,  
                             op1 & 0xffff, &newpc);  
                 }  
         } else if (op == 0x8d) {                        /*  LEA  */  
                 instr_orig = instr;  
                 if (!modrm(cpu, MODRM_READ, mode, mode67,  
                     MODRM_JUST_GET_ADDR, &instr, &newpc, &op1, &op2))  
                         return 0;  
                 op2 = op1;  
                 if (!modrm(cpu, MODRM_WRITE_R, mode, mode67,  
                     0, &instr_orig, NULL, &op1, &op2))  
                         return 0;  
         } else if (op == 0x8f) {  
                 switch ((*instr >> 3) & 0x7) {  
                 case 0: /*  POP m16/m32  */  
                         if (!x86_pop(cpu, &op1, mode))  
                                 return 0;  
                         if (!modrm(cpu, MODRM_WRITE_RM, mode, mode67,  
                             0, &instr, &newpc, &op1, &op2))  
                                 return 0;  
                         break;  
                 default:  
                         fatal("UNIMPLEMENTED 0x%02x,0x%02x\n", op, *instr);  
                         quiet_mode = 0;  
                         x86_cpu_disassemble_instr(cpu,  
                             really_orig_instr, 1|omode, 0, 0);  
                         cpu->running = 0;  
                 }  
         } else if (op == 0x90) {                /*  NOP  */  
         } else if (op >= 0x91 && op <= 0x97) {  /*  XCHG  */  
                 uint64_t tmp;  
                 if (mode == 16) {  
                         tmp = cpu->cd.x86.r[X86_R_AX];  
                         cpu->cd.x86.r[X86_R_AX] = modify(  
                             cpu->cd.x86.r[X86_R_AX], cpu->cd.x86.r[op & 7]);  
                         cpu->cd.x86.r[op & 7] = modify(  
                             cpu->cd.x86.r[op & 7], tmp);  
                 } else {  
                         tmp = cpu->cd.x86.r[X86_R_AX];  
                         cpu->cd.x86.r[X86_R_AX] = cpu->cd.x86.r[op & 7];  
                         cpu->cd.x86.r[op & 7] = tmp;  
                 }  
         } else if (op == 0x98) {                /*  CBW/CWDE  */  
                 if (mode == 16) {  
                         cpu->cd.x86.r[X86_R_AX] &= ~0xff00;  
                         if (cpu->cd.x86.r[X86_R_AX] & 0x80)  
                                 cpu->cd.x86.r[X86_R_AX] |= 0xff00;  
                 } else {  
                         cpu->cd.x86.r[X86_R_AX] &= 0xffff;  
                         if (cpu->cd.x86.r[X86_R_AX] & 0x8000)  
                                 cpu->cd.x86.r[X86_R_AX] |= 0xffff0000ULL;  
                 }  
         } else if (op == 0x99) {                /*  CWD/CDQ  */  
                 if (mode == 16) {  
                         cpu->cd.x86.r[X86_R_DX] &= ~0xffff;  
                         if (cpu->cd.x86.r[X86_R_AX] & 0x8000)  
                                 cpu->cd.x86.r[X86_R_DX] |= 0xffff;  
                 } else {  
                         cpu->cd.x86.r[X86_R_DX] = 0;  
                         if (cpu->cd.x86.r[X86_R_AX] & 0x80000000ULL)  
                                 cpu->cd.x86.r[X86_R_DX] = 0xffffffff;  
                 }  
         } else if (op == 0x9a) {        /*  CALL seg:ofs  */  
                 uint16_t old_tr = cpu->cd.x86.tr;  
                 imm = read_imm(&instr, &newpc, mode);  
                 imm2 = read_imm(&instr, &newpc, 16);  
                 if (!x86_push(cpu, cpu->cd.x86.s[X86_S_CS], mode))  
                         return 0;  
                 if (!x86_push(cpu, newpc, mode)) {  
                         fatal("TODO: push failed in CALL seg:ofs\n");  
                         cpu->running = 0;  
                         return 0;  
                 }  
                 reload_segment_descriptor(cpu, X86_S_CS, imm2, &newpc);  
                 if (cpu->cd.x86.tr == old_tr)  
                         newpc = imm;  
         } else if (op == 0x9b) {                /*  WAIT  */  
         } else if (op == 0x9c) {                /*  PUSHF  */  
                 if (!x86_push(cpu, cpu->cd.x86.rflags, mode))  
                         return 0;  
         } else if (op == 0x9d) {                /*  POPF  */  
                 if (!x86_pop(cpu, &tmp, mode))  
                         return 0;  
                 if (mode == 16)  
                         cpu->cd.x86.rflags = (cpu->cd.x86.rflags & ~0xffff)  
                             | (tmp & 0xffff);  
                 else if (mode == 32)  
                         cpu->cd.x86.rflags = (cpu->cd.x86.rflags & ~0xffffffff)  
                             | (tmp & 0xffffffff);  
                 else  
                         cpu->cd.x86.rflags = tmp;  
                 /*  TODO: only affect some bits?  */  
                 cpu->cd.x86.rflags |= 0x0002;  
                 if (cpu->cd.x86.model.model_number == X86_MODEL_8086)  
                         cpu->cd.x86.rflags |= 0xf000;  
                 /*  TODO: all these bits aren't really cleared on a 286:  */  
                 if (cpu->cd.x86.model.model_number == X86_MODEL_80286)  
                         cpu->cd.x86.rflags &= ~0xf000;  
                 if (cpu->cd.x86.model.model_number == X86_MODEL_80386)  
                         cpu->cd.x86.rflags &= ~X86_FLAGS_AC;  
                 if (cpu->cd.x86.model.model_number == X86_MODEL_80486)  
                         cpu->cd.x86.rflags &= ~X86_FLAGS_ID;  
         } else if (op == 0x9e) {                /*  SAHF  */  
                 int mask = (X86_FLAGS_SF | X86_FLAGS_ZF  
                     | X86_FLAGS_AF | X86_FLAGS_PF | X86_FLAGS_CF);  
                 cpu->cd.x86.rflags &= ~mask;  
                 mask &= ((cpu->cd.x86.r[X86_R_AX] >> 8) & 0xff);  
                 cpu->cd.x86.rflags |= mask;  
         } else if (op == 0x9f) {                /*  LAHF  */  
                 int b = cpu->cd.x86.rflags & (X86_FLAGS_SF | X86_FLAGS_ZF  
                     | X86_FLAGS_AF | X86_FLAGS_PF | X86_FLAGS_CF);  
                 b |= 2;  
                 cpu->cd.x86.r[X86_R_AX] &= ~0xff00;  
                 cpu->cd.x86.r[X86_R_AX] |= (b << 8);  
         } else if (op == 0xa0) {                /*  MOV AL,[addr]  */  
                 imm = read_imm(&instr, &newpc, mode67);  
                 if (!x86_load(cpu, imm, &tmp, 1))  
                         return 0;  
                 cpu->cd.x86.r[X86_R_AX] = (cpu->cd.x86.r[X86_R_AX] & ~0xff)  
                     | (tmp & 0xff);  
         } else if (op == 0xa1) {                /*  MOV AX,[addr]  */  
                 imm = read_imm(&instr, &newpc, mode67);  
                 if (!x86_load(cpu, imm, &tmp, mode/8))  
                         return 0;  
                 cpu->cd.x86.r[X86_R_AX] = modify(cpu->cd.x86.r[X86_R_AX], tmp);  
         } else if (op == 0xa2) {                /*  MOV [addr],AL  */  
                 imm = read_imm(&instr, &newpc, mode67);  
                 if (!x86_store(cpu, imm, cpu->cd.x86.r[X86_R_AX], 1))  
                         return 0;  
         } else if (op == 0xa3) {                /*  MOV [addr],AX  */  
                 imm = read_imm(&instr, &newpc, mode67);  
                 if (!x86_store(cpu, imm, cpu->cd.x86.r[X86_R_AX], mode/8))  
                         return 0;  
         } else if (op == 0xa4 || op == 0xa5 ||          /*  MOVS  */  
             op == 0xa6 || op == 0xa7 ||                 /*  CMPS  */  
             op == 0xaa || op == 0xab ||                 /*  STOS  */  
             op == 0xac || op == 0xad ||                 /*  LODS  */  
             op == 0xae || op == 0xaf) {                 /*  SCAS  */  
                 int dir = 1, movs = 0, lods = 0, cmps = 0, stos = 0, scas = 0;  
                 int origcursegment = cpu->cd.x86.cursegment;  
   
                 len = 1;  
                 if (op & 1)  
                         len = mode / 8;  
                 if (op >= 0xa4 && op <= 0xa5)  
                         movs = 1;  
                 if (op >= 0xa6 && op <= 0xa7)  
                         cmps = 1;  
                 if (op >= 0xaa && op <= 0xab)  
                         stos = 1;  
                 if (op >= 0xac && op <= 0xad)  
                         lods = 1;  
                 if (op >= 0xae && op <= 0xaf)  
                         scas = 1;  
                 if (cpu->cd.x86.rflags & X86_FLAGS_DF)  
                         dir = -1;  
   
                 do {  
                         uint64_t value;  
   
                         if (rep) {  
                                 /*  Abort if [e]cx already 0:  */  
                                 if (mode == 16 && (cpu->cd.x86.r[X86_R_CX] &  
                                     0xffff) == 0)  
                                         break;  
                                 if (mode != 16 && cpu->cd.x86.r[X86_R_CX] == 0)  
                                         break;  
                         }  
   
                         if (!stos && !scas) {  
                                 uint64_t addr = cpu->cd.x86.r[X86_R_SI];  
                                 if (mode67 == 16)  
                                         addr &= 0xffff;  
                                 if (mode67 == 32)  
                                         addr &= 0xffffffff;  
                                 cpu->cd.x86.cursegment = origcursegment;  
                                 if (!x86_load(cpu, addr, &value, len))  
                                         return 0;  
                         } else  
                                 value = cpu->cd.x86.r[X86_R_AX];  
                         if (lods) {  
                                 if (op == 0xac)  
                                         cpu->cd.x86.r[X86_R_AX] =  
                                             (cpu->cd.x86.r[X86_R_AX] & ~0xff)  
                                             | (value & 0xff);  
                                 else if (mode == 16)  
                                         cpu->cd.x86.r[X86_R_AX] =  
                                             (cpu->cd.x86.r[X86_R_AX] & ~0xffff)  
                                             | (value & 0xffff);  
                                 else  
                                         cpu->cd.x86.r[X86_R_AX] = value;  
                         }  
   
                         if (stos || movs) {  
                                 uint64_t addr = cpu->cd.x86.r[X86_R_DI];  
                                 if (mode67 == 16)  
                                         addr &= 0xffff;  
                                 if (mode67 == 32)  
                                         addr &= 0xffffffff;  
                                 cpu->cd.x86.cursegment = X86_S_ES;  
                                 if (!x86_store(cpu, addr, value, len))  
                                         return 0;  
                         }  
                         if (cmps || scas) {  
                                 uint64_t addr = cpu->cd.x86.r[X86_R_DI];  
                                 if (mode67 == 16)  
                                         addr &= 0xffff;  
                                 if (mode67 == 32)  
                                         addr &= 0xffffffff;  
                                 cpu->cd.x86.cursegment = X86_S_ES;  
                                 if (!x86_load(cpu, addr, &tmp, len))  
                                         return 0;  
   
                                 x86_calc_flags(cpu, value, tmp, len*8,  
                                     CALCFLAGS_OP_SUB);  
                         }  
   
                         if (movs || lods || cmps) {  
                                 /*  Modify esi:  */  
                                 if (mode67 == 16)  
                                         cpu->cd.x86.r[X86_R_SI] =  
                                             (cpu->cd.x86.r[X86_R_SI] & ~0xffff)  
                                             | ((cpu->cd.x86.r[X86_R_SI]+len*dir)  
                                             & 0xffff);  
                                 else {  
                                         cpu->cd.x86.r[X86_R_SI] += len*dir;  
                                         if (mode67 == 32)  
                                                 cpu->cd.x86.r[X86_R_SI] &=  
                                                     0xffffffff;  
                                 }  
                         }  
   
                         if (!lods) {  
                                 /*  Modify edi:  */  
                                 if (mode67 == 16)  
                                         cpu->cd.x86.r[X86_R_DI] =  
                                             (cpu->cd.x86.r[X86_R_DI] & ~0xffff)  
                                             | ((cpu->cd.x86.r[X86_R_DI]+len*dir)  
                                             & 0xffff);  
                                 else {  
                                         cpu->cd.x86.r[X86_R_DI] += len*dir;  
                                         if (mode67 == 32)  
                                                 cpu->cd.x86.r[X86_R_DI] &=  
                                                     0xffffffff;  
                                 }  
                         }  
   
                         if (rep) {  
                                 /*  Decrement ecx:  */  
                                 if (mode67 == 16)  
                                         cpu->cd.x86.r[X86_R_CX] =  
                                             (cpu->cd.x86.r[X86_R_CX] & ~0xffff)  
                                             | ((cpu->cd.x86.r[X86_R_CX] - 1)  
                                             & 0xffff);  
                                 else {  
                                         cpu->cd.x86.r[X86_R_CX] --;  
                                         cpu->cd.x86.r[X86_R_CX] &= 0xffffffff;  
                                 }  
                                 if (mode67 == 16 && (cpu->cd.x86.r[X86_R_CX] &  
                                     0xffff) == 0)  
                                         rep = 0;  
                                 if (mode67 != 16 &&  
                                     cpu->cd.x86.r[X86_R_CX] == 0)  
                                         rep = 0;  
   
                                 if (cmps || scas) {  
                                         if (rep == REP_REP && !(  
                                             cpu->cd.x86.rflags & X86_FLAGS_ZF))  
                                                 rep = 0;  
                                         if (rep == REP_REPNE &&  
                                             cpu->cd.x86.rflags & X86_FLAGS_ZF)  
                                                 rep = 0;  
                                 }  
                         }  
                 } while (rep);  
         } else if (op >= 0xa8 && op <= 0xa9) {          /* TEST al/[e]ax,imm */  
                 op1 = cpu->cd.x86.r[X86_R_AX];  
                 op2 = read_imm(&instr, &newpc, op==0xa8? 8 : mode);  
                 op1 &= op2;  
                 x86_calc_flags(cpu, op1, 0, op==0xa8? 8 : mode,  
                     CALCFLAGS_OP_XOR);  
                 cpu->cd.x86.rflags &= ~X86_FLAGS_CF;  
                 cpu->cd.x86.rflags &= ~X86_FLAGS_OF;  
         } else if (op >= 0xb0 && op <= 0xb3) {          /*  MOV Xl,imm  */  
                 imm = read_imm(&instr, &newpc, 8);  
                 cpu->cd.x86.r[op & 3] = (cpu->cd.x86.r[op & 3] & ~0xff)  
                     | (imm & 0xff);  
         } else if (op >= 0xb4 && op <= 0xb7) {          /*  MOV Xh,imm  */  
                 imm = read_imm(&instr, &newpc, 8);  
                 cpu->cd.x86.r[op & 3] = (cpu->cd.x86.r[op & 3] & ~0xff00)  
                     | ((imm & 0xff) << 8);  
         } else if (op >= 0xb8 && op <= 0xbf) {          /*  MOV Xx,imm  */  
                 imm = read_imm(&instr, &newpc, mode);  
                 cpu->cd.x86.r[op & 7] = modify(cpu->cd.x86.r[op & 7], imm);  
         } else if (op == 0xc0 || op == 0xc1) {          /*  Shift/Rotate  */  
                 int n = 1;  
                 instr_orig = instr;  
                 success = modrm(cpu, MODRM_READ, mode, mode67,  
                     op&1? 0 : MODRM_EIGHTBIT, &instr, &newpc, &op1, &op2);  
                 if (!success)  
                         return 0;  
                 n = read_imm(&instr, &newpc, 8);  
                 x86_shiftrotate(cpu, &op1, (*instr_orig >> 3) & 0x7,  
                     n, op&1? mode : 8);  
                 success = modrm(cpu, MODRM_WRITE_RM, mode, mode67,  
                     op&1? 0 : MODRM_EIGHTBIT, &instr_orig, NULL, &op1, &op2);  
                 if (!success)  
                         return 0;  
         } else if (op == 0xc2 || op == 0xc3) {  /*  RET near  */  
                 uint64_t popped_pc;  
                 if (!x86_pop(cpu, &popped_pc, mode))  
                         return 0;  
                 if (op == 0xc2) {  
                         imm = read_imm(&instr, &newpc, 16);  
                         cpu->cd.x86.r[X86_R_SP] = modify(cpu->cd.x86.r[  
                             X86_R_SP], cpu->cd.x86.r[X86_R_SP] + imm);  
                 }  
                 newpc = popped_pc;  
         } else if (op == 0xc4 || op == 0xc5) {          /*  LDS,LES  */  
                 instr_orig = instr;  
                 if (!modrm(cpu, MODRM_READ, mode, mode67,  
                     MODRM_JUST_GET_ADDR, &instr, &newpc, &op1, &op2))  
                         return 0;  
                 /*  op1 is the address to load from  */  
                 if (!x86_load(cpu, op1, &tmp, mode/8))  
                         return 0;  
                 op2 = tmp;  
                 if (!x86_load(cpu, op1 + mode/8, &tmp, 2))  
                         return 0;  
                 reload_segment_descriptor(cpu, op==0xc4? X86_S_ES:X86_S_DS,  
                     tmp, &newpc);  
                 if (!modrm(cpu, MODRM_WRITE_R, mode, mode67,  
                     0, &instr_orig, NULL, &op1, &op2))  
                         return 0;  
         } else if (op >= 0xc6 && op <= 0xc7) {  
                 switch ((*instr >> 3) & 0x7) {  
                 case 0: instr_orig = instr;             /*  MOV r/m, imm  */  
                         success = modrm(cpu, MODRM_READ, mode, mode67,  
                             op == 0xc6? MODRM_EIGHTBIT : 0, &instr,  
                             &newpc, &op1, &op2);  
                         if (!success)  
                                 return 0;  
                         imm = read_imm(&instr, &newpc, op == 0xc6? 8 : mode);  
                         op1 = imm;  
                         success = modrm(cpu, MODRM_WRITE_RM, mode, mode67,  
                             op == 0xc6? MODRM_EIGHTBIT : 0, &instr_orig,  
                             NULL, &op1, &op2);  
                         if (!success)  
                                 return 0;  
                         break;  
                 default:  
                         fatal("UNIMPLEMENTED 0x%02x,0x%02x\n", op, *instr);  
                         quiet_mode = 0;  
                         x86_cpu_disassemble_instr(cpu,  
                             really_orig_instr, 1|omode, 0, 0);  
                         cpu->running = 0;  
                 }  
         } else if (op == 0xc8) {        /*  ENTER  */  
                 uint64_t tmp_frame_ptr;  
                 int level;  
                 imm = read_imm(&instr, &newpc, 16);  
                 level = read_imm(&instr, &newpc, 8);  
                 if (!x86_push(cpu, cpu->cd.x86.r[X86_R_BP], mode))  
                         return 0;  
                 tmp_frame_ptr = cpu->cd.x86.r[X86_R_SP];  
                 if (level > 0) {  
                         while (level-- > 1) {  
                                 uint64_t tmpword;  
                                 cpu->cd.x86.r[X86_R_BP] = modify(  
                                     cpu->cd.x86.r[X86_R_BP],  
                                     cpu->cd.x86.r[X86_R_BP] - mode/8);  
                                 cpu->cd.x86.cursegment = X86_S_SS;  
                                 if (!x86_load(cpu, cpu->cd.x86.r[X86_R_BP],  
                                     &tmpword, mode/8)) {  
                                         fatal("TODO: load error inside"  
                                             " ENTER\n");  
                                         cpu->running = 0;  
                                         return 0;  
                                 }  
                                 if (!x86_push(cpu, tmpword, mode)) {  
                                         fatal("TODO: push error inside"  
                                             " ENTER\n");  
                                         cpu->running = 0;  
                                         return 0;  
                                 }  
                         }  
                         if (!x86_push(cpu, tmp_frame_ptr, mode))  
                                 return 0;  
                 }  
                 cpu->cd.x86.r[X86_R_BP] = modify(cpu->cd.x86.r[X86_R_BP],  
                     tmp_frame_ptr);  
                 if (mode == 16)  
                         cpu->cd.x86.r[X86_R_SP] = (cpu->cd.x86.r[X86_R_SP] &  
                             ~0xffff) | ((cpu->cd.x86.r[X86_R_SP] & 0xffff)  
                             - imm);  
                 else  
                         cpu->cd.x86.r[X86_R_SP] -= imm;  
         } else if (op == 0xc9) {        /*  LEAVE  */  
                 cpu->cd.x86.r[X86_R_SP] = cpu->cd.x86.r[X86_R_BP];  
                 if (!x86_pop(cpu, &tmp, mode)) {  
                         fatal("TODO: pop error inside LEAVE\n");  
                         cpu->running = 0;  
                         return 0;  
                 }  
                 cpu->cd.x86.r[X86_R_BP] = tmp;  
         } else if (op == 0xca || op == 0xcb) {  /*  RET far  */  
                 uint64_t tmp2;  
                 uint16_t old_tr = cpu->cd.x86.tr;  
                 if (op == 0xca)  
                         imm = read_imm(&instr, &newpc, 16);  
                 else  
                         imm = 0;  
                 if (!x86_pop(cpu, &tmp, mode))  
                         return 0;  
                 if (!x86_pop(cpu, &tmp2, mode)) {  
                         fatal("TODO: pop error inside RET\n");  
                         cpu->running = 0;  
                         return 0;  
                 }  
                 cpu->cd.x86.r[X86_R_SP] = modify(cpu->cd.x86.r[X86_R_SP],  
                     cpu->cd.x86.r[X86_R_SP] + imm);  
                 reload_segment_descriptor(cpu, X86_S_CS, tmp2, &newpc);  
                 if (cpu->cd.x86.tr == old_tr)  
                         newpc = tmp;  
         } else if (op == 0xcc) {        /*  INT3  */  
                 cpu->pc = newpc;  
                 return x86_interrupt(cpu, 3, 0);  
         } else if (op == 0xcd) {        /*  INT  */  
                 imm = read_imm(&instr, &newpc, 8);  
                 cpu->pc = newpc;  
                 return x86_interrupt(cpu, imm, 0);  
         } else if (op == 0xcf) {        /*  IRET  */  
                 uint64_t tmp2, tmp3;  
                 uint16_t old_tr = cpu->cd.x86.tr;  
                 if (!x86_pop(cpu, &tmp, mode))  
                         return 0;  
                 if (!x86_pop(cpu, &tmp2, mode))  
                         return 0;  
                 if (!x86_pop(cpu, &tmp3, mode))  
                         return 0;  
 debug("{ iret to 0x%04x:0x%08x }\n", (int)tmp2,(int)tmp);  
                 tmp2 &= 0xffff;  
                 /*  TODO: only affect some bits?  */  
                 if (mode == 16)  
                         cpu->cd.x86.rflags = (cpu->cd.x86.rflags & ~0xffff)  
                             | (tmp3 & 0xffff);  
                 else  
                         cpu->cd.x86.rflags = tmp3;  
                 /*  
                  *  In protected mode, if we're switching back from, say, an  
                  *  interrupt handler, then we should pop the old ss:esp too:  
                  */  
                 if (PROTECTED_MODE && (tmp2 & X86_PL_MASK) >  
                     (cpu->cd.x86.s[X86_S_CS] & X86_PL_MASK)) {  
                         uint64_t old_ss, old_esp;  
                         if (!x86_pop(cpu, &old_esp, mode))  
                                 return 0;  
                         if (!x86_pop(cpu, &old_ss, mode))  
                                 return 0;  
                         old_ss &= 0xffff;  
   
 printf(": : : BEFORE tmp=%016llx tmp2=%016llx tmp3=%016llx\n",  
 (long long)tmp, (long long)tmp2, (long long)tmp3);  
 /* x86_cpu_register_dump(cpu, 1, 1); */  
   
                         reload_segment_descriptor(cpu, X86_S_SS, old_ss,  
                             &newpc);  
                         cpu->cd.x86.r[X86_R_SP] = old_esp;  
   
 /* printf(": : : AFTER\n");  
 x86_cpu_register_dump(cpu, 1, 1); */  
   
                         /*  AFTER popping ss, check that the pl of  
                             the popped ss is the same as tmp2 (the new  
                             pl in cs)!  */  
                         if ((old_ss & X86_PL_MASK) != (tmp2 & X86_PL_MASK))  
                                 fatal("WARNING: iret: popped ss' pl = %i,"  
                                     " different from cs' pl = %i\n",  
                                     (int)(old_ss & X86_PL_MASK),  
                                     (int)(tmp2 & X86_PL_MASK));  
                 }  
                 reload_segment_descriptor(cpu, X86_S_CS, tmp2, &newpc);  
                 if (cpu->cd.x86.tr == old_tr)  
                         newpc = tmp;  
         } else if (op >= 0xd0 && op <= 0xd3) {  
                 int n = 1;  
                 instr_orig = instr;  
                 success = modrm(cpu, MODRM_READ, mode, mode67,  
                     op&1? 0 : MODRM_EIGHTBIT, &instr, &newpc, &op1, &op2);  
                 if (!success)  
                         return 0;  
                 if (op >= 0xd2)  
                         n = cpu->cd.x86.r[X86_R_CX];  
                 x86_shiftrotate(cpu, &op1, (*instr_orig >> 3) & 0x7,  
                     n, op&1? mode : 8);  
                 success = modrm(cpu, MODRM_WRITE_RM, mode, mode67,  
                     op&1? 0 : MODRM_EIGHTBIT, &instr_orig, NULL, &op1, &op2);  
                 if (!success)  
                         return 0;  
         } else if (op == 0xd4) {        /*  AAM  */  
                 int al = cpu->cd.x86.r[X86_R_AX] & 0xff;  
                 /*  al should be in the range 0..81  */  
                 int high;  
                 imm = read_imm(&instr, &newpc, 8);  
                 if (imm == 0) {  
                         fatal("[ x86: \"aam 0\" ]\n");  
                         cpu->running = 0;  
                 } else {  
                         high = al / imm;  
                         al %= imm;  
                         cpu->cd.x86.r[X86_R_AX] = (cpu->cd.x86.r[X86_R_AX] &  
                             ~0xffff) | al | ((high & 0xff) << 8);  
                         x86_calc_flags(cpu, cpu->cd.x86.r[X86_R_AX],  
                             0, 8, CALCFLAGS_OP_XOR);  
                 }  
         } else if (op == 0xd5) {        /*  AAD  */  
                 int al = cpu->cd.x86.r[X86_R_AX] & 0xff;  
                 int ah = (cpu->cd.x86.r[X86_R_AX] >> 8) & 0xff;  
                 imm = read_imm(&instr, &newpc, 8);  
                 cpu->cd.x86.r[X86_R_AX] = (cpu->cd.x86.r[X86_R_AX] & ~0xffff)  
                     | ((al + 10*ah) & 0xff);  
                 x86_calc_flags(cpu, cpu->cd.x86.r[X86_R_AX],  
                     0, 8, CALCFLAGS_OP_XOR);  
         } else if (op == 0xd7) {                /*  XLAT  */  
                 uint64_t addr = cpu->cd.x86.r[X86_R_BX];  
                 if (mode == 16)  
                         addr &= 0xffff;  
                 addr += (cpu->cd.x86.r[X86_R_AX] & 0xff);  
                 if (!x86_load(cpu, addr, &tmp, 1))  
                         return 0;  
                 cpu->cd.x86.r[X86_R_AX] = (cpu->cd.x86.r[X86_R_AX] & ~0xff)  
                     | (tmp & 0xff);  
         } else if (op == 0xd9) {  
                 int subop = (*instr >> 3) & 7;  
                 imm = *instr;  
                 if (subop == 5) {                       /*  FLDCW mem16  */  
                         if (!modrm(cpu, MODRM_READ, 16, mode67, 0, &instr,  
                             &newpc, &op1, &op2))  
                                 return 0;  
                         cpu->cd.x86.fpu_cw = op1;  
                 } else if (subop == 7) {                /*  FSTCW mem16  */  
                         op1 = cpu->cd.x86.fpu_cw;  
                         if (!modrm(cpu, MODRM_WRITE_RM, 16, mode67, 0, &instr,  
                             &newpc, &op1, &op2))  
                                 return 0;  
                 } else {  
                         fatal("UNIMPLEMENTED 0x%02x,0x%02x\n", op, *instr);  
                         quiet_mode = 0;  
                         x86_cpu_disassemble_instr(cpu, really_orig_instr,  
                             1 | omode, 0, 0);  
                         cpu->running = 0;  
                 }  
         } else if (op == 0xdb) {  
                 imm = *instr;  
                 if (imm == 0xe2) {                      /*  FCLEX  */  
                         read_imm(&instr, &newpc, 8);  
                         /*  TODO: actually clear exceptions  */  
                 } else if (imm == 0xe3) {               /*  FINIT  */  
                         read_imm(&instr, &newpc, 8);  
                         /*  TODO: actually init?  */  
                 } else if (imm == 0xe4) {               /*  FSETPM  */  
                         read_imm(&instr, &newpc, 8);  
                 } else {  
                         fatal("UNIMPLEMENTED 0x%02x,0x%02x\n", op, *instr);  
                         quiet_mode = 0;  
                         x86_cpu_disassemble_instr(cpu, really_orig_instr,  
                             1 | omode, 0, 0);  
                         cpu->running = 0;  
                 }  
         } else if (op == 0xdd) {  
                 int subop = (*instr >> 3) & 7;  
                 imm = *instr;  
                 if (subop == 7) {               /*  FSTSW mem16  */  
                         op1 = cpu->cd.x86.fpu_sw;  
                         if (!modrm(cpu, MODRM_WRITE_RM, 16, mode67, 0, &instr,  
                             &newpc, &op1, &op2))  
                                 return 0;  
                 } else {  
                         fatal("UNIMPLEMENTED 0x%02x,0x%02x\n", op, *instr);  
                         quiet_mode = 0;  
                         x86_cpu_disassemble_instr(cpu, really_orig_instr,  
                             1 | omode, 0, 0);  
                         cpu->running = 0;  
                 }  
         } else if (op == 0xdf) {  
                 imm = *instr;  
                 if (imm == 0xe0) {              /*  FSTSW  */  
                         read_imm(&instr, &newpc, 8);  
                         cpu->cd.x86.r[X86_R_AX] = (cpu->cd.x86.r[X86_R_AX] &  
                             ~0xffff) | (cpu->cd.x86.fpu_sw & 0xffff);  
                 } else {  
                         fatal("UNIMPLEMENTED 0x%02x,0x%02x\n", op, *instr);  
                         quiet_mode = 0;  
                         x86_cpu_disassemble_instr(cpu, really_orig_instr,  
                             1 | omode, 0, 0);  
                         cpu->running = 0;  
                 }  
         } else if (op == 0xe4 || op == 0xe5     /*  IN imm,AL or AX/EAX  */  
             || op == 0x6c || op == 0x6d) {      /*  INSB or INSW/INSD  */  
                 unsigned char databuf[8];  
                 int port_nr, ins = 0, len = 1, dir = 1;  
                 if (op == 0x6c || op == 0x6d) {  
                         port_nr = cpu->cd.x86.r[X86_R_DX] & 0xffff;  
                         ins = 1;  
                 } else  
                         port_nr = read_imm(&instr, &newpc, 8);  
                 if (op & 1)  
                         len = mode/8;  
                 if (cpu->cd.x86.rflags & X86_FLAGS_DF)  
                         dir = -1;  
                 do {  
                         cpu->memory_rw(cpu, cpu->mem, X86_IO_BASE+port_nr,  
                             &databuf[0], len, MEM_READ, CACHE_NONE | PHYSICAL);  
   
                         if (ins) {  
                                 uint64_t addr = cpu->cd.x86.r[X86_R_DI];  
                                 uint32_t value = databuf[0] + (databuf[1] << 8)  
                                     + (databuf[2] << 16) + (databuf[3] << 24);  
                                 if (mode67 == 16)  
                                         addr &= 0xffff;  
                                 if (mode67 == 32)  
                                         addr &= 0xffffffff;  
                                 cpu->cd.x86.cursegment = X86_S_ES;  
                                 if (!x86_store(cpu, addr, value, len))  
                                         return 0;  
   
                                 /*  Advance (e)di:  */  
                                 if (mode67 == 16)  
                                         cpu->cd.x86.r[X86_R_DI] =  
                                             (cpu->cd.x86.r[X86_R_DI] & ~0xffff)  
                                             | ((cpu->cd.x86.r[X86_R_DI]+len*dir)  
                                             & 0xffff);  
                                 else {  
                                         cpu->cd.x86.r[X86_R_DI] += len*dir;  
                                         if (mode67 == 32)  
                                                 cpu->cd.x86.r[X86_R_DI] &=  
                                                     0xffffffff;  
                                 }  
   
                                 if (rep) {  
                                         /*  Decrement ecx:  */  
                                         if (mode67 == 16)  
                                                 cpu->cd.x86.r[X86_R_CX] =  
                                                     (cpu->cd.x86.r[X86_R_CX] &  
                                                     ~0xffff) | ((cpu->cd.x86.r[  
                                                     X86_R_CX] - 1) & 0xffff);  
                                         else {  
                                                 cpu->cd.x86.r[X86_R_CX] --;  
                                                 cpu->cd.x86.r[X86_R_CX] &=  
                                                     0xffffffff;  
                                         }  
                                         if (mode67 == 16 && (cpu->cd.x86.r[  
                                             X86_R_CX] & 0xffff) == 0)  
                                                 rep = 0;  
                                         if (mode67 != 16 &&  
                                             cpu->cd.x86.r[X86_R_CX] == 0)  
                                                 rep = 0;  
                                 }  
                         } else {  
                                 if (len == 1)  
                                         cpu->cd.x86.r[X86_R_AX] =  
                                             (cpu->cd.x86.r[X86_R_AX] &  
                                             ~0xff) | databuf[0];  
                                 else if (len == 2)  
                                         cpu->cd.x86.r[X86_R_AX] =  
                                             (cpu->cd.x86.r[X86_R_AX] & ~0xffff)  
                                             | databuf[0] | (databuf[1] << 8);  
                                 else if (len == 4)  
                                         cpu->cd.x86.r[X86_R_AX] = databuf[0] |  
                                             (databuf[1] << 8) | (databuf[2]  
                                             << 16) | (databuf[3] << 24);  
                         }  
                 } while (rep);  
         } else if (op == 0xe6 || op == 0xe7     /*  OUT imm,AL or AX/EAX  */  
             || op == 0x6e || op == 0x6f) {      /*  OUTSB or OUTSW/OUTSD  */  
                 unsigned char databuf[8];  
                 int port_nr, outs = 0, len = 1, dir = 1;  
                 if (op == 0x6e || op == 0x6f) {  
                         port_nr = cpu->cd.x86.r[X86_R_DX] & 0xffff;  
                         outs = 1;  
                 } else  
                         port_nr = read_imm(&instr, &newpc, 8);  
                 if (op & 1)  
                         len = mode/8;  
                 if (cpu->cd.x86.rflags & X86_FLAGS_DF)  
                         dir = -1;  
                 do {  
                         if (outs) {  
                                 int i;  
                                 uint64_t addr = cpu->cd.x86.r[X86_R_DI];  
                                 uint64_t value;  
                                 if (mode67 == 16)  
                                         addr &= 0xffff;  
                                 if (mode67 == 32)  
                                         addr &= 0xffffffff;  
                                 cpu->cd.x86.cursegment = X86_S_ES;  
                                 if (!x86_load(cpu, addr, &value, len))  
                                         return 0;  
   
                                 /*  Advance (e)di:  */  
                                 if (mode67 == 16)  
                                         cpu->cd.x86.r[X86_R_DI] =  
                                             (cpu->cd.x86.r[X86_R_DI] & ~0xffff)  
                                             | ((cpu->cd.x86.r[X86_R_DI]+len*dir)  
                                             & 0xffff);  
                                 else {  
                                         cpu->cd.x86.r[X86_R_DI] += len*dir;  
                                         if (mode67 == 32)  
                                                 cpu->cd.x86.r[X86_R_DI] &=  
                                                     0xffffffff;  
                                 }  
   
                                 for (i=0; i<8; i++)  
                                         databuf[i] = value >> (i*8);  
   
                                 if (rep) {  
                                         /*  Decrement ecx:  */  
                                         if (mode67 == 16)  
                                                 cpu->cd.x86.r[X86_R_CX] =  
                                                     (cpu->cd.x86.r[X86_R_CX] &  
                                                     ~0xffff) | ((cpu->cd.x86.r[  
                                                     X86_R_CX] - 1) & 0xffff);  
                                         else {  
                                                 cpu->cd.x86.r[X86_R_CX] --;  
                                                 cpu->cd.x86.r[X86_R_CX] &=  
                                                     0xffffffff;  
                                         }  
                                         if (mode67 == 16 && (cpu->cd.x86.r[  
                                             X86_R_CX] & 0xffff) == 0)  
                                                 rep = 0;  
                                         if (mode67 != 16 &&  
                                             cpu->cd.x86.r[X86_R_CX] == 0)  
                                                 rep = 0;  
                                 }  
                         } else {  
                                 int i;  
                                 for (i=0; i<8; i++)  
                                         databuf[i] = cpu->cd.x86.r[X86_R_AX]  
                                             >> (i*8);  
                         }  
   
                         cpu->memory_rw(cpu, cpu->mem, X86_IO_BASE+port_nr,  
                             &databuf[0], len, MEM_WRITE, CACHE_NONE | PHYSICAL);  
                 } while (rep);  
         } else if (op == 0xe8 || op == 0xe9) {  /*  CALL/JMP near  */  
                 imm = read_imm(&instr, &newpc, mode);  
                 if (mode == 16)  
                         imm = (int16_t)imm;  
                 if (mode == 32)  
                         imm = (int32_t)imm;  
                 if (op == 0xe8) {  
                         if (!x86_push(cpu, newpc, mode))  
                                 return 0;  
                 }  
                 newpc += imm;  
         } else if (op == 0xea) {        /*  JMP seg:ofs  */  
                 uint16_t old_tr = cpu->cd.x86.tr;  
                 imm = read_imm(&instr, &newpc, mode);  
                 imm2 = read_imm(&instr, &newpc, 16);  
                 reload_segment_descriptor(cpu, X86_S_CS, imm2, &newpc);  
                 if (cpu->cd.x86.tr == old_tr)  
                         newpc = imm;  
         } else if ((op >= 0xe0 && op <= 0xe3) || op == 0xeb) {  /*  LOOP,JMP */  
                 int perform_jump = 0;  
                 imm = read_imm(&instr, &newpc, 8);  
                 switch (op) {  
                 case 0xe0:      /*  loopnz  */  
                 case 0xe1:      /*  loopz  */  
                 case 0xe2:      /*  loop  */  
                         /*  NOTE: address size attribute, not operand size?  */  
                         if (mode67 == 16)  
                                 cpu->cd.x86.r[X86_R_CX] = (~0xffff &  
                                     cpu->cd.x86.r[X86_R_CX]) |  
                                     ((cpu->cd.x86.r[X86_R_CX] - 1) & 0xffff);  
                         else  
                                 cpu->cd.x86.r[X86_R_CX] --;  
                         if (mode67 == 16 && (cpu->cd.x86.r[X86_R_CX] &  
                             0xffff) != 0)  
                                 perform_jump = 1;  
                         if (mode67 == 32 && cpu->cd.x86.r[X86_R_CX] != 0)  
                                 perform_jump = 1;  
                         if (op == 0xe0 && cpu->cd.x86.rflags & X86_FLAGS_ZF)  
                                 perform_jump = 0;  
                         if (op == 0xe1 && (!cpu->cd.x86.rflags & X86_FLAGS_ZF))  
                                 perform_jump = 0;  
                         break;  
                 case 0xe3:      /*  jcxz/jecxz  */  
                         if (mode67 == 16 && (cpu->cd.x86.r[X86_R_CX] & 0xffff)  
                             == 0)  
                                 perform_jump = 1;  
                         if (mode67 != 16 && (cpu->cd.x86.r[X86_R_CX] &  
                             0xffffffffULL) == 0)  
                                 perform_jump = 1;  
                         break;  
                 case 0xeb:      /*  jmp  */  
                         perform_jump = 1;  
                         break;  
                 }  
                 if (perform_jump)  
                         newpc += (signed char)imm;  
         } else if (op == 0xec || op == 0xed) {  /*  IN DX,AL or AX/EAX  */  
                 unsigned char databuf[8];  
                 cpu->memory_rw(cpu, cpu->mem, X86_IO_BASE +  
                     (cpu->cd.x86.r[X86_R_DX] & 0xffff), &databuf[0],  
                     op == 0xec? 1 : (mode/8), MEM_READ, CACHE_NONE | PHYSICAL);  
                 if (op == 0xec)  
                         cpu->cd.x86.r[X86_R_AX] = (cpu->cd.x86.r[X86_R_AX] &  
                             ~0xff) | databuf[0];  
                 else if (op == 0xed && mode == 16)  
                         cpu->cd.x86.r[X86_R_AX] = (cpu->cd.x86.r[X86_R_AX] &  
                             ~0xffff) | databuf[0] | (databuf[1] << 8);  
                 else if (op == 0xed && mode == 32)  
                         cpu->cd.x86.r[X86_R_AX] = databuf[0] |  
                             (databuf[1] << 8) | (databuf[2] << 16) |  
                             (databuf[3] << 24);  
         } else if (op == 0xee || op == 0xef) {  /*  OUT DX,AL or AX/EAX  */  
                 unsigned char databuf[8];  
                 databuf[0] = cpu->cd.x86.r[X86_R_AX];  
                 if (op == 0xef) {  
                         databuf[1] = cpu->cd.x86.r[X86_R_AX] >> 8;  
                         if (mode >= 32) {  
                                 databuf[2] = cpu->cd.x86.r[X86_R_AX] >> 16;  
                                 databuf[3] = cpu->cd.x86.r[X86_R_AX] >> 24;  
                         }  
                 }  
                 cpu->memory_rw(cpu, cpu->mem, X86_IO_BASE +  
                     (cpu->cd.x86.r[X86_R_DX] & 0xffff), &databuf[0],  
                     op == 0xee? 1 : (mode/8), MEM_WRITE, CACHE_NONE | PHYSICAL);  
         } else if (op == 0xf4) {        /*  HLT  */  
                 cpu->cd.x86.halted = 1;  
         } else if (op == 0xf5) {        /*  CMC  */  
                 cpu->cd.x86.rflags ^= X86_FLAGS_CF;  
         } else if (op == 0xf8) {        /*  CLC  */  
                 cpu->cd.x86.rflags &= ~X86_FLAGS_CF;  
         } else if (op == 0xf9) {        /*  STC  */  
                 cpu->cd.x86.rflags |= X86_FLAGS_CF;  
         } else if (op == 0xfa) {        /*  CLI  */  
                 cpu->cd.x86.rflags &= ~X86_FLAGS_IF;  
         } else if (op == 0xfb) {        /*  STI  */  
                 cpu->cd.x86.rflags |= X86_FLAGS_IF;  
         } else if (op == 0xfc) {        /*  CLD  */  
                 cpu->cd.x86.rflags &= ~X86_FLAGS_DF;  
         } else if (op == 0xfd) {        /*  STD  */  
                 cpu->cd.x86.rflags |= X86_FLAGS_DF;  
         } else if (op == 0xf6 || op == 0xf7) {          /*  MUL, DIV etc  */  
                 uint64_t res;  
                 int unsigned_op = 1;  
                 switch ((*instr >> 3) & 0x7) {  
                 case 0: /*  test  */  
                         success = modrm(cpu, MODRM_READ, mode, mode67,  
                             op == 0xf6? MODRM_EIGHTBIT : 0, &instr,  
                             &newpc, &op1, &op2);  
                         if (!success)  
                                 return 0;  
                         op2 = read_imm(&instr, &newpc, op==0xf6? 8 : mode);  
                         op1 &= op2;  
                         x86_calc_flags(cpu, op1, 0, op==0xf6? 8 : mode,  
                             CALCFLAGS_OP_XOR);  
                         break;  
                 case 2: /*  not  */  
                 case 3: /*  neg  */  
                         instr_orig = instr;  
                         success = modrm(cpu, MODRM_READ, mode, mode67,  
                             op == 0xf6? MODRM_EIGHTBIT : 0, &instr,  
                             &newpc, &op1, &op2);  
                         if (!success)  
                                 return 0;  
                         switch ((*instr_orig >> 3) & 0x7) {  
                         case 2: op1 ^= 0xffffffffffffffffULL; break;  
                         case 3: x86_calc_flags(cpu, 0, op1,  
                                     op == 0xf6? 8 : mode, CALCFLAGS_OP_SUB);  
                                 op1 = 0 - op1;  
                                 cpu->cd.x86.rflags &= ~X86_FLAGS_CF;  
                                 if (op1 != 0)  
                                         cpu->cd.x86.rflags |= X86_FLAGS_CF;  
                                 break;  
                         }  
                         success = modrm(cpu, MODRM_WRITE_RM, mode, mode67,  
                             op == 0xf6? MODRM_EIGHTBIT : 0, &instr_orig,  
                             NULL, &op1, &op2);  
                         if (!success)  
                                 return 0;  
                         break;  
                 case 5: /*  imul  */  
                         unsigned_op = 0;  
                 case 4: /*  mul  */  
                         success = modrm(cpu, MODRM_READ, mode, mode67,  
                             op == 0xf6? MODRM_EIGHTBIT : 0, &instr,  
                             &newpc, &op1, &op2);  
                         if (!success)  
                                 return 0;  
                         cpu->cd.x86.rflags &= ~X86_FLAGS_CF;  
                         cpu->cd.x86.rflags &= ~X86_FLAGS_OF;  
                         if (op == 0xf6) {  
                                 if (unsigned_op)  
                                         res = (cpu->cd.x86.r[X86_R_AX] & 0xff)  
                                             * (op1 & 0xff);  
                                 else  
                                         res = (int16_t)(signed char)(cpu->cd.  
                                             x86.r[X86_R_AX] & 0xff) * (int16_t)  
                                             (signed char)(op1 & 0xff);  
                                 cpu->cd.x86.r[X86_R_AX] = (cpu->cd.x86.r[  
                                     X86_R_AX] & ~0xffff) | (res & 0xffff);  
                                 if ((res & 0xffff) >= 0x100)  
                                         cpu->cd.x86.rflags |= X86_FLAGS_CF  
                                             | X86_FLAGS_OF;  
                         } else if (mode == 16) {  
                                 if (unsigned_op)  
                                         res = (cpu->cd.x86.r[X86_R_AX] & 0xffff)  
                                             * (op1 & 0xffff);  
                                 else  
                                         res = (int32_t)(int16_t)(cpu->cd.x86.r[  
                                             X86_R_AX] & 0xffff) * (int32_t)  
                                             (int16_t)(op1 & 0xffff);  
                                 cpu->cd.x86.r[X86_R_AX] = modify(cpu->  
                                     cd.x86.r[X86_R_AX], res & 0xffff);  
                                 cpu->cd.x86.r[X86_R_DX] = modify(cpu->cd.x86  
                                     .r[X86_R_DX], (res>>16) & 0xffff);  
                                 if ((res & 0xffffffff) >= 0x10000)  
                                         cpu->cd.x86.rflags |= X86_FLAGS_CF  
                                             | X86_FLAGS_OF;  
                         } else if (mode == 32) {  
                                 if (unsigned_op)  
                                         res = (cpu->cd.x86.r[X86_R_AX] &  
                                             0xffffffff) * (op1 & 0xffffffff);  
                                 else  
                                         res = (int64_t)(int32_t)(cpu->cd.x86.r[  
                                             X86_R_AX] & 0xffffffff) * (int64_t)  
                                             (int32_t)(op1 & 0xffffffff);  
                                 cpu->cd.x86.r[X86_R_AX] = res & 0xffffffff;  
                                 cpu->cd.x86.r[X86_R_DX] = (res >> 32) &  
                                     0xffffffff;  
                                 if (res >= 0x100000000ULL)  
                                         cpu->cd.x86.rflags |= X86_FLAGS_CF  
                                             | X86_FLAGS_OF;  
                         }  
                         break;  
                 case 7: /*  idiv  */  
                         unsigned_op = 0;  
                 case 6: /*  div  */  
                         success = modrm(cpu, MODRM_READ, mode, mode67,  
                             op == 0xf6? MODRM_EIGHTBIT : 0, &instr,  
                             &newpc, &op1, &op2);  
                         if (!success)  
                                 return 0;  
                         if (op1 == 0) {  
                                 fatal("TODO: division by zero\n");  
                                 cpu->running = 0;  
                                 break;  
                         }  
                         if (op == 0xf6) {  
                                 int al, ah;  
                                 if (unsigned_op) {  
                                         al = (cpu->cd.x86.r[X86_R_AX] &  
                                             0xffff) / op1;  
                                         ah = (cpu->cd.x86.r[X86_R_AX] &  
                                             0xffff) % op1;  
                                 } else {  
                                         al = (int16_t)(cpu->cd.x86.r[  
                                             X86_R_AX] & 0xffff) / (int16_t)op1;  
                                         ah = (int16_t)(cpu->cd.x86.r[  
                                             X86_R_AX] & 0xffff) % (int16_t)op1;  
                                 }  
                                 cpu->cd.x86.r[X86_R_AX] = modify(  
                                     cpu->cd.x86.r[X86_R_AX], (ah<<8) + al);  
                         } else if (mode == 16) {  
                                 uint64_t a = (cpu->cd.x86.r[X86_R_AX] & 0xffff)  
                                     + ((cpu->cd.x86.r[X86_R_DX] & 0xffff)<<16);  
                                 uint32_t ax, dx;  
                                 if (unsigned_op) {  
                                         ax = a / op1, dx = a % op1;  
                                 } else {  
                                         ax = (int32_t)a / (int32_t)op1;  
                                         dx = (int32_t)a % (int32_t)op1;  
                                 }  
                                 cpu->cd.x86.r[X86_R_AX] = modify(  
                                     cpu->cd.x86.r[X86_R_AX], ax);  
                                 cpu->cd.x86.r[X86_R_DX] = modify(  
                                     cpu->cd.x86.r[X86_R_DX], dx);  
                         } else if (mode == 32) {  
                                 uint64_t a = (cpu->cd.x86.r[X86_R_AX] &  
                                     0xffffffffULL) + ((cpu->cd.x86.r[  
                                     X86_R_DX] & 0xffffffffULL) << 32);  
                                 uint32_t eax, edx;  
                                 if (unsigned_op) {  
                                         eax = (uint64_t)a / (uint32_t)op1;  
                                         edx = (uint64_t)a % (uint32_t)op1;  
                                 } else {  
                                         eax = (int64_t)a / (int32_t)op1;  
                                         edx = (int64_t)a % (int32_t)op1;  
                                 }  
                                 cpu->cd.x86.r[X86_R_AX] = eax;  
                                 cpu->cd.x86.r[X86_R_DX] = edx;  
                         }  
                         break;  
                 default:  
                         fatal("UNIMPLEMENTED 0x%02x,0x%02x\n", op, *instr);  
                         quiet_mode = 0;  
                         x86_cpu_disassemble_instr(cpu,  
                             really_orig_instr, 1|omode, 0, 0);  
                         cpu->running = 0;  
                 }  
         } else if (op == 0xfe || op == 0xff) {          /*  INC, DEC etc  */  
                 int old_cf;  
                 switch ((*instr >> 3) & 0x7) {  
                 case 0:  
                 case 1: instr_orig = instr;  
                         success = modrm(cpu, MODRM_READ, mode, mode67,  
                             op == 0xfe? MODRM_EIGHTBIT : 0, &instr,  
                             &newpc, &op1, &op2);  
                         if (!success)  
                                 return 0;  
                         old_cf = cpu->cd.x86.rflags & X86_FLAGS_CF;  
                         switch ((*instr_orig >> 3) & 0x7) {  
                         case 0: x86_calc_flags(cpu, op1, 1, op==0xfe? 8 : mode,  
                                     CALCFLAGS_OP_ADD);  
                                 op1 ++;  
                                 break; /* inc */  
                         case 1: x86_calc_flags(cpu, op1, 1, op==0xfe? 8 : mode,  
                                     CALCFLAGS_OP_SUB);  
                                 op1 --;  
                                 break; /* dec */  
                         }  
                         success = modrm(cpu, MODRM_WRITE_RM, mode, mode67,  
                             op == 0xfe? MODRM_EIGHTBIT : 0, &instr_orig,  
                             NULL, &op1, &op2);  
                         if (!success)  
                                 return 0;  
                         /*  preserve CF:  */  
                         cpu->cd.x86.rflags &= ~X86_FLAGS_CF;  
                         cpu->cd.x86.rflags |= old_cf;  
                         break;  
                 case 2: if (op == 0xfe) {  
                                 fatal("UNIMPLEMENTED 0x%02x,0x%02x\n", op,  
                                     *instr);  
                                 quiet_mode = 0;  
                                 x86_cpu_disassemble_instr(cpu,  
                                     really_orig_instr, 1|omode, 0, 0);  
                                 cpu->running = 0;  
                         } else {  
                                 success = modrm(cpu, MODRM_READ, mode, mode67,  
                                     0, &instr, &newpc, &op1, &op2);  
                                 if (!success)  
                                         return 0;  
                                 /*  Push return [E]IP  */  
                                 if (!x86_push(cpu, newpc, mode))  
                                         return 0;  
                                 newpc = op1;  
                         }  
                         break;  
                 case 3: if (op == 0xfe) {  
                                 fatal("UNIMPLEMENTED 0x%02x,0x%02x\n", op,  
                                     *instr);  
                                 quiet_mode = 0;  
                                 x86_cpu_disassemble_instr(cpu,  
                                     really_orig_instr, 1|omode, 0, 0);  
                                 cpu->running = 0;  
                         } else {  
                                 uint16_t old_tr = cpu->cd.x86.tr;  
                                 uint64_t tmp1, tmp2;  
                                 success = modrm(cpu, MODRM_READ, mode, mode67,  
                                     MODRM_JUST_GET_ADDR, &instr,  
                                     &newpc, &op1, &op2);  
                                 if (!success)  
                                         return 0;  
                                 /*  Load a far address from op1:  */  
                                 if (!x86_load(cpu, op1, &tmp1, mode/8))  
                                         return 0;  
                                 if (!x86_load(cpu, op1 + (mode/8), &tmp2, 2))  
                                         return 0;  
                                 /*  Push return CS:[E]IP  */  
                                 if (!x86_push(cpu, cpu->cd.x86.s[X86_S_CS],  
                                     mode))  
                                         return 0;  
                                 if (!x86_push(cpu, newpc, mode)) {  
                                         fatal("TODO: push failed, call "  
                                             "far indirect?\n");  
                                         cpu->running = 0;  
                                         return 0;  
                                 }  
                                 reload_segment_descriptor(cpu, X86_S_CS,  
                                     tmp2, &newpc);  
                                 if (cpu->cd.x86.tr == old_tr)  
                                         newpc = tmp1;  
                         }  
                         break;  
                 case 4: if (op == 0xfe) {  
                                 fatal("UNIMPLEMENTED 0x%02x,0x%02x\n", op,  
                                     *instr);  
                                 quiet_mode = 0;  
                                 x86_cpu_disassemble_instr(cpu,  
                                     really_orig_instr, 1|omode, 0, 0);  
                                 cpu->running = 0;  
                         } else {  
                                 success = modrm(cpu, MODRM_READ, mode, mode67,  
                                     0, &instr, &newpc, &op1, &op2);  
                                 if (!success)  
                                         return 0;  
                                 newpc = op1;  
                         }  
                         break;  
                 case 5: if (op == 0xfe) {  
                                 fatal("UNIMPLEMENTED 0x%02x,0x%02x\n", op,  
                                     *instr);  
                                 quiet_mode = 0;  
                                 x86_cpu_disassemble_instr(cpu,  
                                     really_orig_instr, 1|omode, 0, 0);  
                                 cpu->running = 0;  
                         } else {  
                                 uint16_t old_tr = cpu->cd.x86.tr;  
                                 uint64_t tmp1, tmp2;  
                                 success = modrm(cpu, MODRM_READ, mode, mode67,  
                                     MODRM_JUST_GET_ADDR, &instr,  
                                     &newpc, &op1, &op2);  
                                 if (!success)  
                                         return 0;  
                                 /*  Load a far address from op1:  */  
                                 if (!x86_load(cpu, op1, &tmp1, mode/8))  
                                         return 0;  
                                 if (!x86_load(cpu, op1 + (mode/8), &tmp2, 2))  
                                         return 0;  
                                 reload_segment_descriptor(cpu, X86_S_CS,  
                                     tmp2, &newpc);  
                                 if (cpu->cd.x86.tr == old_tr)  
                                         newpc = tmp1;  
                         }  
                         break;  
                 case 6: if (op == 0xfe) {  
                                 fatal("UNIMPLEMENTED 0x%02x,0x%02x\n", op,  
                                     *instr);  
                                 quiet_mode = 0;  
                                 x86_cpu_disassemble_instr(cpu,  
                                     really_orig_instr, 1|omode, 0, 0);  
                                 cpu->running = 0;  
                         } else {  
                                 instr_orig = instr;  
                                 success = modrm(cpu, MODRM_READ, mode, mode67,  
                                     0, &instr, &newpc, &op1, &op2);  
                                 if (!success)  
                                         return 0;  
                                 if (!x86_push(cpu, op1, mode))  
                                         return 0;  
                         }  
                         break;  
                 default:  
                         fatal("UNIMPLEMENTED 0x%02x,0x%02x\n", op, *instr);  
                         quiet_mode = 0;  
                         x86_cpu_disassemble_instr(cpu,  
                             really_orig_instr, 1|omode, 0, 0);  
                         cpu->running = 0;  
                 }  
         } else {  
                 fatal("x86_cpu_run_instr(): unimplemented opcode 0x%02x"  
                     " at ", op); print_csip(cpu); fatal("\n");  
                 quiet_mode = 0;  
                 x86_cpu_disassemble_instr(cpu,  
                     really_orig_instr, 1|omode, 0, 0);  
                 cpu->running = 0;  
                 return 0;  
         }  
   
         /*  Wrap-around and update [E]IP:  */  
         cpu->pc = newpc & (((uint64_t)1 << (cpu->cd.x86.descr_cache[  
             X86_S_CS].default_op_size)) - 1);  
   
         if (trap_flag_was_set) {  
                 if (REAL_MODE) {  
                         x86_interrupt(cpu, 1, 0);  
                 } else {  
                         fatal("TRAP flag in protected mode?\n");  
                         cpu->running = 0;  
                 }  
         }  
   
         return 1;  
 }  
   
3151    
 #define CPU_RUN         x86_cpu_run  
 #define CPU_RINSTR      x86_cpu_run_instr  
 #define CPU_RUN_X86  
 #include "cpu_run.c"  
 #undef CPU_RINSTR  
 #undef CPU_RUN_X86  
 #undef CPU_RUN  
3152    
3153    #include "tmp_x86_tail.c"
3154    
 /*  
  *  x86_cpu_family_init():  
  *  
  *  Fill in the cpu_family struct for x86.  
  */  
 int x86_cpu_family_init(struct cpu_family *fp)  
 {  
         fp->name = "x86";  
         fp->cpu_new = x86_cpu_new;  
         fp->list_available_types = x86_cpu_list_available_types;  
         fp->register_match = x86_cpu_register_match;  
         fp->disassemble_instr = x86_cpu_disassemble_instr;  
         fp->register_dump = x86_cpu_register_dump;  
         fp->run = x86_cpu_run;  
         fp->dumpinfo = x86_cpu_dumpinfo;  
         /*  fp->show_full_statistics = x86_cpu_show_full_statistics;  */  
         /*  fp->tlbdump = x86_cpu_tlbdump;  */  
         fp->interrupt = x86_cpu_interrupt;  
         fp->interrupt_ack = x86_cpu_interrupt_ack;  
         return 1;  
 }  
3155    
3156  #endif  /*  ENABLE_X86  */  #endif  /*  ENABLE_X86  */

Legend:
Removed from v.6  
changed lines
  Added in v.12

  ViewVC Help
Powered by ViewVC 1.1.26