1
/*************************************************************************
5
Private core laserdisc player implementation.
7
Copyright Nicola Salmoria and the MAME Team.
8
Visit http://mamedev.org for licensing and usage restrictions.
10
*************************************************************************/
21
/***************************************************************************
23
***************************************************************************/
29
/***************************************************************************
31
***************************************************************************/
33
/* these specs code from IEC 60857, for NTSC players */
34
#define LEAD_IN_MIN_RADIUS_IN_UM 53500 /* 53.5 mm */
35
#define PROGRAM_MIN_RADIUS_IN_UM 55000 /* 55 mm */
36
#define PROGRAM_MAX_RADIUS_IN_UM 145000 /* 145 mm */
37
#define LEAD_OUT_MIN_SIZE_IN_UM 2000 /* 2 mm */
39
/* the track pitch is defined as a range; we pick a nominal pitch
40
that ensures we can fit 54,000 tracks */
41
#define MIN_TRACK_PITCH_IN_NM 1400 /* 1.4 um */
42
#define MAX_TRACK_PITCH_IN_NM 2000 /* 2 um */
43
#define NOMINAL_TRACK_PITCH_IN_NM ((PROGRAM_MAX_RADIUS_IN_UM - PROGRAM_MIN_RADIUS_IN_UM) * 1000 / 54000)
45
/* we simulate extra lead-in and lead-out tracks */
46
#define VIRTUAL_LEAD_IN_TRACKS ((PROGRAM_MIN_RADIUS_IN_UM - LEAD_IN_MIN_RADIUS_IN_UM) * 1000 / NOMINAL_TRACK_PITCH_IN_NM)
47
#define MAX_TOTAL_TRACKS 54000
48
#define VIRTUAL_LEAD_OUT_TRACKS (LEAD_OUT_MIN_SIZE_IN_UM * 1000 / NOMINAL_TRACK_PITCH_IN_NM)
52
/***************************************************************************
54
***************************************************************************/
56
/* video frame data */
57
typedef struct _frame_data frame_data;
60
bitmap_t * bitmap; /* cached bitmap */
61
bitmap_t * visbitmap; /* wrapper around bitmap with only visible lines */
62
UINT8 numfields; /* number of fields in this frame */
63
INT32 lastfield; /* last absolute field number */
67
/* core-specific data */
71
laserdisc_config config; /* copy of the inline config */
72
ldplayer_interface intf; /* interface to the player */
75
chd_file * disc; /* handle to the disc itself */
76
UINT8 * vbidata; /* pointer to precomputed VBI data */
77
int width; /* width of video */
78
int height; /* height of video */
79
UINT32 fps_times_1million; /* frame rate of video */
80
int samplerate; /* audio samplerate */
81
chd_error readresult; /* result of the most recent read */
82
UINT32 chdtracks; /* number of tracks in the CHD */
83
av_codec_decompress_config avconfig; /* decompression configuration */
86
UINT8 audiosquelch; /* audio squelch state: bit 0 = audio 1, bit 1 = audio 2 */
87
UINT8 videosquelch; /* video squelch state: bit 0 = on/off */
88
UINT8 fieldnum; /* field number (0 or 1) */
89
INT32 curtrack; /* current track at this end of this vsync */
90
UINT32 maxtrack; /* maximum track number */
91
attoseconds_t attospertrack; /* attoseconds per track, or 0 if not moving */
92
attotime sliderupdate; /* time of last slider update */
95
frame_data frame[3]; /* circular list of frames */
96
UINT8 videoindex; /* index of the current video buffer */
97
bitmap_t videotarget; /* fake target bitmap for decompression */
98
bitmap_t * emptyframe; /* blank frame */
101
INT16 * audiobuffer[2]; /* buffer for audio samples */
102
UINT32 audiobufsize; /* size of buffer */
103
UINT32 audiobufin; /* input index */
104
UINT32 audiobufout; /* output index */
105
UINT32 audiocursamples; /* current samples this track */
106
UINT32 audiomaxsamples; /* maximum samples per track */
107
device_t *audiocustom; /* custom sound device */
110
vbi_metadata metadata[2]; /* metadata parsed from the stream, for each field */
113
UINT8 datain; /* current input data value */
114
UINT8 linein[LASERDISC_INPUT_LINES]; /* current input line state */
115
UINT8 dataout; /* current output data value */
116
UINT8 lineout[LASERDISC_OUTPUT_LINES]; /* current output line state */
119
UINT8 videoenable; /* is video enabled? */
120
render_texture * videotex; /* texture for the video */
121
palette_t * videopalette; /* palette for the video */
122
UINT8 overenable; /* is the overlay enabled? */
123
bitmap_t * overbitmap[2]; /* overlay bitmaps */
124
int overindex; /* index of the overlay bitmap */
125
render_texture * overtex; /* texture for the overlay */
129
/* sound callback info */
130
typedef struct _sound_token sound_token;
133
sound_stream * stream;
134
laserdisc_state * ld;
139
/***************************************************************************
141
***************************************************************************/
143
/* generic helper functions */
144
static TIMER_CALLBACK( perform_player_update );
145
static void read_track_data(laserdisc_state *ld);
146
static void process_track_data(device_t *device);
147
static DEVICE_START( laserdisc_sound );
148
static STREAM_UPDATE( custom_stream_callback );
149
static void configuration_load(device_t *device, int config_type, xml_data_node *parentnode);
150
static void configuration_save(device_t *device, int config_type, xml_data_node *parentnode);
154
/***************************************************************************
156
***************************************************************************/
160
/***************************************************************************
162
***************************************************************************/
164
/*-------------------------------------------------
165
get_safe_token - makes sure that the passed
166
in device is, in fact, a laserdisc device
167
-------------------------------------------------*/
169
INLINE laserdisc_state *get_safe_token(device_t *device)
171
assert(device != NULL);
172
assert(device_is_laserdisc(device));
174
return (laserdisc_state *)downcast<legacy_device_base *>(device)->token();
178
/*-------------------------------------------------
179
update_audio - update the audio stream to the
181
-------------------------------------------------*/
183
INLINE void update_audio(laserdisc_state *ld)
185
ldcore_data *ldcore = ld->core;
186
if (ldcore->audiocustom != NULL)
188
sound_token *token = (sound_token *)downcast<legacy_device_base *>(ldcore->audiocustom)->token();
189
token->stream->update();
194
/*-------------------------------------------------
195
add_and_clamp_track - add a delta to the
196
current track and clamp to minimum/maximum
198
-------------------------------------------------*/
200
INLINE void add_and_clamp_track(ldcore_data *ldcore, INT32 delta)
202
ldcore->curtrack += delta;
203
ldcore->curtrack = MAX(ldcore->curtrack, 1);
204
ldcore->curtrack = MIN(ldcore->curtrack, ldcore->maxtrack - 1);
208
/*-------------------------------------------------
209
fillbitmap_yuy16 - fill a YUY16 bitmap with a
211
-------------------------------------------------*/
213
INLINE void fillbitmap_yuy16(bitmap_t *bitmap, UINT8 yval, UINT8 cr, UINT8 cb)
215
UINT16 color0 = (yval << 8) | cb;
216
UINT16 color1 = (yval << 8) | cr;
219
/* write 32 bits of color (2 pixels at a time) */
220
for (y = 0; y < bitmap->height; y++)
222
UINT16 *dest = (UINT16 *)bitmap->base + y * bitmap->rowpixels;
223
for (x = 0; x < bitmap->width / 2; x++)
233
/***************************************************************************
234
GENERIC IMPLEMENTATION
235
***************************************************************************/
237
/*-------------------------------------------------
238
update_slider_pos - based on the current
239
speed and elapsed time, update the current
241
-------------------------------------------------*/
243
static void update_slider_pos(ldcore_data *ldcore, attotime curtime)
245
/* if not moving, update to now */
246
if (ldcore->attospertrack == 0)
247
ldcore->sliderupdate = curtime;
249
/* otherwise, compute the number of tracks covered */
252
attoseconds_t delta = (curtime - ldcore->sliderupdate).as_attoseconds();
253
INT32 tracks_covered;
255
/* determine how many tracks we covered and advance */
256
if (ldcore->attospertrack >= 0)
258
tracks_covered = delta / ldcore->attospertrack;
259
add_and_clamp_track(ldcore, tracks_covered);
260
if (tracks_covered != 0)
261
ldcore->sliderupdate += attotime(0, tracks_covered * ldcore->attospertrack);
265
tracks_covered = delta / -ldcore->attospertrack;
266
add_and_clamp_track(ldcore, -tracks_covered);
267
if (tracks_covered != 0)
268
ldcore->sliderupdate += attotime(0, tracks_covered * -ldcore->attospertrack);
274
/*-------------------------------------------------
275
vblank_state_changed - called on each state
276
change of the VBLANK signal
277
-------------------------------------------------*/
279
static void vblank_state_changed(device_t *device, screen_device &screen, bool vblank_state)
281
laserdisc_state *ld = get_safe_token(device);
282
ldcore_data *ldcore = ld->core;
283
attotime curtime = screen.machine().time();
285
/* update current track based on slider speed */
286
update_slider_pos(ldcore, curtime);
288
/* on rising edge, process previously-read frame and inform the player */
291
/* call the player's VSYNC callback */
292
if (ldcore->intf.vsync != NULL)
293
(*ldcore->intf.vsync)(ld, &ldcore->metadata[ldcore->fieldnum], ldcore->fieldnum, curtime);
295
/* set a timer to begin fetching the next frame just before the VBI data would be fetched */
296
screen.machine().scheduler().timer_set(screen.time_until_pos(16*2), FUNC(perform_player_update), 0, ld);
301
/*-------------------------------------------------
302
vblank_state_changed - called on each state
303
change of the VBLANK signal
304
-------------------------------------------------*/
306
static TIMER_CALLBACK( perform_player_update )
308
laserdisc_state *ld = (laserdisc_state *)ptr;
309
ldcore_data *ldcore = ld->core;
310
attotime curtime = machine.time();
312
/* wait for previous read and decode to finish */
313
process_track_data(ld->device);
315
/* update current track based on slider speed */
316
update_slider_pos(ldcore, curtime);
318
/* update the state */
319
if (ldcore->intf.update != NULL)
320
add_and_clamp_track(ldcore, (*ldcore->intf.update)(ld, &ldcore->metadata[ldcore->fieldnum], ldcore->fieldnum, curtime));
322
/* flush any audio before we read more */
325
/* start reading the track data for the next round */
326
ldcore->fieldnum ^= 1;
331
/*-------------------------------------------------
332
laserdisc_data_w - write data to the given
334
-------------------------------------------------*/
336
void laserdisc_data_w(device_t *device, UINT8 data)
338
laserdisc_state *ld = get_safe_token(device);
339
ldcore_data *ldcore = ld->core;
340
UINT8 prev = ldcore->datain;
341
ldcore->datain = data;
343
/* call through to the player-specific write handler */
344
if (ldcore->intf.writedata != NULL)
345
(*ldcore->intf.writedata)(ld, prev, data);
349
/*-------------------------------------------------
350
laserdisc_line_w - control an input line
351
-------------------------------------------------*/
353
void laserdisc_line_w(device_t *device, UINT8 line, UINT8 newstate)
355
laserdisc_state *ld = get_safe_token(device);
356
ldcore_data *ldcore = ld->core;
358
assert(line < LASERDISC_INPUT_LINES);
359
assert(newstate == ASSERT_LINE || newstate == CLEAR_LINE || newstate == PULSE_LINE);
362
if (newstate == ASSERT_LINE || newstate == PULSE_LINE)
364
if (ldcore->linein[line] != ASSERT_LINE)
366
/* call through to the player-specific line handler */
367
if (ldcore->intf.writeline[line] != NULL)
368
(*ldcore->intf.writeline[line])(ld, CLEAR_LINE, ASSERT_LINE);
370
ldcore->linein[line] = ASSERT_LINE;
374
if (newstate == CLEAR_LINE || newstate == PULSE_LINE)
376
if (ldcore->linein[line] != CLEAR_LINE)
378
/* call through to the player-specific line handler */
379
if (ldcore->intf.writeline[line] != NULL)
380
(*ldcore->intf.writeline[line])(ld, ASSERT_LINE, CLEAR_LINE);
382
ldcore->linein[line] = CLEAR_LINE;
387
/*-------------------------------------------------
388
laserdisc_data_r - return the current
390
-------------------------------------------------*/
392
UINT8 laserdisc_data_r(device_t *device)
394
laserdisc_state *ld = get_safe_token(device);
395
ldcore_data *ldcore = ld->core;
396
UINT8 result = ldcore->dataout;
398
/* call through to the player-specific data handler */
399
if (ldcore->intf.readdata != NULL)
400
result = (*ldcore->intf.readdata)(ld);
406
/*-------------------------------------------------
407
laserdisc_line_r - return the current state
409
-------------------------------------------------*/
411
UINT8 laserdisc_line_r(device_t *device, UINT8 line)
413
laserdisc_state *ld = get_safe_token(device);
414
ldcore_data *ldcore = ld->core;
417
assert(line < LASERDISC_OUTPUT_LINES);
418
result = ldcore->lineout[line];
420
/* call through to the player-specific data handler */
421
if (ldcore->intf.readline[line] != NULL)
422
result = (*ldcore->intf.readline[line])(ld);
428
/*-------------------------------------------------
429
laserdisc_get_video - return the current
430
video frame; return TRUE if valid or FALSE
432
-------------------------------------------------*/
434
int laserdisc_get_video(device_t *device, bitmap_t **bitmap)
436
laserdisc_state *ld = get_safe_token(device);
437
ldcore_data *ldcore = ld->core;
440
/* determine the most recent live set of frames */
441
frame = &ldcore->frame[ldcore->videoindex];
442
if (frame->numfields < 2)
443
frame = &ldcore->frame[(ldcore->videoindex + ARRAY_LENGTH(ldcore->frame) - 1) % ARRAY_LENGTH(ldcore->frame)];
445
/* if no video present, return the empty frame */
446
if (ldcore->videosquelch || frame->numfields < 2)
448
*bitmap = ldcore->emptyframe;
453
*bitmap = frame->visbitmap;
459
/*-------------------------------------------------
460
laserdisc_get_field_code - return raw field
461
information read from the disc
462
-------------------------------------------------*/
464
UINT32 laserdisc_get_field_code(device_t *device, UINT32 code, UINT8 zero_if_squelched)
466
laserdisc_state *ld = get_safe_token(device);
467
ldcore_data *ldcore = ld->core;
468
int field = ldcore->fieldnum;
470
/* return nothing if the video is off (external devices can't sense) */
471
if (zero_if_squelched && ldcore->videosquelch)
476
case LASERDISC_CODE_WHITE_FLAG:
477
return ldcore->metadata[field].white;
479
case LASERDISC_CODE_LINE16:
480
return ldcore->metadata[field].line16;
482
case LASERDISC_CODE_LINE17:
483
return ldcore->metadata[field].line17;
485
case LASERDISC_CODE_LINE18:
486
return ldcore->metadata[field].line18;
488
case LASERDISC_CODE_LINE1718:
489
return ldcore->metadata[field].line1718;
497
/***************************************************************************
498
PLAYER-TO-CORE INTERFACES
499
***************************************************************************/
501
/*-------------------------------------------------
502
ldcore_get_safe_token - return a token with
503
type checking from a device
504
-------------------------------------------------*/
506
laserdisc_state *ldcore_get_safe_token(device_t *device)
508
return get_safe_token(device);
512
/*-------------------------------------------------
513
ldcore_set_audio_squelch - set the left/right
515
-------------------------------------------------*/
517
void ldcore_set_audio_squelch(laserdisc_state *ld, UINT8 squelchleft, UINT8 squelchright)
520
ld->core->audiosquelch = (squelchleft ? 1 : 0) | (squelchright ? 2 : 0);
524
/*-------------------------------------------------
525
ldcore_set_video_squelch - set the video
527
-------------------------------------------------*/
529
void ldcore_set_video_squelch(laserdisc_state *ld, UINT8 squelch)
531
ld->core->videosquelch = squelch;
535
/*-------------------------------------------------
536
ldcore_set_slider_speed - dynamically change
538
-------------------------------------------------*/
540
void ldcore_set_slider_speed(laserdisc_state *ld, INT32 tracks_per_vsync)
542
ldcore_data *ldcore = ld->core;
543
attotime vsyncperiod = ld->screen->frame_period();
545
update_slider_pos(ldcore, ld->device->machine().time());
547
/* if 0, set the time to 0 */
548
if (tracks_per_vsync == 0)
549
ldcore->attospertrack = 0;
551
/* positive values store positive times */
552
else if (tracks_per_vsync > 0)
553
ldcore->attospertrack = (vsyncperiod / tracks_per_vsync).as_attoseconds();
555
/* negative values store negative times */
557
ldcore->attospertrack = -(vsyncperiod / -tracks_per_vsync).as_attoseconds();
560
printf("Slider speed = %d\n", tracks_per_vsync);
564
/*-------------------------------------------------
565
ldcore_advance_slider - advance the slider by
566
a certain number of tracks
567
-------------------------------------------------*/
569
void ldcore_advance_slider(laserdisc_state *ld, INT32 numtracks)
571
ldcore_data *ldcore = ld->core;
573
update_slider_pos(ldcore, ld->device->machine().time());
574
add_and_clamp_track(ldcore, numtracks);
576
printf("Advance by %d\n", numtracks);
580
/*-------------------------------------------------
581
ldcore_get_slider_position - get the current
583
-------------------------------------------------*/
585
slider_position ldcore_get_slider_position(laserdisc_state *ld)
587
ldcore_data *ldcore = ld->core;
589
/* update the slider position first */
590
update_slider_pos(ldcore, ld->device->machine().time());
592
/* return the status */
593
if (ldcore->curtrack == 1)
594
return SLIDER_MINIMUM;
595
else if (ldcore->curtrack < VIRTUAL_LEAD_IN_TRACKS)
596
return SLIDER_VIRTUAL_LEADIN;
597
else if (ldcore->curtrack < VIRTUAL_LEAD_IN_TRACKS + ldcore->chdtracks)
599
else if (ldcore->curtrack < VIRTUAL_LEAD_IN_TRACKS + MAX_TOTAL_TRACKS)
600
return SLIDER_OUTSIDE_CHD;
601
else if (ldcore->curtrack < ldcore->maxtrack - 1)
602
return SLIDER_VIRTUAL_LEADOUT;
604
return SLIDER_MAXIMUM;
609
/***************************************************************************
610
GENERIC HELPER FUNCTIONS
611
***************************************************************************/
613
/*-------------------------------------------------
614
ldcore_generic_update - generically update in
615
a way that works for most situations
616
-------------------------------------------------*/
618
INT32 ldcore_generic_update(laserdisc_state *ld, const vbi_metadata *vbi, int fieldnum, attotime curtime, ldplayer_state *newstate)
623
/* start by assuming the state doesn't change */
624
*newstate = ld->state;
626
/* handle things based on the state */
627
switch (ld->state.state)
629
case LDSTATE_EJECTING:
630
/* when time expires, switch to the ejected state */
631
if (curtime >= ld->state.endtime)
632
newstate->state = LDSTATE_EJECTED;
635
case LDSTATE_EJECTED:
643
case LDSTATE_LOADING:
644
/* when time expires, switch to the spinup state */
645
if (curtime >= ld->state.endtime)
646
newstate->state = LDSTATE_SPINUP;
647
advanceby = -GENERIC_SEARCH_SPEED;
651
/* when time expires, switch to the playing state */
652
if (curtime >= ld->state.endtime)
653
newstate->state = LDSTATE_PLAYING;
654
advanceby = -GENERIC_SEARCH_SPEED;
657
case LDSTATE_PAUSING:
658
/* if he hit the start of a frame, switch to paused state */
659
if (is_start_of_frame(vbi))
661
newstate->state = LDSTATE_PAUSED;
662
newstate->param = fieldnum;
665
/* else advance until we hit it */
666
else if (fieldnum == 1)
671
/* if we paused on field 1, we must flip back and forth */
672
if (ld->state.param == 1)
673
advanceby = (fieldnum == 1) ? 1 : -1;
676
case LDSTATE_PLAYING:
677
/* if we hit the target frame, switch to the paused state */
678
if (ld->state.param > 0 && is_start_of_frame(vbi) && frame_from_metadata(vbi) == ld->state.param)
680
newstate->state = LDSTATE_PAUSED;
681
newstate->param = fieldnum;
684
/* otherwise after the second field of each frame */
685
else if (fieldnum == 1)
689
case LDSTATE_PLAYING_SLOW_REVERSE:
690
/* after the second field of each frame, see if we need to advance */
691
if (fieldnum == 1 && ++ld->state.substate > ld->state.param)
694
ld->state.substate = 0;
698
case LDSTATE_PLAYING_SLOW_FORWARD:
699
/* after the second field of each frame, see if we need to advance */
700
if (fieldnum == 1 && ++ld->state.substate > ld->state.param)
703
ld->state.substate = 0;
707
case LDSTATE_PLAYING_FAST_REVERSE:
708
/* advance after the second field of each frame */
710
advanceby = -ld->state.param;
713
case LDSTATE_PLAYING_FAST_FORWARD:
714
/* advance after the second field of each frame */
716
advanceby = ld->state.param;
719
case LDSTATE_SCANNING:
720
/* advance after the second field of each frame */
722
advanceby = ld->state.param >> 8;
724
/* after we run out of vsyncs, revert to the saved state */
725
if (++ld->state.substate >= (ld->state.param & 0xff))
726
*newstate = ld->savestate;
729
case LDSTATE_STEPPING_REVERSE:
730
/* wait for the first field of the frame and then leap backwards */
731
if (is_start_of_frame(vbi))
733
advanceby = (fieldnum == 1) ? -1 : -2;
734
newstate->state = LDSTATE_PAUSING;
738
case LDSTATE_STEPPING_FORWARD:
739
/* wait for the first field of the frame and then switch to pausing state */
740
if (is_start_of_frame(vbi))
741
newstate->state = LDSTATE_PAUSING;
744
case LDSTATE_SEEKING:
745
/* if we're in the final state, look for a matching frame and pause there */
746
frame = frame_from_metadata(vbi);
747
if (ld->state.substate == 1 && is_start_of_frame(vbi) && frame == ld->state.param)
749
newstate->state = LDSTATE_PAUSED;
750
newstate->param = fieldnum;
753
/* otherwise, if we got frame data from the VBI, update our seeking logic */
754
else if (ld->state.substate == 0 && frame != FRAME_NOT_PRESENT)
756
INT32 delta = (ld->state.param - 2) - frame;
758
/* if we're within a couple of frames, just play until we hit it */
759
if (delta >= 0 && delta <= 2)
760
ld->state.substate++;
762
/* otherwise, compute the delta assuming 1:1 track to frame; this will correct eventually */
768
advanceby = MIN(advanceby, GENERIC_SEARCH_SPEED);
769
advanceby = MAX(advanceby, -GENERIC_SEARCH_SPEED);
773
/* otherwise, keep advancing until we know what's up */
786
/*-------------------------------------------------
787
read_track_data - read and process data for
788
a particular video track
789
-------------------------------------------------*/
791
static void read_track_data(laserdisc_state *ld)
793
ldcore_data *ldcore = ld->core;
794
UINT32 tracknum = ldcore->curtrack;
795
UINT32 fieldnum = ldcore->fieldnum;
796
vbi_metadata vbidata = { 0 };
802
/* compute the chdhunk number we are going to read */
803
chdtrack = tracknum - 1 - VIRTUAL_LEAD_IN_TRACKS;
804
chdtrack = MAX(chdtrack, 0);
805
chdtrack = MIN(chdtrack, ldcore->chdtracks - 1);
806
readhunk = chdtrack * 2 + fieldnum;
808
/* cheat and look up the metadata we are about to retrieve */
809
if (ldcore->vbidata != NULL)
810
vbi_metadata_unpack(&vbidata, NULL, &ldcore->vbidata[readhunk * VBI_PACKED_BYTES]);
812
/* if we're in the lead-in area, force the VBI data to be standard lead-in */
813
if (tracknum - 1 < VIRTUAL_LEAD_IN_TRACKS)
816
vbidata.line17 = vbidata.line18 = vbidata.line1718 = VBI_CODE_LEADIN;
818
//printf("track %5d.%d: %06X %06X %06X\n", tracknum, fieldnum, vbidata.line16, vbidata.line17, vbidata.line18);
820
/* if we're about to read the first field in a frame, advance */
821
frame = &ldcore->frame[ldcore->videoindex];
822
if ((vbidata.line1718 & VBI_MASK_CAV_PICTURE) == VBI_CODE_CAV_PICTURE)
824
if (frame->numfields >= 2)
825
ldcore->videoindex = (ldcore->videoindex + 1) % ARRAY_LENGTH(ldcore->frame);
826
frame = &ldcore->frame[ldcore->videoindex];
827
frame->numfields = 0;
830
/* if we're squelched, reset the frame counter */
831
if (ldcore->videosquelch)
832
frame->numfields = 0;
834
/* remember the last field number */
835
frame->lastfield = tracknum * 2 + fieldnum;
837
/* set the video target information */
838
ldcore->videotarget.alloc = NULL;
839
ldcore->videotarget.base = BITMAP_ADDR16(frame->bitmap, fieldnum, 0);
840
ldcore->videotarget.rowpixels = frame->bitmap->rowpixels * 2;
841
ldcore->videotarget.width = frame->bitmap->width;
842
ldcore->videotarget.height = frame->bitmap->height / 2;
843
ldcore->videotarget.format = frame->bitmap->format;
844
ldcore->videotarget.bpp = frame->bitmap->bpp;
845
ldcore->videotarget.palette = frame->bitmap->palette;
846
ldcore->videotarget.cliprect = frame->bitmap->cliprect;
847
ldcore->avconfig.video = &ldcore->videotarget;
849
/* set the audio target information */
850
if (ldcore->audiobufin + ldcore->audiomaxsamples <= ldcore->audiobufsize)
852
/* if we can fit without wrapping, just read the data directly */
853
ldcore->avconfig.audio[0] = &ldcore->audiobuffer[0][ldcore->audiobufin];
854
ldcore->avconfig.audio[1] = &ldcore->audiobuffer[1][ldcore->audiobufin];
858
/* otherwise, read to the beginning of the buffer */
859
ldcore->avconfig.audio[0] = &ldcore->audiobuffer[0][0];
860
ldcore->avconfig.audio[1] = &ldcore->audiobuffer[1][0];
863
/* override if we're not decoding */
864
ldcore->avconfig.maxsamples = ldcore->audiomaxsamples;
865
ldcore->avconfig.actsamples = &ldcore->audiocursamples;
866
ldcore->audiocursamples = 0;
868
/* set the VBI data for the new field from our precomputed data */
869
if (ldcore->vbidata != NULL)
870
vbi_metadata_unpack(&ldcore->metadata[fieldnum], &vbiframe, &ldcore->vbidata[readhunk * VBI_PACKED_BYTES]);
872
/* if we're in the lead-in area, force the VBI data to be standard lead-in */
873
if (tracknum - 1 < VIRTUAL_LEAD_IN_TRACKS)
875
ldcore->metadata[fieldnum].line16 = 0;
876
ldcore->metadata[fieldnum].line17 = ldcore->metadata[fieldnum].line18 = ldcore->metadata[fieldnum].line1718 = VBI_CODE_LEADIN;
879
/* configure the codec and then read */
880
ldcore->readresult = CHDERR_FILE_NOT_FOUND;
881
if (ldcore->disc != NULL && !ldcore->videosquelch)
883
ldcore->readresult = chd_codec_config(ldcore->disc, AV_CODEC_DECOMPRESS_CONFIG, &ldcore->avconfig);
884
if (ldcore->readresult == CHDERR_NONE)
885
ldcore->readresult = chd_read_async(ldcore->disc, readhunk, NULL);
890
/*-------------------------------------------------
891
process_track_data - process data from a
892
track after it has been read
893
-------------------------------------------------*/
895
static void process_track_data(device_t *device)
897
laserdisc_state *ld = get_safe_token(device);
898
ldcore_data *ldcore = ld->core;
900
/* wait for the async operation to complete */
901
if (ldcore->readresult == CHDERR_OPERATION_PENDING)
902
ldcore->readresult = chd_async_complete(ldcore->disc);
904
/* remove the video if we had an error */
905
if (ldcore->readresult != CHDERR_NONE)
906
ldcore->avconfig.video = NULL;
908
/* count the field as read if we are successful */
909
if (ldcore->avconfig.video != NULL)
910
ldcore->frame[ldcore->videoindex].numfields++;
912
/* render the display if present */
913
if (ldcore->avconfig.video != NULL && ldcore->intf.overlay != NULL)
914
(*ldcore->intf.overlay)(ld, ldcore->avconfig.video);
916
/* pass the audio to the callback */
917
if (ldcore->config.audio != NULL)
918
(*ldcore->config.audio)(device, ldcore->samplerate, ldcore->audiocursamples, ldcore->avconfig.audio[0], ldcore->avconfig.audio[1]);
920
/* shift audio data if we read it into the beginning of the buffer */
921
if (ldcore->audiocursamples != 0 && ldcore->audiobufin != 0)
925
/* iterate over channels */
926
for (chnum = 0; chnum < 2; chnum++)
927
if (ldcore->avconfig.audio[chnum] == &ldcore->audiobuffer[chnum][0])
931
/* move data to the end */
932
samplesleft = ldcore->audiobufsize - ldcore->audiobufin;
933
samplesleft = MIN(samplesleft, ldcore->audiocursamples);
934
memmove(&ldcore->audiobuffer[chnum][ldcore->audiobufin], &ldcore->audiobuffer[chnum][0], samplesleft * 2);
936
/* shift data at the beginning */
937
if (samplesleft < ldcore->audiocursamples)
938
memmove(&ldcore->audiobuffer[chnum][0], &ldcore->audiobuffer[chnum][samplesleft], (ldcore->audiocursamples - samplesleft) * 2);
942
/* update the input buffer pointer */
943
ldcore->audiobufin = (ldcore->audiobufin + ldcore->audiocursamples) % ldcore->audiobufsize;
947
/*-------------------------------------------------
948
laserdisc_sound_start - custom audio start
950
-------------------------------------------------*/
952
static DEVICE_START( laserdisc_sound )
954
sound_token *token = (sound_token *)downcast<legacy_device_base *>(device)->token();
955
token->stream = device->machine().sound().stream_alloc(*device, 0, 2, 48000, token, custom_stream_callback);
960
/*-------------------------------------------------
961
laserdisc_sound_get_info - information
962
callback for laserdisc audio
963
-------------------------------------------------*/
965
DEVICE_GET_INFO( laserdisc_sound )
969
/* --- the following bits of info are returned as 64-bit signed integers --- */
970
case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(sound_token); break;
972
/* --- the following bits of info are returned as pointers to data or functions --- */
973
case DEVINFO_FCT_START: info->start = DEVICE_START_NAME(laserdisc_sound);break;
975
/* --- the following bits of info are returned as NULL-terminated strings --- */
976
case DEVINFO_STR_NAME: strcpy(info->s, "Laserdisc Analog"); break;
977
case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break;
982
/*-------------------------------------------------
983
custom_stream_callback - audio streamer
985
-------------------------------------------------*/
987
static STREAM_UPDATE( custom_stream_callback )
989
sound_token *token = (sound_token *)param;
990
laserdisc_state *ld = token->ld;
991
ldcore_data *ldcore = ld->core;
992
stream_sample_t *dst0 = outputs[0];
993
stream_sample_t *dst1 = outputs[1];
994
INT16 leftand, rightand;
995
int samples_avail = 0;
997
/* compute AND values based on the squelch */
998
leftand = (ldcore->audiosquelch & 1) ? 0x0000 : 0xffff;
999
rightand = (ldcore->audiosquelch & 2) ? 0x0000 : 0xffff;
1001
/* see if we have enough samples to fill the buffer; if not, drop out */
1004
samples_avail = ldcore->audiobufin - ldcore->audiobufout;
1005
if (samples_avail < 0)
1006
samples_avail += ldcore->audiobufsize;
1009
/* if no attached ld, just clear the buffers */
1010
if (samples_avail < samples)
1012
memset(dst0, 0, samples * sizeof(dst0[0]));
1013
memset(dst1, 0, samples * sizeof(dst1[0]));
1016
/* otherwise, stream from our buffer */
1019
INT16 *buffer0 = ldcore->audiobuffer[0];
1020
INT16 *buffer1 = ldcore->audiobuffer[1];
1021
int sampout = ldcore->audiobufout;
1023
/* copy samples, clearing behind us as we go */
1024
while (sampout != ldcore->audiobufin && samples-- > 0)
1026
*dst0++ = buffer0[sampout] & leftand;
1027
*dst1++ = buffer1[sampout] & rightand;
1028
buffer0[sampout] = 0;
1029
buffer1[sampout] = 0;
1031
if (sampout >= ldcore->audiobufsize)
1034
ldcore->audiobufout = sampout;
1036
/* clear out the rest of the buffer */
1039
int sampout = (ldcore->audiobufout == 0) ? ldcore->audiobufsize - 1 : ldcore->audiobufout - 1;
1040
stream_sample_t fill0 = buffer0[sampout] & leftand;
1041
stream_sample_t fill1 = buffer1[sampout] & rightand;
1043
while (samples-- > 0)
1054
/***************************************************************************
1055
CONFIG SETTINGS ACCESS
1056
***************************************************************************/
1058
/*-------------------------------------------------
1059
configuration_load - read and apply data from
1060
the configuration file
1061
-------------------------------------------------*/
1063
static void configuration_load(device_t *device, int config_type, xml_data_node *parentnode)
1065
xml_data_node *overnode;
1066
xml_data_node *ldnode;
1068
/* we only care about game files */
1069
if (config_type != CONFIG_TYPE_GAME)
1072
/* might not have any data */
1073
if (parentnode == NULL)
1076
/* iterate over overlay nodes */
1077
for (ldnode = xml_get_sibling(parentnode->child, "device"); ldnode != NULL; ldnode = xml_get_sibling(ldnode->next, "device"))
1079
const char *devtag = xml_get_attribute_string(ldnode, "tag", "");
1080
if (strcmp(devtag,device->tag()) == 0)
1082
laserdisc_state *ld = get_safe_token(device);
1083
ldcore_data *ldcore = ld->core;
1085
/* handle the overlay node */
1086
overnode = xml_get_sibling(ldnode->child, "overlay");
1087
if (overnode != NULL)
1089
/* fetch positioning controls */
1090
ldcore->config.overposx = xml_get_attribute_float(overnode, "hoffset", ldcore->config.overposx);
1091
ldcore->config.overscalex = xml_get_attribute_float(overnode, "hstretch", ldcore->config.overscalex);
1092
ldcore->config.overposy = xml_get_attribute_float(overnode, "voffset", ldcore->config.overposy);
1093
ldcore->config.overscaley = xml_get_attribute_float(overnode, "vstretch", ldcore->config.overscaley);
1100
/*-------------------------------------------------
1101
configuration_save - save data to the
1103
-------------------------------------------------*/
1105
static void configuration_save(device_t *device, int config_type, xml_data_node *parentnode)
1107
/* we only care about game files */
1108
if (config_type != CONFIG_TYPE_GAME)
1111
laserdisc_config *origconfig = (laserdisc_config *)downcast<const legacy_device_base *>(device)->inline_config();
1112
laserdisc_state *ld = get_safe_token(device);
1113
ldcore_data *ldcore = ld->core;
1114
xml_data_node *overnode;
1115
xml_data_node *ldnode;
1118
ldnode = xml_add_child(parentnode, "device", NULL);
1121
int changed = FALSE;
1123
/* output the basics */
1124
xml_set_attribute(ldnode, "tag", device->tag());
1126
/* add an overlay node */
1127
overnode = xml_add_child(ldnode, "overlay", NULL);
1128
if (overnode != NULL)
1130
/* output the positioning controls */
1131
if (ldcore->config.overposx != origconfig->overposx)
1133
xml_set_attribute_float(overnode, "hoffset", ldcore->config.overposx);
1137
if (ldcore->config.overscalex != origconfig->overscalex)
1139
xml_set_attribute_float(overnode, "hstretch", ldcore->config.overscalex);
1143
if (ldcore->config.overposy != origconfig->overposy)
1145
xml_set_attribute_float(overnode, "voffset", ldcore->config.overposy);
1149
if (ldcore->config.overscaley != origconfig->overscaley)
1151
xml_set_attribute_float(overnode, "vstretch", ldcore->config.overscaley);
1156
/* if nothing changed, kill the node */
1158
xml_delete_node(ldnode);
1164
/***************************************************************************
1166
***************************************************************************/
1168
/*-------------------------------------------------
1169
laserdisc_video_enable - enable/disable the
1171
-------------------------------------------------*/
1173
void laserdisc_video_enable(device_t *device, int enable)
1175
laserdisc_state *ld = get_safe_token(device);
1176
ld->core->videoenable = enable;
1180
/*-------------------------------------------------
1181
laserdisc_video_enable - enable/disable the
1183
-------------------------------------------------*/
1185
void laserdisc_overlay_enable(device_t *device, int enable)
1187
laserdisc_state *ld = get_safe_token(device);
1188
ld->core->overenable = enable;
1192
/*-------------------------------------------------
1193
video update callback
1194
-------------------------------------------------*/
1196
SCREEN_UPDATE( laserdisc )
1198
device_t *laserdisc = screen->machine().device("laserdisc"); // TODO: allow more than one laserdisc
1199
if (laserdisc != NULL)
1201
const rectangle &visarea = screen->visible_area();
1202
laserdisc_state *ld = (laserdisc_state *)downcast<legacy_device_base *>(laserdisc)->token();
1203
ldcore_data *ldcore = ld->core;
1204
bitmap_t *overbitmap = ldcore->overbitmap[ldcore->overindex];
1205
bitmap_t *vidbitmap = NULL;
1207
/* handle the overlay if present */
1208
if (overbitmap != NULL && ldcore->config.overupdate != NULL)
1210
rectangle clip = *cliprect;
1212
/* scale the cliprect to the overlay size and then call the update callback */
1213
clip.min_x = ldcore->config.overclip.min_x;
1214
clip.max_x = ldcore->config.overclip.max_x;
1215
clip.min_y = cliprect->min_y * overbitmap->height / bitmap->height;
1216
if (cliprect->min_y == visarea.min_y)
1217
clip.min_y = MIN(clip.min_y, ldcore->config.overclip.min_y);
1218
clip.max_y = (cliprect->max_y + 1) * overbitmap->height / bitmap->height - 1;
1219
(*ldcore->config.overupdate)(screen, overbitmap, &clip);
1222
/* if this is the last update, do the rendering */
1223
if (cliprect->max_y == visarea.max_y)
1225
/* update the texture with the overlay contents */
1226
if (overbitmap != NULL)
1228
if (overbitmap->format == BITMAP_FORMAT_INDEXED16)
1229
ldcore->overtex->set_bitmap(overbitmap, &ldcore->config.overclip, TEXFORMAT_PALETTEA16, laserdisc->machine().palette);
1230
else if (overbitmap->format == BITMAP_FORMAT_RGB32)
1231
ldcore->overtex->set_bitmap(overbitmap, &ldcore->config.overclip, TEXFORMAT_ARGB32);
1234
/* get the laserdisc video */
1235
laserdisc_get_video(laserdisc, &vidbitmap);
1236
if (vidbitmap != NULL)
1237
ldcore->videotex->set_bitmap(vidbitmap, NULL, TEXFORMAT_YUY16, ldcore->videopalette);
1239
/* reset the screen contents */
1240
screen->container().empty();
1242
/* add the video texture */
1243
if (ldcore->videoenable)
1244
screen->container().add_quad(0.0f, 0.0f, 1.0f, 1.0f, MAKE_ARGB(0xff,0xff,0xff,0xff), ldcore->videotex, PRIMFLAG_BLENDMODE(BLENDMODE_NONE) | PRIMFLAG_SCREENTEX(1));
1246
/* add the overlay */
1247
if (ldcore->overenable && overbitmap != NULL)
1249
float x0 = 0.5f - 0.5f * ldcore->config.overscalex + ldcore->config.overposx;
1250
float y0 = 0.5f - 0.5f * ldcore->config.overscaley + ldcore->config.overposy;
1251
float x1 = x0 + ldcore->config.overscalex;
1252
float y1 = y0 + ldcore->config.overscaley;
1253
screen->container().add_quad(x0, y0, x1, y1, MAKE_ARGB(0xff,0xff,0xff,0xff), ldcore->overtex, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_SCREENTEX(1));
1256
/* swap to the next bitmap */
1257
ldcore->overindex = (ldcore->overindex + 1) % ARRAY_LENGTH(ldcore->overbitmap);
1266
/***************************************************************************
1268
***************************************************************************/
1270
/*-------------------------------------------------
1271
laserdisc_get_config - return a copy of the
1272
current live configuration settings
1273
-------------------------------------------------*/
1275
void laserdisc_get_config(device_t *device, laserdisc_config *config)
1277
laserdisc_state *ld = get_safe_token(device);
1278
*config = ld->core->config;
1282
/*-------------------------------------------------
1283
laserdisc_get_config - change the current live
1284
configuration settings
1285
-------------------------------------------------*/
1287
void laserdisc_set_config(device_t *device, const laserdisc_config *config)
1289
laserdisc_state *ld = get_safe_token(device);
1290
ld->core->config = *config;
1295
/***************************************************************************
1297
***************************************************************************/
1299
/*-------------------------------------------------
1300
init_disc - initialize the state of the
1302
-------------------------------------------------*/
1304
static void init_disc(device_t *device)
1306
const laserdisc_config *config = (const laserdisc_config *)downcast<const legacy_device_base *>(device)->inline_config();
1307
laserdisc_state *ld = get_safe_token(device);
1308
ldcore_data *ldcore = ld->core;
1311
/* get a handle to the disc to play */
1312
if (config->getdisc != NULL)
1313
ldcore->disc = (*config->getdisc)(device);
1315
ldcore->disc = get_disk_handle(device->machine(), device->tag());
1317
/* set default parameters */
1318
ldcore->width = 720;
1319
ldcore->height = 240;
1320
ldcore->fps_times_1million = 59940000;
1321
ldcore->samplerate = 48000;
1323
/* get the disc metadata and extract the ld */
1324
ldcore->chdtracks = 0;
1325
ldcore->maxtrack = VIRTUAL_LEAD_IN_TRACKS + MAX_TOTAL_TRACKS + VIRTUAL_LEAD_OUT_TRACKS;
1326
if (ldcore->disc != NULL)
1328
UINT32 totalhunks = chd_get_header(ldcore->disc)->totalhunks;
1329
int fps, fpsfrac, interlaced, channels;
1333
/* require the A/V codec */
1334
if (chd_get_header(ldcore->disc)->compression != CHDCOMPRESSION_AV)
1335
fatalerror("Laserdisc video must be compressed with the A/V codec!");
1337
/* read the metadata */
1338
err = chd_get_metadata(ldcore->disc, AV_METADATA_TAG, 0, metadata, sizeof(metadata), NULL, NULL, NULL);
1339
if (err != CHDERR_NONE)
1340
fatalerror("Non-A/V CHD file specified");
1342
/* extract the metadata */
1343
if (sscanf(metadata, AV_METADATA_FORMAT, &fps, &fpsfrac, &ldcore->width, &ldcore->height, &interlaced, &channels, &ldcore->samplerate) != 7)
1344
fatalerror("Invalid metadata in CHD file");
1346
ldcore->fps_times_1million = fps * 1000000 + fpsfrac;
1348
/* require interlaced video */
1350
fatalerror("Laserdisc video must be interlaced!");
1352
/* determine the maximum track and allocate a frame buffer */
1353
ldcore->chdtracks = totalhunks / 2;
1355
/* allocate memory for the precomputed per-frame metadata */
1356
ldcore->vbidata = auto_alloc_array(device->machine(), UINT8, totalhunks * VBI_PACKED_BYTES);
1357
err = chd_get_metadata(ldcore->disc, AV_LD_METADATA_TAG, 0, ldcore->vbidata, totalhunks * VBI_PACKED_BYTES, &vbilength, NULL, NULL);
1358
if (err != CHDERR_NONE || vbilength != totalhunks * VBI_PACKED_BYTES)
1359
fatalerror("Precomputed VBI metadata missing or incorrect size");
1361
ldcore->maxtrack = MAX(ldcore->maxtrack, VIRTUAL_LEAD_IN_TRACKS + VIRTUAL_LEAD_OUT_TRACKS + ldcore->chdtracks);
1365
/*-------------------------------------------------
1366
init_video - initialize the state of the
1368
-------------------------------------------------*/
1370
static void init_video(device_t *device)
1372
laserdisc_state *ld = get_safe_token(device);
1373
ldcore_data *ldcore = ld->core;
1376
/* register for VBLANK callbacks */
1377
ld->screen->register_vblank_callback(vblank_state_delegate(FUNC(vblank_state_changed), device));
1379
/* allocate video frames */
1380
for (index = 0; index < ARRAY_LENGTH(ldcore->frame); index++)
1382
frame_data *frame = &ldcore->frame[index];
1384
/* first allocate a YUY16 bitmap at 2x the height */
1385
frame->bitmap = auto_alloc(device->machine(), bitmap_t(ldcore->width, ldcore->height * 2, BITMAP_FORMAT_YUY16));
1386
fillbitmap_yuy16(frame->bitmap, 40, 109, 240);
1388
/* make a copy of the bitmap that clips out the VBI and horizontal blanking areas */
1389
frame->visbitmap = auto_alloc(device->machine(), bitmap_t(BITMAP_ADDR16(frame->bitmap, 44, frame->bitmap->width * 8 / 720),
1390
frame->bitmap->width - 2 * frame->bitmap->width * 8 / 720,
1391
frame->bitmap->height - 44,
1392
frame->bitmap->rowpixels, frame->bitmap->format));
1395
/* allocate an empty frame of the same size */
1396
ldcore->emptyframe = auto_bitmap_alloc(device->machine(), ldcore->width, ldcore->height * 2, BITMAP_FORMAT_YUY16);
1397
fillbitmap_yuy16(ldcore->emptyframe, 0, 128, 128);
1399
/* allocate texture for rendering */
1400
ldcore->videoenable = TRUE;
1401
ldcore->videotex = device->machine().render().texture_alloc();
1402
if (ldcore->videotex == NULL)
1403
fatalerror("Out of memory allocating video texture");
1405
/* allocate palette for applying brightness/contrast/gamma */
1406
ldcore->videopalette = palette_alloc(256, 1);
1407
if (ldcore->videopalette == NULL)
1408
fatalerror("Out of memory allocating video palette");
1409
for (index = 0; index < 256; index++)
1410
palette_entry_set_color(ldcore->videopalette, index, MAKE_RGB(index, index, index));
1412
/* allocate overlay */
1413
if (ldcore->config.overwidth > 0 && ldcore->config.overheight > 0 && ldcore->config.overupdate != NULL)
1415
ldcore->overenable = TRUE;
1416
ldcore->overbitmap[0] = auto_bitmap_alloc(device->machine(), ldcore->config.overwidth, ldcore->config.overheight, (bitmap_format)ldcore->config.overformat);
1417
ldcore->overbitmap[1] = auto_bitmap_alloc(device->machine(), ldcore->config.overwidth, ldcore->config.overheight, (bitmap_format)ldcore->config.overformat);
1418
ldcore->overtex = device->machine().render().texture_alloc();
1419
if (ldcore->overtex == NULL)
1420
fatalerror("Out of memory allocating overlay texture");
1425
/*-------------------------------------------------
1426
init_audio - initialize the state of the
1428
-------------------------------------------------*/
1430
static void init_audio(device_t *device)
1432
laserdisc_state *ld = get_safe_token(device);
1433
ldcore_data *ldcore = ld->core;
1435
/* find the custom audio */
1436
ldcore->audiocustom = device->machine().device(ldcore->config.sound);
1438
/* allocate audio buffers */
1439
ldcore->audiomaxsamples = ((UINT64)ldcore->samplerate * 1000000 + ldcore->fps_times_1million - 1) / ldcore->fps_times_1million;
1440
ldcore->audiobufsize = ldcore->audiomaxsamples * 4;
1441
ldcore->audiobuffer[0] = auto_alloc_array(device->machine(), INT16, ldcore->audiobufsize);
1442
ldcore->audiobuffer[1] = auto_alloc_array(device->machine(), INT16, ldcore->audiobufsize);
1447
/***************************************************************************
1449
***************************************************************************/
1451
/*-------------------------------------------------
1452
device start callback
1453
-------------------------------------------------*/
1455
static DEVICE_START( laserdisc )
1457
const laserdisc_config *config = (const laserdisc_config *)downcast<const legacy_device_base *>(device)->inline_config();
1458
laserdisc_state *ld = get_safe_token(device);
1459
ldcore_data *ldcore;
1461
/* ensure that our screen is started first */
1462
ld->screen = downcast<screen_device *>(device->machine().device(config->screen));
1463
assert(ld->screen != NULL);
1464
if (!ld->screen->started())
1465
throw device_missing_dependencies();
1467
/* save a copy of the device pointer */
1468
ld->device = device;
1470
/* allocate memory for the core state */
1471
ld->core = auto_alloc_clear(device->machine(), ldcore_data);
1474
if (device->type() == PIONEER_PR8210)
1475
ldcore->intf = pr8210_interface;
1476
else if (device->type() == SIMUTREK_SPECIAL)
1477
ldcore->intf = simutrek_interface;
1478
else if (device->type() == PIONEER_LDV1000)
1479
ldcore->intf = ldv1000_interface;
1480
else if (device->type() == PHILLIPS_22VP931)
1481
ldcore->intf = vp931_interface;
1483
ld->player = (ldplayer_data *)auto_alloc_array_clear(device->machine(), UINT8, ldcore->intf.statesize);
1485
/* copy config data to the live state */
1486
ldcore->config = *config;
1487
if (ldcore->config.overclip.max_x == ldcore->config.overclip.min_x || ldcore->config.overclip.max_y == ldcore->config.overclip.min_y)
1489
ldcore->config.overclip.min_x = ldcore->config.overclip.min_y = 0;
1490
ldcore->config.overclip.max_x = ldcore->config.overwidth - 1;
1491
ldcore->config.overclip.max_y = ldcore->config.overheight - 1;
1493
if (ldcore->config.overscalex == 0)
1494
ldcore->config.overscalex = 1.0f;
1495
if (ldcore->config.overscaley == 0)
1496
ldcore->config.overscaley = 1.0f;
1498
/* initialize the various pieces */
1503
/* register callbacks */
1504
config_register(device->machine(), "laserdisc", config_saveload_delegate(FUNC(configuration_load), device), config_saveload_delegate(FUNC(configuration_save), device));
1508
/*-------------------------------------------------
1509
device exit callback
1510
-------------------------------------------------*/
1512
static DEVICE_STOP( laserdisc )
1514
laserdisc_state *ld = get_safe_token(device);
1515
ldcore_data *ldcore = ld->core;
1517
/* make sure all async operations have completed */
1518
if (ldcore->disc != NULL)
1519
chd_async_complete(ldcore->disc);
1521
/* free any textures and palettes */
1522
device->machine().render().texture_free(ldcore->videotex);
1523
if (ldcore->videopalette != NULL)
1524
palette_deref(ldcore->videopalette);
1525
device->machine().render().texture_free(ldcore->overtex);
1529
/*-------------------------------------------------
1530
device reset callback
1531
-------------------------------------------------*/
1533
static DEVICE_RESET( laserdisc )
1535
laserdisc_state *ld = get_safe_token(device);
1536
attotime curtime = device->machine().time();
1537
ldcore_data *ldcore = ld->core;
1540
/* attempt to wire up the audio */
1541
if (ldcore->audiocustom != NULL)
1543
sound_token *token = (sound_token *)downcast<legacy_device_base *>(ldcore->audiocustom)->token();
1545
token->stream->set_sample_rate(ldcore->samplerate);
1548
/* set up the general ld */
1549
ldcore->audiosquelch = 3;
1550
ldcore->videosquelch = 1;
1551
ldcore->fieldnum = 0;
1552
ldcore->curtrack = 1;
1553
ldcore->attospertrack = 0;
1554
ldcore->sliderupdate = curtime;
1556
/* reset the I/O lines */
1557
for (line = 0; line < LASERDISC_INPUT_LINES; line++)
1558
ldcore->linein[line] = CLEAR_LINE;
1559
for (line = 0; line < LASERDISC_OUTPUT_LINES; line++)
1560
ldcore->lineout[line] = CLEAR_LINE;
1562
/* call the initialization */
1563
if (ldcore->intf.init != NULL)
1564
(*ldcore->intf.init)(ld);
1568
/*-------------------------------------------------
1569
device set info callback
1570
-------------------------------------------------*/
1572
int laserdisc_get_type(device_t *device)
1574
if (device->type() == PIONEER_PR7820)
1575
return LASERDISC_TYPE_PIONEER_PR7820;
1576
if (device->type() == PIONEER_PR8210)
1577
return LASERDISC_TYPE_PIONEER_PR8210;
1578
if (device->type() == SIMUTREK_SPECIAL)
1579
return LASERDISC_TYPE_SIMUTREK_SPECIAL;
1580
if (device->type() == PIONEER_LDV1000)
1581
return LASERDISC_TYPE_PIONEER_LDV1000;
1582
if (device->type() == PHILLIPS_22VP931)
1583
return LASERDISC_TYPE_PHILLIPS_22VP931;
1584
if (device->type() == PHILLIPS_22VP932)
1585
return LASERDISC_TYPE_PHILLIPS_22VP932;
1586
if (device->type() == SONY_LDP1450)
1587
return LASERDISC_TYPE_SONY_LDP1450;
1588
return LASERDISC_TYPE_UNKNOWN;
1591
void laserdisc_set_type(device_t *device, int type)
1593
// this is no longer supported
1597
DEVICE_GET_INFO( laserdisc )
1601
/* --- the following bits of info are returned as 64-bit signed integers --- */
1602
case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(laserdisc_state); break;
1603
case DEVINFO_INT_INLINE_CONFIG_BYTES: info->i = sizeof(laserdisc_config); break;
1605
/* --- the following bits of info are returned as pointers to data or functions --- */
1606
case DEVINFO_FCT_START: info->start = DEVICE_START_NAME(laserdisc); break;
1607
case DEVINFO_FCT_STOP: info->stop = DEVICE_STOP_NAME(laserdisc); break;
1608
case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME(laserdisc); break;
1610
/* --- the following bits of info are returned as NULL-terminated strings --- */
1611
case DEVINFO_STR_FAMILY: strcpy(info->s, "Laserdisc Player"); break;
1612
case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break;
1613
case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break;
1614
case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break;
1618
DEVICE_GET_INFO( unkldplay )
1622
/* --- the following bits of info are returned as pointers --- */
1623
case DEVINFO_PTR_ROM_REGION: info->romregion = NULL; return;
1624
case DEVINFO_PTR_MACHINE_CONFIG: info->machine_config = NULL; return;
1625
/* --- the following bits of info are returned as NULL-terminated strings --- */
1626
case DEVINFO_STR_NAME: strcpy(info->s,"Unknown Laserdisc Player"); return;
1627
case DEVINFO_STR_SHORTNAME: strcpy(info->s,"unkldplay"); return;
1630
DEVICE_GET_INFO_CALL( laserdisc );
1633
DEVICE_GET_INFO( pioneer_pr7820 )
1635
DEVICE_GET_INFO_CALL( unkldplay );
1638
DEVICE_GET_INFO( pioneer_pr8210 )
1642
/* --- the following bits of info are returned as pointers --- */
1643
case DEVINFO_PTR_ROM_REGION: info->romregion = pr8210_interface.romregion; return;
1644
case DEVINFO_PTR_MACHINE_CONFIG: info->machine_config = pr8210_interface.machine_config; return;
1646
/* --- the following bits of info are returned as NULL-terminated strings -- */
1647
case DEVINFO_STR_NAME: strcpy(info->s, pr8210_interface.name); return;
1648
case DEVINFO_STR_SHORTNAME: strcpy(info->s, pr8210_interface.shortname); return;
1651
DEVICE_GET_INFO_CALL( laserdisc );
1654
DEVICE_GET_INFO( simutrek_special )
1658
/* --- the following bits of info are returned as pointers --- */
1659
case DEVINFO_PTR_ROM_REGION: info->romregion = simutrek_interface.romregion; return;
1660
case DEVINFO_PTR_MACHINE_CONFIG: info->machine_config = simutrek_interface.machine_config; return;
1662
/* --- the following bits of info are returned as NULL-terminated strings -- */
1663
case DEVINFO_STR_NAME: strcpy(info->s, simutrek_interface.name); return;
1664
case DEVINFO_STR_SHORTNAME: strcpy(info->s, simutrek_interface.shortname); return;
1667
DEVICE_GET_INFO_CALL( laserdisc );
1670
DEVICE_GET_INFO( pioneer_ldv1000 )
1674
/* --- the following bits of info are returned as pointers --- */
1675
case DEVINFO_PTR_ROM_REGION: info->romregion = ldv1000_interface.romregion; return;
1676
case DEVINFO_PTR_MACHINE_CONFIG: info->machine_config = ldv1000_interface.machine_config; return;
1678
/* --- the following bits of info are returned as NULL-terminated strings -- */
1679
case DEVINFO_STR_NAME: strcpy(info->s, ldv1000_interface.name); return;
1680
case DEVINFO_STR_SHORTNAME: strcpy(info->s, ldv1000_interface.shortname); return;
1683
DEVICE_GET_INFO_CALL( laserdisc );
1686
DEVICE_GET_INFO( phillips_22vp931 )
1690
/* --- the following bits of info are returned as pointers --- */
1691
case DEVINFO_PTR_ROM_REGION: info->romregion = vp931_interface.romregion; return;
1692
case DEVINFO_PTR_MACHINE_CONFIG: info->machine_config = vp931_interface.machine_config; return;
1694
/* --- the following bits of info are returned as NULL-terminated strings -- */
1695
case DEVINFO_STR_NAME: strcpy(info->s, vp931_interface.name); return;
1696
case DEVINFO_STR_SHORTNAME: strcpy(info->s, vp931_interface.shortname); return;
1699
DEVICE_GET_INFO_CALL( laserdisc );
1702
DEVICE_GET_INFO( phillips_22vp932 )
1704
DEVICE_GET_INFO_CALL( unkldplay );
1707
DEVICE_GET_INFO( sony_ldp1450 )
1709
DEVICE_GET_INFO_CALL( unkldplay );
1712
DEFINE_LEGACY_DEVICE(PIONEER_PR7820,pioneer_pr7820);
1713
DEFINE_LEGACY_DEVICE(PIONEER_PR8210,pioneer_pr8210);
1714
DEFINE_LEGACY_DEVICE(SIMUTREK_SPECIAL,simutrek_special);
1715
DEFINE_LEGACY_DEVICE(PIONEER_LDV1000,pioneer_ldv1000);
1716
DEFINE_LEGACY_DEVICE(PHILLIPS_22VP931,phillips_22vp931);
1717
DEFINE_LEGACY_DEVICE(PHILLIPS_22VP932,phillips_22vp932);
1718
DEFINE_LEGACY_DEVICE(SONY_LDP1450,sony_ldp1450);
1719
DEFINE_LEGACY_SOUND_DEVICE(LASERDISC_SOUND, laserdisc_sound);