summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/machine/ldvp931.c
diff options
context:
space:
mode:
author Aaron Giles <aaron@aarongiles.com>2008-09-25 16:21:35 +0000
committer Aaron Giles <aaron@aarongiles.com>2008-09-25 16:21:35 +0000
commit9b72b5abc479bb16944db43241072d15740b6f25 (patch)
tree029124c77bef8577a90f788217c95618ef3abdb8 /src/emu/machine/ldvp931.c
parente4dc04a323f064827f58eb6774299ed32ca9ecf0 (diff)
Added 22VP931 emulation, which is mostly working. Communication works
fine and basic searching/playback/skipping is functional. Still a bit glitchy. Firefox improvements: - removed need for deprecat.h - memory map is complete from schematics - gutted laserdisc hacks in favor of actual laserdisc implementation - fixed all CPU and sound clocks Removed old laserdsc.c implementation. Added generic timer devices, which simply allocate a timer but don't prime it. This is the preferred method for allocating timers, and may eventually be the only mechanism for doing so in the future.
Diffstat (limited to 'src/emu/machine/ldvp931.c')
-rw-r--r--src/emu/machine/ldvp931.c772
1 files changed, 772 insertions, 0 deletions
diff --git a/src/emu/machine/ldvp931.c b/src/emu/machine/ldvp931.c
new file mode 100644
index 00000000000..34d75e231c8
--- /dev/null
+++ b/src/emu/machine/ldvp931.c
@@ -0,0 +1,772 @@
+/*************************************************************************
+
+ ldvp931.c
+
+ Philips 22VP931 laserdisc emulation.
+
+ Copyright Nicola Salmoria and the MAME Team.
+ Visit http://mamedev.org for licensing and usage restrictions.
+
+**************************************************************************
+
+ Still to do:
+
+ * determine actual slow/fast speeds
+ *
+
+*************************************************************************/
+
+#include "ldcore.h"
+#include "cpu/mcs48/mcs48.h"
+
+
+
+/***************************************************************************
+ DEBUGGING
+***************************************************************************/
+
+#define LOG_COMMANDS 0
+#define LOG_PORTS 0
+
+
+
+/***************************************************************************
+ CONSTANTS
+***************************************************************************/
+
+/* Philips 22VP931 specific information */
+#define VP931_SCAN_SPEED (2000 / 30) /* 2000 frames/second */
+#define VP931_SCAN_FAST_SPEED (4000 / 30) /* 4000 frames/second */
+
+
+
+/***************************************************************************
+ TYPE DEFINITIONS
+***************************************************************************/
+
+/* player-specific data */
+struct _ldplayer_data
+{
+ /* low-level emulation data */
+ int cpunum; /* CPU index of the 8049 */
+ const device_config *tracktimer; /* timer device */
+ int lastframe;
+ UINT8 out0; /* output 0 state */
+ UINT8 out1; /* output 1 state */
+ UINT8 port1; /* port 1 state */
+ UINT8 daticval; /* latched DATIC value */
+ UINT8 daticerp; /* /ERP value from DATIC */
+ UINT8 datastrobe; /* DATA STROBE line from DATIC */
+ UINT8 fromcontroller; /* command byte from the controller */
+ UINT8 fromcontroller_pending; /* TRUE if data is pending */
+ UINT8 tocontroller; /* command byte to the controller */
+ UINT8 tocontroller_pending; /* TRUE if data is pending */
+ INT8 trackdir; /* direction of tracking */
+ UINT8 trackstate; /* state of tracking */
+ UINT8 cmdbuf[3]; /* 3 bytes worth of commands */
+ UINT8 cmdcount; /* number of command bytes seen */
+ INT16 advanced; /* number of frames advanced */
+ void (*data_ready_cb)(const device_config *device, int state); /* data ready callback */
+};
+
+
+
+/***************************************************************************
+ FUNCTION PROTOTYPES
+***************************************************************************/
+
+static void vp931_init(laserdisc_state *ld);
+static void vp931_vsync(laserdisc_state *ld, const vbi_metadata *vbi, int fieldnum, attotime curtime);
+static INT32 vp931_update(laserdisc_state *ld, const vbi_metadata *vbi, int fieldnum, attotime curtime);
+static void vp931_data_w(laserdisc_state *ld, UINT8 prev, UINT8 data);
+static UINT8 vp931_data_r(laserdisc_state *ld);
+static UINT8 vp931_ready(laserdisc_state *ld);
+static UINT8 vp931_data_ready(laserdisc_state *ld);
+
+static WRITE8_HANDLER( output0_w );
+static WRITE8_HANDLER( output1_w );
+static WRITE8_HANDLER( lcd_w );
+static READ8_HANDLER( keypad_r );
+static READ8_HANDLER( datic_r );
+static READ8_HANDLER( from_controller_r );
+static WRITE8_HANDLER( to_controller_w );
+static READ8_HANDLER( port1_r );
+static WRITE8_HANDLER( port1_w );
+static READ8_HANDLER( port2_r );
+static WRITE8_HANDLER( port2_w );
+static READ8_HANDLER( t0_r );
+static READ8_HANDLER( t1_r );
+static TIMER_CALLBACK( vbi_data_fetch );
+static TIMER_CALLBACK( deferred_data_w );
+static TIMER_CALLBACK( irq_off );
+static TIMER_CALLBACK( datastrobe_off );
+static TIMER_CALLBACK( erp_off );
+static TIMER_DEVICE_CALLBACK( track_timer );
+
+
+
+/***************************************************************************
+ INLINE FUNCTIONS
+***************************************************************************/
+
+/*-------------------------------------------------
+ find_vp931 - find our device; assumes there
+ is only one
+-------------------------------------------------*/
+
+INLINE laserdisc_state *find_vp931(running_machine *machine)
+{
+ return ldcore_get_safe_token(device_list_first(machine->config->devicelist, LASERDISC));
+}
+
+
+
+/***************************************************************************
+ 22VP931 ROM AND MACHINE INTERFACES
+***************************************************************************/
+
+static ADDRESS_MAP_START( vp931_portmap, ADDRESS_SPACE_IO, 8 )
+ AM_RANGE(0x00, 0x00) AM_MIRROR(0xcf) AM_READWRITE(keypad_r, output0_w)
+ AM_RANGE(0x10, 0x10) AM_MIRROR(0xcf) AM_WRITE(output1_w)
+ AM_RANGE(0x20, 0x20) AM_MIRROR(0xcf) AM_READWRITE(datic_r, lcd_w)
+ AM_RANGE(0x30, 0x30) AM_MIRROR(0xcf) AM_READWRITE(from_controller_r, to_controller_w)
+ AM_RANGE(MCS48_PORT_P1, MCS48_PORT_P1) AM_READWRITE(port1_r, port1_w)
+ AM_RANGE(MCS48_PORT_P2, MCS48_PORT_P2) AM_READWRITE(port2_r, port2_w)
+ AM_RANGE(MCS48_PORT_T0, MCS48_PORT_T0) AM_READ(t0_r)
+ AM_RANGE(MCS48_PORT_T1, MCS48_PORT_T1) AM_READ(t1_r)
+ADDRESS_MAP_END
+
+
+static MACHINE_DRIVER_START( vp931 )
+ MDRV_CPU_ADD("vp931", I8049, XTAL_11MHz)
+ MDRV_CPU_IO_MAP(vp931_portmap,0)
+ MDRV_TIMER_ADD("tracktimer", track_timer)
+MACHINE_DRIVER_END
+
+
+ROM_START( vp931 )
+ ROM_REGION( 0x800, "vp931", ROMREGION_LOADBYNAME )
+ ROM_LOAD( "at-6-1_a.bin", 0x000, 0x800, CRC(e11b3c8d) SHA1(ea2d7f6a044ed085ce5e09d8b1b1a21c37f0e9b8) )
+ROM_END
+
+
+
+/***************************************************************************
+ PR-8210 PLAYER INTERFACE
+***************************************************************************/
+
+const ldplayer_interface vp931_interface =
+{
+ LASERDISC_TYPE_PHILLIPS_22VP931, /* type of the player */
+ sizeof(ldplayer_data), /* size of the state */
+ "Phillips 22VP931", /* name of the player */
+ rom_vp931, /* pointer to ROM region information */
+ machine_config_vp931, /* pointer to machine configuration */
+ vp931_init, /* initialization callback */
+ vp931_vsync, /* vsync callback */
+ vp931_update, /* update callback */
+ NULL, /* overlay callback */
+ vp931_data_w, /* parallel data write */
+ { /* single line write: */
+ NULL, /* LASERDISC_LINE_ENTER */
+ NULL /* LASERDISC_LINE_CONTROL */
+ },
+ vp931_data_r, /* parallel data read */
+ { /* single line read: */
+ vp931_ready, /* LASERDISC_LINE_READY */
+ NULL, /* LASERDISC_LINE_STATUS */
+ NULL, /* LASERDISC_LINE_COMMAND */
+ vp931_data_ready, /* LASERDISC_LINE_DATA_AVAIL */
+ }
+};
+
+
+
+/***************************************************************************
+ PUBLIC FUNCTIONS
+***************************************************************************/
+
+/*-------------------------------------------------
+ vp931_set_data_ready_callback - set the data
+ ready callback
+-------------------------------------------------*/
+
+void vp931_set_data_ready_callback(const device_config *device, void (*callback)(const device_config *device, int state))
+{
+ laserdisc_state *ld = ldcore_get_safe_token(device);
+ ld->player->data_ready_cb = callback;
+}
+
+
+
+/***************************************************************************
+ PHILLIPS 22VP931 IMPLEMENTATION
+***************************************************************************/
+
+/*-------------------------------------------------
+ vp931_init - Pioneer PR-8210-specific
+ initialization
+-------------------------------------------------*/
+
+static void vp931_init(laserdisc_state *ld)
+{
+ astring *tempstring = astring_alloc();
+ ldplayer_data *player = ld->player;
+
+ /* find our CPU */
+ astring_printf(tempstring, "%s:%s", ld->device->tag, "vp931");
+ player->cpunum = mame_find_cpu_index(ld->device->machine, astring_c(tempstring));
+
+ /* find our timer */
+ astring_printf(tempstring, "%s:%s", ld->device->tag, "tracktimer");
+ player->tracktimer = device_list_find_by_tag(ld->device->machine->config->devicelist, TIMER, astring_c(tempstring));
+ timer_device_set_ptr(player->tracktimer, ld);
+
+ astring_free(tempstring);
+}
+
+
+/*-------------------------------------------------
+ vp931_vsync - VSYNC callback, called at the
+ start of the blanking period
+-------------------------------------------------*/
+
+static void vp931_vsync(laserdisc_state *ld, const vbi_metadata *vbi, int fieldnum, attotime curtime)
+{
+ /* reset our command counter (debugging only) */
+ ld->player->cmdcount = 0;
+
+ /* set the ERP signal to 1 to indicate start of frame, and set a timer to turn it off */
+ ld->player->daticerp = 1;
+ timer_set(video_screen_get_time_until_pos(ld->screen, 15*2, 0), ld, 0, erp_off);
+}
+
+
+/*-------------------------------------------------
+ vp931_update - update callback, called on
+ the first visible line of the frame
+-------------------------------------------------*/
+
+static INT32 vp931_update(laserdisc_state *ld, const vbi_metadata *vbi, int fieldnum, attotime curtime)
+{
+ /* set the first VBI timer to go at the start of line 16 */
+ timer_set(video_screen_get_time_until_pos(ld->screen, 16*2, 0), ld, LASERDISC_CODE_LINE16 << 2, vbi_data_fetch);
+
+ /* play forward by default */
+ return fieldnum;
+}
+
+
+/*-------------------------------------------------
+ vp931_data_w - handle a parallel data write
+ to the 22VP931
+-------------------------------------------------*/
+
+static void vp931_data_w(laserdisc_state *ld, UINT8 prev, UINT8 data)
+{
+ /* set a timer to synchronize execution before sending the data */
+ timer_call_after_resynch(ld, data, deferred_data_w);
+}
+
+
+/*-------------------------------------------------
+ vp931_data_r - handle a parallel data read
+ from the 22VP931
+-------------------------------------------------*/
+
+static UINT8 vp931_data_r(laserdisc_state *ld)
+{
+ ldplayer_data *player = ld->player;
+
+ /* if data is pending, clear the pending flag and notify any callbacks */
+ if (player->tocontroller_pending)
+ {
+ player->tocontroller_pending = FALSE;
+ if (player->data_ready_cb != NULL)
+ (*player->data_ready_cb)(ld->device, FALSE);
+ }
+
+ /* also boost interleave for 4 scanlines to ensure proper communications */
+ cpu_boost_interleave(attotime_zero, attotime_mul(video_screen_get_scan_period(ld->screen), 4));
+ return player->tocontroller;
+}
+
+
+/*-------------------------------------------------
+ vp931_ready - return the status of "ready"
+ to the caller (ready to accept another
+ command)
+-------------------------------------------------*/
+
+static UINT8 vp931_ready(laserdisc_state *ld)
+{
+ /* if data is pending, we are not ready */
+ ldplayer_data *player = ld->player;
+ return player->fromcontroller_pending ? CLEAR_LINE : ASSERT_LINE;
+}
+
+
+/*-------------------------------------------------
+ vp931_data_ready - return the status of
+ "data available" to the caller
+-------------------------------------------------*/
+
+static UINT8 vp931_data_ready(laserdisc_state *ld)
+{
+ ldplayer_data *player = ld->player;
+ return player->tocontroller_pending ? ASSERT_LINE : CLEAR_LINE;
+}
+
+
+/*-------------------------------------------------
+ output0_w - controls audio/video squelch
+ and other bits
+-------------------------------------------------*/
+
+static WRITE8_HANDLER( output0_w )
+{
+ laserdisc_state *ld = find_vp931(machine);
+ ldplayer_data *player = ld->player;
+
+ /*
+ $80 = n/c
+ $40 = LED (?) -> C335
+ $20 = LED (?)
+ $10 = LED (?) -> CX
+ $08 = EJECT
+ $04 = inverted -> AUDIO MUTE II
+ $02 = inverted -> AUDIO MUTE I
+ $01 = inverted -> VIDEO MUTE
+ */
+
+ if (LOG_PORTS && (player->out0 ^ data) & 0xff)
+ {
+ printf("%03X:out0:", activecpu_get_pc());
+ if ( (data & 0x80)) printf(" ???");
+ if ( (data & 0x40)) printf(" LED1");
+ if ( (data & 0x20)) printf(" LED2");
+ if ( (data & 0x10)) printf(" LED3");
+ if ( (data & 0x08)) printf(" EJECT");
+ if (!(data & 0x04)) printf(" AUDMUTE2");
+ if (!(data & 0x02)) printf(" AUDMUTE1");
+ if (!(data & 0x01)) printf(" VIDMUTE");
+ printf("\n");
+ player->out0 = data;
+ }
+
+ /* update a/v squelch */
+ ldcore_set_audio_squelch(ld, !(data & 0x02), !(data & 0x04));
+ ldcore_set_video_squelch(ld, !(data & 0x01));
+}
+
+
+/*-------------------------------------------------
+ output1_w - controls scanning behaviors
+-------------------------------------------------*/
+
+static WRITE8_HANDLER( output1_w )
+{
+ laserdisc_state *ld = find_vp931(machine);
+ ldplayer_data *player = ld->player;
+ INT32 speed = 0;
+
+ /*
+ $80 = n/c
+ $40 = n/c
+ $20 = n/c
+ $10 = n/c
+ $08 = inverted -> SMS
+ $04 = inverted -> SSS
+ $02 = inverted -> SCAN CMD
+ $01 = OSM
+ */
+
+ if (LOG_PORTS && (player->out1 ^ data) & 0x08)
+ {
+ mame_printf_debug("%03X:out1:", activecpu_get_pc());
+ if (!(data & 0x08)) mame_printf_debug(" SMS");
+ mame_printf_debug("\n");
+ player->out1 = data;
+ }
+
+ /* speed is 0 unless SCAN CMD is clear */
+ speed = 0;
+ if (!(data & 0x02))
+ {
+ /* fast/slow is based on bit 2 */
+ speed = (data & 0x04) ? VP931_SCAN_FAST_SPEED : VP931_SCAN_SPEED;
+
+ /* direction is based on bit 0 */
+ if (data & 0x01)
+ speed = -speed;
+ }
+
+ /* update the speed */
+ ldcore_set_slider_speed(ld, speed);
+}
+
+
+/*-------------------------------------------------
+ lcd_w - vestigial LCD frame display
+-------------------------------------------------*/
+
+static WRITE8_HANDLER( lcd_w )
+{
+ /*
+ Frame number is written as 5 digits here; however, it is not actually
+ connected
+ */
+}
+
+
+/*-------------------------------------------------
+ keypad_r - vestigial keypad/button controls
+-------------------------------------------------*/
+
+static READ8_HANDLER( keypad_r )
+{
+ /*
+ From the code, this is apparently a vestigial keypad with basic controls:
+ $01 = play
+ $02 = still
+ $04 = jump 25 frames backward
+ $08 = jump 25 frames forward
+ $10 = search for frame 50(?)
+ $20 = search for frame 350(?)
+ $40 = reset
+ $80 = play reverse
+ */
+ return 0x00;
+}
+
+
+/*-------------------------------------------------
+ datic_r - read the latched value from the
+ DATIC circuit
+-------------------------------------------------*/
+
+static READ8_HANDLER( datic_r )
+{
+ laserdisc_state *ld = find_vp931(machine);
+ return ld->player->daticval;
+}
+
+
+/*-------------------------------------------------
+ from_controller_r - read the value the
+ external controller wrote
+-------------------------------------------------*/
+
+static READ8_HANDLER( from_controller_r )
+{
+ laserdisc_state *ld = find_vp931(machine);
+ ldplayer_data *player = ld->player;
+
+ /* clear the pending flag and return the data */
+ player->fromcontroller_pending = FALSE;
+ return player->fromcontroller;
+}
+
+
+/*-------------------------------------------------
+ to_controller_w - write a value back to the
+ external controller
+-------------------------------------------------*/
+
+static WRITE8_HANDLER( to_controller_w )
+{
+ laserdisc_state *ld = find_vp931(machine);
+ ldplayer_data *player = ld->player;
+
+ /* set the pending flag and stash the data */
+ player->tocontroller_pending = TRUE;
+ player->tocontroller = data;
+
+ /* signal to the callback if provided */
+ if (player->data_ready_cb != NULL)
+ (*player->data_ready_cb)(ld->device, TRUE);
+
+ /* also boost interleave for 4 scanlines to ensure proper communications */
+ cpu_boost_interleave(attotime_zero, attotime_mul(video_screen_get_scan_period(ld->screen), 4));
+}
+
+
+/*-------------------------------------------------
+ port1_r - read the 8048 I/O port 1
+-------------------------------------------------*/
+
+static READ8_HANDLER( port1_r )
+{
+ laserdisc_state *ld = find_vp931(machine);
+ ldplayer_data *player = ld->player;
+ UINT8 result = 0x00;
+
+ /*
+ $80 = P17 = (in) unsure
+ $40 = P16 = (in) /ERP from datic circuit
+ $20 = P15 = (in) D105
+ */
+
+ if (!player->daticerp)
+ result |= 0x40;
+
+ return result;
+}
+
+
+/*-------------------------------------------------
+ port1_w - write the 8048 I/O port 1
+-------------------------------------------------*/
+
+static WRITE8_HANDLER( port1_w )
+{
+ laserdisc_state *ld = find_vp931(machine);
+ ldplayer_data *player = ld->player;
+
+ /*
+ $10 = P14 = (out) D104 -> /SPEED
+ $08 = P13 = (out) D103 -> /TIMER ENABLE
+ $04 = P12 = (out) D102 -> /REV
+ $02 = P11 = (out) D101 -> /FORW
+ $01 = P10 = (out) D100 -> some op-amp then to C334, B56, B332
+ */
+
+ if (LOG_PORTS && (player->port1 ^ data) & 0x1f)
+ {
+ printf("%03X:port1:", activecpu_get_pc());
+ if (!(data & 0x10)) printf(" SPEED");
+ if (!(data & 0x08)) printf(" TIMENABLE");
+ if (!(data & 0x04)) printf(" REV");
+ if (!(data & 0x02)) printf(" FORW");
+ if (!(data & 0x01)) printf(" OPAMP");
+ printf("\n");
+ }
+
+ /* if bit 0 is set, we are not tracking */
+ if (data & 0x01)
+ player->trackdir = 0;
+
+ /* if bit 0 is clear and we weren't tracking before, initialize the state */
+ else if (player->trackdir == 0)
+ {
+ player->advanced = 0;
+
+ /* if bit 2 is clear, we are moving backwards */
+ if (!(data & 0x04))
+ {
+ player->trackdir = -1;
+ player->trackstate = 1;
+ }
+
+ /* if bit 1 is clear, we are moving forward */
+ else if (!(data & 0x02))
+ {
+ player->trackdir = 1;
+ player->trackstate = 0;
+ }
+ }
+
+ /* if we have a timer, adjust it */
+ if (player->tracktimer != NULL)
+ {
+ /* turn it off if we're not tracking */
+ if (player->trackdir == 0)
+ timer_device_adjust_periodic(player->tracktimer, attotime_never, 0, attotime_never);
+
+ /* if we just started tracking, or if the speed was changed, reprime the timer */
+ else if (((player->port1 ^ data) & 0x11) != 0)
+ {
+ /* speeds here are just guesses, but work with the player logic; this is the time per half-track */
+ attotime speed = (data & 0x10) ? ATTOTIME_IN_USEC(60) : ATTOTIME_IN_USEC(10);
+
+ /* always start with an initial long delay; the code expects this */
+ timer_device_adjust_periodic(player->tracktimer, ATTOTIME_IN_USEC(100), 0, speed);
+ }
+ }
+
+ player->port1 = data;
+}
+
+
+/*-------------------------------------------------
+ port2_r - read from the 8048 I/O port 2
+-------------------------------------------------*/
+
+static READ8_HANDLER( port2_r )
+{
+ laserdisc_state *ld = find_vp931(machine);
+ ldplayer_data *player = ld->player;
+ UINT8 result = 0x00;
+
+ /*
+ $80 = P27 = (in) set/reset latch; set by FOC LS, reset by IGR
+ $20 = P25 = (in) D125 -> 0 when data written to controller is preset, reset to 1 when read
+ $10 = P24 = (in) D124 -> 0 when data from controller is present, reset to 1 on a read
+ */
+
+ if (!player->tocontroller_pending)
+ result |= 0x20;
+ if (!player->fromcontroller_pending)
+ result |= 0x10;
+
+ return result;
+}
+
+
+/*-------------------------------------------------
+ port2_w - write the 8048 I/O port 2
+-------------------------------------------------*/
+
+static WRITE8_HANDLER( port2_w )
+{
+ /*
+ $40 = P26 = (out) cleared while data is sent back & forth; set afterwards
+ [Not actually connected, but this is done in the code]
+ */
+}
+
+
+/*-------------------------------------------------
+ t0_r - return the T0 line status, which is
+ connected to the DATIC's data strobe line
+-------------------------------------------------*/
+
+static READ8_HANDLER( t0_r )
+{
+ laserdisc_state *ld = find_vp931(machine);
+ return ld->player->datastrobe;
+}
+
+
+/*-------------------------------------------------
+ t1_r - return the T1 line status, which is
+ connected to the tracking state and is used
+ to count the number of tracks advanced
+-------------------------------------------------*/
+
+static READ8_HANDLER( t1_r )
+{
+ laserdisc_state *ld = find_vp931(machine);
+ return ld->player->trackstate;
+}
+
+
+/*-------------------------------------------------
+ vbi_data_fetch - called 4 times per scanline
+ on lines 16, 17, and 18 to feed the VBI data
+ through one byte at a time
+-------------------------------------------------*/
+
+static TIMER_CALLBACK( vbi_data_fetch )
+{
+ laserdisc_state *ld = ptr;
+ ldplayer_data *player = ld->player;
+ int which = param & 3;
+ int line = param >> 2;
+ UINT32 code = 0;
+
+ /* fetch the code and compute the DATIC latched value */
+ if (line >= LASERDISC_CODE_LINE16 && line <= LASERDISC_CODE_LINE18)
+ code = laserdisc_get_field_code(ld->device, line);
+
+ /* at the start of each line, signal an interrupt and use a timer to turn it off */
+ if (which == 0)
+ {
+ cpunum_set_input_line(machine, player->cpunum, MCS48_INPUT_IRQ, ASSERT_LINE);
+ timer_set(ATTOTIME_IN_NSEC(5580), ld, 0, irq_off);
+ }
+
+ /* clock the data strobe on each subsequent callback */
+ else if (code != 0)
+ {
+ player->daticval = code >> (8 * (3 - which));
+ player->datastrobe = 1;
+ timer_set(ATTOTIME_IN_NSEC(5000), ld, 0, datastrobe_off);
+ }
+
+ /* determine the next bit to fetch and reprime ourself */
+ if (++which == 4)
+ {
+ which = 0;
+ line++;
+ }
+ if (line <= LASERDISC_CODE_LINE18 + 1)
+ timer_set(video_screen_get_time_until_pos(ld->screen, line*2, which * 2 * video_screen_get_width(ld->screen) / 4), ld, (line << 2) | which, vbi_data_fetch);
+}
+
+
+/*-------------------------------------------------
+ deferred_data_w - handle a write from the
+ external controller
+-------------------------------------------------*/
+
+static TIMER_CALLBACK( deferred_data_w )
+{
+ laserdisc_state *ld = ptr;
+ ldplayer_data *player = ld->player;
+
+ /* set the value and mark it pending */
+ player->fromcontroller = param;
+ player->fromcontroller_pending = TRUE;
+
+ /* track the commands for debugging purposes */
+ if (player->cmdcount < ARRAY_LENGTH(player->cmdbuf))
+ {
+ player->cmdbuf[player->cmdcount++] = param;
+ if (LOG_COMMANDS && player->cmdcount == 3)
+ printf("Cmd: %02X %02X %02X\n", player->cmdbuf[0], player->cmdbuf[1], player->cmdbuf[2]);
+ }
+}
+
+
+/*-------------------------------------------------
+ irq_off - turn off the 8048 IRQ signal
+-------------------------------------------------*/
+
+static TIMER_CALLBACK( irq_off )
+{
+ laserdisc_state *ld = ptr;
+ cpunum_set_input_line(machine, ld->player->cpunum, MCS48_INPUT_IRQ, CLEAR_LINE);
+}
+
+
+/*-------------------------------------------------
+ datastrobe_off - turn off the DATIC data
+ strobe signal
+-------------------------------------------------*/
+
+static TIMER_CALLBACK( datastrobe_off )
+{
+ laserdisc_state *ld = ptr;
+ ld->player->datastrobe = 0;
+}
+
+
+/*-------------------------------------------------
+ erp_off - turn off the DATIC ERP signal
+-------------------------------------------------*/
+
+static TIMER_CALLBACK( erp_off )
+{
+ laserdisc_state *ld = ptr;
+ ld->player->daticerp = 0;
+}
+
+
+/*-------------------------------------------------
+ track_timer - advance by one half-track
+-------------------------------------------------*/
+
+static TIMER_DEVICE_CALLBACK( track_timer )
+{
+ laserdisc_state *ld = ptr;
+ ldplayer_data *player = ld->player;
+
+ /* advance by the count and toggle the state */
+ player->trackstate ^= 1;
+ if ((player->trackdir < 0 && !player->trackstate) || (player->trackdir > 0 && player->trackstate))
+ {
+ ldcore_advance_slider(ld, player->trackdir);
+ player->advanced += player->trackdir;
+ }
+}