More accurate registration and acknowledgment of pending interrupts within TMP68301 interrupt controller

This commit is contained in:
AJR 2018-11-27 22:52:20 -05:00
parent c13e586e80
commit 159265d832
4 changed files with 110 additions and 90 deletions

View File

@ -1,5 +1,5 @@
// license:BSD-3-Clause
// copyright-holders:Luca Elia
// copyright-holders:Luca Elia, AJR
/***************************************************************************
TMP68301 basic emulation + Interrupt Handling
@ -9,7 +9,7 @@
all integrated in a single chip.
TODO:
- Interrupt generation: handle pending / in-service mechanisms
- Interrupt generation: edge detection, input expansion (INT3-INT9)
- Parallel port: handle timing latency
- Serial port: not done at all
- (and many other things)
@ -27,6 +27,7 @@ void tmp68301_device::tmp68301_regs(address_map &map)
map(0x080, 0x093).rw(FUNC(tmp68301_device::icr_r), FUNC(tmp68301_device::icr_w)).umask16(0x00ff);
map(0x094, 0x095).rw(FUNC(tmp68301_device::imr_r), FUNC(tmp68301_device::imr_w));
map(0x096, 0x097).rw(FUNC(tmp68301_device::ipr_r), FUNC(tmp68301_device::ipr_w));
map(0x098, 0x099).rw(FUNC(tmp68301_device::iisr_r), FUNC(tmp68301_device::iisr_w));
/* Parallel Port */
@ -37,7 +38,7 @@ void tmp68301_device::tmp68301_regs(address_map &map)
map(0x18e, 0x18f).rw(FUNC(tmp68301_device::scr_r), FUNC(tmp68301_device::scr_w));
}
// IRQ Mask register, 0x94
// IRQ Mask register
READ16_MEMBER(tmp68301_device::imr_r)
{
return m_imr;
@ -46,6 +47,20 @@ READ16_MEMBER(tmp68301_device::imr_r)
WRITE16_MEMBER(tmp68301_device::imr_w)
{
COMBINE_DATA(&m_imr);
update_ipl();
}
// IRQ Pending Register
READ16_MEMBER(tmp68301_device::ipr_r)
{
return m_ipr;
}
WRITE16_MEMBER(tmp68301_device::ipr_w)
{
// software writes only clear bits
m_ipr &= data | ~mem_mask;
update_ipl();
}
// IRQ In-Service Register
@ -56,7 +71,8 @@ READ16_MEMBER(tmp68301_device::iisr_r)
WRITE16_MEMBER(tmp68301_device::iisr_w)
{
COMBINE_DATA(&m_iisr);
// software writes only clear bits
m_iisr &= data | ~mem_mask;
}
// Serial Control Register (TODO: 8-bit wide)
@ -123,7 +139,9 @@ tmp68301_device::tmp68301_device(const machine_config &mconfig, const char *tag,
m_cpu(*this, finder_base::DUMMY_TAG),
m_in_parallel_cb(*this),
m_out_parallel_cb(*this),
m_ipl(0),
m_imr(0),
m_ipr(0),
m_iisr(0),
m_scr(0),
m_pdir(0),
@ -132,7 +150,6 @@ tmp68301_device::tmp68301_device(const machine_config &mconfig, const char *tag,
{
memset(m_regs, 0, sizeof(m_regs));
memset(m_icr, 0, sizeof(m_icr));
memset(m_irq_vector, 0, sizeof(m_irq_vector));
}
@ -151,8 +168,9 @@ void tmp68301_device::device_start()
save_item(NAME(m_regs));
save_item(NAME(m_icr));
save_item(NAME(m_irq_vector));
save_item(NAME(m_ipl));
save_item(NAME(m_imr));
save_item(NAME(m_ipr));
save_item(NAME(m_iisr));
save_item(NAME(m_scr));
save_item(NAME(m_pdir));
@ -165,8 +183,11 @@ void tmp68301_device::device_start()
void tmp68301_device::device_reset()
{
m_ipr = 0;
m_iisr = 0;
m_imr = 0x7f7; // mask all irqs
std::fill(std::begin(m_icr), std::end(m_icr), 0x07);
update_ipl();
}
//-------------------------------------------------
@ -205,31 +226,50 @@ inline void tmp68301_device::write_word(offs_t address, uint16_t data)
IRQ_CALLBACK_MEMBER(tmp68301_device::irq_callback)
{
int vector = m_irq_vector[irqline];
// logerror("%s: irq callback returns %04X for level %x\n",machine().describe_context(),vector,int_level);
return vector;
uint8_t IVNR = m_regs[0x9a/2] & 0xe0; // Interrupt Vector Number Register (IVNR)
for (int src : { 0, 7, 3, 1, 8, 4, 5, 9, 2 })
{
// check if the IPL matches
if (irqline == (m_icr[src] & 0x07))
{
// check if interrupt is pending and not masked out
u16 mask = (src > 2 ? 2 : 1) << src;
if ((m_ipr & mask) != 0 && (m_imr & mask) == 0)
{
// add cause to interrupt in-service register
m_iisr |= mask;
// no longer pending
m_ipr &= ~mask;
update_ipl();
// vary vector number by type
if (src > 6)
return IVNR | (src - 3);
else if (src > 2)
return IVNR | (src - 1) << 2 | serial_interrupt_cause(src - 3);
else /*if (BIT(m_icr[src], 5))*/ // TODO: use external vector otherwise
return IVNR | src;
}
}
}
// default vector
return IVNR | 0x1f;
}
TIMER_CALLBACK_MEMBER( tmp68301_device::timer_callback )
TIMER_CALLBACK_MEMBER(tmp68301_device::timer_callback)
{
int i = param;
uint16_t TCR = m_regs[(0x200 + i * 0x20)/2];
uint16_t ICR = m_regs[0x8e/2+i]; // Interrupt Controller Register (ICR7..9)
uint16_t IVNR = m_regs[0x9a/2]; // Interrupt Vector Number Register (IVNR)
// logerror("s: callback timer %04X, j = %d\n",machine().describe_context(),i,tcount);
if ( (TCR & 0x0004) && // INT
!(m_imr & (0x100<<i))
)
if (TCR & 0x0004) // INT
{
int level = ICR & 0x0007;
// Interrupt Vector Number Register (IVNR)
m_irq_vector[level] = IVNR & 0x00e0;
m_irq_vector[level] += 4+i;
m_cpu->set_input_line(level,HOLD_LINE);
m_ipr |= 0x100 << i;
update_ipl();
}
if (TCR & 0x0080) // N/1
@ -243,7 +283,7 @@ TIMER_CALLBACK_MEMBER( tmp68301_device::timer_callback )
}
}
void tmp68301_device::update_timer( int i )
void tmp68301_device::update_timer(int i)
{
uint16_t TCR = m_regs[(0x200 + i * 0x20)/2];
uint16_t MAX1 = m_regs[(0x204 + i * 0x20)/2];
@ -289,71 +329,38 @@ void tmp68301_device::update_timer( int i )
}
/* Update the IRQ state based on all possible causes */
void tmp68301_device::update_irq_state(uint16_t cause)
void tmp68301_device::update_ipl()
{
int i;
uint8_t new_ipl = 0;
/* Take care of external interrupts */
uint16_t IVNR = m_regs[0x9a/2]; // Interrupt Vector Number Register (IVNR)
// add cause to interrupt in-service register
m_iisr |= cause;
for (i = 0; i < 3; i++)
for (int src = 0; src < 10; src++)
{
if ((m_iisr & 1 << i) && !(m_imr & (1<<i)))
{
uint8_t current_ICR = m_icr[i]; // Interrupt Controller Register (ICR0..2)
u16 mask = (src > 2 ? 2 : 1) << src;
if ((m_ipr & mask) != 0 && (m_imr & mask) == 0 && new_ipl < (m_icr[src] & 0x07))
new_ipl = m_icr[src] & 0x07;
}
// Interrupt Controller Register (ICR0..2)
int level = current_ICR & 0x0007;
if (new_ipl != m_ipl)
{
if (m_ipl != 0)
m_cpu->set_input_line(m_ipl, CLEAR_LINE);
if (new_ipl != 0)
m_cpu->set_input_line(new_ipl, ASSERT_LINE);
// Interrupt Vector Number Register (IVNR)
m_irq_vector[level] = IVNR & 0x00e0;
m_irq_vector[level] += i;
m_cpu->set_input_line(level,HOLD_LINE);
}
m_ipl = new_ipl;
}
}
void tmp68301_device::update_irq_serial(uint16_t cause, uint8_t type)
uint8_t tmp68301_device::serial_interrupt_cause(int channel)
{
int i;
const uint8_t serial_irq_vector[3] = { 8, 0xc, 0x10 };
/* Take care of external interrupts */
uint16_t IVNR = m_regs[0x9a/2]; // Interrupt Vector Number Register (IVNR)
// add cause to interrupt in-service register
m_iisr |= cause;
for (i = 4; i < 7; i++)
{
if ((m_iisr & 1 << i) && !(m_imr & (1<<i)))
{
uint8_t current_ICR = m_icr[i-1]; // Interrupt Controller Register (ICR0..2)
// Interrupt Controller Register (ICR0..2)
int level = current_ICR & 0x0007;
// Interrupt Vector Number Register (IVNR)
m_irq_vector[level] = IVNR & 0x00e0;
/*
* channel number
*/
m_irq_vector[level] += serial_irq_vector[i-4];
/*
* 00 error occurred
* 01 receive completed
* 10 transmit ready
* 11 interrupt cause cleared while interrupt pending
*/
m_irq_vector[level] += type;
m_cpu->set_input_line(level,HOLD_LINE);
}
}
/*
* 00 error occurred
* 01 receive completed
* 10 transmit ready
* 11 interrupt cause cleared while interrupt pending
*/
(void)channel;
return 3;
}
@ -387,6 +394,6 @@ WRITE16_MEMBER( tmp68301_device::regs_w )
}
}
void tmp68301_device::external_interrupt_0() { update_irq_state(EXT_IRQ0); }
void tmp68301_device::external_interrupt_1() { update_irq_state(EXT_IRQ1); }
void tmp68301_device::external_interrupt_2() { update_irq_state(EXT_IRQ2); }
void tmp68301_device::external_interrupt_0() { m_ipr |= EXT_IRQ0; update_ipl(); }
void tmp68301_device::external_interrupt_1() { m_ipr |= EXT_IRQ1; update_ipl(); }
void tmp68301_device::external_interrupt_2() { m_ipr |= EXT_IRQ2; update_ipl(); }

