mirror of
https://github.com/holub/mame
synced 2025-05-30 01:23:07 +03:00
More accurate registration and acknowledgment of pending interrupts within TMP68301 interrupt controller
This commit is contained in:
parent
c13e586e80
commit
159265d832
@ -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(); }
|
||||
|
@ -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;
|
||||
|
@ -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 */
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user