summaryrefslogtreecommitdiffstatshomepage
path: root/trunk/src/emu/machine/ldpr8210.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/src/emu/machine/ldpr8210.c')
-rw-r--r--trunk/src/emu/machine/ldpr8210.c1271
1 files changed, 1271 insertions, 0 deletions
diff --git a/trunk/src/emu/machine/ldpr8210.c b/trunk/src/emu/machine/ldpr8210.c
new file mode 100644
index 00000000000..00f05bbef9d
--- /dev/null
+++ b/trunk/src/emu/machine/ldpr8210.c
@@ -0,0 +1,1271 @@
+/*************************************************************************
+
+ ldpr8210.c
+
+ Pioneer PR-8210 laserdisc emulation.
+
+ Copyright Nicola Salmoria and the MAME Team.
+ Visit http://mamedev.org for licensing and usage restrictions.
+
+**************************************************************************
+
+ Still to do:
+
+ * implement SLOW TRG
+ * figure out Simutrek without jump hack
+ * figure out serial protocol issues (current hack works nicely)
+ * determine actual slow/fast speeds
+
+*************************************************************************/
+
+#include "emu.h"
+#include "ldcore.h"
+#include "cpu/mcs48/mcs48.h"
+
+
+
+/***************************************************************************
+ DEBUGGING
+***************************************************************************/
+
+#define LOG_VBLANK_VBI 0
+#define LOG_SERIAL 0
+#define LOG_SIMUTREK 0
+
+
+
+/***************************************************************************
+ CONSTANTS
+***************************************************************************/
+
+/* Overlay constants, related to 720-pixel wide capture */
+#define OVERLAY_GROUP0_X (82.0f / 720.0f)
+#define OVERLAY_GROUP1_X (162.0f / 720.0f)
+#define OVERLAY_GROUP2_X (322.0f / 720.0f)
+#define OVERLAY_GROUP3_X (483.0f / 720.0f)
+#define OVERLAY_Y (104/2)
+#define OVERLAY_PIXEL_WIDTH (4.5f / 720.0f)
+#define OVERLAY_PIXEL_HEIGHT 2
+#define OVERLAY_X_PIXELS 5
+#define OVERLAY_Y_PIXELS 7
+
+/* scanning speeds */
+#define SCAN_SPEED (2000 / 30) /* 2000 frames/second */
+#define SEEK_FAST_SPEED (4000 / 30) /* 4000 frames/second */
+
+/* serial timing, mostly from the service manual, derived from the XTAL */
+#define SERIAL_CLOCK XTAL_455kHz
+#define SERIAL_0_BIT_TIME attotime::from_hz((double)SERIAL_CLOCK / 512)
+#define SERIAL_1_BIT_TIME attotime::from_hz((double)SERIAL_CLOCK / 1024)
+#define SERIAL_MIDPOINT_TIME attotime::from_hz((double)SERIAL_CLOCK / 600)
+#define SERIAL_MAX_BIT_TIME attotime::from_hz((double)SERIAL_CLOCK / 4096)
+#define SERIAL_MAX_WORD_TIME attotime::from_hz((double)SERIAL_CLOCK / 11520)
+#define SERIAL_REJECT_DUPLICATE_TIME attotime::from_hz((double)SERIAL_CLOCK / 11520 / 4)
+
+
+
+/***************************************************************************
+ TYPE DEFINITIONS
+***************************************************************************/
+
+/* PIA data */
+typedef struct _pioneer_pia pioneer_pia;
+struct _pioneer_pia
+{
+ UINT8 frame[7]; /* (20-26) 7 characters for the chapter/frame */
+ UINT8 text[17]; /* (20-30) 17 characters for the display */
+ UINT8 control; /* (40) control lines */
+ UINT8 latchdisplay; /* flag: set if the display was latched */
+ UINT8 portb; /* (60) port B value (LEDs) */
+ UINT8 display; /* (80) display enable */
+ UINT8 porta; /* (A0) port A value (from serial decoder) */
+ UINT8 vbi1; /* (C0) VBI decoding state 1 */
+ UINT8 vbi2; /* (E0) VBI decoding state 2 */
+};
+
+
+/* Simutrek-specific data */
+typedef struct _simutrek_data simutrek_data;
+struct _simutrek_data
+{
+ device_t *cpu; /* 8748 CPU device */
+ UINT8 audio_squelch; /* audio squelch value */
+ UINT8 data; /* parallel data for simutrek */
+ UINT8 data_ready; /* ready flag for simutrek data */
+ UINT8 port2; /* 8748 port 2 state */
+ UINT8 controlnext; /* latch to control next pair of fields */
+ UINT8 controlthis; /* latched value for our control over the current pair of fields */
+};
+
+
+/* player-specific data */
+struct _ldplayer_data
+{
+ /* serial/command interface processing */
+ UINT8 lastcommand; /* last command seen */
+ UINT16 accumulator; /* bit accumulator */
+ attotime lastcommandtime; /* time of the last command */
+ attotime lastbittime; /* time of last bit received */
+ attotime firstbittime; /* time of first bit in command */
+
+ /* low-level emulation data */
+ device_t *cpu; /* 8049 CPU device */
+ attotime slowtrg; /* time of the last SLOW TRG */
+ pioneer_pia pia; /* PIA state */
+ UINT8 vsync; /* live VSYNC state */
+ UINT8 port1; /* 8049 port 1 state */
+ UINT8 port2; /* 8049 port 2 state */
+
+ /* Simutrek-specific data */
+ simutrek_data simutrek; /* Simutrek-specific data */
+};
+
+
+
+/***************************************************************************
+ FUNCTION PROTOTYPES
+***************************************************************************/
+
+static void pr8210_init(laserdisc_state *ld);
+static void pr8210_vsync(laserdisc_state *ld, const vbi_metadata *vbi, int fieldnum, attotime curtime);
+static INT32 pr8210_update(laserdisc_state *ld, const vbi_metadata *vbi, int fieldnum, attotime curtime);
+static void pr8210_overlay(laserdisc_state *ld, bitmap_t *bitmap);
+static void pr8210_control_w(laserdisc_state *ld, UINT8 prev, UINT8 data);
+
+static TIMER_CALLBACK( vsync_off );
+static TIMER_CALLBACK( vbi_data_fetch );
+
+static READ8_HANDLER( pr8210_pia_r );
+static WRITE8_HANDLER( pr8210_pia_w );
+static READ8_HANDLER( pr8210_bus_r );
+static WRITE8_HANDLER( pr8210_port1_w );
+static WRITE8_HANDLER( pr8210_port2_w );
+static READ8_HANDLER( pr8210_t0_r );
+static READ8_HANDLER( pr8210_t1_r );
+
+static void overlay_draw_group(bitmap_t *bitmap, const UINT8 *text, int count, float xstart);
+static void overlay_erase(bitmap_t *bitmap, float xstart, float xend);
+static void overlay_draw_char(bitmap_t *bitmap, UINT8 ch, float xstart);
+
+static void simutrek_init(laserdisc_state *ld);
+static void simutrek_vsync(laserdisc_state *ld, const vbi_metadata *vbi, int fieldnum, attotime curtime);
+static INT32 simutrek_update(laserdisc_state *ld, const vbi_metadata *vbi, int fieldnum, attotime curtime);
+static UINT8 simutrek_ready_r(laserdisc_state *ld);
+static UINT8 simutrek_status_r(laserdisc_state *ld);
+static void simutrek_data_w(laserdisc_state *ld, UINT8 prev, UINT8 data);
+
+static TIMER_CALLBACK( simutrek_latched_data_w );
+
+static READ8_HANDLER( simutrek_port2_r );
+static WRITE8_HANDLER( simutrek_port2_w );
+static READ8_HANDLER( simutrek_data_r );
+static READ8_HANDLER( simutrek_t0_r );
+
+
+
+/***************************************************************************
+ GLOBAL VARIABLES
+***************************************************************************/
+
+/* bitmaps for the characters */
+static const UINT8 text_bitmap[0x40][7] =
+{
+ { 0 }, /* @ */
+ { 0x20,0x50,0x88,0x88,0xf8,0x88,0x88 }, /* A */
+ { 0 }, /* B */
+ { 0x70,0x88,0x80,0x80,0x80,0x88,0x70 }, /* C */
+ { 0 }, /* D */
+ { 0xf8,0x80,0x80,0xf0,0x80,0x80,0xf8 }, /* E */
+ { 0xf8,0x80,0x80,0xf0,0x80,0x80,0x80 }, /* F */
+ { 0 }, /* G */
+ { 0x88,0x88,0x88,0xf8,0x88,0x88,0x88 }, /* H */
+ { 0 }, /* I */
+ { 0 }, /* J */
+ { 0 }, /* K */
+ { 0 }, /* L */
+ { 0x88,0xd8,0xa8,0xa8,0xa8,0x88,0x88 }, /* M */
+ { 0 }, /* N */
+ { 0 }, /* O */
+ { 0xf0,0x88,0x88,0xf0,0x80,0x80,0x80 }, /* P */
+ { 0 }, /* Q */
+ { 0xf0,0x88,0x88,0xf0,0xa0,0x90,0x88 }, /* R */
+ { 0x70,0x88,0x80,0x70,0x08,0x88,0x70 }, /* S */
+ { 0 }, /* T */
+ { 0 }, /* U */
+ { 0 }, /* V */
+ { 0 }, /* W */
+ { 0 }, /* X */
+ { 0 }, /* Y */
+ { 0 }, /* Z */
+ { 0 }, /* [ */
+ { 0 }, /* \ */
+ { 0 }, /* ] */
+ { 0 }, /* ^ */
+ { 0 }, /* _ */
+
+ { 0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, /* <space> */
+ { 0 }, /* ! */
+ { 0 }, /* " */
+ { 0 }, /* # */
+ { 0 }, /* $ */
+ { 0 }, /* % */
+ { 0 }, /* & */
+ { 0 }, /* ' */
+ { 0 }, /* ( */
+ { 0 }, /* ) */
+ { 0 }, /* * */
+ { 0 }, /* + */
+ { 0 }, /* , */
+ { 0 }, /* - */
+ { 0x00,0x00,0x00,0x00,0x00,0x00,0x40 }, /* . */
+ { 0 }, /* / */
+ { 0x70,0x88,0x88,0x88,0x88,0x88,0x70 }, /* 0 */
+ { 0x20,0x60,0x20,0x20,0x20,0x20,0x70 }, /* 1 */
+ { 0x70,0x88,0x08,0x70,0x80,0x80,0xf8 }, /* 2 */
+ { 0xf8,0x08,0x10,0x30,0x08,0x88,0x70 }, /* 3 */
+ { 0x10,0x30,0x50,0x90,0xf8,0x10,0x10 }, /* 4 */
+ { 0xf8,0x80,0xf0,0x08,0x08,0x88,0x70 }, /* 5 */
+ { 0x78,0x80,0x80,0xf0,0x88,0x88,0x70 }, /* 6 */
+ { 0xf8,0x08,0x08,0x10,0x20,0x40,0x80 }, /* 7 */
+ { 0x70,0x88,0x88,0x70,0x88,0x88,0x70 }, /* 8 */
+ { 0x70,0x88,0x88,0x78,0x08,0x08,0xf0 }, /* 9 */
+ { 0 }, /* : */
+ { 0 }, /* ; */
+ { 0 }, /* < */
+ { 0 }, /* = */
+ { 0 }, /* > */
+ { 0 } /* ? */
+};
+
+
+
+/***************************************************************************
+ INLINE FUNCTIONS
+***************************************************************************/
+
+/*-------------------------------------------------
+ update_video_squelch - update the state of
+ the video squelch
+-------------------------------------------------*/
+
+INLINE void update_video_squelch(laserdisc_state *ld)
+{
+ ldplayer_data *player = ld->player;
+ ldcore_set_video_squelch(ld, (player->port1 & 0x20) != 0);
+}
+
+
+/*-------------------------------------------------
+ update_audio_squelch - update the state of
+ the audio squelch
+-------------------------------------------------*/
+
+INLINE void update_audio_squelch(laserdisc_state *ld)
+{
+ ldplayer_data *player = ld->player;
+ if (player->simutrek.cpu == NULL)
+ ldcore_set_audio_squelch(ld, (player->port1 & 0x40) || !(player->pia.portb & 0x01), (player->port1 & 0x40) || !(player->pia.portb & 0x02));
+ else
+ ldcore_set_audio_squelch(ld, player->simutrek.audio_squelch, player->simutrek.audio_squelch);
+}
+
+
+
+/***************************************************************************
+ PR-8210 ROM AND MACHINE INTERFACES
+***************************************************************************/
+
+static ADDRESS_MAP_START( pr8210_portmap, AS_IO, 8 )
+ AM_RANGE(0x00, 0xff) AM_READWRITE(pr8210_pia_r, pr8210_pia_w)
+ AM_RANGE(MCS48_PORT_BUS, MCS48_PORT_BUS) AM_READ(pr8210_bus_r)
+ AM_RANGE(MCS48_PORT_P1, MCS48_PORT_P1) AM_WRITE(pr8210_port1_w)
+ AM_RANGE(MCS48_PORT_P2, MCS48_PORT_P2) AM_WRITE(pr8210_port2_w)
+ AM_RANGE(MCS48_PORT_T0, MCS48_PORT_T0) AM_READ(pr8210_t0_r)
+ AM_RANGE(MCS48_PORT_T1, MCS48_PORT_T1) AM_READ(pr8210_t1_r)
+ADDRESS_MAP_END
+
+
+static MACHINE_CONFIG_FRAGMENT( pr8210 )
+ MCFG_CPU_ADD("pr8210", I8049, XTAL_4_41MHz)
+ MCFG_CPU_IO_MAP(pr8210_portmap)
+MACHINE_CONFIG_END
+
+
+ROM_START( pr8210 )
+ ROM_REGION( 0x800, "pr8210", 0 )
+ ROM_LOAD( "pr-8210_mcu_ud6005a.bin", 0x000, 0x800, CRC(120fa83b) SHA1(b514326ca1f52d6d89056868f9d17eabd4e3f31d) )
+ROM_END
+
+
+
+/***************************************************************************
+ PR-8210 PLAYER INTERFACE
+***************************************************************************/
+
+const ldplayer_interface pr8210_interface =
+{
+ LASERDISC_TYPE_PIONEER_PR8210, /* type of the player */
+ sizeof(ldplayer_data), /* size of the state */
+ "Pioneer PR-8210", /* name of the player */
+ "pr8210", /* shortname of the player */
+ ROM_NAME(pr8210), /* pointer to ROM region information */
+ MACHINE_CONFIG_NAME(pr8210), /* pointer to machine configuration */
+ pr8210_init, /* initialization callback */
+ pr8210_vsync, /* vsync callback */
+ pr8210_update, /* update callback */
+ pr8210_overlay, /* overlay callback */
+ NULL, /* parallel data write */
+ { /* single line write: */
+ NULL, /* LASERDISC_LINE_ENTER */
+ pr8210_control_w /* LASERDISC_LINE_CONTROL */
+ },
+ NULL, /* parallel data read */
+ { /* single line read: */
+ NULL, /* LASERDISC_LINE_READY */
+ NULL, /* LASERDISC_LINE_STATUS */
+ NULL, /* LASERDISC_LINE_COMMAND */
+ NULL, /* LASERDISC_LINE_DATA_AVAIL */
+ }
+};
+
+
+
+/***************************************************************************
+ PIONEER PR-8210 IMPLEMENTATION
+***************************************************************************/
+
+/*-------------------------------------------------
+ pr8210_init - player-specific initialization
+-------------------------------------------------*/
+
+static void pr8210_init(laserdisc_state *ld)
+{
+ astring tempstring;
+ attotime curtime = ld->device->machine().time();
+ ldplayer_data *player = ld->player;
+
+ /* reset our state */
+ memset(player, 0, sizeof(*player));
+ player->lastcommandtime = curtime;
+ player->firstbittime = curtime;
+ player->lastbittime = curtime;
+ player->slowtrg = curtime;
+
+ /* find our CPU */
+ player->cpu = ld->device->subdevice("pr8210");
+ assert(player->cpu != NULL);
+
+ /* we don't have the Simutrek player overrides */
+ player->simutrek.cpu = NULL;
+ player->simutrek.audio_squelch = FALSE;
+}
+
+
+/*-------------------------------------------------
+ pr8210_vsync - VSYNC callback, called at the
+ start of the blanking period
+-------------------------------------------------*/
+
+static void pr8210_vsync(laserdisc_state *ld, const vbi_metadata *vbi, int fieldnum, attotime curtime)
+{
+ ldplayer_data *player = ld->player;
+
+ /* logging */
+ if (LOG_VBLANK_VBI)
+ {
+ if ((vbi->line1718 & VBI_MASK_CAV_PICTURE) == VBI_CODE_CAV_PICTURE)
+ printf("%3d:VSYNC(%d,%05d)\n", ld->screen->vpos(), fieldnum, VBI_CAV_PICTURE(vbi->line1718));
+ else
+ printf("%3d:VSYNC(%d)\n", ld->screen->vpos(), fieldnum);
+ }
+
+ /* signal VSYNC and set a timer to turn it off */
+ player->vsync = TRUE;
+ ld->device->machine().scheduler().timer_set(ld->screen->scan_period() * 4, FUNC(vsync_off), 0, ld);
+
+ /* also set a timer to fetch the VBI data when it is ready */
+ ld->device->machine().scheduler().timer_set(ld->screen->time_until_pos(19*2), FUNC(vbi_data_fetch), 0, ld);
+}
+
+
+/*-------------------------------------------------
+ pr8210_update - update callback, called on
+ the first visible line of the frame
+-------------------------------------------------*/
+
+static INT32 pr8210_update(laserdisc_state *ld, const vbi_metadata *vbi, int fieldnum, attotime curtime)
+{
+ ldplayer_data *player = ld->player;
+ UINT8 spdl_on = !(player->port1 & 0x10);
+
+ /* logging */
+ if (LOG_VBLANK_VBI)
+ printf("%3d:Update(%d)\n", ld->screen->vpos(), fieldnum);
+
+ /* if the spindle is on, we advance by 1 track after completing field #1 */
+ return (spdl_on) ? fieldnum : 0;
+}
+
+
+/*-------------------------------------------------
+ pr8210_overlay - overlay callback, called
+ during frame processing in update to overlay
+ player data
+-------------------------------------------------*/
+
+static void pr8210_overlay(laserdisc_state *ld, bitmap_t *bitmap)
+{
+ ldplayer_data *player = ld->player;
+
+ /* custom display */
+ if (player->pia.display)
+ {
+ overlay_draw_group(bitmap, &player->pia.text[2], 5, OVERLAY_GROUP1_X);
+ overlay_draw_group(bitmap, &player->pia.text[7], 5, OVERLAY_GROUP2_X);
+ overlay_draw_group(bitmap, &player->pia.text[12], 5, OVERLAY_GROUP3_X);
+ }
+
+ /* chapter/frame display */
+ else
+ {
+ /* frame display */
+ if (player->pia.latchdisplay & 2)
+ overlay_draw_group(bitmap, &player->pia.text[2], 5, OVERLAY_GROUP1_X);
+
+ /* chapter overlay */
+ if (player->pia.latchdisplay & 1)
+ overlay_draw_group(bitmap, &player->pia.text[0], 2, OVERLAY_GROUP0_X);
+ }
+ player->pia.latchdisplay = 0;
+}
+
+
+/*-------------------------------------------------
+ pr8210_control_w - write callback when the
+ CONTROL line is toggled
+-------------------------------------------------*/
+
+static void pr8210_control_w(laserdisc_state *ld, UINT8 prev, UINT8 data)
+{
+ ldplayer_data *player = ld->player;
+
+ /* handle rising edge */
+ if (prev != ASSERT_LINE && data == ASSERT_LINE)
+ {
+ attotime curtime = ld->device->machine().time();
+ attotime delta, overalldelta;
+ int longpulse;
+
+ /* get the time difference from the last assert */
+ /* and update our internal command time */
+ delta = curtime - player->lastbittime;
+ player->lastbittime = curtime;
+
+ /* if we timed out since the first bit, reset the accumulator */
+ overalldelta = curtime - player->firstbittime;
+ if (overalldelta > SERIAL_MAX_WORD_TIME || delta > SERIAL_MAX_BIT_TIME)
+ {
+ player->firstbittime = curtime;
+ player->accumulator = 0x5555;
+ if (LOG_SERIAL)
+ printf("Reset accumulator\n");
+ }
+
+ /* 0 bit delta is 1.05 msec, 1 bit delta is 2.11 msec */
+ longpulse = (delta < SERIAL_MIDPOINT_TIME) ? 0 : 1;
+ player->accumulator = (player->accumulator << 1) | longpulse;
+
+ /* log the deltas for debugging */
+ if (LOG_SERIAL)
+ {
+ int usecdiff = (int)(delta.attoseconds / ATTOSECONDS_IN_USEC(1));
+ printf("bitdelta = %5d (%d) - accum = %04X\n", usecdiff, longpulse, player->accumulator);
+ }
+
+ /* if we have a complete command, signal it */
+ /* a complete command is 0,0,1 followed by 5 bits, followed by 0,0 */
+ if ((player->accumulator & 0x383) == 0x80)
+ {
+ UINT8 newcommand = (player->accumulator >> 2) & 0x1f;
+ attotime rejectuntil;
+
+ /* data is stored to the PIA in bit-reverse order */
+ player->pia.porta = BITSWAP8(newcommand, 0,1,2,3,4,5,6,7);
+
+ /* the MCU logic requires a 0 to execute many commands; however, nobody
+ consistently sends a 0, whereas they do tend to send duplicate commands...
+ if we assume that each duplicate causes a 0, we get the correct results */
+ rejectuntil = player->lastcommandtime + SERIAL_REJECT_DUPLICATE_TIME;
+ player->lastcommandtime = curtime;
+ if (player->pia.porta == player->lastcommand && curtime < rejectuntil)
+ player->pia.porta = 0x00;
+ else
+ player->lastcommand = player->pia.porta;
+
+ /* log the command and wait for a keypress */
+ if (LOG_SERIAL)
+ printf("--- Command = %02X\n", player->pia.porta >> 3);
+
+ /* reset the first bit time so that the accumulator clears on the next write */
+ player->firstbittime = curtime - SERIAL_MAX_WORD_TIME;
+ }
+ }
+}
+
+
+/*-------------------------------------------------
+ vsync_off - timer callback to clear the VSYNC
+ flag
+-------------------------------------------------*/
+
+static TIMER_CALLBACK( vsync_off )
+{
+ laserdisc_state *ld = (laserdisc_state *)ptr;
+ ld->player->vsync = FALSE;
+}
+
+
+/*-------------------------------------------------
+ vbi_data_fetch - timer callback to update the
+ VBI data in the PIA as soon as it is ready;
+ this must happy early in the frame because
+ the player logic relies on fetching it here
+-------------------------------------------------*/
+
+static TIMER_CALLBACK( vbi_data_fetch )
+{
+ laserdisc_state *ld = (laserdisc_state *)ptr;
+ ldplayer_data *player = ld->player;
+ UINT8 focus_on = !(player->port1 & 0x08);
+ UINT8 laser_on = !(player->port2 & 0x01);
+ UINT32 line16 = laserdisc_get_field_code(ld->device, LASERDISC_CODE_LINE16, FALSE);
+ UINT32 line1718 = laserdisc_get_field_code(ld->device, LASERDISC_CODE_LINE1718, FALSE);
+
+ /* logging */
+ if (LOG_VBLANK_VBI)
+ {
+ if ((line1718 & VBI_MASK_CAV_PICTURE) == VBI_CODE_CAV_PICTURE)
+ printf("%3d:VBI(%05d)\n", ld->screen->vpos(), VBI_CAV_PICTURE(line1718));
+ else
+ printf("%3d:VBI()\n", ld->screen->vpos());
+ }
+
+ /* update PIA registers based on vbi code */
+ player->pia.vbi1 = 0xff;
+ player->pia.vbi2 = 0xff;
+ if (focus_on && laser_on)
+ {
+ if (line1718 == VBI_CODE_LEADIN)
+ player->pia.vbi1 &= ~0x01;
+ if (line1718 == VBI_CODE_LEADOUT)
+ player->pia.vbi1 &= ~0x02;
+ if (line16 == VBI_CODE_STOP)
+ player->pia.vbi1 &= ~0x04;
+ /* unsure what this bit means: player->pia.vbi1 &= ~0x08; */
+ if ((line1718 & VBI_MASK_CAV_PICTURE) == VBI_CODE_CAV_PICTURE)
+ {
+ player->pia.vbi1 &= ~0x10;
+ player->pia.frame[2] = 0xf0 | ((line1718 >> 16) & 0x07);
+ player->pia.frame[3] = 0xf0 | ((line1718 >> 12) & 0x0f);
+ player->pia.frame[4] = 0xf0 | ((line1718 >> 8) & 0x0f);
+ player->pia.frame[5] = 0xf0 | ((line1718 >> 4) & 0x0f);
+ player->pia.frame[6] = 0xf0 | ((line1718 >> 0) & 0x0f);
+ }
+ if ((line1718 & VBI_MASK_CHAPTER) == VBI_CODE_CHAPTER)
+ {
+ player->pia.vbi2 &= ~0x01;
+ player->pia.frame[0] = 0xf0 | ((line1718 >> 16) & 0x07);
+ player->pia.frame[1] = 0xf0 | ((line1718 >> 12) & 0x0f);
+ }
+ }
+}
+
+
+/*-------------------------------------------------
+ pr8210_pia_r - handle reads from the mystery
+ Pioneer PIA
+-------------------------------------------------*/
+
+static READ8_HANDLER( pr8210_pia_r )
+{
+ laserdisc_state *ld = ldcore_get_safe_token(space->device().owner());
+ ldplayer_data *player = ld->player;
+ UINT8 result = 0xff;
+
+ switch (offset)
+ {
+ /* (20-26) 7 characters for the chapter/frame */
+ case 0x20: case 0x21:
+ case 0x22: case 0x23: case 0x24: case 0x25: case 0x26:
+ result = player->pia.frame[offset - 0x20];
+ break;
+
+ /* (1D-1F,27) invalid read but normal */
+ case 0x1d: case 0x1e: case 0x1f:
+ case 0x27:
+ break;
+
+ /* (A0) port A value (from serial decoder) */
+ case 0xa0:
+ result = player->pia.porta;
+ break;
+
+ /* (C0) VBI decoding state 1 */
+ case 0xc0:
+ if (LOG_VBLANK_VBI)
+ printf("%3d:PIA(C0)\n", ld->screen->vpos());
+ result = player->pia.vbi1;
+ break;
+
+ /* (E0) VBI decoding state 2 */
+ case 0xe0:
+ if (LOG_VBLANK_VBI)
+ printf("%3d:PIA(E0)\n", ld->screen->vpos());
+ result = player->pia.vbi2;
+ break;
+
+ default:
+ mame_printf_debug("%03X:Unknown PR-8210 PIA read from offset %02X\n", cpu_get_pc(&space->device()), offset);
+ break;
+ }
+ return result;
+}
+
+
+/*-------------------------------------------------
+ pr8210_pia_w - handle writes to the mystery
+ Pioneer PIA
+-------------------------------------------------*/
+
+static WRITE8_HANDLER( pr8210_pia_w )
+{
+ laserdisc_state *ld = ldcore_get_safe_token(space->device().owner());
+ ldplayer_data *player = ld->player;
+ UINT8 value;
+
+ switch (offset)
+ {
+ /* (20-30) 17 characters for the display */
+ case 0x20: case 0x21:
+ case 0x22: case 0x23: case 0x24: case 0x25: case 0x26:
+ case 0x27: case 0x28: case 0x29: case 0x2a: case 0x2b:
+ case 0x2c: case 0x2d: case 0x2e: case 0x2f: case 0x30:
+ player->pia.text[offset - 0x20] = data;
+ break;
+
+ /* (40) control lines */
+ case 0x40:
+
+ /* toggle bit 0 to latch chapter number into display area */
+ if (!(data & 0x01) && (player->pia.control & 0x01))
+ {
+ memcpy(&player->pia.text[0], &player->pia.frame[0], 2);
+ player->pia.latchdisplay |= 1;
+ }
+
+ /* toggle bit 1 to latch frame number into display area */
+ if (!(data & 0x02) && (player->pia.control & 0x02))
+ {
+ memcpy(&player->pia.text[2], &player->pia.frame[2], 5);
+ player->pia.latchdisplay |= 2;
+ }
+ player->pia.control = data;
+ break;
+
+ /* (60) port B value (LEDs) */
+ case 0x60:
+
+ /* these 4 are direct-connect */
+ output_set_value("pr8210_audio1", (data & 0x01) != 0);
+ output_set_value("pr8210_audio2", (data & 0x02) != 0);
+ output_set_value("pr8210_clv", (data & 0x04) != 0);
+ output_set_value("pr8210_cav", (data & 0x08) != 0);
+
+ /* remaining 3 bits select one of 5 LEDs via a mux */
+ value = ((data & 0x40) >> 6) | ((data & 0x20) >> 4) | ((data & 0x10) >> 2);
+ output_set_value("pr8210_srev", (value == 0));
+ output_set_value("pr8210_sfwd", (value == 1));
+ output_set_value("pr8210_play", (value == 2));
+ output_set_value("pr8210_step", (value == 3));
+ output_set_value("pr8210_pause", (value == 4));
+
+ player->pia.portb = data;
+ update_audio_squelch(ld);
+ break;
+
+ /* (80) display enable */
+ case 0x80:
+ player->pia.display = data & 0x01;
+ break;
+
+ /* no other writes known */
+ default:
+ mame_printf_debug("%03X:Unknown PR-8210 PIA write to offset %02X = %02X\n", cpu_get_pc(&space->device()), offset, data);
+ break;
+ }
+}
+
+
+/*-------------------------------------------------
+ pr8210_bus_r - handle reads from the 8049 BUS
+ input, which is enabled via the PIA above
+-------------------------------------------------*/
+
+static READ8_HANDLER( pr8210_bus_r )
+{
+ /*
+ $80 = n/c
+ $40 = (in) slider pot interrupt source (slider position limit detector, inside and outside)
+ $20 = n/c
+ $10 = (in) /FOCUS LOCK
+ $08 = (in) /SPDL LOCK
+ $04 = (in) SIZE 8/12
+ $02 = (in) FG via op-amp (spindle motor stop detector)
+ $01 = (in) SLOW TIMER OUT
+ */
+ laserdisc_state *ld = ldcore_get_safe_token(space->device().owner());
+ ldplayer_data *player = ld->player;
+ slider_position sliderpos = ldcore_get_slider_position(ld);
+ UINT8 focus_on = !(player->port1 & 0x08);
+ UINT8 spdl_on = !(player->port1 & 0x10);
+ UINT8 result = 0x00;
+
+ /* bus bit 6: slider position limit detector, inside and outside */
+ if (sliderpos != SLIDER_MINIMUM && sliderpos != SLIDER_MAXIMUM)
+ result |= 0x40;
+
+ /* bus bit 4: /FOCUS LOCK */
+ if (!focus_on)
+ result |= 0x10;
+
+ /* bus bit 3: /SPDL LOCK */
+ if (!spdl_on)
+ result |= 0x08;
+
+ /* bus bit 1: spindle motor stop detector */
+ if (!spdl_on)
+ result |= 0x02;
+
+ /* bus bit 0: SLOW TIMER OUT */
+
+ /* loop at beginning waits for $40=0, $02=1 */
+ return result;
+}
+
+
+/*-------------------------------------------------
+ pr8210_port1_w - handle writes to the 8049
+ port #1
+-------------------------------------------------*/
+
+static WRITE8_HANDLER( pr8210_port1_w )
+{
+ /*
+ $80 = (out) SCAN C (F/R)
+ $40 = (out) AUDIO SQ
+ $20 = (out) VIDEO SQ
+ $10 = (out) /SPDL ON
+ $08 = (out) /FOCUS ON
+ $04 = (out) SCAN B (L/H)
+ $02 = (out) SCAN A (/SCAN)
+ $01 = (out) JUMP TRG (jump back trigger, clock on high->low)
+ */
+ laserdisc_state *ld = ldcore_get_safe_token(space->device().owner());
+ ldplayer_data *player = ld->player;
+ UINT8 prev = player->port1;
+ int direction;
+
+ /* set the new value */
+ player->port1 = data;
+
+ /* bit 7 selects the direction of slider movement for JUMP TRG and scanning */
+ direction = (data & 0x80) ? 1 : -1;
+
+ /* on the falling edge of bit 0, jump one track in either direction */
+ if (!(data & 0x01) && (prev & 0x01))
+ {
+ /* special override for the Simutrek, which takes over control of this is some situations */
+ if (player->simutrek.cpu == NULL || !player->simutrek.controlthis)
+ {
+ if (LOG_SIMUTREK)
+ printf("%3d:JUMP TRG\n", ld->screen->vpos());
+ ldcore_advance_slider(ld, direction);
+ }
+ else if (LOG_SIMUTREK)
+ printf("%3d:Skipped JUMP TRG\n", ld->screen->vpos());
+ }
+
+ /* bit 1 low enables scanning */
+ if (!(data & 0x02))
+ {
+ /* bit 2 selects the speed */
+ int delta = (data & 0x04) ? SCAN_SPEED : SEEK_FAST_SPEED;
+ ldcore_set_slider_speed(ld, delta * direction);
+ }
+
+ /* bit 1 high stops scanning */
+ else
+ ldcore_set_slider_speed(ld, 0);
+
+ /* video squelch is controlled by bit 5 */
+ update_video_squelch(ld);
+
+ /* audio squelch is controlled by bit 6 */
+ update_audio_squelch(ld);
+}
+
+
+/*-------------------------------------------------
+ pr8210_port2_w - handle writes to the 8049
+ port #2
+-------------------------------------------------*/
+
+static WRITE8_HANDLER( pr8210_port2_w )
+{
+ /*
+ $80 = (out) /CS on PIA
+ $40 = (out) 0 to self-generate IRQ
+ $20 = (out) SLOW TRG
+ $10 = (out) STANDBY LED
+ $08 = (out) TP2
+ $04 = (out) TP1
+ $02 = (out) ???
+ $01 = (out) LASER ON
+ */
+ laserdisc_state *ld = ldcore_get_safe_token(space->device().owner());
+ ldplayer_data *player = ld->player;
+ UINT8 prev = player->port2;
+
+ /* set the new value */
+ player->port2 = data;
+
+ /* on the falling edge of bit 5, start the slow timer */
+ if (!(data & 0x20) && (prev & 0x20))
+ player->slowtrg = space->machine().time();
+
+ /* bit 6 when low triggers an IRQ on the MCU */
+ if (player->cpu != NULL)
+ device_set_input_line(player->cpu, MCS48_INPUT_IRQ, (data & 0x40) ? CLEAR_LINE : ASSERT_LINE);
+
+ /* standby LED is set accordingl to bit 4 */
+ output_set_value("pr8210_standby", (data & 0x10) != 0);
+}
+
+
+/*-------------------------------------------------
+ pr8210_t0_r - return the state of the 8049
+ T0 input (connected to VSYNC)
+-------------------------------------------------*/
+
+static READ8_HANDLER( pr8210_t0_r )
+{
+ /* returns VSYNC state */
+ laserdisc_state *ld = ldcore_get_safe_token(space->device().owner());
+ return !ld->player->vsync;
+}
+
+
+/*-------------------------------------------------
+ pr8210_t1_r - return the state of the 8049
+ T1 input (pulled high)
+-------------------------------------------------*/
+
+static READ8_HANDLER( pr8210_t1_r )
+{
+ return 1;
+}
+
+
+/*-------------------------------------------------
+ overlay_draw_group - draw a single group of
+ characters
+-------------------------------------------------*/
+
+static void overlay_draw_group(bitmap_t *bitmap, const UINT8 *text, int count, float xstart)
+{
+ int skip = TRUE;
+ int x;
+
+ /* rease the background */
+ overlay_erase(bitmap, xstart, xstart + ((OVERLAY_X_PIXELS + 1) * count + 1) * OVERLAY_PIXEL_WIDTH);
+
+ /* draw each character, suppressing leading 0's */
+ for (x = 0; x < count; x++)
+ if (!skip || x == count - 1 || (text[x] & 0x3f) != 0x30)
+ {
+ skip = FALSE;
+ overlay_draw_char(bitmap, text[x], xstart + ((OVERLAY_X_PIXELS + 1) * x + 1) * OVERLAY_PIXEL_WIDTH);
+ }
+}
+
+
+/*-------------------------------------------------
+ overlay_erase - erase the background area
+ where the text overlay will be displayed
+-------------------------------------------------*/
+
+static void overlay_erase(bitmap_t *bitmap, float xstart, float xend)
+{
+ UINT32 xmin = (UINT32)(xstart * 256.0f * (float)bitmap->width);
+ UINT32 xmax = (UINT32)(xend * 256.0f * (float)bitmap->width);
+ UINT32 x, y;
+
+ for (y = OVERLAY_Y; y < (OVERLAY_Y + (OVERLAY_Y_PIXELS + 2) * OVERLAY_PIXEL_HEIGHT); y++)
+ {
+ UINT16 *dest = BITMAP_ADDR16(bitmap, y, xmin >> 8);
+ UINT16 ymin, ymax, yres;
+
+ ymax = *dest >> 8;
+ ymin = ymax * 3 / 8;
+ yres = ymin + ((ymax - ymin) * (xmin & 0xff)) / 256;
+ *dest = (yres << 8) | (*dest & 0xff);
+ dest++;
+
+ for (x = (xmin | 0xff) + 1; x < xmax; x += 0x100)
+ {
+ yres = (*dest >> 8) * 3 / 8;
+ *dest = (yres << 8) | (*dest & 0xff);
+ dest++;
+ }
+
+ ymax = *dest >> 8;
+ ymin = ymax * 3 / 8;
+ yres = ymin + ((ymax - ymin) * (~xmax & 0xff)) / 256;
+ *dest = (yres << 8) | (*dest & 0xff);
+ dest++;
+ }
+}
+
+
+/*-------------------------------------------------
+ overlay_draw_char - draw a single character
+ of the text overlay
+-------------------------------------------------*/
+
+static void overlay_draw_char(bitmap_t *bitmap, UINT8 ch, float xstart)
+{
+ UINT32 xminbase = (UINT32)(xstart * 256.0f * (float)bitmap->width);
+ UINT32 xsize = (UINT32)(OVERLAY_PIXEL_WIDTH * 256.0f * (float)bitmap->width);
+ const UINT8 *chdataptr = &text_bitmap[ch & 0x3f][0];
+ UINT32 x, y, xx, yy;
+
+ /* iterate over pixels */
+ for (y = 0; y < OVERLAY_Y_PIXELS; y++)
+ {
+ UINT8 chdata = *chdataptr++;
+
+ for (x = 0; x < OVERLAY_X_PIXELS; x++, chdata <<= 1)
+ if (chdata & 0x80)
+ {
+ UINT32 xmin = xminbase + x * xsize;
+ UINT32 xmax = xmin + xsize;
+ for (yy = 0; yy < OVERLAY_PIXEL_HEIGHT; yy++)
+ {
+ UINT16 *dest = BITMAP_ADDR16(bitmap, OVERLAY_Y + (y + 1) * OVERLAY_PIXEL_HEIGHT + yy, xmin >> 8);
+ UINT16 ymin, ymax, yres;
+
+ ymax = 0xff;
+ ymin = *dest >> 8;
+ yres = ymin + ((ymax - ymin) * (~xmin & 0xff)) / 256;
+ *dest = (yres << 8) | (*dest & 0xff);
+ dest++;
+
+ for (xx = (xmin | 0xff) + 1; xx < xmax; xx += 0x100)
+ *dest++ = 0xf080;
+
+ ymax = 0xff;
+ ymin = *dest >> 8;
+ yres = ymin + ((ymax - ymin) * (xmax & 0xff)) / 256;
+ *dest = (yres << 8) | (*dest & 0xff);
+ dest++;
+ }
+ }
+ }
+}
+
+
+
+/***************************************************************************
+ SIMUTREK ROM AND MACHINE INTERFACES
+***************************************************************************/
+
+static ADDRESS_MAP_START( simutrek_portmap, AS_IO, 8 )
+ AM_RANGE(0x00, 0xff) AM_READ(simutrek_data_r)
+ AM_RANGE(MCS48_PORT_P2, MCS48_PORT_P2) AM_READWRITE(simutrek_port2_r, simutrek_port2_w)
+ AM_RANGE(MCS48_PORT_T0, MCS48_PORT_T0) AM_READ(simutrek_t0_r)
+ADDRESS_MAP_END
+
+
+static MACHINE_CONFIG_FRAGMENT( simutrek )
+ MCFG_CPU_ADD("simutrek", I8748, XTAL_6MHz)
+ MCFG_CPU_IO_MAP(simutrek_portmap)
+
+ MCFG_FRAGMENT_ADD(pr8210)
+MACHINE_CONFIG_END
+
+
+ROM_START( simutrek )
+ ROM_REGION( 0x800, "pr8210", 0 )
+ ROM_LOAD( "pr-8210_mcu_ud6005a.bin", 0x000, 0x800, CRC(120fa83b) SHA1(b514326ca1f52d6d89056868f9d17eabd4e3f31d) )
+
+ ROM_REGION( 0x400, "simutrek", 0)
+ ROM_LOAD( "laser_player_interface_d8748_a308.bin", 0x0000, 0x0400, CRC(eed3e728) SHA1(1eb3467f1c41553375b2c21952cd593b167f5416) )
+ROM_END
+
+
+
+/***************************************************************************
+ SIMUTREK PLAYER INTERFACE
+***************************************************************************/
+
+const ldplayer_interface simutrek_interface =
+{
+ LASERDISC_TYPE_SIMUTREK_SPECIAL, /* type of the player */
+ sizeof(ldplayer_data), /* size of the state */
+ "Simutrek Modified PR-8210", /* name of the player */
+ "mpr8210", /* shortname of the player */
+ ROM_NAME(simutrek), /* pointer to ROM region information */
+ MACHINE_CONFIG_NAME(simutrek), /* pointer to machine configuration */
+ simutrek_init, /* initialization callback */
+ simutrek_vsync, /* vsync callback */
+ simutrek_update, /* update callback */
+ pr8210_overlay, /* overlay callback */
+ simutrek_data_w, /* parallel data write */
+ { /* single line write: */
+ NULL, /* LASERDISC_LINE_ENTER */
+ NULL /* LASERDISC_LINE_CONTROL */
+ },
+ NULL, /* parallel data read */
+ { /* single line read: */
+ simutrek_ready_r, /* LASERDISC_LINE_READY */
+ simutrek_status_r, /* LASERDISC_LINE_STATUS */
+ NULL, /* LASERDISC_LINE_COMMAND */
+ NULL, /* LASERDISC_LINE_DATA_AVAIL */
+ }
+};
+
+
+
+/***************************************************************************
+ SIMUTREK IMPLEMENTATION
+***************************************************************************/
+
+/*-------------------------------------------------
+ simutrek_set_audio_squelch - Simutrek-specific
+ command to enable/disable audio squelch
+-------------------------------------------------*/
+
+void simutrek_set_audio_squelch(device_t *device, int state)
+{
+ laserdisc_state *ld = ldcore_get_safe_token(device);
+ ldplayer_data *player = ld->player;
+ if (LOG_SIMUTREK && player->simutrek.audio_squelch != (state == 0))
+ printf("--> audio squelch = %d\n", state == 0);
+ player->simutrek.audio_squelch = (state == 0);
+ update_audio_squelch(ld);
+}
+
+
+/*-------------------------------------------------
+ simutrek_init - Simutrek-specific
+ initialization
+-------------------------------------------------*/
+
+static void simutrek_init(laserdisc_state *ld)
+{
+ astring tempstring;
+ ldplayer_data *player = ld->player;
+
+ /* standard PR-8210 initialization */
+ pr8210_init(ld);
+
+ /* initialize the Simutrek state */
+ /* for proper synchronization of initial attract mode, this needs to be set */
+ player->simutrek.data_ready = 1;
+
+ /* find the Simutrek CPU */
+ player->simutrek.cpu = ld->device->subdevice("simutrek");
+}
+
+
+/*-------------------------------------------------
+ simutrek_vsync - VSYNC callback, called at the
+ start of the blanking period
+-------------------------------------------------*/
+
+static TIMER_CALLBACK( irq_off )
+{
+ laserdisc_state *ld = (laserdisc_state *)ptr;
+ ldplayer_data *player = ld->player;
+ device_set_input_line(player->simutrek.cpu, MCS48_INPUT_IRQ, CLEAR_LINE);
+ if (LOG_SIMUTREK)
+ printf("%3d:**** Simutrek IRQ clear\n", ld->screen->vpos());
+}
+
+static void simutrek_vsync(laserdisc_state *ld, const vbi_metadata *vbi, int fieldnum, attotime curtime)
+{
+ ldplayer_data *player = ld->player;
+
+ if (fieldnum == 1)
+ {
+ player->simutrek.controlthis = player->simutrek.controlnext;
+ player->simutrek.controlnext = 0;
+ }
+
+ if (LOG_SIMUTREK)
+ printf("%3d:VSYNC(%d)\n", ld->screen->vpos(), fieldnum);
+ pr8210_vsync(ld, vbi, fieldnum, curtime);
+
+ if (player->simutrek.data_ready)
+ {
+ if (LOG_SIMUTREK)
+ printf("%3d:VSYNC IRQ\n", ld->screen->vpos());
+ device_set_input_line(player->simutrek.cpu, MCS48_INPUT_IRQ, ASSERT_LINE);
+ ld->device->machine().scheduler().timer_set(ld->screen->scan_period(), FUNC(irq_off), 0, ld);
+ }
+}
+
+
+/*-------------------------------------------------
+ simutrek_update - update callback, called on
+ the first visible line of the frame
+-------------------------------------------------*/
+
+static INT32 simutrek_update(laserdisc_state *ld, const vbi_metadata *vbi, int fieldnum, attotime curtime)
+{
+ return pr8210_update(ld, vbi, fieldnum, curtime);
+}
+
+
+/*-------------------------------------------------
+ simutrek_ready_r - read callback when the
+ READY line is read
+-------------------------------------------------*/
+
+static UINT8 simutrek_ready_r(laserdisc_state *ld)
+{
+ return !ld->player->simutrek.data_ready;
+}
+
+
+/*-------------------------------------------------
+ simutrek_status_r - read callback when the
+ STATUS line is read
+-------------------------------------------------*/
+
+static UINT8 simutrek_status_r(laserdisc_state *ld)
+{
+ return ((ld->player->simutrek.port2 & 0x03) == 0x03) ? ASSERT_LINE : CLEAR_LINE;
+}
+
+
+/*-------------------------------------------------
+ simutrek_data_w - write callback when the
+ parallel data port is written to
+-------------------------------------------------*/
+
+static void simutrek_data_w(laserdisc_state *ld, UINT8 prev, UINT8 data)
+{
+ ld->device->machine().scheduler().synchronize(FUNC(simutrek_latched_data_w), data, ld);
+ if (LOG_SIMUTREK)
+ printf("%03d:**** Simutrek Command = %02X\n", ld->screen->vpos(), data);
+}
+
+
+/*-------------------------------------------------
+ simutrek_latched_data_w - deferred write
+ callback for when data is written
+-------------------------------------------------*/
+
+static TIMER_CALLBACK( simutrek_latched_data_w )
+{
+ laserdisc_state *ld = (laserdisc_state *)ptr;
+ ldplayer_data *player = ld->player;
+
+ /* store the data and set the ready flag */
+ player->simutrek.data = param;
+ player->simutrek.data_ready = TRUE;
+}
+
+
+/*-------------------------------------------------
+ simutrek_port2_r - handle reads from the 8748
+ port #2
+-------------------------------------------------*/
+
+static READ8_HANDLER( simutrek_port2_r )
+{
+ laserdisc_state *ld = ldcore_get_safe_token(space->device().owner());
+ ldplayer_data *player = ld->player;
+
+ /* bit $80 is the pr8210 video squelch */
+ return (player->port1 & 0x20) ? 0x00 : 0x80;
+}
+
+
+/*-------------------------------------------------
+ simutrek_port2_w - handle writes to the 8748
+ port #2
+-------------------------------------------------*/
+
+static WRITE8_HANDLER( simutrek_port2_w )
+{
+ laserdisc_state *ld = ldcore_get_safe_token(space->device().owner());
+ ldplayer_data *player = ld->player;
+ UINT8 prev = player->simutrek.port2;
+
+ /* update stat */
+ player->simutrek.port2 = data;
+
+ /* bit $20 goes to the serial line */
+ if ((data ^ prev) & 0x20)
+ pr8210_control_w(ld, (data & 0x20) ? ASSERT_LINE : CLEAR_LINE, (data & 0x20) ? CLEAR_LINE : ASSERT_LINE);
+
+ /* bit $10 goes to JUMP TRG */
+ /* bit $08 controls direction */
+ if (!(data & 0x10) && (prev & 0x10))
+ {
+ int direction = (data & 0x08) ? 1 : -1;
+ if (LOG_SIMUTREK)
+ printf("%3d:JUMP TRG (Simutrek PC=%03X)\n", ld->screen->vpos(), cpu_get_pc(&space->device()));
+ ldcore_advance_slider(ld, direction);
+ }
+
+ /* bit $04 controls who owns the JUMP TRG command */
+ if (LOG_SIMUTREK && ((data ^ prev) & 0x04))
+ printf("%3d:Simutrek ownership line = %d (Simutrek PC=%03X)\n", ld->screen->vpos(), (data >> 2) & 1, cpu_get_pc(&space->device()));
+ player->simutrek.controlnext = (~data >> 2) & 1;
+
+ /* bits $03 control something (status?) */
+ if (LOG_SIMUTREK && ((data ^ prev) & 0x03))
+ printf("Simutrek Status = %d\n", data & 0x03);
+}
+
+
+/*-------------------------------------------------
+ simutrek_data_r - handle external 8748 data
+ reads
+-------------------------------------------------*/
+
+static READ8_HANDLER( simutrek_data_r )
+{
+ laserdisc_state *ld = ldcore_get_safe_token(space->device().owner());
+ ldplayer_data *player = ld->player;
+
+ /* acknowledge the read and clear the data ready flag */
+ player->simutrek.data_ready = FALSE;
+ return player->simutrek.data;
+}
+
+
+/*-------------------------------------------------
+ simutrek_t0_r - return the status of the
+ 8748 T0 input
+-------------------------------------------------*/
+
+static READ8_HANDLER( simutrek_t0_r )
+{
+ /* return 1 if data is waiting from main CPU */
+ laserdisc_state *ld = ldcore_get_safe_token(space->device().owner());
+ return ld->player->simutrek.data_ready;
+}