summaryrefslogtreecommitdiffstatshomepage
path: root/trunk/src/emu/machine/6532riot.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/src/emu/machine/6532riot.c')
-rw-r--r--trunk/src/emu/machine/6532riot.c543
1 files changed, 543 insertions, 0 deletions
diff --git a/trunk/src/emu/machine/6532riot.c b/trunk/src/emu/machine/6532riot.c
new file mode 100644
index 00000000000..429acba1631
--- /dev/null
+++ b/trunk/src/emu/machine/6532riot.c
@@ -0,0 +1,543 @@
+/***************************************************************************
+
+ RIOT 6532 emulation
+
+The timer seems to follow these rules:
+- When the timer flag changes from 0 to 1 the timer continues to count
+ down at a 1 cycle rate.
+- When the timer is being read or written the timer flag is reset.
+- When the timer flag is set and the timer contents are 0, the counting
+ stops.
+
+***************************************************************************/
+
+#include "emu.h"
+#include "6532riot.h"
+
+
+//**************************************************************************
+// CONSTANTS
+//**************************************************************************
+
+// device type definition
+const device_type RIOT6532 = &device_creator<riot6532_device>;
+
+enum
+{
+ TIMER_IDLE,
+ TIMER_COUNTING,
+ TIMER_FINISHING
+};
+
+#define TIMER_FLAG 0x80
+#define PA7_FLAG 0x40
+
+
+
+
+/***************************************************************************
+ INTERNAL FUNCTIONS
+***************************************************************************/
+
+/*-------------------------------------------------
+ update_irqstate - update the IRQ state
+ based on interrupt enables
+-------------------------------------------------*/
+
+void riot6532_device::update_irqstate()
+{
+ int irq = (m_irqstate & m_irqenable) ? ASSERT_LINE : CLEAR_LINE;
+
+ if (m_irq != irq)
+ {
+ m_irq_func(irq);
+ m_irq = irq;
+ }
+}
+
+
+/*-------------------------------------------------
+ apply_ddr - combine inputs and outputs
+ according to the DDR
+-------------------------------------------------*/
+
+UINT8 riot6532_device::apply_ddr(const riot6532_port *port)
+{
+ return (port->m_out & port->m_ddr) | (port->m_in & ~port->m_ddr);
+}
+
+
+/*-------------------------------------------------
+ update_pa7_state - see if PA7 has changed
+ and signal appropriately
+-------------------------------------------------*/
+
+void riot6532_device::update_pa7_state()
+{
+ UINT8 data = apply_ddr(&m_port[0]) & 0x80;
+
+ /* if the state changed in the correct direction, set the PA7 flag and update IRQs */
+ if ((m_pa7prev ^ data) && (m_pa7dir ^ data) == 0)
+ {
+ m_irqstate |= PA7_FLAG;
+ update_irqstate();
+ }
+ m_pa7prev = data;
+}
+
+
+/*-------------------------------------------------
+ get_timer - return the current timer value
+-------------------------------------------------*/
+
+UINT8 riot6532_device::get_timer()
+{
+ /* if idle, return 0 */
+ if (m_timerstate == TIMER_IDLE)
+ {
+ return 0;
+ }
+
+ /* if counting, return the number of ticks remaining */
+ else if (m_timerstate == TIMER_COUNTING)
+ {
+ return m_timer->remaining().as_ticks(clock()) >> m_timershift;
+ }
+
+ /* if finishing, return the number of ticks without the shift */
+ else
+ {
+ return m_timer->remaining().as_ticks(clock());
+ }
+}
+
+
+
+/*-------------------------------------------------
+ timer_end_callback - callback to process the
+ timer
+-------------------------------------------------*/
+
+TIMER_CALLBACK( riot6532_device::timer_end_callback )
+{
+ riot6532_device *via = reinterpret_cast<riot6532_device *>(ptr);
+ via->timer_end();
+}
+
+void riot6532_device::timer_end()
+{
+ assert(m_timerstate != TIMER_IDLE);
+
+ /* if we finished counting, switch to the finishing state */
+ if(m_timerstate == TIMER_COUNTING)
+ {
+ m_timerstate = TIMER_FINISHING;
+ m_timer->adjust(attotime::from_ticks(256, clock()));
+
+ /* signal timer IRQ as well */
+ m_irqstate |= TIMER_FLAG;
+ update_irqstate();
+ }
+
+ /* if we finished finishing, keep spinning */
+ else if (m_timerstate == TIMER_FINISHING)
+ {
+ m_timer->adjust(attotime::from_ticks(256, clock()));
+ }
+}
+
+
+
+/***************************************************************************
+ I/O ACCESS
+***************************************************************************/
+
+/*-------------------------------------------------
+ riot6532_w - master I/O write access
+-------------------------------------------------*/
+
+WRITE8_DEVICE_HANDLER( riot6532_w )
+{
+ riot6532_device *via = downcast<riot6532_device *>(device);
+ via->reg_w(offset, data);
+}
+
+WRITE8_MEMBER( riot6532_device::write )
+{
+ reg_w(offset, data);
+}
+
+void riot6532_device::reg_w(UINT8 offset, UINT8 data)
+{
+ /* if A4 == 1 and A2 == 1, we are writing to the timer */
+ if ((offset & 0x14) == 0x14)
+ {
+ static const UINT8 timershift[4] = { 0, 3, 6, 10 };
+ attotime curtime = machine().time();
+ INT64 target;
+
+ /* A0-A1 contain the timer divisor */
+ m_timershift = timershift[offset & 3];
+
+ /* A3 contains the timer IRQ enable */
+ if (offset & 8)
+ m_irqenable |= TIMER_FLAG;
+ else
+ m_irqenable &= ~TIMER_FLAG;
+
+ /* writes here clear the timer flag */
+ if (m_timerstate != TIMER_FINISHING || get_timer() != 0xff)
+ {
+ m_irqstate &= ~TIMER_FLAG;
+ }
+ update_irqstate();
+
+ /* update the timer */
+ m_timerstate = TIMER_COUNTING;
+ target = curtime.as_ticks(clock()) + 1 + (data << m_timershift);
+ m_timer->adjust(attotime::from_ticks(target, clock()) - curtime);
+ }
+
+ /* if A4 == 0 and A2 == 1, we are writing to the edge detect control */
+ else if ((offset & 0x14) == 0x04)
+ {
+ /* A1 contains the A7 IRQ enable */
+ if (offset & 2)
+ {
+ m_irqenable |= PA7_FLAG;
+ }
+ else
+ {
+ m_irqenable &= ~PA7_FLAG;
+ }
+
+ /* A0 specifies the edge detect direction: 0=negative, 1=positive */
+ m_pa7dir = (offset & 1) << 7;
+ }
+
+ /* if A4 == anything and A2 == 0, we are writing to the I/O section */
+ else
+ {
+ /* A1 selects the port */
+ riot6532_port *port = &m_port[(offset >> 1) & 1];
+
+ /* if A0 == 1, we are writing to the port's DDR */
+ if (offset & 1)
+ {
+ port->m_ddr = data;
+ }
+
+ /* if A0 == 0, we are writing to the port's output */
+ else
+ {
+ port->m_out = data;
+ port->m_out_func(0, data);
+ }
+
+ /* writes to port A need to update the PA7 state */
+ if (port == &m_port[0])
+ {
+ update_pa7_state();
+ }
+ }
+}
+
+
+/*-------------------------------------------------
+ riot6532_r - master I/O read access
+-------------------------------------------------*/
+
+READ8_DEVICE_HANDLER( riot6532_r )
+{
+ riot6532_device *via = downcast<riot6532_device *>(device);
+ return via->reg_r(offset);
+}
+
+READ8_MEMBER( riot6532_device::read )
+{
+ return reg_r(offset);
+}
+
+UINT8 riot6532_device::reg_r(UINT8 offset)
+{
+ UINT8 val = 0;
+
+ /* if A2 == 1 and A0 == 1, we are reading interrupt flags */
+ if ((offset & 0x05) == 0x05)
+ {
+ val = m_irqstate;
+
+ /* implicitly clears the PA7 flag */
+ m_irqstate &= ~PA7_FLAG;
+ update_irqstate();
+ }
+
+ /* if A2 == 1 and A0 == 0, we are reading the timer */
+ else if ((offset & 0x05) == 0x04)
+ {
+ val = get_timer();
+
+ /* A3 contains the timer IRQ enable */
+ if (offset & 8)
+ {
+ m_irqenable |= TIMER_FLAG;
+ }
+ else
+ {
+ m_irqenable &= ~TIMER_FLAG;
+ }
+
+ /* implicitly clears the timer flag */
+ if (m_timerstate != TIMER_FINISHING || val != 0xff)
+ {
+ m_irqstate &= ~TIMER_FLAG;
+ }
+ update_irqstate();
+ }
+
+ /* if A2 == 0 and A0 == anything, we are reading from ports */
+ else
+ {
+ /* A1 selects the port */
+ riot6532_port *port = &m_port[(offset >> 1) & 1];
+
+ /* if A0 == 1, we are reading the port's DDR */
+ if (offset & 1)
+ {
+ val = port->m_ddr;
+ }
+
+ /* if A0 == 0, we are reading the port as an input */
+ else
+ {
+ /* call the input callback if it exists */
+ if (!port->m_in_func.isnull())
+ {
+ port->m_in = port->m_in_func(0);
+
+ /* changes to port A need to update the PA7 state */
+ if (port == &m_port[0])
+ {
+ update_pa7_state();
+ }
+ }
+
+ /* apply the DDR to the result */
+ val = apply_ddr(port);
+ }
+ }
+ return val;
+}
+
+
+/*-------------------------------------------------
+ porta_in_set - set port A input value
+-------------------------------------------------*/
+
+void riot6532_porta_in_set(device_t *device, UINT8 data, UINT8 mask)
+{
+ riot6532_device *via = downcast<riot6532_device *>(device);
+ via->porta_in_set(data, mask);
+}
+
+void riot6532_device::porta_in_set(UINT8 data, UINT8 mask)
+{
+ m_port[0].m_in = (m_port[0].m_in & ~mask) | (data & mask);
+ update_pa7_state();
+}
+
+
+/*-------------------------------------------------
+ portb_in_set - set port B input value
+-------------------------------------------------*/
+
+void riot6532_portb_in_set(device_t *device, UINT8 data, UINT8 mask)
+{
+ riot6532_device *via = downcast<riot6532_device *>(device);
+ via->portb_in_set(data, mask);
+}
+
+void riot6532_device::portb_in_set(UINT8 data, UINT8 mask)
+{
+ m_port[1].m_in = (m_port[1].m_in & ~mask) | (data & mask);
+}
+
+
+/*-------------------------------------------------
+ porta_in_get - return port A input value
+-------------------------------------------------*/
+
+UINT8 riot6532_porta_in_get(device_t *device)
+{
+ riot6532_device *via = downcast<riot6532_device *>(device);
+ return via->porta_in_get();
+}
+
+UINT8 riot6532_device::porta_in_get()
+{
+ return m_port[0].m_in;
+}
+
+
+/*-------------------------------------------------
+ portb_in_get - return port B input value
+-------------------------------------------------*/
+
+UINT8 riot6532_portb_in_get(device_t *device)
+{
+ riot6532_device *via = downcast<riot6532_device *>(device);
+ return via->portb_in_get();
+}
+
+UINT8 riot6532_device::portb_in_get()
+{
+ return m_port[1].m_in;
+}
+
+
+/*-------------------------------------------------
+ porta_in_get - return port A output value
+-------------------------------------------------*/
+
+UINT8 riot6532_porta_out_get(device_t *device)
+{
+ riot6532_device *via = downcast<riot6532_device *>(device);
+ return via->porta_out_get();
+}
+
+UINT8 riot6532_device::porta_out_get()
+{
+ return m_port[0].m_out;
+}
+
+
+/*-------------------------------------------------
+ portb_in_get - return port B output value
+-------------------------------------------------*/
+
+UINT8 riot6532_portb_out_get(device_t *device)
+{
+ riot6532_device *via = downcast<riot6532_device *>(device);
+ return via->portb_out_get();
+}
+
+UINT8 riot6532_device::portb_out_get()
+{
+ return m_port[1].m_out;
+}
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// riot6532_device - constructor
+//-------------------------------------------------
+
+riot6532_device::riot6532_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, RIOT6532, "6532 (RIOT)", tag, owner, clock),
+ m_irq(CLEAR_LINE)
+{
+}
+
+
+//-------------------------------------------------
+// device_config_complete - perform any
+// operations now that the configuration is
+// complete
+//-------------------------------------------------
+
+void riot6532_device::device_config_complete()
+{
+ // inherit a copy of the static data
+ const riot6532_interface *intf = reinterpret_cast<const riot6532_interface *>(static_config());
+ if (intf != NULL)
+ {
+ *static_cast<riot6532_interface *>(this) = *intf;
+ }
+
+ // or initialize to defaults if none provided
+ else
+ {
+ memset(&m_in_a_cb, 0, sizeof(m_in_a_cb));
+ memset(&m_in_b_cb, 0, sizeof(m_in_b_cb));
+ memset(&m_out_a_cb, 0, sizeof(m_out_a_cb));
+ memset(&m_out_b_cb, 0, sizeof(m_out_b_cb));
+ memset(&m_irq_cb, 0, sizeof(m_irq_cb));
+ }
+}
+
+
+/*-------------------------------------------------
+ device_start - device-specific startup
+-------------------------------------------------*/
+
+void riot6532_device::device_start()
+{
+ /* validate arguments */
+ assert(this != NULL);
+
+ /* set static values */
+ device_type_iterator<&device_creator<riot6532_device>, riot6532_device> iter(machine().root_device());
+ m_index = iter.indexof(*this);
+
+ /* configure the ports */
+ m_port[0].m_in_func.resolve(m_in_a_cb, *this);
+ m_port[0].m_out_func.resolve(m_out_a_cb, *this);
+ m_port[1].m_in_func.resolve(m_in_b_cb, *this);
+ m_port[1].m_out_func.resolve(m_out_b_cb, *this);
+
+ /* resolve irq func */
+ m_irq_func.resolve(m_irq_cb, *this);
+
+ /* allocate timers */
+ m_timer = machine().scheduler().timer_alloc(FUNC(timer_end_callback), (void *)this);
+
+ /* register for save states */
+ save_item(NAME(m_port[0].m_in));
+ save_item(NAME(m_port[0].m_out));
+ save_item(NAME(m_port[0].m_ddr));
+ save_item(NAME(m_port[1].m_in));
+ save_item(NAME(m_port[1].m_out));
+ save_item(NAME(m_port[1].m_ddr));
+
+ save_item(NAME(m_irqstate));
+ save_item(NAME(m_irqenable));
+ save_item(NAME(m_irq));
+
+ save_item(NAME(m_pa7dir));
+ save_item(NAME(m_pa7prev));
+
+ save_item(NAME(m_timershift));
+ save_item(NAME(m_timerstate));
+}
+
+
+
+/*-------------------------------------------------
+ device_reset - device-specific reset
+-------------------------------------------------*/
+
+void riot6532_device::device_reset()
+{
+ /* reset I/O states */
+ m_port[0].m_out = 0;
+ m_port[0].m_ddr = 0;
+ m_port[1].m_out = 0;
+ m_port[1].m_ddr = 0;
+
+ /* reset IRQ states */
+ m_irqenable = 0;
+ m_irqstate = 0;
+ update_irqstate();
+
+ /* reset PA7 states */
+ m_pa7dir = 0;
+ m_pa7prev = 0;
+
+ /* reset timer states */
+ m_timershift = 0;
+ m_timerstate = TIMER_IDLE;
+ m_timer->adjust(attotime::never);
+}