View File

@ -40,6 +40,8 @@ public:
private:
DECLARE_READ16_MEMBER(imr_r);
DECLARE_WRITE16_MEMBER(imr_w);
DECLARE_READ16_MEMBER(ipr_r);
DECLARE_WRITE16_MEMBER(ipr_w);
DECLARE_READ16_MEMBER(iisr_r);
DECLARE_WRITE16_MEMBER(iisr_w);
DECLARE_READ16_MEMBER(scr_r);
@ -60,10 +62,10 @@ protected:
virtual space_config_vector memory_space_config() const override;
private:
TIMER_CALLBACK_MEMBER( timer_callback );
void update_timer( int i );
void update_irq_state(uint16_t cause);
void update_irq_serial(uint16_t cause, uint8_t type);
TIMER_CALLBACK_MEMBER(timer_callback);
void update_timer(int i);
void update_ipl();
uint8_t serial_interrupt_cause(int channel);
static constexpr uint16_t EXT_IRQ0 = 1 << 0;
static constexpr uint16_t EXT_IRQ1 = 1 << 1;
@ -89,9 +91,10 @@ private:
emu_timer *m_tmp68301_timer[3]; // 3 Timers
uint16_t m_irq_vector[8];
uint8_t m_ipl; // internal interrupt level
uint16_t m_imr;
uint16_t m_ipr;
uint16_t m_iisr;
uint16_t m_scr;
uint16_t m_pdir;

