mirror of
https://github.com/holub/mame
synced 2025-04-24 17:30:55 +03:00
tmp68301: Add timers and fix interrupts (thanks grdians for the test cases)
This commit is contained in:
parent
ca6e220dbb
commit
407af69762
@ -28,6 +28,7 @@ m68000_device::m68000_device(const machine_config &mconfig, device_type type, co
|
||||
m_cpu_space_config("cpu_space", ENDIANNESS_BIG, 16, 24, 0, address_map_constructor(FUNC(m68000_device::default_autovectors_map), this))
|
||||
{
|
||||
m_mmu = nullptr;
|
||||
m_disable_interrupt_callback = false;
|
||||
}
|
||||
|
||||
void m68000_device::abort_access(u32 reason)
|
||||
@ -444,12 +445,14 @@ void m68000_device::start_interrupt_vector_lookup()
|
||||
// flag for berr -> spurious
|
||||
|
||||
int level = m_next_state >> 24;
|
||||
if(m_interrupt_mixer)
|
||||
standard_irq_callback(level == 7 && m_nmi_uses_generic ? INPUT_LINE_NMI : level, m_pc);
|
||||
else {
|
||||
for(int i=0; i<3; i++)
|
||||
if(level & (1<<i))
|
||||
standard_irq_callback(i, m_pc);
|
||||
if(!m_disable_interrupt_callback) {
|
||||
if(m_interrupt_mixer)
|
||||
standard_irq_callback(level == 7 && m_nmi_uses_generic ? INPUT_LINE_NMI : level, m_pc);
|
||||
else {
|
||||
for(int i=0; i<3; i++)
|
||||
if(level & (1<<i))
|
||||
standard_irq_callback(i, m_pc);
|
||||
}
|
||||
}
|
||||
|
||||
// Clear the nmi flag
|
||||
@ -468,6 +471,7 @@ void m68000_device::end_interrupt_vector_lookup()
|
||||
m68000_mcu_device::m68000_mcu_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock) :
|
||||
m68000_device(mconfig, type, tag, owner, clock)
|
||||
{
|
||||
m_disable_interrupt_callback = true;
|
||||
}
|
||||
|
||||
void m68000_mcu_device::execute_run()
|
||||
@ -564,5 +568,4 @@ void m68000_mcu_device::set_current_interrupt_level(u32 level)
|
||||
m_nmi_pending = true;
|
||||
|
||||
update_interrupt();
|
||||
|
||||
}
|
||||
|
@ -166,6 +166,7 @@ protected:
|
||||
u32 m_int_level;
|
||||
u32 m_int_next_state;
|
||||
bool m_nmi_uses_generic;
|
||||
bool m_disable_interrupt_callback;
|
||||
u64 m_last_vpa_time;
|
||||
|
||||
// Current instruction state and substate
|
||||
|
@ -40,12 +40,22 @@ void tmp68301_device::internal_update(uint64_t current_time)
|
||||
if(m_serial_rx_next_event[i] && current_time >= m_serial_rx_next_event[i])
|
||||
serial_rx_update(i);
|
||||
|
||||
if(m_serial_tx_next_event[i] && m_serial_tx_next_event[i] < event_time)
|
||||
if(m_serial_tx_next_event[i] && (!event_time || m_serial_tx_next_event[i] < event_time))
|
||||
event_time = m_serial_tx_next_event[i];
|
||||
if(m_serial_rx_next_event[i] && m_serial_rx_next_event[i] < event_time)
|
||||
if(m_serial_rx_next_event[i] && (!event_time || m_serial_rx_next_event[i] < event_time))
|
||||
event_time = m_serial_rx_next_event[i];
|
||||
}
|
||||
|
||||
// Timers
|
||||
for(int i=0; i != 3; i++) {
|
||||
// Calling update changes the next event time
|
||||
if(m_timer_next_event[i] && current_time >= m_timer_next_event[i])
|
||||
timer_update(i);
|
||||
|
||||
if(m_timer_next_event[i] && (!event_time || m_timer_next_event[i] < event_time))
|
||||
event_time = m_timer_next_event[i];
|
||||
}
|
||||
|
||||
recompute_bcount(event_time);
|
||||
}
|
||||
|
||||
@ -104,6 +114,8 @@ void tmp68301_device::device_start()
|
||||
save_item(NAME(m_tmcr1));
|
||||
save_item(NAME(m_tmcr2));
|
||||
save_item(NAME(m_tctr));
|
||||
save_item(NAME(m_timer_next_event));
|
||||
save_item(NAME(m_timer_last_sync));
|
||||
}
|
||||
|
||||
void tmp68301_device::device_reset()
|
||||
@ -159,11 +171,14 @@ void tmp68301_device::device_reset()
|
||||
m_serial_rx_line[i] = 0;
|
||||
}
|
||||
|
||||
m_tcr[0] = 0x0052;
|
||||
m_tcr[1] = m_tcr[2] = 0x0012;
|
||||
m_tmcr1[0] = m_tmcr1[1] = m_tmcr1[2] = 0x0000;
|
||||
m_tmcr2[0] = m_tmcr2[1] = m_tmcr2[2] = 0x0000;
|
||||
m_tctr[0] = m_tctr[1] = m_tctr[2] = 0x0000;
|
||||
for(int i=0; i != 3; i++) {
|
||||
m_tcr[i] = i ? 0x0012 : 0x0052;
|
||||
m_tmcr1[i] = 0x0000;
|
||||
m_tmcr2[i] = 0x0000;
|
||||
m_tctr[i] = 0x0000;
|
||||
m_timer_next_event[i] = 0;
|
||||
m_timer_last_sync[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void tmp68301_device::internal_map(address_map &map)
|
||||
@ -579,6 +594,8 @@ u8 tmp68301_device::interrupt_callback()
|
||||
{
|
||||
auto [level, vector, slot] = interrupt_get_current();
|
||||
logerror("interrupt callback ipr=%03x imr=%03x (%x, %02x, %d)\n", m_ipr, m_imr, level, vector, slot);
|
||||
if(slot < 3)
|
||||
standard_irq_callback(slot, m_pc);
|
||||
if(vector != 0x1f) {
|
||||
m_isr |= 1 << slot;
|
||||
if(slot >= 3 || !(m_icr[slot] & 0x08))
|
||||
@ -1114,7 +1131,7 @@ const char *const tmp68301_device::timer_source_names[3][4] = {
|
||||
{ "internal", "external", "ch0", "ch1" }
|
||||
};
|
||||
|
||||
const int tmp68301_device::timer_divider[16] = { 1, 2, 4, 8, 16, 32, 64, 128, 256, 256, 256, 256, 256, 256, 256, 256 };
|
||||
const int tmp68301_device::timer_divider[16] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 8, 8, 8, 8 };
|
||||
|
||||
u16 tmp68301_device::tcr0_r() { return m_tcr[0]; }
|
||||
u16 tmp68301_device::tcr1_r() { return m_tcr[1]; }
|
||||
@ -1124,9 +1141,9 @@ u16 tmp68301_device::tmcr11_r() { return m_tmcr1[1]; }
|
||||
u16 tmp68301_device::tmcr12_r() { return m_tmcr2[1]; }
|
||||
u16 tmp68301_device::tmcr21_r() { return m_tmcr1[2]; }
|
||||
u16 tmp68301_device::tmcr22_r() { return m_tmcr2[2]; }
|
||||
u16 tmp68301_device::tctr0_r() { return m_tctr[0]; }
|
||||
u16 tmp68301_device::tctr1_r() { return m_tctr[1]; }
|
||||
u16 tmp68301_device::tctr2_r() { return m_tctr[2]; }
|
||||
u16 tmp68301_device::tctr0_r() { timer_sync(0); return m_tctr[0]; }
|
||||
u16 tmp68301_device::tctr1_r() { timer_sync(1); return m_tctr[1]; }
|
||||
u16 tmp68301_device::tctr2_r() { timer_sync(2); return m_tctr[2]; }
|
||||
|
||||
void tmp68301_device::tcr0_w(offs_t, u16 data, u16 mem_mask) { tcr_w(0, data, mem_mask); }
|
||||
void tmp68301_device::tcr1_w(offs_t, u16 data, u16 mem_mask) { tcr_w(1, data, mem_mask); }
|
||||
@ -1146,6 +1163,8 @@ void tmp68301_device::tctr2_w(offs_t, u16 data, u16 mem_mask) { tctr_w(2, data,
|
||||
|
||||
void tmp68301_device::tcr_w(int ch, u16 data, u16 mem_mask)
|
||||
{
|
||||
timer_sync(ch);
|
||||
|
||||
static const char *const count_mode[4] = { "normal", "?", "start", "wait" };
|
||||
static const char *const max_mode[4] = { "?", "1", "2", "1&2" };
|
||||
u16 old = m_tmcr1[ch];
|
||||
@ -1159,48 +1178,140 @@ void tmp68301_device::tcr_w(int ch, u16 data, u16 mem_mask)
|
||||
if(ch == 0)
|
||||
logerror("timer %d clk=%s div=%d mode=%s repeat=%s intr=%s %s%s\n",
|
||||
ch,
|
||||
timer_source_names[ch][(m_tcr[ch] >> 14) & 3],
|
||||
timer_divider[(m_tcr[ch] >> 10) & 15],
|
||||
count_mode[(m_tcr[ch] >> 8) & 3],
|
||||
m_tcr[ch] & 0x0080 ? "on" : "off",
|
||||
m_tcr[ch] & 0x0004 ? "on" : "off",
|
||||
m_tcr[ch] & 0x0002 ? "stopped" : "running",
|
||||
m_tcr[ch] & 0x0001 ? "" : " clear");
|
||||
timer_source_names[ch][(m_tcr[ch] >> TCR_CK) & 3],
|
||||
1 << timer_divider[(m_tcr[ch] >> TCR_P) & 15],
|
||||
count_mode[(m_tcr[ch] >> TCR_T) & 3],
|
||||
m_tcr[ch] & TCR_N1 ? "on" : "off",
|
||||
m_tcr[ch] & TCR_INT ? "on" : "off",
|
||||
m_tcr[ch] & TCR_CS ? "stopped" : "running",
|
||||
m_tcr[ch] & TCR_TS ? "" : " clear");
|
||||
else
|
||||
logerror("timer %d clk=%s div=%d mode=%s repeat=%s output=%s max=%s intr=%s %s%s\n",
|
||||
ch,
|
||||
timer_source_names[ch][(m_tcr[ch] >> 14) & 3],
|
||||
timer_divider[(m_tcr[ch] >> 10) & 15],
|
||||
count_mode[(m_tcr[ch] >> 8) & 3],
|
||||
m_tcr[ch] & 0x0080 ? "on" : "off",
|
||||
m_tcr[ch] & 0x0040 ? "invert" : "pulse",
|
||||
max_mode[(m_tcr[ch] >> 4) & 3],
|
||||
m_tcr[ch] & 0x0004 ? "on" : "off",
|
||||
m_tcr[ch] & 0x0002 ? "stopped" : "running",
|
||||
m_tcr[ch] & 0x0001 ? "" : " clear");
|
||||
timer_source_names[ch][(m_tcr[ch] >> TCR_CK) & 3],
|
||||
1 << timer_divider[(m_tcr[ch] >> TCR_P) & 15],
|
||||
count_mode[(m_tcr[ch] >> TCR_T) & 3],
|
||||
m_tcr[ch] & TCR_N1 ? "on" : "off",
|
||||
m_tcr[ch] & TCR_RP ? "invert" : "pulse",
|
||||
max_mode[(m_tcr[ch] >> TCR_MR) & 3],
|
||||
m_tcr[ch] & TCR_INT ? "on" : "off",
|
||||
m_tcr[ch] & TCR_CS ? "stopped" : "running",
|
||||
m_tcr[ch] & TCR_TS ? "" : " clear");
|
||||
|
||||
timer_predict(ch);
|
||||
}
|
||||
|
||||
void tmp68301_device::tmcr1_w(int ch, u16 data, u16 mem_mask)
|
||||
{
|
||||
timer_sync(ch);
|
||||
u16 old = m_tmcr1[ch];
|
||||
COMBINE_DATA(&m_tmcr1[ch]);
|
||||
if(m_tmcr1[ch] == old)
|
||||
return;
|
||||
logerror("timer %d max 1 %04x\n", ch, m_tmcr1[ch]);
|
||||
timer_predict(ch);
|
||||
}
|
||||
|
||||
void tmp68301_device::tmcr2_w(int ch, u16 data, u16 mem_mask)
|
||||
{
|
||||
timer_sync(ch);
|
||||
u16 old = m_tmcr2[ch];
|
||||
COMBINE_DATA(&m_tmcr2[ch]);
|
||||
if(m_tmcr2[ch] == old)
|
||||
return;
|
||||
logerror("timer %d max 2 %04x\n", ch, m_tmcr2[ch]);
|
||||
timer_predict(ch);
|
||||
}
|
||||
|
||||
void tmp68301_device::tctr_w(int ch, u16 data, u16 mem_mask)
|
||||
{
|
||||
timer_sync(ch);
|
||||
logerror("timer %d counter reset\n", ch);
|
||||
if(ch)
|
||||
m_tctr[ch] = 0;
|
||||
timer_predict(ch);
|
||||
}
|
||||
|
||||
void tmp68301_device::timer_update(int ch)
|
||||
{
|
||||
timer_sync(ch);
|
||||
timer_predict(ch);
|
||||
}
|
||||
|
||||
void tmp68301_device::timer_sync(int ch)
|
||||
{
|
||||
if(!(m_tcr[ch] & TCR_TS)) {
|
||||
m_tctr[ch] = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if(m_tcr[ch] & TCR_CS)
|
||||
return;
|
||||
|
||||
u32 div = timer_divider[(m_tcr[ch] >> TCR_P) & 15];
|
||||
u64 ctime = total_cycles();
|
||||
// Don't fold the shifts, the computation would be incorrect
|
||||
u32 ntctr = m_tctr[ch] + ((ctime >> div) - (m_timer_last_sync[ch] >> div));
|
||||
|
||||
u32 maxmode = (m_tcr[ch] >> TCR_MR) & 3;
|
||||
if(maxmode == 1 || maxmode == 2) {
|
||||
u32 max = (maxmode == 1) ? m_tmcr1[ch] : m_tmcr2[ch];
|
||||
if(max == 0)
|
||||
max = 0x10000;
|
||||
if(m_tctr[ch] >= max) {
|
||||
if(ntctr >= 0x10000)
|
||||
ntctr -= 0x10000;
|
||||
else
|
||||
max = 0x10000;
|
||||
}
|
||||
if(ntctr >= max) {
|
||||
if(m_tcr[ch] & TCR_INT)
|
||||
interrupt_internal_trigger(4 + ch);
|
||||
ntctr = ntctr % max;
|
||||
}
|
||||
}
|
||||
m_tctr[ch] = ntctr;
|
||||
m_timer_last_sync[ch] = ctime;
|
||||
}
|
||||
|
||||
void tmp68301_device::timer_predict(int ch)
|
||||
{
|
||||
if(!(m_tcr[ch] & TCR_TS))
|
||||
m_tctr[ch] = 0;
|
||||
|
||||
if((m_tcr[ch] & (TCR_INT|TCR_CS|TCR_TS)) != (TCR_INT|TCR_TS)) {
|
||||
m_timer_next_event[ch] = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
u32 maxmode = (m_tcr[ch] >> TCR_MR) & 3;
|
||||
|
||||
if(maxmode == 0) {
|
||||
logerror("timer %d no max selected ?\n", ch);
|
||||
return;
|
||||
}
|
||||
|
||||
if(maxmode == 3) {
|
||||
logerror("timer %d alternating max mode unsupported\n", ch);
|
||||
return;
|
||||
}
|
||||
|
||||
if((m_tcr[ch] & TCR_N1) == 0) {
|
||||
// Need to add a flag to say "counter done" to make it work, reset on mode change.
|
||||
logerror("timer %d single-shot mode unsupported\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if(((m_tcr[ch] >> TCR_CK) & 3) != 0) {
|
||||
logerror("timer %d source %s unsupported\n", ch, timer_source_names[ch][(m_tcr[ch] >> TCR_CK) & 3]);
|
||||
return;
|
||||
}
|
||||
|
||||
s32 delta = ((maxmode == 1) ? m_tmcr1[ch] : m_tmcr2[ch]) - m_tctr[ch];
|
||||
if(delta <= 0)
|
||||
delta += 0x10000;
|
||||
u32 div = timer_divider[(m_tcr[ch] >> TCR_P) & 15];
|
||||
u64 ctime = total_cycles();
|
||||
m_timer_next_event[ch] = (((ctime >> div) + delta) << div);
|
||||
recompute_bcount(ctime);
|
||||
}
|
||||
|
@ -102,7 +102,7 @@ protected:
|
||||
void ieir_w(u8 data);
|
||||
u8 interrupt_callback();
|
||||
void interrupt_update();
|
||||
void interrupt_internal_trigger(int id);
|
||||
void interrupt_internal_trigger(int vector);
|
||||
std::tuple<u32, u8, u32> interrupt_get_current() const;
|
||||
|
||||
// Parallel interface
|
||||
@ -244,12 +244,29 @@ protected:
|
||||
void scr_w(u8 data);
|
||||
|
||||
// 16-bit timer
|
||||
enum {
|
||||
TCR_CK = 14,
|
||||
TCR_P = 10,
|
||||
TCR_T = 8,
|
||||
TCR_N1 = 0x0080,
|
||||
TCR_RP = 0x0040,
|
||||
TCR_MR = 4,
|
||||
TCR_INT = 0x0004,
|
||||
TCR_CS = 0x0002,
|
||||
TCR_TS = 0x0001
|
||||
};
|
||||
|
||||
u64 m_timer_next_event[3], m_timer_last_sync[3];
|
||||
u16 m_tcr[3], m_tmcr1[3], m_tmcr2[3], m_tctr[3];
|
||||
|
||||
static const int timer_source_id[3][2];
|
||||
static const char *const timer_source_names[3][4];
|
||||
static const int timer_divider[16];
|
||||
|
||||
|
||||
void timer_update(int ch);
|
||||
void timer_sync(int ch);
|
||||
void timer_predict(int ch);
|
||||
|
||||
void tcr_w(int ch, u16 data, u16 mem_mask);
|
||||
void tmcr1_w(int ch, u16 data, u16 mem_mask);
|
||||
void tmcr2_w(int ch, u16 data, u16 mem_mask);
|
||||
|
Loading…
Reference in New Issue
Block a user