i8155: Make timer more efficient by not counting each cycle

This commit is contained in:
AJR 2020-02-02 00:16:03 -05:00
parent b20ba46fc7
commit a9e8be4d67
2 changed files with 70 additions and 52 deletions

View File

@ -109,11 +109,22 @@ enum
// INLINE HELPERS // INLINE HELPERS
//************************************************************************** //**************************************************************************
inline uint8_t i8155_device::get_timer_mode() inline uint8_t i8155_device::get_timer_mode() const
{ {
return (m_count_loaded >> 8) & TIMER_MODE_MASK; return (m_count_loaded >> 8) & TIMER_MODE_MASK;
} }
inline uint16_t i8155_device::get_timer_count() const
{
if (m_timer->enabled())
{
// timer counts down by twos
return std::min((uint16_t(attotime_to_clocks(m_timer->remaining())) + 1) << 1, m_count_loaded & 0x3ffe) | (m_count_even_phase ? 0 : 1);
}
else
return m_count_length;
}
inline void i8155_device::timer_output(int to) inline void i8155_device::timer_output(int to)
{ {
if (to == m_to) if (to == m_to)
@ -128,7 +139,12 @@ inline void i8155_device::timer_output(int to)
inline void i8155_device::timer_stop_count() inline void i8155_device::timer_stop_count()
{ {
// stop counting // stop counting
m_timer->enable(0); if (m_timer->enabled())
{
m_count_loaded = (m_count_loaded & (TIMER_MODE_MASK << 8)) | get_timer_count();
m_timer->enable(false);
}
m_timer_tc->enable(false);
// clear timer output // clear timer output
timer_output(1); timer_output(1);
@ -146,11 +162,10 @@ inline void i8155_device::timer_reload_count()
} }
// begin the odd half of the count, with one extra cycle if count is odd // begin the odd half of the count, with one extra cycle if count is odd
m_counter = (m_count_loaded & 0x3ffe) | 1; m_count_even_phase = false;
m_count_extra = BIT(m_count_loaded, 0);
// set up our timer // set up our timer
m_timer->adjust(attotime::zero, 0, clocks_to_attotime(1)); m_timer->adjust(clocks_to_attotime(((m_count_length & 0x3ffe) >> 1) + (m_count_length & 1)));
timer_output(1); timer_output(1);
switch (get_timer_mode()) switch (get_timer_mode())
@ -271,9 +286,8 @@ i8155_device::i8155_device(const machine_config &mconfig, device_type type, cons
m_status(0), m_status(0),
m_count_length(0), m_count_length(0),
m_count_loaded(0), m_count_loaded(0),
m_counter(0), m_to(0),
m_count_extra(false), m_count_even_phase(false)
m_to(0)
{ {
} }
@ -307,7 +321,8 @@ void i8155_device::device_start()
m_ram = make_unique_clear<uint8_t[]>(256); m_ram = make_unique_clear<uint8_t[]>(256);
// allocate timers // allocate timers
m_timer = timer_alloc(); m_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(i8155_device::timer_half_counted), this));
m_timer_tc = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(i8155_device::timer_tc), this));
// register for state saving // register for state saving
save_item(NAME(m_io_m)); save_item(NAME(m_io_m));
@ -318,8 +333,6 @@ void i8155_device::device_start()
save_pointer(NAME(m_ram), 256); save_pointer(NAME(m_ram), 256);
save_item(NAME(m_count_length)); save_item(NAME(m_count_length));
save_item(NAME(m_count_loaded)); save_item(NAME(m_count_loaded));
save_item(NAME(m_count_extra));
save_item(NAME(m_counter));
save_item(NAME(m_to)); save_item(NAME(m_to));
} }
@ -347,45 +360,15 @@ void i8155_device::device_reset()
//------------------------------------------------- //-------------------------------------------------
// device_timer - handler timer events // timer_half_counted - handler timer events
//------------------------------------------------- //-------------------------------------------------
void i8155_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) TIMER_CALLBACK_MEMBER(i8155_device::timer_half_counted)
{ {
if (m_count_extra) if (m_count_even_phase)
{
m_count_extra = false;
return;
}
// count down by twos
m_counter -= 2;
if (m_counter == 1)
{
LOGMASKED(LOG_TIMER, "Timer count half finished\n");
// reload the even half of the count
m_counter = m_count_loaded & 0x3ffe;
// square wave modes produce a low output in the second half of the counting period
if ((get_timer_mode() & TIMER_MODE_TC_PULSE) == 0)
timer_output(0);
}
else if (m_counter == 2)
{
if ((get_timer_mode() & TIMER_MODE_TC_PULSE) != 0)
{
// pulse low on TC being reached
timer_output(0);
}
// set timer flag
m_status |= STATUS_TIMER;
}
else if (m_counter == 0)
{ {
timer_output(1); timer_output(1);
m_count_even_phase = false;
if ((get_timer_mode() & TIMER_MODE_AUTO_RELOAD) == 0 || (m_command & COMMAND_TM_MASK) == COMMAND_TM_STOP_AFTER_TC) if ((get_timer_mode() & TIMER_MODE_AUTO_RELOAD) == 0 || (m_command & COMMAND_TM_MASK) == COMMAND_TM_STOP_AFTER_TC)
{ {
@ -399,6 +382,37 @@ void i8155_device::device_timer(emu_timer &timer, device_timer_id id, int param,
timer_reload_count(); timer_reload_count();
} }
} }
else
{
LOGMASKED(LOG_TIMER, "Timer count half finished\n");
// reload the even half of the count
m_timer->adjust(clocks_to_attotime((m_count_loaded & 0x3ffe) >> 1));
m_count_even_phase = true;
// square wave modes produce a low output in the second half of the counting period
if ((get_timer_mode() & TIMER_MODE_TC_PULSE) == 0)
timer_output(0);
else
m_timer_tc->adjust(clocks_to_attotime((std::max(m_count_loaded & 0x3ffe, 2) - 2) >> 1));
}
}
//-------------------------------------------------
// timer_tc - generate TC low pulse
//-------------------------------------------------
TIMER_CALLBACK_MEMBER(i8155_device::timer_tc)
{
if ((get_timer_mode() & TIMER_MODE_TC_PULSE) != 0)
{
// pulse low on TC being reached
timer_output(0);
}
// set timer flag
m_status |= STATUS_TIMER;
} }
@ -416,7 +430,8 @@ uint8_t i8155_device::io_r(offs_t offset)
data = m_status; data = m_status;
// clear timer flag // clear timer flag
m_status &= ~STATUS_TIMER; if (!machine().side_effects_disabled())
m_status &= ~STATUS_TIMER;
break; break;
case REGISTER_PORT_A: case REGISTER_PORT_A:
@ -432,11 +447,11 @@ uint8_t i8155_device::io_r(offs_t offset)
break; break;
case REGISTER_TIMER_LOW: case REGISTER_TIMER_LOW:
data = m_counter & 0xff; data = get_timer_count() & 0xff;
break; break;
case REGISTER_TIMER_HIGH: case REGISTER_TIMER_HIGH:
data = (m_counter >> 8 & 0x3f) | get_timer_mode(); data = (get_timer_count() >> 8 & 0x3f) | get_timer_mode();
break; break;
} }

