From 215e9a8be91b6ef9b6ea43d1e9674b7abd661c23 Mon Sep 17 00:00:00 2001 From: hap Date: Mon, 15 Jun 2015 03:24:24 +0200 Subject: [PATCH] added interrupt handling --- src/emu/cpu/melps4/m58846.c | 51 ++++++++++++++- src/emu/cpu/melps4/m58846.h | 7 ++ src/emu/cpu/melps4/melps4.c | 110 ++++++++++++++++++++++++++++---- src/emu/cpu/melps4/melps4.h | 22 ++++++- src/emu/cpu/melps4/melps4op.inc | 11 ++-- 5 files changed, 181 insertions(+), 20 deletions(-) diff --git a/src/emu/cpu/melps4/m58846.c b/src/emu/cpu/melps4/m58846.c index ac14baf797d..824969f8cd6 100644 --- a/src/emu/cpu/melps4/m58846.c +++ b/src/emu/cpu/melps4/m58846.c @@ -10,7 +10,7 @@ */ #include "m58846.h" -#include "debugger.h" +//#include "debugger.h" @@ -50,6 +50,9 @@ offs_t m58846_device::disasm_disassemble(char *buffer, offs_t pc, const UINT8 *o void m58846_device::device_start() { melps4_cpu_device::device_start(); + + m_timer[0] = timer_alloc(0); + m_timer[1] = timer_alloc(1); } @@ -61,6 +64,52 @@ void m58846_device::device_start() void m58846_device::device_reset() { melps4_cpu_device::device_reset(); + + // timer 1 runs continuously + reset_timer1(); +} + + + +//------------------------------------------------- +// timers +//------------------------------------------------- + +void m58846_device::reset_timer1() +{ + // reset 7-bit prescaler + attotime base = attotime::from_ticks(6 * 128, unscaled_clock()); + m_timer[0]->adjust(base); +} + +void m58846_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +{ + switch (id) + { + // timer 1 + case 0: + m_irqflag[1] = true; + m_possible_irq = true; + reset_timer1(); + break; + + // timer 2 + case 1: + break; + + default: + assert_always(FALSE, "Unknown id in m58846_device::device_timer"); + break; + } +} + +void m58846_device::write_v(UINT8 data) +{ + // d0: enable timer 1 irq + m_tmr_irq_enabled[0] = (data & 1) ? true : false; + m_possible_irq = true; + + m_v = data; } diff --git a/src/emu/cpu/melps4/m58846.h b/src/emu/cpu/melps4/m58846.h index 7ee094df568..458f649c4f0 100644 --- a/src/emu/cpu/melps4/m58846.h +++ b/src/emu/cpu/melps4/m58846.h @@ -29,6 +29,13 @@ protected: // device_disasm_interface overrides virtual offs_t disasm_disassemble(char *buffer, offs_t pc, const UINT8 *oprom, const UINT8 *opram, UINT32 options); + + // timers + virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr); + virtual void write_v(UINT8 data); + + emu_timer *m_timer[2]; + void reset_timer1(); }; diff --git a/src/emu/cpu/melps4/melps4.c b/src/emu/cpu/melps4/melps4.c index 5fdd2b1cab6..c4309c6a3b9 100644 --- a/src/emu/cpu/melps4/melps4.c +++ b/src/emu/cpu/melps4/melps4.c @@ -46,12 +46,15 @@ void melps4_cpu_device::state_string_export(const device_state_entry &entry, std { // obviously not from a single flags register, letters are made up case STATE_GENFLAGS: - strprintf(str, "%c%c%c%c%c", + strprintf(str, "%c%c%c%c%c %c%c%c", m_intp ? 'P':'p', m_inte ? 'I':'i', m_sm ? 'S':'s', m_cps ? 'D':'d', - m_cy ? 'C':'c' + m_cy ? 'C':'c', + m_irqflag[0] ? 'X':'.', // exf + m_irqflag[1] ? '1':'.', // 1f + m_irqflag[2] ? '2':'.' // 2f ); break; @@ -111,7 +114,11 @@ void melps4_cpu_device::device_start() m_skip = false; m_inte = 0; m_intp = 1; + m_irqflag[0] = m_irqflag[1] = m_irqflag[2] = false; + m_tmr_irq_enabled[0] = m_tmr_irq_enabled[1] = false; + m_int_state = 0; m_prohibit_irq = false; + m_possible_irq = false; m_a = 0; m_b = 0; @@ -147,7 +154,11 @@ void melps4_cpu_device::device_start() save_item(NAME(m_skip)); save_item(NAME(m_inte)); save_item(NAME(m_intp)); + save_item(NAME(m_irqflag)); + save_item(NAME(m_tmr_irq_enabled)); + save_item(NAME(m_int_state)); save_item(NAME(m_prohibit_irq)); + save_item(NAME(m_possible_irq)); save_item(NAME(m_a)); save_item(NAME(m_b)); @@ -165,7 +176,7 @@ void melps4_cpu_device::device_start() // register state for debugger state_add(STATE_GENPC, "curpc", m_pc).formatstr("%04X").noshow(); - state_add(STATE_GENFLAGS, "GENFLAGS", m_cy).formatstr("%5s").noshow(); + state_add(STATE_GENFLAGS, "GENFLAGS", m_cy).formatstr("%9s").noshow(); state_add(MELPS4_PC, "PC", m_pc).formatstr("%04X"); state_add(MELPS4_A, "A", m_a).formatstr("%2d"); // show in decimal @@ -194,18 +205,20 @@ void melps4_cpu_device::device_reset() { m_sm = m_sms = false; m_ba_flag = false; - m_prohibit_irq = false; - m_skip = false; m_op = m_prev_op = 0; m_pc = m_prev_pc = 0; - m_inte = 0; - m_intp = 1; op_lcps(); // CPS=0 - m_v = 0; - m_w = 0; - + // clear interrupts + m_inte = 0; + m_intp = 1; + write_v(0); + write_w(0); + m_irqflag[0] = m_irqflag[1] = m_irqflag[2] = false; + m_prohibit_irq = false; + m_possible_irq = false; + // clear ports write_d_pin(MELPS4_PORTD_CLR, 0); write_gen_port(MELPS4_PORTS, 0); @@ -291,6 +304,73 @@ void melps4_cpu_device::write_d_pin(int bit, int state) +//------------------------------------------------- +// interrupts +//------------------------------------------------- + +void melps4_cpu_device::execute_set_input(int line, int state) +{ + state = (state) ? 1 : 0; + + switch (line) + { + // external interrupt + case MELPS4_INPUT_LINE_INT: + // irq on rising/falling edge + if (state != m_int_state && state == m_intp) + { + m_irqflag[0] = true; + m_possible_irq = true; + } + m_int_state = state; + break; + + // timer input pin + case MELPS4_INPUT_LINE_T: + break; + + default: + break; + } +} + +void melps4_cpu_device::do_interrupt(int which) +{ + m_inte = 0; + m_irqflag[which] = false; + + m_icount--; + push_pc(); + m_sms = m_sm; + m_sm = false; + m_op = 0; // fake nop + m_pc = m_int_page << 7 | (which * 2); + + standard_irq_callback(which); +} + +void melps4_cpu_device::check_interrupt() +{ + if (!m_inte) + return; + + int which = 0; + + // assume that lower irq vectors have higher priority + if (m_irqflag[0]) + which = 0; + else if (m_irqflag[1] && m_tmr_irq_enabled[0]) + which = 1; + else if (m_irqflag[2] && m_tmr_irq_enabled[1]) + which = 2; + else + return; + + do_interrupt(which); +} + + + //------------------------------------------------- // execute //------------------------------------------------- @@ -314,8 +394,14 @@ void melps4_cpu_device::execute_run() m_prev_op = m_op; m_prev_pc = m_pc; - // irq is not accepted during skip or LXY, LA, EI, DI, RT/RTS/RTI or any branch - //.. + // Interrupts are not accepted during skips or LXY, LA, EI, DI, RT/RTS/RTI or any branch. + // Documentation is conflicting here: older docs say that it is allowed during skips, + // newer docs specifically say when interrupts are prohibited. + if (m_possible_irq && !m_prohibit_irq && !m_skip) + { + m_possible_irq = false; + check_interrupt(); + } m_prohibit_irq = false; // fetch next opcode diff --git a/src/emu/cpu/melps4/melps4.h b/src/emu/cpu/melps4/melps4.h index 023b0b9ba02..e4110dd07e7 100644 --- a/src/emu/cpu/melps4/melps4.h +++ b/src/emu/cpu/melps4/melps4.h @@ -60,6 +60,12 @@ enum MELPS4_PORTU }; +enum +{ + MELPS4_INPUT_LINE_INT = 0, + MELPS4_INPUT_LINE_T +}; + // pinout reference @@ -142,8 +148,9 @@ protected: virtual UINT64 execute_clocks_to_cycles(UINT64 clocks) const { return (clocks + 6 - 1) / 6; } // 6 t-states per machine cycle virtual UINT64 execute_cycles_to_clocks(UINT64 cycles) const { return (cycles * 6); } // " virtual UINT32 execute_min_cycles() const { return 1; } - virtual UINT32 execute_max_cycles() const { return 1; } + virtual UINT32 execute_max_cycles() const { return 1+1; } // max opcode cycles + interrupt duration virtual UINT32 execute_input_lines() const { return 3; } // up to 3 (some internal) + virtual void execute_set_input(int line, int state); virtual void execute_run(); virtual void execute_one(); @@ -189,14 +196,18 @@ protected: UINT8 m_port_s; // " UINT8 m_port_f; // " - bool m_sm, m_sms; // subroutine mode flag + stack + bool m_sm, m_sms; // subroutine mode flag + irq stack bool m_ba_flag; // temp flag indicates BA opcode was executed UINT8 m_sp_param; // temp register holding SP opcode parameter UINT8 m_cps; // DP,CY or DP',CY' selected bool m_skip; // skip next opcode UINT8 m_inte; // interrupt enable flag - UINT8 m_intp; // external interrupt polarity ('40 to '44) + int m_intp; // external interrupt polarity ('40 to '44) + bool m_irqflag[3]; // irq flags: exf, 1f, 2f (external, timer 1, timer 2) + bool m_tmr_irq_enabled[2]; + int m_int_state; // INT pin state bool m_prohibit_irq; // interrupt is prohibited during certain opcodes + bool m_possible_irq; // indicate that irq needs to be rechecked // work registers (unless specified, each is 4-bit) UINT8 m_a; // accumulator @@ -225,6 +236,11 @@ protected: devcb_write8 m_write_g; devcb_write8 m_write_u; devcb_write_line m_write_t; + + virtual void write_v(UINT8 data) { m_v = data; } + virtual void write_w(UINT8 data) { m_w = data; } + virtual void do_interrupt(int which); + virtual void check_interrupt(); UINT8 read_gen_port(int port); void write_gen_port(int port, UINT8 data); diff --git a/src/emu/cpu/melps4/melps4op.inc b/src/emu/cpu/melps4/melps4op.inc index 476de900e93..6640156b16b 100644 --- a/src/emu/cpu/melps4/melps4op.inc +++ b/src/emu/cpu/melps4/melps4op.inc @@ -419,25 +419,27 @@ void melps4_cpu_device::op_tab2() void melps4_cpu_device::op_tva() { // TVA: transfer A to timer control V - m_v = m_a; + write_v(m_a); } void melps4_cpu_device::op_twa() { // TWA: transfer A to timer control W - m_w = m_a; + write_w(m_a); } void melps4_cpu_device::op_snz1() { // SNZ1: skip next on flag 1F - op_illegal(); + m_skip = m_irqflag[1]; + m_irqflag[1] = false; } void melps4_cpu_device::op_snz2() { // SNZ2: skip next on flag 2F - op_illegal(); + m_skip = m_irqflag[2]; + m_irqflag[2] = false; } @@ -631,6 +633,7 @@ void melps4_cpu_device::op_ei() { // EI: enable interrupt flag m_prohibit_irq = true; + m_possible_irq = true; m_inte = 1; }