summaryrefslogtreecommitdiffstatshomepage
path: root/trunk/src/emu/machine/6525tpi.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/src/emu/machine/6525tpi.c')
-rw-r--r--trunk/src/emu/machine/6525tpi.c622
1 files changed, 622 insertions, 0 deletions
diff --git a/trunk/src/emu/machine/6525tpi.c b/trunk/src/emu/machine/6525tpi.c
new file mode 100644
index 00000000000..12a0f76f952
--- /dev/null
+++ b/trunk/src/emu/machine/6525tpi.c
@@ -0,0 +1,622 @@
+/***************************************************************************
+ mos tri port interface 6525
+ mos triple interface adapter 6523
+
+ peter.trauner@jk.uni-linz.ac.at
+
+ used in commodore b series
+ used in commodore c1551 floppy disk drive
+***************************************************************************/
+
+/*
+ mos tpi 6525
+ 40 pin package
+ 3 8 bit ports (pa, pb, pc)
+ 8 registers to pc
+ 0 port a 0 in low
+ 1 port a data direction register 1 output
+ 2 port b
+ 3 port b ddr
+ 4 port c
+ handshaking, interrupt mode
+ 0 interrupt 0 input, 1 interrupt enabled
+ interrupt set on falling edge
+ 1 interrupt 1 input
+ 2 interrupt 2 input
+ 3 interrupt 3 input
+ 4 interrupt 4 input
+ 5 irq output
+ 6 ca handshake line (read handshake answer on I3 preferred)
+ 7 cb handshake line (write handshake clear on I4 preferred)
+ 5 port c ddr
+
+ 6 cr configuration register
+ 0 mc
+ 0 port c normal input output mode like port a and b
+ 1 port c used for handshaking and interrupt input
+ 1 1 interrupt prioritized
+ 2 i3 configure edge
+ 1 interrupt set on positive edge
+ 3 i4 configure edge
+ 5,4 ca handshake
+ 00 on read
+ rising edge of i3 sets ca high
+ read a data from computers sets ca low
+ 01 pulse output
+ 1 microsecond low after read a operation
+ 10 manual output low
+ 11 manual output high
+ 7,6 cb handshake
+ 00 handshake on write
+ write b data from computer sets cb 0
+ rising edge of i4 sets cb high
+ 01 pulse output
+ 1 microsecond low after write b operation
+ 10 manual output low
+ 11 manual output high
+ 7 air active interrupt register
+ 0 I0 occurred
+ 1 I1 occurred
+ 2 I2 occurred
+ 3 I3 occurred
+ 4 I4 occurred
+ read clears interrupt
+
+ non prioritized interrupts
+ interrupt is set when occurred
+ read clears all interrupts
+
+ prioritized interrupts
+ I4>I3>I2>I1>I0
+ highest interrupt can be found in air register
+ read clears highest interrupt
+*/
+
+#include "emu.h"
+#include "6525tpi.h"
+
+
+/***************************************************************************
+ CONSTANTS
+***************************************************************************/
+
+#define VERBOSE_LEVEL 0
+#define DBG_LOG( MACHINE, N, M, A ) \
+ do { \
+ if(VERBOSE_LEVEL >= N) \
+ { \
+ if( M ) \
+ logerror("%11.6f: %-24s", MACHINE.time().as_double(), (char*) M ); \
+ logerror A; \
+ } \
+ } while (0)
+
+
+#define INTERRUPT_MODE (tpi6525->cr & 1)
+#define PRIORIZED_INTERRUPTS (tpi6525->cr & 2)
+#define INTERRUPT3_RISING_EDGE (tpi6525->cr & 4)
+#define INTERRUPT4_RISING_EDGE (tpi6525->cr & 8)
+#define CA_MANUAL_OUT (tpi6525->cr & 0x20)
+#define CA_MANUAL_LEVEL ((tpi6525->cr & 0x10) ? 1 : 0)
+#define CB_MANUAL_OUT (tpi6525->cr & 0x80)
+#define CB_MANUAL_LEVEL ((tpi6525->cr & 0x40) ? 1 : 0)
+
+
+/***************************************************************************
+ TYPE DEFINITIONS
+***************************************************************************/
+
+typedef struct _tpi6525_state tpi6525_state;
+struct _tpi6525_state
+{
+ devcb_resolved_write_line out_irq_func;
+ devcb_resolved_read8 in_pa_func;
+ devcb_resolved_write8 out_pa_func;
+ devcb_resolved_read8 in_pb_func;
+ devcb_resolved_write8 out_pb_func;
+ devcb_resolved_read8 in_pc_func;
+ devcb_resolved_write8 out_pc_func;
+ devcb_resolved_write_line out_ca_func;
+ devcb_resolved_write_line out_cb_func;
+
+ UINT8 port_a, ddr_a, in_a;
+ UINT8 port_b, ddr_b, in_b;
+ UINT8 port_c, ddr_c, in_c;
+
+ UINT8 ca_level, cb_level, interrupt_level;
+
+ UINT8 cr;
+ UINT8 air;
+
+ UINT8 irq_level[5];
+};
+
+
+/*****************************************************************************
+ INLINE FUNCTIONS
+*****************************************************************************/
+
+INLINE tpi6525_state *get_safe_token(device_t *device)
+{
+ assert(device != NULL);
+ assert(device->type() == TPI6525);
+
+ return (tpi6525_state *)downcast<legacy_device_base *>(device)->token();
+}
+
+
+/*****************************************************************************
+ DEVICE INTERFACE
+*****************************************************************************/
+
+static DEVICE_START( tpi6525 )
+{
+ tpi6525_state *tpi6525 = get_safe_token(device);
+ const tpi6525_interface *intf = (const tpi6525_interface*)device->static_config();
+
+ // resolve callbacks
+ tpi6525->out_irq_func.resolve(intf->out_irq_func, *device);
+ tpi6525->in_pa_func.resolve(intf->in_pa_func, *device);
+ tpi6525->out_pa_func.resolve(intf->out_pa_func, *device);
+ tpi6525->in_pb_func.resolve(intf->in_pb_func, *device);
+ tpi6525->out_pb_func.resolve(intf->out_pb_func, *device);
+ tpi6525->in_pc_func.resolve(intf->in_pc_func, *device);
+ tpi6525->out_pc_func.resolve(intf->out_pc_func, *device);
+ tpi6525->out_ca_func.resolve(intf->out_ca_func, *device);
+ tpi6525->out_cb_func.resolve(intf->out_cb_func, *device);
+
+ /* verify that we have an interface assigned */
+ assert(device->static_config() != NULL);
+
+ /* register for state saving */
+ device->save_item(NAME(tpi6525->port_a));
+ device->save_item(NAME(tpi6525->ddr_a));
+ device->save_item(NAME(tpi6525->in_a));
+ device->save_item(NAME(tpi6525->port_b));
+ device->save_item(NAME(tpi6525->ddr_b));
+ device->save_item(NAME(tpi6525->in_b));
+ device->save_item(NAME(tpi6525->port_c));
+ device->save_item(NAME(tpi6525->ddr_c));
+ device->save_item(NAME(tpi6525->in_c));
+ device->save_item(NAME(tpi6525->ca_level));
+ device->save_item(NAME(tpi6525->cb_level));
+ device->save_item(NAME(tpi6525->interrupt_level));
+ device->save_item(NAME(tpi6525->cr));
+ device->save_item(NAME(tpi6525->air));
+ device->save_item(NAME(tpi6525->irq_level));
+}
+
+
+static DEVICE_RESET( tpi6525 )
+{
+ tpi6525_state *tpi6525 = get_safe_token(device);
+
+ /* setup some initial values */
+ tpi6525->in_a = 0xff;
+ tpi6525->in_b = 0xff;
+ tpi6525->in_c = 0xff;
+}
+
+
+DEVICE_GET_INFO( tpi6525 )
+{
+ switch (state)
+ {
+ /* --- the following bits of info are returned as 64-bit signed integers --- */
+ case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(tpi6525_state); break;
+
+ /* --- the following bits of info are returned as pointers to data or functions --- */
+ case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( tpi6525 ); break;
+ case DEVINFO_FCT_STOP: /* Nothing */ break;
+ case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME( tpi6525 ); break;
+
+ /* --- the following bits of info are returned as NULL-terminated strings --- */
+ case DEVINFO_STR_NAME: strcpy(info->s, "6525 TPI"); break;
+ case DEVINFO_STR_FAMILY: strcpy(info->s, "6525 TPI"); 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: strcpy(info->s, "Copyright MESS Team"); break;
+ }
+}
+
+
+/***************************************************************************
+ IMPLEMENTATION
+***************************************************************************/
+
+static void tpi6525_set_interrupt(device_t *device)
+{
+ tpi6525_state *tpi6525 = get_safe_token(device);
+
+ if (!tpi6525->interrupt_level && (tpi6525->air != 0))
+ {
+ tpi6525->interrupt_level = 1;
+
+ DBG_LOG(device->machine(), 3, "tpi6525", ("%s set interrupt\n", device->tag()));
+
+ tpi6525->out_irq_func(tpi6525->interrupt_level);
+ }
+}
+
+
+static void tpi6525_clear_interrupt(device_t *device)
+{
+ tpi6525_state *tpi6525 = get_safe_token(device);
+
+ if (tpi6525->interrupt_level && (tpi6525->air == 0))
+ {
+ tpi6525->interrupt_level = 0;
+
+ DBG_LOG(device->machine(), 3, "tpi6525", ("%s clear interrupt\n", device->tag()));
+
+ tpi6525->out_irq_func(tpi6525->interrupt_level);
+ }
+}
+
+
+WRITE_LINE_DEVICE_HANDLER( tpi6525_i0_w )
+{
+ tpi6525_state *tpi6525 = get_safe_token(device);
+
+ if (INTERRUPT_MODE && (state != tpi6525->irq_level[0]))
+ {
+ tpi6525->irq_level[0] = state;
+
+ if ((state == 0) && !(tpi6525->air & 1) && (tpi6525->ddr_c & 1))
+ {
+ tpi6525->air |= 1;
+ tpi6525_set_interrupt(device);
+ }
+ }
+}
+
+
+WRITE_LINE_DEVICE_HANDLER( tpi6525_i1_w )
+{
+ tpi6525_state *tpi6525 = get_safe_token(device);
+
+ if (INTERRUPT_MODE && (state != tpi6525->irq_level[1]))
+ {
+ tpi6525->irq_level[1] = state;
+
+ if ((state == 0) && !(tpi6525->air & 2) && (tpi6525->ddr_c & 2))
+ {
+ tpi6525->air |= 2;
+ tpi6525_set_interrupt(device);
+ }
+ }
+}
+
+
+WRITE_LINE_DEVICE_HANDLER( tpi6525_i2_w )
+{
+ tpi6525_state *tpi6525 = get_safe_token(device);
+
+ if (INTERRUPT_MODE && (state != tpi6525->irq_level[2]))
+ {
+ tpi6525->irq_level[2] = state;
+
+ if ((state == 0) && !(tpi6525->air & 4) && (tpi6525->ddr_c & 4))
+ {
+ tpi6525->air |= 4;
+ tpi6525_set_interrupt(device);
+ }
+ }
+}
+
+
+WRITE_LINE_DEVICE_HANDLER( tpi6525_i3_w )
+{
+ tpi6525_state *tpi6525 = get_safe_token(device);
+
+ if (INTERRUPT_MODE && (state != tpi6525->irq_level[3]))
+ {
+ tpi6525->irq_level[3] = state;
+
+ if (((INTERRUPT3_RISING_EDGE && (state == 1))
+ || (!INTERRUPT3_RISING_EDGE && (state == 0)))
+ && !(tpi6525->air & 8) && (tpi6525->ddr_c & 8))
+ {
+ tpi6525->air |= 8;
+ tpi6525_set_interrupt(device);
+ }
+ }
+}
+
+
+WRITE_LINE_DEVICE_HANDLER( tpi6525_i4_w )
+{
+ tpi6525_state *tpi6525 = get_safe_token(device);
+
+ if (INTERRUPT_MODE && (state != tpi6525->irq_level[4]) )
+ {
+ tpi6525->irq_level[4] = state;
+
+ if (((INTERRUPT4_RISING_EDGE && (state == 1))
+ ||(!INTERRUPT4_RISING_EDGE&&(state == 0)))
+ && !(tpi6525->air & 0x10) && (tpi6525->ddr_c & 0x10))
+ {
+ tpi6525->air |= 0x10;
+ tpi6525_set_interrupt(device);
+ }
+ }
+}
+
+
+READ8_DEVICE_HANDLER( tpi6525_porta_r )
+{
+ tpi6525_state *tpi6525 = get_safe_token(device);
+ UINT8 data = tpi6525->in_a;
+
+ if (!tpi6525->in_pa_func.isnull())
+ data = tpi6525->in_pa_func(offset);
+
+ data = (data & ~tpi6525->ddr_a) | (tpi6525->ddr_a & tpi6525->port_a);
+
+ return data;
+}
+
+
+WRITE8_DEVICE_HANDLER( tpi6525_porta_w )
+{
+ tpi6525_state *tpi6525 = get_safe_token(device);
+
+ tpi6525->in_a = data;
+}
+
+
+READ8_DEVICE_HANDLER( tpi6525_portb_r )
+{
+ tpi6525_state *tpi6525 = get_safe_token(device);
+ UINT8 data = tpi6525->in_b;
+
+ if (!tpi6525->in_pb_func.isnull())
+ data = tpi6525->in_pb_func(offset);
+
+ data = (data & ~tpi6525->ddr_b) | (tpi6525->ddr_b & tpi6525->port_b);
+
+ return data;
+}
+
+
+WRITE8_DEVICE_HANDLER( tpi6525_portb_w )
+{
+ tpi6525_state *tpi6525 = get_safe_token(device);
+
+ tpi6525->in_b = data;
+}
+
+
+READ8_DEVICE_HANDLER( tpi6525_portc_r )
+{
+ tpi6525_state *tpi6525 = get_safe_token(device);
+ UINT8 data = tpi6525->in_c;
+
+ if (!tpi6525->in_pc_func.isnull())
+ data &= tpi6525->in_pc_func(offset);
+
+ data = (data & ~tpi6525->ddr_c) | (tpi6525->ddr_c & tpi6525->port_c);
+
+ return data;
+}
+
+
+WRITE8_DEVICE_HANDLER( tpi6525_portc_w )
+{
+ tpi6525_state *tpi6525 = get_safe_token(device);
+
+ tpi6525->in_c = data;
+}
+
+
+READ8_DEVICE_HANDLER( tpi6525_r )
+{
+ tpi6525_state *tpi6525 = get_safe_token(device);
+ UINT8 data = 0xff;
+
+ switch (offset & 7)
+ {
+ case 0:
+ data = tpi6525->in_a;
+
+ if (!tpi6525->in_pa_func.isnull())
+ data &= tpi6525->in_pa_func(0);
+
+ data = (data & ~tpi6525->ddr_a) | (tpi6525->ddr_a & tpi6525->port_a);
+
+ break;
+
+ case 1:
+ data = tpi6525->in_b;
+
+ if (!tpi6525->in_pb_func.isnull())
+ data &= tpi6525->in_pb_func(0);
+
+ data = (data & ~tpi6525->ddr_b) | (tpi6525->ddr_b & tpi6525->port_b);
+
+ break;
+
+ case 2:
+ if (INTERRUPT_MODE)
+ {
+ data = 0;
+
+ if (tpi6525->irq_level[0]) data |= 0x01;
+ if (tpi6525->irq_level[1]) data |= 0x02;
+ if (tpi6525->irq_level[2]) data |= 0x04;
+ if (tpi6525->irq_level[3]) data |= 0x08;
+ if (tpi6525->irq_level[4]) data |= 0x10;
+ if (!tpi6525->interrupt_level) data |= 0x20;
+ if (tpi6525->ca_level) data |= 0x40;
+ if (tpi6525->cb_level) data |= 0x80;
+ }
+ else
+ {
+ data = tpi6525->in_c;
+
+ if (!tpi6525->in_pc_func.isnull())
+ data &= tpi6525->in_pc_func(0);
+
+ data = (data & ~tpi6525->ddr_c) | (tpi6525->ddr_c & tpi6525->port_c);
+ }
+
+ DBG_LOG(device->machine(), 2, "tpi6525", ("%s read %.2x %.2x\n", device->tag(), offset, data));
+ break;
+
+ case 3:
+ data = tpi6525->ddr_a;
+ break;
+
+ case 4:
+ data = tpi6525->ddr_b;
+ break;
+
+ case 5:
+ data = tpi6525->ddr_c;
+ break;
+
+ case 6:
+ data = tpi6525->cr;
+ break;
+
+ case 7: /* air */
+ if (PRIORIZED_INTERRUPTS)
+ {
+ if (tpi6525->air & 0x10)
+ {
+ data = 0x10;
+ tpi6525->air &= ~0x10;
+ }
+ else if (tpi6525->air & 8)
+ {
+ data = 8;
+ tpi6525->air &= ~8;
+ }
+ else if (tpi6525->air & 4)
+ {
+ data = 4;
+ tpi6525->air &= ~4;
+ }
+ else if (tpi6525->air & 2)
+ {
+ data = 2;
+ tpi6525->air &= ~2;
+ }
+ else if (tpi6525->air & 1)
+ {
+ data = 1;
+ tpi6525->air &= ~1;
+ }
+ }
+ else
+ {
+ data = tpi6525->air;
+ tpi6525->air = 0;
+ }
+
+ tpi6525_clear_interrupt(device);
+ break;
+
+ }
+
+ DBG_LOG(device->machine(), 3, "tpi6525", ("%s read %.2x %.2x\n", device->tag(), offset, data));
+
+ return data;
+}
+
+
+WRITE8_DEVICE_HANDLER( tpi6525_w )
+{
+ tpi6525_state *tpi6525 = get_safe_token(device);
+
+ DBG_LOG(device->machine(), 2, "tpi6525", ("%s write %.2x %.2x\n", device->tag(), offset, data));
+
+ switch (offset & 7)
+ {
+ case 0:
+ tpi6525->port_a = data;
+ tpi6525->out_pa_func(0, (tpi6525->port_a & tpi6525->ddr_a) | (tpi6525->ddr_a ^ 0xff));
+ break;
+
+ case 1:
+ tpi6525->port_b = data;
+ tpi6525->out_pb_func(0, (tpi6525->port_b & tpi6525->ddr_b) | (tpi6525->ddr_b ^ 0xff));
+ break;
+
+ case 2:
+ tpi6525->port_c = data;
+
+ if (!INTERRUPT_MODE)
+ tpi6525->out_pc_func(0, (tpi6525->port_c & tpi6525->ddr_c) | (tpi6525->ddr_c ^ 0xff));
+ break;
+
+ case 3:
+ tpi6525->ddr_a = data;
+ tpi6525->out_pa_func(0, (tpi6525->port_a & tpi6525->ddr_a) | (tpi6525->ddr_a ^ 0xff));
+ break;
+
+ case 4:
+ tpi6525->ddr_b = data;
+ tpi6525->out_pb_func(0, (tpi6525->port_b & tpi6525->ddr_b) | (tpi6525->ddr_b ^ 0xff));
+ break;
+
+ case 5:
+ tpi6525->ddr_c = data;
+
+ if (!INTERRUPT_MODE)
+ tpi6525->out_pc_func(0, (tpi6525->port_c & tpi6525->ddr_c) | (tpi6525->ddr_c ^ 0xff));
+ break;
+
+ case 6:
+ tpi6525->cr = data;
+
+ if (INTERRUPT_MODE)
+ {
+ if (CA_MANUAL_OUT)
+ {
+ if (tpi6525->ca_level != CA_MANUAL_LEVEL)
+ {
+ tpi6525->ca_level = CA_MANUAL_LEVEL;
+ tpi6525->out_ca_func(tpi6525->ca_level);
+ }
+ }
+ if (CB_MANUAL_OUT)
+ {
+ if (tpi6525->cb_level != CB_MANUAL_LEVEL)
+ {
+ tpi6525->cb_level = CB_MANUAL_LEVEL;
+ tpi6525->out_cb_func(tpi6525->cb_level);
+ }
+ }
+ }
+
+ break;
+
+ case 7:
+ /* tpi6525->air = data; */
+ break;
+ }
+}
+
+
+/* this should probably be done better, needed for amigacd.c */
+
+UINT8 tpi6525_get_ddr_a(device_t *device)
+{
+ tpi6525_state *tpi6525 = get_safe_token(device);
+ return tpi6525->ddr_a;
+}
+
+UINT8 tpi6525_get_ddr_b(device_t *device)
+{
+ tpi6525_state *tpi6525 = get_safe_token(device);
+ return tpi6525->ddr_b;
+}
+
+UINT8 tpi6525_get_ddr_c(device_t *device)
+{
+ tpi6525_state *tpi6525 = get_safe_token(device);
+ return tpi6525->ddr_c;
+}
+
+DEFINE_LEGACY_DEVICE(TPI6525, tpi6525);