summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/machine/6840ptm.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/emu/machine/6840ptm.c')
-rw-r--r--src/emu/machine/6840ptm.c703
1 files changed, 703 insertions, 0 deletions
diff --git a/src/emu/machine/6840ptm.c b/src/emu/machine/6840ptm.c
new file mode 100644
index 00000000000..8fff877fdcf
--- /dev/null
+++ b/src/emu/machine/6840ptm.c
@@ -0,0 +1,703 @@
+/**********************************************************************
+
+
+ Motorola 6840 PTM interface and emulation
+
+ This function is a simple emulation of up to 4 MC6840
+ Programmable Timer Modules
+
+ Written By El Condor based on previous work by Aaron Giles,
+ 'Re-Animator' and Mathis Rosenhauer.
+
+ Todo:
+ Confirm handling for 'Single Shot' operation.
+ (Datasheet suggests that output starts high, going low
+ on timeout, opposite of continuous case)
+ Establish whether ptm6840_set_c? routines can replace
+ hard coding of external clock frequencies.
+
+
+ Operation:
+ The interface is arranged as follows:
+
+ Internal Clock frequency,
+ Clock 1 frequency, Clock 2 frequency, Clock 3 frequency,
+ Clock 1 output, Clock 2 output, Clock 3 output,
+ IRQ function
+
+ If the external clock frequencies are not fixed, they should be
+ entered as '0', and the ptm6840_set_c?(which, state) functions
+ should be used instead if necessary (This should allow the VBLANK
+ clock on the MCR units to operate).
+
+**********************************************************************/
+
+#include "driver.h"
+#include "6840ptm.h"
+
+#ifdef MAME_DEBUG
+#define PTMVERBOSE 1
+#else
+#define PTMVERBOSE 0
+#endif
+
+#if PTMVERBOSE
+#define PLOG(x) logerror x
+#else
+#define PLOG(x)
+#endif
+
+#define PTM_6840_CTRL1 0
+#define PTM_6840_CTRL2 1
+#define PTM_6840_MSBBUF1 2
+#define PTM_6840_LSB1 3
+#define PTM_6840_MSBBUF2 4
+#define PTM_6840_LSB2 5
+#define PTM_6840_MSBBUF3 6
+#define PTM_6840_LSB3 7
+
+typedef struct _ptm6840 ptm6840;
+struct _ptm6840
+{
+ const ptm6840_interface *intf;
+
+ UINT8 control_reg[3];
+ UINT8 output[3]; // output states
+ UINT8 gate[3]; // input gate states
+ UINT8 clock[3]; // clock states
+ UINT8 enabled[3];
+ UINT8 mode[3];
+ UINT8 fired[3];
+ UINT8 t3_divisor;
+ UINT8 IRQ;
+ UINT8 status_reg;
+ UINT8 status_read_since_int;
+ UINT8 lsb_buffer;
+ UINT8 msb_buffer;
+
+ int internal_clock;
+ int external_clock[3];
+
+ // each PTM has 3 timers
+ emu_timer *timer[3];
+
+ UINT16 latch[3];
+ UINT16 counter[3];
+};
+
+// local prototypes ///////////////////////////////////////////////////////
+
+static void ptm6840_timeout(int which, int idx);
+static TIMER_CALLBACK( ptm6840_t1_timeout );
+static TIMER_CALLBACK( ptm6840_t2_timeout );
+static TIMER_CALLBACK( ptm6840_t3_timeout );
+
+// local vars /////////////////////////////////////////////////////////////
+
+static ptm6840 ptm[PTM_6840_MAX];
+
+#if PTMVERBOSE
+static const char *opmode[] =
+{
+ "000 continous mode",
+ "001 freq comparison mode",
+ "010 continous mode",
+ "011 pulse width comparison mode",
+ "100 single shot mode",
+ "101 freq comparison mode",
+ "110 single shot mode",
+ "111 pulse width comparison mode"
+};
+#endif
+
+///////////////////////////////////////////////////////////////////////////
+// //
+// Get enabled status //
+// //
+///////////////////////////////////////////////////////////////////////////
+
+int ptm6840_get_status(int which, int clock)
+{
+ ptm6840 *p = ptm + which;
+ return p->enabled[clock-1];
+}
+
+///////////////////////////////////////////////////////////////////////////
+// //
+// Subtract from Counter //
+// //
+///////////////////////////////////////////////////////////////////////////
+
+static void subtract_from_counter(int counter, int count, int which)
+{
+ int clock;
+ ptm6840 *currptr = ptm + which;
+
+ /* determine the clock frequency for this timer */
+ if (currptr->control_reg[counter] & 0x02)
+ clock = currptr->internal_clock;
+ else
+ clock = currptr->external_clock[counter];
+
+ /* dual-byte mode */
+ if (currptr->control_reg[counter] & 0x04)
+ {
+ int lsb = currptr->counter[counter] & 0xff;
+ int msb = currptr->counter[counter] >> 8;
+
+ /* count the clocks */
+ lsb -= count;
+
+ /* loop while we're less than zero */
+ while (lsb < 0)
+ {
+ /* borrow from the MSB */
+ lsb += (currptr->latch[counter] & 0xff) + 1;
+ msb--;
+ /* if MSB goes less than zero, we've expired */
+ if (msb < 0)
+ {
+ ptm6840_timeout(which, counter);
+ msb = (currptr->latch[counter] >> 8) + 1;
+ }
+ }
+
+ /* store the result */
+ currptr->counter[counter] = (msb << 8) | lsb;
+ timer_adjust(currptr->timer[counter], attotime_mul(ATTOTIME_IN_HZ(clock), currptr->counter[counter]), which, attotime_zero);
+ }
+
+ /* word mode */
+ else
+ {
+ attotime duration;
+ int word = currptr->counter[counter];
+
+ /* count the clocks */
+ word -= count;
+
+ /* loop while we're less than zero */
+ while (word < 0)
+ {
+ /* borrow from the MSB */
+ word += currptr->latch[counter] + 1;
+
+ /* we've expired */
+ ptm6840_timeout(which, counter);
+ }
+
+ /* store the result */
+ currptr->counter[counter] = word;
+ duration = attotime_mul(ATTOTIME_IN_HZ(clock), currptr->counter[counter]);
+ if (counter == 2) duration = attotime_mul(duration, currptr->t3_divisor);
+ timer_adjust(currptr->timer[counter], duration, which, attotime_zero);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////
+// //
+// Update Internal Interrupts //
+// //
+///////////////////////////////////////////////////////////////////////////
+
+INLINE void update_interrupts(int which)
+{
+ ptm6840 *currptr = ptm + which;
+ currptr->status_reg &= ~0x80;
+
+ if ((currptr->status_reg & 0x01) && (currptr->control_reg[0] & 0x40)) currptr->status_reg |= 0x80;
+ if ((currptr->status_reg & 0x02) && (currptr->control_reg[1] & 0x40)) currptr->status_reg |= 0x80;
+ if ((currptr->status_reg & 0x04) && (currptr->control_reg[2] & 0x40)) currptr->status_reg |= 0x80;
+
+ currptr->IRQ = currptr->status_reg >> 7;
+
+ if ( currptr->intf->irq_func )
+ {
+ currptr->intf->irq_func(currptr->IRQ);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////
+// //
+// Compute Counter //
+// //
+///////////////////////////////////////////////////////////////////////////
+
+static UINT16 compute_counter(int counter, int which)
+{
+ ptm6840 *currptr = ptm + which;
+
+ int clock;
+ int remaining=0;
+
+ /* if there's no timer, return the count */
+ if (!currptr->enabled[counter])
+ return currptr->counter[counter];
+
+ /* determine the clock frequency for this timer */
+ if (currptr->control_reg[counter] & 0x02)
+ {
+ clock = currptr->internal_clock;
+ PLOG(("MC6840 #%d: %d internal clock freq %d \n", which,counter,clock));
+ }
+ else
+ {
+ clock = currptr->external_clock[counter];
+ PLOG(("MC6840 #%d: %d external clock freq %d \n", which,counter,clock));
+ }
+ /* see how many are left */
+ remaining = attotime_to_double(attotime_mul(timer_timeleft(currptr->timer[counter]), clock));
+
+ /* adjust the count for dual byte mode */
+ if (currptr->control_reg[counter] & 0x04)
+ {
+ int divisor = (currptr->counter[counter] & 0xff) + 1;
+ int msb = remaining / divisor;
+ int lsb = remaining % divisor;
+ remaining = (msb << 8) | lsb;
+ }
+ PLOG(("MC6840 #%d: read counter(%d): %d\n", which, counter, remaining));
+ return remaining;
+}
+
+///////////////////////////////////////////////////////////////////////////
+// //
+// Reload Counter //
+// //
+///////////////////////////////////////////////////////////////////////////
+
+static void reload_count(int idx, int which)
+{
+ int clock;
+ int count;
+ attotime duration;
+ ptm6840 *currptr = ptm + which;
+
+ /* copy the latched value in */
+ currptr->counter[idx] = currptr->latch[idx];
+
+ /* determine the clock frequency for this timer */
+ if (currptr->control_reg[idx] & 0x02)
+ {
+ clock = currptr->internal_clock;
+ PLOG(("MC6840 #%d: %d internal clock freq %d \n", which,idx, clock));
+ }
+ else
+ {
+ clock = currptr->external_clock[idx];
+ PLOG(("MC6840 #%d: %d external clock freq %d \n", which,idx, clock));
+ }
+
+ /* determine the number of clock periods before we expire */
+ count = currptr->counter[idx];
+ if (currptr->control_reg[idx] & 0x04)
+ count = ((count >> 8) + 1) * ((count & 0xff) + 1);
+ else
+ count = count + 1;
+
+ currptr->fired[idx]=0;
+
+ if ((currptr->mode[idx] == 4)|(currptr->mode[idx] == 6))
+ {
+ currptr->output[idx] = 1;
+ if ( currptr->intf->out_func[idx] ) currptr->intf->out_func[idx](0, currptr->output[idx]);
+ }
+
+ /* set the timer */
+ PLOG(("MC6840 #%d: reload_count(%d): clock = %d count = %d\n", which, idx, clock, count));
+
+ duration = attotime_mul(ATTOTIME_IN_HZ(clock), count);
+ if (idx == 2) duration = attotime_mul(duration, currptr->t3_divisor);
+ timer_adjust(currptr->timer[idx], duration, which, attotime_zero);
+ PLOG(("MC6840 #%d: reload_count(%d): output = %lf\n", which, idx, attotime_to_double(duration)));
+
+ if (!currptr->control_reg[idx] & 0x02)
+ {
+ if (!currptr->intf->external_clock[idx])
+ {
+ currptr->enabled[idx] = 0;
+ timer_enable(currptr->timer[idx],FALSE);
+ }
+ }
+ else
+ {
+ currptr->enabled[idx] = 1;
+ timer_enable(currptr->timer[idx],TRUE);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////
+// //
+// Configure Timer //
+// //
+///////////////////////////////////////////////////////////////////////////
+
+void ptm6840_config(int which, const ptm6840_interface *intf)
+{
+ int i;
+ ptm6840 *currptr = ptm + which;
+
+ assert_always(mame_get_phase(Machine) == MAME_PHASE_INIT, "Can only call ptm6840_config at init time!");
+ assert_always((which >= 0) && (which < PTM_6840_MAX), "ptm6840_config called on an invalid PTM!");
+ assert_always(intf, "ptm6840_config called with an invalid interface!");
+ ptm[which].intf = intf;
+ ptm[which].internal_clock = currptr->intf->internal_clock;
+
+ for (i = 0; i < 3; i++)
+ {
+ if ( currptr->intf->external_clock[i] )
+ {
+ ptm[which].external_clock[i] = currptr->intf->external_clock[i];
+ }
+ else
+ {
+ ptm[which].external_clock[i] = 1;
+ }
+ }
+
+ ptm[which].timer[0] = timer_alloc(ptm6840_t1_timeout);
+ ptm[which].timer[1] = timer_alloc(ptm6840_t2_timeout);
+ ptm[which].timer[2] = timer_alloc(ptm6840_t3_timeout);
+
+ for (i = 0; i < 3; i++)
+ timer_enable(ptm[which].timer[i], FALSE);
+
+ state_save_register_item("6840ptm", which, currptr->lsb_buffer);
+ state_save_register_item("6840ptm", which, currptr->msb_buffer);
+ state_save_register_item("6840ptm", which, currptr->status_read_since_int);
+ state_save_register_item("6840ptm", which, currptr->status_reg);
+ state_save_register_item("6840ptm", which, currptr->t3_divisor);
+ state_save_register_item("6840ptm", which, currptr->internal_clock);
+ state_save_register_item("6840ptm", which, currptr->IRQ);
+
+ state_save_register_item_array("6840ptm", which, currptr->control_reg);
+ state_save_register_item_array("6840ptm", which, currptr->output);
+ state_save_register_item_array("6840ptm", which, currptr->gate);
+ state_save_register_item_array("6840ptm", which, currptr->clock);
+ state_save_register_item_array("6840ptm", which, currptr->mode);
+ state_save_register_item_array("6840ptm", which, currptr->fired);
+ state_save_register_item_array("6840ptm", which, currptr->enabled);
+ state_save_register_item_array("6840ptm", which, currptr->external_clock);
+ state_save_register_item_array("6840ptm", which, currptr->counter);
+ state_save_register_item_array("6840ptm", which, currptr->latch);
+
+ ptm6840_reset(which);
+
+}
+
+///////////////////////////////////////////////////////////////////////////
+// //
+// Reset Timer //
+// //
+///////////////////////////////////////////////////////////////////////////
+
+void ptm6840_reset(int which)
+{
+ int i;
+ ptm[which].control_reg[2] = 0x00;
+ ptm[which].control_reg[1] = 0x00;
+ ptm[which].control_reg[0] = 0x01;
+ ptm[which].status_reg = 0x00;
+ ptm[which].t3_divisor = 1;
+ ptm[which].status_read_since_int = 0x00;
+
+ for ( i = 0; i < 3; i++ )
+ {
+ ptm[which].counter[i] = 0xffff;
+ ptm[which].latch[i] = 0xffff;
+ ptm[which].output[i] = 0;
+ ptm[which].fired[i] = 0;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////
+// //
+// Read Timer //
+// //
+///////////////////////////////////////////////////////////////////////////
+
+int ptm6840_read(int which, int offset)
+{
+ int val;
+ ptm6840 *currptr = ptm + which;
+ switch ( offset )
+ {
+ case PTM_6840_CTRL1 ://0
+ {
+ val = 0;
+ break;
+ }
+
+ case PTM_6840_CTRL2 ://1
+ {
+ PLOG(("%06X: MC6840 #%d: Status read = %04X\n", activecpu_get_previouspc(), which, currptr->status_reg));
+ currptr->status_read_since_int |= currptr->status_reg & 0x07;
+ val = currptr->status_reg;
+ break;
+ }
+
+ case PTM_6840_MSBBUF1://2
+ case PTM_6840_MSBBUF2://4
+ case PTM_6840_MSBBUF3://6
+ {
+ int idx = (offset - 2)/2;
+ int result = compute_counter(idx, which);
+
+ /* clear the interrupt if the status has been read */
+ if (currptr->status_read_since_int & (1 << idx))
+ {
+ currptr->status_reg &= ~(1 << idx);
+ update_interrupts(which);
+ }
+
+ currptr->lsb_buffer = result & 0xff;
+
+ PLOG(("%06X: MC6840 #%d: Counter %d read = %04X\n", activecpu_get_previouspc(), which, idx, result >> 8));
+ val = result >> 8;
+ break;
+ }
+
+ case PTM_6840_LSB1://3
+ case PTM_6840_LSB2://5
+ case PTM_6840_LSB3://7
+ {
+ val = currptr->lsb_buffer;
+ break;
+ }
+
+ default:
+ {
+ val = 0;
+ break;
+ }
+
+ }
+ return val;
+}
+
+///////////////////////////////////////////////////////////////////////////
+// //
+// Write Timer //
+// //
+///////////////////////////////////////////////////////////////////////////
+
+void ptm6840_write (int which, int offset, int data)
+{
+ ptm6840 *currptr = ptm + which;
+
+ int idx;
+ int i;
+ UINT8 diffs;
+
+ switch ( offset )
+ {
+ case PTM_6840_CTRL1 ://0
+ case PTM_6840_CTRL2 ://1
+ {
+ idx = (offset == 1) ? 1 : (currptr->control_reg[1] & 0x01) ? 0 : 2;
+ diffs = data ^ currptr->control_reg[idx];
+ currptr->t3_divisor = (currptr->control_reg[2] & 0x01) ? 8 : 1;
+ currptr->mode[idx] = (data>>3)&0x07;
+ currptr->control_reg[idx] = data;
+
+ PLOG(("MC6840 #%d : Control register %d selected\n",which,idx));
+ PLOG(("operation mode = %s\n", opmode[ currptr->mode[idx] ]));
+ PLOG(("value = %04X\n", currptr->control_reg[idx]));
+ PLOG(("t3divisor = %d\n", currptr->t3_divisor));
+
+ if (!(currptr->control_reg[idx] & 0x80 ))
+ { // output cleared
+ if ( currptr->intf )
+ {
+ if ( currptr->intf->out_func[idx] ) currptr->intf->out_func[idx](0, 0);
+ }
+ }
+ /* reset? */
+ if (idx == 0 && (diffs & 0x01))
+ {
+ /* holding reset down */
+ if (data & 0x01)
+ {
+ PLOG(("MC6840 #%d : Timer reset\n",which));
+ for (i = 0; i < 3; i++)
+ {
+ timer_enable(currptr->timer[i],FALSE);
+ currptr->enabled[i]=0;
+ }
+ }
+
+ /* releasing reset */
+ else
+ {
+ for (i = 0; i < 3; i++)
+ {
+ reload_count(i,which);
+ }
+ }
+
+ currptr->status_reg = 0;
+ update_interrupts(which);
+
+ /* changing the clock source? (e.g. Zwackery) */
+ if (diffs & 0x02)
+ reload_count(idx,which);
+ }
+ break;
+ }
+
+ /* offsets 2, 4, and 6 are MSB buffer registers */
+ case PTM_6840_MSBBUF1://2
+ case PTM_6840_MSBBUF2://4
+ case PTM_6840_MSBBUF3://6
+ {
+ PLOG(("MC6840 #%d msbbuf%d = %02X\n", which, offset/2, data));
+ currptr->msb_buffer = data;
+ break;
+ }
+
+ /* offsets 3, 5, and 7 are Write Timer Latch commands */
+
+ case PTM_6840_LSB1://3
+ case PTM_6840_LSB2://5
+ case PTM_6840_LSB3://7
+ {
+ idx = (offset - 3)/2;
+ currptr->latch[idx] = (currptr->msb_buffer << 8) | (data & 0xff);
+ /* clear the interrupt */
+ currptr->status_reg &= ~(1 << idx);
+ update_interrupts(which);
+ /* reload the count if in an appropriate mode */
+ if (!(currptr->control_reg[idx] & 0x10))
+ {
+ reload_count(idx, which);
+ }
+ PLOG(("%06X:MC6840 #%d: Counter %d latch = %04X\n", activecpu_get_previouspc(), which, idx, currptr->latch[idx]));
+ break;
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////
+// //
+// ptm6840_timeout: called if timer is mature //
+// //
+///////////////////////////////////////////////////////////////////////////
+
+static void ptm6840_timeout(int which, int idx)
+{
+ ptm6840 *p = ptm + which;
+
+ PLOG(("**ptm6840 %d t%d timeout**\n", which, idx+1));
+
+ if ( p->control_reg[idx] & 0x40 )
+ { // interrupt enabled
+ p->status_reg |= (1 << idx);
+ p->status_read_since_int &= ~(1 << idx);
+ update_interrupts(which);
+ }
+
+ if ( p->control_reg[idx] & 0x80 )
+ { // output enabled
+ if ( p->intf )
+ {
+ if ((p->mode[idx] == 0)|(p->mode[idx] == 2))
+ {
+ p->output[idx] = p->output[idx]?0:1;
+ PLOG(("**ptm6840 %d t%d output %d **\n", which, idx+1, p->output[idx]));
+ if ( p->intf->out_func[idx] ) p->intf->out_func[idx](0, p->output[idx]);
+ }
+ if ((p->mode[idx] == 4)|(p->mode[idx] == 6))
+ {
+ if (!p->fired[idx])
+ {
+ p->output[idx] = 1;
+ PLOG(("**ptm6840 %d t%d output %d **\n", which, idx+1, p->output[idx]));
+ if ( p->intf->out_func[idx] ) p->intf->out_func[idx](0, p->output[idx]);
+ p->fired[idx]=1;//no changes in output until reinit
+ }
+ }
+ }
+ }
+ p->enabled[idx]= 0;
+ reload_count(idx,which);
+}
+
+static TIMER_CALLBACK( ptm6840_t1_timeout ) { ptm6840_timeout(param, 0); }
+static TIMER_CALLBACK( ptm6840_t2_timeout ) { ptm6840_timeout(param, 1); }
+static TIMER_CALLBACK( ptm6840_t3_timeout ) { ptm6840_timeout(param, 2); }
+
+///////////////////////////////////////////////////////////////////////////
+// //
+// ptm6840_set_gate: set gate status (0 or 1) //
+// //
+///////////////////////////////////////////////////////////////////////////
+
+INLINE void ptm6840_set_gate(int which, int state, int idx)
+{
+ ptm6840 *p = ptm + which;
+
+ if ((p->mode[idx] == 0)|(p->mode[idx] == 2)|(p->mode[0] == 4)|(p->mode[idx] == 6))
+ {
+ if (state == 0 && p->gate[idx])
+ reload_count(idx,which);
+ }
+ p->gate[idx] = state;
+}
+
+void ptm6840_set_g1(int which, int state) { ptm6840_set_gate(which, state, 0); }
+void ptm6840_set_g2(int which, int state) { ptm6840_set_gate(which, state, 1); }
+void ptm6840_set_g3(int which, int state) { ptm6840_set_gate(which, state, 2); }
+
+///////////////////////////////////////////////////////////////////////////
+// //
+// ptm6840_set_clock: set clock status (0 or 1) //
+// //
+///////////////////////////////////////////////////////////////////////////
+
+INLINE void ptm6840_set_clock(int which, int state, int idx)
+{
+ ptm6840 *p = ptm + which;
+
+ p->clock[idx] = state;
+
+ if (!(p->control_reg[idx] & 0x02))
+ {
+ if (state) subtract_from_counter(idx,1,which);
+ }
+}
+
+void ptm6840_set_c1(int which, int state) { ptm6840_set_clock(which, state, 0); }
+void ptm6840_set_c2(int which, int state) { ptm6840_set_clock(which, state, 1); }
+void ptm6840_set_c3(int which, int state) { ptm6840_set_clock(which, state, 2); }
+
+///////////////////////////////////////////////////////////////////////////
+
+READ8_HANDLER( ptm6840_0_r ) { return ptm6840_read(0, offset); }
+READ8_HANDLER( ptm6840_1_r ) { return ptm6840_read(1, offset); }
+READ8_HANDLER( ptm6840_2_r ) { return ptm6840_read(2, offset); }
+READ8_HANDLER( ptm6840_3_r ) { return ptm6840_read(3, offset); }
+
+WRITE8_HANDLER( ptm6840_0_w ) { ptm6840_write(0, offset, data); }
+WRITE8_HANDLER( ptm6840_1_w ) { ptm6840_write(1, offset, data); }
+WRITE8_HANDLER( ptm6840_2_w ) { ptm6840_write(2, offset, data); }
+WRITE8_HANDLER( ptm6840_3_w ) { ptm6840_write(3, offset, data); }
+
+READ16_HANDLER( ptm6840_0_msb_r ) { return ptm6840_read(0, offset); }
+READ16_HANDLER( ptm6840_1_msb_r ) { return ptm6840_read(1, offset); }
+READ16_HANDLER( ptm6840_2_msb_r ) { return ptm6840_read(2, offset); }
+READ16_HANDLER( ptm6840_3_msb_r ) { return ptm6840_read(3, offset); }
+
+WRITE16_HANDLER( ptm6840_0_msb_w ) { if (ACCESSING_MSB) ptm6840_write(0, offset, (data >> 8) & 0xff); }
+WRITE16_HANDLER( ptm6840_1_msb_w ) { if (ACCESSING_MSB) ptm6840_write(1, offset, (data >> 8) & 0xff); }
+WRITE16_HANDLER( ptm6840_2_msb_w ) { if (ACCESSING_MSB) ptm6840_write(2, offset, (data >> 8) & 0xff); }
+WRITE16_HANDLER( ptm6840_3_msb_w ) { if (ACCESSING_MSB) ptm6840_write(3, offset, (data >> 8) & 0xff); }
+
+READ16_HANDLER( ptm6840_0_lsb_r ) { return ptm6840_read(0, offset << 8 | 0x00ff); }
+READ16_HANDLER( ptm6840_1_lsb_r ) { return ptm6840_read(1, offset << 8 | 0x00ff); }
+READ16_HANDLER( ptm6840_2_lsb_r ) { return ptm6840_read(2, offset << 8 | 0x00ff); }
+READ16_HANDLER( ptm6840_3_lsb_r ) { return ptm6840_read(3, offset << 8 | 0x00ff); }
+
+WRITE16_HANDLER( ptm6840_0_lsb_w ) {if (ACCESSING_LSB) ptm6840_write(0, offset, data & 0xff);}
+WRITE16_HANDLER( ptm6840_1_lsb_w ) {if (ACCESSING_LSB) ptm6840_write(1, offset, data & 0xff);}
+WRITE16_HANDLER( ptm6840_2_lsb_w ) {if (ACCESSING_LSB) ptm6840_write(2, offset, data & 0xff);}
+WRITE16_HANDLER( ptm6840_3_lsb_w ) {if (ACCESSING_LSB) ptm6840_write(3, offset, data & 0xff);}