Files
86Box/src/cpu/808x.c

3639 lines
109 KiB
C

/*
* 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, <https://www.reenigne.org>
* Miran Grca, <mgrca8@gmail.com>
*
* Copyright 2015-2020 Andrew Jenner.
* Copyright 2016-2020 Miran Grca.
*/
#include <math.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <wchar.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>
/* Is the CPU 8088 or 8086. */
int is8086 = 0;
uint8_t use_custom_nmi_vector = 0;
uint32_t custom_nmi_vector = 0x00000000;
/* The prefetch queue (4 bytes for 8088, 6 bytes for 8086). */
static uint8_t pfq[6];
/* Variables to aid with the prefetch queue operation. */
static int biu_cycles = 0, pfq_pos = 0;
/* The IP equivalent of the current prefetch queue position. */
static uint16_t pfq_ip;
/* Pointer tables needed for segment overrides. */
static uint32_t *opseg[4];
static x86seg *_opseg[4];
static int noint = 0;
static int in_lock = 0;
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;
static uint32_t *ovr_seg = NULL;
static int prefetching = 1, completed = 1;
static int in_rep = 0, repeating = 0, rep_c_flag = 0;
static int oldc, clear_lock = 0;
static int refresh = 0, cycdiff;
/* 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) \
{ \
wait(val, 0); \
}
#define CLOCK_CYCLES_ALWAYS(val) \
{ \
wait(val, 0); \
}
#if 0
# define CLOCK_CYCLES_FPU(val) \
{ \
wait(val, 0); \
}
# define CLOCK_CYCLES(val) \
{ \
if (fpu_cycles > 0) { \
fpu_cycles -= (val); \
if (fpu_cycles < 0) { \
wait(val, 0); \
} \
} else { \
wait(val, 0); \
} \
}
# define CONCURRENCY_CYCLES(c) fpu_cycles = (c)
#else
# define CLOCK_CYCLES(val) \
{ \
wait(val, 0); \
}
# define CLOCK_CYCLES_FPU(val) \
{ \
wait(val, 0); \
}
# define CONCURRENCY_CYCLES(c)
#endif
typedef int (*OpFn)(uint32_t fetchdat);
static int tempc_fpu = 0;
#ifdef ENABLE_808X_LOG
void dumpregs(int);
int x808x_do_log = ENABLE_808X_LOG;
int indump = 0;
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 void pfq_add(void);
static void set_pzs(int bits);
uint16_t
get_last_addr(void)
{
return last_addr;
}
static void
clock_start(void)
{
cycdiff = cycles;
}
static void
clock_end(void)
{
int diff = cycdiff - cycles;
#if 0
uint64_t i;
#endif
/* On 808x systems, clock speed is usually crystal frequency divided by an integer. */
tsc += ((uint64_t) diff * ((uint64_t) xt_cpu_multi >> 32ULL)); /* Shift xt_cpu_multi by 32 bits to the right and then multiply. */
#if 0
tsc--;
for (i = 0; i < ((uint64_t) xt_cpu_multi >> 32ULL); i++) {
tsc++;
if (TIMER_VAL_LESS_THAN_VAL(timer_target, (uint32_t) tsc))
timer_process();
}
#else
if (TIMER_VAL_LESS_THAN_VAL(timer_target, (uint32_t) tsc))
timer_process();
#endif
}
static void
process_timers(void)
{
clock_end();
clock_start();
}
static void
cycles_forward(int c)
{
cycles -= c;
if (!is286)
process_timers();
}
static int access_code = 0;
/* TODO: Proper BIU implementation with six states: T1-T4, Ti (idle), and TW (wait).
Type: 0 = idle, 1 = wait, 2 = bus. */
static void
cycles_biu(int bus, int init)
{
/* T1, T2 = Nothing, T3 = Schedule, T4 = Start */
// int schedule_fetch = prefetching && (pfq_pos < pfq_size) && (biu_cycles > 1);
int schedule_fetch = prefetching && (pfq_pos < pfq_size) && (biu_cycles > 2);
int left;
int idle = !bus && !schedule_fetch;
int d;
// pclog("cycles_biu(%i, %i): %i, %i, %i, %i\n", bus, init, prefetching, pfq_pos, pfq_size, biu_cycles);
if (bus) {
/* CPU wants non-code bus access. */
if (init) {
if (schedule_fetch) {
if ((biu_cycles & 3) == 2) {
/* If a fetch has been scheduled but not started, abort it and insert two idle cycles. */
for (d = 0; d < 2; d++)
cycles_forward(1);
access_code = 0;
} else {
/* If a fetch has been started, finish it. */
left = 4 - (biu_cycles & 3);
for (d = 0; d < left; d++) {
cycles_forward(1);
if (pfq_pos < pfq_size) {
if (biu_cycles == 3) {
// pclog("%04X:%04X %02X %i pfq_add()\n", CS, cpu_state.pc, opcode, bus);
pfq_add();
}
biu_cycles = (biu_cycles + 1) & 3;
}
}
}
}
}
biu_cycles = (biu_cycles + 1) & 3;
} else {
/* CPU wants idle or code bus access. */
if (schedule_fetch) {
if (biu_cycles == 0)
access_code = 1;
if (biu_cycles == 3) {
// pclog("%04X:%04X %02X %i pfq_add()\n", CS, cpu_state.pc, opcode, bus);
pfq_add();
}
}
biu_cycles = (biu_cycles + 1) & 3;
}
if ((refresh > 0) && (idle || (biu_cycles >= 2))) {
/* Refresh pending and it's either non-bus cycle or T3-T4,
insert the 6 wait states. */
cycles_forward(6);
/* Decrease the refresh count. */
refresh--;
}
}
static int last_was_code = 0;
static void
bus_init(void)
{
/* Replacement for the old access() stuff. */
if ((biu_cycles == 3) && last_was_code && (opcode != 0x8f) && (opcode != 0xc7) && (opcode != 0xcc) && (opcode != 0xcd) && (opcode != 0xce) && ((opcode & 0xf0) != 0xa0))
cycles_forward(1);
cycles_forward(2);
if (biu_cycles == 1)
cycles_forward(1);
}
/* Bus:
0 CPU cycles without bus access.
1 CPU cycle T1-T4, bus access.
2 CPU cycle Tw (wait state).
3 CPU cycle Ti (idle).
*/
/* This is used for cycle forwards that are not wait states. */
static void
wait(int c, int bus)
{
int d;
for (d = 0; d < c; d++) {
cycles_biu(bus, !d);
cycles_forward(1);
}
}
/* This is for external subtraction of cycles, ie. wait states. */
void
sub_cycles(int c)
{
if (c > 0)
cycles_forward(c);
}
void
resub_cycles(int old_cycles)
{
int cyc_diff = 0;
if (old_cycles > cycles) {
cyc_diff = old_cycles - cycles;
cycles = old_cycles;
sub_cycles(cyc_diff);
}
}
#undef readmemb
#undef readmemw
#undef readmeml
#undef readmemq
static void
cpu_io(int bits, int out, uint16_t port)
{
int old_cycles = cycles;
bus_init();
if (out) {
wait(4, 1);
if (bits == 16) {
if (is8086 && !(port & 1)) {
old_cycles = cycles;
outw(port, AX);
} else {
wait(4, 1);
old_cycles = cycles;
outb(port++, AL);
outb(port, AH);
}
} else {
old_cycles = cycles;
outb(port, AL);
}
} else {
wait(4, 1);
if (bits == 16) {
if (is8086 && !(port & 1)) {
old_cycles = cycles;
AX = inw(port);
} else {
wait(4, 1);
old_cycles = cycles;
AL = inb(port++);
AH = inb(port);
}
} else {
old_cycles = cycles;
AL = inb(port);
}
}
resub_cycles(old_cycles);
last_was_code = 0;
}
/* Reads a byte from the memory and advances the BIU. */
static uint8_t
readmemb(uint32_t a)
{
uint8_t ret;
bus_init();
wait(4, 1);
ret = read_mem_b(a);
last_was_code = 0;
return ret;
}
/* 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);
last_was_code = 1;
return ret;
}
/* Reads a word from the memory and advances the BIU. */
static uint16_t
readmemw(uint32_t s, uint16_t a)
{
uint16_t ret;
bus_init();
wait(4, 1);
if (is8086 && !(a & 1))
ret = read_mem_w(s + a);
else {
wait(4, 1);
ret = read_mem_b(s + a);
ret |= read_mem_b(s + ((is186 && !is_nec) ? (a + 1) : (a + 1) & 0xffff)) << 8;
}
last_was_code = 0;
return ret;
}
static uint16_t
readmemwf(uint16_t a)
{
uint16_t ret;
ret = read_mem_w(cs + (a & 0xffff));
last_was_code = 1;
return ret;
}
static uint16_t
readmem(uint32_t s)
{
if (opcode & 1)
return readmemw(s, cpu_state.eaaddr);
else
return (uint16_t) readmemb(s + cpu_state.eaaddr);
}
static uint32_t
readmeml(uint32_t s, uint16_t a)
{
uint32_t temp;
temp = (uint32_t) (readmemw(s, a + 2)) << 16;
temp |= readmemw(s, a);
return temp;
}
static uint64_t
readmemq(uint32_t s, uint16_t a)
{
uint64_t temp;
temp = (uint64_t) (readmeml(s, a + 4)) << 32;
temp |= readmeml(s, a);
last_was_code = 0;
return temp;
}
/* Writes a byte to the memory and advances the BIU. */
static void
writememb(uint32_t s, uint32_t a, uint8_t v)
{
uint32_t addr = s + a;
bus_init();
wait(4, 1);
write_mem_b(addr, v);
if ((addr >= 0xf0000) && (addr <= 0xfffff))
last_addr = addr & 0xffff;
last_was_code = 0;
}
/* Writes a word to the memory and advances the BIU. */
static void
writememw(uint32_t s, uint32_t a, uint16_t v)
{
uint32_t addr = s + a;
bus_init();
wait(4, 1);
if (is8086 && !(a & 1))
write_mem_w(addr, v);
else {
write_mem_b(addr, v & 0xff);
wait(4, 1);
addr = s + ((is186 && !is_nec) ? (a + 1) : ((a + 1) & 0xffff));
write_mem_b(addr, v >> 8);
}
if ((addr >= 0xf0000) && (addr <= 0xfffff))
last_addr = addr & 0xffff;
last_was_code = 0;
}
static void
writemem(uint32_t s, uint16_t v)
{
if (opcode & 1)
writememw(s, cpu_state.eaaddr, v);
else
writememb(s, cpu_state.eaaddr, (uint8_t) (v & 0xff));
}
static void
writememl(uint32_t s, uint32_t a, uint32_t v)
{
writememw(s, a, v & 0xffff);
writememw(s, a + 2, v >> 16);
}
static void
writememq(uint32_t s, uint32_t a, uint64_t v)
{
writememl(s, a, v & 0xffffffff);
writememl(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 = readmemwf(pfq_ip);
*(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. */
pfq[pfq_pos] = readmembf(pfq_ip);
pfq_ip = (pfq_ip + 1) & 0xffff;
pfq_pos++;
}
if (pfq_pos >= pfq_size)
pfq_pos = pfq_size;
}
static uint8_t
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;
}
/* 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. */
static uint8_t
pfq_fetchb_common(void)
{
uint8_t temp;
if (pfq_pos == 0) {
/* Reset prefetch queue internal position. */
pfq_ip = cpu_state.pc;
/* Fill the queue. */
while (pfq_pos == 0)
wait(1, 0);
}
/* Fetch. */
temp = pfq_read();
return temp;
}
/* The timings are above. */
static uint8_t
pfq_fetchb(void)
{
uint8_t ret;
ret = pfq_fetchb_common();
wait(1, 0);
return ret;
}
/* Fetches a word from the prefetch queue, or from memory if the queue has
been drained. */
static uint16_t
pfq_fetchw(void)
{
uint16_t temp;
temp = pfq_fetchb_common();
wait(1, 0);
temp |= (pfq_fetchb_common() << 8);
return temp;
}
static uint16_t
pfq_fetch(void)
{
if (opcode & 1)
return pfq_fetchw();
else
return (uint16_t) pfq_fetchb();
}
/* Adds bytes to the prefetch queue based on the instruction's cycle count. */
static void
pfq_add(void)
{
if (prefetching && (pfq_pos < pfq_size))
pfq_write();
}
/* Clear the prefetch queue - called on reset and on anything that affects either CS or IP. */
static void
pfq_clear(void)
{
pfq_pos = 0;
prefetching = 0;
biu_cycles = 0;
}
static void
pfq_suspend(void)
{
pfq_clear();
wait(2, 0);
if (biu_cycles != 3)
wait(1, 0);
wait(1, 0);
}
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;
}
void
reset_808x(int hard)
{
biu_cycles = 0;
in_rep = 0;
in_lock = 0;
completed = 1;
repeating = 0;
clear_lock = 0;
refresh = 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;
pfq_size = (is8086) ? 6 : 4;
pfq_clear();
}
load_cs(0xFFFF);
cpu_state.pc = 0;
if (is_nec)
cpu_state.flags |= MD_FLAG;
rammask = 0xfffff;
prefetching = 1;
cpu_alu_op = 0;
biu_cycles = 0;
use_custom_nmi_vector = 0x00;
custom_nmi_vector = 0x00000000;
}
static void
set_ip(uint16_t new_ip)
{
pfq_ip = cpu_state.pc = new_ip;
prefetching = 1;
}
/* Memory refresh read - called by reads and writes on DMA channel 0. */
void
refreshread(void)
{
refresh++;
}
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);
}
/* Fetches the effective address from the prefetch queue according to MOD and R/M. */
static void
do_mod_rm(void)
{
rmdat = pfq_fetchb();
cpu_reg = (rmdat >> 3) & 7;
cpu_mod = (rmdat >> 6) & 3;
cpu_rm = rmdat & 7;
if (cpu_mod == 3)
return;
wait(2, 0);
if ((rmdat & 0xc7) == 0x06) {
cpu_state.eaaddr = pfq_fetchw();
easeg = ovr_seg ? *ovr_seg : ds;
wait(2, 0);
return;
} else switch (cpu_rm) {
case 0:
case 3:
wait(2, 0);
break;
case 1:
case 2:
wait(3, 0);
break;
}
cpu_state.eaaddr = (*mod1add[0][cpu_rm]) + (*mod1add[1][cpu_rm]);
easeg = ovr_seg ? *ovr_seg : *mod1seg[cpu_rm];
switch (rmdat & 0xc0) {
case 0x40:
wait(2, 0);
cpu_state.eaaddr += sign_extend(pfq_fetchb());
wait(1, 0);
break;
case 0x80:
wait(2, 0);
cpu_state.eaaddr += pfq_fetchw();
wait(1, 0);
break;
}
cpu_state.eaaddr &= 0xffff;
wait(2, 0);
}
#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)
{
if (cpu_mod == 3)
return (getr8(cpu_rm));
return readmemb(easeg + cpu_state.eaaddr);
}
/* Reads a word from the effective address. */
static uint16_t
geteaw(void)
{
if (cpu_mod == 3)
return cpu_state.regs[cpu_rm].w;
return readmemw(easeg, cpu_state.eaaddr);
}
/* Neede for 8087 - memory only. */
static uint32_t
geteal(void)
{
if (cpu_mod == 3) {
fatal("808x register geteal()\n");
return 0xffffffff;
}
return readmeml(easeg, cpu_state.eaaddr);
}
/* Neede for 8087 - memory only. */
static uint64_t
geteaq(void)
{
if (cpu_mod == 3) {
fatal("808x register geteaq()\n");
return 0xffffffff;
}
return readmemq(easeg, cpu_state.eaaddr);
}
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_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
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
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 tempc tempc_fpu
#include "x87.h"
#include "x87_ops.h"
#undef tempc
#undef FPU_8087
/* 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
access(int num, int bits)
{
}
/* Calls an interrupt. */
static void
interrupt(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;
new_ip = readmemw(0, cpu_state.eaaddr);
wait(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;
wait(2, 0);
access(25, 16);
tempf = cpu_state.flags & (is_nec ? 0x8fd7 : 0x0fd7);
push(&tempf);
cpu_state.flags &= ~(I_FLAG | T_FLAG);
wait(5, 0);
access(25, 16);
push(&old_cs);
old_ip = cpu_state.pc;
load_cs(new_cs);
pfq_suspend();
set_ip(new_ip);
wait(2, 0);
access(25, 16);
push(&old_ip);
}
void
interrupt_808x(uint16_t addr)
{
interrupt(addr);
}
static void
custom_nmi(void)
{
uint16_t old_cs, old_ip;
uint16_t new_cs, new_ip;
uint16_t tempf;
cpu_state.eaaddr = 0x0002;
old_cs = CS;
(void) readmemw(0, cpu_state.eaaddr);
new_ip = custom_nmi_vector & 0xffff;
wait(1, 0);
cpu_state.eaaddr = (cpu_state.eaaddr + 2) & 0xffff;
access(6, 16);
(void) readmemw(0, cpu_state.eaaddr);
new_cs = custom_nmi_vector >> 16;
prefetching = 0;
pfq_clear();
ovr_seg = NULL;
wait(2, 0);
access(25, 16);
tempf = cpu_state.flags & (is_nec ? 0x8fd7 : 0x0fd7);
push(&tempf);
cpu_state.flags &= ~(I_FLAG | T_FLAG);
wait(5, 0);
access(25, 16);
push(&old_cs);
old_ip = cpu_state.pc;
load_cs(new_cs);
pfq_suspend();
set_ip(new_ip);
wait(2, 0);
access(25, 16);
push(&old_ip);
}
static int
irq_pending(void)
{
uint8_t temp;
temp = (nmi && nmi_enable && nmi_mask) || ((cpu_state.flags & T_FLAG) && !noint) || ((cpu_state.flags & I_FLAG) && pic.int_pending && !noint);
return temp;
}
static int
bus_pic_ack(void)
{
wait(4, 1);
return pic_irq_ack();
}
static void
check_interrupts(void)
{
int temp;
if (irq_pending()) {
if ((cpu_state.flags & T_FLAG) && !noint) {
interrupt(1);
return;
}
if (nmi && nmi_enable && nmi_mask) {
nmi_enable = 0;
wait(2, 0);
if (use_custom_nmi_vector)
custom_nmi();
else
interrupt(2);
#ifndef OLD_NMI_BEHAVIOR
nmi = 0;
#endif
return;
}
if ((cpu_state.flags & I_FLAG) && pic.int_pending && !noint) {
repeating = 0;
completed = 1;
ovr_seg = NULL;
wait(4, 0);
/* ACK to PIC */
temp = bus_pic_ack();
wait(1, 0);
/* ACK to PIC */
temp = bus_pic_ack();
wait(1, 0);
in_lock = 0;
clear_lock = 0;
if (biu_cycles != 2)
wait(1, 0);
wait(5, 0);
/* Here is where temp should be filled, but we cheat. */
opcode = 0x00;
interrupt(temp);
}
}
}
static uint16_t tmpc;
static int
rep_setup(void)
{
if (repeating)
return 0;
wait(2, 0);
if (in_rep == 0)
return 0;
wait(4, 0);
tmpc = CX;
if (tmpc == 0)
return 1;
wait(3, 0);
return 0;
}
static int
rep_interrupt(void)
{
if (!irq_pending()) {
repeating = 1;
completed = 0;
return 0;
}
completed = 1;
CX = tmpc;
pfq_clear();
if (is_nec && (ovr_seg != NULL))
set_ip(cpu_state.pc - 3);
else
set_ip(cpu_state.pc - 2);
return 1;
}
static int
rep_action(int bits)
{
uint16_t t;
if (in_rep == 0)
return 0;
wait(2, 0);
t = CX;
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);
t = 0;
}
if (t == 0) {
wait(1, 0);
completed = 1;
repeating = 0;
return 1;
}
--CX;
completed = 0;
wait(2, 0);
if (!repeating)
wait(2, 0);
return 0;
}
static uint16_t
jump(uint16_t delta)
{
uint16_t old_ip;
wait(1, 0);
pfq_clear();
wait(2, 0);
if (biu_cycles == 3)
wait(1, 0);
wait(2, 0);
old_ip = cpu_state.pc;
set_ip((cpu_state.pc + delta) & 0xffff);
return old_ip;
}
static void
jump_short(void)
{
jump(sign_extend((uint8_t) cpu_data));
}
static uint16_t
jump_near(void)
{
return jump(pfq_fetchw());
}
/* Performs a conditional jump. */
static void
jcc(uint8_t opcode, int cond)
{
/* int8_t offset; */
wait(1, 0);
cpu_data = pfq_fetchb();
wait(1, 0);
if ((!cond) == !!(opcode & 0x01))
jump_short();
}
static void
set_cf(int cond)
{
cpu_state.flags = (cpu_state.flags & ~C_FLAG) | (cond ? C_FLAG : 0);
}
static void
set_if(int cond)
{
cpu_state.flags = (cpu_state.flags & ~I_FLAG) | (cond ? I_FLAG : 0);
}
static void
set_df(int cond)
{
cpu_state.flags = (cpu_state.flags & ~D_FLAG) | (cond ? D_FLAG : 0);
}
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
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_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
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++;
/* Fall through. */
case 0:
add(bits);
break;
case 3:
if (cpu_state.flags & C_FLAG)
cpu_src++;
/* Fall through. */
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;
}
}
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
mul(uint16_t a, uint16_t b)
{
int negate = 0;
int bit_count = 8;
int carry, i;
uint16_t high_bit = 0x80;
uint16_t size_mask;
uint16_t c, r;
size_mask = (1 << bit_count) - 1;
if (opcode != 0xd5) {
if (opcode & 1) {
bit_count = 16;
high_bit = 0x8000;
} else
wait(8, 0);
size_mask = (1 << bit_count) - 1;
if ((rmdat & 0x38) == 0x28) {
if (!top_bit(a, bit_count)) {
if (top_bit(b, bit_count)) {
wait(1, 0);
if ((b & size_mask) != ((opcode & 1) ? 0x8000 : 0x80))
wait(1, 0);
b = ~b + 1;
negate = 1;
}
} else {
wait(1, 0);
a = ~a + 1;
negate = 1;
if (top_bit(b, bit_count)) {
b = ~b + 1;
negate = 0;
} else
wait(4, 0);
}
wait(10, 0);
}
wait(3, 0);
}
c = 0;
a &= size_mask;
carry = (a & 1) != 0;
a >>= 1;
for (i = 0; i < bit_count; ++i) {
wait(7, 0);
if (carry) {
cpu_src = c;
cpu_dest = b;
add(bit_count);
c = cpu_data & size_mask;
wait(1, 0);
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;
wait(9, 0);
}
cpu_data = a;
cpu_dest = c;
set_sf(bit_count);
set_pf();
set_af(0);
}
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_co_mul(int bits, int carry)
{
set_cf(carry);
set_of(carry);
set_zf_ex(!carry);
if (!carry)
wait(1, 0);
}
/* Was div(), renamed to avoid conflicts with stdlib div(). */
static int
x86_div(uint16_t l, uint16_t h)
{
int b, bit_count = 8;
int negative = 0;
int dividend_negative = 0;
int size_mask, 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;
wait(4, 0);
}
if (top_bit(cpu_src, bit_count)) {
cpu_src = ~cpu_src + 1;
negative = !negative;
} else
wait(1, 0);
wait(9, 0);
}
wait(3, 0);
}
wait(8, 0);
cpu_src &= size_mask;
if (h >= cpu_src) {
if (opcode != 0xd4)
wait(1, 0);
interrupt(0);
return 0;
}
if (opcode != 0xd4)
wait(1, 0);
wait(2, 0);
carry = 1;
for (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;
wait(8, 0);
if (carry) {
carry = 0;
h -= cpu_src;
if (b == bit_count - 1)
wait(2, 0);
} else {
carry = cpu_src > h;
if (!carry) {
h -= cpu_src;
wait(1, 0);
if (b == bit_count - 1)
wait(2, 0);
}
}
}
l = ~((l << 1) + (carry ? 1 : 0));
if (opcode != 0xd4 && (rmdat & 0x38) == 0x38) {
wait(4, 0);
if (top_bit(l, bit_count)) {
if (cpu_mod == 3)
wait(1, 0);
interrupt(0);
return 0;
}
wait(7, 0);
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
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 void
aa(void)
{
set_pzs(8);
AL = cpu_data & 0x0f;
wait(6, 0);
}
static void
set_ca(void)
{
set_cf(1);
set_af(1);
}
static void
clear_ca(void)
{
set_cf(0);
set_af(0);
}
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;
}
}
uint16_t
cpu_inw(uint16_t port)
{
if (is8086 && !(port & 1)) {
wait(4, 0);
} else {
wait(8, 0);
}
return inw(port);
}
void
cpu_outw(uint16_t port, uint16_t val)
{
if (is8086 && !(port & 1)) {
wait(4, 0);
} else {
wait(8, 0);
}
return outw(port, val);
}
/* Executes instructions up to the specified number of cycles. */
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 tempw_int, size, tempbp, lowbound;
uint16_t highbound, regval, orig_sp, wordtopush;
uint16_t immediate, old_flags;
uint16_t tmpa;
int bits;
uint32_t dest_seg, i, carry, nibble;
uint32_t srcseg, byteaddr;
cycles += cycs;
while (cycles > 0) {
clock_start();
if (!repeating) {
cpu_state.oldpc = cpu_state.pc;
// opcode = pfq_fetchb();
opcode = pfq_fetchb_common();
handled = 0;
oldc = cpu_state.flags & C_FLAG;
if (clear_lock) {
in_lock = 0;
clear_lock = 0;
}
wait(1, 0);
}
completed = 1;
// pclog("[%04X:%04X] Opcode: %02X\n", CS, cpu_state.pc, opcode);
if (is186) {
switch (opcode) {
case 0x60: /*PUSHA/PUSH R*/
orig_sp = SP;
wait(1, 0);
push(&AX);
push(&CX);
push(&DX);
push(&BX);
push(&orig_sp);
push(&BP);
push(&SI);
push(&DI);
handled = 1;
break;
case 0x61: /*POPA/POP R*/
wait(9, 0);
DI = pop();
SI = pop();
BP = pop();
(void) pop(); /* former orig_sp */
BX = pop();
DX = pop();
CX = pop();
AX = pop();
handled = 1;
break;
case 0x62: /* BOUND r/m */
lowbound = 0;
highbound = 0;
regval = 0;
do_mod_rm();
lowbound = readmemw(easeg, cpu_state.eaaddr);
highbound = readmemw(easeg, cpu_state.eaaddr + 2);
regval = get_reg(cpu_reg);
if (lowbound > regval || highbound < regval) {
cpu_state.pc = cpu_state.oldpc;
interrupt(5);
}
handled = 1;
break;
case 0x64:
case 0x65:
if (is_nec) {
/* REPC/REPNC */
wait(1, 0);
in_rep = (opcode == 0x64 ? 1 : 2);
rep_c_flag = 1;
completed = 0;
handled = 1;
}
break;
case 0x68:
wordtopush = pfq_fetchw();
wait(1, 0);
push(&wordtopush);
handled = 1;
break;
case 0x69:
immediate = 0;
bits = 16;
do_mod_rm();
read_ea(0, 16);
immediate = pfq_fetchw();
mul(cpu_data & 0xFFFF, immediate);
set_reg(cpu_reg, cpu_data);
set_co_mul(16, cpu_dest != 0);
handled = 1;
break;
case 0x6a:
wordtopush = sign_extend(pfq_fetchb());
push(&wordtopush);
handled = 1;
break;
case 0x6b: /* IMUL reg16,reg16/mem16,imm8 */
immediate = 0;
bits = 16;
do_mod_rm();
read_ea(0, 16);
immediate = pfq_fetchb();
mul(cpu_data & 0xFFFF, immediate);
set_reg(cpu_reg, cpu_data);
set_co_mul(16, cpu_dest != 0);
handled = 1;
break;
case 0x6c:
case 0x6d: /* INM dst, DW/INS dst, DX */
bits = 8 << (opcode & 1);
handled = 1;
if (!repeating)
wait(2, 0);
if (rep_action(bits))
break;
else if (!repeating)
wait(7, 0);
if (bits == 16) {
writememw(es, DI, cpu_inw(DX));
DI += (cpu_state.flags & D_FLAG) ? -2 : 2;
} else {
wait(4, 0);
writememb(es, DI, inb(DX));
DI += (cpu_state.flags & D_FLAG) ? -1 : 1;
}
if (in_rep == 0)
break;
repeating = 1;
clock_end();
break;
case 0x6e:
case 0x6f: /* OUTM DW, src/OUTS DX, src */
dest_seg = ovr_seg ? *ovr_seg : ds;
bits = 8 << (opcode & 1);
handled = 1;
if (!repeating)
wait(2, 0);
if (rep_action(bits))
break;
else if (!repeating)
wait(7, 0);
if (bits == 16) {
cpu_outw(DX, readmemw(dest_seg, SI));
SI += (cpu_state.flags & D_FLAG) ? -2 : 2;
} else {
wait(4, 0);
outb(DX, readmemb(dest_seg + SI));
SI += (cpu_state.flags & D_FLAG) ? -1 : 1;
}
if (in_rep == 0)
break;
repeating = 1;
clock_end();
break;
case 0xc8: /* ENTER/PREPARE */
tempw_int = 0;
size = pfq_fetchw();
nests = pfq_fetchb();
i = 0;
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;
handled = 1;
break;
case 0xc0:
case 0xc1: /*rot imm8 */
bits = 8 << (opcode & 1);
do_mod_rm();
if (cpu_mod == 3)
wait(1, 0);
access(53, bits);
cpu_data = get_ea();
cpu_src = pfq_fetchb();
wait((cpu_mod != 3) ? 9 : 6, 0);
if (!is_nec)
cpu_src &= 0x1F;
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;
}
if ((opcode & 2) != 0)
wait(4, 0);
--cpu_src;
}
access(17, bits);
set_ea(cpu_data);
handled = 1;
break;
case 0xc9: /* LEAVE/DISPOSE */
SP = BP;
BP = pop();
handled = 1;
break;
}
}
if (!handled) {
switch (opcode) {
case 0x06:
case 0x0E:
case 0x16:
case 0x1E: /* PUSH seg */
wait(3, 0);
access(29, 16);
push(&(_opseg[(opcode >> 3) & 0x03]->seg));
break;
case 0x07:
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(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(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(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(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(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(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(5, 0);
for (i = 0; i < ((nibbles_count / 2) + odd); i++) {
wait(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(5, 0);
for (i = 0; i < ((nibbles_count / 2) + odd); i++) {
wait(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(5, 0);
for (i = 0; i < ((nibbles_count / 2) + odd); i++) {
wait(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(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(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 */
/* Unimplemented for now. */
fatal("808x: Unsupported 8080 emulation mode attempted to enter into!");
break;
default:
opcode = orig_opcode;
cpu_state.pc = (cpu_state.pc - 1) & 0xffff;
break;
}
} else {
wait(1, 0);
if (opcode == 0x0F) {
load_cs(pop());
pfq_pos = 0;
} else
load_seg(pop(), _opseg[(opcode >> 3) & 0x03]);
/* All POP segment instructions suppress interrupts for one instruction. */
noint = 1;
}
break;
case 0x26: /*ES:*/
case 0x2E: /*CS:*/
case 0x36: /*SS:*/
case 0x3E: /*DS:*/
wait(1, 0);
ovr_seg = opseg[(opcode >> 3) & 0x03];
completed = 0;
break;
case 0x00:
case 0x01:
case 0x02:
case 0x03:
case 0x08:
case 0x09:
case 0x0a:
case 0x0b:
case 0x10:
case 0x11:
case 0x12:
case 0x13:
case 0x18:
case 0x19:
case 0x1a:
case 0x1b:
case 0x20:
case 0x21:
case 0x22:
case 0x23:
case 0x28:
case 0x29:
case 0x2a:
case 0x2b:
case 0x30:
case 0x31:
case 0x32:
case 0x33:
case 0x38:
case 0x39:
case 0x3a:
case 0x3b:
/* alu rm, r / r, rm */
bits = 8 << (opcode & 1);
do_mod_rm();
access(46, bits);
tempw = get_ea();
cpu_alu_op = (opcode >> 3) & 7;
if ((opcode & 2) == 0) {
cpu_dest = tempw;
cpu_src = get_reg(cpu_reg);
} else {
cpu_dest = get_reg(cpu_reg);
cpu_src = tempw;
}
wait(1, 0);
if (cpu_mod != 3)
wait(1, 0);
alu_op(bits);
wait(1, 0);
if (cpu_alu_op != 7) {
if ((opcode & 2) == 0) {
access(10, bits);
set_ea(cpu_data);
if (cpu_mod == 3)
wait(2, 0);
} else
set_reg(cpu_reg, cpu_data);
}
break;
case 0x04:
case 0x05:
case 0x0c:
case 0x0d:
case 0x14:
case 0x15:
case 0x1c:
case 0x1d:
case 0x24:
case 0x25:
case 0x2c:
case 0x2d:
case 0x34:
case 0x35:
case 0x3c:
case 0x3d:
/* alu A, imm */
bits = 8 << (opcode & 1);
wait(1, 0);
cpu_data = pfq_fetch();
cpu_dest = get_accum(bits); /* AX/AL */
cpu_src = cpu_data;
cpu_alu_op = (opcode >> 3) & 7;
alu_op(bits);
if (cpu_alu_op != 7)
set_accum(bits, cpu_data);
wait(1, 0);
break;
case 0x27: /*DAA*/
cpu_dest = AL;
set_of(0);
old_af = !!(cpu_state.flags & A_FLAG);
if ((cpu_state.flags & A_FLAG) || (AL & 0x0f) > 9) {
cpu_src = 6;
cpu_data = cpu_dest + cpu_src;
set_of_add(8);
cpu_dest = cpu_data;
set_af(1);
}
if ((cpu_state.flags & C_FLAG) || AL > (old_af ? 0x9f : 0x99)) {
cpu_src = 0x60;
cpu_data = cpu_dest + cpu_src;
set_of_add(8);
cpu_dest = cpu_data;
set_cf(1);
}
AL = cpu_dest;
set_pzs(8);
wait(3, 0);
break;
case 0x2F: /*DAS*/
cpu_dest = AL;
set_of(0);
old_af = !!(cpu_state.flags & A_FLAG);
if ((cpu_state.flags & A_FLAG) || ((AL & 0xf) > 9)) {
cpu_src = 6;
cpu_data = cpu_dest - cpu_src;
set_of_sub(8);
cpu_dest = cpu_data;
set_af(1);
}
if ((cpu_state.flags & C_FLAG) || AL > (old_af ? 0x9f : 0x99)) {
cpu_src = 0x60;
cpu_data = cpu_dest - cpu_src;
set_of_sub(8);
cpu_dest = cpu_data;
set_cf(1);
}
AL = cpu_dest;
set_pzs(8);
wait(3, 0);
break;
case 0x37: /*AAA*/
wait(1, 0);
if ((cpu_state.flags & A_FLAG) || ((AL & 0xf) > 9)) {
cpu_src = 6;
++AH;
set_ca();
} else {
cpu_src = 0;
clear_ca();
wait(1, 0);
}
cpu_dest = AL;
cpu_data = cpu_dest + cpu_src;
set_of_add(8);
aa();
break;
case 0x3F: /*AAS*/
wait(1, 0);
if ((cpu_state.flags & A_FLAG) || ((AL & 0xf) > 9)) {
cpu_src = 6;
--AH;
set_ca();
} else {
cpu_src = 0;
clear_ca();
wait(1, 0);
}
cpu_dest = AL;
cpu_data = cpu_dest - cpu_src;
set_of_sub(8);
aa();
break;
case 0x40:
case 0x41:
case 0x42:
case 0x43:
case 0x44:
case 0x45:
case 0x46:
case 0x47:
case 0x48:
case 0x49:
case 0x4A:
case 0x4B:
case 0x4C:
case 0x4D:
case 0x4E:
case 0x4F:
/* INCDEC rw */
wait(1, 0);
cpu_dest = cpu_state.regs[opcode & 7].w;
cpu_src = 1;
bits = 16;
if ((opcode & 8) == 0) {
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(16);
cpu_state.regs[opcode & 7].w = cpu_data;
break;
case 0x50:
case 0x51:
case 0x52:
case 0x53: /*PUSH r16*/
case 0x54:
case 0x55:
case 0x56:
case 0x57:
wait(3, 0);
push(&(cpu_state.regs[opcode & 0x07].w));
break;
case 0x58:
case 0x59:
case 0x5A:
case 0x5B: /*POP r16*/
case 0x5C:
case 0x5D:
case 0x5E:
case 0x5F:
wait(1, 0);
cpu_state.regs[opcode & 0x07].w = pop();
break;
case 0x60: /*JO alias*/
case 0x70: /*JO*/
case 0x61: /*JNO alias*/
case 0x71: /*JNO*/
jcc(opcode, cpu_state.flags & V_FLAG);
break;
case 0x62: /*JB alias*/
case 0x72: /*JB*/
case 0x63: /*JNB alias*/
case 0x73: /*JNB*/
jcc(opcode, cpu_state.flags & C_FLAG);
break;
case 0x64: /*JE alias*/
case 0x74: /*JE*/
case 0x65: /*JNE alias*/
case 0x75: /*JNE*/
jcc(opcode, cpu_state.flags & Z_FLAG);
break;
case 0x66: /*JBE alias*/
case 0x76: /*JBE*/
case 0x67: /*JNBE alias*/
case 0x77: /*JNBE*/
jcc(opcode, cpu_state.flags & (C_FLAG | Z_FLAG));
break;
case 0x68: /*JS alias*/
case 0x78: /*JS*/
case 0x69: /*JNS alias*/
case 0x79: /*JNS*/
jcc(opcode, cpu_state.flags & N_FLAG);
break;
case 0x6A: /*JP alias*/
case 0x7A: /*JP*/
case 0x6B: /*JNP alias*/
case 0x7B: /*JNP*/
jcc(opcode, cpu_state.flags & P_FLAG);
break;
case 0x6C: /*JL alias*/
case 0x7C: /*JL*/
case 0x6D: /*JNL alias*/
case 0x7D: /*JNL*/
temp = (cpu_state.flags & N_FLAG) ? 1 : 0;
temp2 = (cpu_state.flags & V_FLAG) ? 1 : 0;
jcc(opcode, temp ^ temp2);
break;
case 0x6E: /*JLE alias*/
case 0x7E: /*JLE*/
case 0x6F: /*JNLE alias*/
case 0x7F: /*JNLE*/
temp = (cpu_state.flags & N_FLAG) ? 1 : 0;
temp2 = (cpu_state.flags & V_FLAG) ? 1 : 0;
jcc(opcode, (cpu_state.flags & Z_FLAG) || (temp != temp2));
break;
case 0x80:
case 0x81:
case 0x82:
case 0x83:
/* alu rm, imm */
bits = 8 << (opcode & 1);
do_mod_rm();
access(47, bits);
cpu_data = get_ea();
cpu_dest = cpu_data;
if (cpu_mod != 3)
wait(1, 0);
wait(1, 0);
if (opcode == 0x81)
cpu_src = pfq_fetchw();
else {
if (opcode == 0x83)
cpu_src = sign_extend(pfq_fetchb());
else
cpu_src = pfq_fetchb() | 0xff00;
}
wait(1, 0);
cpu_alu_op = (rmdat & 0x38) >> 3;
alu_op(bits);
if (cpu_alu_op != 7) {
if (cpu_mod != 3)
wait(1, 0);
access(16, bits);
set_ea(cpu_data);
} else {
if (cpu_mod != 3)
wait(1, 0);
}
break;
case 0x84:
case 0x85:
/* TEST rm, reg */
bits = 8 << (opcode & 1);
do_mod_rm();
access(48, bits);
cpu_data = get_ea();
test(bits, cpu_data, get_reg(cpu_reg));
if (cpu_mod != 3)
wait(1, 0);
wait(2, 0);
break;
case 0x86:
case 0x87:
/* XCHG rm, reg */
bits = 8 << (opcode & 1);
do_mod_rm();
access(49, bits);
cpu_data = get_ea();
cpu_src = get_reg(cpu_reg);
set_reg(cpu_reg, cpu_data);
wait(3, 0);
if (cpu_mod != 3)
wait(3, 0);
access(16, bits);
set_ea(cpu_src);
break;
case 0x88:
case 0x89:
/* MOV rm, reg */
bits = 8 << (opcode & 1);
do_mod_rm();
wait(1, 0);
if (cpu_mod != 3)
wait(3, 0);
access(16, bits);
set_ea(get_reg(cpu_reg));
break;
case 0x8A:
case 0x8B:
/* MOV reg, rm */
bits = 8 << (opcode & 1);
do_mod_rm();
access(50, bits);
set_reg(cpu_reg, get_ea());
wait(1, 0);
if (cpu_mod != 3)
wait(1, 0);
break;
case 0x8C: /*MOV w,sreg*/
do_mod_rm();
wait(1, 0);
if (cpu_mod != 3)
wait(2, 0);
access(14, 16);
seteaw(_opseg[(rmdat & 0x18) >> 3]->seg);
break;
case 0x8D: /*LEA*/
do_mod_rm();
cpu_state.regs[cpu_reg].w = cpu_state.eaaddr;
wait(1, 0);
if (cpu_mod != 3)
wait(1, 0);
break;
case 0x8E: /*MOV sreg,w*/
do_mod_rm();
tempw = geteaw();
if ((rmdat & 0x18) == 0x08) {
load_cs(tempw);
pfq_pos = 0;
} else
load_seg(tempw, _opseg[(rmdat & 0x18) >> 3]);
wait(1, 0);
if (cpu_mod != 3)
wait(1, 0);
if (((rmdat & 0x18) >> 3) == 2)
noint = 1;
break;
case 0x8F: /*POPW*/
do_mod_rm();
wait(2, 0);
cpu_src = cpu_state.eaaddr;
if (cpu_mod != 3)
wait(1, 0);
access(20, 16);
wait(1, 0);
if (cpu_mod != 3)
wait(2, 0);
cpu_data = pop();
cpu_state.eaaddr = cpu_src;
access(16, 16);
seteaw(cpu_data);
break;
case 0x90:
case 0x91:
case 0x92:
case 0x93:
case 0x94:
case 0x95:
case 0x96:
case 0x97:
/* XCHG AX, rw */
wait(1, 0);
cpu_data = cpu_state.regs[opcode & 7].w;
cpu_state.regs[opcode & 7].w = AX;
AX = cpu_data;
wait(1, 0);
break;
case 0x98: /*CBW*/
wait(1, 0);
AX = sign_extend(AL);
break;
case 0x99: /*CWD*/
wait(4, 0);
if (!top_bit(AX, 16))
DX = 0;
else {
wait(1, 0);
DX = 0xffff;
}
break;
case 0x9A: /*CALL FAR*/
wait(1, 0);
new_ip = pfq_fetchw();
wait(1, 0);
new_cs = pfq_fetchw();
wait(1, 0);
pfq_suspend();
access(25, 16);
push(&(CS));
wait(4, 0);
cpu_state.oldpc = cpu_state.pc;
load_cs(new_cs);
set_ip(new_ip);
wait(1, 0);
access(25, 16);
push((uint16_t *) &(cpu_state.oldpc));
break;
case 0x9B: /*WAIT*/
if (!repeating)
wait(2, 0);
wait(5, 0);
#ifdef NO_HACK
if (irq_pending()) {
wait(7, 0);
check_interrupts();
} else {
repeating = 1;
completed = 0;
clock_end();
}
#else
wait(7, 0);
check_interrupts();
#endif
break;
case 0x9C: /*PUSHF*/
wait(4, 0);
access(16, 16);
if (is_nec)
tempw = (cpu_state.flags & 0x8fd7) | 0x7000;
else
tempw = (cpu_state.flags & 0x0fd7) | 0xf000;
push(&tempw);
break;
case 0x9D: /*POPF*/
access(25, 16);
wait(1, 0);
if (is_nec)
cpu_state.flags = pop() | 0x8002;
else
cpu_state.flags = pop() | 0x0002;
break;
case 0x9E: /*SAHF*/
wait(1, 0);
cpu_state.flags = (cpu_state.flags & 0xff02) | AH;
wait(2, 0);
break;
case 0x9F: /*LAHF*/
wait(1, 0);
AH = cpu_state.flags & 0xd7;
break;
case 0xA0:
case 0xA1:
/* MOV A, [iw] */
bits = 8 << (opcode & 1);
wait(2, 0);
cpu_state.eaaddr = pfq_fetchw();
access(6, bits);
set_accum(bits, readmem((ovr_seg ? *ovr_seg : ds)));
break;
case 0xA2:
case 0xA3:
/* MOV [iw], A */
bits = 8 << (opcode & 1);
wait(2, 0);
cpu_state.eaaddr = pfq_fetchw();
access(46, bits);
writemem((ovr_seg ? *ovr_seg : ds), get_accum(bits));
wait(2, 0);
break;
case 0xA4:
case 0xA5: /* MOVS */
case 0xAC:
case 0xAD: /* LODS */
bits = 8 << (opcode & 1);
if (rep_setup())
break;
if (in_rep != 0 && (biu_cycles == 3))
wait(1, 0);
lods(bits);
if ((opcode & 8) == 0) {
wait(1, 0);
stos(bits);
} else
set_accum(bits, cpu_data);
wait(3, 0);
if (in_rep == 0)
break;
--tmpc;
if (rep_interrupt())
break;
CX = tmpc;
if (tmpc == 0) {
completed = 1;
wait(1, 0);
if ((opcode & 8) != 0)
wait(2, 0);
} else {
wait(2, 0);
if ((opcode & 8) != 0)
wait(2, 0);
}
break;
case 0xA6:
case 0xA7: /* CMPS */
case 0xAE:
case 0xAF: /* SCAS */
bits = 8 << (opcode & 1);
if (rep_setup())
break;
tmpa = AX;
if ((opcode & 8) == 0) {
wait(1, 0);
lods(bits);
tmpa = cpu_data;
}
wait(2, 0);
cpu_state.eaaddr = DI;
cpu_data = readmem(es);
DI = string_increment(bits);
cpu_src = cpu_data;
cpu_dest = tmpa;
sub(bits);
wait(2, 0);
if (in_rep == 0) {
wait(2, 0);
break;
}
--tmpc;
CX = tmpc;
if ((!!(cpu_state.flags & (rep_c_flag ? C_FLAG : Z_FLAG))) == (in_rep == 1)) {
wait(3, 0);
break;
}
if (rep_interrupt())
break;
wait(4, 0);
if (tmpc == 0)
completed = 1;
else
wait(1, 0);
break;
case 0xA8:
case 0xA9:
/* TEST A, imm */
bits = 8 << (opcode & 1);
wait(1, 0);
cpu_data = pfq_fetch();
test(bits, get_accum(bits), cpu_data);
wait(1, 0);
break;
case 0xAA:
case 0xAB: /* STOS */
bits = 8 << (opcode & 1);
if (rep_setup())
break;
cpu_data = AX;
if (in_rep == 0 && (biu_cycles == 3))
wait(1, 0);
stos(bits);
wait(3, 0);
if (in_rep == 0)
break;
--tmpc;
if (rep_interrupt())
break;
CX = tmpc;
if (tmpc == 0) {
completed = 1;
wait(1, 0);
} else
wait(2, 0);
break;
case 0xB0:
case 0xB1:
case 0xB2:
case 0xB3: /*MOV cpu_reg,#8*/
case 0xB4:
case 0xB5:
case 0xB6:
case 0xB7:
wait(1, 0);
if (opcode & 0x04)
cpu_state.regs[opcode & 0x03].b.h = pfq_fetchb();
else
cpu_state.regs[opcode & 0x03].b.l = pfq_fetchb();
wait(1, 0);
break;
case 0xB8:
case 0xB9:
case 0xBA:
case 0xBB: /*MOV cpu_reg,#16*/
case 0xBC:
case 0xBD:
case 0xBE:
case 0xBF:
wait(1, 0);
cpu_state.regs[opcode & 0x07].w = pfq_fetchw();
wait(1, 0);
break;
case 0xC0:
case 0xC1:
case 0xC2:
case 0xC3:
case 0xC8:
case 0xC9:
case 0xCA:
case 0xCB:
/* RET */
bits = 8 + (opcode & 0x08);
wait(1, 0);
if (!(opcode & 1)) {
cpu_src = pfq_fetchw();
wait(2, 0);
}
if ((opcode & 9) == 9)
wait(2, 0);
pfq_clear();
access(25, bits);
new_ip = pop();
wait(1, 0);
if ((opcode & 8) == 0) {
new_cs = CS;
if (opcode & 1)
wait(1, 0);
} else {
wait(2, 0);
access(6, bits);
new_cs = pop();
}
if (!(opcode & 1))
SP += cpu_src;
load_cs(new_cs);
set_ip(new_ip);
break;
case 0xC4:
case 0xC5:
/* LsS rw, rmd */
do_mod_rm();
bits = 16;
read_ea(1, bits);
cpu_state.regs[cpu_reg].w = cpu_data;
if (cpu_mod != 3)
wait(2, 0);
access(6, bits);
read_ea2(bits);
load_seg(cpu_data, (opcode & 0x01) ? &cpu_state.seg_ds : &cpu_state.seg_es);
break;
case 0xC6:
case 0xC7:
/* MOV rm, imm */
bits = 8 << (opcode & 1);
do_mod_rm();
wait(1, 0);
cpu_data = pfq_fetch();
wait(2, 0);
set_ea(cpu_data);
break;
case 0xCC: /*INT 3*/
wait(7, 0);
interrupt(3);
break;
case 0xCD: /*INT*/
wait(1, 0);
temp = pfq_fetchb();
wait(1, 0);
if (biu_cycles != 3)
wait(1, 0);
wait(1, 0);
interrupt(temp);
break;
case 0xCE: /*INTO*/
wait(3, 0);
if (cpu_state.flags & V_FLAG) {
wait(5, 0);
interrupt(4);
}
break;
case 0xCF: /*IRET*/
wait(3, 0);
pfq_clear();
access(6, 8);
new_ip = pop();
access(6, 8);
new_cs = pop();
load_cs(new_cs);
set_ip(new_ip);
access(6, 8);
if (is_nec)
cpu_state.flags = pop() | 0x8002;
else
cpu_state.flags = pop() | 0x0002;
wait(5, 0);
noint = 1;
nmi_enable = 1;
break;
case 0xD0:
case 0xD1:
case 0xD2:
case 0xD3:
/* rot rm */
bits = 8 << (opcode & 1);
do_mod_rm();
cpu_data = get_ea();
if (cpu_mod == 3)
wait(1, 0);
if ((opcode & 2) == 0) {
cpu_src = 1;
wait((cpu_mod != 3) ? 4 : 0, 0);
} else {
cpu_src = CL;
wait((cpu_mod != 3) ? 9 : 6, 0);
}
if (is186 && !is_nec)
cpu_src &= 0x1F;
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;
}
if ((opcode & 2) != 0)
wait(4, 0);
--cpu_src;
}
access(16, bits);
set_ea(cpu_data);
break;
case 0xD4: /*AAM*/
wait(1, 0);
if (is_nec) {
(void) pfq_fetchb();
cpu_src = 10;
} else
cpu_src = pfq_fetchb();
if (x86_div(AL, 0))
set_pzs(16);
break;
case 0xD5: /*AAD*/
wait(1, 0);
if (is_nec) {
(void) pfq_fetchb();
mul(10, AH);
} else
mul(pfq_fetchb(), AH);
cpu_dest = AL;
cpu_src = cpu_data;
add(8);
AL = cpu_data;
AH = 0x00;
break;
case 0xD6: /*SALC*/
wait(1, 0);
AL = (cpu_state.flags & C_FLAG) ? 0xff : 0x00;
wait(1, 0);
break;
case 0xD7: /*XLATB*/
cpu_state.eaaddr = (BX + AL) & 0xffff;
wait(4, 0);
access(3, 8);
AL = readmemb((ovr_seg ? *ovr_seg : ds) + cpu_state.eaaddr);
break;
case 0xD8:
case 0xD9:
case 0xDA:
case 0xDB:
case 0xDD:
case 0xDC:
case 0xDE:
case 0xDF:
/* esc i, r, rm */
do_mod_rm();
tempw = cpu_state.pc;
geteaw();
wait(1, 0);
if (cpu_mod != 3)
wait(1, 0);
if (hasfpu) {
if (fpu_softfloat) {
switch (opcode) {
case 0xD8:
ops_sf_fpu_8087_d8[(rmdat >> 3) & 0x1f]((uint32_t) rmdat);
break;
case 0xD9:
ops_sf_fpu_8087_d9[rmdat & 0xff]((uint32_t) rmdat);
break;
case 0xDA:
ops_sf_fpu_8087_da[rmdat & 0xff]((uint32_t) rmdat);
break;
case 0xDB:
ops_sf_fpu_8087_db[rmdat & 0xff]((uint32_t) rmdat);
break;
case 0xDC:
ops_sf_fpu_8087_dc[(rmdat >> 3) & 0x1f]((uint32_t) rmdat);
break;
case 0xDD:
ops_sf_fpu_8087_dd[rmdat & 0xff]((uint32_t) rmdat);
break;
case 0xDE:
ops_sf_fpu_8087_de[rmdat & 0xff]((uint32_t) rmdat);
break;
case 0xDF:
ops_sf_fpu_8087_df[rmdat & 0xff]((uint32_t) rmdat);
break;
}
} else {
switch (opcode) {
case 0xD8:
ops_fpu_8087_d8[(rmdat >> 3) & 0x1f]((uint32_t) rmdat);
break;
case 0xD9:
ops_fpu_8087_d9[rmdat & 0xff]((uint32_t) rmdat);
break;
case 0xDA:
ops_fpu_8087_da[rmdat & 0xff]((uint32_t) rmdat);
break;
case 0xDB:
ops_fpu_8087_db[rmdat & 0xff]((uint32_t) rmdat);
break;
case 0xDC:
ops_fpu_8087_dc[(rmdat >> 3) & 0x1f]((uint32_t) rmdat);
break;
case 0xDD:
ops_fpu_8087_dd[rmdat & 0xff]((uint32_t) rmdat);
break;
case 0xDE:
ops_fpu_8087_de[rmdat & 0xff]((uint32_t) rmdat);
break;
case 0xDF:
ops_fpu_8087_df[rmdat & 0xff]((uint32_t) rmdat);
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:
case 0xE1:
case 0xE2:
case 0xE3:
/* LOOP */
wait(3, 0);
cpu_data = pfq_fetchb();
if (opcode != 0xe2)
wait(1, 0);
if (opcode != 0xe3) {
--CX;
oldc = (CX != 0);
switch (opcode) {
case 0xE0:
if (cpu_state.flags & Z_FLAG)
oldc = 0;
break;
case 0xE1:
if (!(cpu_state.flags & Z_FLAG))
oldc = 0;
break;
}
} else
oldc = (CX == 0);
if (oldc)
jump_short();
break;
case 0xE4:
case 0xE5:
case 0xE6:
case 0xE7:
case 0xEC:
case 0xED:
case 0xEE:
case 0xEF:
bits = 8 << (opcode & 1);
if ((opcode & 0x0e) != 0x0c)
wait(1, 0);
if ((opcode & 8) == 0)
cpu_data = pfq_fetchb();
else
cpu_data = DX;
cpu_state.eaaddr = cpu_data;
if ((opcode & 2) == 0) {
access(3, bits);
if (opcode & 1)
cpu_io(16, 0, cpu_data);
else
cpu_io(8, 0, cpu_data);
wait(1, 0);
} else {
if ((opcode & 8) == 0)
access(8, bits);
else
access(9, bits);
if (opcode & 1)
cpu_io(16, 1, cpu_data);
else
cpu_io(8, 1, cpu_data);
}
break;
case 0xE8: /*CALL rel 16*/
wait(1, 0);
cpu_state.oldpc = jump_near();
wait(2, 0);
access(25, 16);
push((uint16_t *) &(cpu_state.oldpc));
break;
case 0xE9: /*JMP rel 16*/
wait(1, 0);
jump_near();
break;
case 0xEA: /*JMP far*/
wait(1, 0);
addr = pfq_fetchw();
wait(1, 0);
tempw = pfq_fetchw();
load_cs(tempw);
pfq_clear();
wait(4, 0);
set_ip(addr);
break;
case 0xEB: /*JMP rel*/
wait(1, 0);
cpu_data = (int8_t) pfq_fetchb();
jump_short();
wait(1, 0);
break;
case 0xF0:
case 0xF1: /*LOCK - F1 is alias*/
in_lock = 1;
wait(1, 0);
completed = 0;
break;
case 0xF2: /*REPNE*/
case 0xF3: /*REPE*/
wait(1, 0);
in_rep = (opcode == 0xf2 ? 1 : 2);
completed = 0;
rep_c_flag = 0;
break;
case 0xF4: /*HLT*/
if (!repeating) {
if ((biu_cycles == 3) || !last_was_code)
cpu_data = 1;
else
cpu_data = 2;
wait(2, 0);
pfq_clear();
}
wait(1, 0);
if (irq_pending()) {
wait(1, 0);
if (cpu_data == 2)
wait(1, 0);
check_interrupts();
} else {
repeating = 1;
completed = 0;
}
break;
case 0xF5: /*CMC*/
wait(1, 0);
cpu_state.flags ^= C_FLAG;
break;
case 0xF6:
case 0xF7:
bits = 8 << (opcode & 1);
do_mod_rm();
cpu_data = get_ea();
switch (rmdat & 0x38) {
case 0x00:
case 0x08:
/* TEST */
wait(2, 0);
cpu_src = pfq_fetch();
wait(1, 0);
test(bits, cpu_data, cpu_src);
if (cpu_mod != 3)
wait(1, 0);
break;
case 0x10: /* NOT */
case 0x18: /* NEG */
wait(2, 0);
if ((rmdat & 0x38) == 0x10)
cpu_data = ~cpu_data;
else {
cpu_src = cpu_data;
cpu_dest = 0;
sub(bits);
}
if (cpu_mod != 3)
wait(2, 0);
access(16, bits);
set_ea(cpu_data);
break;
case 0x20: /* MUL */
case 0x28: /* IMUL */
old_flags = cpu_state.flags;
wait(1, 0);
mul(get_accum(bits), cpu_data);
if (opcode & 1) {
AX = cpu_data;
DX = cpu_dest;
set_co_mul(bits, DX != ((AX & 0x8000) == 0 || (rmdat & 0x38) == 0x20 ? 0 : 0xffff));
cpu_data = DX;
} else {
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 */
cpu_src = cpu_data;
if (x86_div(AL, AH))
wait(1, 0);
break;
}
break;
case 0xF8:
case 0xF9:
/* CLCSTC */
wait(1, 0);
set_cf(opcode & 1);
break;
case 0xFA:
case 0xFB:
/* CLISTI */
wait(1, 0);
set_if(opcode & 1);
break;
case 0xFC:
case 0xFD:
/* CLDSTD */
wait(1, 0);
set_df(opcode & 1);
break;
case 0xFE:
case 0xFF:
/* misc */
bits = 8 << (opcode & 1);
do_mod_rm();
access(56, bits);
read_ea(((rmdat & 0x38) == 0x18) || ((rmdat & 0x38) == 0x28), bits);
switch (rmdat & 0x38) {
case 0x00: /* INC rm */
case 0x08: /* DEC rm */
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);
wait(2, 0);
access(16, bits);
set_ea(cpu_data);
break;
case 0x10: /* CALL rm */
cpu_data_opff_rm();
wait(2, 0);
pfq_clear();
wait(5, 0);
cpu_state.oldpc = cpu_state.pc;
set_ip(cpu_data);
wait(2, 0);
access(25, bits);
push((uint16_t *) &(cpu_state.oldpc));
break;
case 0x18: /* CALL rmd */
new_ip = cpu_data;
wait(3, 0);
access(6, bits);
read_ea2(bits);
if (!(opcode & 1))
cpu_data |= 0xff00;
new_cs = cpu_data;
wait(1, 0);
pfq_clear();
access(25, bits);
push(&(CS));
wait(4, 0);
cpu_state.oldpc = cpu_state.pc;
load_cs(new_cs);
set_ip(new_ip);
wait(1, 0);
access(25, bits);
push((uint16_t *) &(cpu_state.oldpc));
break;
case 0x20: /* JMP rm */
cpu_data_opff_rm();
wait(2, 0);
pfq_clear();
if (biu_cycles != 3)
wait(1, 0);
set_ip(cpu_data);
break;
case 0x28: /* JMP rmd */
new_ip = cpu_data;
wait(3, 0);
pfq_clear();
wait(1, 0);
access(25, bits);
read_ea2(bits);
if (!(opcode & 1))
cpu_data |= 0xff00;
new_cs = cpu_data;
load_cs(new_cs);
set_ip(new_ip);
break;
case 0x30: /* PUSH rm */
case 0x38:
if (cpu_mod != 3)
wait(1, 0);
wait(4, 0);
access(38, bits);
push((uint16_t *) &(cpu_data));
break;
}
break;
default:
x808x_log("Illegal opcode: %02X\n", opcode);
pfq_fetchb();
wait(8, 0);
break;
}
}
if (completed) {
repeating = 0;
ovr_seg = NULL;
in_rep = 0;
rep_c_flag = 0;
if (in_lock)
clear_lock = 1;
clock_end();
check_interrupts();
if (noint)
noint = 0;
cpu_alu_op = 0;
}
#ifdef USE_GDBSTUB
if (gdbstub_instruction())
return;
#endif
}
}