diff options
Diffstat (limited to 'trunk/src/emu/machine/6525tpi.c')
-rw-r--r-- | trunk/src/emu/machine/6525tpi.c | 622 |
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); |