View File

@ -1378,7 +1378,6 @@ Note: on screen copyright is (c)1998 Coinmaster.
#include "machine/msm6242.h"
#include "machine/nvram.h"
#include "machine/pit8253.h"
#include "machine/tmp68301.h"
#include "machine/watchdog.h"
#include "sound/2203intf.h"
#include "sound/2612intf.h"
@ -9255,13 +9254,19 @@ MACHINE_CONFIG_END
Pro Mahjong Kiwame
***************************************************************************/
WRITE_LINE_MEMBER(seta_state::kiwame_vblank)
{
if (state)
m_tmp68301->external_interrupt_0();
}
MACHINE_CONFIG_START(seta_state::kiwame)
/* basic machine hardware */
MCFG_DEVICE_ADD(m_maincpu, M68000, 16000000) /* 16 MHz */
MCFG_DEVICE_PROGRAM_MAP(kiwame_map)
/* lev 1-7 are the same. WARNING: the interrupt table is written to. */
MCFG_DEVICE_VBLANK_INT_DRIVER("screen", seta_state, irq1_line_hold)
MCFG_DEVICE_IRQ_ACKNOWLEDGE_DEVICE("tmp68301", tmp68301_device, irq_callback)
tmp68301_device &tmp68301(TMP68301(config, "tmp68301", 0));
tmp68301.set_cputag(m_maincpu);
tmp68301.out_parallel_callback().set(FUNC(seta_state::kiwame_row_select_w));
@ -9280,6 +9285,7 @@ MACHINE_CONFIG_START(seta_state::kiwame)
MCFG_SCREEN_VISIBLE_AREA(0*8, 56*8-1, 1*8, 31*8-1)
MCFG_SCREEN_UPDATE_DRIVER(seta_state, screen_update_seta_no_layers)
MCFG_SCREEN_PALETTE("palette")
MCFG_SCREEN_VBLANK_CALLBACK(WRITELINE(*this, seta_state, kiwame_vblank))
MCFG_DEVICE_ADD("gfxdecode", GFXDECODE, "palette", gfx_tndrcade)
MCFG_PALETTE_ADD("palette", 512) /* sprites only */

