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_READ (1U << 2)
|
||||||
#define LOG_TIMER (1U << 3)
|
#define LOG_TIMER (1U << 3)
|
||||||
#define LOG_INT (1U << 4)
|
#define LOG_INT (1U << 4)
|
||||||
|
#define LOG_COUNT (1U << 5)
|
||||||
|
|
||||||
//#define VERBOSE (LOG_SETUP|LOG_INT|LOG_TIMER)
|
//#define VERBOSE (LOG_SETUP|LOG_INT|LOG_TIMER)
|
||||||
|
|
||||||
@ -29,6 +30,7 @@
|
|||||||
#define LOGR(...) LOGMASKED(LOG_READ, __VA_ARGS__)
|
#define LOGR(...) LOGMASKED(LOG_READ, __VA_ARGS__)
|
||||||
#define LOGTIMER(...) LOGMASKED(LOG_TIMER, __VA_ARGS__)
|
#define LOGTIMER(...) LOGMASKED(LOG_TIMER, __VA_ARGS__)
|
||||||
#define LOGINT(...) LOGMASKED(LOG_INT, __VA_ARGS__)
|
#define LOGINT(...) LOGMASKED(LOG_INT, __VA_ARGS__)
|
||||||
|
#define LOGCOUNT(...) LOGMASKED(LOG_COUNT, __VA_ARGS__)
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
#define FUNCNAME __func__
|
#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("- PCLK: Counter uses %s\n", (data & REG_CR_PCLK) ? "prescaler" : "clock");
|
||||||
LOGTIMER("- CPE: Counter is %s\n", (data & REG_CR_CPE) ? "enabled" : "disabled");
|
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("- 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) & 0x1ff) * 2);
|
||||||
LOGTIMER("- Prescaler: Divide by %d\n", (0x101 << ((data & REG_CR_POT_MASK) >> 5) & 0x1fe));
|
|
||||||
LOGTIMER("- MODE: %s\n", std::array<char const *, 8>
|
LOGTIMER("- MODE: %s\n", std::array<char const *, 8>
|
||||||
{{ "Input Capture/Output Compare",
|
{{ "Input Capture/Output Compare",
|
||||||
"Square-Wave Generator - not implemented",
|
"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
|
/* 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
|
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
|
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 */
|
register (PREL1). */
|
||||||
if (m_cr & REG_CR_SWR)
|
if ((m_cr & (REG_CR_SWR | REG_CR_CPE)) == (REG_CR_SWR | REG_CR_CPE))
|
||||||
{
|
{
|
||||||
m_sr &= ~REG_SR_COM;
|
if ((m_sr & REG_SR_ON) == 0)
|
||||||
if (m_cr & REG_CR_CPE)
|
|
||||||
{
|
{
|
||||||
m_sr |= REG_SR_ON; // Starts the counter
|
m_sr |= REG_SR_ON; // Starts the counter
|
||||||
LOGTIMER("Starts counter %d\n", m_cpu->get_timer_index(this));
|
LOGTIMER("Starts counter %d\n", m_cpu->get_timer_index(this));
|
||||||
if ((m_cr & REG_CR_CLK) == 0)
|
if ((m_cr & REG_CR_CLK) == 0)
|
||||||
{
|
{
|
||||||
LOGTIMER("- Using system clock/2\n");
|
attotime period = m_cpu->cycles_to_attotime((0x101 << ((m_cr & REG_CR_POT_MASK) >> 5) & 0x1ff) * 2);
|
||||||
m_timer->adjust(m_cpu->cycles_to_attotime( (m_cpu->clock() / 2) / (0x101 << ((m_cr & REG_CR_POT_MASK) >> 5) & 0x1fe) * 2) );
|
LOGTIMER("- Using system clock/2: %f Hz\n", period.as_hz());
|
||||||
|
m_timer->adjust(period / 2);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LOGTIMER("- Using TIN%d\n", m_cpu->get_timer_index(this));
|
LOGTIMER("- Using TIN%d\n", m_cpu->get_timer_index(this));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ((m_sr & REG_SR_ON) != 0)
|
||||||
{
|
{
|
||||||
m_sr &= ~REG_SR_ON; // Stops the counter
|
m_sr &= ~REG_SR_ON; // Stops the counter
|
||||||
LOGTIMER("Stops counter %d\n", m_cpu->get_timer_index(this));
|
LOGTIMER("Stops counter %d\n", m_cpu->get_timer_index(this));
|
||||||
m_timer->adjust(attotime::never);
|
m_timer->adjust(attotime::never);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else
|
if ((m_cr & REG_CR_SWR) == 0)
|
||||||
{ // TODO: Detect Disable mode setting line to three state
|
|
||||||
if ((m_cr & REG_CR_OC_MASK) == REG_CR_OC_ONE)
|
|
||||||
{
|
{
|
||||||
m_tout_out_cb(ASSERT_LINE);
|
m_sr = (m_sr & ~REG_SR_COM) | 0x00ff;
|
||||||
}
|
m_cntr = m_cntr_reg = 0x0000;
|
||||||
else
|
|
||||||
{
|
if ((m_cr & REG_CR_OC_MASK) == REG_CR_OC_ONE)
|
||||||
m_tout_out_cb(CLEAR_LINE);
|
{
|
||||||
|
tout_set();
|
||||||
|
}
|
||||||
|
else if ((m_cr & REG_CR_OC_MASK) != REG_CR_OC_DISABLED)
|
||||||
|
{
|
||||||
|
tout_clear();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// TODO: Disable mode setting line to three state
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -289,8 +301,8 @@ TIMER_CALLBACK_MEMBER(mc68340_timer_module_device::timer_callback)
|
|||||||
do_timer_tick();
|
do_timer_tick();
|
||||||
if ((m_sr & REG_SR_ON) != 0)
|
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));
|
//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( (m_cpu->clock() / 2) / (0x101 << ((m_cr & REG_CR_POT_MASK) >> 5) & 0x1fe) * 2));
|
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()
|
void mc68340_timer_module_device::device_reset()
|
||||||
{
|
{
|
||||||
|
m_mcr = REG_MCR_SUPV;
|
||||||
|
m_ir = 0x000f;
|
||||||
module_reset();
|
module_reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void mc68340_timer_module_device::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()
|
void mc68340_timer_module_device::do_timer_irq()
|
||||||
@ -341,26 +361,40 @@ void mc68340_timer_module_device::do_timer_tick()
|
|||||||
if (!((m_cr & REG_CR_MODE_MASK) == REG_CR_MODE_ICOC &&
|
if (!((m_cr & REG_CR_MODE_MASK) == REG_CR_MODE_ICOC &&
|
||||||
(m_sr & REG_SR_TG) != 0))
|
(m_sr & REG_SR_TG) != 0))
|
||||||
m_cntr_reg = m_cntr;
|
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--;
|
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
|
|
||||||
value in the COM. This bit does not affect the programmed IRQ signal if the IE0
|
/* TC - Timer Compare Interrupt
|
||||||
bit in the CR is cleared.
|
1 = This bit is set when the counter transitions (off a clock/event falling edge) to the
|
||||||
0 = This bit is cleared by the timer whenever the RESET signal is asserted on the
|
value in the COM. This bit does not affect the programmed IRQ signal if the IE0
|
||||||
IMB, regardless of the mode of operation. This bit may also be cleared by writing
|
bit in the CR is cleared.
|
||||||
a one to it. Writing a zero to this bit does not alter its contents. This bit is not
|
0 = This bit is cleared by the timer whenever the RESET signal is asserted on the
|
||||||
affected by disabling the timer (SWR = 0).*/
|
IMB, regardless of the mode of operation. This bit may also be cleared by writing
|
||||||
if (m_cntr == m_com) // Check COM register
|
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).*/
|
||||||
m_sr |= REG_SR_COM;
|
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
|
/* OC1/OC0 - Output Control
|
||||||
These bits select the conditions under which TOUTx changes These
|
These bits select the conditions under which TOUTx changes These
|
||||||
bits may have a different effect when in the input capture/output compare mode.*/
|
bits may have a different effect when in the input capture/output compare mode.*/
|
||||||
@ -378,18 +412,24 @@ void mc68340_timer_module_device::do_timer_tick()
|
|||||||
TOUTx is immediately set to zero if the timer is disabled (SWR = 0). If the timer is
|
TOUTx is immediately set to zero if the timer is disabled (SWR = 0). If the timer is
|
||||||
enabled (SWR = 1), timer compare events toggle TOUTx. (Timer compare events occur
|
enabled (SWR = 1), timer compare events toggle TOUTx. (Timer compare events occur
|
||||||
when the counter reaches the value stored in the COM.)*/
|
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_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
|
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;
|
break;
|
||||||
@ -402,69 +442,38 @@ 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
|
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
|
= 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.*/
|
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?
|
tout_clear();
|
||||||
{
|
|
||||||
m_tout_out_cb(ASSERT_LINE);
|
|
||||||
}
|
|
||||||
if (m_cntr_reg == 0) // timer reached timeout value?
|
|
||||||
{
|
|
||||||
m_tout_out_cb(CLEAR_LINE);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else if ((m_cr & REG_CR_MODE_MASK) == REG_CR_MODE_ICOC && m_cntr == m_com) // timer reached compare value?
|
||||||
{
|
{
|
||||||
if (m_cntr_reg == 0) // timer reached timeout value?
|
tout_set();
|
||||||
{
|
|
||||||
m_tout_out_cb(CLEAR_LINE);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case REG_CR_OC_ONE:
|
case REG_CR_OC_ONE:
|
||||||
/* One Mode - If the timer is disabled (SWR = 0) when this encoding is programmed,
|
/* One Mode - If the timer is disabled (SWR = 0) when this encoding is programmed,
|
||||||
TOUTx is immediately set to one. If the timer is enabled (SWR = 1), TOUTx will be set
|
TOUTx is immediately set to one. If the timer is enabled (SWR = 1), TOUTx will be set
|
||||||
to one at the next timeout.
|
to one at the next timeout.
|
||||||
|
|
||||||
In the input capture/output compare mode, TOUTx is
|
In the input capture/output compare mode, TOUTx is
|
||||||
immediately set to one if the timer is disabled (SWR = 0). If the timer is enabled (SWR =
|
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
|
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.*/
|
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?
|
tout_set();
|
||||||
{
|
|
||||||
m_tout_out_cb(CLEAR_LINE);
|
|
||||||
}
|
|
||||||
if (m_cntr_reg == 0) // timer reached timeout value?
|
|
||||||
{
|
|
||||||
m_tout_out_cb(ASSERT_LINE);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else if ((m_cr & REG_CR_MODE_MASK) == REG_CR_MODE_ICOC && m_cntr == m_com) // timer reached compare value?
|
||||||
{
|
{
|
||||||
if (m_cntr_reg == 0) // timer reached timeout value?
|
tout_clear();
|
||||||
{
|
|
||||||
m_tout_out_cb(ASSERT_LINE);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
LOGTIMER("Wrong TOUT mode, fix the code!\n");
|
LOGTIMER("Wrong TOUT mode, fix the code!\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_cntr_reg == 0) // timer reached timeout value?
|
if (m_cntr == m_com) // timer reached compare 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? )
|
|
||||||
{
|
{
|
||||||
m_sr |= REG_SR_TC;
|
m_sr |= REG_SR_TC;
|
||||||
if (m_cr & REG_CR_IE0)
|
if (m_cr & REG_CR_IE0)
|
||||||
@ -473,6 +482,35 @@ void mc68340_timer_module_device::do_timer_tick()
|
|||||||
do_timer_irq();
|
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;
|
uint16_t m_timer_counter;
|
||||||
uint32_t m_tin;
|
uint32_t m_tin;
|
||||||
uint32_t m_tgate;
|
uint32_t m_tgate;
|
||||||
uint32_t m_tout;
|
|
||||||
emu_timer *m_timer;
|
emu_timer *m_timer;
|
||||||
|
|
||||||
devcb_write_line m_tout_out_cb;
|
devcb_write_line m_tout_out_cb;
|
||||||
@ -52,6 +51,8 @@ protected:
|
|||||||
devcb_write_line m_tgate_in_cb;
|
devcb_write_line m_tgate_in_cb;
|
||||||
void do_timer_irq();
|
void do_timer_irq();
|
||||||
void do_timer_tick();
|
void do_timer_tick();
|
||||||
|
void tout_set();
|
||||||
|
void tout_clear();
|
||||||
|
|
||||||
TIMER_CALLBACK_MEMBER(timer_callback);
|
TIMER_CALLBACK_MEMBER(timer_callback);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user