summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/machine/6821pia.c
diff options
context:
space:
mode:
author Aaron Giles <aaron@aarongiles.com>2009-03-03 06:58:07 +0000
committer Aaron Giles <aaron@aarongiles.com>2009-03-03 06:58:07 +0000
commit8dd5d2b9531c74b779f17ff38fed33fef8008b55 (patch)
tree723efc795ce5473360cc27fc3220b1c3c65980a9 /src/emu/machine/6821pia.c
parent9fbb1e6d7b85b310b9a379f6c05631d56cc75a47 (diff)
Renamed 6821new.* -> 6821pia.*
Renamed pia_* functions to pia6821_* Fixed a couple of compiler errors.
Diffstat (limited to 'src/emu/machine/6821pia.c')
-rw-r--r--src/emu/machine/6821pia.c1373
1 files changed, 1373 insertions, 0 deletions
diff --git a/src/emu/machine/6821pia.c b/src/emu/machine/6821pia.c
new file mode 100644
index 00000000000..8d0ece042a4
--- /dev/null
+++ b/src/emu/machine/6821pia.c
@@ -0,0 +1,1373 @@
+/**********************************************************************
+
+ Motorola 6821 PIA interface and emulation
+
+**********************************************************************/
+
+#include "6821pia.h"
+
+
+/***************************************************************************
+ TYPE DEFINITIONS
+***************************************************************************/
+
+typedef struct _pia6821 pia6821;
+struct _pia6821
+{
+ devcb_resolved_read8 in_a_func;
+ devcb_resolved_read8 in_b_func;
+ devcb_resolved_read_line in_ca1_func;
+ devcb_resolved_read_line in_cb1_func;
+ devcb_resolved_read_line in_ca2_func;
+ devcb_resolved_read_line in_cb2_func;
+ devcb_resolved_write8 out_a_func;
+ devcb_resolved_write8 out_b_func;
+ devcb_resolved_write_line out_ca2_func;
+ devcb_resolved_write_line out_cb2_func;
+ devcb_resolved_write_line irq_a_func;
+ devcb_resolved_write_line irq_b_func;
+
+ UINT8 in_a;
+ UINT8 in_ca1;
+ UINT8 in_ca2;
+ UINT8 out_a;
+ UINT8 out_ca2;
+ UINT8 port_a_z_mask;
+ UINT8 ddr_a;
+ UINT8 ctl_a;
+ UINT8 irq_a1;
+ UINT8 irq_a2;
+ UINT8 irq_a_state;
+
+ UINT8 in_b;
+ UINT8 in_cb1;
+ UINT8 in_cb2;
+ UINT8 out_b;
+ UINT8 out_cb2;
+ UINT8 last_out_cb2_z;
+ UINT8 ddr_b;
+ UINT8 ctl_b;
+ UINT8 irq_b1;
+ UINT8 irq_b2;
+ UINT8 irq_b_state;
+
+ /* variables that indicate if access a line externally -
+ used to for logging purposes ONLY */
+ UINT8 in_a_pushed;
+ UINT8 out_a_needs_pulled;
+ UINT8 in_ca1_pushed;
+ UINT8 in_ca2_pushed;
+ UINT8 out_ca2_needs_pulled;
+ UINT8 in_b_pushed;
+ UINT8 out_b_needs_pulled;
+ UINT8 in_cb1_pushed;
+ UINT8 in_cb2_pushed;
+ UINT8 out_cb2_needs_pulled;
+ UINT8 logged_port_a_not_connected;
+ UINT8 logged_port_b_not_connected;
+ UINT8 logged_ca1_not_connected;
+ UINT8 logged_ca2_not_connected;
+ UINT8 logged_cb1_not_connected;
+ UINT8 logged_cb2_not_connected;
+};
+
+
+/***************************************************************************
+ MACROS
+***************************************************************************/
+
+#define VERBOSE 0
+
+#define LOG(x) do { if (VERBOSE) logerror x; } while (0)
+
+#define PIA_IRQ1 (0x80)
+#define PIA_IRQ2 (0x40)
+
+#define IRQ1_ENABLED(c) ( (((c) >> 0) & 0x01))
+#define C1_LOW_TO_HIGH(c) ( (((c) >> 1) & 0x01))
+#define C1_HIGH_TO_LOW(c) (!(((c) >> 1) & 0x01))
+#define OUTPUT_SELECTED(c) ( (((c) >> 2) & 0x01))
+#define IRQ2_ENABLED(c) ( (((c) >> 3) & 0x01))
+#define STROBE_E_RESET(c) ( (((c) >> 3) & 0x01))
+#define STROBE_C1_RESET(c) (!(((c) >> 3) & 0x01))
+#define C2_SET(c) ( (((c) >> 3) & 0x01))
+#define C2_LOW_TO_HIGH(c) ( (((c) >> 4) & 0x01))
+#define C2_HIGH_TO_LOW(c) (!(((c) >> 4) & 0x01))
+#define C2_SET_MODE(c) ( (((c) >> 4) & 0x01))
+#define C2_STROBE_MODE(c) (!(((c) >> 4) & 0x01))
+#define C2_OUTPUT(c) ( (((c) >> 5) & 0x01))
+#define C2_INPUT(c) (!(((c) >> 5) & 0x01))
+
+
+/***************************************************************************
+ PROTOTYPES
+***************************************************************************/
+/***************************************************************************
+ INLINE FUNCTIONS
+***************************************************************************/
+
+INLINE pia6821 *get_token(const device_config *device)
+{
+ assert(device != NULL);
+ assert((device->type == PIA6821) || (device->type == PIA6822));
+ return (pia6821 *) device->token;
+}
+
+
+INLINE const pia6821_interface *get_interface(const device_config *device)
+{
+ assert(device != NULL);
+ assert((device->type == PIA6821) || (device->type == PIA6822));
+ return (const pia6821_interface *) device->static_config;
+}
+
+
+/***************************************************************************
+ IMPLEMENTATION
+***************************************************************************/
+
+/*-------------------------------------------------
+ DEVICE_START( pia )
+-------------------------------------------------*/
+
+static DEVICE_START( pia )
+{
+ pia6821 *p = get_token(device);
+ const pia6821_interface *intf = get_interface(device);
+
+ /* clear structure */
+ memset(p, 0, sizeof(*p));
+
+ /* resolve callbacks */
+ devcb_resolve_read8(&p->in_a_func, &intf->in_a_func, device);
+ devcb_resolve_read8(&p->in_b_func, &intf->in_b_func, device);
+ devcb_resolve_read_line(&p->in_ca1_func, &intf->in_ca1_func, device);
+ devcb_resolve_read_line(&p->in_cb1_func, &intf->in_cb1_func, device);
+ devcb_resolve_read_line(&p->in_ca2_func, &intf->in_ca2_func, device);
+ devcb_resolve_read_line(&p->in_cb2_func, &intf->in_cb2_func, device);
+ devcb_resolve_write8(&p->out_a_func, &intf->out_a_func, device);
+ devcb_resolve_write8(&p->out_b_func, &intf->out_b_func, device);
+ devcb_resolve_write_line(&p->out_ca2_func, &intf->out_ca2_func, device);
+ devcb_resolve_write_line(&p->out_cb2_func, &intf->out_cb2_func, device);
+ devcb_resolve_write_line(&p->irq_a_func, &intf->irq_a_func, device);
+ devcb_resolve_write_line(&p->irq_b_func, &intf->irq_b_func, device);
+
+ state_save_register_device_item(device, 0, p->in_a);
+ state_save_register_device_item(device, 0, p->in_ca1);
+ state_save_register_device_item(device, 0, p->in_ca2);
+ state_save_register_device_item(device, 0, p->out_a);
+ state_save_register_device_item(device, 0, p->out_ca2);
+ state_save_register_device_item(device, 0, p->port_a_z_mask);
+ state_save_register_device_item(device, 0, p->ddr_a);
+ state_save_register_device_item(device, 0, p->ctl_a);
+ state_save_register_device_item(device, 0, p->irq_a1);
+ state_save_register_device_item(device, 0, p->irq_a2);
+ state_save_register_device_item(device, 0, p->irq_a_state);
+ state_save_register_device_item(device, 0, p->in_b);
+ state_save_register_device_item(device, 0, p->in_cb1);
+ state_save_register_device_item(device, 0, p->in_cb2);
+ state_save_register_device_item(device, 0, p->out_b);
+ state_save_register_device_item(device, 0, p->out_cb2);
+ state_save_register_device_item(device, 0, p->last_out_cb2_z);
+ state_save_register_device_item(device, 0, p->ddr_b);
+ state_save_register_device_item(device, 0, p->ctl_b);
+ state_save_register_device_item(device, 0, p->irq_b1);
+ state_save_register_device_item(device, 0, p->irq_b2);
+ state_save_register_device_item(device, 0, p->irq_b_state);
+ state_save_register_device_item(device, 0, p->in_a_pushed);
+ state_save_register_device_item(device, 0, p->out_a_needs_pulled);
+ state_save_register_device_item(device, 0, p->in_ca1_pushed);
+ state_save_register_device_item(device, 0, p->in_ca2_pushed);
+ state_save_register_device_item(device, 0, p->out_ca2_needs_pulled);
+ state_save_register_device_item(device, 0, p->in_b_pushed);
+ state_save_register_device_item(device, 0, p->out_b_needs_pulled);
+ state_save_register_device_item(device, 0, p->in_cb1_pushed);
+ state_save_register_device_item(device, 0, p->in_cb2_pushed);
+ state_save_register_device_item(device, 0, p->out_cb2_needs_pulled);
+}
+
+
+/*-------------------------------------------------
+ DEVICE_RESET( pia )
+-------------------------------------------------*/
+
+static DEVICE_RESET( pia )
+{
+ pia6821 *p = get_token(device);
+
+ /*
+ * set default read values.
+ *
+ * ports A,CA1,CA2 default to 1
+ * ports B,CB1,CB2 are three-state and undefined (set to 0)
+ */
+ p->in_a = 0xff;
+ p->in_ca1 = TRUE;
+ p->in_ca2 = TRUE;
+ p->out_a = 0;
+ p->out_ca2 = 0;
+ p->port_a_z_mask = 0;
+ p->ddr_a = 0;
+ p->ctl_a = 0;
+ p->irq_a1 = 0;
+ p->irq_a2 = 0;
+ p->irq_a_state = 0;
+ p->in_b = 0;
+ p->in_cb1 = 0;
+ p->in_cb2 = 0;
+ p->out_b = 0;
+ p->out_cb2 = 0;
+ p->last_out_cb2_z = 0;
+ p->ddr_b = 0;
+ p->ctl_b = 0;
+ p->irq_b1 = 0;
+ p->irq_b2 = 0;
+ p->irq_b_state = 0;
+ p->in_a_pushed = 0;
+ p->out_a_needs_pulled = 0;
+ p->in_ca1_pushed = 0;
+ p->in_ca2_pushed = 0;
+ p->out_ca2_needs_pulled = 0;
+ p->in_b_pushed = 0;
+ p->out_b_needs_pulled = 0;
+ p->in_cb1_pushed = 0;
+ p->in_cb2_pushed = 0;
+ p->out_cb2_needs_pulled = 0;
+ p->logged_port_a_not_connected = 0;
+ p->logged_port_b_not_connected = 0;
+ p->logged_ca1_not_connected = 0;
+ p->logged_ca2_not_connected = 0;
+ p->logged_cb1_not_connected = 0;
+ p->logged_cb2_not_connected = 0;
+
+
+ /* clear the IRQs */
+ devcb_call_write_line(&p->irq_a_func, FALSE);
+ devcb_call_write_line(&p->irq_b_func, FALSE);
+}
+
+
+/*-------------------------------------------------
+ update_interrupts
+-------------------------------------------------*/
+
+static void update_interrupts(const device_config *device)
+{
+ pia6821 *p = get_token(device);
+ int new_state;
+
+ /* start with IRQ A */
+ new_state = (p->irq_a1 && IRQ1_ENABLED(p->ctl_a)) || (p->irq_a2 && IRQ2_ENABLED(p->ctl_a));
+
+ if (new_state != p->irq_a_state)
+ {
+ p->irq_a_state = new_state;
+ devcb_call_write_line(&p->irq_a_func, p->irq_a_state);
+ }
+
+ /* then do IRQ B */
+ new_state = (p->irq_b1 && IRQ1_ENABLED(p->ctl_b)) || (p->irq_b2 && IRQ2_ENABLED(p->ctl_b));
+
+ if (new_state != p->irq_b_state)
+ {
+ p->irq_b_state = new_state;
+ devcb_call_write_line(&p->irq_b_func, p->irq_b_state);
+ }
+}
+
+
+/*-------------------------------------------------
+ get_in_a_value
+-------------------------------------------------*/
+
+static UINT8 get_in_a_value(const device_config *device)
+{
+ pia6821 *p = get_token(device);
+ UINT8 port_a_data = 0;
+ UINT8 ret;
+
+ /* update the input */
+ if (p->in_a_func.read != NULL)
+ port_a_data = devcb_call_read8(&p->in_a_func, 0);
+ else
+ {
+ if (p->in_a_pushed)
+ port_a_data = p->in_a;
+ else
+ {
+ /* mark all pins disconnected */
+ p->port_a_z_mask = 0xff;
+
+ if (!p->logged_port_a_not_connected && (p->ddr_a != 0xff))
+ {
+ logerror("PIA #%s: Warning! No port A read handler. Assuming pins 0x%02X not connected\n", device->tag, p->ddr_a ^ 0xff);
+ p->logged_port_a_not_connected = TRUE;
+ }
+ }
+ }
+
+ /* - connected pins are always read
+ - disconnected pins read the output buffer in output mode
+ - disconnected pins are HI in input mode */
+ ret = (~p->port_a_z_mask & port_a_data) |
+ ( p->port_a_z_mask & p->ddr_a & p->out_a) |
+ ( p->port_a_z_mask & ~p->ddr_a);
+
+ return ret;
+}
+
+
+/*-------------------------------------------------
+ get_in_b_value
+-------------------------------------------------*/
+
+static UINT8 get_in_b_value(const device_config *device)
+{
+ pia6821 *p = get_token(device);
+ UINT8 ret;
+
+ if (p->ddr_b == 0xff)
+ /* all output, just return buffer */
+ ret = p->out_b;
+ else
+ {
+ UINT8 port_b_data;
+
+ /* update the input */
+ if (p->in_b_func.read != NULL)
+ port_b_data = devcb_call_read8(&p->in_b_func, 0);
+ else
+ {
+ if (p->in_b_pushed)
+ port_b_data = p->in_b;
+ else
+ {
+ if (!p->logged_port_b_not_connected && (p->ddr_b != 0xff))
+ {
+ logerror("PIA #%s: Error! No port B read handler. Three-state pins 0x%02X are undefined\n", device->tag, p->ddr_b ^ 0xff);
+ p->logged_port_b_not_connected = TRUE;
+ }
+
+ /* undefined -- need to return something */
+ port_b_data = 0x00;
+ }
+ }
+
+ /* the DDR determines if the pin or the output buffer is read */
+ ret = (p->out_b & p->ddr_b) | (port_b_data & ~p->ddr_b);
+ }
+
+ return ret;
+}
+
+
+/*-------------------------------------------------
+ get_out_a_value
+-------------------------------------------------*/
+
+static UINT8 get_out_a_value(const device_config *device)
+{
+ pia6821 *p = get_token(device);
+ UINT8 ret;
+
+ if (p->ddr_a == 0xff)
+ /* all output */
+ ret = p->out_a;
+ else
+ /* input pins don't change */
+ ret = (p->out_a & p->ddr_a) | (get_in_a_value(device) & ~p->ddr_a);
+
+ return ret;
+}
+
+
+/*-------------------------------------------------
+ get_out_b_value
+-------------------------------------------------*/
+
+static UINT8 get_out_b_value(const device_config *device)
+{
+ pia6821 *p = get_token(device);
+
+ /* input pins are high-impedance - we just send them as zeros for backwards compatibility */
+ return p->out_b & p->ddr_b;
+}
+
+
+/*-------------------------------------------------
+ set_out_ca2
+-------------------------------------------------*/
+
+static void set_out_ca2(const device_config *device, int data)
+{
+ pia6821 *p = get_token(device);
+
+ if (data != p->out_ca2)
+ {
+ p->out_ca2 = data;
+
+ /* send to output function */
+ if (p->out_ca2_func.write)
+ devcb_call_write_line(&p->out_ca2_func, p->out_ca2);
+ else
+ {
+ if (p->out_ca2_needs_pulled)
+ logerror("PIA #%s: Warning! No port CA2 write handler. Previous value has been lost!\n", device->tag);
+
+ p->out_ca2_needs_pulled = TRUE;
+ }
+ }
+}
+
+
+/*-------------------------------------------------
+ set_out_cb2
+-------------------------------------------------*/
+
+static void set_out_cb2(const device_config *device, int data)
+{
+ pia6821 *p = get_token(device);
+
+ int z = pia6821_get_output_cb2_z(device);
+
+ if ((data != p->out_cb2) || (z != p->last_out_cb2_z))
+ {
+ p->out_cb2 = data;
+ p->last_out_cb2_z = z;
+
+ /* send to output function */
+ if (p->out_cb2_func.write)
+ devcb_call_write_line(&p->out_cb2_func, p->out_cb2);
+ else
+ {
+ if (p->out_cb2_needs_pulled)
+ logerror("PIA #%s: Warning! No port CB2 write handler. Previous value has been lost!\n", device->tag);
+
+ p->out_cb2_needs_pulled = TRUE;
+ }
+ }
+}
+
+
+/*-------------------------------------------------
+ port_a_r
+-------------------------------------------------*/
+
+static UINT8 port_a_r(const device_config *device)
+{
+ pia6821 *p = get_token(device);
+
+ UINT8 ret = get_in_a_value(device);
+
+ /* IRQ flags implicitly cleared by a read */
+ p->irq_a1 = FALSE;
+ p->irq_a2 = FALSE;
+ update_interrupts(device);
+
+ /* CA2 is configured as output and in read strobe mode */
+ if (C2_OUTPUT(p->ctl_a) && C2_STROBE_MODE(p->ctl_a))
+ {
+ /* this will cause a transition low */
+ set_out_ca2(device, FALSE);
+
+ /* if the CA2 strobe is cleared by the E, reset it right away */
+ if (STROBE_E_RESET(p->ctl_a))
+ set_out_ca2(device, TRUE);
+ }
+
+ LOG(("PIA #%s: port A read = %02X\n", device->tag, ret));
+
+ return ret;
+}
+
+
+/*-------------------------------------------------
+ ddr_a_r
+-------------------------------------------------*/
+
+static UINT8 ddr_a_r(const device_config *device)
+{
+ pia6821 *p = get_token(device);
+
+ UINT8 ret = p->ddr_a;
+
+ LOG(("PIA #%s: DDR A read = %02X\n", device->tag, ret));
+
+ return ret;
+}
+
+
+/*-------------------------------------------------
+ port_b_r
+-------------------------------------------------*/
+
+static UINT8 port_b_r(const device_config *device)
+{
+ pia6821 *p = get_token(device);
+
+ UINT8 ret = get_in_b_value(device);
+
+ /* This read will implicitly clear the IRQ B1 flag. If CB2 is in write-strobe
+ mode with CB1 restore, and a CB1 active transition set the flag,
+ clearing it will cause CB2 to go high again. Note that this is different
+ from what happens with port A. */
+ if (p->irq_b1 && C2_STROBE_MODE(p->ctl_b) && STROBE_C1_RESET(p->ctl_b))
+ set_out_cb2(device, TRUE);
+
+ /* IRQ flags implicitly cleared by a read */
+ p->irq_b1 = FALSE;
+ p->irq_b2 = FALSE;
+ update_interrupts(device);
+
+ LOG(("PIA #%s: port B read = %02X\n", device->tag, ret));
+
+ return ret;
+}
+
+
+/*-------------------------------------------------
+ ddr_b_r
+-------------------------------------------------*/
+
+static UINT8 ddr_b_r(const device_config *device)
+{
+ pia6821 *p = get_token(device);
+
+ UINT8 ret = p->ddr_b;
+
+ LOG(("PIA #%s: DDR B read = %02X\n", device->tag, ret));
+
+ return ret;
+}
+
+
+/*-------------------------------------------------
+ control_a_r
+-------------------------------------------------*/
+
+static UINT8 control_a_r(const device_config *device)
+{
+ pia6821 *p = get_token(device);
+ UINT8 ret;
+
+ /* update CA1 & CA2 if callback exists, these in turn may update IRQ's */
+ if (p->in_ca1_func.read != NULL)
+ pia6821_ca1_w(device, 0, devcb_call_read_line(&p->in_ca1_func));
+ else if (!p->logged_ca1_not_connected && (!p->in_ca1_pushed))
+ {
+ logerror("PIA #%s: Warning! No CA1 read handler. Assuming pin not connected\n", device->tag);
+ p->logged_ca1_not_connected = TRUE;
+ }
+
+ if (p->in_ca2_func.read != NULL)
+ pia6821_ca2_w(device, 0, devcb_call_read_line(&p->in_ca2_func));
+ else if ( !p->logged_ca2_not_connected && C2_INPUT(p->ctl_a) && !p->in_ca2_pushed)
+ {
+ logerror("PIA #%s: Warning! No CA2 read handler. Assuming pin not connected\n", device->tag);
+ p->logged_ca2_not_connected = TRUE;
+ }
+
+ /* read control register */
+ ret = p->ctl_a;
+
+ /* set the IRQ flags if we have pending IRQs */
+ if (p->irq_a1)
+ ret |= PIA_IRQ1;
+
+ if (p->irq_a2 && C2_INPUT(p->ctl_a))
+ ret |= PIA_IRQ2;
+
+ LOG(("PIA #%s: control A read = %02X\n", device->tag, ret));
+
+ return ret;
+}
+
+
+/*-------------------------------------------------
+ control_b_r
+-------------------------------------------------*/
+
+static UINT8 control_b_r(const device_config *device)
+{
+ pia6821 *p = get_token(device);
+ UINT8 ret;
+
+ /* update CB1 & CB2 if callback exists, these in turn may update IRQ's */
+ if (p->in_cb1_func.read != NULL)
+ pia6821_cb1_w(device, 0, devcb_call_read_line(&p->in_cb1_func));
+ else if (!p->logged_cb1_not_connected && !p->in_cb1_pushed)
+ {
+ logerror("PIA #%s: Error! no CB1 read handler. Three-state pin is undefined\n", device->tag);
+ p->logged_cb1_not_connected = TRUE;
+ }
+
+ if (p->in_cb2_func.read != NULL)
+ pia6821_cb2_w(device, 0, devcb_call_read_line(&p->in_cb2_func));
+ else if (!p->logged_cb2_not_connected && C2_INPUT(p->ctl_b) && !p->in_cb2_pushed)
+ {
+ logerror("PIA #%s: Error! No CB2 read handler. Three-state pin is undefined\n", device->tag);
+ p->logged_cb2_not_connected = TRUE;
+ }
+
+ /* read control register */
+ ret = p->ctl_b;
+
+ /* set the IRQ flags if we have pending IRQs */
+ if (p->irq_b1)
+ ret |= PIA_IRQ1;
+
+ if (p->irq_b2 && C2_INPUT(p->ctl_b))
+ ret |= PIA_IRQ2;
+
+ LOG(("PIA #%s: control B read = %02X\n", device->tag, ret));
+
+ return ret;
+}
+
+
+/*-------------------------------------------------
+ pia6821_r
+-------------------------------------------------*/
+
+READ8_DEVICE_HANDLER( pia6821_r )
+{
+ pia6821 *p = get_token(device);
+ UINT8 ret;
+
+ switch (offset & 0x03)
+ {
+ default: /* impossible */
+ case 0x00:
+ if (OUTPUT_SELECTED(p->ctl_a))
+ ret = port_a_r(device);
+ else
+ ret = ddr_a_r(device);
+ break;
+
+ case 0x01:
+ ret = control_a_r(device);
+ break;
+
+ case 0x02:
+ if (OUTPUT_SELECTED(p->ctl_b))
+ ret = port_b_r(device);
+ else
+ ret = ddr_b_r(device);
+ break;
+
+ case 0x03:
+ ret = control_b_r(device);
+ break;
+ }
+
+ return ret;
+}
+
+
+/*-------------------------------------------------
+ pia6821_alt_r
+-------------------------------------------------*/
+
+READ8_DEVICE_HANDLER( pia6821_alt_r )
+{
+ return pia6821_r(device, ((offset << 1) & 0x02) | ((offset >> 1) & 0x01));
+}
+
+
+/*-------------------------------------------------
+ pia6821_get_port_b_z_mask
+-------------------------------------------------*/
+
+UINT8 pia6821_get_port_b_z_mask(const device_config *device)
+{
+ pia6821 *p = get_token(device);
+ return ~p->ddr_b;
+}
+
+
+/*-------------------------------------------------
+ send_to_out_a_func
+-------------------------------------------------*/
+
+static void send_to_out_a_func(const device_config *device, const char* message)
+{
+ pia6821 *p = get_token(device);
+
+ /* input pins are pulled high */
+ UINT8 data = get_out_a_value(device);
+
+ LOG(("PIA #%s: %s = %02X\n", device->tag, message, data));
+
+ if (p->out_a_func.write != NULL)
+ devcb_call_write8(&p->out_a_func, 0, data);
+ else
+ {
+ if (p->out_a_needs_pulled)
+ logerror("PIA #%s: Warning! No port A write handler. Previous value has been lost!\n", device->tag);
+
+ p->out_a_needs_pulled = TRUE;
+ }
+}
+
+
+/*-------------------------------------------------
+ send_to_out_b_func
+-------------------------------------------------*/
+
+static void send_to_out_b_func(const device_config *device, const char* message)
+{
+ pia6821 *p = get_token(device);
+
+ /* input pins are high-impedance - we just send them as zeros for backwards compatibility */
+ UINT8 data = get_out_b_value(device);
+
+ LOG(("PIA #%s: %s = %02X\n", device->tag, message, data));
+
+ if (p->out_b_func.write != NULL)
+ devcb_call_write8(&p->out_b_func, 0, data);
+ else
+ {
+ if (p->out_b_needs_pulled)
+ logerror("PIA #%s: Warning! No port B write handler. Previous value has been lost!\n", device->tag);
+
+ p->out_b_needs_pulled = TRUE;
+ }
+}
+
+
+/*-------------------------------------------------
+ port_a_w
+-------------------------------------------------*/
+
+static void port_a_w(const device_config *device, UINT8 data)
+{
+ pia6821 *p = get_token(device);
+
+ /* buffer the output value */
+ p->out_a = data;
+
+ send_to_out_a_func(device, "port A write");
+}
+
+
+/*-------------------------------------------------
+ ddr_a_w
+-------------------------------------------------*/
+
+static void ddr_a_w(const device_config *device, UINT8 data)
+{
+ pia6821 *p = get_token(device);
+
+ if (data == 0x00)
+ LOG(("PIA #%s: DDR A write = %02X (input mode)\n", device->tag, data));
+ else if (data == 0xff)
+ LOG(("PIA #%s: DDR A write = %02X (output mode)\n", device->tag, data));
+ else
+ LOG(("PIA #%s: DDR A write = %02X (mixed mode)\n", device->tag, data));
+
+ if (p->ddr_a != data)
+ {
+ /* DDR changed, call the callback again */
+ p->ddr_a = data;
+ p->logged_port_a_not_connected = FALSE;
+ send_to_out_a_func(device, "port A write due to DDR change");
+ }
+}
+
+
+/*-------------------------------------------------
+ port_b_w
+-------------------------------------------------*/
+
+static void port_b_w(const device_config *device, UINT8 data)
+{
+ pia6821 *p = get_token(device);
+
+ /* buffer the output value */
+ p->out_b = data;
+
+ send_to_out_b_func(device, "port B write");
+
+ /* CB2 in write strobe mode */
+ if (C2_STROBE_MODE(p->ctl_b))
+ {
+ /* this will cause a transition low */
+ set_out_cb2(device, FALSE);
+
+ /* if the CB2 strobe is cleared by the E, reset it right away */
+ if (STROBE_E_RESET(p->ctl_b))
+ set_out_cb2(device, TRUE);
+ }
+}
+
+
+/*-------------------------------------------------
+ ddr_b_w
+-------------------------------------------------*/
+
+static void ddr_b_w(const device_config *device, UINT8 data)
+{
+ pia6821 *p = get_token(device);
+
+ if (data == 0x00)
+ LOG(("PIA #%s: DDR B write = %02X (input mode)\n", device->tag, data));
+ else if (data == 0xff)
+ LOG(("PIA #%s: DDR B write = %02X (output mode)\n", device->tag, data));
+ else
+ LOG(("PIA #%s: DDR B write = %02X (mixed mode)\n", device->tag, data));
+
+ if (p->ddr_b != data)
+ {
+ /* DDR changed, call the callback again */
+ p->ddr_b = data;
+ p->logged_port_b_not_connected = FALSE;
+ send_to_out_b_func(device, "port B write due to DDR change");
+ }
+}
+
+
+/*-------------------------------------------------
+ control_a_w
+-------------------------------------------------*/
+
+static void control_a_w(const device_config *device, UINT8 data)
+{
+ pia6821 *p = get_token(device);
+
+ /* bit 7 and 6 are read only */
+ data &= 0x3f;
+
+ LOG(("PIA #%s: control A write = %02X\n", device->tag, data));
+
+ /* update the control register */
+ p->ctl_a = data;
+
+ /* CA2 is configured as output */
+ if (C2_OUTPUT(p->ctl_a))
+ {
+ int temp;
+
+ if (C2_SET_MODE(p->ctl_a))
+ /* set/reset mode - bit value determines the new output */
+ temp = C2_SET(p->ctl_a);
+ else
+ /* strobe mode - output is always high unless strobed */
+ temp = TRUE;
+
+ set_out_ca2(device, temp);
+ }
+
+ /* update externals */
+ update_interrupts(device);
+}
+
+
+/*-------------------------------------------------
+ control_b_w
+-------------------------------------------------*/
+
+static void control_b_w(const device_config *device, UINT8 data)
+{
+ pia6821 *p = get_token(device);
+ int temp;
+
+ /* bit 7 and 6 are read only */
+ data &= 0x3f;
+
+ LOG(("PIA #%s: control B write = %02X\n", device->tag, data));
+
+ /* update the control register */
+ p->ctl_b = data;
+
+ if (C2_SET_MODE(p->ctl_b))
+ /* set/reset mode - bit value determines the new output */
+ temp = C2_SET(p->ctl_b);
+ else
+ /* strobe mode - output is always high unless strobed */
+ temp = TRUE;
+
+ set_out_cb2(device, temp);
+
+ /* update externals */
+ update_interrupts(device);
+}
+
+
+/*-------------------------------------------------
+ pia6821_w
+-------------------------------------------------*/
+
+WRITE8_DEVICE_HANDLER( pia6821_w )
+{
+ pia6821 *p = get_token(device);
+
+ switch (offset & 0x03)
+ {
+ default: /* impossible */
+ case 0x00:
+ if (OUTPUT_SELECTED(p->ctl_a))
+ port_a_w(device, data);
+ else
+ ddr_a_w(device, data);
+ break;
+
+ case 0x01:
+ control_a_w(device, data);
+ break;
+
+ case 0x02:
+ if (OUTPUT_SELECTED(p->ctl_b))
+ port_b_w(device, data);
+ else
+ ddr_b_w(device, data);
+ break;
+
+ case 0x03:
+ control_b_w(device, data);
+ break;
+ }
+}
+
+
+/*-------------------------------------------------
+ pia6821_alt_w
+-------------------------------------------------*/
+
+WRITE8_DEVICE_HANDLER( pia6821_alt_w )
+{
+ pia6821_w(device, ((offset << 1) & 0x02) | ((offset >> 1) & 0x01), data);
+}
+
+
+/*-------------------------------------------------
+ pia6821_set_port_a_z_mask
+-------------------------------------------------*/
+
+void pia6821_set_port_a_z_mask(const device_config *device, UINT8 data)
+{
+ pia6821 *p = get_token(device);
+
+ p->port_a_z_mask = data;
+}
+
+
+/*-------------------------------------------------
+ pia6821_porta_r
+-------------------------------------------------*/
+
+READ8_DEVICE_HANDLER( pia6821_porta_r )
+{
+ pia6821 *p = get_token(device);
+
+ return p->in_a;
+}
+
+
+/*-------------------------------------------------
+ pia6821_set_input_a
+-------------------------------------------------*/
+
+void pia6821_set_input_a(const device_config *device, UINT8 data, UINT8 z_mask)
+{
+ pia6821 *p = get_token(device);
+
+ assert_always(p->in_a_func.read == NULL, "pia6821_porta_w() called when in_a_func implemented");
+
+ LOG(("PIA #%s: set input port A = %02X\n", device->tag, data));
+
+ p->in_a = data;
+ p->port_a_z_mask = z_mask;
+ p->in_a_pushed = TRUE;
+}
+
+
+/*-------------------------------------------------
+ pia6821_porta_w
+-------------------------------------------------*/
+
+WRITE8_DEVICE_HANDLER( pia6821_porta_w )
+{
+ pia6821_set_input_a(device, data, 0);
+}
+
+
+/*-------------------------------------------------
+ pia6821_get_output_a
+-------------------------------------------------*/
+
+UINT8 pia6821_get_output_a(const device_config *device)
+{
+ pia6821 *p = get_token(device);
+
+ p->out_a_needs_pulled = FALSE;
+
+ return get_out_a_value(device);
+}
+
+
+/*-------------------------------------------------
+ pia6821_ca1_r
+-------------------------------------------------*/
+
+READ8_DEVICE_HANDLER( pia6821_ca1_r )
+{
+ pia6821 *p = get_token(device);
+
+ return p->in_ca1;
+}
+
+
+/*-------------------------------------------------
+ pia6821_ca1_w
+-------------------------------------------------*/
+
+WRITE8_DEVICE_HANDLER( pia6821_ca1_w )
+{
+ pia6821 *p = get_token(device);
+
+ /* limit the data to 0 or 1 */
+ data = data ? TRUE : FALSE;
+
+ LOG(("PIA #%s: set input CA1 = %d\n", device->tag, data));
+
+ /* the new state has caused a transition */
+ if ((p->in_ca1 != data) &&
+ ((data && C1_LOW_TO_HIGH(p->ctl_a)) || (!data && C1_HIGH_TO_LOW(p->ctl_a))))
+ {
+ LOG(("PIA #%s: CA1 triggering\n", device->tag));
+
+ /* mark the IRQ */
+ p->irq_a1 = TRUE;
+
+ /* update externals */
+ update_interrupts(device);
+
+ /* CA2 is configured as output and in read strobe mode and cleared by a CA1 transition */
+ if (C2_OUTPUT(p->ctl_a) && C2_STROBE_MODE(p->ctl_a) && STROBE_C1_RESET(p->ctl_a))
+ set_out_ca2(device, TRUE);
+ }
+
+ /* set the new value for CA1 */
+ p->in_ca1 = data;
+ p->in_ca1_pushed = TRUE;
+}
+
+
+/*-------------------------------------------------
+ pia6821_ca2_r
+-------------------------------------------------*/
+
+READ8_DEVICE_HANDLER( pia6821_ca2_r )
+{
+ pia6821 *p = get_token(device);
+
+ return p->in_ca2;
+}
+
+
+/*-------------------------------------------------
+ pia6821_ca2_w
+-------------------------------------------------*/
+
+WRITE8_DEVICE_HANDLER( pia6821_ca2_w )
+{
+ pia6821 *p = get_token(device);
+
+ /* limit the data to 0 or 1 */
+ data = data ? 1 : 0;
+
+ LOG(("PIA #%s: set input CA2 = %d\n", device->tag, data));
+
+ /* if input mode and the new state has caused a transition */
+ if (C2_INPUT(p->ctl_a) &&
+ (p->in_ca2 != data) &&
+ ((data && C2_LOW_TO_HIGH(p->ctl_a)) || (!data && C2_HIGH_TO_LOW(p->ctl_a))))
+ {
+ LOG(("PIA #%s: CA2 triggering\n", device->tag));
+
+ /* mark the IRQ */
+ p->irq_a2 = TRUE;
+
+ /* update externals */
+ update_interrupts(device);
+ }
+
+ /* set the new value for CA2 */
+ p->in_ca2 = data;
+ p->in_ca2_pushed = TRUE;
+}
+
+
+/*-------------------------------------------------
+ pia6821_get_output_ca2
+-------------------------------------------------*/
+
+int pia6821_get_output_ca2(const device_config *device)
+{
+ pia6821 *p = get_token(device);
+
+ p->out_ca2_needs_pulled = FALSE;
+
+ return p->out_ca2;
+}
+
+
+/*-------------------------------------------------
+ pia6821_get_output_ca2_z - version of
+ pia6821_get_output_ca2, which takes account of internal
+ pullup resistor
+-------------------------------------------------*/
+
+int pia6821_get_output_ca2_z(const device_config *device)
+{
+ pia6821 *p = get_token(device);
+
+ p->out_ca2_needs_pulled = FALSE;
+
+ // If it's an output, output the bit, if it's an input, it's
+ // pulled up
+ return p->out_ca2 |
+ C2_INPUT(p->ctl_a);
+}
+
+
+/*-------------------------------------------------
+ pia6821_portb_r
+-------------------------------------------------*/
+
+READ8_DEVICE_HANDLER( pia6821_portb_r )
+{
+ pia6821 *p = get_token(device);
+
+ return p->in_b;
+}
+
+
+/*-------------------------------------------------
+ pia6821_portb_w
+-------------------------------------------------*/
+
+WRITE8_DEVICE_HANDLER( pia6821_portb_w )
+{
+ pia6821 *p = get_token(device);
+
+ assert_always(p->in_b_func.read == NULL, "pia_set_input_b() called when in_b_func implemented");
+
+ LOG(("PIA #%s: set input port B = %02X\n", device->tag, data));
+
+ p->in_b = data;
+ p->in_b_pushed = TRUE;
+}
+
+
+/*-------------------------------------------------
+ pia6821_get_output_b
+-------------------------------------------------*/
+
+UINT8 pia6821_get_output_b(const device_config *device)
+{
+ pia6821 *p = get_token(device);
+
+ p->out_b_needs_pulled = FALSE;
+
+ return get_out_b_value(device);
+}
+
+
+/*-------------------------------------------------
+ pia6821_cb1_r
+-------------------------------------------------*/
+
+READ8_DEVICE_HANDLER( pia6821_cb1_r )
+{
+ pia6821 *p = get_token(device);
+
+ return p->in_cb1;
+}
+
+
+/*-------------------------------------------------
+ pia6821_cb1_w
+-------------------------------------------------*/
+
+WRITE8_DEVICE_HANDLER( pia6821_cb1_w )
+{
+ pia6821 *p = get_token(device);
+
+ /* limit the data to 0 or 1 */
+ data = data ? 1 : 0;
+
+ LOG(("PIA #%s: set input CB1 = %d\n", device->tag, data));
+
+ /* the new state has caused a transition */
+ if ((p->in_cb1 != data) &&
+ ((data && C1_LOW_TO_HIGH(p->ctl_b)) || (!data && C1_HIGH_TO_LOW(p->ctl_b))))
+ {
+ LOG(("PIA #%s: CB1 triggering\n", device->tag));
+
+ /* mark the IRQ */
+ p->irq_b1 = 1;
+
+ /* update externals */
+ update_interrupts(device);
+
+ /* If CB2 is configured as a write-strobe output which is reset by a CB1
+ transition, this reset will only happen when a read from port B implicitly
+ clears the IRQ B1 flag. So we handle the CB2 reset there. Note that this
+ is different from what happens with port A. */
+ }
+
+ /* set the new value for CB1 */
+ p->in_cb1 = data;
+ p->in_cb1_pushed = TRUE;
+}
+
+
+/*-------------------------------------------------
+ pia6821_cb2_r
+-------------------------------------------------*/
+
+READ8_DEVICE_HANDLER( pia6821_cb2_r )
+{
+ pia6821 *p = get_token(device);
+
+ return p->in_cb2;
+}
+
+
+/*-------------------------------------------------
+ pia6821_cb2_w
+-------------------------------------------------*/
+
+WRITE8_DEVICE_HANDLER( pia6821_cb2_w )
+{
+ pia6821 *p = get_token(device);
+
+ /* limit the data to 0 or 1 */
+ data = data ? 1 : 0;
+
+ LOG(("PIA #%s: set input CB2 = %d\n", device->tag, data));
+
+ /* if input mode and the new state has caused a transition */
+ if (C2_INPUT(p->ctl_b) &&
+ (p->in_cb2 != data) &&
+ ((data && C2_LOW_TO_HIGH(p->ctl_b)) || (!data && C2_HIGH_TO_LOW(p->ctl_b))))
+ {
+ LOG(("PIA #%s: CB2 triggering\n", device->tag));
+
+ /* mark the IRQ */
+ p->irq_b2 = 1;
+
+ /* update externals */
+ update_interrupts(device);
+ }
+
+ /* set the new value for CA2 */
+ p->in_cb2 = data;
+ p->in_cb2_pushed = TRUE;
+}
+
+
+/*-------------------------------------------------
+ pia6821_get_output_cb2
+-------------------------------------------------*/
+
+int pia6821_get_output_cb2(const device_config *device)
+{
+ pia6821 *p = get_token(device);
+
+ p->out_cb2_needs_pulled = FALSE;
+
+ return p->out_cb2;
+}
+
+
+/*-------------------------------------------------
+ pia6821_get_output_cb2_z
+-------------------------------------------------*/
+
+int pia6821_get_output_cb2_z(const device_config *device)
+{
+ pia6821 *p = get_token(device);
+
+ return !C2_OUTPUT(p->ctl_b);
+}
+
+
+/*-------------------------------------------------
+ pia6821_get_irq_a
+-------------------------------------------------*/
+
+int pia6821_get_irq_a(const device_config *device)
+{
+ pia6821 *p = get_token(device);
+
+ return p->irq_a_state;
+}
+
+
+/*-------------------------------------------------
+ pia6821_get_irq_b
+-------------------------------------------------*/
+
+int pia6821_get_irq_b(const device_config *device)
+{
+ pia6821 *p = get_token(device);
+
+ return p->irq_b_state;
+}
+
+
+/*-------------------------------------------------
+ DEVICE_SET_INFO( pia )
+-------------------------------------------------*/
+
+static DEVICE_SET_INFO( pia )
+{
+ switch(state)
+ {
+ }
+}
+
+
+/*-------------------------------------------------
+ DEVICE_GET_INFO( pia6821 )
+-------------------------------------------------*/
+
+DEVICE_GET_INFO(pia6821)
+{
+ switch (state)
+ {
+ /* --- the following bits of info are returned as 64-bit signed integers --- */
+ case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(pia6821); break;
+ case DEVINFO_INT_INLINE_CONFIG_BYTES: info->i = 0; break;
+ case DEVINFO_INT_CLASS: info->i = DEVICE_CLASS_PERIPHERAL; break;
+
+ /* --- the following bits of info are returned as pointers to data or functions --- */
+ case DEVINFO_FCT_SET_INFO: info->set_info = DEVICE_SET_INFO_NAME(pia); break;
+ case DEVINFO_FCT_START: info->start = DEVICE_START_NAME(pia); break;
+ case DEVINFO_FCT_STOP: /* Nothing */ break;
+ case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME(pia); break;
+
+ /* --- the following bits of info are returned as NULL-terminated strings --- */
+ case DEVINFO_STR_NAME: strcpy(info->s, "6821 PIA"); break;
+ case DEVINFO_STR_FAMILY: strcpy(info->s, "6821 PIA"); break;
+ case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break;
+ case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break;
+ case DEVINFO_STR_CREDITS: /* Nothing */ break;
+ }
+}
+
+
+/*-------------------------------------------------
+ DEVICE_GET_INFO( pia6822 )
+-------------------------------------------------*/
+
+DEVICE_GET_INFO(pia6822)
+{
+ switch (state)
+ {
+ case DEVINFO_STR_NAME: strcpy(info->s, "6822 PIA"); break;
+ default: DEVICE_GET_INFO_CALL(pia6821); break;
+ }
+}