Implement NEC V20/V30's i8080 emulation mode

This commit is contained in:
Cacodemon345
2024-03-30 16:21:41 +06:00
parent 4e4ad8913c
commit 99e8d13afa
6 changed files with 1025 additions and 365 deletions

View File

@@ -18,10 +18,13 @@
#include <math.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <wchar.h>
#include "i8080.h"
#define HAVE_STDARG_H
#include <86box/86box.h>
#include "cpu.h"
@@ -70,6 +73,9 @@ static int in_rep = 0, repeating = 0, rep_c_flag = 0;
static int oldc, clear_lock = 0;
static int refresh = 0, cycdiff;
static i8080 emulated_processor;
static bool cpu_md_write_disable = 1;
/* Various things needed for 8087. */
#define OP_TABLE(name) ops_##name
@@ -195,6 +201,54 @@ prefetch_queue_get_size(void)
{
return pfq_size;
}
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)
{
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)
@@ -582,6 +636,33 @@ load_seg(uint16_t seg, x86seg *s)
s->seg = seg & 0xffff;
}
uint8_t fetch_i8080_opcode(UNUSED(void* priv), uint16_t addr)
{
return readmemb(cs + addr);
}
uint8_t fetch_i8080_data(UNUSED(void* priv), uint16_t addr)
{
return readmemb(ds + addr);
}
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_io(8, 0, port);
return AL;
}
static void i8080_port_out(UNUSED(void* priv), uint8_t port, uint8_t val)
{
AL = val;
cpu_io(8, 1, port);
}
void
reset_808x(int hard)
{
@@ -619,6 +700,14 @@ reset_808x(int hard)
use_custom_nmi_vector = 0x00;
custom_nmi_vector = 0x00000000;
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 void
@@ -994,6 +1083,11 @@ interrupt(uint16_t addr)
uint16_t new_cs, new_ip;
uint16_t tempf;
if (!(cpu_state.flags & MD_FLAG) && is_nec) {
sync_from_i8080();
pclog("CALLN/INT#/NMI#\n");
}
addr <<= 2;
cpu_state.eaaddr = addr;
old_cs = CS;
@@ -1010,6 +1104,8 @@ interrupt(uint16_t addr)
tempf = cpu_state.flags & (is_nec ? 0x8fd7 : 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;
@@ -1020,6 +1116,56 @@ 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(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();
pclog("BRKEM mode\n");
}
void
retem_i8080(void)
{
sync_from_i8080();
cpu_state.pc = pop();
CS = pop();
cpu_state.flags = pop() | MD_FLAG;
cpu_md_write_disable = 1;
pclog("RETEM mode\n");
}
void
interrupt_808x(uint16_t addr)
{
@@ -1771,6 +1917,15 @@ execx86(int 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;
}
if (!repeating) {
cpu_state.oldpc = cpu_state.pc;
opcode = pfq_fetchb();
@@ -2344,8 +2499,8 @@ execx86(int cycs)
break;
case 0xFF: /* BRKEM */
/* Unimplemented for now. */
fatal("808x: Unsupported 8080 emulation mode attempted to enter into!");
interrupt_brkem(pfq_fetchb());
handled = 1;
break;
default:
@@ -2857,11 +3012,12 @@ execx86(int cycs)
break;
case 0x9D: /*POPF*/
access(25, 16);
if (is_nec)
if (is_nec && cpu_md_write_disable)
cpu_state.flags = pop() | 0x8002;
else
cpu_state.flags = pop() | 0x0002;
wait(1, 0);
sync_to_i8080();
break;
case 0x9E: /*SAHF*/
wait(1, 0);
@@ -3127,13 +3283,15 @@ execx86(int cycs)
access(62, 8);
set_ip(new_ip);
access(45, 8);
if (is_nec)
if (is_nec && cpu_md_write_disable)
cpu_state.flags = pop() | 0x8002;
else
cpu_state.flags = pop() | 0x0002;
wait(5, 0);
noint = 1;
nmi_enable = 1;
if (is_nec && !(cpu_state.flags & MD_FLAG))
sync_to_i8080();
break;
case 0xD0:
@@ -3659,7 +3817,7 @@ execx86(int cycs)
break;
}
}
exec_completed:
if (completed) {
repeating = 0;
ovr_seg = NULL;