i8155: Substantial rewrite of timer emulation

- Modernize logging (nw)
- Abandon "RIOT" name properly belonging to MOS 6532 (nw)
- Provide separate device type for 8156 (nw)

mice.cpp: Eliminate clock rate hack (nw)
This commit is contained in:
AJR 2017-10-22 17:38:49 -04:00
parent 85283a8056
commit 4885c64bbd
3 changed files with 161 additions and 117 deletions

View File

@ -1,8 +1,11 @@
// license:BSD-3-Clause
// copyright-holders:Curt Coder
// copyright-holders:Curt Coder,AJR
/**********************************************************************
Intel 8155/8156 - 2048-Bit Static MOS RAM with I/O Ports and Timer emulation
Intel 8155/8156 - 2048-Bit Static MOS RAM with I/O Ports and Timer
The timer primarily functions as a square-wave generator, but can
also be programmed for a single-cycle low pulse on terminal count.
The only difference between 8155 and 8156 is that pin 8 (CE) is
active low on the former device and active high on the latter.
@ -17,7 +20,8 @@
TODO:
- strobed mode
- ALT 3 and ALT 4 strobed port modes
- optional NVRAM backup for CMOS versions
*/
@ -26,15 +30,18 @@
// device type definitions
DEFINE_DEVICE_TYPE(I8155, i8155_device, "i8155", "Intel 8155 RIOT")
const device_type I8156 = I8155;
DEFINE_DEVICE_TYPE(I8155, i8155_device, "i8155", "Intel 8155 RAM, I/O & Timer")
DEFINE_DEVICE_TYPE(I8156, i8156_device, "i8156", "Intel 8156 RAM, I/O & Timer")
//**************************************************************************
// MACROS / CONSTANTS
//**************************************************************************
#define LOG 0
#define LOG_PORT (1U << 0)
#define LOG_TIMER (1U << 1)
#define VERBOSE (LOG_PORT | LOG_TIMER)
#include "logmacro.h"
enum
{
@ -93,10 +100,8 @@ enum
#define STATUS_TIMER 0x40
#define TIMER_MODE_MASK 0xc0
#define TIMER_MODE_LOW 0x00
#define TIMER_MODE_SQUARE_WAVE 0x40
#define TIMER_MODE_SINGLE_PULSE 0x80
#define TIMER_MODE_AUTOMATIC_RELOAD 0xc0
#define TIMER_MODE_AUTO_RELOAD 0x40
#define TIMER_MODE_TC_PULSE 0x80
@ -117,20 +122,70 @@ ADDRESS_MAP_END
inline uint8_t i8155_device::get_timer_mode()
{
return (m_count_length >> 8) & TIMER_MODE_MASK;
return (m_count_loaded >> 8) & TIMER_MODE_MASK;
}
inline void i8155_device::timer_output()
inline void i8155_device::timer_output(int to)
{
m_out_to_cb(m_to);
if (to == m_to)
return;
if (LOG) logerror("8155 Timer Output: %u\n", m_to);
m_to = to;
m_out_to_cb(to);
LOGMASKED(LOG_TIMER, "Timer output: %u\n", to);
}
inline void i8155_device::pulse_timer_output()
inline void i8155_device::timer_stop_count()
{
m_to = 0; timer_output();
m_to = 1; timer_output();
// stop counting
m_timer->enable(0);
// clear timer output
timer_output(1);
}
inline void i8155_device::timer_reload_count()
{
// valid counts range from 2 to 3FFF
if ((m_count_length & 0x3fff) < 2)
{
timer_stop_count();
return;
}
m_count_loaded = m_count_length;
// begin the odd half of the count, with one extra cycle if count is odd
m_counter = (m_count_loaded & 0x3ffe) | 1;
m_count_extra = BIT(m_count_loaded, 0);
// set up our timer
m_timer->adjust(attotime::zero, 0, clocks_to_attotime(1));
timer_output(1);
switch (get_timer_mode())
{
case 0:
// puts out LOW during second half of count
LOGMASKED(LOG_TIMER, "Timer loaded with %d (Mode: LOW)\n", m_count_loaded & 0x3fff);
break;
case TIMER_MODE_AUTO_RELOAD:
// square wave, i.e. the period of the square wave equals the count length programmed with automatic reload at terminal count
LOGMASKED(LOG_TIMER, "Timer loaded with %d (Mode: Square wave)\n", m_count_loaded & 0x3fff);
break;
case TIMER_MODE_TC_PULSE:
// single pulse upon TC being reached
LOGMASKED(LOG_TIMER, "Timer loaded with %d (Mode: Single pulse)\n", m_count_loaded & 0x3fff);
break;
case TIMER_MODE_TC_PULSE | TIMER_MODE_AUTO_RELOAD:
// automatic reload, i.e. single pulse every time TC is reached
LOGMASKED(LOG_TIMER, "Timer loaded with %d (Mode: Automatic reload)\n", m_count_loaded & 0x3fff);
break;
}
}
inline int i8155_device::get_port_mode(int port)
@ -210,7 +265,12 @@ inline void i8155_device::write_port(int port, uint8_t data)
//-------------------------------------------------
i8155_device::i8155_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: device_t(mconfig, I8155, tag, owner, clock),
: i8155_device(mconfig, I8155, tag, owner, clock)
{
}
i8155_device::i8155_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
: device_t(mconfig, type, tag, owner, clock),
device_memory_interface(mconfig, *this),
m_in_pa_cb(*this),
m_in_pb_cb(*this),
@ -221,11 +281,26 @@ i8155_device::i8155_device(const machine_config &mconfig, const char *tag, devic
m_out_to_cb(*this),
m_command(0),
m_status(0),
m_count_length(0),
m_count_loaded(0),
m_counter(0),
m_count_extra(false),
m_to(0),
m_space_config("ram", ENDIANNESS_LITTLE, 8, 8, 0, nullptr, *ADDRESS_MAP_NAME(i8155))
{
}
//-------------------------------------------------
// i8156_device - constructor
//-------------------------------------------------
i8156_device::i8156_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: i8155_device(mconfig, I8156, tag, owner, clock)
{
}
//-------------------------------------------------
// device_start - device-specific startup
//-------------------------------------------------
@ -251,6 +326,8 @@ void i8155_device::device_start()
save_item(NAME(m_status));
save_item(NAME(m_output));
save_item(NAME(m_count_length));
save_item(NAME(m_count_loaded));
save_item(NAME(m_count_extra));
save_item(NAME(m_counter));
save_item(NAME(m_to));
}
@ -273,12 +350,8 @@ void i8155_device::device_reset()
// clear timer flag
m_status &= ~STATUS_TIMER;
// stop counting
m_timer->enable(0);
// clear timer output
m_to = 1;
timer_output();
// stop timer
timer_stop_count();
}
@ -288,60 +361,52 @@ void i8155_device::device_reset()
void i8155_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
{
// count down
m_counter--;
if (get_timer_mode() == TIMER_MODE_LOW)
if (m_count_extra)
{
// pulse on every count
pulse_timer_output();
m_count_extra = false;
return;
}
if (m_counter == 0)
// count down by twos
m_counter -= 2;
if (m_counter == 1)
{
if (LOG) logerror("8155 Timer Count Reached\n");
LOGMASKED(LOG_TIMER, "Timer count half finished\n");
switch (get_timer_mode())
// 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)
{
case TIMER_MODE_LOW:
case TIMER_MODE_SQUARE_WAVE:
// toggle timer output
m_to = !m_to;
timer_output();
break;
case TIMER_MODE_SINGLE_PULSE:
case TIMER_MODE_AUTOMATIC_RELOAD:
// pulse upon TC being reached
pulse_timer_output();
break;
// pulse low on TC being reached
timer_output(0);
}
// set timer flag
m_status |= STATUS_TIMER;
}
else if (m_counter == 0)
{
timer_output(1);
if ((m_command & COMMAND_TM_MASK) == COMMAND_TM_START)
{
// load new timer counter
m_counter = m_count_length & 0x3fff;
if (LOG) logerror("8155 Timer New Start\n");
}
else if ((m_command & COMMAND_TM_MASK) == COMMAND_TM_STOP_AFTER_TC || get_timer_mode() == TIMER_MODE_SINGLE_PULSE)
if ((get_timer_mode() & TIMER_MODE_AUTO_RELOAD) == 0 || (m_command & COMMAND_TM_MASK) == COMMAND_TM_STOP_AFTER_TC)
{
// stop timer
m_timer->enable(0);
if (LOG) logerror("8155 Timer Stopped\n");
timer_stop_count();
LOGMASKED(LOG_TIMER, "Timer stopped\n");
}
else
{
// automatically reload the counter
m_counter = m_count_length & 0x3fff;
timer_reload_count();
}
// clear timer command
m_command &= ~COMMAND_TM_MASK;
}
}
@ -412,28 +477,28 @@ void i8155_device::register_w(int offset, uint8_t data)
case REGISTER_COMMAND:
m_command = data;
if (LOG) logerror("8155 Port A Mode: %s\n", (data & COMMAND_PA) ? "output" : "input");
if (LOG) logerror("8155 Port B Mode: %s\n", (data & COMMAND_PB) ? "output" : "input");
LOGMASKED(LOG_PORT, "Port A Mode: %s\n", (data & COMMAND_PA) ? "output" : "input");
LOGMASKED(LOG_PORT, "Port B Mode: %s\n", (data & COMMAND_PB) ? "output" : "input");
if (LOG) logerror("8155 Port A Interrupt: %s\n", (data & COMMAND_IEA) ? "enabled" : "disabled");
if (LOG) logerror("8155 Port B Interrupt: %s\n", (data & COMMAND_IEB) ? "enabled" : "disabled");
LOGMASKED(LOG_PORT, "Port A Interrupt: %s\n", (data & COMMAND_IEA) ? "enabled" : "disabled");
LOGMASKED(LOG_PORT, "Port B Interrupt: %s\n", (data & COMMAND_IEB) ? "enabled" : "disabled");
switch (data & COMMAND_PC_MASK)
{
case COMMAND_PC_ALT_1:
if (LOG) logerror("8155 Port C Mode: Alt 1\n");
LOGMASKED(LOG_PORT, "Port C Mode: Alt 1\n");
break;
case COMMAND_PC_ALT_2:
if (LOG) logerror("8155 Port C Mode: Alt 2\n");
LOGMASKED(LOG_PORT, "Port C Mode: Alt 2\n");
break;
case COMMAND_PC_ALT_3:
if (LOG) logerror("8155 Port C Mode: Alt 3\n");
LOGMASKED(LOG_PORT, "Port C Mode: Alt 3\n");
break;
case COMMAND_PC_ALT_4:
if (LOG) logerror("8155 Port C Mode: Alt 4\n");
LOGMASKED(LOG_PORT, "Port C Mode: Alt 4\n");
break;
}
@ -445,19 +510,17 @@ void i8155_device::register_w(int offset, uint8_t data)
case COMMAND_TM_STOP:
// NOP if timer has not started, stop counting if the timer is running
if (LOG) logerror("8155 Timer Command: Stop\n");
m_to = 1;
timer_output();
m_timer->enable(0);
LOGMASKED(LOG_PORT, "Timer Command: Stop\n");
timer_stop_count();
break;
case COMMAND_TM_STOP_AFTER_TC:
// stop immediately after present TC is reached (NOP if timer has not started)
if (LOG) logerror("8155 Timer Command: Stop after TC\n");
LOGMASKED(LOG_PORT, "Timer Command: Stop after TC\n");
break;
case COMMAND_TM_START:
if (LOG) logerror("8155 Timer Command: Start\n");
LOGMASKED(LOG_PORT, "Timer Command: Start\n");
if (m_timer->enabled())
{
@ -466,12 +529,7 @@ void i8155_device::register_w(int offset, uint8_t data)
else
{
// load mode and CNT length and start immediately after loading (if timer is not running)
m_counter = m_count_length & 0x3fff;
if (clock() > 0) // a clock of 0 causes MAME to freeze.
m_timer->adjust(attotime::zero, 0, attotime::from_hz(clock()));
// clear timer command so this won't execute twice
m_command &= ~COMMAND_TM_MASK;
timer_reload_count();
}
break;
}
@ -491,35 +549,10 @@ void i8155_device::register_w(int offset, uint8_t data)
case REGISTER_TIMER_LOW:
m_count_length = (m_count_length & 0xff00) | data;
if (LOG) logerror("8155 Count Length Low: %04x\n", m_count_length);
break;
case REGISTER_TIMER_HIGH:
m_count_length = (data << 8) | (m_count_length & 0xff);
if (LOG) logerror("8155 Count Length High: %04x\n", m_count_length);
switch (data & TIMER_MODE_MASK)
{
case TIMER_MODE_LOW:
// puts out LOW during second half of count
if (LOG) logerror("8155 Timer Mode: LOW\n");
break;
case TIMER_MODE_SQUARE_WAVE:
// square wave, i.e. the period of the square wave equals the count length programmed with automatic reload at terminal count
if (LOG) logerror("8155 Timer Mode: Square wave\n");
break;
case TIMER_MODE_SINGLE_PULSE:
// single pulse upon TC being reached
if (LOG) logerror("8155 Timer Mode: Single pulse\n");
break;
case TIMER_MODE_AUTOMATIC_RELOAD:
// automatic reload, i.e. single pulse every time TC is reached
if (LOG) logerror("8155 Timer Mode: Automatic reload\n");
break;
}
break;
}
}

