summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/machine/mos6530.c
diff options
context:
space:
mode:
author Miodrag Milanovic <mmicko@gmail.com>2015-09-13 08:41:44 +0200
committer Miodrag Milanovic <mmicko@gmail.com>2015-09-13 08:41:44 +0200
commitf88cefad27a1737c76e09d99c9fb43e173506081 (patch)
tree2d8167d03579c46e226471747eb4407bd00ed6fa /src/devices/machine/mos6530.c
parente92ac9e0fa8e99869894bea00589bbb526be30aa (diff)
Move all devices into separate part of src tree (nw)
Diffstat (limited to 'src/devices/machine/mos6530.c')
-rw-r--r--src/devices/machine/mos6530.c382
1 files changed, 382 insertions, 0 deletions
diff --git a/src/devices/machine/mos6530.c b/src/devices/machine/mos6530.c
new file mode 100644
index 00000000000..a32383fcbda
--- /dev/null
+++ b/src/devices/machine/mos6530.c
@@ -0,0 +1,382 @@
+// license:BSD-3-Clause
+// copyright-holders:Curt Coder
+/***************************************************************************
+
+ MIOT 6530 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.
+
+From the operation of the KIM1 it expects the irqflag to be set whenever
+the unit is reset. This is something that is not clear from the datasheet
+and should be verified against real hardware.
+
+***************************************************************************/
+
+#include "emu.h"
+#include "mos6530.h"
+
+
+/***************************************************************************
+ CONSTANTS
+***************************************************************************/
+
+enum
+{
+ TIMER_IDLE,
+ TIMER_COUNTING,
+ TIMER_FINISHING
+};
+
+#define TIMER_FLAG 0x80
+
+/***************************************************************************
+ DEVICE INTERFACE
+***************************************************************************/
+
+const device_type MOS6530 = &device_creator<mos6530_device>;
+
+mos6530_device::mos6530_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, MOS6530, "MOS6530 RRIOT", tag, owner, clock, "mos6530", __FILE__),
+ m_in_pa_cb(*this),
+ m_out_pa_cb(*this),
+ m_in_pb_cb(*this),
+ m_out_pb_cb(*this)
+{
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void mos6530_device::device_start()
+{
+ /* set static values */
+ m_clock = clock();
+
+ /* resolve callbacks */
+ m_in_pa_cb.resolve_safe(0);
+ m_out_pa_cb.resolve_safe();
+ m_in_pb_cb.resolve_safe(0);
+ m_out_pb_cb.resolve_safe();
+
+ /* allocate timers */
+ m_timer = timer_alloc(TIMER_END_CALLBACK);
+
+ /* 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_timershift));
+ save_item(NAME(m_timerstate));
+}
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void mos6530_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 = TIMER_FLAG;
+ update_irqstate();
+
+ /* reset timer states */
+ m_timershift = 0;
+ m_timerstate = TIMER_IDLE;
+ m_timer->adjust(attotime::never);
+}
+
+
+/***************************************************************************
+ TYPE DEFINITIONS
+***************************************************************************/
+
+
+/*-------------------------------------------------
+ update_irqstate - update the IRQ state
+ based on interrupt enables
+-------------------------------------------------*/
+
+void mos6530_device::update_irqstate()
+{
+ UINT8 out = m_port[1].m_out;
+
+ if (m_irqenable)
+ out = ((m_irqstate & TIMER_FLAG) ? 0x00 : 0x80) | (out & 0x7F);
+
+ m_out_pb_cb((offs_t)0, out);
+}
+
+
+/*-------------------------------------------------
+ get_timer - return the current timer value
+-------------------------------------------------*/
+
+UINT8 mos6530_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(m_clock) >> m_timershift;
+
+ /* if finishing, return the number of ticks without the shift */
+ else
+ return m_timer->remaining().as_ticks(m_clock);
+}
+
+
+/***************************************************************************
+ INTERNAL FUNCTIONS
+***************************************************************************/
+
+/*-------------------------------------------------
+ timer_end_callback - callback to process the
+ timer
+-------------------------------------------------*/
+
+void mos6530_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ switch (id)
+ {
+ // deferred reset
+ case TIMER_END_CALLBACK:
+ 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, m_clock));
+
+ /* signal timer IRQ as well */
+ m_irqstate |= TIMER_FLAG;
+ update_irqstate();
+ }
+
+ /* if we finished finishing, switch to the idle state */
+ else if (m_timerstate == TIMER_FINISHING)
+ {
+ m_timerstate = TIMER_IDLE;
+ m_timer->adjust(attotime::never);
+ }
+ break;
+ }
+}
+
+/***************************************************************************
+ I/O ACCESS
+***************************************************************************/
+
+/*-------------------------------------------------
+ mos6530_w - master I/O write access
+-------------------------------------------------*/
+
+WRITE8_MEMBER( mos6530_device::write )
+{
+ /* if A2 == 1, we are writing to the timer */
+ if (offset & 0x04)
+ {
+ static const UINT8 timershift[4] = { 0, 3, 6, 10 };
+ attotime curtime = space.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(m_clock) + 1 + (data << m_timershift);
+ m_timer->adjust(attotime::from_ticks(target, m_clock) - curtime);
+ }
+
+ /* if A2 == 0, we are writing to the I/O section */
+ else
+ {
+ /* A1 selects the port */
+ mos6530_port *port = &m_port[BIT(offset, 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
+ {
+ UINT8 olddata = port->m_out;
+ port->m_out = data;
+
+ if ((offset & 2) && m_irqenable)
+ {
+ olddata = ((m_irqstate & TIMER_FLAG) ? 0x00 : 0x80) | (olddata & 0x7F);
+ data = ((m_irqstate & TIMER_FLAG) ? 0x00 : 0x80) | (data & 0x7F);
+ }
+
+ if (!BIT(offset, 1))
+ m_out_pa_cb((offs_t)0, data);
+ else
+ m_out_pb_cb((offs_t)0, data);
+ }
+ }
+}
+
+
+/*-------------------------------------------------
+ mos6530_r - master I/O read access
+-------------------------------------------------*/
+
+READ8_MEMBER( mos6530_device::read )
+{
+ UINT8 val = 0;
+
+ /* if A2 == 1 and A0 == 1, we are reading interrupt flags */
+ if ((offset & 0x05) == 0x05)
+ {
+ val = m_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 */
+ mos6530_port *port = &m_port[BIT(offset, 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
+ {
+ UINT8 out = port->m_out;
+
+ if ((offset & 2) && m_irqenable)
+ out = ((m_irqstate & TIMER_FLAG) ? 0x00 : 0x80) | (out & 0x7F);
+
+ /* call the input callback if it exists */
+ if (!BIT(offset, 1))
+ port->m_in = m_in_pa_cb(0);
+ else
+ port->m_in = m_in_pb_cb(0);
+
+ /* apply the DDR to the result */
+ val = (out & port->m_ddr) | (port->m_in & ~port->m_ddr);
+ }
+ }
+ return val;
+}
+
+
+/*-------------------------------------------------
+ mos6530_porta_in_set - set port A input
+ value
+-------------------------------------------------*/
+
+void mos6530_device::porta_in_set(UINT8 data, UINT8 mask)
+{
+ m_port[0].m_in = (m_port[0].m_in & ~mask) | (data & mask);
+}
+
+
+/*-------------------------------------------------
+ mos6530_portb_in_set - set port B input
+ value
+-------------------------------------------------*/
+
+void mos6530_device::portb_in_set(UINT8 data, UINT8 mask)
+{
+ m_port[1].m_in = (m_port[1].m_in & ~mask) | (data & mask);
+}
+
+
+/*-------------------------------------------------
+ mos6530_porta_in_get - return port A input
+ value
+-------------------------------------------------*/
+
+UINT8 mos6530_device::porta_in_get()
+{
+ return m_port[0].m_in;
+}
+
+
+/*-------------------------------------------------
+ mos6530_portb_in_get - return port B input
+ value
+-------------------------------------------------*/
+
+UINT8 mos6530_device::portb_in_get()
+{
+ return m_port[1].m_in;
+}
+
+
+/*-------------------------------------------------
+ mos6530_porta_in_get - return port A output
+ value
+-------------------------------------------------*/
+
+UINT8 mos6530_device::porta_out_get()
+{
+ return m_port[0].m_out;
+}
+
+
+/*-------------------------------------------------
+ mos6530_portb_in_get - return port B output
+ value
+-------------------------------------------------*/
+
+UINT8 mos6530_device::portb_out_get()
+{
+ return m_port[1].m_out;
+}