mirror of
https://github.com/holub/mame
synced 2025-10-06 09:00:04 +03:00
mc68340: Timer improvements
- Fix prescaler calculation - Handle various reset behaviors - TOUT changes recorded in status register - Better handling of COM flag and compare register being zero - Reload not immediately after reaching zero but on next cycle
This commit is contained in:
parent
78d3026f86
commit
e72d1130fe
@ -19,6 +19,7 @@
|
||||
#define LOG_READ (1U << 2)
|
||||
#define LOG_TIMER (1U << 3)
|
||||
#define LOG_INT (1U << 4)
|
||||
#define LOG_COUNT (1U << 5)
|
||||
|
||||
//#define VERBOSE (LOG_SETUP|LOG_INT|LOG_TIMER)
|
||||
|
||||
@ -29,6 +30,7 @@
|
||||
#define LOGR(...) LOGMASKED(LOG_READ, __VA_ARGS__)
|
||||
#define LOGTIMER(...) LOGMASKED(LOG_TIMER, __VA_ARGS__)
|
||||
#define LOGINT(...) LOGMASKED(LOG_INT, __VA_ARGS__)
|
||||
#define LOGCOUNT(...) LOGMASKED(LOG_COUNT, __VA_ARGS__)
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define FUNCNAME __func__
|
||||
@ -144,8 +146,7 @@ WRITE16_MEMBER( mc68340_timer_module_device::write )
|
||||
LOGTIMER("- PCLK: Counter uses %s\n", (data & REG_CR_PCLK) ? "prescaler" : "clock");
|
||||
LOGTIMER("- CPE: Counter is %s\n", (data & REG_CR_CPE) ? "enabled" : "disabled");
|
||||
LOGTIMER("- CLK: Clock is %s\n", (data & REG_CR_CLK) ? "TIN (external)" : "system clock / 2");
|
||||
LOGTIMER("- Prescaler: Divide by %d\n", (data & REG_CR_POT_MASK) ? ( 1 << ((data & REG_CR_POT_MASK) >> 5)) : 256);
|
||||
LOGTIMER("- Prescaler: Divide by %d\n", (0x101 << ((data & REG_CR_POT_MASK) >> 5) & 0x1fe));
|
||||
LOGTIMER("- Prescaler: Divide by %d\n", (0x101 << ((data & REG_CR_POT_MASK) >> 5) & 0x1ff) * 2);
|
||||
LOGTIMER("- MODE: %s\n", std::array<char const *, 8>
|
||||
{{ "Input Capture/Output Compare",
|
||||
"Square-Wave Generator - not implemented",
|
||||
@ -162,40 +163,51 @@ WRITE16_MEMBER( mc68340_timer_module_device::write )
|
||||
/* The timer is enabled when the counter prescaler enable (CPE) and SWRx bits in the CR
|
||||
are set. Once enabled, the counter enable (ON) bit in the SR is set, and the next falling
|
||||
edge of the counter clock causes the counter to be loaded with the value in the preload 1
|
||||
register (PREL1). TODO: make sure of the intial load of PREL1 on first falling flank */
|
||||
if (m_cr & REG_CR_SWR)
|
||||
register (PREL1). */
|
||||
if ((m_cr & (REG_CR_SWR | REG_CR_CPE)) == (REG_CR_SWR | REG_CR_CPE))
|
||||
{
|
||||
m_sr &= ~REG_SR_COM;
|
||||
if (m_cr & REG_CR_CPE)
|
||||
if ((m_sr & REG_SR_ON) == 0)
|
||||
{
|
||||
m_sr |= REG_SR_ON; // Starts the counter
|
||||
LOGTIMER("Starts counter %d\n", m_cpu->get_timer_index(this));
|
||||
if ((m_cr & REG_CR_CLK) == 0)
|
||||
{
|
||||
LOGTIMER("- Using system clock/2\n");
|
||||
m_timer->adjust(m_cpu->cycles_to_attotime( (m_cpu->clock() / 2) / (0x101 << ((m_cr & REG_CR_POT_MASK) >> 5) & 0x1fe) * 2) );
|
||||
attotime period = m_cpu->cycles_to_attotime((0x101 << ((m_cr & REG_CR_POT_MASK) >> 5) & 0x1ff) * 2);
|
||||
LOGTIMER("- Using system clock/2: %f Hz\n", period.as_hz());
|
||||
m_timer->adjust(period / 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGTIMER("- Using TIN%d\n", m_cpu->get_timer_index(this));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((m_sr & REG_SR_ON) != 0)
|
||||
{
|
||||
m_sr &= ~REG_SR_ON; // Stops the counter
|
||||
LOGTIMER("Stops counter %d\n", m_cpu->get_timer_index(this));
|
||||
m_timer->adjust(attotime::never);
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // TODO: Detect Disable mode setting line to three state
|
||||
|
||||
if ((m_cr & REG_CR_SWR) == 0)
|
||||
{
|
||||
m_sr = (m_sr & ~REG_SR_COM) | 0x00ff;
|
||||
m_cntr = m_cntr_reg = 0x0000;
|
||||
|
||||
if ((m_cr & REG_CR_OC_MASK) == REG_CR_OC_ONE)
|
||||
{
|
||||
m_tout_out_cb(ASSERT_LINE);
|
||||
tout_set();
|
||||
}
|
||||
else if ((m_cr & REG_CR_OC_MASK) != REG_CR_OC_DISABLED)
|
||||
{
|
||||
tout_clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_tout_out_cb(CLEAR_LINE);
|
||||
// TODO: Disable mode setting line to three state
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -289,8 +301,8 @@ TIMER_CALLBACK_MEMBER(mc68340_timer_module_device::timer_callback)
|
||||
do_timer_tick();
|
||||
if ((m_sr & REG_SR_ON) != 0)
|
||||
{
|
||||
LOGTIMER("Re-arming timer %d using system clock/2 as base: %d Hz\n", m_cpu->get_timer_index(this) + 1, (m_cpu->clock() / 2) / (0x101 << ((m_cr & REG_CR_POT_MASK) >> 5) & 0x1fe));
|
||||
m_timer->adjust(m_cpu->cycles_to_attotime( (m_cpu->clock() / 2) / (0x101 << ((m_cr & REG_CR_POT_MASK) >> 5) & 0x1fe) * 2));
|
||||
//LOGTIMER("Re-arming timer %d using system clock/2 as base\n", m_cpu->get_timer_index(this) + 1);
|
||||
m_timer->adjust(m_cpu->cycles_to_attotime((0x101 << ((m_cr & REG_CR_POT_MASK) >> 5) & 0x1ff) * 2) / 2);
|
||||
}
|
||||
}
|
||||
|
||||
@ -310,12 +322,20 @@ void mc68340_timer_module_device::device_start()
|
||||
|
||||
void mc68340_timer_module_device::device_reset()
|
||||
{
|
||||
m_mcr = REG_MCR_SUPV;
|
||||
m_ir = 0x000f;
|
||||
module_reset();
|
||||
}
|
||||
|
||||
void mc68340_timer_module_device::module_reset()
|
||||
{
|
||||
// TODO
|
||||
m_cr = 0x0000;
|
||||
m_sr = (m_sr & REG_SR_TG) | 0x00ff;
|
||||
m_cntr = m_cntr_reg = 0x0000;
|
||||
m_prel1 = m_prel2 = 0xffff;
|
||||
m_com = 0x0000;
|
||||
m_timer->adjust(attotime::never);
|
||||
m_cpu->update_ipl();
|
||||
}
|
||||
|
||||
void mc68340_timer_module_device::do_timer_irq()
|
||||
@ -341,10 +361,20 @@ void mc68340_timer_module_device::do_timer_tick()
|
||||
if (!((m_cr & REG_CR_MODE_MASK) == REG_CR_MODE_ICOC &&
|
||||
(m_sr & REG_SR_TG) != 0))
|
||||
m_cntr_reg = m_cntr;
|
||||
return;
|
||||
}
|
||||
else // Falling flank
|
||||
|
||||
// Counter decrements on falling flank
|
||||
if (m_cntr == 0)
|
||||
{
|
||||
m_cntr = m_prel1; // TODO: Support prel2 for certain modes
|
||||
LOGCOUNT("Counter reloaded with %04X\n", m_cntr);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_cntr--;
|
||||
LOGCOUNT("Counter decremented to %04X\n", m_cntr);
|
||||
}
|
||||
|
||||
/* TC - Timer Compare Interrupt
|
||||
1 = This bit is set when the counter transitions (off a clock/event falling edge) to the
|
||||
@ -354,13 +384,17 @@ void mc68340_timer_module_device::do_timer_tick()
|
||||
IMB, regardless of the mode of operation. This bit may also be cleared by writing
|
||||
a one to it. Writing a zero to this bit does not alter its contents. This bit is not
|
||||
affected by disabling the timer (SWR = 0).*/
|
||||
if (m_cntr == m_com) // Check COM register
|
||||
if (m_cntr == 0)
|
||||
{
|
||||
LOGINT("Timeout reached\n");
|
||||
m_sr &= ~REG_SR_COM;
|
||||
}
|
||||
else if (m_cntr == m_com) // Check COM register
|
||||
{
|
||||
LOGINT("COM value reached\n");
|
||||
m_sr |= REG_SR_COM;
|
||||
}
|
||||
}
|
||||
|
||||
LOGINT("%s reached\n", m_cntr_reg == 0 ? "Timeout" : "COM value");
|
||||
/* OC1/OC0 - Output Control
|
||||
These bits select the conditions under which TOUTx changes These
|
||||
bits may have a different effect when in the input capture/output compare mode.*/
|
||||
@ -380,16 +414,22 @@ void mc68340_timer_module_device::do_timer_tick()
|
||||
when the counter reaches the value stored in the COM.)*/
|
||||
if ((m_cr & REG_CR_MODE_MASK) == REG_CR_MODE_ICOC) // Detect Input Capture/Output Compare mode
|
||||
{
|
||||
if ((m_sr & REG_SR_COM) != 0) // timer reached compare value?
|
||||
if (m_cntr == m_com) // timer reached compare value?
|
||||
{
|
||||
m_tout_out_cb((m_tout++ & 1) != 0 ? ASSERT_LINE : CLEAR_LINE);
|
||||
if ((m_sr & REG_SR_OUT) != 0)
|
||||
tout_clear();
|
||||
else
|
||||
tout_set();
|
||||
}
|
||||
}
|
||||
else // Any oher mode
|
||||
{
|
||||
if (m_cntr_reg == 0) // Counter reached timeout?
|
||||
if (m_cntr == 0) // Counter reached timeout?
|
||||
{
|
||||
m_tout_out_cb((m_tout++ & 1) != 0 ? ASSERT_LINE : CLEAR_LINE);
|
||||
if ((m_sr & REG_SR_OUT) != 0)
|
||||
tout_clear();
|
||||
else
|
||||
tout_set();
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -402,23 +442,13 @@ void mc68340_timer_module_device::do_timer_tick()
|
||||
immediately set to zero if the timer is disabled (SWR = 0). If the timer is enabled (SWR
|
||||
= 1), TOUTx will be set to zero at timeouts and set to one at timer compare events. If
|
||||
the COM is $0000, TOUTx will be set to zero at the timeout/timer compare event.*/
|
||||
if ((m_cr & REG_CR_MODE_MASK) == REG_CR_MODE_ICOC) // Detect Input Capture/Output Compare mode
|
||||
if (m_cntr == 0) // timer reached timeout value?
|
||||
{
|
||||
if ((m_sr & REG_SR_COM) != 0) // timer reached compare value?
|
||||
{
|
||||
m_tout_out_cb(ASSERT_LINE);
|
||||
tout_clear();
|
||||
}
|
||||
if (m_cntr_reg == 0) // timer reached timeout value?
|
||||
else if ((m_cr & REG_CR_MODE_MASK) == REG_CR_MODE_ICOC && m_cntr == m_com) // timer reached compare value?
|
||||
{
|
||||
m_tout_out_cb(CLEAR_LINE);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_cntr_reg == 0) // timer reached timeout value?
|
||||
{
|
||||
m_tout_out_cb(CLEAR_LINE);
|
||||
}
|
||||
tout_set();
|
||||
}
|
||||
break;
|
||||
case REG_CR_OC_ONE:
|
||||
@ -430,41 +460,20 @@ void mc68340_timer_module_device::do_timer_tick()
|
||||
immediately set to one if the timer is disabled (SWR = 0). If the timer is enabled (SWR =
|
||||
1), TOUTx will be set to one at timeouts and set to zero at timer compare events. If the
|
||||
COM is $0000, TOUTx will be set to one at the timeout/timer compare event.*/
|
||||
if ((m_cr & REG_CR_MODE_MASK) == REG_CR_MODE_ICOC) // Detect Input Capture/Output Compare mode
|
||||
if (m_cntr == 0) // timer reached timeout value?
|
||||
{
|
||||
if ((m_sr & REG_SR_COM) != 0) // timer reached compare value?
|
||||
{
|
||||
m_tout_out_cb(CLEAR_LINE);
|
||||
tout_set();
|
||||
}
|
||||
if (m_cntr_reg == 0) // timer reached timeout value?
|
||||
else if ((m_cr & REG_CR_MODE_MASK) == REG_CR_MODE_ICOC && m_cntr == m_com) // timer reached compare value?
|
||||
{
|
||||
m_tout_out_cb(ASSERT_LINE);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_cntr_reg == 0) // timer reached timeout value?
|
||||
{
|
||||
m_tout_out_cb(ASSERT_LINE);
|
||||
}
|
||||
tout_clear();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LOGTIMER("Wrong TOUT mode, fix the code!\n");
|
||||
}
|
||||
|
||||
if (m_cntr_reg == 0) // timer reached timeout value?
|
||||
{
|
||||
m_sr &= ~REG_SR_COM;
|
||||
m_cntr = m_prel1; // TODO: Support prel2 for certain modes
|
||||
m_sr |= REG_SR_TO;
|
||||
if (m_cr & REG_CR_IE2)
|
||||
{
|
||||
LOGTIMER(" - TO interrupt\n");
|
||||
do_timer_irq();
|
||||
}
|
||||
}
|
||||
if ((m_sr & REG_SR_COM) != 0) // timer reached compare value? )
|
||||
if (m_cntr == m_com) // timer reached compare value? )
|
||||
{
|
||||
m_sr |= REG_SR_TC;
|
||||
if (m_cr & REG_CR_IE0)
|
||||
@ -473,6 +482,35 @@ void mc68340_timer_module_device::do_timer_tick()
|
||||
do_timer_irq();
|
||||
}
|
||||
}
|
||||
if (m_cntr == 0) // timer reached timeout value?
|
||||
{
|
||||
m_sr |= REG_SR_TO;
|
||||
if (m_cr & REG_CR_IE2)
|
||||
{
|
||||
LOGTIMER(" - TO interrupt\n");
|
||||
do_timer_irq();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void mc68340_timer_module_device::tout_set()
|
||||
{
|
||||
if ((m_sr & REG_SR_OUT) == 0)
|
||||
{
|
||||
m_sr |= REG_SR_OUT;
|
||||
LOGTIMER(" - TOUT set\n");
|
||||
m_tout_out_cb(1);
|
||||
}
|
||||
}
|
||||
|
||||
void mc68340_timer_module_device::tout_clear()
|
||||
{
|
||||
if ((m_sr & REG_SR_OUT) != 0)
|
||||
{
|
||||
m_sr &= ~REG_SR_OUT;
|
||||
LOGTIMER(" - TOUT cleared\n");
|
||||
m_tout_out_cb(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -44,7 +44,6 @@ protected:
|
||||
uint16_t m_timer_counter;
|
||||
uint32_t m_tin;
|
||||
uint32_t m_tgate;
|
||||
uint32_t m_tout;
|
||||
emu_timer *m_timer;
|
||||
|
||||
devcb_write_line m_tout_out_cb;
|
||||
@ -52,6 +51,8 @@ protected:
|
||||
devcb_write_line m_tgate_in_cb;
|
||||
void do_timer_irq();
|
||||
void do_timer_tick();
|
||||
void tout_set();
|
||||
void tout_clear();
|
||||
|
||||
TIMER_CALLBACK_MEMBER(timer_callback);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user