mn1880: Use separate interrupt registers for each CPU

This commit is contained in:
AJR 2021-09-11 19:33:50 -04:00
parent c608e9b7e2
commit 413e48c9b0
2 changed files with 107 additions and 85 deletions

View File

@ -76,15 +76,14 @@ mn1880_device::mn1880_device(const machine_config &mconfig, device_type type, co
: cpu_device(mconfig, type, tag, owner, clock)
, m_program_config("program", ENDIANNESS_BIG, 8, 16, 0)
, m_data_config("data", ENDIANNESS_LITTLE, 8, 16, 0, data_map)
, m_cpu_select(false)
, m_cpum(0)
, m_ustate(microstate::UNKNOWN)
, m_da(0)
, m_tmp1(0)
, m_tmp2(0)
, m_output_queued(false)
, m_output_queue_state(0xff)
, m_icount(0)
, m_ie{0, 0}
, m_if(0)
{
}
@ -95,22 +94,26 @@ mn1880_device::mn1880_device(const machine_config &mconfig, const char *tag, dev
u8 mn1880_device::ie0_r()
{
return m_ie[0];
return (get_active_cpu().ie & 0x00f) | (m_cpu[0].iemask ? 0x10 : 0) | (m_cpu[1].iemask ? 0x20 : 0);
}
void mn1880_device::ie0_w(u8 data)
{
m_ie[0] = data;
cpu_registers &cpu = output_queued() ? m_cpu[m_output_queue_state] : get_active_cpu();
cpu.ie = (cpu.ie & 0xff0) | (data & 0x0f);
m_cpu[0].iemask = BIT(data, 4);
m_cpu[1].iemask = BIT(data, 5);
}
u8 mn1880_device::ie1_r()
{
return m_ie[1];
return (get_active_cpu().ie & 0xff0) >> 4;
}
void mn1880_device::ie1_w(u8 data)
{
m_ie[1] = data;
cpu_registers &cpu = output_queued() ? m_cpu[m_output_queue_state] : get_active_cpu();
cpu.ie = u16(data) << 4 | (cpu.ie & 0x00f);
}
u8 mn1880_device::cpum_r()
@ -120,7 +123,7 @@ u8 mn1880_device::cpum_r()
void mn1880_device::cpum_w(u8 data)
{
m_cpum = data;
m_cpum = (data & 0xef) | (m_cpum & 0x10);
}
void mn1880_device::internal_data_map(address_map &map)
@ -151,60 +154,68 @@ void mn1880_device::device_start()
using namespace std::placeholders;
state_add<u16>(MN1880_IP, "IP",
[this] () { return m_cpu[int(m_cpu_select)].ip; },
[this] (u16 data) { m_cpu[int(m_cpu_select)].ip = data; }
[this] () { return get_active_cpu().ip; },
[this] (u16 data) { get_active_cpu().ip = data; }
).noshow();
state_add<u16>(STATE_GENPC, "GENPC",
[this] () { return m_cpu[int(m_cpu_select)].ip; },
[this] (u16 data) { m_cpu[int(m_cpu_select)].ip = data; }
[this] () { return get_active_cpu().ip; },
[this] (u16 data) { get_active_cpu().ip = data; }
).noshow();
state_add<u16>(STATE_GENPCBASE, "CURPC",
[this] () { return m_cpu[int(m_cpu_select)].irp; },
[this] (u16 data) { m_cpu[int(m_cpu_select)].irp = data; }
[this] () { return get_active_cpu().irp; },
[this] (u16 data) { get_active_cpu().irp = data; }
).noshow();
state_add<u8>(MN1880_IR, "IR",
[this] () { return m_cpu[int(m_cpu_select)].ir; },
[this] (u8 data) { m_cpu[int(m_cpu_select)].ir = data; }
[this] () { return get_active_cpu().ir; },
[this] (u8 data) { get_active_cpu().ir = data; }
).noshow();
state_add<u8>(MN1880_FS, "FS",
[this] () { return m_cpu[int(m_cpu_select)].fs; },
[this] (u8 data) { m_cpu[int(m_cpu_select)].fs = data; }
[this] () { return get_active_cpu().fs; },
[this] (u8 data) { get_active_cpu().fs = data; }
).noshow();
state_add<u8>(STATE_GENFLAGS, "FLAGS",
[this] () { return m_cpu[int(m_cpu_select)].fs; },
[this] (u8 data) { m_cpu[int(m_cpu_select)].fs = data; }
[this] () { return get_active_cpu().fs; },
[this] (u8 data) { get_active_cpu().fs = data; }
).formatstr("%10s").noshow();
state_add<u16>(MN1880_XP, "XP",
[this] () { return m_cpu[int(m_cpu_select)].xp; },
[this] (u16 data) { m_cpu[int(m_cpu_select)].xp = data; }
[this] () { return get_active_cpu().xp; },
[this] (u16 data) { get_active_cpu().xp = data; }
).noshow();
state_add<u16>(MN1880_YP, "YP",
[this] () { return m_cpu[int(m_cpu_select)].yp; },
[this] (u16 data) { m_cpu[int(m_cpu_select)].yp = data; }
[this] () { return get_active_cpu().yp; },
[this] (u16 data) { get_active_cpu().yp = data; }
).noshow();
state_add<u8>(MN1880_XPL, "XPl",
[this] () { return m_cpu[int(m_cpu_select)].xp & 0x00ff; },
[this] (u8 data) { setl(m_cpu[int(m_cpu_select)].xp, data); }
[this] () { return get_active_cpu().xp & 0x00ff; },
[this] (u8 data) { setl(get_active_cpu().xp, data); }
).noshow();
state_add<u8>(MN1880_XPH, "XPh",
[this] () { return (m_cpu[int(m_cpu_select)].xp & 0xff00 >> 8); },
[this] (u8 data) { seth(m_cpu[int(m_cpu_select)].xp, data); }
[this] () { return (get_active_cpu().xp & 0xff00 >> 8); },
[this] (u8 data) { seth(get_active_cpu().xp, data); }
).noshow();
state_add<u8>(MN1880_YPL, "YPl",
[this] () { return m_cpu[int(m_cpu_select)].yp & 0x00ff; },
[this] (u8 data) { setl(m_cpu[int(m_cpu_select)].yp, data); }
[this] () { return get_active_cpu().yp & 0x00ff; },
[this] (u8 data) { setl(get_active_cpu().yp, data); }
).noshow();
state_add<u8>(MN1880_YPH, "YPh",
[this] () { return (m_cpu[int(m_cpu_select)].yp & 0xff00 >> 8); },
[this] (u8 data) { seth(m_cpu[int(m_cpu_select)].yp, data); }
[this] () { return (get_active_cpu().yp & 0xff00 >> 8); },
[this] (u8 data) { seth(get_active_cpu().yp, data); }
).noshow();
state_add<u16>(MN1880_SP, "SP",
[this] () { return m_cpu[int(m_cpu_select)].sp; },
[this] (u16 data) { m_cpu[int(m_cpu_select)].sp = data; }
[this] () { return get_active_cpu().sp; },
[this] (u16 data) { get_active_cpu().sp = data; }
).noshow();
state_add<u16>(MN1880_LP, "LP",
[this] () { return m_cpu[int(m_cpu_select)].lp; },
[this] (u16 data) { m_cpu[int(m_cpu_select)].lp = data; }
[this] () { return get_active_cpu().lp; },
[this] (u16 data) { get_active_cpu().lp = data; }
).noshow();
state_add<u16>(MN1880_IE, "IE",
[this] () { return get_active_cpu().ie; },
[this] (u16 data) { get_active_cpu().ie = data; }
).mask(0xfff).noshow();
state_add<bool>(MN1880_IE, "IEMASK",
[this] () { return get_active_cpu().iemask; },
[this] (bool data) { get_active_cpu().iemask = data; }
).noshow();
for (int i = 0; i < 2; i++)
@ -216,11 +227,12 @@ void mn1880_device::device_start()
state_add(MN1880_YPA + i, util::string_format("YP%c", 'a' + i).c_str(), m_cpu[i].yp);
state_add(MN1880_SPA + i, util::string_format("SP%c", 'a' + i).c_str(), m_cpu[i].sp);
state_add(MN1880_LPA + i, util::string_format("LP%c", 'a' + i).c_str(), m_cpu[i].lp);
state_add(MN1880_IEA + i, util::string_format("IE%c", 'a' + i).c_str(), m_cpu[i].ie).mask(0xfff);
state_add(MN1880_IEMASKA + i, util::string_format("IEMASK%c", 'a' + i).c_str(), m_cpu[i].iemask);
state_add_divider(MN1880_DIVIDER1 + i);
}
state_add(MN1880_IE0, "IE0", m_ie[0]);
state_add(MN1880_IE1, "IE1", m_ie[1]);
state_add(MN1880_IF, "IF", m_if).mask(0xfff);
state_add(MN1880_CPUM, "CPUM", m_cpum);
save_item(STRUCT_MEMBER(m_cpu, ip));
@ -231,14 +243,15 @@ void mn1880_device::device_start()
save_item(STRUCT_MEMBER(m_cpu, yp));
save_item(STRUCT_MEMBER(m_cpu, sp));
save_item(STRUCT_MEMBER(m_cpu, lp));
save_item(NAME(m_cpu_select));
save_item(STRUCT_MEMBER(m_cpu, ie));
save_item(STRUCT_MEMBER(m_cpu, iemask));
save_item(NAME(m_if));
save_item(NAME(m_cpum));
save_item(NAME(m_ustate));
save_item(NAME(m_da));
save_item(NAME(m_tmp1));
save_item(NAME(m_tmp2));
save_item(NAME(m_output_queued));
save_item(NAME(m_ie));
save_item(NAME(m_output_queue_state));
}
void mn1880_device::device_reset()
@ -249,6 +262,8 @@ void mn1880_device::device_reset()
cpu.fs |= 0x10; // HACK: skip fake first instruction in debugger
cpu.ir = 0xf6;
cpu.wait = 0;
cpu.ie = 0;
cpu.iemask = true;
}
m_cpu[0].ip = 0x0000;
m_cpu[1].ip = 0x0020;
@ -259,11 +274,10 @@ void mn1880_device::device_reset()
m_cpu[1].sp = 0x0200;
m_cpu[1].lp = 0x0160;
m_ie[0] = 0x30;
m_ie[1] = 0x00;
m_if = 0;
m_cpum = 0x0c;
m_ustate = microstate::NEXT;
m_output_queued = false;
m_output_queue_state = 0xff;
}
const mn1880_device::microstate mn1880_device::s_decode_map[256] =
@ -440,13 +454,13 @@ void mn1880_device::cpu_registers::branch(u16 label)
void mn1880_device::swap_cpus()
{
if ((m_cpum & 0x03) != (m_cpu_select ? 0x01 : 0x02))
m_cpu_select = !m_cpu_select;
if ((m_cpum & 0x03) != (BIT(m_cpum, 4) ? 0x01 : 0x02))
m_cpum ^= 0x10;
}
void mn1880_device::next_instruction(u8 input)
{
cpu_registers &cpu = m_cpu[int(m_cpu_select)];
cpu_registers &cpu = get_active_cpu();
if ((cpu.fs & 0x0f) != 0)
cpu.fs = (cpu.fs - 1) | 0x10;
else
@ -464,13 +478,13 @@ void mn1880_device::execute_run()
{
while (m_icount-- > 0)
{
cpu_registers &cpu = m_cpu[int(m_cpu_select)];
cpu_registers &cpu = get_active_cpu();
if (m_ustate == microstate::NEXT && cpu.wait == 0)
{
if (!BIT(cpu.fs, 4))
debugger_instruction_hook(cpu.irp);
if (!m_output_queued && !BIT(s_input_queue_map[cpu.ir >> 4], cpu.ir & 0x0f))
if (!output_queued() && !BIT(s_input_queue_map[cpu.ir >> 4], cpu.ir & 0x0f))
m_ustate = s_decode_map[cpu.ir];
}
@ -485,10 +499,10 @@ void mn1880_device::execute_run()
}
else
{
if (m_output_queued)
if (output_queued())
{
m_data.write_byte(m_da, m_tmp1 & 0x00ff);
m_output_queued = false;
m_output_queue_state = 0xff;
}
if (BIT(s_input_queue_map[cpu.ir >> 4], cpu.ir & 0x0f))
{
@ -569,7 +583,7 @@ void mn1880_device::execute_run()
m_da = cpu.xp;
if ((cpu.fs & 0x1f) != 0)
++cpu.xp;
m_output_queued = true;
set_output_queued();
next_instruction(input);
break;
@ -597,7 +611,7 @@ void mn1880_device::execute_run()
case microstate::MOVL35_4:
m_tmp1 = m_data.read_byte(cpu.yp);
m_da = m_tmp2 + 1;
m_output_queued = true;
set_output_queued();
next_instruction(input);
break;
@ -612,7 +626,7 @@ void mn1880_device::execute_run()
m_da = cpu.xp;
if ((cpu.fs & 0x1f) != 0)
++cpu.xp;
m_output_queued = true;
set_output_queued();
next_instruction(input);
break;
@ -660,7 +674,7 @@ void mn1880_device::execute_run()
m_data.write_byte(m_da, m_tmp1 & 0x00ff);
++m_da;
m_tmp1 >>= 8;
m_output_queued = true;
set_output_queued();
next_instruction(input);
break;
@ -679,7 +693,7 @@ void mn1880_device::execute_run()
m_tmp1 = m_data.read_byte(m_da); // TODO: read latch instead of terminal
cpu.fs = (m_tmp1 & 0x80) | (cpu.fs & 0x7f);
m_tmp1 <<= 1;
m_output_queued = true;
set_output_queued();
next_instruction(input);
break;
@ -696,7 +710,7 @@ void mn1880_device::execute_run()
case microstate::ASR_2:
m_tmp1 = cpu.asrc(m_data.read_byte(m_da)); // TODO: read latch instead of terminal
m_output_queued = true;
set_output_queued();
next_instruction(input);
break;
@ -812,7 +826,7 @@ void mn1880_device::execute_run()
case microstate::XCH4_2:
m_tmp1 = m_data.read_byte(m_da); // TODO: read latch instead of terminal
m_tmp1 = (m_tmp1 << 4) | (m_tmp1 >> 4);
m_output_queued = true;
set_output_queued();
next_instruction(input);
break;
@ -867,7 +881,7 @@ void mn1880_device::execute_run()
case microstate::ROL_2:
m_tmp1 = cpu.rolc(m_data.read_byte(m_da)); // TODO: read latch instead of terminal
m_output_queued = true;
set_output_queued();
next_instruction(input);
break;
@ -888,7 +902,7 @@ void mn1880_device::execute_run()
case microstate::ROR_2:
m_tmp1 = cpu.rorc(m_data.read_byte(m_da)); // TODO: read latch instead of terminal
m_output_queued = true;
set_output_queued();
next_instruction(input);
break;
@ -948,7 +962,7 @@ void mn1880_device::execute_run()
m_data.write_byte(m_da, m_tmp1 & 0x00ff);
++m_da;
cpu.xp = m_da;
m_output_queued = true;
set_output_queued();
next_instruction(input);
break;
@ -1050,7 +1064,7 @@ void mn1880_device::execute_run()
m_data.write_byte(m_da, m_tmp1 & 0x00ff);
m_tmp1 >>= 8;
m_da = m_tmp2;
m_output_queued = true;
set_output_queued();
next_instruction(input);
break;
@ -1095,7 +1109,7 @@ void mn1880_device::execute_run()
if ((cpu.fs & 0x1f) != 0)
++cpu.xp;
m_tmp1 >>= 8;
m_output_queued = true;
set_output_queued();
next_instruction(input);
break;
@ -1160,7 +1174,7 @@ void mn1880_device::execute_run()
case microstate::MOVL5E_4:
m_tmp1 = m_data.read_byte(m_da);
m_da = m_tmp2 + 1;
m_output_queued = true;
set_output_queued();
next_instruction(input);
break;
@ -1272,7 +1286,7 @@ void mn1880_device::execute_run()
cpu.fs |= 0x40;
else
cpu.fs &= 0xbf;
m_output_queued = true;
set_output_queued();
next_instruction(input);
break;
@ -1321,7 +1335,7 @@ void mn1880_device::execute_run()
cpu.fs |= 0x40;
else
cpu.fs &= 0xbf;
m_output_queued = true;
set_output_queued();
next_instruction(input);
break;
@ -1370,7 +1384,7 @@ void mn1880_device::execute_run()
cpu.fs |= 0x40;
else
cpu.fs &= 0xbf;
m_output_queued = true;
set_output_queued();
next_instruction(input);
break;
@ -1415,7 +1429,7 @@ void mn1880_device::execute_run()
case microstate::SUBC_3:
m_tmp1 = cpu.subcz(m_data.read_byte(m_da), m_tmp1, BIT(cpu.fs, 7), BIT(cpu.fs, 4)); // TODO: read latch instead of terminal
m_output_queued = true;
set_output_queued();
next_instruction(input);
break;
@ -1460,7 +1474,7 @@ void mn1880_device::execute_run()
case microstate::SUBD_3:
m_tmp1 = cpu.subdcz(m_data.read_byte(m_da), m_tmp1, BIT(cpu.fs, 7)); // TODO: read latch instead of terminal
m_output_queued = true;
set_output_queued();
next_instruction(input);
break;
@ -1474,7 +1488,7 @@ void mn1880_device::execute_run()
else
cpu.fs &= 0xbf;
}
m_output_queued = true;
set_output_queued();
next_instruction(input);
break;
@ -1519,7 +1533,7 @@ void mn1880_device::execute_run()
case microstate::ADDC_3:
m_tmp1 = cpu.addcz(m_data.read_byte(m_da), m_tmp1, BIT(cpu.fs, 7), BIT(cpu.fs, 4)); // TODO: read latch instead of terminal
m_output_queued = true;
set_output_queued();
next_instruction(input);
break;
@ -1577,7 +1591,7 @@ void mn1880_device::execute_run()
else
cpu.fs &= 0xbf;
}
m_output_queued = true;
set_output_queued();
next_instruction(input);
break;
@ -1703,7 +1717,7 @@ void mn1880_device::execute_run()
m_tmp1 = cpu.subcz(m_data.read_byte(m_da), m_tmp1, BIT(cpu.fs, 7), true); // TODO: read latch instead of terminal
if (!BIT(cpu.ir, 1))
cpu.xp = m_da + 1;
m_output_queued = true;
set_output_queued();
next_instruction(input);
break;
@ -1757,7 +1771,7 @@ void mn1880_device::execute_run()
m_tmp1 = cpu.addcz(m_data.read_byte(m_da), m_tmp1, BIT(cpu.fs, 7), true); // TODO: read latch instead of terminal
if (!BIT(cpu.ir, 1))
cpu.xp = m_da + 1;
m_output_queued = true;
set_output_queued();
next_instruction(input);
break;
@ -1822,7 +1836,7 @@ void mn1880_device::execute_run()
m_tmp1 = m_data.read_byte(m_da); // TODO: read latch instead of terminal
cpu.sp = m_da + 1;
m_da = m_tmp2;
m_output_queued = true;
set_output_queued();
next_instruction(input);
break;
@ -2175,13 +2189,13 @@ void mn1880_device::execute_run()
case microstate::MOV1_4:
m_tmp1 |= m_data.read_byte(m_da); // TODO: read latch instead of terminal
m_output_queued = true;
set_output_queued();
next_instruction(input);
break;
case microstate::MOV1N_4:
m_tmp1 = m_data.read_byte(m_da) & ~m_tmp1; // TODO: read latch instead of terminal
m_output_queued = true;
set_output_queued();
next_instruction(input);
break;
@ -2334,7 +2348,7 @@ void mn1880_device::state_string_export(const device_state_entry &entry, std::st
{
case STATE_GENFLAGS:
{
u8 fs = m_cpu[int(m_cpu_select)].fs;
u8 fs = get_active_cpu().fs;
str = string_format("%c%c%c%c RC=%-2d",
BIT(fs, 7) ? 'C' : '.', // Carry flag
BIT(fs, 6) ? 'Z' : '.', // Zero flag
@ -2345,11 +2359,11 @@ void mn1880_device::state_string_export(const device_state_entry &entry, std::st
}
case MN1880_IPA:
str = string_format("%04X%c", m_cpu[0].ip, m_cpu_select ? ' ' : '*');
str = string_format("%04X%c", m_cpu[0].ip, BIT(m_cpum, 4) ? ' ' : '*');
break;
case MN1880_IPB:
str = string_format("%04X%c", m_cpu[1].ip, m_cpu_select ? '*' : ' ');
str = string_format("%04X%c", m_cpu[1].ip, BIT(m_cpum, 4) ? '*' : ' ');
break;
}
}

View File

@ -21,8 +21,10 @@ public:
MN1880_YPL, MN1880_YPH,
MN1880_SP, MN1880_SPA, MN1880_SPB,
MN1880_LP, MN1880_LPA, MN1880_LPB,
MN1880_IE, MN1880_IEA, MN1880_IEB,
MN1880_IEMASK, MN1880_IEMASKA, MN1880_IEMASKB,
MN1880_DIVIDER1, MN1880_DIVIDER2,
MN1880_IE0, MN1880_IE1,
MN1880_IF,
MN1880_CPUM
};
@ -50,7 +52,7 @@ protected:
private:
struct cpu_registers
{
cpu_registers() : ip(0), irp(0), ir(0), fs(0), xp(0), yp(0), sp(0), lp(0), wait(0) { }
cpu_registers() : ip(0), irp(0), ir(0), fs(0), xp(0), yp(0), sp(0), lp(0), wait(0), ie(0), iemask(false) { }
u8 addcz(u8 data1, u8 data2, bool carry, bool holdz);
u8 adddcz(u8 data1, u8 data2, bool carry);
@ -70,6 +72,8 @@ private:
u16 sp;
u16 lp;
u16 wait;
u16 ie;
bool iemask;
};
enum class microstate : u8 {
@ -172,6 +176,11 @@ private:
static void setl(u16 &pr, u8 data) { pr = (pr & 0xff00) | data; }
static void seth(u16 &pr, u8 data) { pr = (pr & 0x00ff) | (data << 8); }
cpu_registers &get_active_cpu() { return m_cpu[BIT(m_cpum, 4)]; }
const cpu_registers &get_active_cpu() const { return m_cpu[BIT(m_cpum, 4)]; }
bool output_queued() const { return m_output_queue_state != 0xff; }
void set_output_queued() { m_output_queue_state = BIT(m_cpum, 4); }
u8 ie0_r();
void ie0_w(u8 data);
u8 ie1_r();
@ -196,17 +205,16 @@ private:
// execution state
cpu_registers m_cpu[2];
bool m_cpu_select;
u8 m_cpum;
microstate m_ustate;
u16 m_da;
u16 m_tmp1;
u16 m_tmp2;
bool m_output_queued;
u8 m_output_queue_state;
s32 m_icount;
// interrupt state
u8 m_ie[2];
u16 m_if;
};
DECLARE_DEVICE_TYPE(MN1880, mn1880_device)