View File

@ -2,8 +2,7 @@
// copyright-holders:Curt Coder
/**********************************************************************
Intel 8155/8156 - 2048-Bit Static MOS RAM with I/O Ports and Timer emulation
8156 is the same as 8155, except that chip enable is active high instead of low
Intel 8155/8156 - 2048-Bit Static MOS RAM with I/O Ports and Timer
**********************************************************************
_____ _____
@ -95,6 +94,8 @@ public:
DECLARE_WRITE8_MEMBER( write );
protected:
i8155_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock);
// device-level overrides
virtual void device_start() override;
virtual void device_reset() override;
@ -103,8 +104,9 @@ protected:
virtual space_config_vector memory_space_config() const override;
inline uint8_t get_timer_mode();
inline void timer_output();
inline void pulse_timer_output();
inline void timer_output(int to);
inline void timer_stop_count();
inline void timer_reload_count();
inline int get_port_mode(int port);
inline uint8_t read_port(int port);
inline void write_port(int port, uint8_t data);
@ -125,16 +127,18 @@ private:
// CPU interface
int m_io_m; // I/O or memory select
uint8_t m_ad; // address
uint8_t m_ad; // address
// registers
uint8_t m_command; // command register
uint8_t m_status; // status register
uint8_t m_output[3]; // output latches
uint8_t m_command; // command register
uint8_t m_status; // status register
uint8_t m_output[3]; // output latches
// counter
uint16_t m_count_length; // count length register
uint16_t m_counter; // counter register
uint16_t m_count_length; // count length register (assigned)
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
// timers
@ -144,9 +148,16 @@ private:
};
class i8156_device : public i8155_device
{
public:
// construction/destruction
i8156_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
};
// device type definition
DECLARE_DEVICE_TYPE(I8155, i8155_device)
extern const device_type I8156;
DECLARE_DEVICE_TYPE(I8156, i8156_device)
#endif

View File

@ -109,7 +109,7 @@ static MACHINE_CONFIG_START( mice )
MCFG_RS232_CTS_HANDLER(DEVWRITELINE("uart", i8251_device, write_cts))
MCFG_DEVICE_CARD_DEVICE_INPUT_DEFAULTS("terminal", terminal)
MCFG_DEVICE_ADD("rpt", I8155, XTAL_6_144MHz) // divider is likely to be 2; timer emulation appears faulty
MCFG_DEVICE_ADD("rpt", I8155, XTAL_6_144MHz / 2)
MCFG_I8155_IN_PORTC_CB(IOPORT("BAUD"))
MCFG_I8155_OUT_TIMEROUT_CB(DEVWRITELINE("uart", i8251_device, write_txc))
MCFG_DEVCB_CHAIN_OUTPUT(DEVWRITELINE("uart", i8251_device, write_rxc))