diff --git a/src/cpu/808x.c b/src/cpu/808x.c index 4e2e830d0..7908160e8 100644 --- a/src/cpu/808x.c +++ b/src/cpu/808x.c @@ -51,7 +51,8 @@ uint32_t custom_nmi_vector = 0x00000000; static uint8_t pfq[6]; /* Variables to aid with the prefetch queue operation. */ -static int biu_cycles = 0, pfq_pos = 0; +static int biu_cycles = 0; +int pfq_pos = 0; /* The IP equivalent of the current prefetch queue position. */ static uint16_t pfq_ip; @@ -66,7 +67,7 @@ static int cpu_alu_op, pfq_size; static uint32_t cpu_src = 0, cpu_dest = 0; static uint32_t cpu_data = 0; -static uint16_t last_addr = 0x0000; +uint16_t last_addr = 0x0000; static uint32_t *ovr_seg = NULL; static int prefetching = 1, completed = 1; @@ -204,67 +205,12 @@ prefetch_queue_get_size(void) } static void set_if(int cond); -void -sync_from_i8080(void) -{ - AL = emulated_processor.a; - BH = emulated_processor.h; - BL = emulated_processor.l; - CH = emulated_processor.b; - CL = emulated_processor.c; - DH = emulated_processor.d; - DL = emulated_processor.e; - BP = emulated_processor.sp; - - cpu_state.pc = emulated_processor.pc; - cpu_state.flags &= 0xFF00; - cpu_state.flags |= emulated_processor.sf << 7; - cpu_state.flags |= emulated_processor.zf << 6; - cpu_state.flags |= emulated_processor.hf << 4; - cpu_state.flags |= emulated_processor.pf << 2; - cpu_state.flags |= 1 << 1; - cpu_state.flags |= emulated_processor.cf << 0; - set_if(emulated_processor.iff); -} - -void -sync_to_i8080(void) -{ - if (!is_nec) - return; - emulated_processor.a = AL; - emulated_processor.h = BH; - emulated_processor.l = BL; - emulated_processor.b = CH; - emulated_processor.c = CL; - emulated_processor.d = DH; - emulated_processor.e = DL; - emulated_processor.sp = BP; - emulated_processor.pc = cpu_state.pc; - emulated_processor.iff = !!(cpu_state.flags & I_FLAG); - - emulated_processor.sf = (cpu_state.flags >> 7) & 1; - emulated_processor.zf = (cpu_state.flags >> 6) & 1; - emulated_processor.hf = (cpu_state.flags >> 4) & 1; - emulated_processor.pf = (cpu_state.flags >> 2) & 1; - emulated_processor.cf = (cpu_state.flags >> 0) & 1; - - emulated_processor.interrupt_delay = noint; -} - - uint16_t get_last_addr(void) { return last_addr; } -static void -clock_start(void) -{ - cycdiff = cycles; -} - static void clock_end(void) { @@ -293,7 +239,7 @@ fetch_and_bus(int c, int bus) pfq_add(c, !bus); if (bus < 2) { clock_end(); - clock_start(); + cycdiff = cycles; } } @@ -412,7 +358,7 @@ readmemw(uint32_t s, uint16_t a) else { wait_cycs(4, 1); ret = read_mem_b(s + a); - ret |= read_mem_b(s + ((is186 && !is_nec) ? (a + 1) : (a + 1) & 0xffff)) << 8; + ret |= read_mem_b(s + (is186 ? (a + 1) : (a + 1) & 0xffff)) << 8; } return ret; @@ -484,7 +430,7 @@ writememw(uint32_t s, uint32_t a, uint16_t v) else { write_mem_b(addr, v & 0xff); wait_cycs(4, 1); - addr = s + ((is186 && !is_nec) ? (a + 1) : ((a + 1) & 0xffff)); + addr = s + (is186 ? (a + 1) : ((a + 1) & 0xffff)); write_mem_b(addr, v >> 8); } @@ -693,8 +639,6 @@ reset_808x(int hard) load_cs(0xFFFF); cpu_state.pc = 0; - if (is_nec) - cpu_state.flags |= MD_FLAG; rammask = 0xfffff; prefetching = 1; @@ -929,7 +873,7 @@ seteaq(uint64_t val) static void push(uint16_t *val) { - if ((is186 && !is_nec) && (SP == 1)) { + if (is186 && (SP == 1)) { writememw(ss - 1, 0, *val); SP = cpu_state.eaaddr = 0xFFFF; return; @@ -1085,11 +1029,6 @@ interrupt(uint16_t addr) uint16_t new_cs, new_ip; uint16_t tempf; - if (!(cpu_state.flags & MD_FLAG) && is_nec) { - sync_from_i8080(); - x808x_log("CALLN/INT#/NMI#\n"); - } - addr <<= 2; cpu_state.eaaddr = addr; old_cs = CS; @@ -1103,11 +1042,9 @@ interrupt(uint16_t addr) pfq_clear(); ovr_seg = NULL; access(39, 16); - tempf = cpu_state.flags & (is_nec ? 0x8fd7 : 0x0fd7); + tempf = cpu_state.flags & 0x0fd7; push(&tempf); cpu_state.flags &= ~(I_FLAG | T_FLAG); - if (is_nec) - cpu_state.flags |= MD_FLAG; access(40, 16); push(&old_cs); old_ip = cpu_state.pc; @@ -1118,71 +1055,6 @@ interrupt(uint16_t addr) push(&old_ip); } -/* Ditto, but for breaking into emulation mode. */ -static void -interrupt_brkem(uint16_t addr) -{ - uint16_t old_cs, old_ip; - uint16_t new_cs, new_ip; - uint16_t tempf; - - addr <<= 2; - cpu_state.eaaddr = addr; - old_cs = CS; - access(5, 16); - new_ip = readmemw(0, cpu_state.eaaddr); - wait_cycs(1, 0); - cpu_state.eaaddr = (cpu_state.eaaddr + 2) & 0xffff; - access(6, 16); - new_cs = readmemw(0, cpu_state.eaaddr); - prefetching = 0; - pfq_clear(); - ovr_seg = NULL; - access(39, 16); - tempf = cpu_state.flags & (is_nec ? 0x8fd7 : 0x0fd7); - push(&tempf); - cpu_state.flags &= ~(MD_FLAG); - cpu_md_write_disable = 0; - access(40, 16); - push(&old_cs); - old_ip = cpu_state.pc; - load_cs(new_cs); - access(68, 16); - set_ip(new_ip); - access(41, 16); - push(&old_ip); - sync_to_i8080(); - x808x_log("BRKEM mode\n"); -} - -void -retem_i8080(void) -{ - sync_from_i8080(); - - prefetching = 0; - pfq_clear(); - - set_ip(pop()); - load_cs(pop()); - cpu_state.flags = pop(); - - emulated_processor.iff = !!(cpu_state.flags & I_FLAG); - - cpu_md_write_disable = 1; - - noint = 1; - nmi_enable = 1; - - x808x_log("RETEM mode\n"); -} - -void -interrupt_808x(uint16_t addr) -{ - interrupt(addr); -} - static void custom_nmi(void) { @@ -1190,11 +1062,6 @@ custom_nmi(void) uint16_t new_cs, new_ip; uint16_t tempf; - if (!(cpu_state.flags & MD_FLAG) && is_nec) { - sync_from_i8080(); - pclog("NMI# (CUTSOM)\n"); - } - cpu_state.eaaddr = 0x0002; old_cs = CS; access(5, 16); @@ -1209,11 +1076,9 @@ custom_nmi(void) pfq_clear(); ovr_seg = NULL; access(39, 16); - tempf = cpu_state.flags & (is_nec ? 0x8fd7 : 0x0fd7); + tempf = cpu_state.flags & 0x0fd7; push(&tempf); cpu_state.flags &= ~(I_FLAG | T_FLAG); - if (is_nec) - cpu_state.flags |= MD_FLAG; access(40, 16); push(&old_cs); old_ip = cpu_state.pc; @@ -1225,10 +1090,10 @@ custom_nmi(void) } static int -irq_pending(int nec_hlt) +irq_pending(void) { uint8_t temp; - int i_flag = (cpu_state.flags & I_FLAG) || nec_hlt; + int i_flag = (cpu_state.flags & I_FLAG); temp = (nmi && nmi_enable && nmi_mask) || ((cpu_state.flags & T_FLAG) && !noint) || (i_flag && pic.int_pending && !noint); @@ -1236,12 +1101,12 @@ irq_pending(int nec_hlt) } static void -check_interrupts(int nec_hlt) +check_interrupts(void) { int temp; - int i_flag = (cpu_state.flags & I_FLAG) || nec_hlt; + int i_flag = (cpu_state.flags & I_FLAG); - if (irq_pending(nec_hlt)) { + if (irq_pending()) { if ((cpu_state.flags & T_FLAG) && !(noint & 1)) { interrupt(1); return; @@ -1290,13 +1155,10 @@ rep_action(int bits) return 0; wait_cycs(2, 0); t = CX; - if (irq_pending(0) && (repeating != 0)) { + if (irq_pending() && (repeating != 0)) { access(71, bits); pfq_clear(); - if (is_nec && (ovr_seg != NULL)) - set_ip(cpu_state.pc - 3); - else - set_ip(cpu_state.pc - 2); + set_ip(cpu_state.pc - 2); t = 0; } if (t == 0) { @@ -1919,32 +1781,16 @@ void execx86(int cycs) { uint8_t temp = 0, temp2, old_af, nests; - uint8_t temp_val, temp_al, bit, handled = 0; - uint8_t odd, zero, nibbles_count, destcmp; - uint8_t destbyte, srcbyte, nibble_result, bit_length; - uint8_t bit_offset; - int8_t nibble_result_s; - uint16_t addr, tempw, new_cs, new_ip; + uint16_t addr, tempw, new_cs, new_ip, handled = 0; uint16_t tempw_int, size, tempbp, lowbound; - uint16_t highbound, regval, orig_sp, wordtopush; - uint16_t immediate, old_flags; + uint16_t highbound, regval, orig_sp, wordtopush, immediate; int bits; - uint32_t dest_seg, i, carry, nibble; - uint32_t srcseg, byteaddr; + uint32_t dest_seg; cycles += cycs; while (cycles > 0) { - clock_start(); - - if (is_nec && !(cpu_state.flags & MD_FLAG)) { - i8080_step(&emulated_processor); - set_if(emulated_processor.iff); - cycles -= emulated_processor.cyc; - emulated_processor.cyc = 0; - completed = 1; - goto exec_completed; - } + cycdiff = cycles; if (!repeating) { cpu_state.oldpc = cpu_state.pc; @@ -2004,18 +1850,6 @@ execx86(int cycs) handled = 1; break; - case 0x64: - case 0x65: - if (is_nec) { - /* REPC/REPNC */ - wait_cycs(1, 0); - in_rep = (opcode == 0x64 ? 1 : 2); - rep_c_flag = 1; - completed = 0; - handled = 1; - } - break; - case 0x68: wordtopush = pfq_fetchw(); wait_cycs(1, 0); @@ -2111,7 +1945,6 @@ execx86(int cycs) tempw_int = 0; size = pfq_fetchw(); nests = pfq_fetchb(); - i = 0; push(&BP); tempw_int = SP; @@ -2141,8 +1974,7 @@ execx86(int cycs) wait_cycs((cpu_mod != 3) ? 9 : 6, 0); - if (!is_nec) - cpu_src &= 0x1F; + cpu_src &= 0x1F; while (cpu_src != 0) { cpu_dest = cpu_data; oldc = cpu_state.flags & C_FLAG; @@ -2239,299 +2071,6 @@ execx86(int cycs) case 0x0F: case 0x17: case 0x1F: /* POP seg */ - if (is_nec && (opcode == 0x0F)) { - uint8_t orig_opcode = opcode; - opcode = pfq_fetchb(); - switch (opcode) { - case 0x28: /* ROL4 r/m */ - do_mod_rm(); - wait_cycs(21, 0); - - temp_val = geteab(); - temp_al = AL; - - temp_al &= 0xF; - temp_al |= (temp_val & 0xF0); - temp_val = (temp_al & 0xF) | ((temp_val & 0xF) << 4); - temp_al >>= 4; - temp_al &= 0xF; - seteab(temp_val); - AL = temp_al; - - handled = 1; - break; - - case 0x2a: /* ROR4 r/m */ - do_mod_rm(); - wait_cycs(21, 0); - - temp_val = geteab(); - temp_al = AL; - - AL = temp_val & 0xF; - temp_val = (temp_val >> 4) | ((temp_al & 0xF) << 4); - - seteab(temp_val); - - handled = 1; - break; - - case 0x10: /* TEST1 r8/m8, CL*/ - case 0x11: /* TEST1 r16/m16, CL*/ - case 0x18: /* TEST1 r8/m8, imm3 */ - case 0x19: /* TEST1 r16/m16, imm4 */ - bits = 8 << (opcode & 0x1); - do_mod_rm(); - wait_cycs(3, 0); - - bit = (opcode & 0x8) ? (pfq_fetchb()) : (CL); - bit &= ((1 << (3 + (opcode & 0x1))) - 1); - read_ea(0, bits); - - set_zf_ex(!(cpu_data & (1 << bit))); - cpu_state.flags &= ~(V_FLAG | C_FLAG); - - handled = 1; - break; - - case 0x16: /* NOT1 r8/m8, CL*/ - case 0x17: /* NOT1 r16/m16, CL*/ - case 0x1e: /* NOT1 r8/m8, imm3 */ - case 0x1f: /* NOT1 r16/m16, imm4 */ - bits = 8 << (opcode & 0x1); - do_mod_rm(); - wait_cycs(3, 0); - - bit = (opcode & 0x8) ? (pfq_fetchb()) : (CL); - bit &= ((1 << (3 + (opcode & 0x1))) - 1); - read_ea(0, bits); - - if (bits == 8) - seteab((cpu_data & 0xFF) ^ (1 << bit)); - else - seteaw((cpu_data & 0xFFFF) ^ (1 << bit)); - - handled = 1; - break; - - case 0x14: /* SET1 r8/m8, CL*/ - case 0x15: /* SET1 r16/m16, CL*/ - case 0x1c: /* SET1 r8/m8, imm3 */ - case 0x1d: /* SET1 r16/m16, imm4 */ - bits = 8 << (opcode & 0x1); - do_mod_rm(); - wait_cycs(3, 0); - - bit = (opcode & 0x8) ? (pfq_fetchb()) : (CL); - bit &= ((1 << (3 + (opcode & 0x1))) - 1); - read_ea(0, bits); - - if (bits == 8) - seteab((cpu_data & 0xFF) | (1 << bit)); - else - seteaw((cpu_data & 0xFFFF) | (1 << bit)); - - handled = 1; - break; - - case 0x12: /* CLR1 r8/m8, CL*/ - case 0x13: /* CLR1 r16/m16, CL*/ - case 0x1a: /* CLR1 r8/m8, imm3 */ - case 0x1b: /* CLR1 r16/m16, imm4 */ - bits = 8 << (opcode & 0x1); - do_mod_rm(); - wait_cycs(3, 0); - - bit = (opcode & 0x8) ? (pfq_fetchb()) : (CL); - bit &= ((1 << (3 + (opcode & 0x1))) - 1); - read_ea(0, bits); - - if (bits == 8) - seteab((cpu_data & 0xFF) & ~(1 << bit)); - else - seteaw((cpu_data & 0xFFFF) & ~(1 << bit)); - - handled = 1; - break; - - case 0x20: /* ADD4S */ - odd = !!(CL % 2); - zero = 1; - nibbles_count = CL - odd; - i = 0; - carry = 0; - nibble = 0; - srcseg = ovr_seg ? *ovr_seg : ds; - - wait_cycs(5, 0); - for (i = 0; i < ((nibbles_count / 2) + odd); i++) { - wait_cycs(19, 0); - destcmp = read_mem_b((es) + DI + i); - for (nibble = 0; nibble < 2; nibble++) { - destbyte = destcmp >> (nibble ? 4 : 0); - srcbyte = read_mem_b(srcseg + SI + i) >> (nibble ? 4 : 0); - destbyte &= 0xF; - srcbyte &= 0xF; - nibble_result = (i == (nibbles_count / 2) && nibble == 1) ? (destbyte + carry) : ((uint8_t) (destbyte)) + ((uint8_t) (srcbyte)) + ((uint32_t) carry); - carry = 0; - while (nibble_result >= 10) { - nibble_result -= 10; - carry++; - } - if (zero != 0 || (i == (nibbles_count / 2) && nibble == 1)) - zero = (nibble_result == 0); - destcmp = ((destcmp & (nibble ? 0x0F : 0xF0)) | (nibble_result << (4 * nibble))); - } - write_mem_b(es + DI + i, destcmp); - } - set_cf(!!carry); - set_zf(!!zero); - handled = 1; - break; - - case 0x22: /* SUB4S */ - odd = !!(CL % 2); - zero = 1; - nibbles_count = CL - odd; - i = 0; - carry = 0; - nibble = 0; - srcseg = ovr_seg ? *ovr_seg : ds; - - wait_cycs(5, 0); - for (i = 0; i < ((nibbles_count / 2) + odd); i++) { - wait_cycs(19, 0); - destcmp = read_mem_b((es) + DI + i); - for (nibble = 0; nibble < 2; nibble++) { - destbyte = destcmp >> (nibble ? 4 : 0); - srcbyte = read_mem_b(srcseg + SI + i) >> (nibble ? 4 : 0); - destbyte &= 0xF; - srcbyte &= 0xF; - nibble_result_s = (i == (nibbles_count / 2) && nibble == 1) ? ((int8_t) destbyte - (int8_t) carry) : ((int8_t) (destbyte)) - ((int8_t) (srcbyte)) - ((int8_t) carry); - carry = 0; - while (nibble_result_s < 0) { - nibble_result_s += 10; - carry++; - } - if (zero != 0 || (i == (nibbles_count / 2) && nibble == 1)) - zero = (nibble_result_s == 0); - destcmp = ((destcmp & (nibble ? 0x0F : 0xF0)) | (nibble_result_s << (4 * nibble))); - } - write_mem_b(es + DI + i, destcmp); - } - set_cf(!!carry); - set_zf(!!zero); - handled = 1; - break; - - case 0x26: /* CMP4S */ - odd = !!(CL % 2); - zero = 1; - nibbles_count = CL - odd; - i = 0; - carry = 0; - nibble = 0; - srcseg = ovr_seg ? *ovr_seg : ds; - - wait_cycs(5, 0); - for (i = 0; i < ((nibbles_count / 2) + odd); i++) { - wait_cycs(19, 0); - destcmp = read_mem_b((es) + DI + i); - for (nibble = 0; nibble < 2; nibble++) { - destbyte = destcmp >> (nibble ? 4 : 0); - srcbyte = read_mem_b(srcseg + SI + i) >> (nibble ? 4 : 0); - destbyte &= 0xF; - srcbyte &= 0xF; - nibble_result_s = ((int8_t) (destbyte)) - ((int8_t) (srcbyte)) - ((int8_t) carry); - carry = 0; - while (nibble_result_s < 0) { - nibble_result_s += 10; - carry++; - } - if (zero != 0 || (i == (nibbles_count / 2) && nibble == 1)) - zero = (nibble_result_s == 0); - destcmp = ((destcmp & (nibble ? 0x0F : 0xF0)) | (nibble_result_s << (4 * nibble))); - } - } - set_cf(!!carry); - set_zf(!!zero); - handled = 1; - break; - - case 0x31: /* INS reg1, reg2 */ - case 0x39: /* INS reg8, imm4 */ - do_mod_rm(); - wait_cycs(1, 0); - - bit_length = ((opcode & 0x8) ? (pfq_fetchb() & 0xF) : (getr8(cpu_reg) & 0xF)) + 1; - bit_offset = getr8(cpu_rm) & 0xF; - byteaddr = (es) + DI; - i = 0; - - if (bit_offset >= 8) { - DI++; - byteaddr++; - bit_offset -= 8; - } - for (i = 0; i < bit_length; i++) { - byteaddr = (es) + DI; - writememb(es, DI, (read_mem_b(byteaddr) & ~(1 << (bit_offset))) | ((!!(AX & (1 << i))) << bit_offset)); - bit_offset++; - if (bit_offset == 8) { - DI++; - bit_offset = 0; - } - } - setr8(cpu_rm, bit_offset); - - handled = 1; - break; - - case 0x33: /* EXT reg1, reg2 */ - case 0x3b: /* EXT reg8, imm4 */ - do_mod_rm(); - wait_cycs(1, 0); - - bit_length = ((opcode & 0x8) ? (pfq_fetchb() & 0xF) : (getr8(cpu_reg) & 0xF)) + 1; - bit_offset = getr8(cpu_rm) & 0xF; - byteaddr = (ds) + SI; - i = 0; - - if (bit_offset >= 8) { - SI++; - byteaddr++; - bit_offset -= 8; - } - - AX = 0; - for (i = 0; i < bit_length; i++) { - byteaddr = (ds) + SI; - AX |= (!!(readmemb(byteaddr) & (1 << bit_offset))) << i; - bit_offset++; - if (bit_offset == 8) { - SI++; - bit_offset = 0; - } - } - setr8(cpu_rm, bit_offset); - - handled = 1; - break; - - case 0xFF: /* BRKEM */ - interrupt_brkem(pfq_fetchb()); - handled = 1; - break; - - default: - opcode = orig_opcode; - cpu_state.pc--; - break; - } - } else - handled = 0; - if (handled) - break; access(22, 16); if (opcode == 0x0F) { load_cs(pop()); @@ -3019,28 +2558,21 @@ execx86(int cycs) } #else wait_cycs(7, 0); - check_interrupts(0); + check_interrupts(); #endif break; case 0x9C: /*PUSHF*/ access(33, 16); - if (is_nec) - tempw = (cpu_state.flags & 0x8fd7) | 0x7000; - else - tempw = (cpu_state.flags & 0x0fd7) | 0xf000; + tempw = (cpu_state.flags & 0x0fd7) | 0xf000; push(&tempw); break; case 0x9D: { /*POPF*/ uint16_t old_flags = cpu_state.flags; access(25, 16); - if (is_nec && cpu_md_write_disable) - cpu_state.flags = pop() | 0x8002; - else - cpu_state.flags = pop() | 0x0002; + cpu_state.flags = pop() | 0x0002; wait_cycs(1, 0); if ((old_flags ^ cpu_state.flags) & T_FLAG) noint = 1; - sync_to_i8080(); break; } case 0x9E: /*SAHF*/ wait_cycs(1, 0); @@ -3306,15 +2838,10 @@ execx86(int cycs) access(62, 8); set_ip(new_ip); access(45, 8); - if (is_nec && cpu_md_write_disable) - cpu_state.flags = pop() | 0x8002; - else - cpu_state.flags = pop() | 0x0002; + cpu_state.flags = pop() | 0x0002; wait_cycs(5, 0); noint = 2; nmi_enable = 1; - if (is_nec && !(cpu_state.flags & MD_FLAG)) - sync_to_i8080(); break; case 0xD0: @@ -3335,7 +2862,7 @@ execx86(int cycs) cpu_src = CL; wait_cycs((cpu_mod != 3) ? 9 : 6, 0); } - if (is186 && !is_nec) + if (is186) cpu_src &= 0x1F; while (cpu_src != 0) { cpu_dest = cpu_data; @@ -3414,15 +2941,7 @@ execx86(int cycs) case 0xD4: /*AAM*/ wait_cycs(1, 0); -#ifdef NO_VARIANT_ON_NEC - if (is_nec) { - (void) pfq_fetchb(); - cpu_src = 10; - } else - cpu_src = pfq_fetchb(); -#else cpu_src = pfq_fetchb(); -#endif if (x86_div(AL, 0)) { cpu_data = AL; set_pzs(8); @@ -3430,11 +2949,7 @@ execx86(int cycs) break; case 0xD5: /*AAD*/ wait_cycs(1, 0); - if (is_nec) { - (void) pfq_fetchb(); - mul(10, AH); - } else - mul(pfq_fetchb(), AH); + mul(pfq_fetchb(), AH); cpu_dest = AL; cpu_src = cpu_data; add(8); @@ -3443,13 +2958,10 @@ execx86(int cycs) set_pzs(8); break; case 0xD6: /*SALC*/ - if (!is_nec) { - wait_cycs(1, 0); - AL = (cpu_state.flags & C_FLAG) ? 0xff : 0x00; - wait_cycs(1, 0); - break; - } - fallthrough; + wait_cycs(1, 0); + AL = (cpu_state.flags & C_FLAG) ? 0xff : 0x00; + wait_cycs(1, 0); + break; case 0xD7: /*XLATB*/ cpu_state.eaaddr = (BX + AL) & 0xffff; access(4, 8); @@ -3651,9 +3163,9 @@ execx86(int cycs) pfq_clear(); } wait_cycs(1, 0); - if (irq_pending(is_nec)) { + if (irq_pending()) { wait_cycs(cycles & 1, 0); - check_interrupts(is_nec); + check_interrupts(); } else { repeating = 1; completed = 0; @@ -3699,7 +3211,6 @@ execx86(int cycs) break; case 0x20: /* MUL */ case 0x28: /* IMUL */ - old_flags = cpu_state.flags; wait_cycs(1, 0); mul(get_accum(bits), cpu_data); if (opcode & 1) { @@ -3711,17 +3222,12 @@ execx86(int cycs) AL = (uint8_t) cpu_data; AH = (uint8_t) cpu_dest; set_co_mul(bits, AH != ((AL & 0x80) == 0 || (rmdat & 0x38) == 0x20 ? 0 : 0xff)); - if (!is_nec) - cpu_data = AH; + cpu_data = AH; } set_sf(bits); set_pf(); if (cpu_mod != 3) wait_cycs(1, 0); - /* NOTE: When implementing the V20, care should be taken to not change - the zero flag. */ - if (is_nec) - cpu_state.flags = (cpu_state.flags & ~Z_FLAG) | (old_flags & Z_FLAG); break; case 0x30: /* DIV */ case 0x38: /* IDIV */ @@ -3843,7 +3349,7 @@ execx86(int cycs) break; } } -exec_completed: + if (completed) { repeating = 0; ovr_seg = NULL; @@ -3852,7 +3358,7 @@ exec_completed: if (in_lock) clear_lock = 1; clock_end(); - check_interrupts(0); + check_interrupts(); if (noint) noint = 0; diff --git a/src/cpu/CMakeLists.txt b/src/cpu/CMakeLists.txt index dc7f5ac11..7eadb94fa 100644 --- a/src/cpu/CMakeLists.txt +++ b/src/cpu/CMakeLists.txt @@ -20,6 +20,8 @@ add_library(cpu OBJECT cpu_table.c fpu.c x86.c 808x.c + vx0.c + vx0_biu.c 386.c 386_common.c 386_dynarec.c diff --git a/src/cpu/cpu.c b/src/cpu/cpu.c index 6e675acc8..e0868861d 100644 --- a/src/cpu/cpu.c +++ b/src/cpu/cpu.c @@ -1865,6 +1865,8 @@ cpu_set(void) cpu_exec = exec386_2386; } else if (cpu_s->cpu_type >= CPU_286) cpu_exec = exec386_2386; + else if (is_nec) + cpu_exec = execvx0; else cpu_exec = execx86; mmx_init(); diff --git a/src/cpu/cpu.h b/src/cpu/cpu.h index 865e76ffb..8e74bc78a 100644 --- a/src/cpu/cpu.h +++ b/src/cpu/cpu.h @@ -718,6 +718,7 @@ extern void codegen_reset(void); extern void cpu_set_edx(void); extern int divl(uint32_t val); extern void execx86(int32_t cycs); +extern void execvx0(int32_t cycs); extern void enter_smm(int in_hlt); extern void enter_smm_check(int in_hlt); extern void leave_smm(void); @@ -809,6 +810,8 @@ extern void SF_FPU_reset(void); extern void reset_808x(int hard); extern void interrupt_808x(uint16_t addr); +extern void reset_vx0(int hard); + extern void cpu_register_fast_off_handler(void *timer); extern void cpu_fast_off_advance(void); extern void cpu_fast_off_period_set(uint16_t vla, double period); diff --git a/src/cpu/vx0.c b/src/cpu/vx0.c new file mode 100644 index 000000000..bd1a8da20 --- /dev/null +++ b/src/cpu/vx0.c @@ -0,0 +1,4783 @@ +/* + * 86Box A hypervisor and IBM PC system emulator that specializes in + * running old operating systems and software designed for IBM + * PC systems and compatibles from 1981 through fairly recent + * system designs based on the PCI bus. + * + * This file is part of the 86Box distribution. + * + * 808x CPU emulation, mostly ported from reenigne's XTCE, which + * is cycle-accurate. + * + * Authors: Andrew Jenner, + * Miran Grca, + * + * Copyright 2015-2020 Andrew Jenner. + * Copyright 2016-2020 Miran Grca. + */ +#include +#include +#include +#include +#include +#include + +#include "i8080.h" + +#define HAVE_STDARG_H +#include <86box/86box.h> +#include "cpu.h" +#include "x86.h" +#include <86box/machine.h> +#include <86box/io.h> +#include <86box/mem.h> +#include <86box/rom.h> +#include <86box/nmi.h> +#include <86box/pic.h> +#include <86box/ppi.h> +#include <86box/timer.h> +#include <86box/gdbstub.h> +#include <86box/plat_fallthrough.h> +#include <86box/plat_unused.h> +#include "vx0_biu.h" + +#define do_cycle() wait(1) +#define do_cycle_no_modrm() if (!nx) \ + do_cycle() +#define do_cycle_i() do_cycle() +#define do_cycles(c) wait(c) +#define do_cycles_i(c) do_cycles(c) +#define do_cycle_nx() nx = 1 +#define do_cycle_nx_i() nx = 1 +#define do_cycles_nx(c) nx = 1; \ + if (c > 1) \ + do_cycles(c - 1) +#define do_cycles_nx_i(c) nx = 1; \ + if (c > 1) \ + do_cycles(c - 1) +#define addr_mode_match() cpu_mod == 3 +#define math_op(o) cpu_alu_op = o; \ + alu_op(bits) + +/* Various things needed for 8087. */ +#define OP_TABLE(name) ops_##name + +#define CPU_BLOCK_END() +#define SEG_CHECK_READ(seg) +#define SEG_CHECK_WRITE(seg) +#define CHECK_READ(a, b, c) +#define CHECK_WRITE(a, b, c) +#define UN_USED(x) (void) (x) +#define fetch_ea_16(val) +#define fetch_ea_32(val) +#define PREFETCH_RUN(a, b, c, d, e, f, g, h) + +#define CYCLES(val) \ + { \ + do_cycles(val); \ + } + +#define CLOCK_CYCLES_ALWAYS(val) \ + { \ + do_cycles(val); \ + } + +#define CLOCK_CYCLES_FPU(val) \ + { \ + do_cycles(val); \ + } + +# define CLOCK_CYCLES(val) \ + { \ + if (fpu_cycles > 0) { \ + fpu_cycles -= (val); \ + if (fpu_cycles < 0) { \ + do_cycles(val); \ + } \ + } else { \ + do_cycles(val); \ + } \ + } + +#define CONCURRENCY_CYCLES(c) fpu_cycles = (c) + +#define OP_MRM 1 +#define OP_EA 2 +#define OP_MEA (OP_MRM | OP_EA) +#define OP_GRP 4 +#define OP_DELAY 8 +#define OP_PRE 16 + +#define readmemb readmemb_vx0 +#define readmemw readmemw_vx0 +#define readmem readmem_vx0 +#define readmeml readmeml_vx0 +#define readmemq readmemq_vx0 +#define writememb writememb_vx0 +#define writememw writememw_vx0 +#define writemem writemem_vx0 +#define writememl writememl_vx0 +#define writememq writememq_vx0 + +typedef int (*OpFn)(uint32_t fetchdat); + +static void farret(int far); + +const uint8_t opf[256] = { OP_MEA, OP_MEA, OP_MEA, OP_MEA, 0, 0, 0, 0, /* 00 */ + OP_MEA, OP_MEA, OP_MEA, OP_MEA, 0, 0, 0, 0, /* 08 */ + OP_MEA, OP_MEA, OP_MEA, OP_MEA, 0, 0, 0, 0, /* 10 */ + OP_MEA, OP_MEA, OP_MEA, OP_MEA, 0, 0, 0, 0, /* 18 */ + OP_MEA, OP_MEA, OP_MEA, OP_MEA, 0, 0, OP_PRE, 0, /* 20 */ + OP_MEA, OP_MEA, OP_MEA, OP_MEA, 0, 0, OP_PRE, 0, /* 28 */ + OP_MEA, OP_MEA, OP_MEA, OP_MEA, 0, 0, OP_PRE, 0, /* 30 */ + OP_MEA, OP_MEA, OP_MEA, OP_MEA, 0, 0, OP_PRE, 0, /* 38 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 40 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 48 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 50 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 58 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 60 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 68 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 70 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 78 */ + OP_GRP, OP_GRP, OP_GRP, OP_GRP, OP_MEA, OP_MEA, OP_MEA, OP_MEA, /* 80 */ + OP_MRM, OP_MRM, OP_MEA, OP_MEA, OP_MRM, OP_MRM, OP_MEA, OP_MRM, /* 88 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 90 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 98 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* A0 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* A8 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* B0 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* B8 */ + 0, 0, 0, 0, OP_MEA, OP_MEA, OP_MRM, OP_MRM, /* C0 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* C8 */ + OP_GRP, OP_GRP, OP_GRP, OP_GRP, 0, 0, 0, 0, /* D0 */ + OP_MRM, OP_MRM, OP_MRM, OP_MRM, OP_MRM, OP_MRM, OP_MRM, OP_MRM, /* D8 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* E0 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* E8 */ + OP_PRE, OP_PRE, OP_PRE, OP_PRE, 0, 0, OP_GRP, OP_GRP, /* F0 */ + 0, 0, 0, 0, 0, 0, OP_GRP, OP_GRP }; /* F8 */ + +const uint8_t opf_nec[256] = { OP_MEA, OP_MEA, OP_MEA, OP_MEA, 0, 0, 0, 0, /* 00 */ + OP_MEA, OP_MEA, OP_MEA, OP_MEA, 0, 0, 0, OP_PRE, /* 08 */ + OP_MEA, OP_MEA, OP_MEA, OP_MEA, 0, 0, 0, 0, /* 10 */ + OP_MEA, OP_MEA, OP_MEA, OP_MEA, 0, 0, 0, 0, /* 18 */ + OP_MEA, OP_MEA, OP_MEA, OP_MEA, 0, 0, OP_PRE, 0, /* 20 */ + OP_MEA, OP_MEA, OP_MEA, OP_MEA, 0, 0, OP_PRE, 0, /* 28 */ + OP_MEA, OP_MEA, OP_MEA, OP_MEA, 0, 0, OP_PRE, 0, /* 30 */ + OP_MEA, OP_MEA, OP_MEA, OP_MEA, 0, 0, OP_PRE, 0, /* 38 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 40 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 48 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 50 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 58 */ + 0, 0, OP_MRM, OP_MRM, OP_PRE, OP_PRE, OP_MRM, OP_MRM, /* 60 */ + 0, OP_MRM, 0, OP_MEA, 0, 0, 0, 0, /* 68 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 70 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 78 */ + OP_GRP, OP_GRP, OP_GRP, OP_GRP, OP_MEA, OP_MEA, OP_MEA, OP_MEA, /* 80 */ + OP_MRM, OP_MRM, OP_MEA, OP_MEA, OP_MRM, OP_MRM, OP_MEA, OP_MRM, /* 88 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 90 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 98 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* A0 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* A8 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* B0 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* B8 */ + OP_GRP, OP_GRP, 0, 0, OP_MEA, OP_MEA, OP_MRM, OP_MRM, /* C0 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* C8 */ + OP_GRP, OP_GRP, OP_GRP, OP_GRP, 0, 0, 0, 0, /* D0 */ + OP_MRM, OP_MRM, OP_MRM, OP_MRM, OP_MRM, OP_MRM, OP_MRM, OP_MRM, /* D8 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* E0 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* E8 */ + OP_PRE, OP_PRE, OP_PRE, OP_PRE, 0, 0, OP_GRP, OP_GRP, /* F0 */ + 0, 0, 0, 0, 0, 0, OP_GRP, OP_GRP }; /* F8 */ + +const uint8_t opf_0f[256] = { 0, 0, 0, 0, 0, 0, 0, 0, /* 00 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 08 */ + OP_MRM, OP_MRM, OP_MRM, OP_MRM, OP_MRM, OP_MRM, OP_MRM, OP_MRM, /* 10 */ + OP_MRM, OP_MRM, OP_MRM, OP_MRM, OP_MRM, OP_MRM, OP_MRM, OP_MRM, /* 18 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 20 */ + OP_MRM, 0, OP_MRM, 0, 0, 0, 0, 0, /* 28 */ + OP_MRM, 0, OP_MRM, 0, 0, 0, 0, 0, /* 30 */ + OP_MRM, 0, OP_MRM, 0, 0, 0, 0, 0, /* 38 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 40 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 48 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 50 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 58 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 60 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 68 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 70 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 78 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 80 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 88 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 90 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 98 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* A0 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* A8 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* B0 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* B8 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* C0 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* C8 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* D0 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* D8 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* E0 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* E8 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* F0 */ + 0, 0, 0, 0, 0, 0, 0, 0 }; /* F8 */ + +uint8_t use_custom_nmi_vector = 0; + +uint32_t custom_nmi_vector = 0x00000000; + +/* Is the CPU 8088 or 8086. */ +int is8086 = 0; +int nx = 0; + +static uint32_t cpu_src = 0; +static uint32_t cpu_dest = 0; + +static uint32_t cpu_data = 0; + +static int oldc; +static int cpu_alu_op; +static int completed = 1; +static int in_rep = 0; +static int repeating = 0; +static int rep_c_flag = 0; +static int clear_lock = 0; +static int noint = 0; +static int tempc_fpu = 0; +static int started = 0; +static int group_delay = 0; +static int modrm_loaded = 0; +static int in_0f = 0; +static int in_hlt = 0; +static int retem = 0; +static int halted = 0; + +static uint32_t * ovr_seg = NULL; + +/* Pointer tables needed for segment overrides. */ +static uint32_t * opseg[4]; + +static x86seg * _opseg[4]; + +enum { + MODRM_ADDR_BX_SI = 0x00, + MODRM_ADDR_BX_DI, + MODRM_ADDR_BP_SI, + MODRM_ADDR_BP_DI, + MODRM_ADDR_SI, + MODRM_ADDR_DI, + MODRM_ADDR_DISP16, + MODRM_ADDR_BX, + + MODRM_ADDR_BX_SI_DISP8 = 0x40, + MODRM_ADDR_BX_DI_DISP8, + MODRM_ADDR_BP_SI_DISP8, + MODRM_ADDR_BP_DI_DISP8, + MODRM_ADDR_SI_DISP8, + MODRM_ADDR_DI_DISP8, + MODRM_ADDR_BP_DISP8, + MODRM_ADDR_BX_DISP8, + + MODRM_ADDR_BX_SI_DISP16 = 0x80, + MODRM_ADDR_BX_DI_DISP16, + MODRM_ADDR_BP_SI_DISP16, + MODRM_ADDR_BP_DI_DISP16, + MODRM_ADDR_SI_DISP16, + MODRM_ADDR_DI_DISP16, + MODRM_ADDR_BP_DISP16, + MODRM_ADDR_BX_DISP16 +}; + +static uint8_t modrm_cycs_pre[256] = { [MODRM_ADDR_BX_SI] = 4, + [MODRM_ADDR_BX_DI] = 5, + [MODRM_ADDR_BP_SI] = 5, + [MODRM_ADDR_BP_DI] = 4, + [MODRM_ADDR_SI] = 2, + [MODRM_ADDR_DI] = 2, + [MODRM_ADDR_DISP16] = 0, + [MODRM_ADDR_BX] = 2, + [0x08 ... 0x3f] = 0, + [MODRM_ADDR_BX_SI_DISP8] = 4, + [MODRM_ADDR_BX_DI_DISP8] = 5, + [MODRM_ADDR_BP_SI_DISP8] = 5, + [MODRM_ADDR_BP_DI_DISP8] = 4, + [MODRM_ADDR_SI_DISP8] = 2, + [MODRM_ADDR_DI_DISP8] = 2, + [MODRM_ADDR_BP_DISP8] = 2, + [MODRM_ADDR_BX_DISP8] = 2, + [0x48 ... 0x7f] = 0, + [MODRM_ADDR_BX_SI_DISP16] = 4, + [MODRM_ADDR_BX_DI_DISP16] = 5, + [MODRM_ADDR_BP_SI_DISP16] = 5, + [MODRM_ADDR_BP_DI_DISP16] = 4, + [MODRM_ADDR_SI_DISP16] = 2, + [MODRM_ADDR_DI_DISP16] = 2, + [MODRM_ADDR_BP_DISP16] = 2, + [MODRM_ADDR_BX_DISP16] = 2, + [0x88 ... 0xff] = 0 }; + +static uint8_t modrm_cycs_post[256] = { [MODRM_ADDR_BX_SI] = 0, + [MODRM_ADDR_BX_DI] = 0, + [MODRM_ADDR_BP_SI] = 0, + [MODRM_ADDR_BP_DI] = 0, + [MODRM_ADDR_SI] = 0, + [MODRM_ADDR_DI] = 0, + [MODRM_ADDR_DISP16] = 1, + [MODRM_ADDR_BX] = 0, + [0x08 ... 0x3f] = 0, + [MODRM_ADDR_BX_SI_DISP8] = 3, + [MODRM_ADDR_BX_DI_DISP8] = 3, + [MODRM_ADDR_BP_SI_DISP8] = 3, + [MODRM_ADDR_BP_DI_DISP8] = 3, + [MODRM_ADDR_SI_DISP8] = 3, + [MODRM_ADDR_DI_DISP8] = 3, + [MODRM_ADDR_BP_DISP8] = 3, + [MODRM_ADDR_BX_DISP8] = 3, + [0x48 ... 0x7f] = 0, + [MODRM_ADDR_BX_SI_DISP16] = 2, + [MODRM_ADDR_BX_DI_DISP16] = 2, + [MODRM_ADDR_BP_SI_DISP16] = 2, + [MODRM_ADDR_BP_DI_DISP16] = 2, + [MODRM_ADDR_SI_DISP16] = 2, + [MODRM_ADDR_DI_DISP16] = 2, + [MODRM_ADDR_BP_DISP16] = 2, + [MODRM_ADDR_BX_DISP16] = 2, + [0x88 ... 0xff] = 0 }; + +#ifdef ENABLE_808X_LOG +#if 0 +void dumpregs(int); +#endif +int x808x_do_log = ENABLE_808X_LOG; + +static void +x808x_log(const char *fmt, ...) +{ + va_list ap; + + if (x808x_do_log) { + va_start(ap, fmt); + pclog_ex(fmt, ap); + va_end(ap); + } +} +#else +# define x808x_log(fmt, ...) +#endif + +static i8080 emulated_processor; +static bool cpu_md_write_disable = 1; + +static void +set_if(int cond) +{ + cpu_state.flags = (cpu_state.flags & ~I_FLAG) | (cond ? I_FLAG : 0); +} + +void +sync_from_i8080(void) +{ + AL = emulated_processor.a; + BH = emulated_processor.h; + BL = emulated_processor.l; + CH = emulated_processor.b; + CL = emulated_processor.c; + DH = emulated_processor.d; + DL = emulated_processor.e; + BP = emulated_processor.sp; + + cpu_state.pc = emulated_processor.pc; + cpu_state.flags &= 0xFF00; + cpu_state.flags |= emulated_processor.sf << 7; + cpu_state.flags |= emulated_processor.zf << 6; + cpu_state.flags |= emulated_processor.hf << 4; + cpu_state.flags |= emulated_processor.pf << 2; + cpu_state.flags |= 1 << 1; + cpu_state.flags |= emulated_processor.cf << 0; + set_if(emulated_processor.iff); +} + +void +sync_to_i8080(void) +{ + if (!is_nec) + return; + emulated_processor.a = AL; + emulated_processor.h = BH; + emulated_processor.l = BL; + emulated_processor.b = CH; + emulated_processor.c = CL; + emulated_processor.d = DH; + emulated_processor.e = DL; + emulated_processor.sp = BP; + emulated_processor.pc = cpu_state.pc; + emulated_processor.iff = !!(cpu_state.flags & I_FLAG); + + emulated_processor.sf = (cpu_state.flags >> 7) & 1; + emulated_processor.zf = (cpu_state.flags >> 6) & 1; + emulated_processor.hf = (cpu_state.flags >> 4) & 1; + emulated_processor.pf = (cpu_state.flags >> 2) & 1; + emulated_processor.cf = (cpu_state.flags >> 0) & 1; + + emulated_processor.interrupt_delay = noint; +} + +uint16_t +get_last_addr(void) +{ + return last_addr; +} + +static void +set_ip(uint16_t new_ip) +{ + cpu_state.pc = new_ip; +} + +static void +startx86(void) +{ + /* Reset takes 6 cycles before first fetch. */ + do_cycle(); + biu_suspend_fetch(); + do_cycles_i(2); + biu_queue_flush(); + do_cycles_i(3); +} + +static void +load_cs(uint16_t seg) +{ + cpu_state.seg_cs.base = seg << 4; + cpu_state.seg_cs.seg = seg & 0xffff; +} + +static void +load_seg(uint16_t seg, x86seg *s) +{ + s->base = seg << 4; + s->seg = seg & 0xffff; +} + +static uint8_t +fetch_i8080_opcode(UNUSED(void* priv), uint16_t addr) +{ + return readmemb(cs, addr); +} + +static uint8_t +fetch_i8080_data(UNUSED(void* priv), uint16_t addr) +{ + return readmemb(ds, addr); +} + +static void +put_i8080_data(UNUSED(void* priv), uint16_t addr, uint8_t val) +{ + writememb(ds, addr, val); +} + +static +uint8_t i8080_port_in(UNUSED(void* priv), uint8_t port) +{ + cpu_data = port; + cpu_state.eaaddr = cpu_data; + cpu_io_vx0(8, 0, cpu_state.eaaddr); + return AL; +} + +static +void i8080_port_out(UNUSED(void* priv), uint8_t port, uint8_t val) +{ + cpu_data = DX; + AL = val; + do_cycle_i(); + + cpu_state.eaaddr = cpu_data; + cpu_data = AL; + cpu_io_vx0(8, 1, port); +} + +void +reset_vx0(int hard) +{ + halted = 0; + in_hlt = 0; + in_0f = 0; + in_rep = 0; + in_lock = 0; + completed = 1; + repeating = 0; + clear_lock = 0; + ovr_seg = NULL; + + if (hard) { + opseg[0] = &es; + opseg[1] = &cs; + opseg[2] = &ss; + opseg[3] = &ds; + _opseg[0] = &cpu_state.seg_es; + _opseg[1] = &cpu_state.seg_cs; + _opseg[2] = &cpu_state.seg_ss; + _opseg[3] = &cpu_state.seg_ds; + } + + load_cs(0xFFFF); + cpu_state.pc = 0; + + if (is_nec) + cpu_state.flags |= MD_FLAG; + rammask = 0xfffff; + + cpu_alu_op = 0; + + nx = 0; + + use_custom_nmi_vector = 0x00; + custom_nmi_vector = 0x00000000; + + biu_reset(); + + started = 1; + modrm_loaded = 0; + + cpu_md_write_disable = 1; + i8080_init(&emulated_processor); + emulated_processor.write_byte = put_i8080_data; + emulated_processor.read_byte = fetch_i8080_data; + emulated_processor.read_byte_seg = fetch_i8080_opcode; + emulated_processor.port_in = i8080_port_in; + emulated_processor.port_out = i8080_port_out; +} + +static uint16_t +get_accum(int bits) +{ + return (bits == 16) ? AX : AL; +} + +static void +set_accum(int bits, uint16_t val) +{ + if (bits == 16) + AX = val; + else + AL = val; +} + +static uint16_t +sign_extend(uint8_t data) +{ + return data + (data < 0x80 ? 0 : 0xff00); +} + +#undef getr8 +#define getr8(r) ((r & 4) ? cpu_state.regs[r & 3].b.h : cpu_state.regs[r & 3].b.l) + +#undef setr8 +#define setr8(r, v) \ + if (r & 4) \ + cpu_state.regs[r & 3].b.h = v; \ + else \ + cpu_state.regs[r & 3].b.l = v; + +/* Reads a byte from the effective address. */ +static uint8_t +geteab(void) +{ + uint8_t ret; + + if (cpu_mod == 3) + ret = (getr8(cpu_rm)); + else + ret = readmemb(easeg, cpu_state.eaaddr); + + return ret; +} + +/* Reads a word from the effective address. */ +static uint16_t +geteaw(void) +{ + uint16_t ret; + + if (cpu_mod == 3) + ret = cpu_state.regs[cpu_rm].w; + else + ret = readmemw(easeg, cpu_state.eaaddr); + + return ret; +} + +/* Neede for 8087 - memory only. */ +static uint32_t +geteal(void) +{ + uint32_t ret; + + if (cpu_mod == 3) { + fatal("808x register geteal()\n"); + ret = 0xffffffff; + } else + ret = readmeml(easeg, cpu_state.eaaddr); + + return ret; +} + +/* Neede for 8087 - memory only. */ +static uint64_t +geteaq(void) +{ + uint32_t ret; + + if (cpu_mod == 3) { + fatal("808x register geteaq()\n"); + ret = 0xffffffff; + } else + ret = readmemq(easeg, cpu_state.eaaddr); + + return ret; +} + +static void +read_ea(int memory_only, int bits) +{ + if (cpu_mod != 3) { + if (bits == 16) + cpu_data = readmemw(easeg, cpu_state.eaaddr); + else + cpu_data = readmemb(easeg, cpu_state.eaaddr); + return; + } + if (!memory_only) { + if (bits == 8) { + cpu_data = getr8(cpu_rm); + } else + cpu_data = cpu_state.regs[cpu_rm].w; + } +} + +static void +read_ea_8to16(void) +{ + cpu_data = cpu_state.regs[cpu_rm & 3].w; +} + +static void +read_ea2(int bits) +{ + cpu_state.eaaddr = (cpu_state.eaaddr + 2) & 0xffff; + if (bits == 16) + cpu_data = readmemw(easeg, cpu_state.eaaddr); + else + cpu_data = readmemb(easeg, cpu_state.eaaddr); +} + +/* Writes a byte to the effective address. */ +static void +seteab(uint8_t val) +{ + if (cpu_mod == 3) { + setr8(cpu_rm, val); + } else { + do_cycle(); + writememb(easeg, cpu_state.eaaddr, val); + } +} + +/* Writes a word to the effective address. */ +static void +seteaw(uint16_t val) +{ + if (cpu_mod == 3) + cpu_state.regs[cpu_rm].w = val; + else { + do_cycle(); + writememw(easeg, cpu_state.eaaddr, val); + } +} + +static void +seteal(uint32_t val) +{ + if (cpu_mod == 3) { + fatal("808x register seteal()\n"); + return; + } else + writememl(easeg, cpu_state.eaaddr, val); +} + +static void +seteaq(uint64_t val) +{ + if (cpu_mod == 3) { + fatal("808x register seteaq()\n"); + return; + } else + writememq(easeg, cpu_state.eaaddr, val); +} + +/* Leave out the 686 stuff as it's not needed and + complicates compiling. */ +#define FPU_8087 +#define FPU_NEC +#define tempc tempc_fpu +#include "x87_sf.h" +#include "x87.h" +#include "x87_ops.h" +#undef tempc +#undef FPU_8087 + +static void +set_cf(int cond) +{ + cpu_state.flags = (cpu_state.flags & ~C_FLAG) | (cond ? C_FLAG : 0); +} + +static void +set_df(int cond) +{ + cpu_state.flags = (cpu_state.flags & ~D_FLAG) | (cond ? D_FLAG : 0); +} + +static void +set_of(int of) +{ + cpu_state.flags = (cpu_state.flags & ~0x800) | (of ? 0x800 : 0); +} + +static int +top_bit(uint16_t w, int bits) +{ + return (w & (1 << (bits - 1))); +} + +static void +set_of_add(int bits) +{ + set_of(top_bit((cpu_data ^ cpu_src) & (cpu_data ^ cpu_dest), bits)); +} + +static void +set_of_sub(int bits) +{ + set_of(top_bit((cpu_dest ^ cpu_src) & (cpu_data ^ cpu_dest), bits)); +} + +static void +set_af(int af) +{ + cpu_state.flags = (cpu_state.flags & ~0x10) | (af ? 0x10 : 0); +} + +static void +do_af(void) +{ + set_af(((cpu_data ^ cpu_src ^ cpu_dest) & 0x10) != 0); +} + +static void +set_sf(int bits) +{ + cpu_state.flags = (cpu_state.flags & ~0x80) | (top_bit(cpu_data, bits) ? 0x80 : 0); +} + +static void +set_pf(void) +{ + cpu_state.flags = (cpu_state.flags & ~4) | (!__builtin_parity(cpu_data & 0xFF) << 2); +} + +static void +set_of_rotate(int bits) +{ + set_of(top_bit(cpu_data ^ cpu_dest, bits)); +} + +static void +set_zf_ex(int zf) +{ + cpu_state.flags = (cpu_state.flags & ~0x40) | (zf ? 0x40 : 0); +} + +static void +set_zf(int bits) +{ + int size_mask = (1 << bits) - 1; + + set_zf_ex((cpu_data & size_mask) == 0); +} + +static void +set_pzs(int bits) +{ + set_pf(); + set_zf(bits); + set_sf(bits); +} + +static void +set_apzs(int bits) +{ + set_pzs(bits); + do_af(); +} + +static void +add(int bits) +{ + int size_mask = (1 << bits) - 1; + + cpu_data = cpu_dest + cpu_src; + set_apzs(bits); + set_of_add(bits); + + /* Anything - FF with carry on is basically anything + 0x100: value stays + unchanged but carry goes on. */ + if ((cpu_alu_op == 2) && !(cpu_src & size_mask) && (cpu_state.flags & C_FLAG)) + cpu_state.flags |= C_FLAG; + else + set_cf((cpu_src & size_mask) > (cpu_data & size_mask)); +} + +static void +sub(int bits) +{ + int size_mask = (1 << bits) - 1; + + cpu_data = cpu_dest - cpu_src; + set_apzs(bits); + set_of_sub(bits); + + /* Anything - FF with carry on is basically anything - 0x100: value stays + unchanged but carry goes on. */ + if ((cpu_alu_op == 3) && !(cpu_src & size_mask) && (cpu_state.flags & C_FLAG)) + cpu_state.flags |= C_FLAG; + else + set_cf((cpu_src & size_mask) > (cpu_dest & size_mask)); +} + +static void +bitwise(int bits, uint16_t data) +{ + cpu_data = data; + cpu_state.flags &= ~(C_FLAG | A_FLAG | V_FLAG); + set_pzs(bits); +} + +static void +test(int bits, uint16_t dest, uint16_t src) +{ + cpu_dest = dest; + cpu_src = src; + bitwise(bits, (cpu_dest & cpu_src)); +} + +static void +alu_op(int bits) +{ + switch (cpu_alu_op) { + case 1: + bitwise(bits, (cpu_dest | cpu_src)); + break; + case 2: + if (cpu_state.flags & C_FLAG) + cpu_src++; + fallthrough; + case 0: + add(bits); + break; + case 3: + if (cpu_state.flags & C_FLAG) + cpu_src++; + fallthrough; + case 5: + case 7: + sub(bits); + break; + case 4: + test(bits, cpu_dest, cpu_src); + break; + case 6: + bitwise(bits, (cpu_dest ^ cpu_src)); + break; + + default: + break; + } +} + +static void +mul(uint16_t a, uint16_t b) +{ + int negate = 0; + int bit_count = 8; + int carry; + uint16_t high_bit = 0x80; + uint16_t size_mask; + uint16_t c; + uint16_t r; + + size_mask = (1 << bit_count) - 1; + + if (opcode != 0xd5) { + if (opcode & 1) { + bit_count = 16; + high_bit = 0x8000; + } else + do_cycles(8); + + size_mask = (1 << bit_count) - 1; + + if ((rmdat & 0x38) == 0x28) { + if (!top_bit(a, bit_count)) { + if (top_bit(b, bit_count)) { + do_cycle(); + if ((b & size_mask) != ((opcode & 1) ? 0x8000 : 0x80)) + do_cycle(); + b = ~b + 1; + negate = 1; + } + } else { + do_cycle(); + a = ~a + 1; + negate = 1; + if (top_bit(b, bit_count)) { + b = ~b + 1; + negate = 0; + } else + do_cycles(4); + } + do_cycles(10); + } + do_cycles(3); + } + + c = 0; + a &= size_mask; + carry = (a & 1) != 0; + a >>= 1; + for (int i = 0; i < bit_count; ++i) { + do_cycles(7); + if (carry) { + cpu_src = c; + cpu_dest = b; + add(bit_count); + c = cpu_data & size_mask; + do_cycles(1); + carry = !!(cpu_state.flags & C_FLAG); + } + r = (c >> 1) + (carry ? high_bit : 0); + carry = (c & 1) != 0; + c = r; + r = (a >> 1) + (carry ? high_bit : 0); + carry = (a & 1) != 0; + a = r; + } + if (negate) { + c = ~c; + a = (~a + 1) & size_mask; + if (a == 0) + ++c; + do_cycles(9); + } + cpu_data = a; + cpu_dest = c; + + set_sf(bit_count); + set_pf(); + set_af(0); +} + +static void +set_co_mul(UNUSED(int bits), int carry) +{ + set_cf(carry); + set_of(carry); + set_zf_ex(!carry); + if (!carry) + do_cycle(); +} + +/* Pushes a word to the stack. */ +static void +push(uint16_t *val) +{ + if ((is186 && !is_nec) && (SP == 1)) { + writememw(ss - 1, 0, *val); + SP = cpu_state.eaaddr = 0xFFFF; + return; + } + SP -= 2; + cpu_state.eaaddr = (SP & 0xffff); + writememw(ss, cpu_state.eaaddr, *val); +} + +/* Pops a word from the stack. */ +static uint16_t +pop(void) +{ + cpu_state.eaaddr = (SP & 0xffff); + SP += 2; + return readmemw(ss, cpu_state.eaaddr); +} + +static void +nearcall(uint16_t new_ip) +{ + uint16_t ret_ip = cpu_state.pc & 0xffff; + + do_cycle_i(); + set_ip(new_ip); + biu_queue_flush(); + do_cycles_i(3); + push(&ret_ip); +} + +static void +farcall2(uint16_t new_cs, uint16_t new_ip) +{ + do_cycles_i(3); + push(&CS); + load_cs(new_cs); + do_cycles_i(2); + nearcall(new_ip); +} + +/* The INTR microcode routine. */ +static void +intr_routine(uint16_t intr, int skip_first) +{ + uint16_t vector = intr * 4; + uint16_t tempf = cpu_state.flags & (is_nec ? 0x8fd7 : 0x0fd7); + uint16_t new_cs; + uint16_t new_ip; + + if (!(cpu_state.flags & MD_FLAG) && is_nec) { + sync_from_i8080(); + x808x_log("CALLN/INT#/NMI#\n"); + } + + if (!skip_first) + do_cycle_i(); + do_cycles_i(2); + + cpu_state.eaaddr = vector & 0xffff; + new_ip = readmemw(0, cpu_state.eaaddr); + do_cycle_i(); + cpu_state.eaaddr = (cpu_state.eaaddr + 2) & 0xffff; + new_cs = readmemw(0, cpu_state.eaaddr); + + biu_suspend_fetch(); + do_cycles_i(2); + push(&tempf); + cpu_state.flags &= ~(I_FLAG | T_FLAG); + if (is_nec) + cpu_state.flags |= MD_FLAG; + do_cycle_i(); + + farcall2(new_cs, new_ip); +} + +/* Was div(), renamed to avoid conflicts with stdlib div(). */ +static int +x86_div(uint16_t l, uint16_t h) +{ + int bit_count = 8; + int negative = 0; + int dividend_negative = 0; + int size_mask; + int carry; + uint16_t r; + + if (opcode & 1) { + l = AX; + h = DX; + bit_count = 16; + } + + size_mask = (1 << bit_count) - 1; + + if (opcode != 0xd4) { + if ((rmdat & 0x38) == 0x38) { + if (top_bit(h, bit_count)) { + h = ~h; + l = (~l + 1) & size_mask; + if (l == 0) + ++h; + h &= size_mask; + negative = 1; + dividend_negative = 1; + do_cycles(4); + } + if (top_bit(cpu_src, bit_count)) { + cpu_src = ~cpu_src + 1; + negative = !negative; + } else + do_cycle(); + do_cycles(9); + } + do_cycles(3); + } + do_cycles(8); + cpu_src &= size_mask; + if (h >= cpu_src) { + if (opcode != 0xd4) + do_cycle(); + intr_routine(0, 0); + return 0; + } + if (opcode != 0xd4) + do_cycle(); + do_cycles(2); + carry = 1; + for (int b = 0; b < bit_count; ++b) { + r = (l << 1) + (carry ? 1 : 0); + carry = top_bit(l, bit_count); + l = r; + r = (h << 1) + (carry ? 1 : 0); + carry = top_bit(h, bit_count); + h = r; + do_cycles(8); + if (carry) { + carry = 0; + h -= cpu_src; + if (b == bit_count - 1) + do_cycles(2); + } else { + carry = cpu_src > h; + if (!carry) { + h -= cpu_src; + do_cycle(); + if (b == bit_count - 1) + do_cycles(2); + } + } + } + l = ~((l << 1) + (carry ? 1 : 0)); + if (opcode != 0xd4 && (rmdat & 0x38) == 0x38) { + do_cycles(4); + if (top_bit(l, bit_count)) { + if (cpu_mod == 3) + do_cycle(); + intr_routine(0, 0); + return 0; + } + do_cycles(7); + if (negative) + l = ~l + 1; + if (dividend_negative) + h = ~h + 1; + } + if (opcode == 0xd4) { + AL = h & 0xff; + AH = l & 0xff; + } else { + AH = h & 0xff; + AL = l & 0xff; + if (opcode & 1) { + DX = h; + AX = l; + } + } + return 1; +} + +static uint16_t +string_increment(int bits) +{ + int d = bits >> 3; + if (cpu_state.flags & D_FLAG) + cpu_state.eaaddr -= d; + else + cpu_state.eaaddr += d; + cpu_state.eaaddr &= 0xffff; + return cpu_state.eaaddr; +} + +static void +lods(int bits) +{ + cpu_state.eaaddr = SI; + if (bits == 16) + cpu_data = readmemw((ovr_seg ? *ovr_seg : ds), cpu_state.eaaddr); + else + cpu_data = readmemb((ovr_seg ? *ovr_seg : ds), cpu_state.eaaddr); + SI = string_increment(bits); +} + +static void +lods_di(int bits) +{ + cpu_state.eaaddr = DI; + if (bits == 16) + cpu_data = readmemw(es, cpu_state.eaaddr); + else + cpu_data = readmemb(es, cpu_state.eaaddr); + DI = string_increment(bits); +} + +static void +stos(int bits) +{ + cpu_state.eaaddr = DI; + if (bits == 16) + writememw(es, cpu_state.eaaddr, cpu_data); + else + writememb(es, cpu_state.eaaddr, (uint8_t) (cpu_data & 0xff)); + DI = string_increment(bits); +} + +static uint16_t +get_ea(void) +{ + if (opcode & 1) + return geteaw(); + else + return (uint16_t) geteab(); +} + +static uint16_t +get_reg(uint8_t reg) +{ + if (opcode & 1) + return cpu_state.regs[reg].w; + else + return (uint16_t) getr8(reg); +} + +static void +set_ea(uint16_t val) +{ + if (opcode & 1) + seteaw(val); + else + seteab((uint8_t) (val & 0xff)); +} + +static void +set_reg(uint8_t reg, uint16_t val) +{ + if (opcode & 1) + cpu_state.regs[reg].w = val; + else + setr8(reg, (uint8_t) (val & 0xff)); +} + +static void +cpu_data_opff_rm(void) +{ + if (!(opcode & 1)) { + if (cpu_mod != 3) + cpu_data |= 0xff00; + else + cpu_data = cpu_state.regs[cpu_rm].w; + } +} + +static void +farcall(uint16_t new_cs, uint16_t new_ip, int jump) +{ + if (jump) + do_cycle_i(); + biu_suspend_fetch(); + do_cycles_i(2); + push(&CS); + load_cs(new_cs); + do_cycles_i(2); + nearcall(new_ip); +} + +/* Calls an interrupt. */ +static void +sw_int(uint16_t intr) +{ + uint16_t vector = intr * 4; + uint16_t tempf = cpu_state.flags & (is_nec ? 0x8fd7 : 0x0fd7); + uint16_t new_cs; + uint16_t new_ip; + uint16_t old_ip; + + if (!(cpu_state.flags & MD_FLAG) && is_nec) { + sync_from_i8080(); + x808x_log("CALLN/INT#/NMI#\n"); + } + + do_cycles_i(3); + cpu_state.eaaddr = vector & 0xffff; + new_ip = readmemw(0, cpu_state.eaaddr); + do_cycle_i(); + cpu_state.eaaddr = (cpu_state.eaaddr + 2) & 0xffff; + new_cs = readmemw(0, cpu_state.eaaddr); + + biu_suspend_fetch(); + do_cycles_i(2); + push(&tempf); + cpu_state.flags &= ~(I_FLAG | T_FLAG); + if (is_nec) + cpu_state.flags |= MD_FLAG; + + /* FARCALL2 */ + do_cycles_i(4); + push(&CS); + load_cs(new_cs); + do_cycle_i(); + + /* NEARCALL */ + old_ip = cpu_state.pc & 0xffff; + do_cycles_i(2); + set_ip(new_ip); + biu_queue_flush(); + do_cycles_i(3); + push(&old_ip); +} + +static void +int3(void) +{ + do_cycles_i(4); + intr_routine(3, 0); +} + +/* Ditto, but for breaking into emulation mode. */ +static void +interrupt_brkem(uint16_t addr) +{ + uint16_t tempf = cpu_state.flags & (is_nec ? 0x8fd7 : 0x0fd7); + uint16_t new_cs; + uint16_t new_ip; + uint16_t old_ip; + + do_cycles_i(3); + cpu_state.eaaddr = addr << 2; + new_ip = readmemw(0, cpu_state.eaaddr); + do_cycle_i(); + cpu_state.eaaddr = (cpu_state.eaaddr + 2) & 0xffff; + new_cs = readmemw(0, cpu_state.eaaddr); + + biu_suspend_fetch(); + do_cycles_i(2); + push(&tempf); + cpu_state.flags &= ~(MD_FLAG); + cpu_md_write_disable = 0; + + /* FARCALL2 */ + do_cycles_i(4); + push(&CS); + load_cs(new_cs); + do_cycle_i(); + + /* NEARCALL */ + old_ip = cpu_state.pc & 0xffff; + do_cycles_i(2); + set_ip(new_ip); + biu_queue_flush(); + do_cycles_i(3); + push(&old_ip); + + sync_to_i8080(); + x808x_log("BRKEM mode\n"); +} + +void +retem_i8080(void) +{ + sync_from_i8080(); + + do_cycle_i(); + farret(1); + /* pop_flags() */ + cpu_state.flags = pop(); + do_cycle_i(); + + noint = 1; + nmi_enable = 1; + + emulated_processor.iff = !!(cpu_state.flags & I_FLAG); + + cpu_md_write_disable = 1; + + retem = 1; + + x808x_log("RETEM mode\n"); +} + +void +interrupt_808x(uint16_t addr) +{ + biu_suspend_fetch(); + do_cycles_i(2); + + intr_routine(addr, 0); +} + +static void +custom_nmi(void) +{ + uint16_t tempf = cpu_state.flags & (is_nec ? 0x8fd7 : 0x0fd7); + uint16_t new_cs; + uint16_t new_ip; + + if (!(cpu_state.flags & MD_FLAG) && is_nec) { + sync_from_i8080(); + x808x_log("CALLN/INT#/NMI#\n"); + } + + do_cycle_i(); + do_cycles_i(2); + + cpu_state.eaaddr = 0x0002; + (void) readmemw(0, cpu_state.eaaddr); + new_ip = custom_nmi_vector & 0xffff; + do_cycle_i(); + cpu_state.eaaddr = (cpu_state.eaaddr + 2) & 0xffff; + (void) readmemw(0, cpu_state.eaaddr); + new_cs = custom_nmi_vector >> 16; + + biu_suspend_fetch(); + do_cycles_i(2); + push(&tempf); + cpu_state.flags &= ~(I_FLAG | T_FLAG); + if (is_nec) + cpu_state.flags |= MD_FLAG; + do_cycle_i(); + + farcall2(new_cs, new_ip); +} + +static int +irq_pending(void) +{ + uint8_t temp; + + temp = (nmi && nmi_enable && nmi_mask) || ((cpu_state.flags & T_FLAG) && !noint) || + ((in_hlt || (cpu_state.flags & I_FLAG)) && pic.int_pending && !noint); + + return temp; +} + +static int +bus_pic_ack(void) +{ + int old_in_lock = in_lock; + + in_lock = 1; + bus_request_type = BUS_PIC; + biu_begin_eu(); + biu_wait_for_read_finish(); + in_lock = old_in_lock; + return pic_data; +} + +static void +hw_int(uint16_t vector) +{ + biu_suspend_fetch(); + do_cycles_i(2); + + intr_routine(vector, 0); +} + +static void +int1(void) +{ + do_cycles_i(2); + intr_routine(1, 1); +} + +static void +int2(void) +{ + do_cycles_i(2); + intr_routine(2, 1); +} + +static void +check_interrupts(void) +{ + int temp; + + if (irq_pending()) { + if ((cpu_state.flags & T_FLAG) && !noint) { + int1(); + return; + } + if (nmi && nmi_enable && nmi_mask) { + nmi_enable = 0; + if (use_custom_nmi_vector) { + do_cycles(2); + custom_nmi(); + } else + int2(); + return; + } + if ((in_hlt || (cpu_state.flags & I_FLAG)) && pic.int_pending && !noint) { + repeating = 0; + completed = 1; + ovr_seg = NULL; + do_cycles(4); + /* ACK to PIC */ + biu_begin_eu(); + temp = bus_pic_ack(); + do_cycle(); + /* ACK to PIC */ + temp = bus_pic_ack(); + in_lock = 0; + clear_lock = 0; + /* Here is where temp should be filled, but we cheat. */ + opcode = 0x00; + hw_int(temp); + } + } +} + +/* The FARRET microcode routine. */ +static void +farret(int far) +{ + uint8_t far2 = !!(opcode & 0x08); + + do_cycle_i(); + set_ip(pop()); + biu_suspend_fetch(); + do_cycles_i(2); + + if ((!!far) != far2) + fatal("Far call distance mismatch (%i = %i)\n", !!far, far2); + + if (far) { + do_cycle_i(); + load_cs(pop()); + + biu_queue_flush(); + do_cycles_i(2); + } else { + biu_queue_flush(); + do_cycles_i(2); + } + + do_cycles_i(2); +} + +static void +iret_routine(void) +{ + do_cycle_i(); + farret(1); + /* pop_flags() */ + if (is_nec && cpu_md_write_disable) + cpu_state.flags = pop() | 0x8002; + else + cpu_state.flags = pop() | 0x0002; + do_cycle_i(); + noint = 1; + nmi_enable = 1; +} + +static void +rep_end(void) +{ + repeating = 0; + in_rep = 0; + completed = 1; +} + +static int +rep_start(void) +{ + if (!repeating) { + if (in_rep != 0) { + if (CX == 0) { + do_cycles_i(is_nec ? 1 : 4); + rep_end(); + return 0; + } else + do_cycles_i(is_nec ? 1 : 7); + } + } + + completed = 1; + return 1; +} + +static void +rep_interrupt(void) +{ + biu_suspend_fetch(); + do_cycles_i(4); + biu_queue_flush(); + + if (is_nec && (ovr_seg != NULL)) + set_ip((cpu_state.pc - 3) & 0xffff); + else + set_ip((cpu_state.pc - 2) & 0xffff); + + rep_end(); +} + +static void +sign_extend_al(void) +{ + if ((AL & 0x80) != 0) + AH = 0xff; + else + AH = 0x00; +} + +static void +sign_extend_ax(void) +{ + do_cycles(3); + if ((AX & 0x8000) == 0) + DX = 0x0000; + else { + do_cycle(); + DX = 0xffff; + } +} + +static void +reljmp(uint16_t new_ip, int jump) +{ + if (!is_nec && jump) + do_cycle_i(); + + biu_suspend_fetch(); + if (!is_nec) + do_cycles_i(3); + set_ip(new_ip); + biu_queue_flush(); + do_cycle_i(); +} + +static void +daa(void) +{ + uint16_t old_cf = cpu_state.flags & C_FLAG; + uint16_t old_af = cpu_state.flags & A_FLAG; + uint8_t old_al = AL; + uint8_t al_check; + + cpu_state.flags &= ~C_FLAG; + + al_check = (old_af ? 0x9f : 0x99); + + cpu_state.flags &= ~V_FLAG; + if (old_cf) { + if ((AL >= 0x1a) && (AL <= 0x7f)) + cpu_state.flags |= V_FLAG; + } else if ((AL >= 0x7a) && (AL <= 0x7f)) + cpu_state.flags |= V_FLAG; + + if (((AL & 0x0f) > 9) || (cpu_state.flags & A_FLAG)) { + AL += 6; + cpu_state.flags |= A_FLAG; + } else + cpu_state.flags &= ~A_FLAG; + + if ((old_al > al_check) || old_cf) { + AL += 0x60; + cpu_state.flags |= C_FLAG; + } else + cpu_state.flags &= ~C_FLAG; + + set_pzs(8); +} + +static void +das(void) +{ + uint8_t old_al = AL; + uint16_t old_af = cpu_state.flags & A_FLAG; + uint16_t old_cf = cpu_state.flags & C_FLAG; + uint8_t al_check = (old_af ? 0x9f : 0x99); + + cpu_state.flags &= ~V_FLAG; + + if (!old_af && !old_cf) { + if ((AL >= 0x9a) && (AL <= 0xdf)) + cpu_state.flags |= V_FLAG; + } else if (old_af && !old_cf) { + if (((AL >= 0x80) && (AL <= 0x85)) || ((AL >= 0xa0) && (AL <= 0xe5))) + cpu_state.flags |= V_FLAG; + } else if (!old_af && old_cf) { + if ((AL >= 0x80) && (AL <= 0xdf)) + cpu_state.flags |= V_FLAG; + } else if (old_af && old_cf) { + if ((AL >= 0x80) && (AL <= 0xe5)) + cpu_state.flags |= V_FLAG; + } + + cpu_state.flags &= ~C_FLAG; + if (((AL & 0x0f) > 9) || (cpu_state.flags & A_FLAG)) { + AL -= 6; + cpu_state.flags |= A_FLAG; + } else + cpu_state.flags &= ~A_FLAG; + + if ((old_al > al_check) || old_cf) { + AL -= 0x60; + cpu_state.flags |= C_FLAG; + } else + cpu_state.flags &= ~C_FLAG; + + set_pzs(8); +} + +static void +aaa(void) +{ + uint8_t old_al = AL; + uint8_t new_al; + + if (((AL & 0x0f) > 9) || (cpu_state.flags & A_FLAG)) { + AH += 1; + new_al = AL + 6; + AL = new_al & 0x0f; + cpu_state.flags |= A_FLAG; + cpu_state.flags |= C_FLAG; + } else { + new_al = AL; + AL &= 0x0f; + cpu_state.flags &= ~A_FLAG; + cpu_state.flags &= ~C_FLAG; + do_cycle_i(); + } + + cpu_state.flags &= ~(V_FLAG | Z_FLAG | N_FLAG); + if (new_al == 0x00) + cpu_state.flags |= Z_FLAG; + if ((old_al >= 0x7a) && (old_al <= 0x7f)) + cpu_state.flags |= V_FLAG; + if ((old_al >= 0x7a) && (old_al <= 0xf9)) + cpu_state.flags |= N_FLAG; + + cpu_data = new_al; + set_pf(); +} + +static void +aas(void) +{ + uint8_t old_al = AL; + uint16_t old_af = cpu_state.flags & A_FLAG; + uint8_t new_al; + + do_cycles_i(6); + + if (((AL & 0x0f) > 9) || old_af) { + new_al = AL - 6; + AH++; + AL = new_al & 0x0f; + cpu_state.flags |= (A_FLAG | C_FLAG); + } else { + new_al = AL; + AL &= 0x0f; + cpu_state.flags &= ~(C_FLAG | A_FLAG); + do_cycle_i(); + } + + cpu_state.flags &= ~(V_FLAG | Z_FLAG | N_FLAG); + if (new_al == 0x00) + cpu_state.flags |= Z_FLAG; + if (old_af && (old_al >= 0x80) && (old_al <= 0x85)) + cpu_state.flags |= V_FLAG; + if (!old_af && (old_al >= 0x80)) + cpu_state.flags |= N_FLAG; + if (old_af && ((old_al <= 0x05) || (old_al >= 0x86))) + cpu_state.flags |= N_FLAG; + + cpu_data = new_al; + set_pf(); +} + +static void +finalize(void) +{ + in_0f = 0; + repeating = 0; + ovr_seg = NULL; + in_rep = 0; + rep_c_flag = 0; + if (in_lock) + clear_lock = 1; + cpu_alu_op = 0; + + if (pfq_pos == 0) { + do { + if (nx) + nx = 0; + do_cycle(); + } while (pfq_pos == 0); + biu_preload_byte = biu_pfq_read(); + biu_queue_preload = 1; + do_cycle(); + } else { + biu_queue_preload = 1; + biu_preload_byte = biu_pfq_read(); + + biu_resume_on_queue_read(); + + do_cycle(); + } + + if (irq_pending()) + cpu_state.pc--; +} + +/* Fetches the effective address from the prefetch queue according to MOD and R/M. */ +static void +do_mod_rm(void) +{ + rmdat = biu_pfq_fetchb(); + cpu_reg = (rmdat >> 3) & 7; + cpu_mod = (rmdat >> 6) & 3; + cpu_rm = rmdat & 7; + + if (cpu_mod != 3) { + do_cycle(); + if (is_nec) + do_cycle(); + else if (modrm_cycs_pre[rmdat & 0xc7]) + do_cycles(modrm_cycs_pre[rmdat & 0xc7]); + + if ((rmdat & 0xc7) == 0x06) { + cpu_state.eaaddr = biu_pfq_fetchw(); + easeg = ovr_seg ? *ovr_seg : ds; + } else { + cpu_state.eaaddr = (*mod1add[0][cpu_rm]) + (*mod1add[1][cpu_rm]); + easeg = ovr_seg ? *ovr_seg : *mod1seg[cpu_rm]; + switch (rmdat & 0xc0) { + default: + break; + case 0x40: + cpu_state.eaaddr += sign_extend(biu_pfq_fetchb()); + break; + case 0x80: + cpu_state.eaaddr += biu_pfq_fetchw(); + break; + } + cpu_state.eaaddr &= 0xffff; + } + + if (!is_nec && modrm_cycs_post[rmdat & 0xc7]) + do_cycles(modrm_cycs_post[rmdat & 0xc7]); + } +} + +static void +decode(void) +{ + uint8_t op_f; + uint8_t prefix = 0; + + if (halted) + opcode = 0xf4; + else + opcode = biu_pfq_fetchb_common(); + + modrm_loaded = 0; + + while (1) { + prefix = 0; + + switch (opcode) { + case 0x0f: /* NEC/186 */ + if (is_nec) { + in_0f = 1; + prefix = 1; + } + break; + case 0x26: /* ES: */ + case 0x2e: /* CS: */ + case 0x36: /* SS: */ + case 0x3e: /* DS: */ + ovr_seg = opseg[(opcode >> 3) & 0x03]; + prefix = 1; + break; + case 0x64: /* REPNC */ + case 0x65: /* REPC */ + if (is_nec) { + in_rep = (opcode == 0x64 ? 1 : 2); + rep_c_flag = 1; + prefix = 1; + } + break; + case 0xf0: + case 0xf1: /* LOCK - F1 is alias */ + in_lock = 1; + prefix = 1; + break; + case 0xf2: /* REPNE */ + case 0xf3: /* REPE */ + in_rep = (opcode == 0xf2 ? 1 : 2); + rep_c_flag = 0; + prefix = 1; + break; + default: + break; + } + + if (prefix == 0) + break; + + do_cycle(); + + opcode = biu_pfq_fetchb_common(); + } + + if (is_nec) { + if (in_0f) + op_f = (uint8_t) opf_0f[opcode]; + else + op_f = (uint8_t) opf_nec[opcode]; + } else + op_f = (uint8_t) opf[opcode]; + + if (op_f & OP_GRP) { + do_mod_rm(); + modrm_loaded = 1; + + op_f |= (OP_MRM | OP_EA); + + if (opcode >= 0xf0) { + op_f |= OP_DELAY; + group_delay = 1; + } + } + + if (!modrm_loaded && (op_f & OP_MRM)) { + do_mod_rm(); + modrm_loaded = 1; + } + + if (modrm_loaded && !(op_f & OP_EA)) { + if (is_nec) + do_cycle(); + else { + if (opcode == 0x8f) { + if (cpu_mod == 3) + do_cycles_i(2); + } else + do_cycles_i(2); + } + } +} + +static void +string_op(int bits) +{ + uint16_t tmpa; + uint16_t old_ax; + + if ((opcode & 0xf0) == 0x60) switch (opcode & 0x0e) { + case 0x0c: + old_ax = AX; + cpu_data = DX; + cpu_state.eaaddr = cpu_data; + cpu_io_vx0(bits, 0, cpu_state.eaaddr); + cpu_data = AX; + stos(bits); + AX = old_ax; + break; + case 0x0e: + old_ax = AX; + lods(bits); + set_accum(bits, cpu_data); + cpu_data = DX; + do_cycle_i(); + /* biu_io_write_u16() */ + cpu_state.eaaddr = cpu_data; + cpu_io_vx0(bits, 1, cpu_state.eaaddr); + AX = old_ax; + break; + } else switch (opcode & 0x0e) { + case 0x04: + lods(bits); + do_cycle_i(); + stos(bits); + break; + case 0x06: + do_cycle_i(); + if (is_nec) { + if (in_rep) { + lods_di(bits); + tmpa = cpu_data; + lods(bits); + } else { + lods(bits); + tmpa = cpu_data; + lods_di(bits); + } + } else { + lods(bits); + tmpa = cpu_data; + do_cycles_i(2); + lods_di(bits); + do_cycles_i(3); + } + + cpu_src = cpu_data; + cpu_dest = tmpa; + sub(bits); + break; + case 0x0e: + tmpa = AX; + do_cycles_i(2); + lods_di(bits); + do_cycles_i(3); + + cpu_src = cpu_data; + cpu_dest = tmpa; + sub(bits); + break; + case 0x0a: + cpu_data = AX; + stos(bits); + break; + case 0x0c: + lods(bits); + set_accum(bits, cpu_data); + break; + } +} + +static int do_print = 1; + +static void +execvx0_0f(void) +{ + uint8_t bit; + uint8_t odd; + uint8_t nibbles_count; + uint8_t destcmp; + uint8_t destbyte; + uint8_t srcbyte; + uint8_t nibble_result; + uint8_t temp_val; + uint8_t temp_al; + uint8_t bit_length; + uint8_t bit_offset; + int8_t nibble_result_s; + int bits; + uint32_t i; + uint32_t carry; + uint32_t nibble; + uint32_t srcseg; + uint32_t byteaddr; + + switch (opcode) { + case 0x10: /* TEST1 r8/m8, CL*/ + case 0x11: /* TEST1 r16/m16, CL*/ + case 0x18: /* TEST1 r8/m8, imm3 */ + case 0x19: /* TEST1 r16/m16, imm4 */ + bits = 8 << (opcode & 0x1); + do_cycles(2); + + bit = (opcode & 0x8) ? biu_pfq_fetchb() : CL; + bit &= ((1 << (3 + (opcode & 0x1))) - 1); + read_ea(0, bits); + + set_zf_ex(!(cpu_data & (1 << bit))); + cpu_state.flags &= ~(V_FLAG | C_FLAG); + break; + + case 0x12: /* CLR1 r8/m8, CL*/ + case 0x13: /* CLR1 r16/m16, CL*/ + case 0x1a: /* CLR1 r8/m8, imm3 */ + case 0x1b: /* CLR1 r16/m16, imm4 */ + bits = 8 << (opcode & 0x1); + do_cycles(2); + + bit = (opcode & 0x8) ? biu_pfq_fetchb() : CL; + bit &= ((1 << (3 + (opcode & 0x1))) - 1); + read_ea(0, bits); + + if (bits == 8) + seteab((cpu_data & 0xff) & ~(1 << bit)); + else + seteaw((cpu_data & 0xffff) & ~(1 << bit)); + break; + + case 0x14: /* SET1 r8/m8, CL*/ + case 0x15: /* SET1 r16/m16, CL*/ + case 0x1c: /* SET1 r8/m8, imm3 */ + case 0x1d: /* SET1 r16/m16, imm4 */ + bits = 8 << (opcode & 0x1); + do_cycles(2); + + bit = (opcode & 0x8) ? biu_pfq_fetchb() : CL; + bit &= ((1 << (3 + (opcode & 0x1))) - 1); + read_ea(0, bits); + + if (bits == 8) + seteab((cpu_data & 0xff) | (1 << bit)); + else + seteaw((cpu_data & 0xffff) | (1 << bit)); + break; + + case 0x16: /* NOT1 r8/m8, CL*/ + case 0x17: /* NOT1 r16/m16, CL*/ + case 0x1e: /* NOT1 r8/m8, imm3 */ + case 0x1f: /* NOT1 r16/m16, imm4 */ + bits = 8 << (opcode & 0x1); + do_cycles(2); + + bit = (opcode & 0x8) ? (biu_pfq_fetchb()) : (CL); + bit &= ((1 << (3 + (opcode & 0x1))) - 1); + read_ea(0, bits); + + if (bits == 8) + seteab((cpu_data & 0xff) ^ (1 << bit)); + else + seteaw((cpu_data & 0xffff) ^ (1 << bit)); + break; + + case 0x20: /* ADD4S */ + odd = !!(CL % 2); + zero = 1; + nibbles_count = CL - odd; + i = 0; + carry = 0; + nibble = 0; + srcseg = ovr_seg ? *ovr_seg : ds; + + do_cycles(4); + + for (i = 0; i < ((nibbles_count / 2) + odd); i++) { + do_cycles(19); + destcmp = read_mem_b((es) + DI + i); + + for (nibble = 0; nibble < 2; nibble++) { + destbyte = destcmp >> (nibble ? 4 : 0); + srcbyte = read_mem_b(srcseg + SI + i) >> (nibble ? 4 : 0); + destbyte &= 0xF; + srcbyte &= 0xF; + nibble_result = (i == (nibbles_count / 2) && nibble == 1) ? (destbyte + carry) : + ((uint8_t) (destbyte)) + ((uint8_t) (srcbyte)) + ((uint32_t) carry); + carry = 0; + + while (nibble_result >= 10) { + nibble_result -= 10; + carry++; + } + + if (zero != 0 || (i == (nibbles_count / 2) && nibble == 1)) + zero = (nibble_result == 0); + + destcmp = ((destcmp & (nibble ? 0x0F : 0xF0)) | (nibble_result << (4 * nibble))); + } + + write_mem_b(es + DI + i, destcmp); + } + + set_cf(!!carry); + set_zf(!!zero); + break; + + case 0x22: /* SUB4S */ + odd = !!(CL % 2); + zero = 1; + nibbles_count = CL - odd; + i = 0; + carry = 0; + nibble = 0; + srcseg = ovr_seg ? *ovr_seg : ds; + + do_cycles(4); + + for (i = 0; i < ((nibbles_count / 2) + odd); i++) { + do_cycles(19); + destcmp = read_mem_b((es) + DI + i); + + for (nibble = 0; nibble < 2; nibble++) { + destbyte = destcmp >> (nibble ? 4 : 0); + srcbyte = read_mem_b(srcseg + SI + i) >> (nibble ? 4 : 0); + destbyte &= 0xF; + srcbyte &= 0xF; + nibble_result_s = (i == (nibbles_count / 2) && nibble == 1) ? ((int8_t) destbyte - (int8_t) carry) : + ((int8_t) (destbyte)) - ((int8_t) (srcbyte)) - ((int8_t) carry); + carry = 0; + + while (nibble_result_s < 0) { + nibble_result_s += 10; + carry++; + } + + if (zero != 0 || (i == (nibbles_count / 2) && nibble == 1)) + zero = (nibble_result_s == 0); + + destcmp = ((destcmp & (nibble ? 0x0F : 0xF0)) | (nibble_result_s << (4 * nibble))); + } + + write_mem_b(es + DI + i, destcmp); + } + + set_cf(!!carry); + set_zf(!!zero); + break; + + case 0x26: /* CMP4S */ + odd = !!(CL % 2); + zero = 1; + nibbles_count = CL - odd; + i = 0; + carry = 0; + nibble = 0; + srcseg = ovr_seg ? *ovr_seg : ds; + + do_cycles(4); + + for (i = 0; i < ((nibbles_count / 2) + odd); i++) { + do_cycles(19); + destcmp = read_mem_b((es) + DI + i); + + for (nibble = 0; nibble < 2; nibble++) { + destbyte = destcmp >> (nibble ? 4 : 0); + srcbyte = read_mem_b(srcseg + SI + i) >> (nibble ? 4 : 0); + destbyte &= 0xF; + srcbyte &= 0xF; + nibble_result_s = ((int8_t) (destbyte)) - ((int8_t) (srcbyte)) - ((int8_t) carry); + carry = 0; + + while (nibble_result_s < 0) { + nibble_result_s += 10; + carry++; + } + + if (zero != 0 || (i == (nibbles_count / 2) && nibble == 1)) + zero = (nibble_result_s == 0); + + destcmp = ((destcmp & (nibble ? 0x0F : 0xF0)) | (nibble_result_s << (4 * nibble))); + } + } + + set_cf(!!carry); + set_zf(!!zero); + break; + + case 0x28: /* ROL4 r/m */ + do_cycles(20); + + temp_val = geteab(); + temp_al = AL; + + temp_al &= 0x0f; + temp_al |= (temp_val & 0xf0); + temp_val = (temp_al & 0x0f) | ((temp_val & 0x0f) << 4); + temp_al >>= 4; + temp_al &= 0x0f; + seteab(temp_val); + AL = temp_al; + break; + + case 0x2a: /* ROR4 r/m */ + do_cycles(20); + + temp_val = geteab(); + temp_al = AL; + + AL = temp_val & 0x0f; + temp_val = (temp_val >> 4) | ((temp_al & 0x0f) << 4); + + seteab(temp_val); + break; + + case 0x31: /* INS reg1, reg2 */ + case 0x39: /* INS reg8, imm4 */ + bit_length = ((opcode & 0x8) ? (biu_pfq_fetchb() & 0x0f) : (getr8(cpu_reg) & 0x0f)) + 1; + bit_offset = getr8(cpu_rm) & 0x0f; + byteaddr = (es) + DI; + i = 0; + + if (bit_offset >= 8) { + DI++; + byteaddr++; + bit_offset -= 8; + } + + for (i = 0; i < bit_length; i++) { + byteaddr = (es) + DI; + writememb(es, DI, (read_mem_b(byteaddr) & ~(1 << (bit_offset))) | ((!!(AX & (1 << i))) << bit_offset)); + bit_offset++; + + if (bit_offset == 8) { + DI++; + bit_offset = 0; + } + } + + setr8(cpu_rm, bit_offset); + break; + + case 0x33: /* EXT reg1, reg2 */ + case 0x3b: /* EXT reg8, imm4 */ + bit_length = ((opcode & 0x8) ? (biu_pfq_fetchb() & 0x0f) : (getr8(cpu_reg) & 0x0f)) + 1; + bit_offset = getr8(cpu_rm) & 0x0f; + byteaddr = (ds) + SI; + i = 0; + + if (bit_offset >= 8) { + SI++; + byteaddr++; + bit_offset -= 8; + } + + AX = 0; + + for (i = 0; i < bit_length; i++) { + byteaddr = (ds) + SI; + AX |= (!!(readmemb(ds, SI) & (1 << bit_offset))) << i; + bit_offset++; + + if (bit_offset == 8) { + SI++; + bit_offset = 0; + } + } + + setr8(cpu_rm, bit_offset); + break; + + case 0xff: /* BRKEM */ + interrupt_brkem(biu_pfq_fetchb()); + break; + + default: + do_cycles_nx_i(2); /* Guess, based on NOP. */ + break; + } +} + +static void +execvx0_6x(uint16_t *jump) +{ + uint16_t lowbound; + uint16_t highbound; + uint16_t regval; + uint16_t wordtopush; + uint16_t immediate; + uint16_t tempw; + int bits; + int32_t templ; + + switch (opcode) { + case 0x60: /* PUSHA/PUSH R */ + writememw(ss, ((SP - 2) & 0xffff), AX); + biu_state_set_eu(); + writememw(ss, ((SP - 4) & 0xffff), CX); + biu_state_set_eu(); + writememw(ss, ((SP - 6) & 0xffff), DX); + biu_state_set_eu(); + writememw(ss, ((SP - 8) & 0xffff), BX); + biu_state_set_eu(); + writememw(ss, ((SP - 10) & 0xffff), SP); + biu_state_set_eu(); + writememw(ss, ((SP - 12) & 0xffff), BP); + biu_state_set_eu(); + writememw(ss, ((SP - 14) & 0xffff), SI); + biu_state_set_eu(); + writememw(ss, ((SP - 16) & 0xffff), DI); + SP -= 16; + break; + + case 0x61: /* POPA/POP R */ + DI = readmemw(ss, ((SP) & 0xffff)); + biu_state_set_eu(); + SI = readmemw(ss, ((SP + 2) & 0xffff)); + biu_state_set_eu(); + BP = readmemw(ss, ((SP + 4) & 0xffff)); + biu_state_set_eu(); + BX = readmemw(ss, ((SP + 8) & 0xffff)); + biu_state_set_eu(); + DX = readmemw(ss, ((SP + 10) & 0xffff)); + biu_state_set_eu(); + CX = readmemw(ss, ((SP + 12) & 0xffff)); + biu_state_set_eu(); + AX = readmemw(ss, ((SP + 14) & 0xffff)); + SP += 16; + break; + + case 0x62: /* BOUND r/m */ + lowbound = 0; + highbound = 0; + regval = 0; + + lowbound = readmemw(easeg, cpu_state.eaaddr); + highbound = readmemw(easeg, cpu_state.eaaddr + 2); + regval = get_reg(cpu_reg); + + if ((lowbound > regval) || (highbound < regval)) { + sw_int(5); + *jump = 1; + } + break; + + case 0x63: + if (is_nec) { + /* read_operand16() */ + if (cpu_mod != 3) + do_cycles_i(2); /* load_operand() */ + tempw = cpu_state.pc; + geteaw(); + do_cycles(60); + } + break; + + case 0x64: + case 0x65: + if (!is_nec) { + do_cycles_nx_i(2); /* Guess, based on NOP. */ + } + break; + + case 0x66 ... 0x67: /* FPO2 - NEC FPU instructions. */ + /* read_operand16() */ + if (cpu_mod != 3) + do_cycles_i(2); /* load_operand() */ + tempw = cpu_state.pc; + geteaw(); + /* fpu_op() */ + cpu_state.pc = tempw; /* Do this as the x87 code advances it, which is needed on + the 286+ core, but not here. */ + break; + + case 0x68: + wordtopush = biu_pfq_fetchw(); + push(&wordtopush); + break; + + case 0x69: + bits = 16; + read_ea(0, 16); + immediate = biu_pfq_fetchw(); + + templ = ((int) cpu_data) * ((int) immediate); + if ((templ >> 15) != 0 && (templ >> 15) != -1) + cpu_state.flags |= C_FLAG | V_FLAG; + else + cpu_state.flags &= ~(C_FLAG | V_FLAG); + set_reg(cpu_reg, templ & 0xffff); + do_cycles((cpu_mod == 3) ? 20 : 26); + break; + + case 0x6a: + wordtopush = sign_extend(biu_pfq_fetchb()); + push(&wordtopush); + break; + + case 0x6b: /* IMUL reg16,reg16/mem16,imm8 */ + read_ea(0, 16); + immediate = biu_pfq_fetchb(); + immediate = geteaw(); + if (immediate & 0x80) + immediate |= 0xff00; + + templ = ((int) cpu_data) * ((int) immediate); + if ((templ >> 15) != 0 && (templ >> 15) != -1) + cpu_state.flags |= C_FLAG | V_FLAG; + else + cpu_state.flags &= ~(C_FLAG | V_FLAG); + set_reg(cpu_reg, templ & 0xffff); + do_cycles((cpu_mod == 3) ? 24 : 30); + break; + + case 0x6c: + case 0x6d: /* INM dst, DW/INS dst, DX */ + bits = 8 << (opcode & 1); + if (rep_start()) { + string_op(bits); + do_cycle_i(); + + if (in_rep != 0) { + completed = 0; + repeating = 1; + + do_cycle_i(); + if (irq_pending()) { + do_cycle_i(); + rep_interrupt(); + } + + do_cycle_i(); + /* decrement_register16() */ + CX--; + if (CX == 0) + rep_end(); + else + do_cycle_i(); + } else + do_cycle_i(); + } + break; + + case 0x6e: + case 0x6f: /* OUTM DW, src/OUTS DX, src */ + bits = 8 << (opcode & 1); + if (rep_start()) { + string_op(bits); + do_cycles_i(3); + + if (in_rep != 0) { + completed = 0; + repeating = 1; + + do_cycle_i(); + /* decrement_register16() */ + CX--; + + if (irq_pending()) { + do_cycles_i(2); + rep_interrupt(); + } else { + do_cycles_i(2); + + if (CX == 0) + rep_end(); + else + do_cycle_i(); + } + } + } + break; + + default: + do_cycles_nx_i(2); /* Guess, based on NOP. */ + break; + } +} + +/* Executes a single instruction. */ +void +execute_instruction(void) +{ + uint8_t temp = 0; + uint8_t tempb; + uint8_t nests; + + int8_t rel8; + int8_t temps; + + uint16_t addr; + uint16_t jump = 0; + uint16_t new_cs; + uint16_t new_ip; + uint16_t tempw; + uint16_t zero_cond; + uint16_t old_cs; + uint16_t old_ip; + uint16_t old_flags; + uint16_t prod16; + uint16_t tempw_int; + uint16_t size; + uint16_t tempbp; + uint16_t src16; + + int16_t rel16; + int16_t temps16; + + uint32_t prod32; + uint32_t templ; + uint32_t templ2 = 0; + + int bits; + int negate; + int tempws = 0; + int tempws2 = 0; + + completed = 1; + + if (nx) { + do_cycle(); + nx = 0; + } else if (!modrm_loaded) + do_cycle(); + + if (group_delay) { + group_delay = 0; + do_cycle(); + nx = 0; + } + + if (!is_nec && (opcode >= 0xc0) && (opcode <= 0xc1)) + opcode |= 0x02; + + if (is_nec && !(cpu_state.flags & MD_FLAG)) { + i8080_step(&emulated_processor); + set_if(emulated_processor.iff); + if (retem) + jump = 1; + retem = 0; + do_cycles(emulated_processor.cyc); + emulated_processor.cyc = 0; + } else if (is_nec && in_0f) + execvx0_0f(); + else if (is_nec && ((opcode & 0xf0) == 0x60)) + execvx0_6x(&jump); + else switch (opcode) { + case 0x00: /* ADD r/m8, r8; r8, r/m8; al, imm8 */ + case 0x02: + case 0x04: + case 0x08: /* OR r/m8, r8; r8, r/m8; al, imm8 */ + case 0x0a: + case 0x0c: + case 0x10: /* ADC r/m8, r8; r8, r/m8; al, imm8 */ + case 0x12: + case 0x14: + case 0x18: /* SBB r/m8, r8; r8, r/m8; al, imm8 */ + case 0x1a: + case 0x1c: + case 0x20: /* AND r/m8, r8; r8, r/m8; al, imm8 */ + case 0x22: + case 0x24: + case 0x28: /* SUB r/m8, r8; r8, r/m8; al, imm8 */ + case 0x2a: + case 0x2c: + case 0x30: /* XOR r/m8, r8; r8, r/m8; al, imm8 */ + case 0x32: + case 0x34: + bits = 8; + /* read_operand8() */ + if (opcode & 0x04) { + cpu_data = biu_pfq_fetch(); + cpu_dest = get_accum(bits); /* AX/AL */ + cpu_src = cpu_data; + } else { + if (cpu_mod != 3) + do_cycles_i(2); /* load_operand() */ + tempw = get_ea(); + if (opcode & 2) { + cpu_dest = get_reg(cpu_reg); + cpu_src = tempw; + } else { + cpu_dest = tempw; + cpu_src = get_reg(cpu_reg); + } + } + do_cycles_nx_i(2); + if (!(opcode & 0x06) && (cpu_mod != 3)) + do_cycles_i(2); + + /* math_op8() */ + math_op((opcode >> 3) & 7); + /* write_operand8() */ + if (opcode & 0x04) + set_accum(bits, cpu_data); + else { + if (opcode & 2) + set_reg(cpu_reg, cpu_data); + else + set_ea(cpu_data); + } + break; + + case 0x01: /* ADD r/m16, r16; r16, r/m16; ax, imm16 */ + case 0x03: + case 0x05: + case 0x09: /* OR r/m16, r16; r16, r/m16; ax, imm16 */ + case 0x0b: + case 0x0d: + case 0x11: /* ADC r/m16, r16; r16, r/m16; ax, imm16 */ + case 0x13: + case 0x15: + case 0x19: /* SBB r/m16, r16; r16, r/m16; ax, imm16 */ + case 0x1b: + case 0x1d: + case 0x21: /* AND r/m16, r16; r16, r/m16; ax, imm16 */ + case 0x23: + case 0x25: + case 0x29: /* SUB r/m16, r16; r16, r/m16; ax, imm16 */ + case 0x2b: + case 0x2d: + case 0x31: /* XOR r/m16, r16; r16, r/m16; ax, imm16 */ + case 0x33: + case 0x35: + bits = 16; + nx = 1; + /* read_operand16() */ + if (opcode & 0x04) { + cpu_data = biu_pfq_fetch(); + cpu_dest = get_accum(bits); /* AX/AL */ + cpu_src = cpu_data; + } else { + if (cpu_mod != 3) + do_cycles_i(is_nec ? 1 : 2); /* load_operand() */ + tempw = get_ea(); + if (opcode & 2) { + cpu_dest = get_reg(cpu_reg); + cpu_src = tempw; + } else { + cpu_dest = tempw; + cpu_src = get_reg(cpu_reg); + } + } + do_cycles_nx_i(2); + if (!(opcode & 0x06) && (cpu_mod != 3)) + do_cycles_i(2); + + /* math_op16() */ + math_op((opcode >> 3) & 7); + /* write_operand16() */ + if (opcode & 0x04) + set_accum(bits, cpu_data); + else { + if (opcode & 0x02) + set_reg(cpu_reg, cpu_data); + else + set_ea(cpu_data); + } + break; + + case 0x06: + case 0x0e: + case 0x16: + case 0x1e: /* PUSH seg */ + do_cycles_i(3); + push(&(_opseg[(opcode >> 3) & 0x03]->seg)); + break; + + case 0x07: + case 0x17: + case 0x1f: /* POP seg */ + load_seg(pop(), _opseg[(opcode >> 3) & 0x03]); + /* All POP segment instructions suppress interrupts for one instruction. */ + noint = 1; + break; + + case 0x0f: + if (!is_nec) { + load_cs(pop()); + /* All POP segment instructions suppress interrupts for one instruction. */ + noint = 1; + } + break; + + case 0x26: /* ES: */ + case 0x2e: /* CS: */ + case 0x36: /* SS: */ + case 0x3e: /* DS: */ + break; + + case 0x27: /* DAA */ + do_cycles_nx_i(3); + daa(); + break; + + case 0x2f: /* DAS */ + do_cycles_nx_i(3); + das(); + break; + + case 0x37: /* AAA */ + aaa(); + break; + + case 0x38: /* CMP r/m8, r8; r8, r/m8; al, imm8 */ + case 0x3a: + case 0x3c: + bits = 8; + /* read_operand8() */ + if (opcode & 0x04) { + cpu_data = biu_pfq_fetch(); + cpu_dest = get_accum(bits); /* AX/AL */ + cpu_src = cpu_data; + } else { + if (cpu_mod != 3) + do_cycles_i(2); /* load_operand() */ + tempw = get_ea(); + if (opcode & 2) { + cpu_dest = get_reg(cpu_reg); + cpu_src = tempw; + } else { + cpu_dest = tempw; + cpu_src = get_reg(cpu_reg); + } + } + + do_cycles_nx_i(2); + + /* math_op8() */ + math_op(7); + break; + + case 0x39: /* CMP r/m16, r16; r16, r/m16; ax, imm16 */ + case 0x3b: + case 0x3d: + bits = 16; + /* read_operand16() */ + if (opcode & 0x04) { + cpu_data = biu_pfq_fetch(); + cpu_dest = get_accum(bits); /* AX/AL */ + cpu_src = cpu_data; + } else { + if (cpu_mod != 3) + do_cycles_i(2); /* load_operand() */ + tempw = get_ea(); + if (opcode & 2) { + cpu_dest = get_reg(cpu_reg); + cpu_src = tempw; + } else { + cpu_dest = tempw; + cpu_src = get_reg(cpu_reg); + } + } + + do_cycles_nx_i((opcode == 0x3d) ? 1 : 2); + + /* math_op16() */ + math_op(7); + break; + + case 0x3f: /*AAS*/ + aas(); + break; + + case 0x40 ... 0x47: /* INC r16 */ + /* read_operand16() */ + cpu_dest = cpu_state.regs[opcode & 7].w; + cpu_src = 1; + bits = 16; + /* math_op16() */ + cpu_data = cpu_dest + cpu_src; + set_of_add(bits); + do_af(); + set_pzs(16); + /* write_operand16() */ + cpu_state.regs[opcode & 7].w = cpu_data; + + do_cycles_nx(1); + break; + + case 0x48 ... 0x4f: /* DEC r16 */ + /* read_operand16() */ + cpu_dest = cpu_state.regs[opcode & 7].w; + cpu_src = 1; + bits = 16; + /* math_op16() */ + cpu_data = cpu_dest - cpu_src; + set_of_sub(bits); + do_af(); + set_pzs(16); + /* write_operand16() */ + cpu_state.regs[opcode & 7].w = cpu_data; + + do_cycles_nx(1); + break; + + case 0x50 ... 0x57: /* PUSH r16 */ + do_cycles_i(3); + + push(&(cpu_state.regs[opcode & 0x07].w)); + break; + + case 0x58 ... 0x5f: /* POP r16 */ + cpu_state.regs[opcode & 0x07].w = pop(); + do_cycle_nx_i(); + break; + + case 0x60 ... 0x7f: /* JMP rel8 */ + switch ((opcode >> 1) & 0x07) { + case 0x00: + jump = cpu_state.flags & V_FLAG; + break; + case 0x01: + jump = cpu_state.flags & C_FLAG; + break; + case 0x02: + jump = cpu_state.flags & Z_FLAG; + break; + case 0x03: + jump = cpu_state.flags & (C_FLAG | Z_FLAG); + break; + case 0x04: + jump = cpu_state.flags & N_FLAG; + break; + case 0x05: + jump = cpu_state.flags & P_FLAG; + break; + case 0x06: + jump = (!!(cpu_state.flags & N_FLAG)) != (!!(cpu_state.flags & V_FLAG)); + break; + case 0x07: + jump = (cpu_state.flags & Z_FLAG) || + ((!!(cpu_state.flags & N_FLAG)) != (!!(cpu_state.flags & V_FLAG))); + break; + } + if (opcode & 1) + jump = !jump; + + rel8 = (int8_t) biu_pfq_fetchb(); + new_ip = cpu_state.pc + rel8; + if (!is_nec) + do_cycle_i(); + + if (jump) + reljmp(new_ip, 1); + break; + + case 0x80: /* ADD/OR/ADC/SBB/AND/SUB/XOR/CMP r/m8, imm8 */ + case 0x82: + bits = 8; + /* read_opreand8() */ + if (cpu_mod != 3) + do_cycles_i(2); /* load_operand() */ + cpu_data = get_ea(); + cpu_dest = cpu_data; + cpu_src = biu_pfq_fetchb() | 0xff00; + + do_cycle_nx(); + math_op((rmdat & 0x38) >> 3); + + if (cpu_mod != 3) + do_cycles_i(2); + + /* write_operand8() */ + if (cpu_alu_op != 7) + set_ea(cpu_data); + break; + + case 0x81: /* ADD/OR/ADC/SBB/AND/SUB/XOR/CMP r/m16, imm16 */ + bits = 16; + /* read_opreand16() */ + if (cpu_mod != 3) + do_cycles_i(2); /* load_operand() */ + cpu_data = get_ea(); + cpu_dest = cpu_data; + cpu_src = biu_pfq_fetchw(); + + do_cycle_nx(); + math_op((rmdat & 0x38) >> 3); + + if (cpu_mod != 3) { + if (cpu_alu_op != 7) + do_cycles_i(2); + else { + do_cycles_nx_i(2); + } + } + + /* write_operand16() */ + if (cpu_alu_op != 7) + set_ea(cpu_data); + break; + + case 0x83: /* ADD/OR/ADC/SBB/AND/SUB/XOR/CMP r/m16, imm8 (sign-extended) */ + bits = 16; + /* read_opreand16() */ + if (cpu_mod != 3) + do_cycles_i(2); /* load_operand() */ + cpu_data = get_ea(); + cpu_dest = cpu_data; + cpu_src = sign_extend(biu_pfq_fetchb()); + + do_cycle_nx(); + math_op((rmdat & 0x38) >> 3); + + if (cpu_mod != 3) + do_cycles_i(2); + + /* write_operand16() */ + if (cpu_alu_op != 7) + set_ea(cpu_data); + break; + + case 0x84: /* TEST r/m8, r8 */ + bits = 8; + /* read_operand8() */ + if (cpu_mod != 3) + do_cycles_i(2); /* load_operand() */ + cpu_data = get_ea(); + + /* math_op8() */ + test(bits, cpu_data, get_reg(cpu_reg)); + + do_cycles_nx_i(2); + break; + + case 0x85: /* TEST r/m16, r16 */ + bits = 16; + /* read_operand16() */ + if (cpu_mod != 3) + do_cycles_i(2); /* load_operand() */ + cpu_data = get_ea(); + + /* math_op16() */ + test(bits, cpu_data, get_reg(cpu_reg)); + + do_cycles_nx_i(2); + break; + + case 0x86: /* XHG r8, r/m8 */ + bits = 8; + /* read_operand8() */ + if (cpu_mod != 3) + do_cycles_i(2); /* load_operand() */ + cpu_data = get_ea(); + cpu_src = get_reg(cpu_reg); + + do_cycles_nx(3); + + if (cpu_mod != 3) + do_cycles(2); + + /* write_operand8() */ + set_reg(cpu_reg, cpu_data); + set_ea(cpu_src); + break; + + case 0x87: /* XCHG r16, r/m16 */ + bits = 16; + /* read_operand16() */ + if (cpu_mod != 3) + do_cycles_i(2); /* load_operand() */ + cpu_data = get_ea(); + cpu_src = get_reg(cpu_reg); + + do_cycles_nx(3); + + if (cpu_mod != 3) + do_cycles(2); + + /* write_operand16() */ + set_reg(cpu_reg, cpu_data); + set_ea(cpu_src); + break; + + case 0x88: /* MOV r/m8, r8; MOV r8, r/m8 */ + case 0x8a: + bits = 8; + if (cpu_mod != 3) + do_cycles_i(2); /* load_operand() */ + do_cycle_nx(); + /* read_operand8() */ + if (opcode == 0x88) + tempb = get_reg(cpu_reg); + else + tempb = get_ea(); + + if ((opcode == 0x88) && (cpu_mod != 3)) + do_cycles_i(2); + + /* write_operand8() */ + if (opcode == 0x88) + set_ea(tempb); + else + set_reg(cpu_reg, tempb); + break; + + case 0x89: /* MOV r/m16, r16; MOV r16, r/m16 */ + case 0x8b: + bits = 16; + if (cpu_mod != 3) + do_cycles_i(2); /* load_operand() */ + do_cycle_nx(); + /* read_operand16() */ + if (opcode == 0x89) + tempw = get_reg(cpu_reg); + else + tempw = get_ea(); + + if ((opcode == 0x89) && (cpu_mod != 3)) + do_cycles_i(2); + + /* write_operand16() */ + if (opcode == 0x89) + set_ea(tempw); + else + set_reg(cpu_reg, tempw); + break; + + case 0x8c: /* MOV r/m16, SReg; MOV SReg, r/m16 */ + case 0x8e: + bits = 16; + if (cpu_mod != 3) + do_cycles_i(2); /* load_operand() */ + if ((opcode == 0x8c) && (cpu_mod != 3)) + do_cycle_i(); + /* read_operand16() */ + if (opcode == 0x8c) + tempw = _opseg[(rmdat & 0x18) >> 3]->seg; + else + tempw = geteaw(); + /* write_operand16() */ + if (opcode == 0x8c) + seteaw(tempw); + else { + if ((rmdat & 0x18) == 0x08) + load_cs(tempw); + else + load_seg(tempw, _opseg[(rmdat & 0x18) >> 3]); + if (((rmdat & 0x18) >> 3) == 2) + noint = 1; + } + break; + + case 0x8d: /* LEA */ + if (cpu_mod != 3) + do_cycles_i(2); /* load_operand() */ + cpu_state.regs[cpu_reg].w = cpu_state.eaaddr; + break; + + case 0x8f: /* POP r/m16 */ + if (cpu_mod != 3) + do_cycles_i(2); /* load_operand() */ + do_cycle_i(); + /* pop_u16() */ + cpu_src = cpu_state.eaaddr; + cpu_data = pop(); + do_cycle_i(); + if (cpu_mod != 3) + do_cycles_i(2); + /* write_operand16() */ + cpu_state.eaaddr = cpu_src; + seteaw(cpu_data); + break; + + case 0x90 ... 0x97: /* XCHG AX, r */ + cpu_data = cpu_state.regs[opcode & 7].w; + do_cycles_nx_i(2); + cpu_state.regs[opcode & 7].w = AX; + AX = cpu_data; + break; + + case 0x98: /* CBW */ + sign_extend_al(); + break; + + case 0x99: /* CWD */ + sign_extend_ax(); + break; + + case 0x9a: /* CALLF */ + /* read_operand_faraddr() */ + new_ip = biu_pfq_fetchw(); + new_cs = biu_pfq_fetchw(); + + farcall(new_cs, new_ip, 1); + + jump = 1; + break; + + case 0x9b: /* WAIT */ + do_cycles(3); + break; + + case 0x9c: /* PUSHF */ + do_cycles(3); + /* push_flags() */ + if (is_nec) + tempw = (cpu_state.flags & 0x8fd7) | 0x7000; + else + tempw = (cpu_state.flags & 0x0fd7) | 0xf000; + push(&tempw); + break; + + case 0x9d: /* POPF */ + /* pop_flags() */ + if (is_nec && cpu_md_write_disable) + cpu_state.flags = pop() | 0x8002; + else + cpu_state.flags = pop() | 0x0002; + sync_to_i8080(); + break; + + case 0x9e: /* SAHF */ + /* store_flags() */ + cpu_state.flags = (cpu_state.flags & 0xff02) | AH; + break; + + case 0x9f: /*LAHF*/ + /* load_flags() */ + /* set_register8() */ + AH = cpu_state.flags & 0xd7; + break; + + case 0xa0: /* MOV al, offset8 */ + bits = 8; + /* read_operand8() */ + cpu_state.eaaddr = biu_pfq_fetchw(); + tempb = readmem(ovr_seg ? *ovr_seg : ds); + /* set_register8() */ + set_accum(bits, tempb); + break; + + case 0xa1: /* MOV al, offset16 */ + bits = 16; + /* read_operand16() */ + cpu_state.eaaddr = biu_pfq_fetchw(); + tempw = readmem(ovr_seg ? *ovr_seg : ds); + /* set_register16() */ + set_accum(bits, tempw); + break; + + case 0xa2: /* MOV offset8, Al */ + bits = 8; + tempb = get_accum(bits); + /* write_operand8() */ + cpu_state.eaaddr = biu_pfq_fetchw(); + writemem((ovr_seg ? *ovr_seg : ds), tempb); + break; + + case 0xa3: /* MOV offset16, AX */ + bits = 16; + tempw = get_accum(bits); + /* write_operand16() */ + cpu_state.eaaddr = biu_pfq_fetchw(); + writemem((ovr_seg ? *ovr_seg : ds), tempw); + break; + + case 0xa4: /* MOVSB & MOVSW */ + case 0xa5: + bits = 8 << (opcode & 1); + + if (rep_start()) { + string_op(bits); + do_cycle_i(); + + if (in_rep != 0) { + completed = 0; + repeating = 1; + + /* decrement_register16() */ + CX--; + + if (irq_pending()) { + do_cycles_i(2); + rep_interrupt(); + } else { + do_cycles_i(2); + + if (CX == 0) + rep_end(); + else + do_cycle_i(); + } + } else + do_cycle_i(); + } + break; + + case 0xa6: /* CMPSB, CMPSW, SCASB, SCASW */ + case 0xa7: + case 0xae: + case 0xaf: + bits = 8 << (opcode & 1); + if (rep_start()) { + string_op(bits); + + if (in_rep) { + uint8_t end = 0; + + completed = 0; + repeating = 1; + + do_cycle_i(); + /* decrement_register16() */ + CX--; + + if ((!!(cpu_state.flags & (rep_c_flag ? C_FLAG : Z_FLAG))) == (in_rep == 1)) { + rep_end(); + do_cycle_i(); + end = 1; + } + + if (!end) { + do_cycle_i(); + + if (irq_pending()) { + do_cycle_i(); + rep_interrupt(); + } + + do_cycle_i(); + if (CX == 0) + rep_end(); + else + do_cycle_i(); + } else + do_cycle_i(); + } + } + break; + + case 0xa8: /* TEST al, imm8 */ + bits = 8; + /* read_operand8() */ + cpu_data = biu_pfq_fetch(); + /* math_op8() */ + test(bits, get_accum(bits), cpu_data); + break; + + case 0xa9: /* TEST ax, imm16 */ + bits = 16; + /* read_operand16() */ + cpu_data = biu_pfq_fetch(); + /* math_op16() */ + test(bits, get_accum(bits), cpu_data); + break; + + case 0xaa: /* STOSB & STOSW */ + case 0xab: + bits = 8 << (opcode & 1); + if (rep_start()) { + string_op(bits); + do_cycle_i(); + + if (in_rep != 0) { + completed = 0; + repeating = 1; + + do_cycle_i(); + if (irq_pending()) { + do_cycle_i(); + rep_interrupt(); + } + + do_cycle_i(); + /* decrement_register16() */ + CX--; + if (CX == 0) + rep_end(); + else + do_cycle_i(); + } else + do_cycle_i(); + } + break; + + case 0xac: /* LODSB * LODSW */ + case 0xad: + bits = 8 << (opcode & 1); + if (rep_start()) { + string_op(bits); + do_cycles_i(3); + + if (in_rep != 0) { + completed = 0; + repeating = 1; + + do_cycle_i(); + /* decrement_register16() */ + CX--; + + if (irq_pending()) { + do_cycles_i(2); + rep_interrupt(); + } else { + do_cycles_i(2); + + if (CX == 0) + rep_end(); + else + do_cycle_i(); + } + } + } + break; + + case 0xb0 ... 0xb7: /* MOV r8, imm8 */ + bits = 8; + /* read_operand8() */ + tempb = biu_pfq_fetchb(); + /* set_register8() */ + if (opcode & 0x04) + cpu_state.regs[opcode & 0x03].b.h = tempb; + else + cpu_state.regs[opcode & 0x03].b.l = tempb; + do_cycle_i(); + break; + + case 0xb8 ... 0xbf: /* MOV r16, imm16 */ + bits = 16; + /* read_operand16() */ + tempw = biu_pfq_fetchw(); + /* set_register16() */ + cpu_state.regs[opcode & 0x07].w = tempw; + break; + + case 0xc0: /* rot imm8 */ + case 0xc1: + /* rot rm */ + bits = 8 << (opcode & 1); + if (cpu_mod != 3) + do_cycles_i(is_nec ? 1 : 2); /* load_operand() */ + /* read_operand() */ + cpu_data = get_ea(); + cpu_src = biu_pfq_fetchb(); + if (is186 && !is_nec) + cpu_src &= 0x1F; + do_cycles_i(6); + if (cpu_src > 0) { + for (uint8_t i = 0; i < cpu_src; i++) + do_cycles_i(4); + } + if (cpu_mod != 3) + do_cycle_i(); + /* bitshift_op() */ + while (cpu_src != 0) { + cpu_dest = cpu_data; + oldc = cpu_state.flags & C_FLAG; + switch (rmdat & 0x38) { + case 0x00: /* ROL */ + set_cf(top_bit(cpu_data, bits)); + cpu_data <<= 1; + cpu_data |= ((cpu_state.flags & C_FLAG) ? 1 : 0); + set_of_rotate(bits); + set_af(0); + break; + case 0x08: /* ROR */ + set_cf((cpu_data & 1) != 0); + cpu_data >>= 1; + if (cpu_state.flags & C_FLAG) + cpu_data |= (!(opcode & 1) ? 0x80 : 0x8000); + set_of_rotate(bits); + set_af(0); + break; + case 0x10: /* RCL */ + set_cf(top_bit(cpu_data, bits)); + cpu_data = (cpu_data << 1) | (oldc ? 1 : 0); + set_of_rotate(bits); + set_af(0); + break; + case 0x18: /* RCR */ + set_cf((cpu_data & 1) != 0); + cpu_data >>= 1; + if (oldc) + cpu_data |= (!(opcode & 0x01) ? 0x80 : 0x8000); + set_cf((cpu_dest & 1) != 0); + set_of_rotate(bits); + set_af(0); + break; + case 0x20: /* SHL */ + set_cf(top_bit(cpu_data, bits)); + cpu_data <<= 1; + set_of_rotate(bits); + set_af((cpu_data & 0x10) != 0); + set_pzs(bits); + break; + case 0x28: /* SHR */ + set_cf((cpu_data & 1) != 0); + cpu_data >>= 1; + set_of_rotate(bits); + set_af(0); + set_pzs(bits); + break; + case 0x30: /* SETMO - undocumented? */ + bitwise(bits, 0xffff); + set_cf(0); + set_of_rotate(bits); + set_af(0); + set_pzs(bits); + break; + case 0x38: /* SAR */ + set_cf((cpu_data & 1) != 0); + cpu_data >>= 1; + if (!(opcode & 1)) + cpu_data |= (cpu_dest & 0x80); + else + cpu_data |= (cpu_dest & 0x8000); + set_of_rotate(bits); + set_af(0); + set_pzs(bits); + break; + + default: + break; + } + --cpu_src; + } + + if (opcode <= 0xd1) { + if (cpu_mod != 3) + do_cycle_i(); + } + + /* write_operand() */ + set_ea(cpu_data); + break; + + case 0xc2: /* RETN imm16 */ + bits = 8; + cpu_src = biu_pfq_fetchw(); + do_cycle_i(); + new_ip = pop(); + biu_suspend_fetch(); + set_ip(new_ip); + + do_cycles_i(2); + biu_queue_flush(); + do_cycles_i(3); + + /* release() */ + SP += cpu_src; + + jump = 1; + break; + + case 0xc3: /* RETN */ + bits = 8; + new_ip = pop(); + biu_suspend_fetch(); + set_ip(new_ip); + do_cycle_i(); + biu_queue_flush(); + do_cycles_i(2); + + jump = 1; + break; + + case 0xc4: /* LES */ + case 0xc5: /* LDS */ + bits = 16; + if (cpu_mod != 3) + do_cycles_i(2); /* load_operand() */ + + do_cycles_i(2); + + /* read_operand_farptr() */ + read_ea(1, bits); + cpu_state.regs[cpu_reg].w = cpu_data; + read_ea2(bits); + + /* write_operand16() */ + load_seg(cpu_data, (opcode & 0x01) ? &cpu_state.seg_ds : &cpu_state.seg_es); + break; + + case 0xc6: /* MOV r/m8, imm8 */ + bits = 8; + /* read_operand8() */ + if (cpu_mod != 3) + do_cycles_i(2); /* load_operand() */ + cpu_data = biu_pfq_fetch(); + do_cycles(2); + /* write_operand8() */ + set_ea(cpu_data); + break; + + case 0xc7: /* MOV r/m16, imm16 */ + bits = 16; + /* read_operand16() */ + if (cpu_mod != 3) + do_cycles_i(2); /* load_operand() */ + cpu_data = biu_pfq_fetch(); + do_cycle_i(); + /* write_operand16() */ + set_ea(cpu_data); + break; + + case 0xc8: /* RETF imm16 */ + if (is_nec) { + /* ENTER/PREPARE */ + tempw_int = 0; + size = biu_pfq_fetchw(); + nests = biu_pfq_fetchb(); + + push(&BP); + tempw_int = SP; + if (nests > 0) { + while (--nests) { + tempbp = 0; + BP -= 2; + tempbp = readmemw(ss, BP); + push(&tempbp); + } + push(&tempw_int); + } + BP = tempw_int; + SP -= size; + break; + } else + fallthrough; + case 0xca: + bits = 16; + /* read_operand16() */ + cpu_src = biu_pfq_fetchw(); + farret(1); + /* release() */ + SP += cpu_src; + do_cycle_i(); + jump = 1; + break; + + case 0xc9: /* RETF */ + if (is_nec) { + /* LEAVE/DISPOSE */ + SP = BP; + BP = pop(); + break; + } else + fallthrough; + case 0xcb: + bits = 16; + do_cycle_i(); + farret(1); + jump = 1; + break; + + case 0xcc: /* INT 3 */ + do_cycles_i(4); + int3(); + jump = 1; + break; + + case 0xcd: /* INT imm8 */ + /* read_operand8() */ + temp = biu_pfq_fetchb(); + do_cycle_i(); + sw_int(temp); + jump = 1; + break; + + case 0xce: /* INTO */ + if (cpu_state.flags & V_FLAG) { + sw_int(4); + jump = 1; + } + break; + + case 0xcf: /* IRET */ + iret_routine(); + if (is_nec && !(cpu_state.flags & MD_FLAG)) + sync_to_i8080(); + jump = 1; + break; + + case 0xd0: /* ROL, ROR, RCL, RCR, SHL, SHR, SAR: r/m 8, 0x01 */ + case 0xd1: /* ROL, ROR, RCL, RCR, SHL, SHR, SAR: r/m 16, 0x01 */ + case 0xd2: /* ROL, ROR, RCL, RCR, SHL, SHR, SAR: r/m 8, cl */ + case 0xd3: /* ROL, ROR, RCL, RCR, SHL, SHR, SAR: r/m 16, cl */ + /* rot rm */ + bits = 8 << (opcode & 1); + if (cpu_mod != 3) + do_cycles_i(is_nec ? 1 : 2); /* load_operand() */ + /* read_operand() */ + cpu_data = get_ea(); + if ((opcode & 2) == 0) + cpu_src = 1; + else + cpu_src = CL; + if (is186 && !is_nec) + cpu_src &= 0x1F; + if (opcode >= 0xd2) { + do_cycles_i(6); + if (CL > 0) { + for (uint8_t i = 0; i < CL; i++) + do_cycles_i(4); + } + if (cpu_mod != 3) + do_cycle_i(); + } + /* bitshift_op() */ + while (cpu_src != 0) { + cpu_dest = cpu_data; + oldc = cpu_state.flags & C_FLAG; + if (is_nec && ((rmdat & 0x38) == 0x30)) + rmdat &= 0xef; /* Make it 0x20, so it aliases to SHL. */ + switch (rmdat & 0x38) { + case 0x00: /* ROL */ + set_cf(top_bit(cpu_data, bits)); + cpu_data <<= 1; + cpu_data |= ((cpu_state.flags & C_FLAG) ? 1 : 0); + set_of_rotate(bits); + set_af(0); + break; + case 0x08: /* ROR */ + set_cf((cpu_data & 1) != 0); + cpu_data >>= 1; + if (cpu_state.flags & C_FLAG) + cpu_data |= (!(opcode & 1) ? 0x80 : 0x8000); + set_of_rotate(bits); + set_af(0); + break; + case 0x10: /* RCL */ + set_cf(top_bit(cpu_data, bits)); + cpu_data = (cpu_data << 1) | (oldc ? 1 : 0); + set_of_rotate(bits); + set_af(0); + break; + case 0x18: /* RCR */ + set_cf((cpu_data & 1) != 0); + cpu_data >>= 1; + if (oldc) + cpu_data |= (!(opcode & 0x01) ? 0x80 : 0x8000); + set_cf((cpu_dest & 1) != 0); + set_of_rotate(bits); + set_af(0); + break; + case 0x20: /* SHL */ + set_cf(top_bit(cpu_data, bits)); + cpu_data <<= 1; + set_of_rotate(bits); + set_af((cpu_data & 0x10) != 0); + set_pzs(bits); + break; + case 0x28: /* SHR */ + set_cf((cpu_data & 1) != 0); + cpu_data >>= 1; + set_of_rotate(bits); + set_af(0); + set_pzs(bits); + break; + case 0x30: /* SETMO - undocumented? */ + bitwise(bits, 0xffff); + set_cf(0); + set_of_rotate(bits); + set_af(0); + set_pzs(bits); + break; + case 0x38: /* SAR */ + set_cf((cpu_data & 1) != 0); + cpu_data >>= 1; + if (!(opcode & 1)) + cpu_data |= (cpu_dest & 0x80); + else + cpu_data |= (cpu_dest & 0x8000); + set_of_rotate(bits); + set_af(0); + set_pzs(bits); + break; + + default: + break; + } + --cpu_src; + } + + if (opcode <= 0xd1) { + if (cpu_mod != 3) + do_cycle_i(); + } + + /* write_operand() */ + set_ea(cpu_data); + break; + + case 0xd4: /* AAM */ + /* read_operand8() */ + cpu_src = biu_pfq_fetchb(); + + if (is_nec) { + if (!cpu_src) + cpu_src = 10; + AH = AL / cpu_src; + AL %= cpu_src; + cpu_data = AL; + set_pzs(8); + do_cycles(12); + } else { + /* Confirmed to be identical on V20/V30 to 808x, per + XTIDE working correctly on both (it uses AAM with + parameter other than 10. */ + /* aam() */ + if (x86_div(AL, 0)) { + cpu_data = AL; + set_pzs(8); + } + } + break; + + case 0xd5: /* AAD */ + /* read_operand8() */ + cpu_src = biu_pfq_fetchb(); + + if (is_nec) { + cpu_src = 10; + AL = (AH * cpu_src) + AL; + AH = 0; + cpu_data = AL; + set_pzs(8); + do_cycles(4); + } else { + if (is_nec) + cpu_src = 10; + /* aad() */ + mul(cpu_src, AH); + cpu_dest = AL; + cpu_src = cpu_data; + add(8); + AL = cpu_data; + AH = 0x00; + set_pzs(8); + } + break; + + case 0xd6: /* SALC */ + if (is_nec) { + do_cycles(14); + fallthrough; + } else { + AL = (cpu_state.flags & C_FLAG) ? 0xff : 0x00; + break; + } + case 0xd7: /* XLAT */ + do_cycles_i(3); + /* biu_read_u8() */ + cpu_state.eaaddr = (BX + AL) & 0xffff; + tempb = readmemb((ovr_seg ? *ovr_seg : ds), cpu_state.eaaddr); + /* set_register8() */ + AL = tempb; + break; + + case 0xd8 ... 0xdf: /* ESC - FPU instructions. */ + /* read_operand16() */ + if (cpu_mod != 3) + do_cycles_i(2); /* load_operand() */ + tempw = cpu_state.pc; + geteaw(); + /* fpu_op() */ + if (hasfpu) { + if (fpu_softfloat) { + switch (opcode) { + case 0xd8: + ops_sf_fpu_8087_d8[(rmdat >> 3) & 0x1f](rmdat); + break; + case 0xd9: + ops_sf_fpu_8087_d9[rmdat & 0xff](rmdat); + break; + case 0xda: + ops_sf_fpu_8087_da[rmdat & 0xff](rmdat); + break; + case 0xdb: + ops_sf_fpu_8087_db[rmdat & 0xff](rmdat); + break; + case 0xdc: + ops_sf_fpu_8087_dc[(rmdat >> 3) & 0x1f](rmdat); + break; + case 0xdd: + ops_sf_fpu_8087_dd[rmdat & 0xff](rmdat); + break; + case 0xde: + ops_sf_fpu_8087_de[rmdat & 0xff](rmdat); + break; + case 0xdf: + ops_sf_fpu_8087_df[rmdat & 0xff](rmdat); + break; + + default: + break; + } + } else { + switch (opcode) { + case 0xd8: + ops_fpu_8087_d8[(rmdat >> 3) & 0x1f](rmdat); + break; + case 0xd9: + ops_fpu_8087_d9[rmdat & 0xff](rmdat); + break; + case 0xdA: + ops_fpu_8087_da[rmdat & 0xff](rmdat); + break; + case 0xdb: + ops_fpu_8087_db[rmdat & 0xff](rmdat); + break; + case 0xdc: + ops_fpu_8087_dc[(rmdat >> 3) & 0x1f](rmdat); + break; + case 0xdd: + ops_fpu_8087_dd[rmdat & 0xff](rmdat); + break; + case 0xde: + ops_fpu_8087_de[rmdat & 0xff](rmdat); + break; + case 0xdf: + ops_fpu_8087_df[rmdat & 0xff](rmdat); + break; + + default: + break; + } + } + } + cpu_state.pc = tempw; /* Do this as the x87 code advances it, which is needed on + the 286+ core, but not here. */ + break; + + case 0xe0: /* LOOPNE & LOOPE */ + case 0xe1: + /* decrement_register16() */ + --CX; + do_cycles_i(2); + + zero_cond = !(cpu_state.flags & Z_FLAG); + if (opcode == 0xe1) + zero_cond = !zero_cond; + + /* read_operand8() */ + cpu_data = biu_pfq_fetchb(); + + if ((CX != 0x0000) && zero_cond) { + rel8 = (int8_t) cpu_data; + new_ip = (cpu_state.pc + rel8) & 0xffff; + reljmp(new_ip, 1); + jump = 1; + do_print = 0; + } else { + do_cycle_i(); + do_print = 1; + } + break; + + case 0xe2: /* LOOP */ + /* decrement_register16() */ + --CX; + do_cycles_i(2); + + /* read_operand8() */ + cpu_data = biu_pfq_fetchb(); + + if (CX != 0x0000) { + rel8 = (int8_t) cpu_data; + new_ip = (cpu_state.pc + rel8) & 0xffff; + reljmp(new_ip, 1); + jump = 1; + do_print = 0; + } + if (!jump) { + do_cycle(); + do_print = 1; + } + break; + + case 0xe3: /* JCXZ */ + do_cycles_i(2); + /* read_operand8() */ + cpu_data = biu_pfq_fetchb(); + + do_cycle_i(); + + if (CX == 0x0000) { + rel8 = (int8_t) cpu_data; + new_ip = (cpu_state.pc + rel8) & 0xffff; + reljmp(new_ip, 1); + jump = 1; + } else + do_cycle_i(); + break; + + case 0xe4: /* IN al, imm8 */ + bits = 8; + /* read_operand8() */ + cpu_data = biu_pfq_fetchb(); + do_cycles_i(2); + + /* biu_io_read_u8() */ + cpu_state.eaaddr = cpu_data; + cpu_io_vx0(bits, 0, cpu_state.eaaddr); + /* set_register8() */ + break; + + case 0xe5: /* IN ax, imm8 */ + bits = 16; + /* read_operand16() */ + cpu_data = biu_pfq_fetchb(); + do_cycles_i(2); + + /* biu_io_read_u16() */ + cpu_state.eaaddr = cpu_data; + cpu_io_vx0(bits, 0, cpu_state.eaaddr); + /* set_register16() */ + break; + + case 0xe6: /* OUT imm8, al */ + bits = 8; + /* read_operand8() */ + cpu_data = biu_pfq_fetchb(); + /* read_operand8() */ + tempb = AL; + do_cycles_i(2); + + /* biu_io_write_u8() */ + cpu_state.eaaddr = cpu_data; + cpu_data = tempb; + cpu_io_vx0(bits, 1, cpu_state.eaaddr); + break; + + case 0xe7: /* OUT imm8, ax */ + bits = 16; + /* read_operand8() */ + cpu_data = biu_pfq_fetchb(); + /* read_operand16() */ + tempw = AX; + do_cycles_i(2); + + /* biu_io_write_u16() */ + cpu_state.eaaddr = cpu_data; + cpu_data = tempw; + cpu_io_vx0(bits, 1, cpu_state.eaaddr); + break; + + case 0xe8: /* CALL rel16 */ + /* read_operand16() */ + rel16 = (int16_t) biu_pfq_fetchw(); + + biu_suspend_fetch(); + do_cycles_i(4); + + old_ip = cpu_state.pc; + new_ip = cpu_state.pc + rel16; + + set_ip(new_ip); + biu_queue_flush(); + do_cycles_i(3); + + push(&old_ip); + jump = 1; + break; + + case 0xe9: /* JMP rel16 */ + /* read_operand16() */ + rel16 = (int16_t) biu_pfq_fetchw(); + new_ip = (cpu_state.pc + rel16) & 0xffff; + + reljmp(new_ip, 1); + jump = 1; + break; + + case 0xea: /* JMP far [addr16:16] */ + /* read_operand_faraddr() */ + addr = biu_pfq_fetchw(); + tempw = biu_pfq_fetchw(); + load_cs(tempw); + set_ip(addr); + + biu_suspend_fetch(); + do_cycles_i(2); + biu_queue_flush(); + do_cycle_i(); + jump = 1; + break; + + case 0xeb: /* JMP rel8 */ + /* read_operand8() */ + rel8 = (int8_t) biu_pfq_fetchb(); + new_ip = (cpu_state.pc + rel8) & 0xffff; + + reljmp(new_ip, 1); + jump = 1; + break; + + case 0xec: /* IN al, dx */ + bits = 8; + /* read_operand8() */ + cpu_data = DX; + /* biu_io_read_u8() */ + cpu_state.eaaddr = cpu_data; + cpu_io_vx0(bits, 0, cpu_state.eaaddr); + /* set_register8() */ + break; + + case 0xed: /* IN ax, dx */ + bits = 16; + /* read_operand16() */ + cpu_data = DX; + /* biu_io_read_u16() */ + cpu_state.eaaddr = cpu_data; + cpu_io_vx0(bits, 0, cpu_state.eaaddr); + /* set_register16() */ + break; + + case 0xee: /* OUT dx, al */ + bits = 8; + /* read_operand8() */ + cpu_data = DX; + /* read_operand8() */ + tempb = AL; + do_cycle_i(); + + /* biu_io_write_u8() */ + cpu_state.eaaddr = cpu_data; + cpu_data = tempb; + cpu_io_vx0(bits, 1, cpu_state.eaaddr); + break; + + case 0xef: /* OUT dx, ax */ + bits = 16; + /* read_operand8() */ + cpu_data = DX; + /* read_operand16() */ + tempw = AX; + do_cycle_i(); + + /* biu_io_write_u16() */ + cpu_state.eaaddr = cpu_data; + cpu_data = tempw; + cpu_io_vx0(bits, 1, cpu_state.eaaddr); + break; + + case 0xf0: + case 0xf1: /* LOCK - F1 is alias */ + break; + + case 0xf2: /* REPNE */ + case 0xf3: /* REPE */ + break; + + case 0xf4: /* HLT */ + if (is_nec) + in_hlt = 1; + + if (!repeating) { + biu_suspend_fetch(); + // biu_queue_flush(); + do_cycles(2); + /* TODO: biu_halt(); */ + do_cycle(); + } + + do_cycle_i(); + do_cycle_i(); + do_cycle_i(); + if (irq_pending()) { + halted = 0; + check_interrupts(); + } else { + repeating = 1; + completed = 0; + + halted = 1; + } + + if (is_nec) + in_hlt = 0; + break; + + case 0xf5: /* CMC */ + cpu_state.flags ^= C_FLAG; + break; + + case 0xf6: /* Miscellaneuous Opcode Extensions, r/m8, imm8 */ + bits = 8; + if (cpu_mod != 3) + do_cycles_i(is_nec ? 1 : 2); /* load_operand() */ + negate = !!in_rep; + + if (is_nec && ((rmdat & 0x38) >= 0x20)) switch (rmdat & 0x38) { + case 0x20: /* MUL */ + /* read_operand8() */ + cpu_data = get_ea(); + + AX = AL * cpu_data; + if (AH) + cpu_state.flags |= (C_FLAG | V_FLAG); + else + cpu_state.flags &= ~(C_FLAG | V_FLAG); + + do_cycles((cpu_mod == 3) ? 24 : 30); + break; + case 0x28: /* IMUL */ + /* read_operand8() */ + cpu_data = get_ea(); + + tempws = (int) ((int8_t) AL) * (int) ((int8_t) cpu_data); + AX = tempws & 0xffff; + if (((int16_t) AX >> 7) != 0 && ((int16_t) AX >> 7) != -1) + cpu_state.flags |= (C_FLAG | V_FLAG); + else + cpu_state.flags &= ~(C_FLAG | V_FLAG); + + do_cycles((cpu_mod == 3) ? 13 : 19); + break; + case 0x30: /* DIV */ + /* read_operand8() */ + cpu_data = get_ea(); + + src16 = AX; + if (cpu_data) + tempw = src16 / cpu_data; + if (cpu_data && !(tempw & 0xff00)) { + AH = src16 % cpu_data; + AL = (src16 / cpu_data) & 0xff; + cpu_state.flags |= 0x8D5; + cpu_state.flags &= ~1; + } else { + intr_routine(0, 0); + break; + } + + do_cycles((cpu_mod == 3) ? 21 : 27); + break; + case 0x38: /* IDIV */ + /* read_operand8() */ + cpu_data = get_ea(); + + tempws = (int) (int16_t) AX; + if (cpu_data != 0) + tempws2 = tempws / (int) ((int8_t) cpu_data); + temps = tempws2 & 0xff; + if (cpu_data && ((int) temps == tempws2)) { + AH = (tempws % (int) ((int8_t) cpu_data)) & 0xff; + AL = tempws2 & 0xff; + cpu_state.flags |= 0x8D5; + cpu_state.flags &= ~1; + } else { + intr_routine(0, 0); + break; + } + + do_cycles((cpu_mod == 3) ? 11 : 17); + break; + } else switch (rmdat & 0x38) { + case 0x00: /* TEST */ + case 0x08: + /* read_operand8() */ + cpu_data = get_ea(); + /* read_operand8() */ + cpu_src = biu_pfq_fetch(); + + do_cycles_i(is_nec ? 1 : 2); + + /* math_op8() */ + test(bits, cpu_data, cpu_src); + break; + case 0x10: /* NOT */ + case 0x18: /* NEG */ + /* read_operand8() */ + cpu_data = get_ea(); + /* math_op8() */ + if ((rmdat & 0x38) == 0x10) + cpu_data = ~cpu_data; + else { + cpu_src = cpu_data; + cpu_dest = 0; + sub(bits); + } + + if (cpu_mod != 3) + do_cycles_i(2); + + /* write_operand8() */ + set_ea(cpu_data); + break; + + case 0x20: /* MUL */ + case 0x28: /* IMUL */ + /* read_operand8() */ + cpu_data = get_ea(); + + /* mul8() */ + old_flags = cpu_state.flags; + mul(get_accum(bits), cpu_data); + prod16 = ((cpu_dest & 0xff) << 8) | (cpu_data & 0xff); + if (negate) + prod16 = -prod16; + cpu_dest = prod16 >> 8; + cpu_data = prod16 & 0xff; + AL = (uint8_t) cpu_data; + AH = (uint8_t) cpu_dest; + set_co_mul(bits, AH != ((AL & 0x80) == 0 || + (rmdat & 0x38) == 0x20 ? 0 : 0xff)); + if (!is_nec) + cpu_data = AH; + set_sf(bits); + set_pf(); + /* NOTE: When implementing the V20, care should be taken to not change + the zero flag. */ + if (is_nec) + cpu_state.flags = (cpu_state.flags & ~Z_FLAG) | (old_flags & Z_FLAG); + break; + + case 0x30: /* DIV */ + case 0x38: /* IDIV */ + /* read_operand8() */ + cpu_data = get_ea(); + + cpu_src = cpu_data; + if (x86_div(AL, AH)) { + if (!is_nec && negate) + AL = -AL; + do_cycle(); + } + break; + } + break; + + case 0xf7: /* Miscellaneuous Opcode Extensions, r/m16, imm16 */ + bits = 16; + if (cpu_mod != 3) + do_cycles_i(is_nec ? 1 : 2); /* load_operand() */ + negate = !!in_rep; + + if (is_nec && ((rmdat & 0x38) >= 0x20)) switch (rmdat & 0x38) { + case 0x20: /* MUL */ + /* read_operand16() */ + cpu_data = get_ea(); + + templ = AX * cpu_data; + AX = templ & 0xFFFF; + DX = templ >> 16; + if (DX) + cpu_state.flags |= (C_FLAG | V_FLAG); + else + cpu_state.flags &= ~(C_FLAG | V_FLAG); + + do_cycles((cpu_mod == 3) ? 29 : 35); + break; + case 0x28: /* IMUL */ + /* read_operand16() */ + cpu_data = get_ea(); + + templ = (int) ((int16_t) AX) * (int) ((int16_t) cpu_data); + AX = templ & 0xFFFF; + DX = templ >> 16; + if (((int32_t) templ >> 15) != 0 && ((int32_t) templ >> 15) != -1) + cpu_state.flags |= (C_FLAG | V_FLAG); + else + cpu_state.flags &= ~(C_FLAG | V_FLAG); + + do_cycles((cpu_mod == 3) ? 17 : 27); + break; + case 0x30: /* DIV */ + /* read_operand16() */ + cpu_data = get_ea(); + + templ = (DX << 16) | AX; + if (cpu_data) + templ2 = templ / cpu_data; + if (cpu_data && !(templ2 & 0xffff0000)) { + DX = templ % cpu_data; + AX = (templ / cpu_data) & 0xffff; + cpu_data = AX; + set_pzs(16); + } else { + intr_routine(0, 0); + break; + } + + do_cycles((cpu_mod == 3) ? 26 : 36); + break; + case 0x38: /* IDIV */ + /* read_operand16() */ + cpu_data = get_ea(); + + tempws = (int) ((DX << 16) | AX); + if (cpu_data) + tempws2 = tempws / (int) ((int16_t) cpu_data); + temps16 = tempws2 & 0xffff; + if ((cpu_data != 0) && ((int) temps16 == tempws2)) { + DX = tempws % (int) ((int16_t) cpu_data); + AX = tempws2 & 0xffff; + cpu_data = AX; + set_pzs(16); + } else { + intr_routine(0, 0); + break; + } + + do_cycles((cpu_mod == 3) ? 13 : 23); + break; + } else switch (rmdat & 0x38) { + case 0x00: /* TEST */ + case 0x08: + /* read_operand16() */ + cpu_data = get_ea(); + /* read_operand16() */ + cpu_src = biu_pfq_fetch(); + + do_cycle_i(); + + /* math_op16() */ + test(bits, cpu_data, cpu_src); + break; + case 0x10: /* NOT */ + case 0x18: /* NEG */ + /* read_operand16() */ + cpu_data = get_ea(); + /* math_op16() */ + if ((rmdat & 0x38) == 0x10) + cpu_data = ~cpu_data; + else { + cpu_src = cpu_data; + cpu_dest = 0; + sub(bits); + } + + if (cpu_mod != 3) + do_cycles_i(2); + + /* write_operand16() */ + set_ea(cpu_data); + break; + + case 0x20: /* MUL */ + case 0x28: /* IMUL */ + /* read_operand16() */ + cpu_data = get_ea(); + + /* mul8() */ + old_flags = cpu_state.flags; + mul(get_accum(bits), cpu_data); + prod32 = (((uint32_t) cpu_dest) << 16) | cpu_data; + if (negate) + prod32 = -prod32; + cpu_dest = prod32 >> 16; + cpu_data = prod32 & 0xffff; + AX = cpu_data; + DX = cpu_dest; + set_co_mul(bits, DX != ((AX & 0x8000) == 0 || + (rmdat & 0x38) == 0x20 ? 0 : 0xffff)); + cpu_data = DX; + set_sf(bits); + set_pf(); + /* NOTE: When implementing the V20, care should be taken to not change + the zero flag. */ + if (is_nec) + cpu_state.flags = (cpu_state.flags & ~Z_FLAG) | (old_flags & Z_FLAG); + break; + + case 0x30: /* DIV */ + case 0x38: /* IDIV */ + /* read_operand16() */ + cpu_data = get_ea(); + + cpu_src = cpu_data; + if (x86_div(AX, DX)) { + if (!is_nec && negate) + AX = -AX; + do_cycle(); + } + break; + } + break; + + case 0xf8: /* CLCSTC */ + case 0xf9: + set_cf(opcode & 1); + break; + + case 0xfa: /* CLISTI */ + case 0xfb: + set_if(opcode & 1); + break; + + case 0xfc: /* CLDSTD */ + case 0xfd: + set_df(opcode & 1); + break; + + case 0xfe: + bits = 8; + if (cpu_mod != 3) + do_cycles_i(is_nec ? 1 : 2); /* load_operand() */ + read_ea(((rmdat & 0x38) == 0x18) || ((rmdat & 0x38) == 0x28), bits); + switch (rmdat & 0x38) { + case 0x00: /* INC rm */ + case 0x08: /* DEC rm */ + /* read_operand8() */ + /* math_op8() */ + cpu_dest = cpu_data; + cpu_src = 1; + if ((rmdat & 0x38) == 0x00) { + cpu_data = cpu_dest + cpu_src; + set_of_add(bits); + } else { + cpu_data = cpu_dest - cpu_src; + set_of_sub(bits); + } + do_af(); + set_pzs(bits); + + if (cpu_mod != 3) + do_cycles_i(2); + /* write_operand8() */ + set_ea(cpu_data); + break; + case 0x10: /* CALL rm */ + /* read_operand8() */ + cpu_data_opff_rm(); + + cpu_state.oldpc = cpu_state.pc; + push((uint16_t *) &(cpu_state.oldpc)); + + biu_suspend_fetch(); + do_cycles(4); + biu_queue_flush(); + + set_ip(cpu_data | 0xff00); + break; + case 0x18: /* CALL rmd */ + if (cpu_mod == 3) { + /* biu_read_u8() */ + cpu_state.eaaddr = 0x0004; + tempb = readmemb((ovr_seg ? *ovr_seg : ds), cpu_state.eaaddr); + + old_cs = CS & 0x00ff; + push(&old_cs); + old_ip = cpu_state.pc & 0x00ff; + push(&old_ip); + + biu_suspend_fetch(); + do_cycles(4); + biu_queue_flush(); + + read_ea_8to16(); + set_ip(cpu_data); + } else { + /* read_operand8() */ + new_ip = cpu_data | 0xff00; + + do_cycles_i(3); + + /* biu_read_u8() */ + read_ea2(bits); + cpu_data |= 0xff00; + new_cs = cpu_data; + + do_cycle_i(); + biu_suspend_fetch(); + do_cycles_i(3); + + old_cs = CS & 0x00ff; + push(&old_cs); + old_ip = cpu_state.pc & 0x00ff; + + load_cs(new_cs); + set_ip(new_ip); + + do_cycles_i(3); + biu_queue_flush(); + do_cycles_i(3); + + push(&old_ip); + } + break; + case 0x20: /* JMP rm */ + /* read_operand8() */ + cpu_data_opff_rm(); + + set_ip(cpu_data | 0xff00); + + biu_suspend_fetch(); + do_cycles(4); + biu_queue_flush(); + break; + case 0x28: /* JMP rmd */ + if (cpu_mod == 3) { + /* biu_read_u8() */ + cpu_state.eaaddr = 0x0004; + tempb = readmemb((ovr_seg ? *ovr_seg : ds), cpu_state.eaaddr); + + biu_suspend_fetch(); + do_cycles(4); + biu_queue_flush(); + + read_ea_8to16(); + set_ip(cpu_data); + } else { + /* read_operand8() */ + new_ip = cpu_data | 0xff00; + + /* biu_read_u8() */ + read_ea2(bits); + cpu_data |= 0xff00; + new_cs = cpu_data; + + biu_suspend_fetch(); + do_cycles(4); + biu_queue_flush(); + + load_cs(new_cs); + set_ip(new_ip); + } + break; + case 0x30: /* PUSH rm */ + case 0x38: + /* read_operand8() */ + do_cycles_i(3); + cpu_data &= 0x00ff; + push((uint16_t *) &cpu_data); + break; + } + break; + + case 0xff: + bits = 16; + if (cpu_mod != 3) + do_cycles_i(is_nec ? 1 : 2); /* load_operand() */ + read_ea(((rmdat & 0x38) == 0x18) || ((rmdat & 0x38) == 0x28), bits); + switch (rmdat & 0x38) { + case 0x00: /* INC rm */ + case 0x08: /* DEC rm */ + /* read_operand16() */ + /* math_op16() */ + cpu_dest = cpu_data; + cpu_src = 1; + if ((rmdat & 0x38) == 0x00) { + cpu_data = cpu_dest + cpu_src; + set_of_add(bits); + } else { + cpu_data = cpu_dest - cpu_src; + set_of_sub(bits); + } + do_af(); + set_pzs(bits); + if (cpu_mod != 3) + do_cycles_i(2); + /* write_operand16() */ + set_ea(cpu_data); + break; + case 0x10: /* CALL rm */ + /* read_operand16() */ + cpu_data_opff_rm(); + + biu_suspend_fetch(); + do_cycles(4); + + cpu_state.oldpc = cpu_state.pc; + + old_ip = cpu_state.pc; + set_ip(cpu_data); + biu_queue_flush(); + do_cycles_i(3); + + push(&old_ip); + break; + case 0x18: /* CALL rmd */ + if (cpu_mod == 3) { + new_ip = cpu_data; + + /* biu_read_u16() */ + cpu_state.eaaddr = 0x0004; + new_cs = readmemw((ovr_seg ? *ovr_seg : ds), cpu_state.eaaddr); + + do_cycle_i(); + biu_suspend_fetch(); + do_cycles_i(3); + + push(&CS); + old_ip = cpu_state.pc; + set_ip(new_ip); + + load_cs(new_cs); + + do_cycles_i(3); + biu_queue_flush(); + do_cycles_i(3); + + push(&old_ip); + } else { + do_cycle_i(); + /* read_operand_farptr() */ + new_ip = cpu_data; + read_ea2(bits); + new_cs = cpu_data; + + do_cycle_i(); + + biu_suspend_fetch(); + do_cycles_i(3); + + push(&CS); + + load_cs(new_cs); + old_ip = cpu_state.pc; + set_ip(new_ip); + do_cycles_i(3); + biu_queue_flush(); + do_cycles_i(3); + push(&old_ip); + } + break; + case 0x20: /* JMP rm */ + /* read_operand16() */ + cpu_data_opff_rm(); + + biu_suspend_fetch(); + do_cycle_i(); + set_ip(cpu_data); + biu_queue_flush(); + break; + case 0x28: /* JMP rmd */ + if (cpu_mod == 3) { + new_ip = cpu_data; + + do_cycle(); + biu_suspend_fetch(); + do_cycle(); + + /* biu_read_u16() */ + cpu_state.eaaddr = 0x0004; + new_cs = readmemw((ovr_seg ? *ovr_seg : ds), cpu_state.eaaddr); + + push(&CS); + biu_queue_flush(); + } else { + do_cycle_i(); + biu_suspend_fetch(); + do_cycle_i(); + + /* read_operand_farptr() */ + new_ip = cpu_data; + read_ea2(bits); + new_cs = cpu_data; + + load_cs(new_cs); + set_ip(new_ip); + biu_queue_flush(); + } + break; + case 0x30: /* PUSH rm */ + case 0x38: + /* read_operand16() */ + do_cycles_i(3); + + if (cpu_rm == 4) + cpu_rm -= 2; + push((uint16_t *) &cpu_data); + break; + } + break; + + default: + x808x_log("Illegal opcode: %02X\n", opcode); + biu_pfq_fetchb(); + do_cycles(8); + break; + } +} + +/* Executes instructions up to the specified number of cycles. */ +void +execvx0(int cycs) +{ + cycles += cycs; + + while (cycles > 0) { + if (started) { + started = 0; + startx86(); + } + + if (!repeating) { + cpu_state.oldpc = cpu_state.pc; + + if (clear_lock) { + in_lock = 0; + clear_lock = 0; + } + + if (!is_nec || (cpu_state.flags & MD_FLAG)) + decode(); + + oldc = cpu_state.flags & C_FLAG; + } + + x808x_log("[%04X:%04X] Opcode: %02X\n", CS, cpu_state.pc, opcode); + + execute_instruction(); + + if (completed) { + if (opcode != 0xf4) + finalize(); + + check_interrupts(); + + if (noint) + noint = 0; + } + +#ifdef USE_GDBSTUB + if (gdbstub_instruction()) + return; +#endif + } +} diff --git a/src/cpu/vx0_biu.c b/src/cpu/vx0_biu.c new file mode 100644 index 000000000..b52b14245 --- /dev/null +++ b/src/cpu/vx0_biu.c @@ -0,0 +1,1152 @@ +/* + * 86Box A hypervisor and IBM PC system emulator that specializes in + * running old operating systems and software designed for IBM + * PC systems and compatibles from 1981 through fairly recent + * system designs based on the PCI bus. + * + * This file is part of the 86Box distribution. + * + * 808x BIU emulation. + * + * Authors: Andrew Jenner, + * Miran Grca, + * + * Copyright 2015-2020 Andrew Jenner. + * Copyright 2016-2020 Miran Grca. + */ +#include +#include +#include +#include +#include +#include + +#define HAVE_STDARG_H +#include <86box/86box.h> +#include "cpu.h" +#include "x86.h" +#include <86box/machine.h> +#include <86box/io.h> +#include <86box/mem.h> +#include <86box/rom.h> +#include <86box/nmi.h> +#include <86box/pic.h> +#include <86box/ppi.h> +#include <86box/timer.h> +#include <86box/gdbstub.h> +#include <86box/plat_fallthrough.h> +#include <86box/plat_unused.h> +#include "vx0_biu.h" + +#define do_cycle() wait(1) +#define do_cycle_i() do_cycle() + +uint8_t biu_preload_byte = 0x00; + +int bus_request_type = 0; + +int pic_data = -1; +int biu_queue_preload = 0; + +/* The IP equivalent of the current prefetch queue position. */ +static uint16_t pfq_ip = 0x0000; +static uint16_t pfq_in = 0x0000; + +/* Variables to aid with the prefetch queue operation. */ +static int pfq_size = 0; + +static int cycles_ex = 0; + +/* The prefetch queue (4 bytes for 8088, 6 bytes for 8086). */ +static uint8_t pfq[6]; + +static int biu_cycles = 0; +static int biu_wait = 0; +static int biu_wait_length = 0; +static int refresh = 0; +static uint16_t mem_data = 0; +static uint32_t mem_seg = 0; +static uint16_t mem_addr = 0; +static int biu_state = 0; +static int biu_next_state = 0; +static int biu_scheduled_state = 0; +static int biu_state_length = 0; +static int biu_state_total_len = 0; +static int dma_state = 0; +static int dma_state_length = 0; +static int wait_states = 0; +static int fetch_suspended = 0; +static int ready = 1; +static int dma_wait_states = 0; + +#define BUS_CYCLE (biu_cycles & 3) +#define BUS_CYCLE_T1 biu_cycles = 0 +#define BUS_CYCLE_NEXT biu_cycles = (biu_cycles + 1) & 3 + +/* DEBUG stuff. */ +const char *lpBiuStates[7] = { "Ti ", "Ti S ", "Ti D ", "Ti R ", "Tw ", "T%i PF", "T%i EU" }; + +#ifdef ENABLE_808X_BIU_LOG +int x808x_biu_do_log = ENABLE_808X_BIU_LOG; + +static void +x808x_biu_log(const char *fmt, ...) +{ + va_list ap; + + if (x808x_biu_do_log) { + va_start(ap, fmt); + pclog_ex(fmt, ap); + va_end(ap); + } +} +#else +# define x808x_biu_log(fmt, ...) +#endif + +void +biu_set_bus_cycle(int bus_cycle) +{ + biu_cycles = bus_cycle; +} + +void +biu_set_bus_state(int bus_state) +{ + biu_state = bus_state; +} + +void +biu_set_bus_next_state(int bus_next_state) +{ + biu_state = bus_next_state; +} + +void +biu_set_cycle_t1(void) +{ + BUS_CYCLE_T1; +} + +void +biu_set_next_cycle(void) +{ + BUS_CYCLE_NEXT; +} + +int +biu_get_bus_cycle(void) +{ + return BUS_CYCLE; +} + +int +biu_get_bus_state(void) +{ + return biu_state; +} + +int +biu_get_bus_next_state(void) +{ + return biu_next_state; +} + +static void pfq_add(void); + +static void +pfq_resume(int delay) +{ + if (is_nec) + biu_state = BIU_STATE_PF; + else { + biu_state = BIU_STATE_RESUME; + biu_state_length = delay; + biu_state_total_len = delay; + } +} + +static void +pfq_switch_to_pf(int delay) +{ + if (is_nec) + biu_next_state = BIU_STATE_PF; + else { + biu_next_state = BIU_STATE_RESUME; + biu_state_length = delay; + biu_state_total_len = delay; + } +} + +static uint8_t +biu_queue_delay(void) +{ + if (is8086) + return pfq_pos == 4; + else + return pfq_pos == 3; +} + +static void +pfq_schedule(void) +{ + if (biu_state == BIU_STATE_EU) { + if (!is_nec && biu_queue_delay()) { + biu_next_state = BIU_STATE_DELAY; + biu_state_length = 3; + biu_state_total_len = 3; + } else if ((is_nec || !fetch_suspended) && (pfq_pos < 4)) + biu_next_state = BIU_STATE_PF; + else + biu_next_state = BIU_STATE_IDLE; + } else { + if (!is_nec && biu_queue_delay()) { + biu_next_state = BIU_STATE_DELAY; + biu_state_length = 3; + biu_state_total_len = 3; + } else + biu_next_state = BIU_STATE_PF; + } +} + +void +biu_reset(void) +{ + BUS_CYCLE_T1; + biu_cycles = 0; + biu_wait = 0; + refresh = 0; + bus_request_type = 0; + biu_queue_preload = 0; + pic_data = -1; + mem_data = 0; + mem_seg = 0; + mem_addr = 0; + wait_states = 0; + dma_state = DMA_STATE_IDLE; + dma_state_length = 0; + biu_state = BIU_STATE_IDLE; + biu_next_state = BIU_STATE_IDLE; + biu_scheduled_state = BIU_STATE_IDLE; + biu_state_length = 0; + pfq_size = is8086 ? 6 : 4; + pfq_in = 0x0000; +} + +static void +process_timers(void) +{ + /* On 808x systems, clock speed is usually crystal frequency divided by an integer. */ + tsc += ((uint64_t) xt_cpu_multi >> 32ULL); /* Shift xt_cpu_multi by 32 bits to + the right and then multiply. */ + if (TIMER_VAL_LESS_THAN_VAL(timer_target, (uint64_t) tsc)) + timer_process(); +} + +static void +cycles_forward(int c) +{ + for (int i = 0; i < c; i++) { + cycles--; + if (!is286) + process_timers(); + } + + cycles_ex++; +} + +static void +bus_outb(uint16_t port, uint8_t val) +{ + outb(port, val); +} + +static void +bus_outw(uint16_t port, uint16_t val) +{ + outw(port, val); +} + +static uint8_t +bus_inb(uint16_t port) +{ + uint8_t ret; + + ret = inb(port); + + return ret; +} + +static uint16_t +bus_inw(uint16_t port) +{ + uint16_t ret; + + ret = inw(port); + + return ret; +} + +static void +bus_do_io(int io_type) +{ + int old_cycles = cycles; + + x808x_biu_log("(%02X) bus_do_io(%02X): %04X\n", opcode, io_type, cpu_state.eaaddr); + + if (io_type & BUS_OUT) { + if (io_type & BUS_WIDE) + bus_outw((uint16_t) cpu_state.eaaddr, AX); + else if (io_type & BUS_HIGH) + bus_outb(((uint16_t) cpu_state.eaaddr + 1) & 0xffff, AH); + else + bus_outb((uint16_t) cpu_state.eaaddr, AL); + } else { + if (io_type & BUS_WIDE) + AX = bus_inw((uint16_t) cpu_state.eaaddr); + else if (io_type & BUS_HIGH) + AH = bus_inb(((uint16_t) cpu_state.eaaddr + 1) & 0xffff); + else + AL = bus_inb((uint16_t) cpu_state.eaaddr); + } + + resub_cycles(old_cycles); +} + +static void +bus_writeb(uint32_t seg, uint32_t addr, uint8_t val) +{ + write_mem_b(seg + addr, val); +} + +static void +bus_writew(uint32_t seg, uint32_t addr, uint16_t val) +{ + write_mem_w(seg + addr, val); +} + +static uint8_t +bus_readb(uint32_t seg, uint32_t addr) +{ + uint8_t ret = read_mem_b(seg + addr); + + return ret; +} + +static uint16_t +bus_readw(uint32_t seg, uint32_t addr) +{ + uint16_t ret = read_mem_w(seg + addr); + + return ret; +} + +static void +bus_do_mem(int io_type) +{ + int old_cycles = cycles; + + if (io_type & BUS_OUT) { + if (io_type & BUS_WIDE) + bus_writew(mem_seg, (uint32_t) mem_addr, mem_data); + else if (io_type & BUS_HIGH) { + if (is186 && !is_nec) + bus_writeb(mem_seg, ((uint32_t) mem_addr) + 1, mem_data >> 8); + else + bus_writeb(mem_seg, (uint32_t) ((mem_addr + 1) & 0xffff), mem_data >> 8); + } else + bus_writeb(mem_seg, (uint32_t) mem_addr, mem_data & 0xff); + } else { + if (io_type & BUS_WIDE) + mem_data = bus_readw(mem_seg, (uint32_t) mem_addr); + else if (io_type & BUS_HIGH) { + if (is186 && !is_nec) + mem_data = (mem_data & 0x00ff) | + (((uint16_t) bus_readb(mem_seg, ((uint32_t) mem_addr) + 1)) << 8); + else + mem_data = (mem_data & 0x00ff) | + (((uint16_t) bus_readb(mem_seg, (uint32_t) ((mem_addr + 1) & 0xffff))) << 8); + } else + mem_data = (mem_data & 0xff00) | ((uint16_t) bus_readb(mem_seg, (uint32_t) mem_addr)); + } + + resub_cycles(old_cycles); +} + +static void +biu_print_cycle(void) +{ + if ((CS == DEBUG_SEG) && (cpu_state.pc >= DEBUG_OFF_L) && (cpu_state.pc <= DEBUG_OFF_H)) { + if (biu_state >= BIU_STATE_PF) { + if (biu_wait) { + x808x_biu_log("[%04X:%04X] [%i, %i] (%i) %s (%i)\n", CS, cpu_state.pc, dma_state, dma_wait_states, + pfq_pos, lpBiuStates[BIU_STATE_WAIT], wait_states); + } else { + char temp[16] = { 0 }; + + sprintf(temp, lpBiuStates[biu_state], biu_cycles + 1); + x808x_biu_log("[%04X:%04X] [%i, %i] (%i) %s\n", CS, cpu_state.pc, dma_state, dma_wait_states, + pfq_pos, temp); + } + } else { + x808x_biu_log("[%04X:%04X] [%i, %i] (%i) %s\n", CS, cpu_state.pc, dma_state, dma_wait_states, + pfq_pos, lpBiuStates[biu_state]); + } + } +} + +static void +do_wait(void) +{ + if (wait_states > 0) + wait_states--; + + if (dma_wait_states > 0) + dma_wait_states--; +} + +static void +run_dma_cycle(void) +{ + int bus_cycle_check = ((biu_state < BIU_STATE_PF) || (BUS_CYCLE == BUS_T3) || + (BUS_CYCLE == BUS_T4) || biu_wait) && !in_lock; + + switch (dma_state) { + case DMA_STATE_TIMER: + dma_state = DMA_STATE_DREQ; + dma_state_length = 1; + break; + case DMA_STATE_DREQ: + dma_state = DMA_STATE_HRQ; + dma_state_length = 1; + break; + case DMA_STATE_HRQ: + if (!in_lock && bus_cycle_check) { + dma_state = DMA_STATE_HLDA; + dma_state_length = 1; + } + break; + case DMA_STATE_HLDA: + dma_state = DMA_STATE_OPERATING; + dma_state_length = 4; + break; + case DMA_STATE_OPERATING: + dma_state_length--; + if (dma_state_length == 3) { + dma_wait_states = 7; + ready = 0; + } else if (dma_state_length == 0) { + dma_state = DMA_STATE_IDLE; + dma_state_length = 1; + } + break; + } +} + +static void +biu_cycle_idle(int type) +{ + if ((CS == DEBUG_SEG) && (cpu_state.pc >= DEBUG_OFF_L) && (cpu_state.pc <= DEBUG_OFF_H)) { + x808x_biu_log("[%04X:%04X] [%i, %i] (%i) %s\n", CS, cpu_state.pc, dma_state, dma_wait_states, + pfq_pos, lpBiuStates[type]); + } + + run_dma_cycle(); + cycles_forward(1); + + do_wait(); +} + +/* Reads a byte from the memory but does not advance the BIU. */ +static uint8_t +readmembf(uint32_t a) +{ + uint8_t ret; + + a = cs + (a & 0xffff); + ret = read_mem_b(a); + + return ret; +} + +static uint16_t +readmemwf(uint16_t a) +{ + uint16_t ret; + + ret = read_mem_w(cs + (a & 0xffff)); + + return ret; +} + +static void +do_bus_access(void) +{ + int io_type = (biu_state == BIU_STATE_EU) ? bus_request_type : BUS_CODE; + + x808x_biu_log("[%04X:%04X] %02X bus access %02X\n", CS, cpu_state.pc, opcode, io_type); + + if (io_type != 0) { + wait_states = 0; + switch (io_type & BUS_ACCESS_TYPE) { + case BUS_CODE: + if (is8086) + pfq_in = readmemwf(pfq_ip); + else + pfq_in = readmembf(pfq_ip); + break; + case BUS_IO: + bus_do_io(io_type); + break; + case BUS_MEM: + bus_do_mem(io_type); + break; + case BUS_PIC: + pic_data = pic_irq_ack(); + break; + default: + break; + } + } +} + +void +resub_cycles(int old_cycles) +{ + if (old_cycles > cycles) + wait_states = old_cycles - cycles; + + cycles = old_cycles; +} + +static uint8_t +biu_queue_has_room(void) +{ + if (is8086) + return pfq_pos < 5; + else + return pfq_pos < 4; +} + +static int bus_access_done = 0; + +static void +biu_do_cycle(void) +{ + int biu_old_state = biu_state; + + biu_print_cycle(); + + switch (biu_state) { + default: + fatal("Invalid BIU state: %02X\n", biu_state); + break; + case BIU_STATE_RESUME: + if (biu_state_length > 0) { + biu_state_length--; + if (biu_state_length == 0) { + biu_state = BIU_STATE_PF; + biu_next_state = BIU_STATE_PF; + } + } else { + biu_state = BIU_STATE_PF; + biu_next_state = BIU_STATE_PF; + } + break; + case BIU_STATE_IDLE: + case BIU_STATE_SUSP: + biu_state = biu_next_state; + break; + case BIU_STATE_DELAY: + if (biu_state_length > 0) { + biu_state_length--; + if (biu_state_length == 0) { + if (biu_queue_has_room()) { + biu_state = BIU_STATE_PF; + biu_next_state = BIU_STATE_PF; + } else { + biu_state = BIU_STATE_IDLE; + biu_next_state = BIU_STATE_IDLE; + } + } + } else { + if (biu_queue_has_room()) { + biu_state = BIU_STATE_PF; + biu_next_state = BIU_STATE_PF; + } else { + biu_state = BIU_STATE_IDLE; + biu_next_state = BIU_STATE_IDLE; + } + } + break; + case BIU_STATE_PF: + case BIU_STATE_EU: + if (biu_wait) { + if ((wait_states == 0) && (dma_wait_states == 0)) { + biu_wait = 0; + BUS_CYCLE_NEXT; + } + } else { + if (BUS_CYCLE == BUS_T4) { + if (biu_state == BIU_STATE_PF) + pfq_add(); + biu_state = biu_next_state; + } + + if ((BUS_CYCLE == BUS_T3) && (biu_state == BIU_STATE_EU)) { + if ((bus_request_type != 0) && ((bus_request_type & BUS_ACCESS_TYPE) == BUS_IO)) + wait_states++; + } + + if ((BUS_CYCLE == BUS_T3) && ((wait_states != 0) || (dma_wait_states != 0))) + biu_wait = 1; + else { + biu_wait = 0; + BUS_CYCLE_NEXT; + } + + if ((BUS_CYCLE == BUS_T1) && (biu_state == BIU_STATE_IDLE) && biu_queue_delay()) { + if (biu_old_state == BIU_STATE_EU) + pfq_resume(2); + else + biu_state = BIU_STATE_PF; + } + } + + if (bus_access_done && !biu_wait) + bus_access_done = 0; + break; + } +} + +static int +biu_is_last_tw(void) +{ + return ((biu_state >= BIU_STATE_PF) && biu_wait && ((wait_states + dma_wait_states) == 1)); +} + +static void +biu_cycle(void) +{ + if (biu_state >= BIU_STATE_PF) { + if (BUS_CYCLE == BUS_T2) + pfq_schedule(); + else if (((BUS_CYCLE == BUS_T3) && !biu_wait) || biu_is_last_tw()) { + if (!bus_access_done) { + do_bus_access(); + bus_access_done = 1; + } + } + } + + run_dma_cycle(); + + biu_do_cycle(); + + cycles_forward(1); + + do_wait(); +} + +static void +biu_eu_request(void) +{ + switch (biu_state) { + default: + fatal("Invalid BIU state: %02X\n", biu_state); + break; + case BIU_STATE_RESUME: + /* Resume it - leftover cycles. */ + if (!is_nec) for (uint8_t i = 0; i < (biu_state_total_len - biu_state_length); i++) + biu_cycle_idle(biu_state); + break; + case BIU_STATE_IDLE: + case BIU_STATE_SUSP: + /* Resume it - 3 cycles. */ + if (!is_nec) for (uint8_t i = 0; i < 3; i++) + biu_cycle_idle(biu_state); + break; + case BIU_STATE_DELAY: + case BIU_STATE_EU: + /* Do the request immediately (needs hardware testing). */ + biu_state_length = 0; + break; + case BIU_STATE_PF: + /* Transition the state. */ + switch (BUS_CYCLE) { + case BUS_T1: + case BUS_T2: + /* Leftover BIU cycles. */ + do + biu_cycle(); + while (BUS_CYCLE != BUS_T1); + break; + case BUS_T3: + case BUS_T4: + /* Leftover BIU cycles. */ + do + biu_cycle(); + while (BUS_CYCLE != BUS_T1); + /* The two abort cycles. */ + if (!is_nec) for (uint8_t i = 0; i < 2; i++) + biu_cycle_idle(BIU_STATE_IDLE); + break; + + default: + break; + } + break; + } + + biu_state = BIU_STATE_EU; + biu_next_state = BIU_STATE_EU; + + biu_state_length = 0; +} + +void +wait(int c) +{ + x808x_biu_log("[%04X:%04X] %02X %i cycles\n", CS, cpu_state.pc, opcode, c); + + for (uint8_t i = 0; i < c; i++) + biu_cycle(); +} + +/* This is for external subtraction of cycles, ie. wait states. */ +void +sub_cycles(int c) +{ + cycles -= c; +} + +void +biu_begin_eu(void) +{ + biu_eu_request(); +} + +static void +biu_wait_for_write_finish(void) +{ + while (BUS_CYCLE != BUS_T4) { + biu_cycle(); + if (biu_wait_length == 1) + break; + } +} + +void +biu_wait_for_read_finish(void) +{ + biu_wait_for_write_finish(); + biu_cycle(); +} + +void +cpu_io_vx0(int bits, int out, uint16_t port) +{ + /* Do this, otherwise, the first half of the operation never happens. */ + if ((BUS_CYCLE == BUS_T4) && (biu_state == BIU_STATE_EU)) + BUS_CYCLE_T1; + + if (out) { + if (bits == 16) { + if (is8086 && !(port & 1)) { + bus_request_type = BUS_IO | BUS_OUT | BUS_WIDE; + biu_begin_eu(); + biu_wait_for_write_finish(); + } else { + bus_request_type = BUS_IO | BUS_OUT; + biu_begin_eu(); + biu_wait_for_write_finish(); + biu_cycle(); + biu_state = BIU_STATE_EU; + biu_state_length = 0; + bus_request_type = BUS_IO | BUS_OUT | BUS_HIGH; + biu_wait_for_write_finish(); + } + } else { + bus_request_type = BUS_IO | BUS_OUT; + biu_begin_eu(); + biu_wait_for_write_finish(); + } + } else { + if (bits == 16) { + if (is8086 && !(port & 1)) { + bus_request_type = BUS_IO | BUS_WIDE; + biu_begin_eu(); + biu_wait_for_read_finish(); + } else { + bus_request_type = BUS_IO; + biu_begin_eu(); + biu_wait_for_read_finish(); + biu_state = BIU_STATE_EU; + biu_state_length = 0; + bus_request_type = BUS_IO | BUS_HIGH; + biu_wait_for_read_finish(); + } + } else { + bus_request_type = BUS_IO; + biu_begin_eu(); + biu_wait_for_read_finish(); + } + } + + bus_request_type = 0; +} + +void +biu_state_set_eu(void) +{ + biu_state = BIU_STATE_EU; + biu_state_length = 0; +} + +/* Reads a byte from the memory and advances the BIU. */ +uint8_t +readmemb_vx0(uint32_t s, uint16_t a) +{ + uint8_t ret; + + mem_seg = s; + mem_addr = a; + /* Do this, otherwise, the first half of the operation never happens. */ + if ((BUS_CYCLE == BUS_T4) && (biu_state == BIU_STATE_EU)) + BUS_CYCLE_T1; + bus_request_type = BUS_MEM; + biu_begin_eu(); + biu_wait_for_read_finish(); + ret = mem_data & 0xff; + bus_request_type = 0; + + return ret; +} + +/* Reads a word from the memory and advances the BIU. */ +uint16_t +readmemw_vx0(uint32_t s, uint16_t a) +{ + uint16_t ret; + + mem_seg = s; + mem_addr = a; + /* Do this, otherwise, the first half of the operation never happens. */ + if ((BUS_CYCLE == BUS_T4) && (biu_state == BIU_STATE_EU)) + BUS_CYCLE_T1; + if (is8086 && !(a & 1)) { + bus_request_type = BUS_MEM | BUS_WIDE; + biu_begin_eu(); + biu_wait_for_read_finish(); + } else { + bus_request_type = BUS_MEM | BUS_HIGH; + biu_begin_eu(); + biu_wait_for_read_finish(); + biu_state = BIU_STATE_EU; + biu_state_length = 0; + bus_request_type = BUS_MEM; + biu_wait_for_read_finish(); + } + ret = mem_data; + bus_request_type = 0; + + return ret; +} + +uint16_t +readmem_vx0(uint32_t s) +{ + if (opcode & 1) + return readmemw_vx0(s, cpu_state.eaaddr); + else + return (uint16_t) readmemb_vx0(s, cpu_state.eaaddr); +} + +uint32_t +readmeml_vx0(uint32_t s, uint16_t a) +{ + uint32_t temp; + + temp = (uint32_t) (readmemw_vx0(s, a + 2)) << 16; + temp |= readmemw_vx0(s, a); + + return temp; +} + +uint64_t +readmemq_vx0(uint32_t s, uint16_t a) +{ + uint64_t temp; + + temp = (uint64_t) (readmeml_vx0(s, a + 4)) << 32; + temp |= readmeml_vx0(s, a); + + return temp; +} + +/* Writes a byte to the memory and advances the BIU. */ +void +writememb_vx0(uint32_t s, uint32_t a, uint8_t v) +{ + uint32_t addr = s + a; + + mem_seg = s; + mem_addr = a; + mem_data = v; + /* Do this, otherwise, the first half of the operation never happens. */ + if ((BUS_CYCLE == BUS_T4) && (biu_state == BIU_STATE_EU)) + BUS_CYCLE_T1; + bus_request_type = BUS_MEM | BUS_OUT; + biu_begin_eu(); + biu_wait_for_write_finish(); + bus_request_type = 0; + + if ((addr >= 0xf0000) && (addr <= 0xfffff)) + last_addr = addr & 0xffff; +} + +/* Writes a word to the memory and advances the BIU. */ +void +writememw_vx0(uint32_t s, uint32_t a, uint16_t v) +{ + uint32_t addr = s + a; + + mem_seg = s; + mem_addr = a; + mem_data = v; + /* Do this, otherwise, the first half of the operation never happens. */ + if ((BUS_CYCLE == BUS_T4) && (biu_state == BIU_STATE_EU)) + BUS_CYCLE_T1; + if (is8086 && !(a & 1)) { + bus_request_type = BUS_MEM | BUS_OUT | BUS_WIDE; + biu_begin_eu(); + biu_wait_for_write_finish(); + } else { + bus_request_type = BUS_MEM | BUS_OUT | BUS_HIGH; + biu_begin_eu(); + biu_wait_for_write_finish(); + biu_cycle(); + biu_state = BIU_STATE_EU; + biu_state_length = 0; + bus_request_type = BUS_MEM | BUS_OUT; + biu_wait_for_write_finish(); + } + bus_request_type = 0; + + if ((addr >= 0xf0000) && (addr <= 0xfffff)) + last_addr = addr & 0xffff; +} + +void +writemem_vx0(uint32_t s, uint16_t v) +{ + if (opcode & 1) + writememw_vx0(s, cpu_state.eaaddr, v); + else + writememb_vx0(s, cpu_state.eaaddr, (uint8_t) (v & 0xff)); +} + +void +writememl_vx0(uint32_t s, uint32_t a, uint32_t v) +{ + writememw_vx0(s, a, v & 0xffff); + writememw_vx0(s, a + 2, v >> 16); +} + +void +writememq_vx0(uint32_t s, uint32_t a, uint64_t v) +{ + writememl_vx0(s, a, v & 0xffffffff); + writememl_vx0(s, a + 4, v >> 32); +} + +static void +pfq_write(void) +{ + uint16_t tempw; + /* Byte fetch on odd addres on 8086 to simulate the HL toggle. */ + int fetch_word = is8086 && !(pfq_ip & 1); + + if (fetch_word && (pfq_pos < (pfq_size - 1))) { + /* The 8086 fetches 2 bytes at a time, and only if there's at least 2 bytes + free in the queue. */ + tempw = pfq_in; + *(uint16_t *) &(pfq[pfq_pos]) = tempw; + pfq_ip = (pfq_ip + 2) & 0xffff; + pfq_pos += 2; + } else if (!fetch_word && (pfq_pos < pfq_size)) { + /* The 8088 fetches 1 byte at a time, and only if there's at least 1 byte + free in the queue. */ + if (pfq_pos >= 0) + pfq[pfq_pos] = pfq_in & 0xff; + pfq_ip = (pfq_ip + 1) & 0xffff; + pfq_pos++; + } + + if (pfq_pos >= pfq_size) + pfq_pos = pfq_size; +} + +uint8_t +biu_pfq_read(void) +{ + uint8_t temp; + + temp = pfq[0]; + for (int i = 0; i < (pfq_size - 1); i++) + pfq[i] = pfq[i + 1]; + pfq_pos--; + if (pfq_pos < 0) + pfq_pos = 0; + cpu_state.pc = (cpu_state.pc + 1) & 0xffff; + return temp; +} + +void +biu_resume_on_queue_read(void) +{ + if ((biu_next_state == BIU_STATE_IDLE) && (pfq_pos == 3)) + // pfq_switch_to_pf(is_nec ? 0 : ((biu_state == BIU_STATE_IDLE) ? 3 : 0)); + pfq_switch_to_pf(is_nec ? 0 : 3); +} + +/* Fetches a byte from the prefetch queue, or from memory if the queue has + been drained. + + Cycles: 1 If fetching from the queue; + (4 - (biu_cycles & 3)) If fetching from the bus - fetch into the queue; + 1 If fetching from the bus - delay. */ +uint8_t +biu_pfq_fetchb_common(void) +{ + uint8_t temp; + + if (biu_queue_preload) { + if (nx) + nx = 0; + + biu_queue_preload = 0; + return biu_preload_byte; + } + + if (pfq_pos > 0) { + if (biu_state == BIU_STATE_DELAY) { + while (biu_state == BIU_STATE_DELAY) + biu_cycle(); + } + + temp = biu_pfq_read(); + biu_resume_on_queue_read(); + } else { + /* Fill the queue. */ + while (pfq_pos == 0) + biu_cycle(); + + /* Fetch. */ + temp = biu_pfq_read(); + } + + do_cycle(); + return temp; +} + +/* The timings are above. */ +uint8_t +biu_pfq_fetchb(void) +{ + uint8_t ret; + + ret = biu_pfq_fetchb_common(); + return ret; +} + +/* Fetches a word from the prefetch queue, or from memory if the queue has + been drained. */ +uint16_t +biu_pfq_fetchw(void) +{ + uint16_t temp; + + temp = biu_pfq_fetchb_common(); + temp |= (biu_pfq_fetchb_common() << 8); + + return temp; +} + +uint16_t +biu_pfq_fetch(void) +{ + if (opcode & 1) + return biu_pfq_fetchw(); + else + return (uint16_t) biu_pfq_fetchb(); +} + +/* Adds bytes to the prefetch queue based on the instruction's cycle count. */ +static void +pfq_add(void) +{ + if ((biu_state == BIU_STATE_PF) && (pfq_pos < pfq_size)) + pfq_write(); +} + +void +biu_update_pc(void) +{ + pfq_ip = cpu_state.pc; + biu_queue_preload = 0; +} + +/* Clear the prefetch queue - called on reset and on anything that affects either CS or IP. */ +void +biu_queue_flush(void) +{ + pfq_pos = 0; + biu_update_pc(); + + fetch_suspended = 0; + + /* FLUSH command. */ + if ((biu_state == BIU_STATE_SUSP) || (biu_state == BIU_STATE_IDLE)) + pfq_resume(3); +} + +static void +biu_bus_wait_finish(void) +{ + while (BUS_CYCLE != BUS_T4) + biu_cycle(); +} + +void +biu_suspend_fetch(void) +{ + biu_state_length = 0; + fetch_suspended = 1; + + if (biu_state == BIU_STATE_PF) { + if (is_nec) + BUS_CYCLE_T1; + else { + biu_bus_wait_finish(); + biu_cycle(); + } + biu_state = BIU_STATE_IDLE; + biu_next_state = BIU_STATE_IDLE; + } else { + if (biu_state == BIU_STATE_EU) + BUS_CYCLE_T1; + biu_state = BIU_STATE_IDLE; + biu_next_state = BIU_STATE_IDLE; + } +} + +/* Memory refresh read - called by reads and writes on DMA channel 0. */ +void +refreshread(void) +{ + if (dma_state == DMA_STATE_IDLE) { + dma_state = DMA_STATE_TIMER; + dma_state_length = 1; + } +} diff --git a/src/cpu/vx0_biu.h b/src/cpu/vx0_biu.h new file mode 100644 index 000000000..58e2ae257 --- /dev/null +++ b/src/cpu/vx0_biu.h @@ -0,0 +1,116 @@ +/* + * 86Box A hypervisor and IBM PC system emulator that specializes in + * running old operating systems and software designed for IBM + * PC systems and compatibles from 1981 through fairly recent + * system designs based on the PCI bus. + * + * This file is part of the 86Box distribution. + * + * 808x BIU emulation header. + * + * Authors: Andrew Jenner, + * Miran Grca, + * + * Copyright 2015-2020 Andrew Jenner. + * Copyright 2016-2020 Miran Grca. + */ +#ifndef EMU_808X_BIU_H +#define EMU_808X_BIU_H + +#define DEBUG_SEG 0xf000 +// #define DEBUG_SEG 0x0f3c +// #define DEBUG_SEG 0x1e1f +// #define DEBUG_SEG 0xf000 +// #define DEBUG_SEG 0xc800 +// #define DEBUG_SEG 0x0070 +// #define DEBUG_SEG 0x0291 +// #define DEBUG_SEG 0xefff +// #define DEBUG_SEG 0x15a2 + +// #define DEBUG_OFF_L 0x2c3b +// #define DEBUG_OFF_L 0xe182 +// #define DEBUG_OFF_L 0xf000 +// #define DEBUG_OFF_H 0xefff +// #define DEBUG_OFF_L 0x0000 +// #define DEBUG_OFF_H 0xffff +#define DEBUG_OFF_L 0xf300 +#define DEBUG_OFF_H 0xf3ff + +#define BUS_OUT 1 +#define BUS_HIGH 2 +#define BUS_WIDE 4 +#define BUS_CODE 8 +#define BUS_IO 16 +#define BUS_MEM 32 +#define BUS_PIC 64 +#define BUS_ACCESS_TYPE (BUS_CODE | BUS_IO | BUS_MEM | BUS_PIC) + +#undef readmemb +#undef readmemw +#undef readmeml +#undef readmemq + +enum { + BUS_T1 = 0, + BUS_T2, + BUS_T3, + BUS_T4 +}; + +enum { + BIU_STATE_IDLE, + BIU_STATE_SUSP, + BIU_STATE_DELAY, + BIU_STATE_RESUME, + BIU_STATE_WAIT, + BIU_STATE_PF, + BIU_STATE_EU +}; + +enum { + DMA_STATE_IDLE, + DMA_STATE_TIMER, + DMA_STATE_DREQ, + DMA_STATE_HRQ, + DMA_STATE_HLDA, + DMA_STATE_OPERATING +}; + +/* Temporary BIU externs - move to 808x_biu.h. */ +extern void biu_resume_on_queue_read(void); +extern void wait(int c); +extern void biu_reset(void); +extern void cpu_io_vx0(int bits, int out, uint16_t port); +extern void biu_state_set_eu(void); +extern uint8_t readmemb_vx0(uint32_t s, uint16_t a); +extern uint16_t readmemw_vx0(uint32_t s, uint16_t a); +extern uint16_t readmem_vx0(uint32_t s); +extern uint32_t readmeml_vx0(uint32_t s, uint16_t a); +extern uint64_t readmemq_vx0(uint32_t s, uint16_t a); +extern void writememb_vx0(uint32_t s, uint32_t a, uint8_t v); +extern void writememw_vx0(uint32_t s, uint32_t a, uint16_t v); +extern void writemem_vx0(uint32_t s, uint16_t v); +extern void writememl_vx0(uint32_t s, uint32_t a, uint32_t v); +extern void writememq_vx0(uint32_t s, uint32_t a, uint64_t v); +extern uint8_t biu_pfq_read(void); +extern uint8_t biu_pfq_fetchb_common(void); +extern uint8_t biu_pfq_fetchb(void); +extern uint16_t biu_pfq_fetchw(void); +extern uint16_t biu_pfq_fetch(void); +extern void biu_update_pc(void); +extern void biu_queue_flush(void); +extern void biu_suspend_fetch(void); +extern void biu_begin_eu(void); +extern void biu_wait_for_read_finish(void); + +extern uint8_t biu_preload_byte; + +extern int nx; + +extern int schedule_fetch; +extern int in_lock; +extern int bus_request_type; +extern int pic_data; +extern int biu_queue_preload; + +#endif /*EMU_808X_BIU_H*/ diff --git a/src/cpu/x86.c b/src/cpu/x86.c index 38e50a488..a19ac440a 100644 --- a/src/cpu/x86.c +++ b/src/cpu/x86.c @@ -367,8 +367,12 @@ reset_common(int hard) } else device_reset_all(DEVICE_SOFTRESET); - if (!is286) - reset_808x(hard); + if (!is286) { + if (is_nec) + reset_vx0(hard); + else + reset_808x(hard); + } in_lock = 0; diff --git a/src/cpu/x86.h b/src/cpu/x86.h index ccfeadea0..c0140520f 100644 --- a/src/cpu/x86.h +++ b/src/cpu/x86.h @@ -37,6 +37,7 @@ extern uint8_t opcode; extern uint8_t flags_p; extern uint8_t znptable8[256]; +extern uint16_t last_addr; extern uint16_t zero; extern uint16_t oldcs; extern uint16_t lastcs; @@ -44,6 +45,7 @@ extern uint16_t lastpc; extern uint16_t *mod1add[2][8]; extern uint16_t znptable16[65536]; +extern int pfq_pos; extern int x86_was_reset; extern int trap; extern int codegen_flat_ss; diff --git a/src/cpu/x87_ops.h b/src/cpu/x87_ops.h index 4d099885a..f74e5e91a 100644 --- a/src/cpu/x87_ops.h +++ b/src/cpu/x87_ops.h @@ -551,7 +551,11 @@ static int FPU_ILLEGAL_a16(UNUSED(uint32_t fetchdat)) { geteaw(); +#ifdef FPU_NEC + do_cycles(timing_rr); +#else wait_cycs(timing_rr, 0); +#endif return 0; } #else diff --git a/src/cpu/x87_ops_misc.h b/src/cpu/x87_ops_misc.h index 417beea62..68955e07d 100644 --- a/src/cpu/x87_ops_misc.h +++ b/src/cpu/x87_ops_misc.h @@ -7,7 +7,11 @@ opFI(uint32_t fetchdat) cpu_state.npxc &= ~0x80; if (rmdat == 0xe1) cpu_state.npxc |= 0x80; +#ifdef FPU_NEC + do_cycles(3); +#else wait_cycs(3, 0); +#endif return 0; } #else diff --git a/src/cpu/x87_ops_sf.h b/src/cpu/x87_ops_sf.h index adbaa2003..722d86435 100644 --- a/src/cpu/x87_ops_sf.h +++ b/src/cpu/x87_ops_sf.h @@ -354,7 +354,11 @@ sf_FI(uint32_t fetchdat) fpu_state.cwd &= ~FPU_SW_Summary; if (rmdat == 0xe1) fpu_state.cwd |= FPU_SW_Summary; +#ifdef FPU_NEC + do_cycles(3); +#else wait_cycs(3, 0); +#endif return 0; } #else