View File

@ -71,7 +71,6 @@ protected:
// device-level overrides // device-level overrides
virtual void device_start() override; virtual void device_start() override;
virtual void device_reset() override; virtual void device_reset() override;
virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override;
private: private:
devcb_read8 m_in_pa_cb; devcb_read8 m_in_pa_cb;
@ -100,16 +99,17 @@ private:
// counter // counter
uint16_t m_count_length; // count length register (assigned) uint16_t m_count_length; // count length register (assigned)
uint16_t m_count_loaded; // count length register (loaded) uint16_t m_count_loaded; // count length register (loaded)
uint16_t m_counter; // counter register
bool m_count_extra; // extra cycle when count is odd
int m_to; // timer output int m_to; // timer output
bool m_count_even_phase;
// timers // timers
emu_timer *m_timer; // counter timer emu_timer *m_timer; // counter timer
emu_timer *m_timer_tc; // counter timer (for TC)
const address_space_config m_space_config; const address_space_config m_space_config;
inline uint8_t get_timer_mode(); inline uint8_t get_timer_mode() const;
inline uint16_t get_timer_count() const;
inline void timer_output(int to); inline void timer_output(int to);
inline void timer_stop_count(); inline void timer_stop_count();
inline void timer_reload_count(); inline void timer_reload_count();
@ -119,6 +119,9 @@ private:
void write_command(uint8_t data); void write_command(uint8_t data);
void register_w(int offset, uint8_t data); void register_w(int offset, uint8_t data);
TIMER_CALLBACK_MEMBER(timer_half_counted);
TIMER_CALLBACK_MEMBER(timer_tc);
}; };