summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/machine/laserdsc.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/emu/machine/laserdsc.c')
-rw-r--r--src/emu/machine/laserdsc.c3361
1 files changed, 3361 insertions, 0 deletions
diff --git a/src/emu/machine/laserdsc.c b/src/emu/machine/laserdsc.c
new file mode 100644
index 00000000000..3ab6eeb7412
--- /dev/null
+++ b/src/emu/machine/laserdsc.c
@@ -0,0 +1,3361 @@
+/*************************************************************************
+
+ laserdsc.c
+
+ Generic laserdisc support.
+
+ Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team.
+ Visit http://mamedev.org for licensing and usage restrictions.
+
+*************************************************************************/
+
+#include "driver.h"
+#include "laserdsc.h"
+#include "avcomp.h"
+#include "profiler.h"
+#include "streams.h"
+#include "sound/custom.h"
+
+
+
+/***************************************************************************
+ DEBUGGING
+***************************************************************************/
+
+#define PRINTF_COMMANDS 1
+
+#if PRINTF_COMMANDS
+#define CMDPRINTF(x) mame_printf_debug x
+#else
+#define CMDPRINTF(x)
+#endif
+
+
+
+/***************************************************************************
+ CONSTANTS
+***************************************************************************/
+
+/* fractional track handling */
+#define FRACBITS 12
+#define FRAC_ONE (1 << FRACBITS)
+#define INT_TO_FRAC(x) ((x) << FRACBITS)
+#define FRAC_TO_INT(x) ((x) >> FRACBITS)
+
+/* general laserdisc states */
+enum _laserdisc_state
+{
+ LASERDISC_EJECTED, /* no disc present */
+ LASERDISC_EJECTING, /* in the process of ejecting */
+ LASERDISC_LOADED, /* disc just loaded */
+ LASERDISC_LOADING, /* in the process of loading */
+ LASERDISC_PARKED, /* disc loaded, not playing, head on parked position */
+ LASERDISC_SPINUP, /* disc loaded, spinning up to speed */
+
+ LASERDISC_SEARCHING_FRAME, /* searching (seeking) to a new frame */
+ LASERDISC_SEARCH_FINISHED, /* finished searching; same as stopped */
+
+ LASERDISC_STOPPED, /* stopped (paused) with video visible and no sound */
+ LASERDISC_AUTOSTOPPED, /* autostopped; same as stopped */
+
+ LASERDISC_PLAYING_FORWARD, /* playing at 1x in the forward direction */
+ LASERDISC_PLAYING_REVERSE, /* playing at 1x in the reverse direction */
+ LASERDISC_PLAYING_SLOW_FORWARD, /* playing forward slowly */
+ LASERDISC_PLAYING_SLOW_REVERSE, /* playing backward slowly */
+ LASERDISC_PLAYING_FAST_FORWARD, /* playing fast forward */
+ LASERDISC_PLAYING_FAST_REVERSE, /* playing fast backward */
+ LASERDISC_STEPPING_FORWARD, /* stepping forward */
+ LASERDISC_STEPPING_REVERSE, /* stepping backward */
+ LASERDISC_SCANNING_FORWARD, /* scanning forward at the scan rate */
+ LASERDISC_SCANNING_REVERSE /* scanning backward at the scan rate */
+};
+typedef enum _laserdisc_state laserdisc_state;
+
+/* generic states and configuration */
+#define FRAMEFLAG_PREV_SAME_FRAME 0x01
+#define FRAMEFLAG_NEXT_SAME_FRAME 0x02
+
+#define AUDIO_CH1_ENABLE 0x01
+#define AUDIO_CH2_ENABLE 0x02
+#define AUDIO_EXPLICIT_MUTE 0x04
+#define AUDIO_IMPLICIT_MUTE 0x08
+
+#define VIDEO_ENABLE 0x01
+#define VIDEO_EXPLICIT_MUTE 0x02
+#define VIDEO_IMPLICIT_MUTE 0x04
+
+#define DISPLAY_ENABLE 0x01
+
+#define NULL_TARGET_FRAME 0 /* frame 0 indicates no target */
+#define ONE_TRACK INT_TO_FRAC(1) /* a single track, or track #1 */
+#define STOP_SPEED INT_TO_FRAC(0) /* no movement */
+#define PLAY_SPEED INT_TO_FRAC(1) /* regular playback speed */
+
+#define GENERIC_SPINUP_TIME (attotime_make(3, 0))
+#define GENERIC_LOAD_TIME (attotime_make(10, 0))
+#define GENERIC_RESET_SPEED INT_TO_FRAC(5000)
+
+/* Pioneer PR-7820 specific states */
+#define PR7820_MODE_MANUAL 0
+#define PR7820_MODE_AUTOMATIC 1
+#define PR7820_MODE_PROGRAM 2
+
+#define PR7820_SEARCH_SPEED INT_TO_FRAC(5000)
+
+/* Pioneer PR-8210/LD-V1100 specific states */
+#define PR8210_MODE_GET_1ST 0
+#define PR8210_MODE_GET_2ND 1
+#define PR8210_MODE_GET_3RD 2
+
+#define PR8210_SCAN_SPEED (INT_TO_FRAC(2000) / 30)
+#define PR8210_FAST_SPEED (PLAY_SPEED * 3)
+#define PR8210_SLOW_SPEED (PLAY_SPEED / 5)
+#define PR8210_STEP_SPEED (PLAY_SPEED / 7)
+#define PR8210_SEARCH_SPEED INT_TO_FRAC(5000)
+
+/* Pioneer LD-V1000 specific states */
+#define LDV1000_MODE_STATUS 0
+#define LDV1000_MODE_GET_FRAME 1
+#define LDV1000_MODE_GET_1ST 2
+#define LDV1000_MODE_GET_2ND 3
+#define LDV1000_MODE_GET_RAM 4
+
+#define LDV1000_SCAN_SPEED (INT_TO_FRAC(2000) / 30)
+#define LDV1000_SEARCH_SPEED INT_TO_FRAC(5000)
+
+/* Sony LDP-1450 specific states */
+#define LDP1450_SCAN_SPEED (INT_TO_FRAC(2000) / 30)
+#define LDP1450_FAST_SPEED (PLAY_SPEED * 3)
+#define LDP1450_SLOW_SPEED (PLAY_SPEED / 5)
+#define LDP1450_STEP_SPEED (PLAY_SPEED / 7)
+#define LDP1450_SEARCH_SPEED INT_TO_FRAC(5000)
+
+/* Philips 22VP932 specific states */
+#define VP932_SCAN_SPEED (INT_TO_FRAC(2000) / 30)
+#define VP932_FAST_SPEED (PLAY_SPEED * 3)
+#define VP932_SLOW_SPEED (PLAY_SPEED / 5)
+#define VP932_STEP_SPEED (PLAY_SPEED / 7)
+#define VP932_SEARCH_SPEED INT_TO_FRAC(5000)
+
+
+
+/***************************************************************************
+ TYPE DEFINITIONS
+***************************************************************************/
+
+/* PR-7820-specific data */
+typedef struct _pr7820_info pr7820_info;
+struct _pr7820_info
+{
+ UINT8 mode; /* current mode */
+ UINT32 curpc; /* current PC for automatic execution */
+ INT32 configspeed; /* configured speed */
+ UINT32 activereg; /* active register index */
+ UINT8 ram[1024]; /* RAM */
+};
+
+
+/* PR8210-specific data */
+typedef struct _pr8210_info pr8210_info;
+struct _pr8210_info
+{
+ UINT8 mode; /* current mode */
+ UINT16 commandtriplet[3]; /* current command triplet */
+ attotime commandtime; /* command time */
+ UINT8 commandbits; /* command bit count */
+ UINT8 seekstate; /* state of the seek command */
+};
+
+
+/* LD-V1000-specific data */
+typedef struct _ldv1000_info ldv1000_info;
+struct _ldv1000_info
+{
+ UINT8 mode; /* current mode */
+ UINT32 activereg; /* active register index */
+ UINT8 ram[1024]; /* RAM */
+ UINT32 readpos; /* current read position */
+ UINT32 readtotal; /* current read position */
+ UINT8 readbuf[256]; /* temporary read buffer */
+ UINT8 statusmask; /* status bits mask */
+};
+
+
+/* LDP-1450-specific data */
+typedef struct _ldp1450_info ldp1450_info;
+struct _ldp1450_info
+{
+ UINT32 readpos; /* current read position */
+ UINT32 readtotal; /* current read position */
+ UINT8 readbuf[256]; /* temporary read buffer */
+};
+
+
+/* 22VP932-specific data */
+typedef struct _vp932_info vp932_info;
+struct _vp932_info
+{
+ UINT8 incount; /* number of pending bytes accumulated */
+ UINT8 inbuffer[8]; /* input data */
+ UINT8 outcount; /* number of pending bytes to send */
+ UINT8 outbuffer[8]; /* output data */
+};
+
+
+/* per-field metadata */
+typedef struct _field_metadata field_metadata;
+struct _field_metadata
+{
+ UINT8 version; /* version of the data */
+ UINT8 frameflags; /* per-frame flags */
+ UINT8 whiteflag; /* white flag */
+ UINT32 line16; /* line 16 Philips code */
+ UINT32 line17; /* line 17 Philips code */
+ UINT32 line18; /* line 18 Philips code */
+};
+
+
+/* generic data */
+struct _laserdisc_info
+{
+ /* disc parameters */
+ chd_file * disc; /* handle to the disc itself */
+ av_codec_decompress_config avconfig; /* decompression configuration */
+ UINT8 readpending; /* true if a read is pending */
+ UINT8 * framebuffer; /* buffer to hold one frame */
+ UINT32 maxfractrack; /* maximum track number */
+ UINT32 fieldnum; /* field number (0 or 1) */
+
+ /* video data */
+ mame_bitmap * videoframe[2]; /* currently cached frames */
+ UINT8 videofields[2]; /* number of fields in each frame */
+ UINT32 videoframenum[2]; /* frame number contained in each frame */
+ UINT8 videoindex; /* index of the current video buffer */
+ mame_bitmap * emptyframe; /* blank frame */
+
+ /* audio data */
+ INT16 * audiobuffer[2]; /* buffer for audio samples */
+ UINT32 audiobufsize; /* size of buffer */
+ UINT32 audiobufin; /* input index */
+ UINT32 audiobufout; /* output index */
+ int audiocustom; /* custom sound index */
+
+ /* metadata */
+ field_metadata metadata[2]; /* metadata parsed from the stream, for each field */
+ int last_frame; /* last seen frame number */
+ int last_chapter; /* last seen chapter number */
+
+ /* core states */
+ UINT8 type; /* laserdisc type */
+ laserdisc_state state; /* current player state */
+ UINT8 video; /* video state: bit 0 = on/off */
+ UINT8 audio; /* audio state: bit 0 = audio 1, bit 1 = audio 2 */
+ UINT8 display; /* display state: bit 0 = on/off */
+ attotime lastvsynctime; /* time of the last vsync */
+
+ /* deferred states */
+ attotime holdfinished; /* time when current state will advance */
+ UINT8 postholdstate; /* state to switch into after holding */
+ INT32 postholdfracspeed; /* speed after the hold */
+
+ /* input data */
+ UINT8 datain; /* current input data value */
+ UINT8 linein[LASERDISC_INPUT_LINES]; /* current input line state */
+
+ /* output data */
+ UINT8 dataout; /* current output data value */
+ UINT8 lineout[LASERDISC_OUTPUT_LINES]; /* current output line state */
+
+ /* command and parameter buffering */
+ UINT8 command; /* current command */
+ INT32 parameter; /* command parameter */
+
+ /* playback/search/scan speeds */
+ INT32 curfracspeed; /* current speed the head is moving */
+ INT32 curfractrack; /* current track */
+ INT32 targetframe; /* target frame (0 means no target) */
+
+ /* debugging */
+ char text[100]; /* buffer for the state */
+
+ /* filled in by player-specific init */
+ void (*writedata)(laserdisc_info *info, UINT8 prev, UINT8 new); /* write callback */
+ void (*writeline[LASERDISC_INPUT_LINES])(laserdisc_info *info, UINT8 new); /* write line callback */
+ UINT8 (*readdata)(laserdisc_info *info); /* status callback */
+ UINT8 (*readline[LASERDISC_OUTPUT_LINES])(laserdisc_info *info); /* read line callback */
+ void (*statechanged)(laserdisc_info *info, UINT8 oldstate); /* state changed callback */
+
+ /* some player-specific data */
+ union
+ {
+ pr7820_info pr7820; /* PR-7820-specific info */
+ pr8210_info pr8210; /* PR-8210-specific info */
+ ldv1000_info ldv1000; /* LD-V1000-specific info */
+ ldp1450_info ldp1450; /* LDP-1450-specific info */
+ vp932_info vp932; /* 22VP932-specific info */
+ } u;
+};
+
+
+/* sound callback info */
+typedef struct _sound_token sound_token;
+struct _sound_token
+{
+ sound_stream * stream;
+ laserdisc_info *info;
+};
+
+
+
+/***************************************************************************
+ FUNCTION PROTOTYPES
+***************************************************************************/
+
+/* generic helper functions */
+static int update_position(laserdisc_info *info);
+static void read_track_data(laserdisc_info *info);
+static void process_track_data(laserdisc_info *info);
+static int parse_metadata(const UINT8 *rawdata, UINT32 track, UINT8 which, field_metadata *metadata);
+static void *custom_start(int clock, const struct CustomSound_interface *config);
+static void custom_stream_callback(void *param, stream_sample_t **inputs, stream_sample_t **outputs, int samples);
+
+/* Pioneer PR-7820 implementation */
+static void pr7820_init(laserdisc_info *info);
+static void pr7820_soft_reset(laserdisc_info *info);
+static void pr7820_enter_w(laserdisc_info *info, UINT8 data);
+static UINT8 pr7820_ready_r(laserdisc_info *info);
+static UINT8 pr7820_status_r(laserdisc_info *info);
+
+/* Pioneer PR-8210 implementation */
+static void pr8210_init(laserdisc_info *info);
+static void pr8210_soft_reset(laserdisc_info *info);
+static void pr8210_command(laserdisc_info *info);
+static void pr8210_control_w(laserdisc_info *info, UINT8 data);
+
+/* Pioneer LDV-1000 implementation */
+static void ldv1000_init(laserdisc_info *info);
+static void ldv1000_soft_reset(laserdisc_info *info);
+static void ldv1000_data_w(laserdisc_info *info, UINT8 prev, UINT8 data);
+static UINT8 ldv1000_status_strobe_r(laserdisc_info *info);
+static UINT8 ldv1000_command_strobe_r(laserdisc_info *info);
+static UINT8 ldv1000_status_r(laserdisc_info *info);
+
+/* Sony LDP-1450 implementation */
+static void ldp1450_init(laserdisc_info *info);
+static void ldp1450_soft_reset(laserdisc_info *info);
+static void ldp1450_compute_status(laserdisc_info *info);
+static void ldp1450_data_w(laserdisc_info *info, UINT8 prev, UINT8 data);
+static UINT8 ldp1450_data_avail_r(laserdisc_info *info);
+static UINT8 ldp1450_data_r(laserdisc_info *info);
+static void ldp1450_state_changed(laserdisc_info *info, UINT8 oldstate);
+
+/* Philips 22VP932 implementation */
+static void vp932_init(laserdisc_info *info);
+static void vp932_soft_reset(laserdisc_info *info);
+static void vp932_data_w(laserdisc_info *info, UINT8 prev, UINT8 data);
+static UINT8 vp932_data_avail_r(laserdisc_info *info);
+static UINT8 vp932_data_r(laserdisc_info *info);
+static void vp932_state_changed(laserdisc_info *info, UINT8 oldstate);
+
+
+
+/***************************************************************************
+ GLOBAL VARIABLES
+***************************************************************************/
+
+struct CustomSound_interface laserdisc_custom_interface =
+{
+ custom_start
+};
+
+
+
+/***************************************************************************
+ INLINE FUNCTIONS
+***************************************************************************/
+
+/*-------------------------------------------------
+ audio_channel_active - return TRUE if the
+ given audio channel should be output
+-------------------------------------------------*/
+
+INLINE int audio_channel_active(laserdisc_info *info, int channel)
+{
+ int result = (info->audio >> channel) & 1;
+
+ /* apply muting */
+ if (info->audio & (AUDIO_EXPLICIT_MUTE | AUDIO_IMPLICIT_MUTE))
+ result = 0;
+
+ /* implicitly muted during some states */
+ switch (info->state)
+ {
+ case LASERDISC_EJECTED:
+ case LASERDISC_EJECTING:
+ case LASERDISC_LOADING:
+ case LASERDISC_SPINUP:
+ case LASERDISC_PARKED:
+ case LASERDISC_LOADED:
+ case LASERDISC_SEARCHING_FRAME:
+ case LASERDISC_SEARCH_FINISHED:
+ case LASERDISC_STOPPED:
+ case LASERDISC_AUTOSTOPPED:
+ case LASERDISC_PLAYING_SLOW_FORWARD:
+ case LASERDISC_PLAYING_SLOW_REVERSE:
+ case LASERDISC_PLAYING_FAST_FORWARD:
+ case LASERDISC_PLAYING_FAST_REVERSE:
+ case LASERDISC_SCANNING_FORWARD:
+ case LASERDISC_SCANNING_REVERSE:
+ case LASERDISC_STEPPING_FORWARD:
+ case LASERDISC_STEPPING_REVERSE:
+ result = 0;
+ break;
+
+ default:
+ break;
+ }
+ return result;
+}
+
+
+/*-------------------------------------------------
+ video_active - return TRUE if the video should
+ be output
+-------------------------------------------------*/
+
+INLINE int video_active(laserdisc_info *info)
+{
+ int result = info->video & VIDEO_ENABLE;
+
+ /* apply muting */
+ if (info->video & (VIDEO_EXPLICIT_MUTE | VIDEO_IMPLICIT_MUTE))
+ result = 0;
+
+ /* implicitly muted during some states */
+ switch (info->state)
+ {
+ case LASERDISC_EJECTED:
+ case LASERDISC_EJECTING:
+ case LASERDISC_LOADING:
+ case LASERDISC_SPINUP:
+ case LASERDISC_PARKED:
+ case LASERDISC_LOADED:
+ case LASERDISC_SEARCHING_FRAME:
+ result = 0;
+ break;
+
+ default:
+ break;
+ }
+ return result;
+}
+
+
+/*-------------------------------------------------
+ laserdisc_ready - return TRUE if the
+ disc is not in a transient state
+-------------------------------------------------*/
+
+INLINE int laserdisc_ready(laserdisc_info *info)
+{
+ switch (info->state)
+ {
+ case LASERDISC_EJECTED:
+ case LASERDISC_EJECTING:
+ case LASERDISC_LOADING:
+ case LASERDISC_SPINUP:
+ case LASERDISC_PARKED:
+ return FALSE;
+
+ case LASERDISC_LOADED:
+ case LASERDISC_SEARCHING_FRAME:
+ case LASERDISC_SEARCH_FINISHED:
+ case LASERDISC_STOPPED:
+ case LASERDISC_AUTOSTOPPED:
+ case LASERDISC_PLAYING_FORWARD:
+ case LASERDISC_PLAYING_REVERSE:
+ case LASERDISC_PLAYING_SLOW_FORWARD:
+ case LASERDISC_PLAYING_SLOW_REVERSE:
+ case LASERDISC_PLAYING_FAST_FORWARD:
+ case LASERDISC_PLAYING_FAST_REVERSE:
+ case LASERDISC_STEPPING_FORWARD:
+ case LASERDISC_STEPPING_REVERSE:
+ case LASERDISC_SCANNING_FORWARD:
+ case LASERDISC_SCANNING_REVERSE:
+ return TRUE;
+ }
+ fatalerror("Unexpected state in laserdisc_ready\n");
+}
+
+
+/*-------------------------------------------------
+ laserdisc_active - return TRUE if the
+ disc is in a playing/spinning state
+-------------------------------------------------*/
+
+INLINE int laserdisc_active(laserdisc_info *info)
+{
+ switch (info->state)
+ {
+ case LASERDISC_EJECTED:
+ case LASERDISC_EJECTING:
+ case LASERDISC_LOADED:
+ case LASERDISC_LOADING:
+ case LASERDISC_SPINUP:
+ case LASERDISC_PARKED:
+ return FALSE;
+
+ case LASERDISC_SEARCHING_FRAME:
+ case LASERDISC_SEARCH_FINISHED:
+ case LASERDISC_STOPPED:
+ case LASERDISC_AUTOSTOPPED:
+ case LASERDISC_PLAYING_FORWARD:
+ case LASERDISC_PLAYING_REVERSE:
+ case LASERDISC_PLAYING_SLOW_FORWARD:
+ case LASERDISC_PLAYING_SLOW_REVERSE:
+ case LASERDISC_PLAYING_FAST_FORWARD:
+ case LASERDISC_PLAYING_FAST_REVERSE:
+ case LASERDISC_STEPPING_FORWARD:
+ case LASERDISC_STEPPING_REVERSE:
+ case LASERDISC_SCANNING_FORWARD:
+ case LASERDISC_SCANNING_REVERSE:
+ return TRUE;
+ }
+ fatalerror("Unexpected state in laserdisc_ready\n");
+}
+
+
+/*-------------------------------------------------
+ set_state - configure the current playback
+ state
+-------------------------------------------------*/
+
+INLINE void set_state(laserdisc_info *info, laserdisc_state state, INT32 fracspeed, INT32 targetframe)
+{
+ info->holdfinished.seconds = 0;
+ info->holdfinished.attoseconds = 0;
+ info->state = state;
+ info->curfracspeed = fracspeed;
+ info->targetframe = targetframe;
+}
+
+
+/*-------------------------------------------------
+ set_hold_state - configure a hold time and
+ a playback state to follow
+-------------------------------------------------*/
+
+INLINE void set_hold_state(laserdisc_info *info, attotime holdtime, laserdisc_state state, INT32 fracspeed)
+{
+ info->holdfinished = attotime_add(timer_get_time(), holdtime);
+ info->postholdstate = state;
+ info->postholdfracspeed = fracspeed;
+}
+
+
+/*-------------------------------------------------
+ add_to_current_track - add a value to the
+ current track, stopping if we hit the min or
+ max
+-------------------------------------------------*/
+
+INLINE int add_to_current_track(laserdisc_info *info, INT32 delta)
+{
+ info->curfractrack += delta;
+ if (info->curfractrack < ONE_TRACK)
+ {
+ info->curfractrack = ONE_TRACK;
+ return TRUE;
+ }
+ else if (info->curfractrack >= info->maxfractrack - ONE_TRACK)
+ {
+ info->curfractrack = info->maxfractrack - ONE_TRACK;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+/*-------------------------------------------------
+ frame_from_metadata - return the frame number
+ encoded in the metadata, if present, or -1
+-------------------------------------------------*/
+
+INLINE int frame_from_metadata(const field_metadata *metadata)
+{
+ UINT32 data;
+
+ if ((metadata->line17 & 0xf80000) == 0xf80000)
+ data = metadata->line17 & 0x7ffff;
+ else if ((metadata->line18 & 0xf80000) == 0xf80000)
+ data = metadata->line18 & 0x7ffff;
+ else
+ return -1;
+
+ return (((data >> 16) & 0x0f) * 10000) + (((data >> 12) & 0x0f) * 1000) + (((data >> 8) & 0x0f) * 100) + (((data >> 4) & 0x0f) * 10) + (data & 0x0f);
+}
+
+
+/*-------------------------------------------------
+ chapter_from_metadata - return the chapter
+ number encoded in the metadata, if present,
+ or -1
+-------------------------------------------------*/
+
+INLINE int chapter_from_metadata(const field_metadata *metadata)
+{
+ UINT32 data;
+
+ if ((metadata->line17 & 0xf00fff) == 0x800ddd)
+ data = metadata->line17 & 0x7f000;
+ else if ((metadata->line18 & 0xf00fff) == 0x800ddd)
+ data = metadata->line18 & 0x7f000;
+ else
+ return -1;
+
+ return (((data >> 16) & 0x0f) * 10) + ((data >> 12) & 0x0f);
+}
+
+
+/*-------------------------------------------------
+ read_16bits_from_ram_be - read 16 bits from
+ player RAM in big-endian format
+-------------------------------------------------*/
+
+INLINE UINT16 read_16bits_from_ram_be(UINT8 *ram, UINT32 offset)
+{
+ return (ram[offset + 0] << 8) | ram[offset + 1];
+}
+
+
+/*-------------------------------------------------
+ write_16bits_to_ram_be - write 16 bits to
+ player RAM in big endian format
+-------------------------------------------------*/
+
+INLINE void write_16bits_to_ram_be(UINT8 *ram, UINT32 offset, UINT16 data)
+{
+ ram[offset + 0] = data >> 8;
+ ram[offset + 1] = data >> 0;
+}
+
+
+/*-------------------------------------------------
+ fillbitmap_yuy16 - fill a YUY16 bitmap with a
+ given color pattern
+-------------------------------------------------*/
+
+INLINE void fillbitmap_yuy16(mame_bitmap *bitmap, UINT8 yval, UINT8 cr, UINT8 cb)
+{
+ UINT16 color0 = (yval << 8) | cb;
+ UINT16 color1 = (yval << 8) | cr;
+ int x, y;
+
+ /* write 32 bits of color (2 pixels at a time) */
+ for (y = 0; y < bitmap->height; y++)
+ {
+ UINT16 *dest = (UINT16 *)bitmap->base + y * bitmap->rowpixels;
+ for (x = 0; x < bitmap->width / 2; x++)
+ {
+ *dest++ = color0;
+ *dest++ = color1;
+ }
+ }
+}
+
+
+
+/***************************************************************************
+ GENERIC IMPLEMENTATION
+***************************************************************************/
+
+/*-------------------------------------------------
+ laserdisc_init - initialize state for
+ laserdisc playback
+-------------------------------------------------*/
+
+laserdisc_info *laserdisc_init(int type, chd_file *chd, int custom_index)
+{
+ int fps = 30, fpsfrac = 0, width = 720, height = 240, interlaced = 1, channels = 2, rate = 44100, metabytes = 0;
+ UINT32 fps_times_1million, max_samples_per_track;
+ laserdisc_info *info;
+ char metadata[256];
+ chd_error err;
+
+ assert_always(mame_get_phase(Machine) == MAME_PHASE_INIT, "Can only call laserdisc_init at init time!");
+
+ /* initialize the info */
+ info = auto_malloc(sizeof(*info));
+ memset(info, 0, sizeof(*info));
+ info->audiocustom = custom_index;
+
+ /* get the disc metadata and extract the info */
+ info->disc = chd;
+ if (info->disc != NULL)
+ {
+ /* require the A/V codec */
+ if (chd_get_header(info->disc)->compression != CHDCOMPRESSION_AV)
+ fatalerror("Laserdisc video must be compressed with the A/V codec!");
+
+ /* read and extract the metadata */
+ err = chd_get_metadata(info->disc, AV_METADATA_TAG, 0, metadata, sizeof(metadata), NULL, NULL);
+ if (err != CHDERR_NONE)
+ fatalerror("Non-A/V CHD file specified");
+ if (sscanf(metadata, AV_METADATA_FORMAT, &fps, &fpsfrac, &width, &height, &interlaced, &channels, &rate, &metabytes) != 8)
+ fatalerror("Invalid metadata in CHD file");
+
+ /* require interlaced video */
+ if (!interlaced)
+ fatalerror("Laserdisc video must be interlaced!");
+
+ /* determine the maximum track and allocate a frame buffer */
+ info->maxfractrack = INT_TO_FRAC(chd_get_header(info->disc)->totalhunks / (interlaced + 1));
+ info->framebuffer = auto_malloc(chd_get_header(info->disc)->hunkbytes);
+ }
+ else
+ info->maxfractrack = INT_TO_FRAC(54000);
+
+ /* allocate video frames */
+ info->videoframe[0] = auto_bitmap_alloc(width, height * 2, BITMAP_FORMAT_YUY16);
+ fillbitmap_yuy16(info->videoframe[0], 40, 109, 240);
+ info->videoframe[1] = auto_bitmap_alloc(width, height * 2, BITMAP_FORMAT_YUY16);
+ fillbitmap_yuy16(info->videoframe[1], 40, 109, 240);
+ info->emptyframe = auto_bitmap_alloc(width, height * 2, BITMAP_FORMAT_YUY16);
+ fillbitmap_yuy16(info->emptyframe, 0, 128, 128);
+
+ /* allocate audio buffers */
+ fps_times_1million = fps * 1000000 + fpsfrac;
+ max_samples_per_track = ((UINT64)rate * 1000000 + fps_times_1million - 1) / fps_times_1million;
+ info->audiobufsize = max_samples_per_track * 4;
+ info->audiobuffer[0] = auto_malloc(info->audiobufsize * sizeof(info->audiobuffer[0][0]));
+ info->audiobuffer[1] = auto_malloc(info->audiobufsize * sizeof(info->audiobuffer[1][0]));
+
+ /* attempt to wire up the audio */
+ if (sndti_exists(SOUND_CUSTOM, info->audiocustom))
+ {
+ sound_token *token = custom_get_token(info->audiocustom);
+ token->info = info;
+ stream_set_sample_rate(token->stream, rate);
+ }
+
+ /* reset the disc */
+ laserdisc_reset(info, type);
+ return info;
+}
+
+
+/*-------------------------------------------------
+ laserdisc_reset - reset laserdisc,
+ potentially changing the type
+-------------------------------------------------*/
+
+void laserdisc_reset(laserdisc_info *info, int type)
+{
+ int i;
+
+ /* set up the general info */
+ if (type != 0)
+ info->type = type;
+ info->video = VIDEO_ENABLE;
+ info->audio = AUDIO_CH1_ENABLE | AUDIO_CH2_ENABLE;
+ info->display = DISPLAY_ENABLE;
+
+ /* seek to frame 1 to start with */
+ set_state(info, LASERDISC_LOADING, GENERIC_RESET_SPEED, 1);
+
+ /* reset the I/O lines */
+ for (i = 0; i < LASERDISC_INPUT_LINES; i++)
+ info->linein[i] = CLEAR_LINE;
+ for (i = 0; i < LASERDISC_OUTPUT_LINES; i++)
+ info->lineout[i] = CLEAR_LINE;
+
+ /* reset callbacks */
+ info->writedata = NULL;
+ memset(info->writeline, 0, sizeof(info->writeline));
+ info->readdata = NULL;
+ memset(info->readline, 0, sizeof(info->readline));
+ info->statechanged = NULL;
+
+ /* each player can init */
+ switch (info->type)
+ {
+ case LASERDISC_TYPE_PR7820:
+ pr7820_init(info);
+ break;
+
+ case LASERDISC_TYPE_LDV1000:
+ ldv1000_init(info);
+ break;
+
+ case LASERDISC_TYPE_LDP1450:
+ ldp1450_init(info);
+ break;
+
+ case LASERDISC_TYPE_PR8210:
+ pr8210_init(info);
+ break;
+
+ case LASERDISC_TYPE_22VP932:
+ vp932_init(info);
+ break;
+
+ default:
+ fatalerror("Invalid laserdisc player type!");
+ break;
+ }
+
+ /* default to track 1 */
+ info->curfractrack = ONE_TRACK;
+}
+
+
+/*-------------------------------------------------
+ laserdisc_exit - cleanup
+-------------------------------------------------*/
+
+void laserdisc_exit(laserdisc_info *info)
+{
+ /* make sure all async operations have completed */
+ if (info->disc != NULL)
+ chd_async_complete(info->disc);
+}
+
+
+/*-------------------------------------------------
+ laserdisc_vsync - call this once per field
+ on the VSYNC signal
+-------------------------------------------------*/
+
+void laserdisc_vsync(laserdisc_info *info)
+{
+ UINT8 origstate = info->state;
+ UINT8 hittarget;
+
+ /* remember the time */
+ info->lastvsynctime = timer_get_time();
+
+ /* if we're holding, stay in this state until finished */
+ if (info->holdfinished.seconds != 0 || info->holdfinished.attoseconds != 0)
+ {
+ if (attotime_compare(info->lastvsynctime, info->holdfinished) < 0)
+ return;
+ info->state = info->postholdstate;
+ info->curfracspeed = info->postholdfracspeed;
+ info->holdfinished.seconds = 0;
+ info->holdfinished.attoseconds = 0;
+ }
+
+ /* wait for previous read and decode to finish */
+ process_track_data(info);
+
+ /* update our position for this field */
+ hittarget = update_position(info);
+
+ /* switch off the state */
+ switch (info->state)
+ {
+ /* parked: do nothing */
+ case LASERDISC_EJECTED:
+ case LASERDISC_EJECTING:
+ case LASERDISC_LOADED:
+ case LASERDISC_PARKED:
+ break;
+
+ /* playing: if we hit our target, indicate so; if we hit the beginning/end, stop */
+ case LASERDISC_STOPPED:
+ case LASERDISC_AUTOSTOPPED:
+ case LASERDISC_SEARCH_FINISHED:
+ case LASERDISC_PLAYING_FORWARD:
+ case LASERDISC_PLAYING_REVERSE:
+ case LASERDISC_PLAYING_SLOW_FORWARD:
+ case LASERDISC_PLAYING_SLOW_REVERSE:
+ case LASERDISC_PLAYING_FAST_FORWARD:
+ case LASERDISC_PLAYING_FAST_REVERSE:
+ case LASERDISC_STEPPING_FORWARD:
+ case LASERDISC_STEPPING_REVERSE:
+ case LASERDISC_SCANNING_FORWARD:
+ case LASERDISC_SCANNING_REVERSE:
+
+ /* autostop if we hit the target frame */
+ if (hittarget)
+ set_state(info, LASERDISC_AUTOSTOPPED, STOP_SPEED, NULL_TARGET_FRAME);
+ break;
+
+ /* loading; keep searching until we hit the target, then go into the stopped state */
+ case LASERDISC_LOADING:
+ case LASERDISC_SPINUP:
+
+ /* if we hit the target, go into search finished state */
+ if (hittarget)
+ set_state(info, LASERDISC_STOPPED, STOP_SPEED, NULL_TARGET_FRAME);
+ break;
+
+ /* searching; keep seeking until we hit the target */
+ case LASERDISC_SEARCHING_FRAME:
+
+ /* if we hit the target, go into search finished state */
+ if (hittarget)
+ set_state(info, LASERDISC_SEARCH_FINISHED, STOP_SPEED, NULL_TARGET_FRAME);
+ break;
+ }
+
+ /* if the state changed, notify */
+ if (info->state != origstate)
+ {
+ /* on a state change, implicity round to the nearest fraction */
+ info->curfractrack = INT_TO_FRAC(FRAC_TO_INT(info->curfractrack));
+ if (!(info->fieldnum & 1))
+ info->curfractrack += FRAC_ONE / 2;
+
+ /* notify the disc handler */
+ if (info->statechanged != NULL)
+ (*info->statechanged)(info, origstate);
+ }
+
+ /* start reading the track data for the next round */
+ info->fieldnum++;
+ read_track_data(info);
+}
+
+
+/*-------------------------------------------------
+ laserdisc_describe_state - return a text
+ string describing the current state
+-------------------------------------------------*/
+
+const char *laserdisc_describe_state(laserdisc_info *info)
+{
+ static const struct
+ {
+ laserdisc_state state;
+ const char *string;
+ } state_strings[] =
+ {
+ { LASERDISC_EJECTED, "Ejected" },
+ { LASERDISC_EJECTING, "Ejecting" },
+ { LASERDISC_LOADED, "Loaded" },
+ { LASERDISC_LOADING, "Loading" },
+ { LASERDISC_SPINUP, "Spinning Up" },
+ { LASERDISC_PARKED, "Parked" },
+ { LASERDISC_SEARCHING_FRAME, "Searching Frame" },
+ { LASERDISC_SEARCH_FINISHED, "Search Finished" },
+ { LASERDISC_STOPPED, "Stopped" },
+ { LASERDISC_AUTOSTOPPED, "Autostopped" },
+ { LASERDISC_PLAYING_FORWARD, "Playing Forward" },
+ { LASERDISC_PLAYING_REVERSE, "Playing Reverse" },
+ { LASERDISC_PLAYING_SLOW_FORWARD, "Playing Slow Forward x" },
+ { LASERDISC_PLAYING_SLOW_REVERSE, "Playing Slow Reverse x" },
+ { LASERDISC_PLAYING_FAST_FORWARD, "Playing Fast Forward x" },
+ { LASERDISC_PLAYING_FAST_REVERSE, "Playing Fast Reverse x" },
+ { LASERDISC_STEPPING_FORWARD, "Stepping Forward" },
+ { LASERDISC_STEPPING_REVERSE, "Stepping Reverse" },
+ { LASERDISC_SCANNING_FORWARD, "Scanning Forward" },
+ { LASERDISC_SCANNING_REVERSE, "Scanning Reverse" }
+ };
+ const char *description = "Unknown";
+ int i;
+
+ /* find the string */
+ for (i = 0; i < ARRAY_LENGTH(state_strings); i++)
+ if (state_strings[i].state == info->state)
+ description = state_strings[i].string;
+
+ /* construct the string */
+ if (description[strlen(description) - 1] == 'x')
+ sprintf(info->text, "%05d (%s%.1f)", FRAC_TO_INT(info->curfractrack), description, (float)info->curfracspeed / (float)INT_TO_FRAC(1));
+ else
+ sprintf(info->text, "%05d (%s)", FRAC_TO_INT(info->curfractrack), description);
+ return info->text;
+}
+
+
+/*-------------------------------------------------
+ laserdisc_data_w - write data to the given
+ laserdisc player
+-------------------------------------------------*/
+
+void laserdisc_data_w(laserdisc_info *info, UINT8 data)
+{
+ UINT8 prev = info->datain;
+ info->datain = data;
+
+ /* call through to the player-specific write handler */
+ if (info->writedata != NULL)
+ (*info->writedata)(info, prev, data);
+}
+
+
+/*-------------------------------------------------
+ laserdisc_line_w - control an input line
+-------------------------------------------------*/
+
+void laserdisc_line_w(laserdisc_info *info, UINT8 line, UINT8 newstate)
+{
+ assert(line < LASERDISC_INPUT_LINES);
+ assert(newstate == ASSERT_LINE || newstate == CLEAR_LINE || newstate == PULSE_LINE);
+
+ /* assert */
+ if (newstate == ASSERT_LINE || newstate == PULSE_LINE)
+ {
+ if (info->linein[line] != ASSERT_LINE)
+ {
+ /* call through to the player-specific line handler */
+ if (info->writeline[line] != NULL)
+ (*info->writeline[line])(info, ASSERT_LINE);
+ }
+ info->linein[line] = ASSERT_LINE;
+ }
+
+ /* deassert */
+ if (newstate == CLEAR_LINE || newstate == PULSE_LINE)
+ {
+ if (info->linein[line] != CLEAR_LINE)
+ {
+ /* call through to the player-specific line handler */
+ if (info->writeline[line] != NULL)
+ (*info->writeline[line])(info, CLEAR_LINE);
+ }
+ info->linein[line] = CLEAR_LINE;
+ }
+}
+
+
+/*-------------------------------------------------
+ laserdisc_data_r - return the current
+ data byte
+-------------------------------------------------*/
+
+UINT8 laserdisc_data_r(laserdisc_info *info)
+{
+ UINT8 result = info->dataout;
+
+ /* call through to the player-specific data handler */
+ if (info->readdata != NULL)
+ result = (*info->readdata)(info);
+
+ return result;
+}
+
+
+/*-------------------------------------------------
+ laserdisc_line_r - return the current state
+ of an output line
+-------------------------------------------------*/
+
+UINT8 laserdisc_line_r(laserdisc_info *info, UINT8 line)
+{
+ UINT8 result;
+
+ assert(line < LASERDISC_OUTPUT_LINES);
+ result = info->lineout[line];
+
+ /* call through to the player-specific data handler */
+ if (info->readline[line] != NULL)
+ result = (*info->readline[line])(info);
+
+ return result;
+}
+
+
+/*-------------------------------------------------
+ laserdisc_get_video - return the current
+ video frame
+-------------------------------------------------*/
+
+UINT32 laserdisc_get_video(laserdisc_info *info, mame_bitmap **bitmap)
+{
+ int frameindex;
+
+ /* determine the most recent live set of frames */
+ frameindex = info->videoindex;
+ if (info->videofields[frameindex] < 2)
+ frameindex ^= 1;
+
+ /* if no video present, return the empty frame */
+ if (!video_active(info) || info->videofields[frameindex] < 2)
+ {
+ *bitmap = info->emptyframe;
+ return 0;
+ }
+ else
+ {
+ *bitmap = info->videoframe[frameindex];
+ return info->videoframenum[frameindex];
+ }
+}
+
+
+/*-------------------------------------------------
+ laserdisc_get_field_code - return raw field
+ information read from the disc
+-------------------------------------------------*/
+
+UINT32 laserdisc_get_field_code(laserdisc_info *info, UINT8 code)
+{
+ int field = (info->fieldnum & 1) ^ 1;
+
+ /* if no video present, return */
+ if (!video_active(info))
+ return 0;
+
+ switch (code)
+ {
+ case LASERDISC_CODE_FRAME_FLAGS:
+ return info->metadata[field].frameflags;
+
+ case LASERDISC_CODE_WHITE_FLAG:
+ return info->metadata[field].whiteflag;
+
+ case LASERDISC_CODE_LINE16:
+ return info->metadata[field].line16;
+
+ case LASERDISC_CODE_LINE17:
+ return info->metadata[field].line17;
+
+ case LASERDISC_CODE_LINE18:
+ return info->metadata[field].line18;
+ }
+
+ return 0;
+}
+
+
+
+/***************************************************************************
+ GENERIC HELPER FUNCTIONS
+***************************************************************************/
+
+/*-------------------------------------------------
+ process_number - scan a list of bytecodes
+ and treat them as single digits if we get
+ a match
+-------------------------------------------------*/
+
+static int process_number(laserdisc_info *info, UINT8 byte, const UINT8 numbers[])
+{
+ int value;
+
+ /* look for a match in the list of number values; if we got one, append it to the parameter */
+ for (value = 0; value < 10; value++)
+ if (numbers[value] == byte)
+ {
+ info->parameter = (info->parameter == -1) ? value : (info->parameter * 10 + value);
+ return TRUE;
+ }
+
+ /* no match; return FALSE */
+ return FALSE;
+}
+
+
+/*-------------------------------------------------
+ update_position - update the head position
+ for this VSYNC
+-------------------------------------------------*/
+
+static int update_position(laserdisc_info *info)
+{
+ UINT32 tracknum = FRAC_TO_INT(info->curfractrack);
+ UINT32 fieldnum = info->fieldnum & 1;
+ INT32 speed = info->curfracspeed;
+ INT32 framedelta;
+
+ /* if video isn't active, we don't need to be careful about fields and frames */
+ if (!video_active(info))
+ {
+ int frame = frame_from_metadata(&info->metadata[fieldnum]);
+ int direction;
+
+ /* if we have no target, don't do anything */
+ if (info->targetframe == 0)
+ return TRUE;
+
+ /* if we didn't get any frame information this field, move onto the next */
+ if (frame == -1)
+ {
+ /* if we're in the lead-in section, advance more aggressively */
+ if (info->metadata[fieldnum].line17 == 0x88ffff || info->metadata[fieldnum].line18 == 0x88ffff)
+ add_to_current_track(info, 10 * ONE_TRACK);
+
+ /* if we're on the second field of a frame, the next frame is on the next track */
+ /* but don't do it if we're seeking to frame 1, since that might be the very first frame */
+ else if (fieldnum == 1 && (info->last_frame == 1 || info->targetframe != 1))
+ add_to_current_track(info, ONE_TRACK);
+ return FALSE;
+ }
+
+ /* if we hit our frame, we're done */
+ if (frame == info->targetframe)
+ {
+ info->curfractrack = INT_TO_FRAC(tracknum);
+ return TRUE;
+ }
+
+ /* determine the frame delta and direction */
+ framedelta = info->targetframe - frame;
+ direction = (framedelta < 0) ? -1 : 1;
+
+ /* if we're going backwards, go back at least 2 frames since we tend to move forward */
+ if (framedelta == -1)
+ framedelta = -2;
+
+ /* if we're only going forward one frame, only advance if we're on the second field */
+ /* otherwise, we might overshoot */
+ else if (framedelta == 1 && fieldnum == 0)
+ framedelta = 0;
+
+ /* scale up to fractional bits */
+ framedelta = INT_TO_FRAC(abs(framedelta));
+
+ /* determine the stepdelta */
+ add_to_current_track(info, MIN(framedelta, speed / 2) * direction);
+ return FALSE;
+ }
+
+ /* only advance on the second field */
+ if (fieldnum != 1)
+ return FALSE;
+
+ /* if we're moving backwards, advance back until we hit track 1 or the target */
+ if (speed < 0)
+ {
+ /* if we've hit the target, stop now */
+ if (info->targetframe != 0 && info->last_frame <= info->targetframe)
+ {
+ info->curfractrack = INT_TO_FRAC(tracknum);
+ return TRUE;
+ }
+
+ /* otherwise, clamp our delta so we don't overshoot */
+ else
+ {
+ framedelta = INT_TO_FRAC((info->targetframe == 0) ? -1000 : (info->targetframe - info->last_frame));
+ add_to_current_track(info, MAX(framedelta, speed));
+ }
+ }
+
+ /* if we're moving forwards, advance forward until we hit the max track or the target */
+ else if (speed > 0)
+ {
+ /* if we've hit the target, stop now */
+ if (info->targetframe != 0 && info->last_frame >= info->targetframe)
+ {
+ info->curfractrack = INT_TO_FRAC(tracknum);
+ return TRUE;
+ }
+
+ /* otherwise, clamp our delta so we don't overshoot */
+ else
+ {
+ framedelta = INT_TO_FRAC((info->targetframe == 0) ? 1000 : (info->targetframe - info->last_frame));
+ add_to_current_track(info, MIN(framedelta, speed));
+ }
+ }
+ return FALSE;
+}
+
+
+/*-------------------------------------------------
+ read_track_data - read and process data for
+ a particular video track
+-------------------------------------------------*/
+
+static void read_track_data(laserdisc_info *info)
+{
+ UINT32 tracknum = FRAC_TO_INT(info->curfractrack);
+ UINT32 fieldnum = info->fieldnum & 1;
+ UINT32 chdhunk = (tracknum - 1) * 2 + fieldnum;
+ chd_error err;
+
+ /* initialize the decompression structure */
+ info->avconfig.decode_mask = AVCOMP_DECODE_META;
+ info->avconfig.video_buffer = NULL;
+ info->avconfig.video_xor = BYTE_XOR_LE(0);
+ info->avconfig.audio_xor = BYTE_XOR_BE(0);
+
+ /* if video is active, enable video decoding */
+ if (video_active(info))
+ {
+ /* if the flags on the previous field indicate that the next field
+ is part of the same frame, then keep steady; otherwise, bump to
+ the next videoindex */
+ if (info->videofields[info->videoindex] > 0 && !(info->metadata[fieldnum ^ 1].frameflags & FRAMEFLAG_NEXT_SAME_FRAME))
+ {
+ info->videoindex ^= 1;
+ info->videofields[info->videoindex] = 0;
+ }
+
+ /* enable video and configure it to read into the videoframe */
+ info->avconfig.decode_mask |= AVCOMP_DECODE_VIDEO;
+ info->avconfig.video_buffer = (UINT8 *)BITMAP_ADDR16(info->videoframe[info->videoindex], fieldnum, 0);
+ info->avconfig.video_stride = 4 * info->videoframe[info->videoindex]->rowpixels;
+ }
+ else
+ info->videofields[0] = info->videofields[1] = 0;
+
+ /* if audio is active, enable audio decoding */
+ if (audio_channel_active(info, 0))
+ info->avconfig.decode_mask |= AVCOMP_DECODE_AUDIO(0);
+ if (audio_channel_active(info, 1))
+ info->avconfig.decode_mask |= AVCOMP_DECODE_AUDIO(1);
+
+ /* configure the codec and then read */
+ if (info->disc != NULL)
+ {
+ err = chd_codec_config(info->disc, AV_CODEC_DECOMPRESS_CONFIG, &info->avconfig);
+ if (err == CHDERR_NONE)
+ {
+ err = chd_read_async(info->disc, chdhunk, info->framebuffer);
+ info->readpending = TRUE;
+ }
+ }
+}
+
+
+/*-------------------------------------------------
+ process_track_data - process data from a
+ track after it has been read
+-------------------------------------------------*/
+
+static void process_track_data(laserdisc_info *info)
+{
+ UINT32 tracknum = FRAC_TO_INT(info->curfractrack);
+ UINT32 fieldnum = info->fieldnum & 1;
+ const UINT8 *rawdata = NULL;
+ const INT16 *sampsource[2];
+ int frame, chapter;
+ chd_error chderr;
+ int samples;
+
+ /* wait for the async operation to complete */
+ if (info->disc != NULL && info->readpending)
+ {
+ /* complete the async operation */
+ chderr = chd_async_complete(info->disc);
+ if (chderr == CHDERR_NONE || chderr == CHDERR_NO_ASYNC_OPERATION)
+ rawdata = info->framebuffer;
+ }
+ info->readpending = FALSE;
+
+ /* parse the metadata */
+ parse_metadata(rawdata, tracknum, fieldnum, &info->metadata[fieldnum]);
+
+ /* update the last seen frame and chapter */
+ frame = frame_from_metadata(&info->metadata[fieldnum]);
+ if (frame != -1)
+ info->last_frame = frame;
+ chapter = chapter_from_metadata(&info->metadata[fieldnum]);
+ if (chapter != -1)
+ info->last_chapter = chapter;
+
+ /* update video info */
+ if (rawdata != NULL && (info->avconfig.decode_mask & AVCOMP_DECODE_VIDEO))
+ {
+ info->videofields[info->videoindex]++;
+ info->videoframenum[info->videoindex] = info->last_frame;
+ }
+
+ /* stream the audio into our ring buffers */
+ if (rawdata != NULL)
+ {
+ samples = (rawdata[6] << 8) + rawdata[7];
+ sampsource[0] = (const INT16 *)(rawdata + 12 + rawdata[4]) + 0 * samples;
+ sampsource[1] = sampsource[0] + samples;
+
+ /* loop until all samples are copied */
+ while (samples != 0)
+ {
+ int samples_to_copy = MIN(info->audiobufsize - info->audiobufin, samples);
+ int channum;
+
+ /* don't overrun the output pointer */
+ if (info->audiobufout > info->audiobufin)
+ {
+ samples_to_copy = MIN(samples_to_copy, info->audiobufout - info->audiobufin);
+ if (samples_to_copy == 0)
+ break;
+ }
+
+ /* for reach channel, copy the data or clear to 0 */
+ for (channum = 0; channum < 2; channum++)
+ {
+ if (audio_channel_active(info, channum))
+ {
+ memcpy(&info->audiobuffer[channum][info->audiobufin], sampsource[channum], samples_to_copy * 2);
+ sampsource[channum] += samples_to_copy;
+ }
+ else
+ memset(&info->audiobuffer[channum][info->audiobufin], 0, samples_to_copy * 2);
+ }
+ samples -= samples_to_copy;
+
+ /* point past the data */
+ info->audiobufin += samples_to_copy;
+ if (info->audiobufin >= info->audiobufsize)
+ info->audiobufin = 0;
+ }
+ }
+}
+
+
+/*-------------------------------------------------
+ parse_metadata - parse raw metadata into
+ something more useful
+-------------------------------------------------*/
+
+static int parse_metadata(const UINT8 *rawdata, UINT32 track, UINT8 which, field_metadata *metadata)
+{
+ /* verify we have data to parse */
+ if (rawdata == NULL || rawdata[4] < 1)
+ goto fakeit;
+
+ /* first byte is the version */
+ metadata->version = rawdata[12+0];
+
+ /* version 1 data */
+ if (metadata->version == 2 && rawdata[4] == 12)
+ {
+ /*
+ 1 byte = version
+ 1 byte = internal flags
+ D0 = prev field is same frame
+ D1 = next field is same frame
+ 1 byte = white flag
+ 3 bytes = line 16 Philips code
+ 3 bytes = line 17 Philips code
+ 3 bytes = line 18 Philips code
+ */
+ metadata->frameflags = rawdata[12+1];
+ metadata->whiteflag = rawdata[12+2];
+ metadata->line16 = (rawdata[12+3] << 16) | (rawdata[12+4] << 8) | rawdata[12+5];
+ metadata->line17 = (rawdata[12+6] << 16) | (rawdata[12+7] << 8) | rawdata[12+8];
+ metadata->line18 = (rawdata[12+9] << 16) | (rawdata[12+10] << 8) | rawdata[12+11];
+ return TRUE;
+ }
+
+fakeit:
+ metadata->frameflags = (which == 0) ? 0x02 : 0x01;
+ metadata->whiteflag = (which == 0) ? 1 : 0;
+ metadata->line16 = 0x000000;
+ if (which == 0)
+ metadata->line17 = metadata->line18 = 0xf80000 | (((track / 10000) % 10) << 16) | (((track / 1000) % 10) << 12) | (((track / 100) % 10) << 8) | (((track / 10) % 10) << 4) | (track % 10);
+ else
+ metadata->line17 = metadata->line18 = 0x000000;
+ return FALSE;
+}
+
+
+/*-------------------------------------------------
+ custom_start - custom audio start
+ for laserdiscs
+-------------------------------------------------*/
+
+static void *custom_start(int clock, const struct CustomSound_interface *config)
+{
+ sound_token *token = auto_malloc(sizeof(*token));
+ token->stream = stream_create(0, 2, 44100, token, custom_stream_callback);
+ token->info = NULL;
+ return token;
+}
+
+
+/*-------------------------------------------------
+ custom_stream_callback - audio streamer
+ for laserdiscs
+-------------------------------------------------*/
+
+static void custom_stream_callback(void *param, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+{
+ sound_token *token = param;
+ laserdisc_info *info = token->info;
+ stream_sample_t *dst0 = outputs[0];
+ stream_sample_t *dst1 = outputs[1];
+ int samples_avail = 0;
+
+ /* see if we have enough samples to fill the buffer; if not, drop out */
+ if (info != NULL)
+ {
+ samples_avail = info->audiobufin - info->audiobufout;
+ if (samples_avail < 0)
+ samples_avail += info->audiobufsize;
+ }
+
+ /* if no attached info, just clear the buffers */
+ if (samples_avail < samples)
+ {
+ memset(dst0, 0, samples * sizeof(dst0[0]));
+ memset(dst1, 0, samples * sizeof(dst1[0]));
+ }
+
+ /* otherwise, stream from our buffer */
+ else
+ {
+ const INT16 *buffer0 = info->audiobuffer[0];
+ const INT16 *buffer1 = info->audiobuffer[1];
+ int sampout = info->audiobufout;
+
+ /* copy samples */
+ while (sampout != info->audiobufin && samples-- > 0)
+ {
+ *dst0++ = buffer0[sampout];
+ *dst1++ = buffer1[sampout];
+ sampout++;
+ if (sampout >= info->audiobufsize)
+ sampout = 0;
+ }
+ info->audiobufout = sampout;
+
+ /* clear out the rest of the buffer */
+ if (samples > 0)
+ {
+ int sampout = (info->audiobufout == 0) ? info->audiobufsize - 1 : info->audiobufout - 1;
+ stream_sample_t fill0 = buffer0[sampout];
+ stream_sample_t fill1 = buffer1[sampout];
+
+ while (samples-- > 0)
+ {
+ *dst0++ = fill0;
+ *dst1++ = fill1;
+ }
+ }
+ }
+}
+
+
+
+/***************************************************************************
+ PIONEER PR-7820 IMPLEMENTATION
+***************************************************************************/
+
+/*-------------------------------------------------
+ pr7820_init - Pioneer PR-7820-specific
+ initialization
+-------------------------------------------------*/
+
+static void pr7820_init(laserdisc_info *info)
+{
+ pr7820_info *pr7820 = &info->u.pr7820;
+
+ /* set up the write callbacks */
+ info->writeline[LASERDISC_LINE_ENTER] = pr7820_enter_w;
+
+ /* set up the read callbacks */
+ info->readdata = pr7820_status_r;
+ info->readline[LASERDISC_LINE_READY] = pr7820_ready_r;
+
+ /* do a soft reset */
+ pr7820->configspeed = PLAY_SPEED / 2;
+ pr7820_soft_reset(info);
+}
+
+
+/*-------------------------------------------------
+ pr7820_soft_reset - Pioneer PR-7820-specific
+ soft reset
+-------------------------------------------------*/
+
+static void pr7820_soft_reset(laserdisc_info *info)
+{
+ pr7820_info *pr7820 = &info->u.pr7820;
+
+ info->audio = AUDIO_CH1_ENABLE | AUDIO_CH2_ENABLE;
+ info->display = 0;
+ pr7820->mode = PR7820_MODE_MANUAL;
+ pr7820->activereg = 0;
+ write_16bits_to_ram_be(pr7820->ram, 0, 1);
+}
+
+
+/*-------------------------------------------------
+ pr7820_enter_w - write callback when the
+ ENTER state is asserted
+-------------------------------------------------*/
+
+static void pr7820_enter_w(laserdisc_info *info, UINT8 newstate)
+{
+ static const UINT8 numbers[10] = { 0x3f, 0x0f, 0x8f, 0x4f, 0x2f, 0xaf, 0x6f, 0x1f, 0x9f, 0x5f };
+ pr7820_info *pr7820 = &info->u.pr7820;
+ UINT8 data = (pr7820->mode == PR7820_MODE_AUTOMATIC) ? pr7820->ram[pr7820->curpc++ % 1024] : info->datain;
+
+ /* we only care about assertions */
+ if (newstate != ASSERT_LINE)
+ return;
+
+ /* if we're in program mode, just write data */
+ if (pr7820->mode == PR7820_MODE_PROGRAM && data != 0xef)
+ {
+ pr7820->ram[info->parameter++ % 1024] = data;
+ return;
+ }
+
+ /* look for and process numbers */
+ if (process_number(info, data, numbers))
+ return;
+
+ /* handle commands */
+ switch (data)
+ {
+ case 0x7f: CMDPRINTF(("pr7820: %d Recall\n", info->parameter));
+ /* set the active register */
+ pr7820->activereg = info->parameter;
+ break;
+
+ case 0xa0:
+ case 0xa1:
+ case 0xa2:
+ case 0xa3: CMDPRINTF(("pr7820: Direct audio control %d\n", data & 0x03));
+ /* control both channels directly */
+ info->audio = 0;
+ if (data & 0x01)
+ info->audio |= AUDIO_CH1_ENABLE;
+ if (data & 0x02)
+ info->audio |= AUDIO_CH2_ENABLE;
+ break;
+
+ case 0xbf: CMDPRINTF(("pr7820: %d Halt\n", info->parameter));
+ /* stop automatic mode */
+ pr7820->mode = PR7820_MODE_MANUAL;
+ break;
+
+ case 0xcc: CMDPRINTF(("pr7820: Load\n"));
+ /* load program from disc -- not implemented */
+ break;
+
+ case 0xcf: CMDPRINTF(("pr7820: %d Branch\n", info->parameter));
+ /* branch to a new PC */
+ if (pr7820->mode == PR7820_MODE_AUTOMATIC)
+ pr7820->curpc = (info->parameter == -1) ? 0 : info->parameter;
+ break;
+
+ case 0xdf: CMDPRINTF(("pr7820: %d Write program\n", info->parameter));
+ /* enter program mode */
+ pr7820->mode = PR7820_MODE_PROGRAM;
+ break;
+
+ case 0xe1: CMDPRINTF(("pr7820: Soft Reset\n"));
+ /* soft reset */
+ pr7820_soft_reset(info);
+ break;
+
+ case 0xe3: CMDPRINTF(("pr7820: Display off\n"));
+ /* turn off frame display */
+ info->display = 0x00;
+ break;
+
+ case 0xe4: CMDPRINTF(("pr7820: Display on\n"));
+ /* turn on frame display */
+ info->display = 0x01;
+ break;
+
+ case 0xe5: CMDPRINTF(("pr7820: Audio 2 off\n"));
+ /* turn off audio channel 2 */
+ info->audio &= ~AUDIO_CH2_ENABLE;
+ break;
+
+ case 0xe6: CMDPRINTF(("pr7820: Audio 2 on\n"));
+ /* turn on audio channel 2 */
+ info->audio |= AUDIO_CH2_ENABLE;
+ break;
+
+ case 0xe7: CMDPRINTF(("pr7820: Audio 1 off\n"));
+ /* turn off audio channel 1 */
+ info->audio &= ~AUDIO_CH1_ENABLE;
+ break;
+
+ case 0xe8: CMDPRINTF(("pr7820: Audio 1 on\n"));
+ /* turn on audio channel 1 */
+ info->audio |= AUDIO_CH1_ENABLE;
+ break;
+
+ case 0xe9: CMDPRINTF(("pr7820: %d Dump RAM\n", info->parameter));
+ /* not implemented */
+ break;
+
+ case 0xea: CMDPRINTF(("pr7820: %d Dump frame\n", info->parameter));
+ /* not implemented */
+ break;
+
+ case 0xeb: CMDPRINTF(("pr7820: %d Dump player status\n", info->parameter));
+ /* not implemented */
+ break;
+
+ case 0xef: CMDPRINTF(("pr7820: %d End program\n", info->parameter));
+ /* exit programming mode */
+ pr7820->mode = PR7820_MODE_MANUAL;
+ break;
+
+ case 0xf0: CMDPRINTF(("pr7820: %d Decrement reg\n", info->parameter));
+ /* decrement register; if we hit 0, skip past the next branch statement */
+ if (pr7820->mode == PR7820_MODE_AUTOMATIC)
+ {
+ UINT16 tempreg = read_16bits_from_ram_be(pr7820->ram, (info->parameter * 2) % 1024);
+ tempreg = (tempreg == 0) ? 0 : tempreg - 1;
+ write_16bits_to_ram_be(pr7820->ram, (info->parameter * 2) % 1024, tempreg);
+ if (tempreg == 0)
+ while (pr7820->ram[pr7820->curpc++ % 1024] != 0xcf) ;
+ }
+ break;
+
+ case 0xf1: CMDPRINTF(("pr7820: %d Display\n", info->parameter));
+ /* toggle or set the frame display */
+ info->display = (info->parameter == -1) ? !info->display : (info->parameter & 1);
+ break;
+
+ case 0xf2: CMDPRINTF(("pr7820: %d Slow forward\n", info->parameter));
+ /* play forward at slow speed (controlled by lever on the front of the player) */
+ if (laserdisc_ready(info))
+ {
+ if (info->parameter != -1)
+ set_state(info, LASERDISC_PLAYING_SLOW_FORWARD, pr7820->configspeed, info->parameter);
+ else
+ set_state(info, LASERDISC_PLAYING_SLOW_FORWARD, pr7820->configspeed, NULL_TARGET_FRAME);
+ }
+ break;
+
+ case 0xf3: CMDPRINTF(("pr7820: %d Autostop\n", info->parameter));
+ /* play to a particular location and stop there */
+ if (laserdisc_ready(info))
+ {
+ INT32 targetframe = info->parameter;
+
+ if (targetframe == -1)
+ targetframe = read_16bits_from_ram_be(pr7820->ram, (pr7820->activereg * 2) % 1024);
+ pr7820->activereg++;
+
+ if (targetframe > info->last_frame)
+ set_state(info, LASERDISC_PLAYING_FORWARD, PLAY_SPEED, targetframe);
+ else
+ set_state(info, LASERDISC_SEARCHING_FRAME, PR7820_SEARCH_SPEED, targetframe);
+ }
+ break;
+
+ case 0xf4: CMDPRINTF(("pr7820: %d Audio track 1\n", info->parameter));
+ /* toggle or set the state of audio channel 1 */
+ if (info->parameter == -1)
+ info->audio ^= AUDIO_CH1_ENABLE;
+ else
+ info->audio = (info->audio & ~AUDIO_CH1_ENABLE) | ((info->parameter & 1) ? AUDIO_CH1_ENABLE : 0);
+ break;
+
+ case 0xf5: CMDPRINTF(("pr7820: %d Store\n", info->parameter));
+ /* store either the current frame number or an explicit value into the active register */
+ if (info->parameter == -1)
+ write_16bits_to_ram_be(pr7820->ram, (pr7820->activereg * 2) % 1024, info->last_frame);
+ else
+ write_16bits_to_ram_be(pr7820->ram, (pr7820->activereg * 2) % 1024, info->parameter);
+ pr7820->activereg++;
+ break;
+
+ case 0xf6: CMDPRINTF(("pr7820: Step forward\n"));
+ /* step forward one frame */
+ if (laserdisc_ready(info))
+ {
+ set_state(info, LASERDISC_STEPPING_FORWARD, STOP_SPEED, NULL_TARGET_FRAME);
+ add_to_current_track(info, ONE_TRACK);
+ }
+ break;
+
+ case 0xf7: CMDPRINTF(("pr7820: %d Search\n", info->parameter));
+ /* search to a particular frame number */
+ if (laserdisc_ready(info))
+ {
+ INT32 targetframe = info->parameter;
+
+ if (targetframe == -1)
+ targetframe = read_16bits_from_ram_be(pr7820->ram, (pr7820->activereg * 2) % 1024);
+ pr7820->activereg++;
+
+ if (targetframe == 0)
+ targetframe = 1;
+ set_state(info, LASERDISC_SEARCHING_FRAME, PR7820_SEARCH_SPEED, targetframe);
+ }
+ break;
+
+ case 0xf8: CMDPRINTF(("pr7820: %d Input\n", info->parameter));
+ /* wait for user input -- not implemented */
+ break;
+
+ case 0xf9: CMDPRINTF(("pr7820: Reject\n"));
+ /* eject the disc */
+ if (laserdisc_ready(info))
+ {
+ set_state(info, LASERDISC_EJECTING, STOP_SPEED, NULL_TARGET_FRAME);
+ set_hold_state(info, GENERIC_LOAD_TIME, LASERDISC_EJECTED, STOP_SPEED);
+ }
+ break;
+
+ case 0xfa: CMDPRINTF(("pr7820: %d Slow reverse\n", info->parameter));
+ /* play backwards at slow speed (controlled by lever on the front of the player) */
+ if (laserdisc_ready(info))
+ set_state(info, LASERDISC_PLAYING_SLOW_REVERSE, -pr7820->configspeed, (info->parameter != -1) ? info->parameter : NULL_TARGET_FRAME);
+ break;
+
+ case 0xfb: CMDPRINTF(("pr7820: %d Stop/Wait\n", info->parameter));
+ /* pause at the current location for a fixed amount of time (in 1/10ths of a second) */
+ if (laserdisc_ready(info))
+ {
+ laserdisc_state prevstate = info->state;
+ INT32 prevspeed = info->curfracspeed;
+ set_state(info, LASERDISC_STOPPED, STOP_SPEED, NULL_TARGET_FRAME);
+ if (info->parameter != -1)
+ set_hold_state(info, double_to_attotime(info->parameter * 0.1), prevstate, prevspeed);
+ }
+ break;
+
+ case 0xfc: CMDPRINTF(("pr7820: %d Audio track 2\n", info->parameter));
+ /* toggle or set the state of audio channel 2 */
+ if (info->parameter == -1)
+ info->audio ^= AUDIO_CH2_ENABLE;
+ else
+ info->audio = (info->audio & ~AUDIO_CH2_ENABLE) | ((info->parameter & 1) ? AUDIO_CH2_ENABLE : 0);
+ break;
+
+ case 0xfd: CMDPRINTF(("pr7820: Play\n"));
+ /* begin playing at regular speed, or load the disc if it is parked */
+ if (laserdisc_ready(info))
+ set_state(info, LASERDISC_PLAYING_FORWARD, PLAY_SPEED, NULL_TARGET_FRAME);
+ else
+ {
+ if (info->state == LASERDISC_PARKED)
+ {
+ set_state(info, LASERDISC_SPINUP, STOP_SPEED, NULL_TARGET_FRAME);
+ set_hold_state(info, GENERIC_SPINUP_TIME, LASERDISC_PLAYING_FORWARD, PLAY_SPEED);
+ }
+ else
+ {
+ set_state(info, LASERDISC_LOADING, STOP_SPEED, NULL_TARGET_FRAME);
+ set_hold_state(info, GENERIC_LOAD_TIME, LASERDISC_PLAYING_FORWARD, PLAY_SPEED);
+ }
+
+ info->curfractrack = ONE_TRACK;
+ }
+ break;
+
+ case 0xfe: CMDPRINTF(("pr7820: Step reverse\n"));
+ /* step backwards one frame */
+ if (laserdisc_ready(info))
+ {
+ set_state(info, LASERDISC_STEPPING_REVERSE, STOP_SPEED, NULL_TARGET_FRAME);
+ add_to_current_track(info, -ONE_TRACK);
+ }
+ break;
+
+ default: CMDPRINTF(("pr7820: %d Unknown command %02X\n", info->parameter, data));
+ /* unknown command */
+ break;
+ }
+
+ /* reset the parameter after executing a command */
+ info->parameter = -1;
+}
+
+
+/*-------------------------------------------------
+ pr7820_ready_r - return state of the ready
+ line
+-------------------------------------------------*/
+
+static UINT8 pr7820_ready_r(laserdisc_info *info)
+{
+ return (info->state != LASERDISC_SEARCHING_FRAME) ? ASSERT_LINE : CLEAR_LINE;
+}
+
+
+/*-------------------------------------------------
+ pr7820_status_r - return state of the ready
+ line
+-------------------------------------------------*/
+
+static UINT8 pr7820_status_r(laserdisc_info *info)
+{
+ pr7820_info *pr7820 = &info->u.pr7820;
+
+ /* top 3 bits reflect audio and display states */
+ UINT8 status = (info->audio << 6) | (info->display << 5);
+
+ /* low 5 bits reflect player states */
+
+ /* handle program mode */
+ if (pr7820->mode == PR7820_MODE_PROGRAM)
+ status |= 0x0c;
+ else
+ {
+ /* convert generic status into specific state equivalents */
+ switch (info->state)
+ {
+ case LASERDISC_EJECTED: status |= 0x0d; break;
+ case LASERDISC_EJECTING: status |= 0x0d; break;
+ case LASERDISC_LOADED: status |= 0x01; break;
+ case LASERDISC_SPINUP: status |= 0x01; break;
+ case LASERDISC_LOADING: status |= 0x01; break;
+ case LASERDISC_PARKED: status |= 0x01; break;
+
+ case LASERDISC_SEARCHING_FRAME: status |= 0x06; break;
+ case LASERDISC_SEARCH_FINISHED: status |= 0x07; break;
+
+ case LASERDISC_STOPPED: status |= 0x03; break;
+ case LASERDISC_AUTOSTOPPED: status |= 0x09; break;
+
+ case LASERDISC_PLAYING_FORWARD: status |= 0x02; break;
+ case LASERDISC_PLAYING_REVERSE: status |= 0x02; break;
+ case LASERDISC_PLAYING_SLOW_FORWARD: status |= 0x04; break;
+ case LASERDISC_PLAYING_SLOW_REVERSE: status |= 0x05; break;
+ case LASERDISC_PLAYING_FAST_FORWARD: status |= 0x02; break;
+ case LASERDISC_PLAYING_FAST_REVERSE: status |= 0x02; break;
+ case LASERDISC_STEPPING_FORWARD: status |= 0x03; break;
+ case LASERDISC_STEPPING_REVERSE: status |= 0x03; break;
+ case LASERDISC_SCANNING_FORWARD: status |= 0x02; break;
+ case LASERDISC_SCANNING_REVERSE: status |= 0x02; break;
+ default:
+ fatalerror("Unexpected disc state in pr7820_status_r\n");
+ break;
+ }
+ }
+ return status;
+}
+
+
+/*-------------------------------------------------
+ pr7820_set_slow_speed - set the speed of
+ "slow" playback, which is controlled by a
+ slider on the device
+-------------------------------------------------*/
+
+void pr7820_set_slow_speed(laserdisc_info *info, double frame_rate_scaler)
+{
+ pr7820_info *pr7820 = &info->u.pr7820;
+ pr7820->configspeed = PLAY_SPEED * frame_rate_scaler;
+}
+
+
+
+/***************************************************************************
+ PIONEER PR-8210 IMPLEMENTATION
+***************************************************************************/
+
+/*-------------------------------------------------
+ pr8210_init - Pioneer PR-8210-specific
+ initialization
+-------------------------------------------------*/
+
+static void pr8210_init(laserdisc_info *info)
+{
+ /* set up the write callbacks */
+ info->writeline[LASERDISC_LINE_CONTROL] = pr8210_control_w; /* serial access */
+
+ /* do a soft reset */
+ pr8210_soft_reset(info);
+}
+
+
+/*-------------------------------------------------
+ pr8210_soft_reset - Pioneer PR-8210-specific
+ soft reset
+-------------------------------------------------*/
+
+static void pr8210_soft_reset(laserdisc_info *info)
+{
+ pr8210_info *pr8210 = &info->u.pr8210;
+
+ info->audio = AUDIO_CH1_ENABLE | AUDIO_CH2_ENABLE;
+ info->display = 0;
+ pr8210->mode = PR8210_MODE_GET_1ST;
+ pr8210->commandtime = timer_get_time();
+ pr8210->commandbits = 0;
+ memset( pr8210->commandtriplet, 0, 3*sizeof(UINT16) );
+ pr8210->seekstate = 0;
+}
+
+
+/*-------------------------------------------------
+ pr8210_command - Pioneer PR-8210-specific
+ command processing
+-------------------------------------------------*/
+
+static void pr8210_command(laserdisc_info *info)
+{
+ pr8210_info *pr8210 = &info->u.pr8210;
+
+ /* if we don't have the entire command triplet yet, keep going */
+ if ( pr8210->mode < PR8210_MODE_GET_3RD )
+ {
+ /* do some sanity checks on the command data */
+ if ( pr8210->mode == PR8210_MODE_GET_2ND )
+ {
+ /* if the commands don't match, then reassign the new command to the first word, then keep fetching */
+ if ( pr8210->commandtriplet[PR8210_MODE_GET_1ST] != pr8210->commandtriplet[PR8210_MODE_GET_2ND] )
+ {
+ pr8210->commandtriplet[PR8210_MODE_GET_1ST] = pr8210->commandtriplet[PR8210_MODE_GET_2ND];
+ pr8210->mode = PR8210_MODE_GET_2ND;
+ return;
+ }
+ }
+
+ pr8210->mode++;
+ }
+ else /* we're ready to process the command */
+ {
+ /* do some sanity checks on the command data */
+ static const UINT8 numbers[10] = { 0x01,0x11,0x09,0x19,0x05,0x15,0x0d,0x1d,0x03,0x13 };
+ UINT16 cmd = pr8210->commandtriplet[PR8210_MODE_GET_1ST];
+
+ /* more sanity checking: bit 7 must be set for a valid command */
+ if ( cmd & 0x80 )
+ {
+ /* extract the actual command number */
+ cmd = ( cmd >> 2 ) & 0x1f;
+
+ /* look for and process numbers */
+ if (!process_number(info, cmd, numbers))
+ {
+ switch( cmd )
+ {
+ case 0x00: CMDPRINTF(("pr8210: EOC\n"));
+ /* EOC marker - can be safely ignored */
+ break;
+
+ case 0x02: CMDPRINTF(("pr8210: Slow reverse\n"));
+ /* slow reverse */
+ if (laserdisc_ready(info))
+ set_state(info, LASERDISC_PLAYING_SLOW_REVERSE, -PR8210_SLOW_SPEED, NULL_TARGET_FRAME);
+ break;
+
+ case 0x04: CMDPRINTF(("pr8210: Step forward\n"));
+ /* step forward */
+ if (laserdisc_ready(info))
+ set_state(info, LASERDISC_STEPPING_FORWARD, PR8210_STEP_SPEED, NULL_TARGET_FRAME);
+ break;
+
+ case 0x06 : CMDPRINTF(("pr8210: Chapter\n"));
+ /* chapter -- not implemented */
+ break;
+
+ case 0x08: CMDPRINTF(("pr8210: Scan forward\n"));
+ /* scan forward */
+ if (laserdisc_ready(info))
+ set_state(info, LASERDISC_SCANNING_FORWARD, PR8210_SCAN_SPEED, NULL_TARGET_FRAME);
+ break;
+
+ case 0x0a: CMDPRINTF(("pr8210: Pause\n"));
+ /* still picture */
+ if (laserdisc_ready(info))
+ set_state(info, LASERDISC_STOPPED, STOP_SPEED, NULL_TARGET_FRAME);
+ break;
+
+ case 0x0b : CMDPRINTF(("pr8210: Frame\n"));
+ /* frame -- not implemented */
+ break;
+
+ case 0x0c: CMDPRINTF(("pr8210: Fast reverse\n"));
+ /* play reverse fast speed */
+ if (laserdisc_ready(info))
+ set_state(info, LASERDISC_PLAYING_FAST_REVERSE, -PR8210_FAST_SPEED, NULL_TARGET_FRAME);
+ break;
+
+ case 0x0e: CMDPRINTF(("pr8210: Ch1 toggle\n"));
+ /* channel 1 audio toggle */
+ info->audio ^= AUDIO_CH1_ENABLE;
+ break;
+
+ case 0x10: CMDPRINTF(("pr8210: Fast forward\n"));
+ /* play forward fast speed */
+ if (laserdisc_ready(info))
+ set_state(info, LASERDISC_PLAYING_FAST_FORWARD, PR8210_FAST_SPEED, NULL_TARGET_FRAME);
+ break;
+
+ case 0x12: CMDPRINTF(("pr8210: Step reverse\n"));
+ /* step backwards one frame */
+ if (laserdisc_ready(info))
+ {
+ set_state(info, LASERDISC_STEPPING_REVERSE, STOP_SPEED, NULL_TARGET_FRAME);
+ add_to_current_track(info, -ONE_TRACK);
+ }
+ break;
+
+ case 0x14: CMDPRINTF(("pr8210: Play\n"));
+ /* begin playing at regular speed, or load the disc if it is parked */
+ if (laserdisc_ready(info))
+ set_state(info, LASERDISC_PLAYING_FORWARD, PLAY_SPEED, NULL_TARGET_FRAME);
+ else
+ {
+ /* if we're already spinning up or loading, ignore */
+ if (info->state != LASERDISC_SPINUP && info->state != LASERDISC_LOADING)
+ {
+ if (info->state == LASERDISC_PARKED)
+ {
+ set_state(info, LASERDISC_SPINUP, STOP_SPEED, NULL_TARGET_FRAME);
+ set_hold_state(info, GENERIC_SPINUP_TIME, LASERDISC_PLAYING_FORWARD, PLAY_SPEED);
+ }
+ else
+ {
+ set_state(info, LASERDISC_LOADING, STOP_SPEED, NULL_TARGET_FRAME);
+ set_hold_state(info, GENERIC_LOAD_TIME, LASERDISC_PLAYING_FORWARD, PLAY_SPEED);
+ }
+
+ info->curfractrack = ONE_TRACK;
+ }
+ }
+ break;
+
+ case 0x16: CMDPRINTF(("pr8210: Ch2 toggle\n"));
+ /* channel 1 audio toggle */
+ info->audio ^= AUDIO_CH2_ENABLE;
+ break;
+
+ case 0x18: CMDPRINTF(("pr8210: Slow forward\n"));
+ /* slow forward */
+ if (laserdisc_ready(info))
+ set_state(info, LASERDISC_PLAYING_SLOW_FORWARD, PR8210_SLOW_SPEED, NULL_TARGET_FRAME);
+ break;
+
+ case 0x1a: CMDPRINTF(("pr8210: Seek\n"));
+ /* seek */
+ if ( pr8210->seekstate )
+ {
+ CMDPRINTF(("pr8210: Seeking to frame:%d\n", info->parameter));
+ /* we're ready to seek */
+ set_state(info, LASERDISC_SEARCHING_FRAME, PR8210_SEARCH_SPEED, info->parameter);
+ }
+ else
+ {
+ /* waiting for digits indicating position */
+ info->parameter = 0;
+ }
+ pr8210->seekstate ^=1 ;
+ break;
+
+ case 0x1c: CMDPRINTF(("pr8210: Scan reverse\n"));
+ /* scan reverse */
+ if (laserdisc_ready(info))
+ set_state(info, LASERDISC_SCANNING_REVERSE, -PR8210_SCAN_SPEED, NULL_TARGET_FRAME);
+ break;
+
+ case 0x1e: CMDPRINTF(("pr8210: Reject\n"));
+ /* eject the disc */
+ if (laserdisc_ready(info))
+ {
+ set_state(info, LASERDISC_EJECTING, STOP_SPEED, NULL_TARGET_FRAME);
+ set_hold_state(info, GENERIC_LOAD_TIME, LASERDISC_EJECTED, STOP_SPEED);
+ }
+ break;
+
+ default: CMDPRINTF(("pr8210: Unknown command %02X\n", cmd));
+ /* unknown command */
+ break;
+ }
+ }
+ }
+
+ /* reset our command data */
+ memset( pr8210->commandtriplet, 0, 3*sizeof(UINT16) );
+ pr8210->mode = PR8210_MODE_GET_1ST;
+ }
+}
+
+
+/*-------------------------------------------------
+ pr8210_command_w - write callback when the
+ CONTROL line is toggled
+-------------------------------------------------*/
+
+static void pr8210_control_w(laserdisc_info *info, UINT8 data)
+{
+ pr8210_info *pr8210 = &info->u.pr8210;
+
+ if ( data == ASSERT_LINE )
+ {
+ /* get the time difference from the last assert */
+ attotime delta = attotime_sub(timer_get_time(), pr8210->commandtime);
+
+ /* and update our internal command time */
+ pr8210->commandtime = timer_get_time();
+
+#if 0
+ {
+ int usecdiff = (int)(delta.attoseconds / ATTOSECONDS_IN_USEC(1));
+
+ printf( "bitdelta = %d\n", usecdiff );
+ }
+#endif
+
+ /* if the delay is less than 3 msec, we're receiving data */
+ if ( delta.attoseconds < ATTOTIME_IN_MSEC(3).attoseconds )
+ {
+ /* 0 bit delta is 1.05 msec, 1 bit delta is 2.11 msec */
+ int longpulse = ( delta.attoseconds < ATTOTIME_IN_USEC(1500).attoseconds ) ? 0 : 1;
+ pr8210->commandtriplet[pr8210->mode] <<= 1;
+ pr8210->commandtriplet[pr8210->mode] |= longpulse;
+
+ /* if we received 10 bits, see what we need to do */
+ if ( ++pr8210->commandbits >= 10 )
+ {
+ /* reset bit shift count */
+ pr8210->commandbits = 0;
+
+ /* mask just the 10 bits */
+ pr8210->commandtriplet[pr8210->mode] &= 0x3ff;
+
+ /* execute command */
+ pr8210_command( info );
+ }
+ }
+ }
+}
+
+
+
+/***************************************************************************
+ PIONEER LD-V1000 IMPLEMENTATION
+***************************************************************************/
+
+/*-------------------------------------------------
+ ldv1000_init - Pioneer LDV-1000-specific
+ initialization
+-------------------------------------------------*/
+
+static void ldv1000_init(laserdisc_info *info)
+{
+ /* set up the write callbacks */
+ info->writedata = ldv1000_data_w;
+
+ /* set up the read callbacks */
+ info->readdata = ldv1000_status_r;
+ info->readline[LASERDISC_LINE_STATUS] = ldv1000_status_strobe_r;
+ info->readline[LASERDISC_LINE_COMMAND] = ldv1000_command_strobe_r;
+
+ /* do a soft reset */
+ ldv1000_soft_reset(info);
+}
+
+
+/*-------------------------------------------------
+ ldv1000_soft_reset - Pioneer LDV-1000-specific
+ soft reset
+-------------------------------------------------*/
+
+static void ldv1000_soft_reset(laserdisc_info *info)
+{
+ ldv1000_info *ldv1000 = &info->u.ldv1000;
+
+ info->audio = AUDIO_CH1_ENABLE | AUDIO_CH2_ENABLE;
+ info->display = FALSE;
+ ldv1000->mode = LDV1000_MODE_STATUS;
+ ldv1000->activereg = 0;
+ ldv1000->statusmask = 0xff;
+ write_16bits_to_ram_be(ldv1000->ram, 0, 1);
+}
+
+
+/*-------------------------------------------------
+ ldv1000_data_w - write callback when the
+ ENTER state is written
+-------------------------------------------------*/
+
+static void ldv1000_data_w(laserdisc_info *info, UINT8 prev, UINT8 data)
+{
+ static const UINT8 numbers[10] = { 0x3f, 0x0f, 0x8f, 0x4f, 0x2f, 0xaf, 0x6f, 0x1f, 0x9f, 0x5f };
+ ldv1000_info *ldv1000 = &info->u.ldv1000;
+
+ /* 0xFF bytes are used for synchronization */
+ if (data == 0xff)
+ {
+ ldv1000->statusmask = 0xff; /* EHC 012207: reset our status mask */
+ return;
+ }
+
+ /* if we're not ready, return, otherwise signal ourselves busy */
+ if (ldv1000->statusmask == 0x7f)
+ return;
+ ldv1000->statusmask = 0x7f;
+
+ /* look for and process numbers */
+ if (process_number(info, data, numbers))
+ return;
+
+ /* handle commands */
+ switch (data)
+ {
+ case 0x7f: CMDPRINTF(("ldv1000: %d Recall\n", info->parameter));
+ /* set the active register */
+ ldv1000->activereg = info->parameter;
+ /* should also display the register value */
+ break;
+
+ case 0x20: CMDPRINTF(("ldv1000: x0 reverse (stop) - Badlands special\n"));
+ /* play reverse at 0 speed (stop) */
+ if (laserdisc_ready(info))
+ set_state(info, LASERDISC_PLAYING_SLOW_REVERSE, STOP_SPEED, NULL_TARGET_FRAME);
+ break;
+
+ case 0x21: CMDPRINTF(("ldv1000: x1/4 reverse - Badlands special\n"));
+ /* play reverse at 1/4 speed */
+ if (laserdisc_ready(info))
+ set_state(info, LASERDISC_PLAYING_SLOW_REVERSE, -PLAY_SPEED / 4, NULL_TARGET_FRAME);
+ break;
+
+ case 0x22: CMDPRINTF(("ldv1000: x1/2 reverse - Badlands special\n"));
+ /* play reverse at 1/2 speed */
+ if (laserdisc_ready(info))
+ set_state(info, LASERDISC_PLAYING_SLOW_REVERSE, -PLAY_SPEED / 2, NULL_TARGET_FRAME);
+ break;
+
+ case 0x23:
+ case 0x24:
+ case 0x25:
+ case 0x26:
+ case 0x27: CMDPRINTF(("ldv1000: x%d reverse - Badlands special\n", (data & 0x07) - 2));
+ /* play reverse at 1-5x speed */
+ if (laserdisc_ready(info))
+ set_state(info, LASERDISC_PLAYING_FAST_REVERSE, -PLAY_SPEED * ((data & 0x07) - 2), NULL_TARGET_FRAME);
+ break;
+
+ case 0xa0: CMDPRINTF(("ldv1000: x0 forward (stop)\n"));
+ /* play forward at 0 speed (stop) */
+ if (laserdisc_ready(info))
+ set_state(info, LASERDISC_PLAYING_SLOW_FORWARD, STOP_SPEED, NULL_TARGET_FRAME);
+ break;
+
+ case 0xa1: CMDPRINTF(("ldv1000: x1/4 forward\n"));
+ /* play forward at 1/4 speed */
+ if (laserdisc_ready(info))
+ set_state(info, LASERDISC_PLAYING_SLOW_FORWARD, PLAY_SPEED / 4, NULL_TARGET_FRAME);
+ break;
+
+ case 0xa2: CMDPRINTF(("ldv1000: x1/2 forward\n"));
+ /* play forward at 1/2 speed */
+ if (laserdisc_ready(info))
+ set_state(info, LASERDISC_PLAYING_SLOW_FORWARD, PLAY_SPEED / 2, NULL_TARGET_FRAME);
+ break;
+
+ case 0xa3:
+ case 0xa4:
+ case 0xa5:
+ case 0xa6:
+ case 0xa7: CMDPRINTF(("ldv1000: x%d forward\n", (data & 0x07) - 2));
+ /* play forward at 1-5x speed */
+ if (laserdisc_ready(info))
+ set_state(info, LASERDISC_PLAYING_FAST_FORWARD, PLAY_SPEED * ((data & 0x07) - 2), NULL_TARGET_FRAME);
+ break;
+
+ case 0xb1:
+ case 0xb2:
+ case 0xb3:
+ case 0xb4:
+ case 0xb5:
+ case 0xb6:
+ case 0xb7:
+ case 0xb8:
+ case 0xb9:
+ case 0xba: CMDPRINTF(("ldv1000: Skip forward %d0\n", data & 0x0f));
+ /* skip forward */
+ if (laserdisc_active(info))
+ {
+ /* note that this skips tracks, not frames; the track->frame count is not 1:1 */
+ /* in the case of 3:2 pulldown or other effects; for now, we just ignore the diff */
+ add_to_current_track(info, INT_TO_FRAC(10 * (data & 0x0f)));
+ }
+ break;
+
+ case 0x31:
+ case 0x32:
+ case 0x33:
+ case 0x34:
+ case 0x35:
+ case 0x36:
+ case 0x37:
+ case 0x38:
+ case 0x39:
+ case 0x3a: CMDPRINTF(("ldv1000: Skip backwards %d0 - Badlands special\n", data & 0x0f));
+ /* skip backward */
+ if (laserdisc_active(info))
+ {
+ /* note that this skips tracks, not frames; the track->frame count is not 1:1 */
+ /* in the case of 3:2 pulldown or other effects; for now, we just ignore the diff */
+ add_to_current_track(info, -INT_TO_FRAC(10 * (data & 0x0f)));
+ }
+ break;
+
+ case 0xbf: CMDPRINTF(("ldv1000: %d Clear\n", info->parameter));
+ /* clears register display and removes pending arguments */
+ break;
+
+ case 0xc2: CMDPRINTF(("ldv1000: Get frame no.\n"));
+ /* returns the current frame number */
+ ldv1000->mode = LDV1000_MODE_GET_FRAME;
+ ldv1000->readpos = 0;
+ ldv1000->readtotal = 5;
+ sprintf((char *)ldv1000->readbuf, "%05d", info->last_frame);
+ break;
+
+ case 0xc3: CMDPRINTF(("ldv1000: Get 2nd display\n"));
+ /* returns the data from the 2nd display line */
+ ldv1000->mode = LDV1000_MODE_GET_2ND;
+ ldv1000->readpos = 0;
+ ldv1000->readtotal = 8;
+ sprintf((char *)ldv1000->readbuf, " \x1c\x1c\x1c");
+ break;
+
+ case 0xc4: CMDPRINTF(("ldv1000: Get 1st display\n"));
+ /* returns the data from the 1st display line */
+ ldv1000->mode = LDV1000_MODE_GET_1ST;
+ ldv1000->readpos = 0;
+ ldv1000->readtotal = 8;
+ sprintf((char *)ldv1000->readbuf, " \x1c\x1c\x1c");
+ break;
+
+ case 0xc8: CMDPRINTF(("ldv1000: Transfer memory\n"));
+ /* returns the data from the 1st display line */
+ ldv1000->mode = LDV1000_MODE_GET_RAM;
+ ldv1000->readpos = 0;
+ ldv1000->readtotal = 1024;
+ break;
+
+ case 0xcc: CMDPRINTF(("ldv1000: Load\n"));
+ /* load program from disc -- not implemented */
+ break;
+
+ case 0xcd: CMDPRINTF(("ldv1000: Display disable\n"));
+ /* disables the display of current command -- not implemented */
+ break;
+
+ case 0xce: CMDPRINTF(("ldv1000: Display enable\n"));
+ /* enables the display of current command -- not implemented */
+ break;
+
+ case 0xf0: CMDPRINTF(("ldv1000: Scan forward\n"));
+ /* scan forward */
+ if (laserdisc_ready(info))
+ set_state(info, LASERDISC_SCANNING_FORWARD, LDV1000_SCAN_SPEED, NULL_TARGET_FRAME);
+ break;
+
+ case 0xf1: CMDPRINTF(("ldv1000: %d Display\n", info->parameter));
+ /* toggle or set the frame display */
+ info->display = (info->parameter == -1) ? !info->display : (info->parameter & 1);
+ break;
+
+ case 0xf3: CMDPRINTF(("ldv1000: %d Autostop\n", info->parameter));
+ /* play to a particular location and stop there */
+ if (laserdisc_ready(info))
+ {
+ INT32 targetframe = info->parameter;
+
+ if (targetframe == -1)
+ targetframe = read_16bits_from_ram_be(ldv1000->ram, (ldv1000->activereg++ * 2) % 1024);
+
+ if (targetframe > info->last_frame)
+ set_state(info, LASERDISC_PLAYING_FORWARD, PLAY_SPEED, targetframe);
+ else
+ set_state(info, LASERDISC_SEARCHING_FRAME, PR7820_SEARCH_SPEED, targetframe);
+ }
+ break;
+
+ case 0xf4: CMDPRINTF(("ldv1000: %d Audio track 1\n", info->parameter));
+ /* toggle or set the state of audio channel 1 */
+ if (info->parameter == -1)
+ info->audio ^= AUDIO_CH1_ENABLE;
+ else
+ info->audio = (info->audio & ~AUDIO_CH1_ENABLE) | ((info->parameter & 1) ? AUDIO_CH1_ENABLE : 0);
+ break;
+
+ case 0xf5: CMDPRINTF(("ldv1000: %d Store\n", info->parameter));
+ /* store either the current frame number or an explicit value into the active register */
+ if (info->parameter == -1)
+ write_16bits_to_ram_be(ldv1000->ram, (ldv1000->activereg * 2) % 1024, info->last_frame);
+ else
+ write_16bits_to_ram_be(ldv1000->ram, (ldv1000->activereg * 2) % 1024, info->parameter);
+ ldv1000->activereg++;
+ break;
+
+ case 0xf6: CMDPRINTF(("ldv1000: Step forward\n"));
+ /* step forward one frame */
+ if (laserdisc_ready(info))
+ {
+ set_state(info, LASERDISC_STEPPING_FORWARD, STOP_SPEED, NULL_TARGET_FRAME);
+ add_to_current_track(info, ONE_TRACK);
+ }
+ break;
+
+ case 0xf7: CMDPRINTF(("ldv1000: %d Search\n", info->parameter));
+ /* search to a particular frame number */
+ if (laserdisc_ready(info))
+ {
+ INT32 targetframe = info->parameter;
+
+ if (targetframe == -1)
+ targetframe = read_16bits_from_ram_be(ldv1000->ram, (ldv1000->activereg++ * 2) % 1024);
+ ldv1000->activereg++;
+
+ set_state(info, LASERDISC_SEARCHING_FRAME, LDV1000_SEARCH_SPEED, targetframe);
+ }
+ break;
+
+ case 0xf8: CMDPRINTF(("ldv1000: Scan reverse\n"));
+ /* scan reverse */
+ if (laserdisc_ready(info))
+ set_state(info, LASERDISC_SCANNING_REVERSE, -LDV1000_SCAN_SPEED, NULL_TARGET_FRAME);
+ break;
+
+ case 0xf9: CMDPRINTF(("ldv1000: Reject\n"));
+ /* move the head to parked position, and stop rotation */
+ set_state(info, LASERDISC_PARKED, STOP_SPEED, NULL_TARGET_FRAME);
+ break;
+
+ case 0xfb: CMDPRINTF(("ldv1000: %d Stop/Wait\n", info->parameter));
+ /* pause at the current location for a fixed amount of time (in 1/10ths of a second) */
+ if (laserdisc_ready(info))
+ {
+ laserdisc_state prevstate = info->state;
+ INT32 prevspeed = info->curfracspeed;
+ set_state(info, LASERDISC_STOPPED, STOP_SPEED, NULL_TARGET_FRAME);
+ if (info->parameter != -1)
+ set_hold_state(info, double_to_attotime(info->parameter * 0.1), prevstate, prevspeed);
+ }
+ break;
+
+ case 0xfc: CMDPRINTF(("ldv1000: %d Audio track 2\n", info->parameter));
+ /* toggle or set the state of audio channel 2 */
+ if (info->parameter == -1)
+ info->audio ^= AUDIO_CH2_ENABLE;
+ else
+ info->audio = (info->audio & ~AUDIO_CH2_ENABLE) | ((info->parameter & 1) ? AUDIO_CH2_ENABLE : 0);
+ break;
+
+ case 0xfd: CMDPRINTF(("ldv1000: Play\n"));
+ /* begin playing at regular speed, or load the disc if it is parked */
+ if (laserdisc_ready(info))
+ set_state(info, LASERDISC_PLAYING_FORWARD, PLAY_SPEED, NULL_TARGET_FRAME);
+ else
+ {
+ if (info->state == LASERDISC_PARKED)
+ {
+ set_state(info, LASERDISC_SPINUP, STOP_SPEED, NULL_TARGET_FRAME);
+ set_hold_state(info, GENERIC_SPINUP_TIME, LASERDISC_PLAYING_FORWARD, PLAY_SPEED);
+ }
+ else
+ {
+ set_state(info, LASERDISC_LOADING, STOP_SPEED, NULL_TARGET_FRAME);
+ set_hold_state(info, GENERIC_LOAD_TIME, LASERDISC_PLAYING_FORWARD, PLAY_SPEED);
+ }
+
+ info->curfractrack = ONE_TRACK;
+ }
+ break;
+
+ case 0xfe: CMDPRINTF(("ldv1000: Step reverse\n"));
+ /* step backwards one frame */
+ if (laserdisc_ready(info))
+ {
+ set_state(info, LASERDISC_STEPPING_REVERSE, STOP_SPEED, NULL_TARGET_FRAME);
+ add_to_current_track(info, -ONE_TRACK);
+ }
+ break;
+
+ default: CMDPRINTF(("ldv1000: %d Unknown command %02X\n", info->parameter, data));
+ /* unknown command */
+ break;
+ }
+
+ /* reset the parameter after executing a command */
+ info->parameter = -1;
+}
+
+
+/*-------------------------------------------------
+ ldv1000_status_strobe_r - return state of the
+ status strobe
+-------------------------------------------------*/
+
+static UINT8 ldv1000_status_strobe_r(laserdisc_info *info)
+{
+ /* the status strobe is asserted (active low) 500-650usec after VSYNC */
+ /* for a duration of 26usec; we pick 600-626usec */
+ attotime delta = attotime_sub(timer_get_time(), info->lastvsynctime);
+ if (delta.attoseconds >= ATTOTIME_IN_USEC(600).attoseconds &&
+ delta.attoseconds < ATTOTIME_IN_USEC(626).attoseconds)
+ return ASSERT_LINE;
+
+ return CLEAR_LINE;
+}
+
+
+/*-------------------------------------------------
+ ldv1000_command_strobe_r - return state of the
+ command strobe
+-------------------------------------------------*/
+
+static UINT8 ldv1000_command_strobe_r(laserdisc_info *info)
+{
+ /* the command strobe is asserted (active low) 54 or 84usec after the status */
+ /* strobe for a duration of 25usec; we pick 600+84 = 684-709usec */
+ /* for a duration of 26usec; we pick 600-626usec */
+ attotime delta = attotime_sub(timer_get_time(), info->lastvsynctime);
+ if (delta.attoseconds >= ATTOTIME_IN_USEC(684).attoseconds &&
+ delta.attoseconds < ATTOTIME_IN_USEC(709).attoseconds)
+ return ASSERT_LINE;
+
+ return CLEAR_LINE;
+}
+
+
+/*-------------------------------------------------
+ ldv1000_status_r - return state of the ready
+ line
+-------------------------------------------------*/
+
+static UINT8 ldv1000_status_r(laserdisc_info *info)
+{
+ ldv1000_info *ldv1000 = &info->u.ldv1000;
+ UINT8 status = 0xff;
+
+ /* switch off the current mode */
+ switch (ldv1000->mode)
+ {
+ /* reading frame number returns 5 characters */
+ /* reading display lines returns 8 characters */
+ case LDV1000_MODE_GET_FRAME:
+ case LDV1000_MODE_GET_1ST:
+ case LDV1000_MODE_GET_2ND:
+ assert(ldv1000->readpos < ldv1000->readtotal);
+ status = ldv1000->readbuf[ldv1000->readpos++];
+ if (ldv1000->readpos == ldv1000->readtotal)
+ ldv1000->mode = LDV1000_MODE_STATUS;
+ break;
+
+ /* reading RAM returns 1024 bytes */
+ case LDV1000_MODE_GET_RAM:
+ assert(ldv1000->readpos < ldv1000->readtotal);
+ status = ldv1000->ram[1023 - ldv1000->readpos++];
+ if (ldv1000->readpos == ldv1000->readtotal)
+ ldv1000->mode = LDV1000_MODE_STATUS;
+ break;
+
+ /* otherwise, we just compute a status code */
+ default:
+ case LDV1000_MODE_STATUS:
+ switch (info->state)
+ {
+ case LASERDISC_EJECTED: status = 0xe0; break;
+ case LASERDISC_EJECTING: status = 0x60; break;
+ case LASERDISC_LOADED: status = 0xc8; break;
+ case LASERDISC_SPINUP:
+ case LASERDISC_LOADING: status = 0x48; break;
+ case LASERDISC_PARKED: status = 0xfc; break;
+
+ case LASERDISC_SEARCHING_FRAME: status = 0x50; break;
+ case LASERDISC_SEARCH_FINISHED: status = 0xd0; break;
+
+ case LASERDISC_STOPPED: status = 0xe5; break;
+ case LASERDISC_AUTOSTOPPED: status = 0x54; break;
+
+ case LASERDISC_PLAYING_FORWARD: status = 0xe4; break;
+ case LASERDISC_PLAYING_REVERSE: status = 0xe4; break;
+ case LASERDISC_PLAYING_SLOW_FORWARD: status = 0xae; break;
+ case LASERDISC_PLAYING_SLOW_REVERSE: status = 0xae; break;
+ case LASERDISC_PLAYING_FAST_FORWARD: status = 0xae; break;
+ case LASERDISC_PLAYING_FAST_REVERSE: status = 0xae; break;
+ case LASERDISC_STEPPING_FORWARD: status = 0xe5; break;
+ case LASERDISC_STEPPING_REVERSE: status = 0xe5; break;
+ case LASERDISC_SCANNING_FORWARD: status = 0x4c; break;
+ case LASERDISC_SCANNING_REVERSE: status = 0x4c; break;
+ default:
+ fatalerror("Unexpected disc state in ldv1000_status_r\n");
+ break;
+ }
+ break;
+ }
+
+ /* bit 7 indicates our busy status */
+ return status & ldv1000->statusmask;
+}
+
+
+
+/***************************************************************************
+ SONY LDP-1450 IMPLEMENTATION
+***************************************************************************/
+
+/*-------------------------------------------------
+ ldp1450_init - Sony LDP-1450-specific
+ initialization
+-------------------------------------------------*/
+
+static void ldp1450_init(laserdisc_info *info)
+{
+ /* set up the write callbacks */
+ info->writedata = ldp1450_data_w;
+
+ /* set up the read callbacks */
+ info->readdata = ldp1450_data_r;
+ info->readline[LASERDISC_LINE_DATA_AVAIL] = ldp1450_data_avail_r;
+
+ /* use a state changed callback */
+ info->statechanged = ldp1450_state_changed;
+
+ /* do a soft reset */
+ ldp1450_soft_reset(info);
+}
+
+
+/*-------------------------------------------------
+ ldp1450_soft_reset - Sony LDP-1450-specific
+ soft reset
+-------------------------------------------------*/
+
+static void ldp1450_soft_reset(laserdisc_info *info)
+{
+ info->audio = AUDIO_CH1_ENABLE | AUDIO_CH2_ENABLE;
+ info->display = FALSE;
+ set_state(info, LASERDISC_STOPPED, STOP_SPEED, NULL_TARGET_FRAME);
+ info->curfractrack = ONE_TRACK;
+}
+
+
+/*-------------------------------------------------
+ ldp1450_compute_status - compute the current
+ status bytes on the LDP-1450
+-------------------------------------------------*/
+
+static void ldp1450_compute_status(laserdisc_info *info)
+{
+ /*
+ Byte 0: LDP ready status:
+ 0x80 Ready
+ 0x82 Disc Spinning down
+ 0x86 Tray closed & no disc loaded
+ 0x8A Disc ejecting, tray opening
+ 0x92 Disc loading, tray closing
+ 0x90 Disc spinning up
+ 0xC0 Busy Searching
+
+ Byte 1: Error status:
+ 0x00 No error
+ 0x01 Focus unlock
+ 0x02 Communication Error
+
+ Byte 2: Disc/motor status:
+ 0x01 Motor Off (disc spinning down or tray opening/ed)
+ 0x02 Tray closed & motor Off (no disc loaded)
+ 0x11 Motor On
+
+ Byte 3: Command/argument status:
+ 0x00 Normal
+ 0x01 Relative search or Mark Set pending (Enter not sent)
+ 0x03 Search pending (Enter not sent)
+ 0x05 Repeat pending (Enter not sent) or Memory Search in progress
+ 0x80 FWD or REV Step
+
+ Byte 4: Playback mode:
+ 0x00 Searching, Motor On (spinning up), motor Off (spinning down), tray opened/ing (no motor phase lock)
+ 0x01 FWD Play
+ 0x02 FWD Fast
+ 0x04 FWD Slow
+ 0x08 FWD Step
+ 0x10 FWD Scan
+ 0x20 Still
+ 0x81 REV Play
+ 0x82 REV Fast
+ 0x84 REV Slow
+ 0x88 REV Step
+ 0x90 REV Scan
+ */
+
+ ldp1450_info *ldp1450 = &info->u.ldp1450;
+ UINT32 statusbytes = 0;
+
+ switch (info->state)
+ {
+ case LASERDISC_EJECTED: statusbytes = 0x86000200; break;
+ case LASERDISC_EJECTING: statusbytes = 0x8a000100; break;
+ case LASERDISC_LOADED: statusbytes = 0x80000100; break;
+ case LASERDISC_SPINUP:
+ case LASERDISC_LOADING: statusbytes = 0x92001100; break;
+ case LASERDISC_PARKED: statusbytes = 0x80000100; break;
+ case LASERDISC_SEARCHING_FRAME: statusbytes = 0xc0001100; break;
+
+ case LASERDISC_SEARCH_FINISHED:
+ case LASERDISC_STOPPED:
+ case LASERDISC_AUTOSTOPPED: statusbytes = 0x80001120; break;
+
+ case LASERDISC_PLAYING_FORWARD: statusbytes = 0x80001101; break;
+ case LASERDISC_PLAYING_REVERSE: statusbytes = 0x80001181; break;
+ case LASERDISC_PLAYING_SLOW_FORWARD: statusbytes = 0x80001104; break;
+ case LASERDISC_PLAYING_SLOW_REVERSE: statusbytes = 0x80001184; break;
+ case LASERDISC_PLAYING_FAST_FORWARD: statusbytes = 0x80001102; break;
+ case LASERDISC_PLAYING_FAST_REVERSE: statusbytes = 0x80001182; break;
+ case LASERDISC_STEPPING_FORWARD: statusbytes = 0x80001108; break;
+ case LASERDISC_STEPPING_REVERSE: statusbytes = 0x80001188; break;
+ case LASERDISC_SCANNING_FORWARD: statusbytes = 0x80001110; break;
+ case LASERDISC_SCANNING_REVERSE: statusbytes = 0x80001190; break;
+
+ default:
+ fatalerror("Unexpected disc state in ldp1450_compute_status\n");
+ break;
+ }
+
+ /* copy in the result bytes */
+ ldp1450->readtotal = 0;
+ ldp1450->readbuf[ldp1450->readtotal++] = (statusbytes >> 24) & 0xff;
+ ldp1450->readbuf[ldp1450->readtotal++] = (statusbytes >> 16) & 0xff;
+ ldp1450->readbuf[ldp1450->readtotal++] = (statusbytes >> 8) & 0xff;
+ switch (info->command)
+ {
+ case 0x43: ldp1450->readbuf[ldp1450->readtotal++] = 0x03; break;
+ default: ldp1450->readbuf[ldp1450->readtotal++] = 0x00; break;
+ }
+ ldp1450->readbuf[ldp1450->readtotal++] = (statusbytes >> 0) & 0xff;
+}
+
+
+/*-------------------------------------------------
+ ldp1450_data_w - write callback when the
+ ENTER state is written
+-------------------------------------------------*/
+
+static void ldp1450_data_w(laserdisc_info *info, UINT8 prev, UINT8 data)
+{
+ static const UINT8 numbers[10] = { 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39 };
+ ldp1450_info *ldp1450 = &info->u.ldp1450;
+
+ /* by default, we return an ack on each command */
+ ldp1450->readpos = ldp1450->readtotal = 0;
+ ldp1450->readbuf[ldp1450->readtotal++] = 0x0a;
+
+ /* look for and process numbers */
+ if (process_number(info, data, numbers))
+ {
+ CMDPRINTF(("%c", data));
+ return;
+ }
+
+ /* handle commands */
+ switch (data)
+ {
+ case 0x24: CMDPRINTF(("ldp1450: Audio Mute On\n"));
+ /* mute audio */
+ info->audio |= AUDIO_EXPLICIT_MUTE;
+ break;
+
+ case 0x25: CMDPRINTF(("ldp1450: Audio Mute Off\n"));
+ /* unmute audio */
+ info->audio &= ~AUDIO_EXPLICIT_MUTE;
+ break;
+
+ case 0x26: CMDPRINTF(("ldp1450: Video Mute Off\n"));
+ /* mute video -- not implemented */
+ break;
+
+ case 0x27: CMDPRINTF(("ldp1450: Video Mute On\n"));
+ /* unmute video -- not implemented */
+ break;
+
+ case 0x28: CMDPRINTF(("ldp1450: PSC Enable\n"));
+ /* enable Picture Stop Codes (PSC) -- not implemented */
+ break;
+
+ case 0x29: CMDPRINTF(("ldp1450: PSC Disable\n"));
+ /* disable Picture Stop Codes (PSC) -- not implemented */
+ break;
+
+ case 0x2a: CMDPRINTF(("ldp1450: %d Eject\n", info->parameter));
+ /* eject the disc */
+ if (laserdisc_ready(info))
+ {
+ set_state(info, LASERDISC_EJECTING, STOP_SPEED, NULL_TARGET_FRAME);
+ set_hold_state(info, GENERIC_LOAD_TIME, LASERDISC_EJECTED, STOP_SPEED);
+ }
+ break;
+
+ case 0x2b: CMDPRINTF(("ldp1450: %d Step forward\n", info->parameter));
+ /* step forward one frame */
+ if (laserdisc_ready(info))
+ {
+ set_state(info, LASERDISC_STEPPING_FORWARD, STOP_SPEED, NULL_TARGET_FRAME);
+ add_to_current_track(info, ONE_TRACK);
+ }
+ break;
+
+ case 0x2c: CMDPRINTF(("ldp1450: %d Step reverse\n", info->parameter));
+ /* step backwards one frame */
+ if (laserdisc_ready(info))
+ {
+ set_state(info, LASERDISC_STEPPING_REVERSE, STOP_SPEED, NULL_TARGET_FRAME);
+ add_to_current_track(info, -ONE_TRACK);
+ }
+ break;
+
+ case 0x2d: CMDPRINTF(("ldp1450: %d Search multiple tracks forward\n", info->parameter));
+ /* enable Picture Stop Codes (PSC) -- not implemented */
+ break;
+
+ case 0x2e: CMDPRINTF(("ldp1450: %d Search multiple tracks reverse\n", info->parameter));
+ /* disable Picture Stop Codes (PSC) -- not implemented */
+ break;
+
+ case 0x3a: CMDPRINTF(("ldp1450: %d Play\n", info->parameter));
+ /* begin playing at regular speed, or load the disc if it is parked */
+ if (laserdisc_ready(info))
+ set_state(info, LASERDISC_PLAYING_FORWARD, PLAY_SPEED, NULL_TARGET_FRAME);
+ else
+ {
+ if (info->state == LASERDISC_PARKED)
+ {
+ set_state(info, LASERDISC_SPINUP, STOP_SPEED, NULL_TARGET_FRAME);
+ set_hold_state(info, GENERIC_SPINUP_TIME, LASERDISC_PLAYING_FORWARD, PLAY_SPEED);
+ }
+ else
+ {
+ set_state(info, LASERDISC_LOADING, STOP_SPEED, NULL_TARGET_FRAME);
+ set_hold_state(info, GENERIC_LOAD_TIME, LASERDISC_PLAYING_FORWARD, PLAY_SPEED);
+ }
+
+ info->curfractrack = ONE_TRACK;
+ }
+ break;
+
+ case 0x3b: CMDPRINTF(("ldp1450: %d Fast forward play\n", info->parameter));
+ /* play forward fast speed */
+ if (laserdisc_ready(info))
+ set_state(info, LASERDISC_PLAYING_FAST_FORWARD, LDP1450_FAST_SPEED, NULL_TARGET_FRAME);
+ break;
+
+ case 0x3c: CMDPRINTF(("ldp1450: %d Slow forward play\n", info->parameter));
+ /* play forward slow speed */
+ if (laserdisc_ready(info))
+ set_state(info, LASERDISC_PLAYING_SLOW_FORWARD, LDP1450_SLOW_SPEED, NULL_TARGET_FRAME);
+ break;
+
+ case 0x3d: CMDPRINTF(("ldp1450: %d Variable forward play\n", info->parameter));
+ /* play forward variable speed */
+ if (laserdisc_ready(info))
+ {
+ set_state(info, LASERDISC_STEPPING_FORWARD, LDP1450_STEP_SPEED, NULL_TARGET_FRAME);
+ info->command = 0x3d;
+ }
+ break;
+
+ case 0x3e: CMDPRINTF(("ldp1450: %d Scan forward\n", info->parameter));
+ /* scan forward */
+ if (laserdisc_ready(info))
+ set_state(info, LASERDISC_SCANNING_FORWARD, LDP1450_SCAN_SPEED, NULL_TARGET_FRAME);
+ break;
+
+ case 0x3f: CMDPRINTF(("ldp1450: Stop\n"));
+ /* pause at the current location */
+ if (laserdisc_ready(info))
+ set_state(info, LASERDISC_STOPPED, STOP_SPEED, NULL_TARGET_FRAME);
+ break;
+
+ case 0x40: CMDPRINTF((" ... Enter\n"));
+ /* Enter -- execute command with parameter */
+ switch (info->command)
+ {
+ case 0x3d: /* forward variable speed */
+ if (info->parameter != 0)
+ info->curfracspeed = PLAY_SPEED / info->parameter;
+ break;
+
+ case 0x43: /* search */
+ set_state(info, LASERDISC_SEARCHING_FRAME, LDP1450_SEARCH_SPEED, info->parameter);
+ break;
+
+ case 0x4d: /* reverse variable speed */
+ if (info->parameter != 0)
+ info->curfracspeed = -PLAY_SPEED / info->parameter;
+ break;
+
+ default: CMDPRINTF(("Unknown command: %02X\n", info->command));
+ break;
+ }
+ break;
+
+ case 0x41: CMDPRINTF(("ldp1450: %d Clear entry\n", info->parameter));
+ /* Clear entry */
+ break;
+
+ case 0x42: CMDPRINTF(("ldp1450: %d Menu\n", info->parameter));
+ /* Menu -- not implemented */
+ break;
+
+ case 0x43: CMDPRINTF(("ldp1450: Search ... "));
+ /* search to a particular frame number */
+ if (laserdisc_ready(info))
+ {
+ info->command = data;
+
+ /* Note that the disc stops as soon as the search command is issued */
+ set_state(info, LASERDISC_STOPPED, STOP_SPEED, NULL_TARGET_FRAME);
+ }
+ break;
+
+ case 0x44: CMDPRINTF(("ldp1450: %d Repeat\n", info->parameter));
+ /* Repeat -- not implemented */
+ break;
+
+ case 0x46: CMDPRINTF(("ldp1450: Ch1 On\n"));
+ /* channel 1 audio on */
+ info->audio |= AUDIO_CH1_ENABLE;
+ break;
+
+ case 0x47: CMDPRINTF(("ldp1450: Ch1 Off\n"));
+ /* channel 1 audio off */
+ info->audio &= ~AUDIO_CH1_ENABLE;
+ break;
+
+ case 0x48: CMDPRINTF(("ldp1450: Ch2 On\n"));
+ /* channel 1 audio on */
+ info->audio |= AUDIO_CH2_ENABLE;
+ break;
+
+ case 0x49: CMDPRINTF(("ldp1450: Ch2 Off\n"));
+ /* channel 1 audio off */
+ info->audio &= ~AUDIO_CH2_ENABLE;
+ break;
+
+ case 0x4a: CMDPRINTF(("ldp1450: %d Reverse Play\n", info->parameter));
+ /* begin playing at regular speed, or load the disc if it is parked */
+ if (laserdisc_ready(info))
+ set_state(info, LASERDISC_PLAYING_REVERSE, -PLAY_SPEED, NULL_TARGET_FRAME);
+ break;
+
+ case 0x4b: CMDPRINTF(("ldp1450: %d Fast reverse play\n", info->parameter));
+ /* play reverse fast speed */
+ if (laserdisc_ready(info))
+ set_state(info, LASERDISC_PLAYING_FAST_REVERSE, -LDP1450_FAST_SPEED, NULL_TARGET_FRAME);
+ break;
+
+ case 0x4c: CMDPRINTF(("ldp1450: %d Slow reverse play\n", info->parameter));
+ /* play reverse slow speed */
+ if (laserdisc_ready(info))
+ set_state(info, LASERDISC_PLAYING_SLOW_REVERSE, -LDP1450_SLOW_SPEED, NULL_TARGET_FRAME);
+ break;
+
+ case 0x4d: CMDPRINTF(("ldp1450: %d Variable reverse play\n", info->parameter));
+ /* play reverse variable speed */
+ if (laserdisc_ready(info))
+ {
+ set_state(info, LASERDISC_STEPPING_REVERSE, -LDP1450_STEP_SPEED, NULL_TARGET_FRAME);
+ info->command = 0x4d;
+ }
+ break;
+
+ case 0x4e: CMDPRINTF(("ldp1450: %d Scan reverse\n", info->parameter));
+ /* play forward variable speed */
+ if (laserdisc_ready(info))
+ set_state(info, LASERDISC_SCANNING_REVERSE, -LDP1450_SCAN_SPEED, NULL_TARGET_FRAME);
+ break;
+
+ case 0x4f: CMDPRINTF(("ldp1450: %d Still\n", info->parameter));
+ /* still picture */
+ if (laserdisc_ready(info))
+ set_state(info, LASERDISC_STOPPED, STOP_SPEED, NULL_TARGET_FRAME);
+ break;
+
+ case 0x50: CMDPRINTF(("ldp1450: Index On\n"));
+ /* index on -- not implemented */
+ break;
+
+ case 0x51: CMDPRINTF(("ldp1450: Index Off\n"));
+ /* index off -- not implemented */
+ break;
+
+ case 0x55: CMDPRINTF(("ldp1450: %d Set to frame number mode\n", info->parameter));
+ /* set to frame number mode -- not implemented */
+ break;
+
+ case 0x56: CMDPRINTF(("ldp1450: %d Clear all\n", info->parameter));
+ /* clear all */
+ ldp1450_soft_reset(info);
+ break;
+
+ case 0x5a: CMDPRINTF(("ldp1450: %d Memory\n", info->parameter));
+ /* memorize current position -- not implemented */
+ break;
+
+ case 0x5b: CMDPRINTF(("ldp1450: %d Memory Search\n", info->parameter));
+ /* locate memorized position -- not implemented */
+ break;
+
+ case 0x60: CMDPRINTF(("ldp1450: %d Address Inquire\n", info->parameter));
+ /* inquire for current address */
+ ldp1450->readtotal = 0;
+ ldp1450->readbuf[ldp1450->readtotal++] = '0' + (info->last_frame / 10000) % 10;
+ ldp1450->readbuf[ldp1450->readtotal++] = '0' + (info->last_frame / 1000) % 10;
+ ldp1450->readbuf[ldp1450->readtotal++] = '0' + (info->last_frame / 100) % 10;
+ ldp1450->readbuf[ldp1450->readtotal++] = '0' + (info->last_frame / 10) % 10;
+ ldp1450->readbuf[ldp1450->readtotal++] = '0' + (info->last_frame / 1) % 10;
+ break;
+
+ case 0x61: CMDPRINTF(("ldp1450: %d Continue\n", info->parameter));
+ /* resume the mode prior to still -- not implemented */
+ break;
+
+ case 0x62: CMDPRINTF(("ldp1450: Motor On\n"));
+ /* motor on -- not implemented */
+ break;
+
+ case 0x63: CMDPRINTF(("ldp1450: Motor Off\n"));
+ /* motor off -- not implemented */
+ break;
+
+ case 0x67: CMDPRINTF(("ldp1450: %d Status Inquire\n", info->parameter));
+ /* inquire status of player */
+ ldp1450_compute_status(info);
+ break;
+
+ case 0x69: CMDPRINTF(("ldp1450: %d Chapter Mode\n", info->parameter));
+ /* set to chapter number mode -- not implemented */
+ break;
+
+ case 0x6e: CMDPRINTF(("ldp1450: CX On\n"));
+ /* CX noise reduction on -- not implemented */
+ break;
+
+ case 0x6f: CMDPRINTF(("ldp1450: CX Off\n"));
+ /* CX noise reduction off -- not implemented */
+ break;
+
+ case 0x71: CMDPRINTF(("ldp1450: %d Non-CF Play\n", info->parameter));
+ /* disengage color framing -- not implemented */
+ break;
+
+ case 0x72: CMDPRINTF(("ldp1450: %d ROM Version\n", info->parameter));
+ /* inquire ROM version -- not implemented */
+ break;
+
+ case 0x73: CMDPRINTF(("ldp1450: %d Mark Set\n", info->parameter));
+ /* set mark position -- not implemented */
+ break;
+
+ case 0x74: CMDPRINTF(("ldp1450: Eject Enable On\n"));
+ /* activate eject function -- not implemented */
+ break;
+
+ case 0x75: CMDPRINTF(("ldp1450: Eject Disable Off\n"));
+ /* deactivate eject function -- not implemented */
+ break;
+
+ case 0x76: CMDPRINTF(("ldp1450: %d Chapter Inquire\n", info->parameter));
+ /* inquire current chapter -- not implemented */
+ break;
+
+ case 0x79: CMDPRINTF(("ldp1450: %d User Code Inquire\n", info->parameter));
+ /* inquire user's code -- not implemented */
+ break;
+
+ case 0x80: CMDPRINTF(("ldp1450: %d User Index Control\n", info->parameter));
+ /* set user defined index -- not implemented */
+ break;
+
+ case 0x81: CMDPRINTF(("ldp1450: Activate user defined index\n"));
+ /* activate eject function -- not implemented */
+ break;
+
+ case 0x82: CMDPRINTF(("ldp1450: Deactivate user defined index\n"));
+ /* deactivate eject function -- not implemented */
+ break;
+
+ default: CMDPRINTF(("ldp1450: %d Unknown command %02X\n", info->parameter, data));
+ /* unknown command -- respond with a NAK */
+ ldp1450->readtotal = 0;
+ ldp1450->readbuf[ldp1450->readtotal++] = 0x0b;
+ break;
+ }
+
+ /* reset the parameter after executing a command */
+ info->parameter = -1;
+}
+
+
+/*-------------------------------------------------
+ ldp1450_data_avail_r - return ASSERT_LINE if
+ serial data is available
+-------------------------------------------------*/
+
+static UINT8 ldp1450_data_avail_r(laserdisc_info *info)
+{
+ ldp1450_info *ldp1450 = &info->u.ldp1450;
+ return (ldp1450->readpos < ldp1450->readtotal) ? ASSERT_LINE : CLEAR_LINE;
+}
+
+
+/*-------------------------------------------------
+ ldp1450_data_r - return data from the player
+-------------------------------------------------*/
+
+static UINT8 ldp1450_data_r(laserdisc_info *info)
+{
+ ldp1450_info *ldp1450 = &info->u.ldp1450;
+
+ if (ldp1450->readpos < ldp1450->readtotal)
+ return ldp1450->readbuf[ldp1450->readpos++];
+ return 0xff;
+}
+
+
+/*-------------------------------------------------
+ ldp1450_state_changed - Sony LDP-1450-specific
+ state changed callback
+-------------------------------------------------*/
+
+static void ldp1450_state_changed(laserdisc_info *info, UINT8 oldstate)
+{
+ ldp1450_info *ldp1450 = &info->u.ldp1450;
+
+ /* look for searching -> search finished state */
+ if (info->state == LASERDISC_SEARCH_FINISHED && oldstate == LASERDISC_SEARCHING_FRAME)
+ {
+ ldp1450->readpos = ldp1450->readtotal = 0;
+ ldp1450->readbuf[ldp1450->readtotal++] = 0x01;
+ }
+}
+
+
+
+/***************************************************************************
+ PHILPS 22VP932 IMPLEMENTATION
+***************************************************************************/
+
+/*-------------------------------------------------
+ vp932_init - Philips 22VP932-specific
+ initialization
+-------------------------------------------------*/
+
+static void vp932_init(laserdisc_info *info)
+{
+ /* set up the write callbacks */
+ info->writedata = vp932_data_w;
+
+ /* set up the read callbacks */
+ info->readdata = vp932_data_r;
+ info->readline[LASERDISC_LINE_DATA_AVAIL] = vp932_data_avail_r;
+
+ /* use a state changed callback */
+ info->statechanged = vp932_state_changed;
+
+ /* do a soft reset */
+ vp932_soft_reset(info);
+}
+
+
+/*-------------------------------------------------
+ vp932_soft_reset - Philips 22VP932-specific
+ soft reset
+-------------------------------------------------*/
+
+static void vp932_soft_reset(laserdisc_info *info)
+{
+ vp932_info *vp932 = &info->u.vp932;
+
+ info->audio = AUDIO_CH1_ENABLE | AUDIO_CH2_ENABLE;
+ info->display = 0;
+
+ /* reset the pending count */
+ vp932->incount = 0;
+ vp932->outcount = 0;
+}
+
+
+/*-------------------------------------------------
+ vp932_data_w - write callback when data is
+ written
+-------------------------------------------------*/
+
+static void vp932_data_w(laserdisc_info *info, UINT8 prev, UINT8 data)
+{
+ vp932_info *vp932 = &info->u.vp932;
+
+ /* ignore 0's */
+ if (data == 0x00)
+ return;
+
+ /* if this isn't a CR, just accumulate it */
+ if (data != 0x0d)
+ {
+ if (vp932->incount < ARRAY_LENGTH(vp932->inbuffer))
+ vp932->inbuffer[vp932->incount++] = data;
+ }
+
+ /* otherwise, execute the command */
+ else
+ {
+ vp932->inbuffer[vp932->incount] = 0;
+ CMDPRINTF(("22vp932: %s\n", vp932->inbuffer));
+
+ switch (vp932->inbuffer[vp932->incount - 1])
+ {
+ case 'R': /* seek */
+ if (laserdisc_ready(info))
+ {
+ INT32 targetframe = 0, i;
+
+ for (i = 0; i < vp932->incount - 1; i++)
+ if (vp932->inbuffer[i] >= '0' && vp932->inbuffer[i] <= '9')
+ targetframe = (targetframe * 10) + (vp932->inbuffer[i] - '0');
+ set_state(info, LASERDISC_SEARCHING_FRAME, VP932_SEARCH_SPEED, targetframe);
+
+ vp932->outcount = 0;
+ vp932->outbuffer[vp932->outcount++] = 'A';
+ vp932->outbuffer[vp932->outcount++] = '0';
+ vp932->outbuffer[vp932->outcount++] = 0x0d;
+ }
+ break;
+ }
+
+ /* reset the command buffer */
+ vp932->incount = 0;
+ }
+}
+
+
+/*-------------------------------------------------
+ vp932_data_r - read callback when data is
+ returned
+-------------------------------------------------*/
+
+static UINT8 vp932_data_r(laserdisc_info *info)
+{
+ vp932_info *vp932 = &info->u.vp932;
+ UINT8 result = 0;
+
+ /* grab data if we can */
+ if (vp932->outcount > 0)
+ {
+ result = vp932->outbuffer[0];
+ if (--vp932->outcount > 0)
+ memmove(&vp932->outbuffer[0], &vp932->outbuffer[1], vp932->outcount);
+ }
+
+ return result;
+}
+
+
+/*-------------------------------------------------
+ vp932_data_avail_r - return ASSERT_LINE if
+ serial data is available
+-------------------------------------------------*/
+
+static UINT8 vp932_data_avail_r(laserdisc_info *info)
+{
+ vp932_info *vp932 = &info->u.vp932;
+ return (vp932->outcount > 0) ? ASSERT_LINE : CLEAR_LINE;
+}
+
+
+/*-------------------------------------------------
+ vp932_state_changed - Sony LDP-1450-specific
+ state changed callback
+-------------------------------------------------*/
+
+static void vp932_state_changed(laserdisc_info *info, UINT8 oldstate)
+{
+ vp932_info *vp932 = &info->u.vp932;
+
+ /* look for searching -> search finished state */
+ if (info->state == LASERDISC_SEARCH_FINISHED && oldstate == LASERDISC_SEARCHING_FRAME)
+ {
+ vp932->outcount = 0;
+ vp932->outbuffer[vp932->outcount++] = 'A';
+ vp932->outbuffer[vp932->outcount++] = '0';
+ vp932->outbuffer[vp932->outcount++] = 0x0d;
+ }
+}