summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author AJR <ajrhacker@users.noreply.github.com>2017-09-03 21:05:52 -0400
committer AJR <ajrhacker@users.noreply.github.com>2017-09-03 21:06:56 -0400
commite3906f0de09af4de8e900e4dad67312e4a4a9e48 (patch)
treefb2ea015f0fa69604e3384b7fd05c4917657f70e
parent1ec4c337b0dbbc567013a4a1749b890f40bcaf49 (diff)
z8: Interrupts are working now
-rw-r--r--src/devices/cpu/z8/z8.cpp213
-rw-r--r--src/devices/cpu/z8/z8.h8
-rw-r--r--src/mame/drivers/amerihok.cpp9
3 files changed, 199 insertions, 31 deletions
diff --git a/src/devices/cpu/z8/z8.cpp b/src/devices/cpu/z8/z8.cpp
index cbabf70eab2..3f1b681d024 100644
--- a/src/devices/cpu/z8/z8.cpp
+++ b/src/devices/cpu/z8/z8.cpp
@@ -11,12 +11,12 @@
TODO:
- strobed I/O
- - interrupts
- expose register file to disassembler
- decimal adjust instruction
- timer Tin/Tout modes
- serial
- instruction pipeline
+ - internal diagnostic ROM in data space (requires high voltage reset)
*/
@@ -115,14 +115,16 @@ enum
#define Z8_P3M_P3_SERIAL 0x40 /* not supported */
#define Z8_P3M_PARITY 0x80 /* not supported */
-#define Z8_IMR_ENABLE 0x80 /* not supported */
+#define Z8_IMR_ENABLE 0x80
#define Z8_IMR_RAM_PROTECT 0x40 /* not supported */
-#define Z8_IMR_ENABLE_IRQ5 0x20 /* not supported */
-#define Z8_IMR_ENABLE_IRQ4 0x10 /* not supported */
-#define Z8_IMR_ENABLE_IRQ3 0x08 /* not supported */
-#define Z8_IMR_ENABLE_IRQ2 0x04 /* not supported */
-#define Z8_IMR_ENABLE_IRQ1 0x02 /* not supported */
-#define Z8_IMR_ENABLE_IRQ0 0x01 /* not supported */
+
+#define Z8_IRQ_MASK 0x3f
+#define Z8_IRQ_FLAG_IRQ5 0x20
+#define Z8_IRQ_FLAG_IRQ4 0x10
+#define Z8_IRQ_FLAG_IRQ3 0x08
+#define Z8_IRQ_FLAG_IRQ2 0x04
+#define Z8_IRQ_FLAG_IRQ1 0x02
+#define Z8_IRQ_FLAG_IRQ0 0x01
#define Z8_FLAGS_F1 0x01
#define Z8_FLAGS_F2 0x02
@@ -721,7 +723,7 @@ TIMER_CALLBACK_MEMBER( z8_device::t0_tick )
m_t0 = T0;
m_t0_timer->adjust(attotime::zero, 0, cycles_to_attotime(4 * ((PRE0 >> 2) + 1)));
m_t0_timer->enable(PRE0 & Z8_PRE0_COUNT_MODULO_N);
- m_irq[4] = ASSERT_LINE;
+ m_r[Z8_REGISTER_IRQ] |= Z8_IRQ_FLAG_IRQ4;
}
}
@@ -734,7 +736,7 @@ TIMER_CALLBACK_MEMBER( z8_device::t1_tick )
m_t1 = T1;
m_t1_timer->adjust(attotime::zero, 0, cycles_to_attotime(4 * ((PRE1 >> 2) + 1)));
m_t1_timer->enable(PRE1 & Z8_PRE0_COUNT_MODULO_N);
- m_irq[5] = ASSERT_LINE;
+ m_r[Z8_REGISTER_IRQ] |= Z8_IRQ_FLAG_IRQ5;
}
}
@@ -784,8 +786,8 @@ void z8_device::device_start()
m_t1_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(z8_device::t1_tick), this));
/* Clear state */
- for (auto & elem : m_irq)
- elem = 0;
+ for (auto & elem : m_irq_line)
+ elem = CLEAR_LINE;
for (auto & elem : m_r)
elem = 0;
for ( int i = 0; i < 4; i++ )
@@ -804,12 +806,156 @@ void z8_device::device_start()
save_item(NAME(m_r));
save_item(NAME(m_input));
save_item(NAME(m_output));
- save_item(NAME(m_irq));
+ save_item(NAME(m_irq_line));
+ save_item(NAME(m_irq_taken));
m_icountptr = &m_icount;
}
/***************************************************************************
+ INTERRUPTS
+***************************************************************************/
+
+void z8_device::take_interrupt(int irq)
+{
+ //logerror("Taking IRQ%d (previous PC = %04X)\n", irq, m_pc);
+ m_irq_taken = true;
+
+ // disable interrupts
+ m_r[Z8_REGISTER_IMR] &= ~Z8_IMR_ENABLE;
+
+ // acknowledge the IRQ
+ m_r[Z8_REGISTER_IRQ] &= ~(1 << irq);
+
+ // get the interrupt vector address
+ uint16_t vector = irq * 2;
+ if (m_rom_size == 0)
+ vector = mask_external_address(vector);
+
+ // push registers onto stack
+ stack_push_word(m_pc);
+ stack_push_byte(m_r[Z8_REGISTER_FLAGS]);
+
+ // branch to the vector
+ m_pc = m_direct->read_byte(vector) << 16;
+ m_pc |= m_direct->read_byte(vector + 1);
+}
+
+void z8_device::process_interrupts()
+{
+ m_irq_taken = false;
+ uint8_t pending_irqs = m_r[Z8_REGISTER_IMR] & m_r[Z8_REGISTER_IRQ] & Z8_IRQ_MASK;
+ if (!(m_r[Z8_REGISTER_IMR] & Z8_IMR_ENABLE) || pending_irqs == 0)
+ return;
+
+ int group_a[2] = { 5, 3 };
+ int group_b[2] = { 2, 0 };
+ int group_c[2] = { 1, 4 };
+
+ if (BIT(m_r[Z8_REGISTER_IPR], 5))
+ std::swap(group_a[0], group_a[1]);
+ if (BIT(m_r[Z8_REGISTER_IPR], 2))
+ std::swap(group_b[0], group_b[1]);
+ if (BIT(m_r[Z8_REGISTER_IPR], 1))
+ std::swap(group_c[0], group_c[1]);
+
+ switch ((m_r[Z8_REGISTER_IPR] & 0x18) >> 2 | (m_r[Z8_REGISTER_IPR] & 0x01))
+ {
+ case 0: // (000) reserved according to Zilog (but must process at least IRQ4)
+ case 1: // (001) C > A > B
+ if (BIT(pending_irqs, group_c[0]))
+ take_interrupt(group_c[0]);
+ else if (BIT(pending_irqs, group_c[1]))
+ take_interrupt(group_c[1]);
+ else if (BIT(pending_irqs, group_a[0]))
+ take_interrupt(group_a[0]);
+ else if (BIT(pending_irqs, group_a[1]))
+ take_interrupt(group_a[1]);
+ else if (BIT(pending_irqs, group_b[0]))
+ take_interrupt(group_b[0]);
+ else if (BIT(pending_irqs, group_b[1]))
+ take_interrupt(group_b[1]);
+ break;
+
+ case 2: // (010) A > B > C
+ if (BIT(pending_irqs, group_a[0]))
+ take_interrupt(group_a[0]);
+ else if (BIT(pending_irqs, group_a[1]))
+ take_interrupt(group_a[1]);
+ else if (BIT(pending_irqs, group_b[0]))
+ take_interrupt(group_b[0]);
+ else if (BIT(pending_irqs, group_b[1]))
+ take_interrupt(group_b[1]);
+ else if (BIT(pending_irqs, group_c[0]))
+ take_interrupt(group_c[0]);
+ else if (BIT(pending_irqs, group_c[1]))
+ take_interrupt(group_c[1]);
+ break;
+
+ case 3: // (011) A > C > B
+ if (BIT(pending_irqs, group_a[0]))
+ take_interrupt(group_a[0]);
+ else if (BIT(pending_irqs, group_a[1]))
+ take_interrupt(group_a[1]);
+ else if (BIT(pending_irqs, group_c[0]))
+ take_interrupt(group_c[0]);
+ else if (BIT(pending_irqs, group_c[1]))
+ take_interrupt(group_c[1]);
+ else if (BIT(pending_irqs, group_b[0]))
+ take_interrupt(group_b[0]);
+ else if (BIT(pending_irqs, group_b[1]))
+ take_interrupt(group_b[1]);
+ break;
+
+ case 4: // (100) B > C > A
+ if (BIT(pending_irqs, group_b[0]))
+ take_interrupt(group_b[0]);
+ else if (BIT(pending_irqs, group_b[1]))
+ take_interrupt(group_b[1]);
+ else if (BIT(pending_irqs, group_c[0]))
+ take_interrupt(group_c[0]);
+ else if (BIT(pending_irqs, group_c[1]))
+ take_interrupt(group_c[1]);
+ else if (BIT(pending_irqs, group_a[0]))
+ take_interrupt(group_a[0]);
+ else if (BIT(pending_irqs, group_a[1]))
+ take_interrupt(group_a[1]);
+ break;
+
+ case 5: // (101) C > B > A
+ if (BIT(pending_irqs, group_c[0]))
+ take_interrupt(group_c[0]);
+ else if (BIT(pending_irqs, group_c[1]))
+ take_interrupt(group_c[1]);
+ else if (BIT(pending_irqs, group_b[0]))
+ take_interrupt(group_b[0]);
+ else if (BIT(pending_irqs, group_b[1]))
+ take_interrupt(group_b[1]);
+ else if (BIT(pending_irqs, group_a[0]))
+ take_interrupt(group_a[0]);
+ else if (BIT(pending_irqs, group_a[1]))
+ take_interrupt(group_a[1]);
+ break;
+
+ case 6: // (110) B > A > C
+ case 7: // (111) reserved according to Zilog
+ if (BIT(pending_irqs, group_b[0]))
+ take_interrupt(group_b[0]);
+ else if (BIT(pending_irqs, group_b[1]))
+ take_interrupt(group_b[1]);
+ else if (BIT(pending_irqs, group_a[0]))
+ take_interrupt(group_a[0]);
+ else if (BIT(pending_irqs, group_a[1]))
+ take_interrupt(group_a[1]);
+ else if (BIT(pending_irqs, group_c[0]))
+ take_interrupt(group_c[0]);
+ else if (BIT(pending_irqs, group_c[1]))
+ take_interrupt(group_c[1]);
+ break;
+ }
+}
+
+/***************************************************************************
EXECUTION
***************************************************************************/
@@ -817,17 +963,23 @@ void z8_device::execute_run()
{
do
{
- /* TODO: sample interrupts */
- m_input[3] = m_input_cb[3]();
-
- /* fetch opcode */
- uint8_t opcode = fetch_opcode();
- int cycles = Z8601_OPCODE_MAP[opcode].execution_cycles;
+ process_interrupts();
+ if (m_irq_taken)
+ {
+ // interrupt processing takes 58 external clock cycles
+ m_icount -= 27;
+ }
+ else
+ {
+ /* fetch opcode */
+ uint8_t opcode = fetch_opcode();
+ int cycles = Z8601_OPCODE_MAP[opcode].execution_cycles;
- /* execute instruction */
- (this->*(Z8601_OPCODE_MAP[opcode].function))(opcode, &cycles);
+ /* execute instruction */
+ (this->*(Z8601_OPCODE_MAP[opcode].function))(opcode, &cycles);
- m_icount -= cycles;
+ m_icount -= cycles;
+ }
}
while (m_icount > 0);
}
@@ -847,6 +999,7 @@ void z8_device::device_reset()
register_write(Z8_REGISTER_P3M, 0x00);
register_write(Z8_REGISTER_P01M, 0x4d);
register_write(Z8_REGISTER_IRQ, 0x00);
+ register_write(Z8_REGISTER_IMR, 0x00);
register_write(Z8_REGISTER_RP, 0x00);
}
@@ -912,19 +1065,27 @@ void z8_device::execute_set_input(int inputnum, int state)
switch ( inputnum )
{
case INPUT_LINE_IRQ0:
- m_irq[0] = state;
+ if (state != CLEAR_LINE && m_irq_line[0] == CLEAR_LINE)
+ m_r[Z8_REGISTER_IRQ] |= Z8_IRQ_FLAG_IRQ0;
+ m_irq_line[0] = state;
break;
case INPUT_LINE_IRQ1:
- m_irq[1] = state;
+ if (state != CLEAR_LINE && m_irq_line[1] == CLEAR_LINE)
+ m_r[Z8_REGISTER_IRQ] |= Z8_IRQ_FLAG_IRQ1;
+ m_irq_line[1] = state;
break;
case INPUT_LINE_IRQ2:
- m_irq[2] = state;
+ if (state != CLEAR_LINE && m_irq_line[2] == CLEAR_LINE)
+ m_r[Z8_REGISTER_IRQ] |= Z8_IRQ_FLAG_IRQ2;
+ m_irq_line[2] = state;
break;
case INPUT_LINE_IRQ3:
- m_irq[3] = state;
+ if (state != CLEAR_LINE && m_irq_line[3] == CLEAR_LINE)
+ m_r[Z8_REGISTER_IRQ] |= Z8_IRQ_FLAG_IRQ3;
+ m_irq_line[3] = state;
break;
}
diff --git a/src/devices/cpu/z8/z8.h b/src/devices/cpu/z8/z8.h
index bc2698e5607..b7064c82ae7 100644
--- a/src/devices/cpu/z8/z8.h
+++ b/src/devices/cpu/z8/z8.h
@@ -75,7 +75,7 @@ protected:
// device_execute_interface overrides
virtual uint32_t execute_min_cycles() const override { return 6; }
- virtual uint32_t execute_max_cycles() const override { return 20; }
+ virtual uint32_t execute_max_cycles() const override { return 27; }
virtual uint32_t execute_input_lines() const override { return 4; }
virtual uint64_t execute_clocks_to_cycles(uint64_t clocks) const override { return (clocks + 2 - 1) / 2; }
virtual uint64_t execute_cycles_to_clocks(uint64_t cycles) const override { return (cycles * 2); }
@@ -126,7 +126,8 @@ private:
uint8_t m_fake_r[16]; /* fake working registers */
/* interrupts */
- int m_irq[6]; /* interrupts */
+ int m_irq_line[4]; /* IRQ line state */
+ bool m_irq_taken;
/* execution logic */
int m_icount; /* instruction counter */
@@ -138,6 +139,9 @@ private:
TIMER_CALLBACK_MEMBER( t0_tick );
TIMER_CALLBACK_MEMBER( t1_tick );
+ void take_interrupt(int irq);
+ void process_interrupts();
+
inline uint16_t mask_external_address(uint16_t addr);
inline uint8_t fetch();
inline uint8_t fetch_opcode();
diff --git a/src/mame/drivers/amerihok.cpp b/src/mame/drivers/amerihok.cpp
index f784b92ab18..c690eeee588 100644
--- a/src/mame/drivers/amerihok.cpp
+++ b/src/mame/drivers/amerihok.cpp
@@ -39,6 +39,9 @@ static ADDRESS_MAP_START(amerihok_map, AS_PROGRAM, 8, amerihok_state)
AM_RANGE(0x0000, 0xffff) AM_ROM
ADDRESS_MAP_END
+static ADDRESS_MAP_START(amerihok_data_map, AS_DATA, 8, amerihok_state)
+ADDRESS_MAP_END
+
static INPUT_PORTS_START( amerihok )
INPUT_PORTS_END
@@ -56,14 +59,14 @@ void amerihok_state::machine_reset()
static MACHINE_CONFIG_START( amerihok )
/* basic machine hardware */
- MCFG_CPU_ADD("maincpu", Z8681, XTAL_12MHz) // type guessed
+ MCFG_CPU_ADD("maincpu", Z8681, XTAL_12MHz)
MCFG_CPU_PROGRAM_MAP(amerihok_map)
-// MCFG_CPU_VBLANK_INT_DRIVER("screen", amerihok_state, irq4_line_hold)
+ MCFG_CPU_DATA_MAP(amerihok_data_map)
/* sound hardware */
MCFG_SPEAKER_STANDARD_MONO("mono")
- MCFG_SOUND_ADD("oki", OKIM6376, 1000000) // 64-pin surface mount
+ MCFG_SOUND_ADD("oki", OKIM6376, 1000000) // 64-pin QFP (probably actually MSM6650)
MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 1.0)
MACHINE_CONFIG_END