25 |
* SUCH DAMAGE. |
* SUCH DAMAGE. |
26 |
* |
* |
27 |
* |
* |
28 |
* $Id: cpu_x86.c,v 1.163 2005/06/26 22:23:42 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 |
* |
* |
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; |
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) ( \ |
2443 |
} |
} |
2444 |
|
|
2445 |
|
|
2446 |
|
|
2447 |
/* |
/* |
2448 |
* x86_cpuid(): |
* x86_cpuid(): |
2449 |
* |
* |
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 |
*/ |
*/ |
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 */ |