View File

@ -16,6 +16,7 @@
#include "machine/gen_latch.h"
#include "machine/ticket.h"
#include "machine/timer.h"
#include "machine/tmp68301.h"
#include "machine/upd4701.h"
#include "machine/upd4992.h"
#include "sound/x1_010.h"
@ -45,6 +46,7 @@ public:
seta_state(const machine_config &mconfig, device_type type, const char *tag) :
driver_device(mconfig, type, tag),
m_maincpu(*this,"maincpu"),
m_tmp68301(*this, "tmp68301"),
m_audiocpu(*this, "audiocpu"),
m_subcpu(*this,"sub"),
m_seta001(*this, "spritegen"),
@ -147,6 +149,7 @@ public:
protected:
required_device<cpu_device> m_maincpu;
optional_device<tmp68301_device> m_tmp68301;
optional_device<cpu_device> m_audiocpu;
optional_device<cpu_device> m_subcpu;
required_device<seta001_device> m_seta001;
@ -300,6 +303,7 @@ protected:
TIMER_DEVICE_CALLBACK_MEMBER(tndrcade_sub_interrupt);
TIMER_DEVICE_CALLBACK_MEMBER(calibr50_interrupt);
TIMER_DEVICE_CALLBACK_MEMBER(crazyfgt_interrupt);
DECLARE_WRITE_LINE_MEMBER(kiwame_vblank);
void set_pens();
void usclssic_set_pens();
void draw_tilemap_palette_effect(bitmap_ind16 &bitmap, const rectangle &cliprect, tilemap_t *tilemap, int scrollx, int scrolly, int gfxnum, int flipscreen);