From 1eb3c2d964342aef09a08d854bdab6716a6792bd Mon Sep 17 00:00:00 2001 From: Aaron Giles Date: Thu, 18 Sep 2008 15:17:42 +0000 Subject: [PATCH] Changed requirements for laserdisc CHDs to require a new chunk of metadata with pre-decoded frame information. Modified chdman to automatically produce this for CHDs that are of the appropriate parameters. To fix up existing CHDs, use chdman -fixavdata on the CHD. Modified the laserdisc core to leverage the pre-decoded frame metadata, which is now required. This improves seek times when searching and allows the player-specific emulation access to the VBI data as soon as it would really be available. Changed update callback timing to fire just before the first line of VBI data would be read; at that point, the frame selection is assumed to be committed. Converted PR-8210 emulation over to using the actual MCU from the laserdisc player. This MCU controls low-level functions such as slider position and laser on/off, and receives decoded vertical blanking data in order to make decisions. Removed old HLE behavior. Note that the overlay text is displayed via the UI; this is temporary and will be fixed shortly. Converted Simutrek-hacked laserdisc emulation to using the actual MCU from the game, which in turn hands off commands to the PR-8210 MCU. This is still not 100% but is pretty close at this point and achieves the correct behaviors in most cases. Fixed Cube Quest overlay scaling to cover the whole screen. Changed laserdisc video parameters to position the screen area at the bottom rather than the top, since this corresponds more closely to standard line numbering. Extended the vbiparse code to support pack/unpack, and to more fully document all the meanings of the VBI codes. Updated ldplayer to support slow/fast forward movement, frame/chapter display, and separate controls for scanning/stepping. Added new built-in variable "frame" to the debugger. Fixed device-based ROM loading to support loading ROMs from the game's ZIP as well. --- src/emu/cpu/mcs48/mcs48.c | 7 +- src/emu/cpu/mcs48/mcs48dsm.c | 4 +- src/emu/debug/debugcpu.c | 18 + src/emu/machine/laserdsc.c | 6 +- src/emu/machine/laserdsc.h | 9 +- src/emu/machine/ldcore.c | 255 +++--- src/emu/machine/ldcore.h | 34 +- src/emu/machine/ldpr8210.c | 1516 ++++++++++++++++------------------ src/emu/romload.c | 2 +- src/ldplayer/ldplayer.c | 190 +++-- src/lib/util/chd.h | 3 + src/lib/util/vbiparse.c | 45 +- src/lib/util/vbiparse.h | 46 ++ src/mame/drivers/cubeqst.c | 24 +- src/mame/drivers/gottlieb.c | 2 +- src/tools/chdman.c | 279 ++++++- 16 files changed, 1380 insertions(+), 1060 deletions(-) diff --git a/src/emu/cpu/mcs48/mcs48.c b/src/emu/cpu/mcs48/mcs48.c index 11017e76d33..a3d3a798ec9 100644 --- a/src/emu/cpu/mcs48/mcs48.c +++ b/src/emu/cpu/mcs48/mcs48.c @@ -1114,10 +1114,11 @@ static void mcs48_get_info(UINT32 state, cpuinfo *info) case CPUINFO_STR_CORE_CREDITS: strcpy(info->s, "Copyright Mirko Buffoni\nBased on the original work Copyright Dan Boris"); break; case CPUINFO_STR_FLAGS: - sprintf(info->s, "%c %c%c%c%c%c%c%c%c", - mcs48.a11 ? 'M':'.', + sprintf(info->s, "%c%c %c%c%c%c%c%c%c%c", + mcs48.irq_state ? 'I':'.', + mcs48.a11 ? 'M':'.', PSW & 0x80 ? 'C':'.', - PSW & 0x40 ? 'a':'.', + PSW & 0x40 ? 'A':'.', PSW & 0x20 ? 'F':'.', PSW & 0x10 ? 'B':'.', PSW & 0x08 ? '?':'.', diff --git a/src/emu/cpu/mcs48/mcs48dsm.c b/src/emu/cpu/mcs48/mcs48dsm.c index c26ccb7bd68..aad9f7304d2 100644 --- a/src/emu/cpu/mcs48/mcs48dsm.c +++ b/src/emu/cpu/mcs48/mcs48dsm.c @@ -278,8 +278,8 @@ offs_t mcs48_dasm(char *buffer, offs_t pc, const UINT8 *oprom, const UINT8 *opra cp++; switch (*cp++) { - case 'A': sprintf(num,"$%04X",a); break; - case 'J': sprintf(num,"$%04X",((pc+1) & 0xf00) | a); break; + case 'A': sprintf(num,"$%03X",a); break; + case 'J': sprintf(num,"$%03X",((pc+1) & 0xf00) | a); break; case 'B': sprintf(num,"%d",b); break; case 'D': sprintf(num,"%d",d); break; case 'X': sprintf(num,"%X",d); break; diff --git a/src/emu/debug/debugcpu.c b/src/emu/debug/debugcpu.c index d164e2ac0de..89f778df3c9 100644 --- a/src/emu/debug/debugcpu.c +++ b/src/emu/debug/debugcpu.c @@ -120,6 +120,7 @@ static UINT64 get_tempvar(void *ref); static UINT64 get_logunmap(void *ref); static UINT64 get_beamx(void *ref); static UINT64 get_beamy(void *ref); +static UINT64 get_frame(void *ref); static void set_tempvar(void *ref, UINT64 value); static void set_logunmap(void *ref, UINT64 value); static UINT64 get_current_pc(void *ref); @@ -200,6 +201,7 @@ void debug_cpu_init(running_machine *machine) symtable_add_register(global_symtable, "logunmapi", (void *)ADDRESS_SPACE_IO, get_logunmap, set_logunmap); symtable_add_register(global_symtable, "beamx", NULL, get_beamx, NULL); symtable_add_register(global_symtable, "beamy", NULL, get_beamy, NULL); + symtable_add_register(global_symtable, "frame", NULL, get_frame, NULL); /* add the temporary variables to the global symbol table */ for (regnum = 0; regnum < NUM_TEMP_VARIABLES; regnum++) @@ -2815,6 +2817,22 @@ static UINT64 get_beamy(void *ref) } +/*------------------------------------------------- + get_frame - get current frame number +-------------------------------------------------*/ + +static UINT64 get_frame(void *ref) +{ + UINT64 ret = 0; + const device_config *screen = device_list_find_by_index(Machine->config->devicelist, VIDEO_SCREEN, 0); + + if (screen != NULL) + ret = video_screen_get_frame_number(screen); + + return ret; +} + + /*------------------------------------------------- set_logunmap - setter callback for the logumap symbols diff --git a/src/emu/machine/laserdsc.c b/src/emu/machine/laserdsc.c index 980a99cc243..efa1a883b4a 100644 --- a/src/emu/machine/laserdsc.c +++ b/src/emu/machine/laserdsc.c @@ -1086,7 +1086,7 @@ UINT8 laserdisc_line_r(const device_config *device, UINT8 line) video frame -------------------------------------------------*/ -UINT32 laserdisc_get_video(const device_config *device, bitmap_t **bitmap) +int laserdisc_get_video(const device_config *device, bitmap_t **bitmap) { laserdisc_state *ld = get_safe_token(device); int frameindex; @@ -1100,12 +1100,12 @@ UINT32 laserdisc_get_video(const device_config *device, bitmap_t **bitmap) if (!video_active(ld) || ld->videofields[frameindex] < 2) { *bitmap = ld->emptyframe; - return 0; + return FALSE; } else { *bitmap = ld->videovisframe[frameindex]; - return ld->videoframenum[frameindex]; + return TRUE; } } diff --git a/src/emu/machine/laserdsc.h b/src/emu/machine/laserdsc.h index ac7954f9854..407e2f2a804 100644 --- a/src/emu/machine/laserdsc.h +++ b/src/emu/machine/laserdsc.h @@ -129,7 +129,7 @@ struct _laserdisc_config \ MDRV_SCREEN_ADD(_tag, RASTER) \ MDRV_SCREEN_FORMAT(_overlayformat) \ - MDRV_SCREEN_RAW_PARAMS(XTAL_14_31818MHz*2, 910, 0, 704, 525, 0, 480) \ + MDRV_SCREEN_RAW_PARAMS(XTAL_14_31818MHz*2, 910, 0, 704, 525, 44, 524) \ /* not correct yet; fix me... */ #define MDRV_LASERDISC_SCREEN_ADD_PAL(_tag, _format) \ @@ -157,8 +157,8 @@ extern const custom_sound_interface laserdisc_custom_interface; /* ----- core control and status ----- */ -/* get a bitmap for the current frame (and the frame number) */ -UINT32 laserdisc_get_video(const device_config *device, bitmap_t **bitmap); +/* get a bitmap for the current frame; return TRUE if valid or FALSE if video off */ +int laserdisc_get_video(const device_config *device, bitmap_t **bitmap); /* return the raw philips or white flag codes */ UINT32 laserdisc_get_field_code(const device_config *device, UINT8 code); @@ -189,9 +189,6 @@ void pr7820_set_slow_speed(const device_config *device, double frame_rate_scaler /* control the audio squelch of the Simutrek modified players */ void simutrek_set_audio_squelch(const device_config *device, int state); -/* set the callback */ -void simutrek_set_cmd_ack_callback(const device_config *device, void (*callback)(void)); - /* ----- video interface ----- */ diff --git a/src/emu/machine/ldcore.c b/src/emu/machine/ldcore.c index af72f34a781..cb9bda03622 100644 --- a/src/emu/machine/ldcore.c +++ b/src/emu/machine/ldcore.c @@ -23,7 +23,7 @@ DEBUGGING ***************************************************************************/ -#define LOG_POSITION(x) /*printf x*/ +#define LOG_SLIDER 0 @@ -42,6 +42,17 @@ TYPE DEFINITIONS ***************************************************************************/ +/* video frame data */ +typedef struct _frame_data frame_data; +struct _frame_data +{ + bitmap_t * bitmap; /* cached bitmap */ + bitmap_t * visbitmap; /* wrapper around bitmap with only visible lines */ + UINT8 numfields; /* number of fields in this frame */ + INT32 lastfield; /* last absolute field number */ +}; + + /* core-specific data */ struct _ldcore_data { @@ -51,11 +62,12 @@ struct _ldcore_data /* disc parameters */ chd_file * disc; /* handle to the disc itself */ + UINT8 * vbidata; /* pointer to precomputed VBI data */ int width; /* width of video */ int height; /* height of video */ UINT32 fps_times_1million; /* frame rate of video */ int samplerate; /* audio samplerate */ - UINT8 readpending; /* true if a read is pending */ + chd_error readresult; /* result of the most recent read */ UINT32 chdtracks; /* number of tracks in the CHD */ av_codec_decompress_config avconfig; /* decompression configuration */ @@ -69,10 +81,7 @@ struct _ldcore_data attotime sliderupdate; /* time of last slider update */ /* video data */ - bitmap_t * videoframe[3]; /* currently cached frames */ - bitmap_t * videovisframe[3]; /* wrapper around videoframe with only visible lines */ - UINT8 videofields[3]; /* number of fields in each frame */ - UINT32 videoframenum[3]; /* frame number contained in each frame */ + frame_data frame[3]; /* circular list of frames */ UINT8 videoindex; /* index of the current video buffer */ bitmap_t videotarget; /* fake target bitmap for decompression */ bitmap_t * emptyframe; /* blank frame */ @@ -88,8 +97,6 @@ struct _ldcore_data /* metadata */ vbi_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 */ /* I/O data */ UINT8 datain; /* current input data value */ @@ -122,9 +129,9 @@ struct _sound_token ***************************************************************************/ /* generic helper functions */ +static TIMER_CALLBACK( perform_player_update ); static void read_track_data(laserdisc_state *ld); static void process_track_data(const device_config *device); -static void fake_metadata(UINT32 track, UINT8 which, vbi_metadata *metadata); //static void render_display(UINT16 *videodata, UINT32 rowpixels, UINT32 width, int frame); static void *custom_start(int clock, const custom_sound_interface *config); static void custom_stream_callback(void *param, stream_sample_t **inputs, stream_sample_t **outputs, int samples); @@ -377,17 +384,35 @@ static void vblank_state_changed(const device_config *screen, void *param, int v /* update current track based on slider speed */ update_slider_pos(ldcore, curtime); - /* on rising edge, call the player-specific VSYNC function */ + /* on rising edge, process previously-read frame and inform the player */ if (vblank_state) { + /* call the player's VSYNC callback */ if (ldcore->intf.vsync != NULL) - (*ldcore->intf.vsync)(ld); - return; + (*ldcore->intf.vsync)(ld, &ldcore->metadata[ldcore->fieldnum], ldcore->fieldnum, curtime); + + /* set a timer to begin fetching the next frame just before the VBI data would be fetched */ + timer_set(video_screen_get_time_until_pos(screen, 16*2, 0), ld, 0, perform_player_update); } +} + + +/*------------------------------------------------- + vblank_state_changed - called on each state + change of the VBLANK signal +-------------------------------------------------*/ + +static TIMER_CALLBACK( perform_player_update ) +{ + laserdisc_state *ld = ptr; + ldcore_data *ldcore = ld->core; + attotime curtime = timer_get_time(); - /* on falling edge, do the bulk of the processing */ /* wait for previous read and decode to finish */ - process_track_data(device); + process_track_data(ld->device); + + /* update current track based on slider speed */ + update_slider_pos(ldcore, curtime); /* update the state */ if (ldcore->intf.update != NULL) @@ -501,30 +526,31 @@ UINT8 laserdisc_line_r(const device_config *device, UINT8 line) /*------------------------------------------------- laserdisc_get_video - return the current - video frame + video frame; return TRUE if valid or FALSE + if video off -------------------------------------------------*/ -UINT32 laserdisc_get_video(const device_config *device, bitmap_t **bitmap) +int laserdisc_get_video(const device_config *device, bitmap_t **bitmap) { laserdisc_state *ld = get_safe_token(device); ldcore_data *ldcore = ld->core; - int frameindex; + frame_data *frame; /* determine the most recent live set of frames */ - frameindex = ldcore->videoindex; - if (ldcore->videofields[frameindex] < 2) - frameindex = (frameindex + ARRAY_LENGTH(ldcore->videofields) - 1) % ARRAY_LENGTH(ldcore->videofields); + frame = &ldcore->frame[ldcore->videoindex]; + if (frame->numfields < 2) + frame = &ldcore->frame[(ldcore->videoindex + ARRAY_LENGTH(ldcore->frame) - 1) % ARRAY_LENGTH(ldcore->frame)]; /* if no video present, return the empty frame */ - if (ldcore->videosquelch || ldcore->videofields[frameindex] < 2) + if (ldcore->videosquelch || frame->numfields < 2) { *bitmap = ldcore->emptyframe; - return 0; + return FALSE; } else { - *bitmap = ldcore->videovisframe[frameindex]; - return ldcore->videoframenum[frameindex]; + *bitmap = frame->visbitmap; + return TRUE; } } @@ -538,11 +564,7 @@ UINT32 laserdisc_get_field_code(const device_config *device, UINT8 code) { laserdisc_state *ld = get_safe_token(device); ldcore_data *ldcore = ld->core; - int field = ldcore->fieldnum ^ 1; - - /* if no video present, return */ - if (ldcore->videosquelch) - return 0; + int field = ldcore->fieldnum; switch (code) { @@ -557,6 +579,9 @@ UINT32 laserdisc_get_field_code(const device_config *device, UINT8 code) case LASERDISC_CODE_LINE18: return ldcore->metadata[field].line18; + + case LASERDISC_CODE_LINE1718: + return ldcore->metadata[field].line1718; } return 0; @@ -626,7 +651,8 @@ void ldcore_set_slider_speed(laserdisc_state *ld, INT32 tracks_per_vsync) else ldcore->attospertrack = -attotime_to_attoseconds(attotime_div(vsyncperiod, -tracks_per_vsync)); -printf("Slider speed = %d\n", tracks_per_vsync); + if (LOG_SLIDER) + printf("Slider speed = %d\n", tracks_per_vsync); } @@ -641,7 +667,8 @@ void ldcore_advance_slider(laserdisc_state *ld, INT32 numtracks) update_slider_pos(ldcore, timer_get_time()); add_and_clamp_track(ldcore, numtracks); -printf("Advance by %d\n", numtracks); + if (LOG_SLIDER) + printf("Advance by %d\n", numtracks); } @@ -861,21 +888,38 @@ static void read_track_data(laserdisc_state *ld) ldcore_data *ldcore = ld->core; UINT32 tracknum = ldcore->curtrack; UINT32 fieldnum = ldcore->fieldnum; - chd_error err; + frame_data *frame; + UINT32 vbiframe; + UINT32 readhunk; + INT32 chdtrack; - /* if the previous field had the white flag, force the new field to pair with it */ - if (ldcore->metadata[fieldnum ^ 1].white) - ldcore->videofields[ldcore->videoindex] = 1; + /* if the previous field had a frame number, and the new field immediately follows it, + force the new field to pair with the previous one */ + frame = &ldcore->frame[ldcore->videoindex]; + if ((ldcore->metadata[fieldnum ^ 1].line1718 & VBI_MASK_CAV_PICTURE) == VBI_CODE_CAV_PICTURE && (tracknum * 2 + fieldnum == frame->lastfield + 1)) + frame->numfields = 1; + + /* otherwise, keep the frames in sync with the absolute field numbers */ + else if (frame->numfields == 2 && fieldnum != 0) + frame->numfields--; /* if we already have both fields on the current videoindex, advance */ - if (ldcore->videofields[ldcore->videoindex] >= 2) + else if (frame->numfields >= 2) { - ldcore->videoindex = (ldcore->videoindex + 1) % ARRAY_LENGTH(ldcore->videofields); - ldcore->videofields[ldcore->videoindex] = 0; + ldcore->videoindex = (ldcore->videoindex + 1) % ARRAY_LENGTH(ldcore->frame); + frame = &ldcore->frame[ldcore->videoindex]; + frame->numfields = 0; } + + /* if we're squelched, reset the frame counter */ + if (ldcore->videosquelch) + frame->numfields = 0; + + /* remember the last field number */ + frame->lastfield = tracknum * 2 + fieldnum; /* set the video target information */ - ldcore->videotarget = *ldcore->videoframe[ldcore->videoindex]; + ldcore->videotarget = *frame->bitmap; ldcore->videotarget.base = BITMAP_ADDR16(&ldcore->videotarget, fieldnum, 0); ldcore->videotarget.height /= 2; ldcore->videotarget.rowpixels *= 2; @@ -900,20 +944,23 @@ static void read_track_data(laserdisc_state *ld) ldcore->avconfig.actsamples = &ldcore->audiocursamples; ldcore->audiocursamples = 0; - /* configure the codec and then read */ - if (ldcore->disc != NULL) - { - err = chd_codec_config(ldcore->disc, AV_CODEC_DECOMPRESS_CONFIG, &ldcore->avconfig); - if (err == CHDERR_NONE) - { - INT32 chdtrack = tracknum - 1 - VIRTUAL_LEAD_IN_TRACKS; + /* compute the chdhunk number we are going to read */ + chdtrack = tracknum - 1 - VIRTUAL_LEAD_IN_TRACKS; + chdtrack = MAX(chdtrack, 0); + chdtrack = MIN(chdtrack, ldcore->chdtracks - 1); + readhunk = chdtrack * 2 + fieldnum; + + /* set the VBI data for the new field from our precomputed data */ + if (ldcore->vbidata != NULL) + vbi_metadata_unpack(&ldcore->metadata[fieldnum], &vbiframe, &ldcore->vbidata[readhunk * VBI_PACKED_BYTES]); - /* clamp the track */ - chdtrack = MAX(chdtrack, 0); - chdtrack = MIN(chdtrack, ldcore->chdtracks - 1); - err = chd_read_async(ldcore->disc, chdtrack * 2 + fieldnum, NULL); - ldcore->readpending = TRUE; - } + /* configure the codec and then read */ + ldcore->readresult = CHDERR_FILE_NOT_FOUND; + if (ldcore->disc != NULL && !ldcore->videosquelch) + { + ldcore->readresult = chd_codec_config(ldcore->disc, AV_CODEC_DECOMPRESS_CONFIG, &ldcore->avconfig); + if (ldcore->readresult == CHDERR_NONE) + ldcore->readresult = chd_read_async(ldcore->disc, readhunk, NULL); } } @@ -927,47 +974,23 @@ static void process_track_data(const device_config *device) { laserdisc_state *ld = get_safe_token(device); ldcore_data *ldcore = ld->core; - UINT32 tracknum = ldcore->curtrack; - UINT32 fieldnum = ldcore->fieldnum; - int frame, chapter; - chd_error chderr; /* wait for the async operation to complete */ - if (ldcore->disc != NULL && ldcore->readpending) - { - /* complete the async operation */ - chderr = chd_async_complete(ldcore->disc); - if (chderr != CHDERR_NONE && chderr != CHDERR_NO_ASYNC_OPERATION) - ldcore->avconfig.video = NULL; - } - ldcore->readpending = FALSE; - - /* parse the metadata */ - if (ldcore->disc != NULL && ldcore->avconfig.video != NULL) - vbi_parse_all((const UINT16 *)ldcore->avconfig.video->base, ldcore->avconfig.video->rowpixels, ldcore->avconfig.video->width, 8, &ldcore->metadata[fieldnum]); - else - fake_metadata(tracknum, fieldnum, &ldcore->metadata[fieldnum]); -// printf("Track %5d: Metadata = %d %08X %08X %08X %08X\n", tracknum, ldcore->metadata[fieldnum].white, ldcore->metadata[fieldnum].line16, ldcore->metadata[fieldnum].line17, ldcore->metadata[fieldnum].line18, ldcore->metadata[fieldnum].line1718); - - /* update the last seen frame and chapter */ - frame = frame_from_metadata(&ldcore->metadata[fieldnum]); - if (frame >= 1 && frame < 99999) - ldcore->last_frame = frame; - chapter = chapter_from_metadata(&ldcore->metadata[fieldnum]); - if (chapter != -1) - ldcore->last_chapter = chapter; + if (ldcore->readresult == CHDERR_OPERATION_PENDING) + ldcore->readresult = chd_async_complete(ldcore->disc); + /* remove the video if we had an error */ + if (ldcore->readresult != CHDERR_NONE) + ldcore->avconfig.video = NULL; + + /* count the field as read if we are successful */ + if (ldcore->avconfig.video != NULL) + ldcore->frame[ldcore->videoindex].numfields++; + /* render the display if present */ // if (ldcore->display && ldcore->avconfig.video != NULL) // render_display((UINT16 *)ldcore->avconfig.video->base, ldcore->avconfig.video->rowpixels, ldcore->avconfig.video->width, ldcore->last_frame); - /* update video field */ - if (ldcore->avconfig.video != NULL) - { - ldcore->videofields[ldcore->videoindex]++; - ldcore->videoframenum[ldcore->videoindex] = ldcore->last_frame; - } - /* pass the audio to the callback */ if (ldcore->config.audio != NULL) (*ldcore->config.audio)(device, ldcore->samplerate, ldcore->audiocursamples, ldcore->avconfig.audio[0], ldcore->avconfig.audio[1]); @@ -999,29 +1022,6 @@ static void process_track_data(const device_config *device) } -/*------------------------------------------------- - fake_metadata - fake metadata when there's - no disc present --------------------------------------------------*/ - -static void fake_metadata(UINT32 track, UINT8 which, vbi_metadata *metadata) -{ - if (which == 0) - { - metadata->white = 1; - metadata->line16 = 0; - metadata->line17 = metadata->line18 = 0xf80000 | - (((track / 10000) % 10) << 16) | - (((track / 1000) % 10) << 12) | - (((track / 100) % 10) << 8) | - (((track / 10) % 10) << 4) | - (((track / 1) % 10) << 0); - } - else - memset(metadata, 0, sizeof(*metadata)); -} - - /*------------------------------------------------- render_display - draw the frame display -------------------------------------------------*/ @@ -1321,6 +1321,7 @@ VIDEO_UPDATE( laserdisc ) const device_config *laserdisc = device_list_first(screen->machine->config->devicelist, LASERDISC); if (laserdisc != NULL) { + const rectangle *visarea = video_screen_get_visible_area(screen); laserdisc_state *ld = laserdisc->token; ldcore_data *ldcore = ld->core; bitmap_t *overbitmap = ldcore->overbitmap[ldcore->overindex]; @@ -1335,12 +1336,14 @@ VIDEO_UPDATE( laserdisc ) clip.min_x = 0; clip.max_x = ldcore->config.overwidth - 1; clip.min_y = cliprect->min_y * overbitmap->height / bitmap->height; + if (cliprect->min_y == visarea->min_y) + clip.min_y = MIN(clip.min_y, ldcore->config.overclip.min_y); clip.max_y = (cliprect->max_y + 1) * overbitmap->height / bitmap->height - 1; (*ldcore->config.overupdate)(screen, overbitmap, &clip); } /* if this is the last update, do the rendering */ - if (cliprect->max_y == video_screen_get_visible_area(screen)->max_y) + if (cliprect->max_y == visarea->max_y) { float x0, y0, x1, y1; @@ -1443,8 +1446,10 @@ static void init_disc(const device_config *device) ldcore->maxtrack = VIRTUAL_LEAD_IN_TRACKS + MAX_TOTAL_TRACKS + VIRTUAL_LEAD_OUT_TRACKS; if (ldcore->disc != NULL) { + UINT32 totalhunks = chd_get_header(ldcore->disc)->totalhunks; int fps, fpsfrac, interlaced, channels; char metadata[256]; + UINT32 vbilength; /* require the A/V codec */ if (chd_get_header(ldcore->disc)->compression != CHDCOMPRESSION_AV) @@ -1466,7 +1471,13 @@ static void init_disc(const device_config *device) fatalerror("Laserdisc video must be interlaced!"); /* determine the maximum track and allocate a frame buffer */ - ldcore->chdtracks = chd_get_header(ldcore->disc)->totalhunks / 2; + ldcore->chdtracks = totalhunks / 2; + + /* allocate memory for the precomputed per-frame metadata */ + ldcore->vbidata = auto_malloc(totalhunks * VBI_PACKED_BYTES); + err = chd_get_metadata(ldcore->disc, AV_LD_METADATA_TAG, 0, ldcore->vbidata, totalhunks * VBI_PACKED_BYTES, &vbilength, NULL); + if (err != CHDERR_NONE || vbilength != totalhunks * VBI_PACKED_BYTES) + fatalerror("Precomputed VBI metadata missing or incorrect size"); } ldcore->maxtrack = MAX(ldcore->maxtrack, VIRTUAL_LEAD_IN_TRACKS + VIRTUAL_LEAD_OUT_TRACKS + ldcore->chdtracks); } @@ -1487,18 +1498,20 @@ static void init_video(const device_config *device) video_screen_register_vblank_callback(ld->screen, vblank_state_changed, (void *)device); /* allocate video frames */ - for (index = 0; index < ARRAY_LENGTH(ldcore->videofields); index++) + for (index = 0; index < ARRAY_LENGTH(ldcore->frame); index++) { + frame_data *frame = &ldcore->frame[index]; + /* first allocate a YUY16 bitmap at 2x the height */ - ldcore->videoframe[index] = auto_bitmap_alloc(ldcore->width, ldcore->height * 2, BITMAP_FORMAT_YUY16); - fillbitmap_yuy16(ldcore->videoframe[index], 40, 109, 240); + frame->bitmap = auto_bitmap_alloc(ldcore->width, ldcore->height * 2, BITMAP_FORMAT_YUY16); + fillbitmap_yuy16(frame->bitmap, 40, 109, 240); /* make a copy of the bitmap that clips out the VBI and horizontal blanking areas */ - ldcore->videovisframe[index] = auto_malloc(sizeof(*ldcore->videovisframe[index])); - *ldcore->videovisframe[index] = *ldcore->videoframe[index]; - ldcore->videovisframe[index]->base = BITMAP_ADDR16(ldcore->videovisframe[index], 44, ldcore->videoframe[index]->width * 8 / 720); - ldcore->videovisframe[index]->height -= 44; - ldcore->videovisframe[index]->width -= 2 * ldcore->videoframe[index]->width * 8 / 720; + frame->visbitmap = auto_malloc(sizeof(*frame->visbitmap)); + *frame->visbitmap = *frame->bitmap; + frame->visbitmap->base = BITMAP_ADDR16(frame->visbitmap, 44, frame->bitmap->width * 8 / 720); + frame->visbitmap->height -= 44; + frame->visbitmap->width -= 2 * frame->bitmap->width * 8 / 720; } /* allocate an empty frame of the same size */ @@ -1674,10 +1687,6 @@ static DEVICE_RESET( laserdisc ) ldcore->attospertrack = 0; ldcore->sliderupdate = curtime; - /* reset metadata */ - ldcore->last_frame = 0; - ldcore->last_chapter = 0; - /* reset the I/O lines */ for (line = 0; line < LASERDISC_INPUT_LINES; line++) ldcore->linein[line] = CLEAR_LINE; diff --git a/src/emu/machine/ldcore.h b/src/emu/machine/ldcore.h index 8b4c4d366de..8a8521e3947 100644 --- a/src/emu/machine/ldcore.h +++ b/src/emu/machine/ldcore.h @@ -134,8 +134,9 @@ struct _laserdisc_state /* player-specific callbacks */ typedef void (*laserdisc_init_func)(laserdisc_state *ld); -typedef void (*laserdisc_vsync_func)(laserdisc_state *ld); +typedef void (*laserdisc_vsync_func)(laserdisc_state *ld, const vbi_metadata *vbi, int fieldnum, attotime curtime); typedef INT32 (*laserdisc_update_func)(laserdisc_state *ld, const vbi_metadata *vbi, int fieldnum, attotime curtime); +typedef void (*laserdisc_overlay_func)(laserdisc_state *ld, bitmap_t *bitmap); typedef void (*laserdisc_w_func)(laserdisc_state *ld, UINT8 prev, UINT8 new); typedef UINT8 (*laserdisc_r_func)(laserdisc_state *ld); @@ -151,7 +152,8 @@ struct _ldplayer_interface const machine_config_token *machine_config; /* pointer to machine configuration */ laserdisc_init_func init; /* initialization callback */ laserdisc_vsync_func vsync; /* vsync begin callback */ - laserdisc_update_func update; /* update callback (vblank end) */ + laserdisc_update_func update; /* update callback (line 16) */ + laserdisc_overlay_func overlay; /* overlay callback */ laserdisc_w_func writedata; /* parallel data write */ laserdisc_w_func writeline[LASERDISC_INPUT_LINES]; /* single line write */ laserdisc_r_func readdata; /* parallel data read */ @@ -234,18 +236,13 @@ INLINE int is_start_of_frame(const vbi_metadata *vbi) INLINE int frame_from_metadata(const vbi_metadata *metadata) { - UINT32 data; - - if ((metadata->line1718 & 0xf80000) == 0xf80000) - data = metadata->line1718 & 0x7ffff; - else if (metadata->line1718 == 0x88ffff) + if ((metadata->line1718 & VBI_MASK_CAV_PICTURE) == VBI_CODE_CAV_PICTURE) + return VBI_CAV_PICTURE(metadata->line1718); + else if (metadata->line1718 == VBI_CODE_LEADIN) return FRAME_LEAD_IN; - else if (metadata->line1718 == 0x80eeee) + else if (metadata->line1718 == VBI_CODE_LEADOUT) return FRAME_LEAD_OUT; - else - return FRAME_NOT_PRESENT; - - return (((data >> 16) & 0x0f) * 10000) + (((data >> 12) & 0x0f) * 1000) + (((data >> 8) & 0x0f) * 100) + (((data >> 4) & 0x0f) * 10) + (data & 0x0f); + return FRAME_NOT_PRESENT; } @@ -257,18 +254,13 @@ INLINE int frame_from_metadata(const vbi_metadata *metadata) INLINE int chapter_from_metadata(const vbi_metadata *metadata) { - UINT32 data; - if ((metadata->line1718 & 0xf00fff) == 0x800ddd) - data = metadata->line1718 & 0x7f000; - else if (metadata->line1718 == 0x88ffff) + return VBI_CHAPTER(metadata->line1718); + else if (metadata->line1718 == VBI_CODE_LEADIN) return CHAPTER_LEAD_IN; - else if (metadata->line1718 == 0x80eeee) + else if (metadata->line1718 == VBI_CODE_LEADOUT) return CHAPTER_LEAD_OUT; - else - return CHAPTER_NOT_PRESENT; - - return (((data >> 16) & 0x0f) * 10) + ((data >> 12) & 0x0f); + return CHAPTER_NOT_PRESENT; } diff --git a/src/emu/machine/ldpr8210.c b/src/emu/machine/ldpr8210.c index a9c890a77b2..b4f6bb97cfa 100644 --- a/src/emu/machine/ldpr8210.c +++ b/src/emu/machine/ldpr8210.c @@ -7,6 +7,16 @@ Copyright Nicola Salmoria and the MAME Team. Visit http://mamedev.org for licensing and usage restrictions. +************************************************************************** + + Still to do: + + * add overlay properly (need capture) + * implement SLOW TRG + * figure out Simutrek without jump hack + * figure out serial protocol issues (current hack works nicely) + * determine actual slow/fast speeds + *************************************************************************/ #include "ldcore.h" @@ -18,10 +28,9 @@ DEBUGGING ***************************************************************************/ -#define EMULATE_PR8210_ROM 0 - -#define PRINTF_COMMANDS 1 -#define CMDPRINTF(x) do { if (PRINTF_COMMANDS) mame_printf_debug x; } while (0) +#define LOG_VBLANK_VBI 0 +#define LOG_SERIAL 0 +#define LOG_SIMUTREK 0 @@ -29,23 +38,17 @@ CONSTANTS ***************************************************************************/ -/* Player-specific states */ -enum -{ - LDSTATE_STEPPING_BY_PARAMETER = LDSTATE_OTHER, - LDSTATE_STEPPING_BY_PARAMETER_PAUSED -}; - /* Pioneer PR-8210 specific information */ -#define PR8210_SCAN_SPEED (2000 / 30) /* 2000 frames/second */ -#define PR8210_SCAN_DURATION (10) /* scan for 10 vsyncs each time */ -#define PR8210_SLOW_SPEED (5) /* 1/5x normal speed */ -#define PR8210_FAST_SPEED (3) /* 3x normal speed */ -#define PR8210_JUMP_REV_SPEED (15) /* 15x normal speed */ -#define PR8210_SEEK_FAST_SPEED (1000) /* 1000x normal speed */ +#define PR8210_SCAN_SPEED (2000 / 30) /* 2000 frames/second */ +#define PR8210_SEEK_FAST_SPEED (4000 / 30) /* 4000 frames/second */ -/* Us vs. Them requires at least this much "non-video" time in order to work */ -#define PR8210_MINIMUM_SEEK_TIME ATTOTIME_IN_MSEC(150) +/* serial timing, mostly from the service manual, derived from the XTAL */ +#define PR8210_SERIAL_CLOCK XTAL_455kHz +#define PR8210_0_BIT_TIME ATTOTIME_IN_HZ(PR8210_SERIAL_CLOCK / 512) +#define PR8210_1_BIT_TIME ATTOTIME_IN_HZ(PR8210_SERIAL_CLOCK / 1024) +#define PR8210_MIDPOINT_TIME ATTOTIME_IN_HZ(PR8210_SERIAL_CLOCK / 600) +#define PR8210_MAX_WORD_TIME ATTOTIME_IN_HZ(PR8210_SERIAL_CLOCK / 11520) +#define PR8210_REJECT_DUPLICATE_TIME ATTOTIME_IN_HZ(PR8210_SERIAL_CLOCK / 11520 / 4) @@ -53,35 +56,55 @@ enum TYPE DEFINITIONS ***************************************************************************/ +/* PIA data */ +typedef struct _pioneer_pia pioneer_pia; +struct _pioneer_pia +{ + UINT8 frame[7]; /* (20-26) 7 characters for the chapter/frame */ + UINT8 text[15]; /* (22-30) 15 characters for the display */ + UINT8 control; /* (40) control lines */ + UINT8 latchdisplay; /* flag: set if the display was latched */ + UINT8 portb; /* (60) port B value (LEDs) */ + UINT8 display; /* (80) display enable */ + UINT8 porta; /* (A0) port A value (from serial decoder) */ + UINT8 vbi1; /* (C0) VBI decoding state 1 */ + UINT8 vbi2; /* (E0) VBI decoding state 2 */ +}; + + +/* Simutrek-specific data */ +typedef struct _simutrek_data simutrek_data; +struct _simutrek_data +{ + int cpunum; /* CPU index of the 8748 */ + UINT8 audio_squelch; /* audio squelch value */ + UINT8 data; /* parallel data for simutrek */ + UINT8 data_ready; /* ready flag for simutrek data */ + UINT8 port2; /* 8748 port 2 state */ + UINT8 jumphack; +}; + + /* player-specific data */ struct _ldplayer_data { - /* general modes and states */ - UINT8 audio1disable; /* explicit disable for audio 1 */ - UINT8 audio2disable; /* explicit disable for audio 2 */ - UINT8 framedisplay; /* frame display */ - INT32 parameter; /* parameter for numbers */ - /* serial/command interface processing */ - UINT8 lastcommand; /* last command byte received */ - UINT8 seekstate; /* state of the seek command */ + UINT8 lastcommand; /* last command seen */ UINT16 accumulator; /* bit accumulator */ + attotime lastcommandtime; /* time of the last command */ attotime lastbittime; /* time of last bit received */ attotime firstbittime; /* time of first bit in command */ - /* Simutrek-specific data */ - UINT8 cmdcnt; /* counter for multi-byte command */ - UINT8 cmdbytes[3]; /* storage for multi-byte command */ - void (*cmd_ack_callback)(void); /* callback to clear game command write flag */ - /* low-level emulation data */ - int cpunum; - UINT8 pia[0x100]; - UINT8 vsync; - UINT8 porta; - UINT8 portb; - UINT8 pia_porta; - UINT8 pia_portb; + int cpunum; /* CPU index of the 8049 */ + attotime slowtrg; /* time of the last SLOW TRG */ + pioneer_pia pia; /* PIA state */ + UINT8 vsync; /* live VSYNC state */ + UINT8 port1; /* 8049 port 1 state */ + UINT8 port2; /* 8049 port 2 state */ + + /* Simutrek-specific data */ + simutrek_data simutrek; /* Simutrek-specific data */ }; @@ -91,41 +114,106 @@ struct _ldplayer_data ***************************************************************************/ static void pr8210_init(laserdisc_state *ld); -static void pr8210_soft_reset(laserdisc_state *ld); -static void pr8210_update_squelch(laserdisc_state *ld); -static int pr8210_switch_state(laserdisc_state *ld, UINT8 newstate, INT32 stateparam); -static INT32 pr8210_hle_update(laserdisc_state *ld, const vbi_metadata *vbi, int fieldnum, attotime curtime); -#if (EMULATE_PR8210_ROM) -static void pr8210_vsync(laserdisc_state *ld); +static void pr8210_vsync(laserdisc_state *ld, const vbi_metadata *vbi, int fieldnum, attotime curtime); static INT32 pr8210_update(laserdisc_state *ld, const vbi_metadata *vbi, int fieldnum, attotime curtime); -#else -static void pr8210_hle_command(laserdisc_state *ld); -#endif +static void pr8210_overlay(laserdisc_state *ld, bitmap_t *bitmap); static void pr8210_control_w(laserdisc_state *ld, UINT8 prev, UINT8 data); +static TIMER_CALLBACK( vsync_off ); +static TIMER_CALLBACK( vbi_data_fetch ); +static READ8_HANDLER( pr8210_pia_r ); +static WRITE8_HANDLER( pr8210_pia_w ); +static READ8_HANDLER( pr8210_bus_r ); +static WRITE8_HANDLER( pr8210_port1_w ); +static WRITE8_HANDLER( pr8210_port2_w ); +static READ8_HANDLER( pr8210_t0_r ); +static READ8_HANDLER( pr8210_t1_r ); static void simutrek_init(laserdisc_state *ld); +static void simutrek_vsync(laserdisc_state *ld, const vbi_metadata *vbi, int fieldnum, attotime curtime); +static INT32 simutrek_update(laserdisc_state *ld, const vbi_metadata *vbi, int fieldnum, attotime curtime); +static UINT8 simutrek_ready_r(laserdisc_state *ld); static UINT8 simutrek_status_r(laserdisc_state *ld); static void simutrek_data_w(laserdisc_state *ld, UINT8 prev, UINT8 data); +static TIMER_CALLBACK( simutrek_latched_data_w ); +static READ8_HANDLER( simutrek_port2_r ); +static WRITE8_HANDLER( simutrek_port2_w ); +static READ8_HANDLER( simutrek_data_r ); +static READ8_HANDLER( simutrek_t0_r ); /*************************************************************************** - ROM AND MACHINE INTERFACES + INLINE FUNCTIONS ***************************************************************************/ -MACHINE_DRIVER_EXTERN( pr8210 ); +/*------------------------------------------------- + find_pr8210 - find our device; assumes there + is only one +-------------------------------------------------*/ + +INLINE laserdisc_state *find_pr8210(running_machine *machine) +{ + return ldcore_get_safe_token(device_list_first(machine->config->devicelist, LASERDISC)); +} + + +/*------------------------------------------------- + update_video_squelch - update the state of + the video squelch +-------------------------------------------------*/ + +INLINE void update_video_squelch(laserdisc_state *ld) +{ + ldplayer_data *player = ld->player; + ldcore_set_video_squelch(ld, (player->port1 & 0x20) != 0); +} + + +/*------------------------------------------------- + update_audio_squelch - update the state of + the audio squelch +-------------------------------------------------*/ + +INLINE void update_audio_squelch(laserdisc_state *ld) +{ + ldplayer_data *player = ld->player; + if (player->simutrek.cpunum == -1) + ldcore_set_audio_squelch(ld, (player->port1 & 0x40) || !(player->pia.portb & 0x01), (player->port1 & 0x40) || !(player->pia.portb & 0x02)); + else + ldcore_set_audio_squelch(ld, player->simutrek.audio_squelch, player->simutrek.audio_squelch); +} + + + +/*************************************************************************** + PR-8210 ROM AND MACHINE INTERFACES +***************************************************************************/ + +static ADDRESS_MAP_START( pr8210_portmap, ADDRESS_SPACE_IO, 8 ) + AM_RANGE(0x00, 0xff) AM_READWRITE(pr8210_pia_r, pr8210_pia_w) + AM_RANGE(MCS48_PORT_BUS, MCS48_PORT_BUS) AM_READ(pr8210_bus_r) + AM_RANGE(MCS48_PORT_P1, MCS48_PORT_P1) AM_WRITE(pr8210_port1_w) + AM_RANGE(MCS48_PORT_P2, MCS48_PORT_P2) AM_WRITE(pr8210_port2_w) + AM_RANGE(MCS48_PORT_T0, MCS48_PORT_T0) AM_READ(pr8210_t0_r) + AM_RANGE(MCS48_PORT_T1, MCS48_PORT_T1) AM_READ(pr8210_t1_r) +ADDRESS_MAP_END + + +static MACHINE_DRIVER_START( pr8210 ) + MDRV_CPU_ADD("pr8210", I8049, XTAL_4_41MHz) + MDRV_CPU_IO_MAP(pr8210_portmap,0) +MACHINE_DRIVER_END + -#if EMULATE_PR8210_ROM ROM_START( pr8210 ) ROM_REGION( 0x800, "pr8210", ROMREGION_LOADBYNAME ) ROM_LOAD( "pr-8210_mcu_ud6005a.bin", 0x000, 0x800, CRC(120fa83b) SHA1(b514326ca1f52d6d89056868f9d17eabd4e3f31d) ) ROM_END -#endif /*************************************************************************** - INTERFACES + PR-8210 PLAYER INTERFACE ***************************************************************************/ const ldplayer_interface pr8210_interface = @@ -133,19 +221,12 @@ const ldplayer_interface pr8210_interface = LASERDISC_TYPE_PIONEER_PR8210, /* type of the player */ sizeof(ldplayer_data), /* size of the state */ "Pioneer PR-8210", /* name of the player */ -#if EMULATE_PR8210_ROM rom_pr8210, /* pointer to ROM region information */ machine_config_pr8210, /* pointer to machine configuration */ pr8210_init, /* initialization callback */ pr8210_vsync, /* vsync callback */ pr8210_update, /* update callback */ -#else - NULL, /* pointer to ROM region information */ - NULL, /* pointer to machine configuration */ - pr8210_init, /* initialization callback */ - NULL, /* vsync callback */ - pr8210_hle_update, /* update callback */ -#endif + pr8210_overlay, /* overlay callback */ NULL, /* parallel data write */ { /* single line write: */ NULL, /* LASERDISC_LINE_ENTER */ @@ -161,62 +242,6 @@ const ldplayer_interface pr8210_interface = }; -const ldplayer_interface simutrek_interface = -{ - LASERDISC_TYPE_SIMUTREK_SPECIAL, /* type of the player */ - sizeof(ldplayer_data), /* size of the state */ - "Simutrek Modified PR-8210", /* name of the player */ - NULL, /* pointer to ROM region information */ - NULL, /* pointer to machine configuration */ - simutrek_init, /* initialization callback */ - NULL, /* vsync callback */ - pr8210_hle_update, /* update callback */ - simutrek_data_w, /* parallel data write */ - { /* single line write: */ - NULL, /* LASERDISC_LINE_ENTER */ - NULL /* LASERDISC_LINE_CONTROL */ - }, - NULL, /* parallel data read */ - { /* single line read: */ - NULL, /* LASERDISC_LINE_READY */ - simutrek_status_r, /* LASERDISC_LINE_STATUS */ - NULL, /* LASERDISC_LINE_COMMAND */ - NULL, /* LASERDISC_LINE_DATA_AVAIL */ - } -}; - - - -/*************************************************************************** - INLINE FUNCTIONS -***************************************************************************/ - -/*------------------------------------------------- - requires_state_save - returns TRUE if the - given state will return to the previous - state when done --------------------------------------------------*/ - -INLINE int requires_state_save(UINT8 state) -{ - return (state == LDSTATE_SCANNING); -} - - -/*------------------------------------------------- - requires_state_save - returns TRUE if the - given state will return to the previous - state when done --------------------------------------------------*/ - -INLINE void update_audio_squelch(laserdisc_state *ld) -{ - ldplayer_data *player = ld->player; - ldcore_set_audio_squelch(ld, (player->porta & 0x40) || !(player->pia_portb & 0x01), (player->porta & 0x40) || !(player->pia_portb & 0x02)); -} - - - /*************************************************************************** PIONEER PR-8210 IMPLEMENTATION @@ -230,227 +255,77 @@ INLINE void update_audio_squelch(laserdisc_state *ld) static void pr8210_init(laserdisc_state *ld) { astring *tempstring = astring_alloc(); + attotime curtime = timer_get_time(); + ldplayer_data *player = ld->player; /* find our CPU */ astring_printf(tempstring, "%s:%s", ld->device->tag, "pr8210"); - ld->player->cpunum = mame_find_cpu_index(ld->device->machine, astring_c(tempstring)); + player->cpunum = mame_find_cpu_index(ld->device->machine, astring_c(tempstring)); astring_free(tempstring); /* do a soft reset */ - pr8210_soft_reset(ld); -} - - -/*------------------------------------------------- - pr8210_soft_reset - Pioneer PR-8210-specific - soft reset --------------------------------------------------*/ - -static void pr8210_soft_reset(laserdisc_state *ld) -{ - ldplayer_data *player = ld->player; - attotime curtime = timer_get_time(); - - ld->state.state = LDSTATE_NONE; - ld->state.substate = 0; - ld->state.param = 0; - ld->state.endtime = curtime; - - player->audio1disable = 0; - player->audio2disable = 0; - player->parameter = 0; - player->lastcommand = 0; - player->seekstate = 0; player->accumulator = 0; + player->lastcommandtime = curtime; player->firstbittime = curtime; player->lastbittime = curtime; + player->slowtrg = curtime; - pr8210_switch_state(ld, LDSTATE_PARKED, 0); + /* we don't have the Simutrek player overrides */ + player->simutrek.cpunum = -1; + player->simutrek.audio_squelch = FALSE; } /*------------------------------------------------- - pr8210_update_squelch - update the squelch - settings based on the current state + pr8210_vsync - VSYNC callback, called at the + start of the blanking period -------------------------------------------------*/ -static void pr8210_update_squelch(laserdisc_state *ld) +static void pr8210_vsync(laserdisc_state *ld, const vbi_metadata *vbi, int fieldnum, attotime curtime) { ldplayer_data *player = ld->player; - switch (ld->state.state) + /* logging */ + if (LOG_VBLANK_VBI) { - /* video on, audio on */ - case LDSTATE_PLAYING: - ldcore_set_audio_squelch(ld, player->audio1disable, player->audio2disable); - ldcore_set_video_squelch(ld, FALSE); - break; - - /* video on, audio off */ - case LDSTATE_PAUSING: - case LDSTATE_PAUSED: - case LDSTATE_PLAYING_SLOW_REVERSE: - case LDSTATE_PLAYING_SLOW_FORWARD: - case LDSTATE_PLAYING_FAST_REVERSE: - case LDSTATE_PLAYING_FAST_FORWARD: - case LDSTATE_STEPPING_REVERSE: - case LDSTATE_STEPPING_FORWARD: - case LDSTATE_SCANNING: - ldcore_set_audio_squelch(ld, TRUE, TRUE); - ldcore_set_video_squelch(ld, FALSE); - break; - - /* video off, audio off */ - case LDSTATE_NONE: - case LDSTATE_EJECTED: - case LDSTATE_EJECTING: - case LDSTATE_PARKED: - case LDSTATE_LOADING: - case LDSTATE_SPINUP: - case LDSTATE_SEEKING: - ldcore_set_audio_squelch(ld, TRUE, TRUE); - ldcore_set_video_squelch(ld, TRUE); - break; - - /* Simutrek: stepping by parameter with explicit audio squelch controls */ - case LDSTATE_STEPPING_BY_PARAMETER: - case LDSTATE_STEPPING_BY_PARAMETER_PAUSED: - ldcore_set_video_squelch(ld, FALSE); - break; + if ((vbi->line1718 & VBI_MASK_CAV_PICTURE) == VBI_CODE_CAV_PICTURE) + printf("%3d:VSYNC(%d,%05d)\n", video_screen_get_vpos(ld->screen), fieldnum, VBI_CAV_PICTURE(vbi->line1718)); + else + printf("%3d:VSYNC(%d)\n", video_screen_get_vpos(ld->screen), fieldnum); } -} - -/*------------------------------------------------- - pr8210_switch_state - attempt to switch states - if appropriate, and ensure the squelch values - are correct --------------------------------------------------*/ - -static int pr8210_switch_state(laserdisc_state *ld, UINT8 newstate, INT32 parameter) -{ - attotime curtime = timer_get_time(); - - /* if this is the same state, ignore */ - if (ld->state.state == newstate) - return TRUE; - - /* if we're in a timed state, we ignore changes until the time elapses */ - if (attotime_compare(curtime, ld->state.endtime) < 0) - return FALSE; - - /* if moving to a state that requires it, save the old state */ - if (requires_state_save(newstate) && !requires_state_save(ld->state.state)) - ld->savestate = ld->state; - - /* set up appropriate squelch and timing */ - ld->state.state = newstate; - ld->state.substate = 0; - ld->state.param = parameter; - ld->state.endtime = curtime; - pr8210_update_squelch(ld); - - /* if we're switching to a timed state, set the appropriate timer */ - switch (ld->state.state) - { - case LDSTATE_EJECTING: - ld->state.endtime = attotime_add(curtime, GENERIC_EJECT_TIME); - break; - - case LDSTATE_LOADING: - ld->state.endtime = attotime_add(curtime, GENERIC_LOAD_TIME); - break; - - case LDSTATE_SPINUP: - ld->state.endtime = attotime_add(curtime, GENERIC_SPINUP_TIME); - break; - - case LDSTATE_SEEKING: - ld->state.endtime = attotime_add(curtime, PR8210_MINIMUM_SEEK_TIME); - break; - } - return TRUE; -} - - -/*------------------------------------------------- - pr8210_vsync - Start of VSYNC callback --------------------------------------------------*/ - -#if (EMULATE_PR8210_ROM) -static TIMER_CALLBACK( vsync_off ) -{ - ldplayer_data *player = ptr; - player->vsync = FALSE; -} - -static void pr8210_vsync(laserdisc_state *ld) -{ - ldplayer_data *player = ld->player; + /* signal VSYNC and set a timer to turn it off */ player->vsync = TRUE; - timer_set(attotime_mul(video_screen_get_scan_period(ld->screen), 4), player, 0, vsync_off); + timer_set(attotime_mul(video_screen_get_scan_period(ld->screen), 4), ld, 0, vsync_off); + + /* also set a timer to fetch the VBI data when it is ready */ + timer_set(video_screen_get_time_until_pos(ld->screen, 19*2, 0), ld, 0, vbi_data_fetch); } -#endif /*------------------------------------------------- - pr8210_update - Pioneer PR-8210-specific - update callback when using a ROM + pr8210_update - update callback, called on + the first visible line of the frame -------------------------------------------------*/ -#if (EMULATE_PR8210_ROM) - static INT32 pr8210_update(laserdisc_state *ld, const vbi_metadata *vbi, int fieldnum, attotime curtime) { ldplayer_data *player = ld->player; - UINT8 focus_on = !(player->porta & 0x08); - UINT8 laser_on = !(player->portb & 0x01); - UINT8 spdl_on = !(player->porta & 0x10); - INT32 advanceby = 0; - int frame, chapter; - - /* update PIA registers based on vbi code */ - frame = frame_from_metadata(vbi); - chapter = chapter_from_metadata(vbi); - if (focus_on && laser_on && !(player->pia[0x80] & 1)) - { - if (frame == FRAME_LEAD_IN) - player->pia[0xc0] = 0x10; /* or 0x12 */ - else if (frame == FRAME_LEAD_OUT) - player->pia[0xc0] = 0x11; - else if (frame != FRAME_NOT_PRESENT) - { - player->pia[0xc0] = 0x02; /* bit 0x02 must be set for forward scanning to work */ - player->pia[0x22] = 0xf0 | ((frame / 10000) % 10); - player->pia[0x23] = 0xf0 | ((frame / 1000) % 10); - player->pia[0x24] = 0xf0 | ((frame / 100) % 10); - player->pia[0x25] = 0xf0 | ((frame / 10) % 10); - player->pia[0x26] = 0xf0 | ((frame / 1) % 10); -printf("Frame:%05d\n", frame); - } - else if (chapter != CHAPTER_NOT_PRESENT) - { - player->pia[0xc0] = 0x13; - player->pia[0x20] = 0xf0 | ((chapter / 10) % 10); - player->pia[0x21] = 0xf0 | ((chapter / 1) % 10); - } -// else -// player->pia[0xc0] = 0x00; - } - player->pia[0xc0] |= 12;//4;//fieldnum << 2; - - if (spdl_on) - advanceby = fieldnum; + UINT8 spdl_on = !(player->port1 & 0x10); + + /* logging */ + if (LOG_VBLANK_VBI) + printf("%3d:Update(%d)\n", video_screen_get_vpos(ld->screen), fieldnum); /* update overlay */ - if ((player->pia[0x80] & 1) || player->framedisplay) + if (player->pia.display || player->pia.latchdisplay) { char buffer[16] = { 0 }; int i; for (i = 0; i < 15; i++) { - UINT8 c = player->pia[0x22 + i]; + UINT8 c = player->pia.text[i]; if (c >= 0xf0 && c <= 0xf9) c = '0' + (c - 0xf0); else if (c < 0x20 || c > 0x7f) @@ -459,251 +334,27 @@ printf("Frame:%05d\n", frame); } popmessage("%s", buffer); } - player->framedisplay = 0; + else + popmessage(NULL); + player->pia.latchdisplay = 0; -if (advanceby != 0) - printf("Advancing by %d\n", advanceby); - return advanceby; + /* if the spindle is on, we advance by 1 track after completing field #1 */ + return (spdl_on) ? fieldnum : 0; } -#endif /*------------------------------------------------- - pr8210_hle_update - Pioneer PR-8210-specific - update callback when using HLE + pr8210_overlay - overlay callback, called + during frame processing in update to overlay + player data -------------------------------------------------*/ -static INT32 pr8210_hle_update(laserdisc_state *ld, const vbi_metadata *vbi, int fieldnum, attotime curtime) +void pr8210_overlay(laserdisc_state *ld, bitmap_t *bitmap) { - ldplayer_state newstate; - INT32 advanceby = 0; - int frame; - - /* handle things based on the state */ - switch (ld->state.state) - { - case LDSTATE_EJECTING: - case LDSTATE_EJECTED: - case LDSTATE_PARKED: - case LDSTATE_LOADING: - case LDSTATE_SPINUP: - case LDSTATE_PAUSING: - case LDSTATE_PAUSED: - case LDSTATE_PLAYING: - case LDSTATE_PLAYING_SLOW_REVERSE: - case LDSTATE_PLAYING_SLOW_FORWARD: - case LDSTATE_PLAYING_FAST_REVERSE: - case LDSTATE_PLAYING_FAST_FORWARD: - case LDSTATE_STEPPING_REVERSE: - case LDSTATE_STEPPING_FORWARD: - case LDSTATE_SCANNING: - /* generic behaviors are appropriate for all of these commands */ - advanceby = ldcore_generic_update(ld, vbi, fieldnum, curtime, &newstate); - pr8210_switch_state(ld, newstate.state, newstate.param); - break; - - case LDSTATE_SEEKING: - /* if we're in the final state, look for a matching frame and pause there */ - frame = frame_from_metadata(vbi); - if (ld->state.substate == 3 && is_start_of_frame(vbi) && frame == ld->state.param) - pr8210_switch_state(ld, LDSTATE_PAUSED, fieldnum); - - /* otherwise, if we got frame data from the VBI, update our seeking logic */ - else if (ld->state.substate < 3 && frame != FRAME_NOT_PRESENT) - { - static const INT32 seekspeed[] = { -PR8210_SEEK_FAST_SPEED, PR8210_SEEK_FAST_SPEED, -PR8210_JUMP_REV_SPEED }; - INT32 curseekspeed = seekspeed[ld->state.substate]; - INT32 delta = (ld->state.param - 1) - frame; - - if ((curseekspeed < 0 && delta <= 0) || (curseekspeed > 0 && delta >= 0)) - advanceby = curseekspeed; - else - ld->state.substate++; - } - - /* otherwise, keep advancing until we know what's up */ - else if (fieldnum == 1) - advanceby = 1; - break; - - case LDSTATE_STEPPING_BY_PARAMETER: - /* advance after the second field of each frame */ - if (fieldnum == 1) - { - /* note that we switch directly to PAUSED as we are not looking for frames */ - advanceby = ld->state.param; - pr8210_switch_state(ld, LDSTATE_STEPPING_BY_PARAMETER_PAUSED, 0); - } - break; - - case LDSTATE_STEPPING_BY_PARAMETER_PAUSED: - /* generic pause behavior */ - ld->state.state = LDSTATE_PAUSED; - advanceby = ldcore_generic_update(ld, vbi, fieldnum, curtime, &newstate); - if (newstate.state != LDSTATE_PAUSED) - pr8210_switch_state(ld, newstate.state, newstate.param); - else - ld->state.state = LDSTATE_STEPPING_BY_PARAMETER_PAUSED; - break; - } - - return advanceby; +// ldplayer_data *player = ld->player; } - -/*------------------------------------------------- - pr8210_hle_command - Pioneer PR-8210-specific - command processing --------------------------------------------------*/ - -#if (!EMULATE_PR8210_ROM) -static void pr8210_hle_command(laserdisc_state *ld) -{ - ldplayer_data *player = ld->player; - UINT8 cmd = player->lastcommand; - - switch (cmd) - { - case 0x00: CMDPRINTF(("pr8210: EOC\n")); - /* EOC marker - can be safely ignored */ - break; - - case 0x01: CMDPRINTF(("pr8210: 0\n")); - player->parameter = (player->parameter == -1) ? 0 : (player->parameter * 10 + 0); - break; - - case 0x02: CMDPRINTF(("pr8210: Slow reverse\n")); - pr8210_switch_state(ld, LDSTATE_PLAYING_SLOW_REVERSE, PR8210_SLOW_SPEED); - break; - - case 0x03: CMDPRINTF(("pr8210: 8\n")); - player->parameter = (player->parameter == -1) ? 8 : (player->parameter * 10 + 8); - break; - - case 0x04: CMDPRINTF(("pr8210: Step forward\n")); - pr8210_switch_state(ld, LDSTATE_STEPPING_FORWARD, 0); - break; - - case 0x05: CMDPRINTF(("pr8210: 4\n")); - player->parameter = (player->parameter == -1) ? 4 : (player->parameter * 10 + 4); - break; - - case 0x06 : CMDPRINTF(("pr8210: Chapter\n")); - /* chapter -- not implemented */ - break; - - case 0x08: CMDPRINTF(("pr8210: Scan forward\n")); - if (ld->state.state != LDSTATE_SCANNING) - pr8210_switch_state(ld, LDSTATE_SCANNING, SCANNING_PARAM(PR8210_SCAN_SPEED, PR8210_SCAN_DURATION)); - else - ld->state.substate = 0; - break; - - case 0x09: CMDPRINTF(("pr8210: 2\n")); - player->parameter = (player->parameter == -1) ? 2 : (player->parameter * 10 + 2); - break; - - case 0x0a: CMDPRINTF(("pr8210: Pause\n")); - pr8210_switch_state(ld, LDSTATE_PAUSING, 0); - break; - - case 0x0b : CMDPRINTF(("pr8210: Frame\n")); - /* frame -- not implemented */ - break; - - case 0x0c: CMDPRINTF(("pr8210: Fast reverse\n")); - pr8210_switch_state(ld, LDSTATE_PLAYING_FAST_REVERSE, PR8210_FAST_SPEED); - break; - - case 0x0d: CMDPRINTF(("pr8210: 6\n")); - player->parameter = (player->parameter == -1) ? 6 : (player->parameter * 10 + 6); - break; - - case 0x0e: CMDPRINTF(("pr8210: Ch1 toggle\n")); - player->audio1disable = !player->audio1disable; - pr8210_update_squelch(ld); - break; - - case 0x10: CMDPRINTF(("pr8210: Fast forward\n")); - pr8210_switch_state(ld, LDSTATE_PLAYING_FAST_FORWARD, PR8210_FAST_SPEED); - break; - - case 0x11: CMDPRINTF(("pr8210: 1\n")); - player->parameter = (player->parameter == -1) ? 1 : (player->parameter * 10 + 1); - break; - - case 0x12: CMDPRINTF(("pr8210: Step reverse\n")); - pr8210_switch_state(ld, LDSTATE_STEPPING_REVERSE, 0); - break; - - case 0x13: CMDPRINTF(("pr8210: 9\n")); - player->parameter = (player->parameter == -1) ? 9 : (player->parameter * 10 + 9); - break; - - case 0x14: CMDPRINTF(("pr8210: Play\n")); - if (ld->state.state == LDSTATE_EJECTED) - pr8210_switch_state(ld, LDSTATE_LOADING, 0); - else if (ld->state.state == LDSTATE_PARKED) - pr8210_switch_state(ld, LDSTATE_SPINUP, 0); - else - pr8210_switch_state(ld, LDSTATE_PLAYING, 0); - break; - - case 0x15: CMDPRINTF(("pr8210: 5\n")); - player->parameter = (player->parameter == -1) ? 5 : (player->parameter * 10 + 5); - break; - - case 0x16: CMDPRINTF(("pr8210: Ch2 toggle\n")); - player->audio2disable = !player->audio2disable; - pr8210_update_squelch(ld); - break; - - case 0x18: CMDPRINTF(("pr8210: Slow forward\n")); - pr8210_switch_state(ld, LDSTATE_PLAYING_SLOW_FORWARD, PR8210_SLOW_SPEED); - break; - - case 0x19: CMDPRINTF(("pr8210: 3\n")); - player->parameter = (player->parameter == -1) ? 3 : (player->parameter * 10 + 3); - break; - - case 0x1a: CMDPRINTF(("pr8210: Seek\n")); - if (player->seekstate) - { - /* we're ready to seek */ - CMDPRINTF(("pr8210: Seeking to frame %d\n", player->parameter)); - pr8210_switch_state(ld, LDSTATE_SEEKING, player->parameter); - } - else - { - /* waiting for digits indicating position */ - player->parameter = 0; - } - player->seekstate ^= 1; - break; - - case 0x1c: CMDPRINTF(("pr8210: Scan reverse\n")); - if (ld->state.state != LDSTATE_SCANNING) - pr8210_switch_state(ld, LDSTATE_SCANNING, SCANNING_PARAM(-PR8210_SCAN_SPEED, PR8210_SCAN_DURATION)); - else - ld->state.substate = 0; - break; - - case 0x1d: CMDPRINTF(("pr8210: 7\n")); - player->parameter = (player->parameter == -1) ? 7 : (player->parameter * 10 + 7); - break; - - case 0x1e: CMDPRINTF(("pr8210: Reject\n")); - pr8210_switch_state(ld, LDSTATE_EJECTING, 0); - break; - - default: CMDPRINTF(("pr8210: Unknown command %02X\n", cmd)); - break; - } -} -#endif - - /*------------------------------------------------- pr8210_control_w - write callback when the CONTROL line is toggled @@ -713,19 +364,21 @@ static void pr8210_control_w(laserdisc_state *ld, UINT8 prev, UINT8 data) { ldplayer_data *player = ld->player; - if (data == ASSERT_LINE) + /* handle rising edge */ + if (prev != ASSERT_LINE && data == ASSERT_LINE) { attotime curtime = timer_get_time(); attotime delta; int longpulse; - /* if we timed out, reset the accumulator */ + /* if we timed out since the first bit, reset the accumulator */ delta = attotime_sub(curtime, player->firstbittime); - if (delta.attoseconds > ATTOTIME_IN_USEC(25320).attoseconds) + if (attotime_compare(delta, PR8210_MAX_WORD_TIME) > 0) { player->firstbittime = curtime; player->accumulator = 0x5555; -// printf("Reset accumulator\n"); + if (LOG_SERIAL) + printf("Reset accumulator\n"); } /* get the time difference from the last assert */ @@ -734,290 +387,245 @@ static void pr8210_control_w(laserdisc_state *ld, UINT8 prev, UINT8 data) player->lastbittime = curtime; /* 0 bit delta is 1.05 msec, 1 bit delta is 2.11 msec */ - longpulse = (delta.attoseconds < ATTOTIME_IN_USEC(1500).attoseconds) ? 0 : 1; + longpulse = (attotime_compare(delta, PR8210_MIDPOINT_TIME) < 0) ? 0 : 1; player->accumulator = (player->accumulator << 1) | longpulse; -#if 0 + /* log the deltas for debugging */ + if (LOG_SERIAL) { int usecdiff = (int)(delta.attoseconds / ATTOSECONDS_IN_USEC(1)); printf("bitdelta = %5d (%d) - accum = %04X\n", usecdiff, longpulse, player->accumulator); } -#endif /* if we have a complete command, signal it */ /* a complete command is 0,0,1 followed by 5 bits, followed by 0,0 */ if ((player->accumulator & 0x383) == 0x80) { UINT8 newcommand = (player->accumulator >> 2) & 0x1f; - -//printf("New command = %02X (last=%02X)\n", newcommand, player->lastcommand); - -#if EMULATE_PR8210_ROM - printf("Command = %02X\n", newcommand); -// player->pia_porta = BITSWAP8(newcommand, 4,3,2,1,0,5,6,7); - player->pia_porta = BITSWAP8(newcommand, 0,1,2,3,4,5,6,7); -#else - /* if we got a double command, act on it */ - if (newcommand == player->lastcommand) - { - pr8210_hle_command(ld); - player->lastcommand = 0; - } + attotime rejectuntil; + + /* data is stored to the PIA in bit-reverse order */ + player->pia.porta = BITSWAP8(newcommand, 0,1,2,3,4,5,6,7); + + /* the MCU logic requires a 0 to execute many commands; however, nobody + consistently sends a 0, whereas they do tend to send duplicate commands... + if we assume that each duplicate causes a 0, we get the correct results */ + rejectuntil = attotime_add(player->lastcommandtime, PR8210_REJECT_DUPLICATE_TIME); + player->lastcommandtime = curtime; + if (player->pia.porta == player->lastcommand && attotime_compare(curtime, rejectuntil) < 0) + player->pia.porta = 0x00; else - player->lastcommand = newcommand; -#endif + player->lastcommand = player->pia.porta; + + /* log the command and wait for a keypress */ + if (LOG_SERIAL) + { + printf("--- Command = %02X\n", player->pia.porta >> 3); + while (input_code_pressed(KEYCODE_ENTER)) ; + while (!input_code_pressed(KEYCODE_ENTER)) ; + } + + /* reset the first bit time so that the accumulator clears on the next write */ + player->firstbittime = attotime_sub(curtime, PR8210_MAX_WORD_TIME); } } } - -/*************************************************************************** - SIMUTREK MODIFIED PR-8210 PLAYER IMPLEMENTATION -***************************************************************************/ - /*------------------------------------------------- - - Command Set: - - FX XX XX : Seek to frame XXXXX - 01-19 : Skip forward 1-19 frames - 99-81 : Skip back 1-19 frames - 5a : Toggle frame display - + vsync_off - timer callback to clear the VSYNC + flag -------------------------------------------------*/ -/*------------------------------------------------- - simutrek_init - Simutrek-specific - initialization --------------------------------------------------*/ - -static void simutrek_init(laserdisc_state *ld) +static TIMER_CALLBACK( vsync_off ) { - /* do a soft reset */ - pr8210_soft_reset(ld); + laserdisc_state *ld = ptr; + ld->player->vsync = FALSE; } /*------------------------------------------------- - simutrek_status_r - Simutrek-specific - command processing + vbi_data_fetch - timer callback to update the + VBI data in the PIA as soon as it is ready; + this must happy early in the frame because + the player logic relies on fetching it here -------------------------------------------------*/ -static UINT8 simutrek_status_r(laserdisc_state *ld) -{ - return (ld->state.state != LDSTATE_SEEKING) ? ASSERT_LINE : CLEAR_LINE; -} - - -/*------------------------------------------------- - simutrek_data_w - Simutrek-specific - data processing --------------------------------------------------*/ - -static void simutrek_data_w(laserdisc_state *ld, UINT8 prev, UINT8 data) +static TIMER_CALLBACK( vbi_data_fetch ) { + laserdisc_state *ld = ptr; ldplayer_data *player = ld->player; + UINT8 focus_on = !(player->port1 & 0x08); + UINT8 laser_on = !(player->port2 & 0x01); + UINT32 line16 = laserdisc_get_field_code(ld->device, LASERDISC_CODE_LINE16); + UINT32 line1718 = laserdisc_get_field_code(ld->device, LASERDISC_CODE_LINE1718); - /* Acknowledge every command byte */ - if (player->cmd_ack_callback != NULL) - (*player->cmd_ack_callback)(); - - /* Is this byte part of a multi-byte seek command? */ - if (player->cmdcnt > 0) + /* logging */ + if (LOG_VBLANK_VBI) { - CMDPRINTF(("Simutrek: Seek to frame byte %d of 3\n", player->cmdcnt + 1)); - player->cmdbytes[player->cmdcnt++] = data; + if ((line1718 & VBI_MASK_CAV_PICTURE) == VBI_CODE_CAV_PICTURE) + printf("%3d:VBI(%05d)\n", video_screen_get_vpos(ld->screen), VBI_CAV_PICTURE(line1718)); + else + printf("%3d:VBI()\n", video_screen_get_vpos(ld->screen)); + } - if (player->cmdcnt == 3) + /* update PIA registers based on vbi code */ + player->pia.vbi1 = 0xff; + player->pia.vbi2 = 0xff; + if (focus_on && laser_on) + { + if (line1718 == VBI_CODE_LEADIN) + player->pia.vbi1 &= ~0x01; + if (line1718 == VBI_CODE_LEADOUT) + player->pia.vbi1 &= ~0x02; + if (line16 == VBI_CODE_STOP) + player->pia.vbi1 &= ~0x04; + /* unsure what this bit means: player->pia.vbi1 &= ~0x08; */ + if ((line1718 & VBI_MASK_CAV_PICTURE) == VBI_CODE_CAV_PICTURE) { - player->parameter = ((player->cmdbytes[0] & 0xf) * 10000) + - ((player->cmdbytes[1] >> 4) * 1000) + - ((player->cmdbytes[1] & 0xf) * 100) + - ((player->cmdbytes[2] >> 4) * 10) + - (player->cmdbytes[2] & 0xf); - - CMDPRINTF(("Simutrek: Seek to frame %d\n", player->parameter)); - - pr8210_switch_state(ld, LDSTATE_SEEKING, player->parameter); - player->cmdcnt = 0; + player->pia.vbi1 &= ~0x10; + player->pia.frame[2] = 0xf0 | ((line1718 >> 16) & 0x07); + player->pia.frame[3] = 0xf0 | ((line1718 >> 12) & 0x0f); + player->pia.frame[4] = 0xf0 | ((line1718 >> 8) & 0x0f); + player->pia.frame[5] = 0xf0 | ((line1718 >> 4) & 0x0f); + player->pia.frame[6] = 0xf0 | ((line1718 >> 0) & 0x0f); + } + if ((line1718 & VBI_MASK_CHAPTER) == VBI_CODE_CHAPTER) + { + player->pia.vbi2 &= ~0x01; + player->pia.frame[0] = 0xf0 | ((line1718 >> 16) & 0x07); + player->pia.frame[1] = 0xf0 | ((line1718 >> 12) & 0x0f); } } - else if (data == 0) - { - CMDPRINTF(("Simutrek: 0 ?\n")); - } - else if ((data & 0xf0) == 0xf0) - { - CMDPRINTF(("Simutrek: Seek to frame byte 1 of 3\n")); - player->cmdbytes[player->cmdcnt++] = data; - } - else if (data >= 1 && data <= 0x19) - { - int count = ((data >> 4) * 10) + (data & 0xf); - CMDPRINTF(("Simutrek: Step forwards by %d frame(s)\n", count)); - pr8210_switch_state(ld, LDSTATE_STEPPING_BY_PARAMETER, count); - } - else if (data >= 0x81 && data <= 0x99) - { - int count = (((data >> 4) * 10) + (data & 0xf)) - 100; - CMDPRINTF(("Simutrek: Step backwards by %d frame(s)\n", count)); - pr8210_switch_state(ld, LDSTATE_STEPPING_BY_PARAMETER, count); - } - else if (data == 0x5a) - { - CMDPRINTF(("Simutrek: Frame window toggle\n")); - player->framedisplay ^= 1; - } - else - { - CMDPRINTF(("Simutrek: Unknown command (%.2x)\n", data)); - } } /*------------------------------------------------- - simutrek_set_audio_squelch - Simutrek-specific - command to enable/disable audio squelch + pr8210_pia_r - handle reads from the mystery + Pioneer PIA -------------------------------------------------*/ -void simutrek_set_audio_squelch(const device_config *device, int state) -{ - laserdisc_state *ld = ldcore_get_safe_token(device); - int squelch = (state == 0); - if (squelch) - ldcore_set_audio_squelch(ld, squelch, squelch); - else - ldcore_set_audio_squelch(ld, squelch, squelch); -} - - -/*------------------------------------------------- - simutrek_set_audio_squelch - Simutrek-specific - command to set callback function for - player/interface command acknowledge --------------------------------------------------*/ - -void simutrek_set_cmd_ack_callback(const device_config *device, void (*callback)(void)) -{ - laserdisc_state *ld = ldcore_get_safe_token(device); - ldplayer_data *player = ld->player; - - player->cmd_ack_callback = callback; -} - - - -/*************************************************************************** - RE-IMPLEMENTATION USING ACTUAL ROMS -***************************************************************************/ - -/************************************* - * - * Test PR-8210 ROM emulation - * - *************************************/ - -static laserdisc_state *find_pr8210(running_machine *machine) -{ - return ldcore_get_safe_token(device_list_first(machine->config->devicelist, LASERDISC)); -} - - static READ8_HANDLER( pr8210_pia_r ) { laserdisc_state *ld = find_pr8210(machine); ldplayer_data *player = ld->player; - UINT8 result = player->pia[offset]; + UINT8 result = 0xff; + switch (offset) { - case 0x22: - case 0x23: - case 0x24: - case 0x25: - case 0x26: - case 0x27: - case 0xc0: - case 0xe0: + /* (20-26) 7 characters for the chapter/frame */ + case 0x20: case 0x21: + case 0x22: case 0x23: case 0x24: case 0x25: case 0x26: + result = player->pia.frame[offset - 0x20]; break; - + + /* (A0) port A value (from serial decoder) */ case 0xa0: -// printf("%03X:pia_r(%02X) = %02X\n", activecpu_get_pc(), offset, player->pia_porta); - result = player->pia_porta; -// player->pia_porta = 0; + result = player->pia.porta; + break; + + /* (C0) VBI decoding state 1 */ + case 0xc0: + if (LOG_VBLANK_VBI) + printf("%3d:PIA(C0)\n", video_screen_get_vpos(ld->screen)); + result = player->pia.vbi1; + break; + + /* (E0) VBI decoding state 2 */ + case 0xe0: + if (LOG_VBLANK_VBI) + printf("%3d:PIA(E0)\n", video_screen_get_vpos(ld->screen)); + result = player->pia.vbi2; break; default: - printf("%03X:pia_r(%02X)\n", activecpu_get_pc(), offset); + mame_printf_debug("%03X:Unknown PR-8210 PIA read from offset %02X\n", activecpu_get_pc(), offset); break; } return result; } + +/*------------------------------------------------- + pr8210_pia_w - handle writes to the mystery + Pioneer PIA +-------------------------------------------------*/ + static WRITE8_HANDLER( pr8210_pia_w ) { - /* - $22-26 (R) = read and copied to memory $23-27 - $23 (R) = something compared against $F4 - $22-26 (W) = SRCH. text - $27-2B (W) = FRAME/CHAP. text - $2C-30 (W) = frame or chapter number - $40 (W) = $CF at initialization, tracked by ($78) - $60 (W) = port B output, tracked by ($77) - $80 = n/c - $40 = (out) LED3 - $20 = (out) LED2 - $10 = (out) LED1 - 123 -> LHL = Play - -> HLL = Slow fwd - -> LLL = Slow rev - -> HHL = Still - -> LLH = Pause - -> HHH = all off - $08 = (out) CAV LED - $04 = (out) CLV LED - $02 = (out) A2 LED/AUDIO 2 - $01 = (out) A1 LED/AUDIO 1 - $80 (W) = 0 or 1 - $A0 (R) = port A input - $C0 (R) = stored to ($2E) - $E0 (R) = stored to ($2F) - */ laserdisc_state *ld = find_pr8210(machine); ldplayer_data *player = ld->player; - if (player->pia[offset] != data) + UINT8 value; + + switch (offset) { - switch (offset) - { - case 0x40: - if (!(data & 0x02) && (player->pia[offset] & 0x02)) - player->framedisplay = 1; - printf("%03X:pia_w(%02X) = %02X\n", activecpu_get_pc(), offset, data); - break; + /* (22-30) 15 characters for the display */ + case 0x22: case 0x23: case 0x24: case 0x25: case 0x26: + case 0x27: case 0x28: case 0x29: case 0x2a: case 0x2b: + case 0x2c: case 0x2d: case 0x2e: case 0x2f: case 0x30: + player->pia.text[offset - 0x22] = data; + break; + + /* (40) control lines */ + case 0x40: + + /* toggle bit 0 to latch chapter number into display area */ + if (!(data & 0x01) && (player->pia.control & 0x01)) + { + memcpy(&player->pia.text[0], &player->pia.frame[0], 2); + memset(&player->pia.text[3], 0, 10); + player->pia.latchdisplay = 1; + } - case 0x60: - printf("%03X:pia_w(%02X) = %02X (PORT B LEDS:", activecpu_get_pc(), offset, data); - output_set_value("pr8210_audio1", (data & 0x01) != 0); - output_set_value("pr8210_audio2", (data & 0x02) != 0); - output_set_value("pr8210_clv", (data & 0x04) != 0); - output_set_value("pr8210_cav", (data & 0x08) != 0); - output_set_value("pr8210_led1", (data & 0x10) == 0); - output_set_value("pr8210_led2", (data & 0x20) == 0); - output_set_value("pr8210_led3", (data & 0x40) == 0); - if (!(data & 0x80)) printf(" ???"); - printf(")\n"); - player->pia_portb = data; - update_audio_squelch(ld); - break; + /* toggle bit 1 to latch frame number into display area */ + if (!(data & 0x02) && (player->pia.control & 0x02)) + { + memcpy(&player->pia.text[0], &player->pia.frame[2], 5); + memset(&player->pia.text[5], 0, 10); + player->pia.latchdisplay = 1; + } + player->pia.control = data; + break; + + /* (60) port B value (LEDs) */ + case 0x60: + + /* these 4 are direct-connect */ + output_set_value("pr8210_audio1", (data & 0x01) != 0); + output_set_value("pr8210_audio2", (data & 0x02) != 0); + output_set_value("pr8210_clv", (data & 0x04) != 0); + output_set_value("pr8210_cav", (data & 0x08) != 0); + + /* remaining 3 bits select one of 5 LEDs via a mux */ + value = ((data & 0x40) >> 6) | ((data & 0x20) >> 4) | ((data & 0x10) >> 2); + output_set_value("pr8210_srev", (value == 0)); + output_set_value("pr8210_sfwd", (value == 1)); + output_set_value("pr8210_play", (value == 2)); + output_set_value("pr8210_step", (value == 3)); + output_set_value("pr8210_pause", (value == 4)); - default: - printf("%03X:pia_w(%02X) = %02X\n", activecpu_get_pc(), offset, data); - break; - } - player->pia[offset] = data; + player->pia.portb = data; + update_audio_squelch(ld); + break; + + /* (80) display enable */ + case 0x80: + player->pia.display = data & 0x01; + break; + + /* no other writes known */ + default: + mame_printf_debug("%03X:Unknown PR-8210 PIA write to offset %02X = %02X\n", activecpu_get_pc(), offset, data); + break; } } + +/*------------------------------------------------- + pr8210_bus_r - handle reads from the 8049 BUS + input, which is enabled via the PIA above +-------------------------------------------------*/ + static READ8_HANDLER( pr8210_bus_r ) { /* @@ -1033,8 +641,8 @@ static READ8_HANDLER( pr8210_bus_r ) laserdisc_state *ld = find_pr8210(machine); ldplayer_data *player = ld->player; slider_position sliderpos = ldcore_get_slider_position(ld); - UINT8 focus_on = !(player->porta & 0x08); - UINT8 spdl_on = !(player->porta & 0x10); + UINT8 focus_on = !(player->port1 & 0x08); + UINT8 spdl_on = !(player->port1 & 0x10); UINT8 result = 0x00; /* bus bit 6: slider position limit detector, inside and outside */ @@ -1052,12 +660,21 @@ static READ8_HANDLER( pr8210_bus_r ) /* bus bit 1: spindle motor stop detector */ if (!spdl_on) result |= 0x02; + + /* bus bit 0: SLOW TIMER OUT */ +// if (attotime_compare(attotime_sub(timer_get_time(), player->slowtrg), /* loop at beginning waits for $40=0, $02=1 */ return result; } -static WRITE8_HANDLER( pr8210_porta_w ) + +/*------------------------------------------------- + pr8210_port1_w - handle writes to the 8049 + port #1 +-------------------------------------------------*/ + +static WRITE8_HANDLER( pr8210_port1_w ) { /* $80 = (out) SCAN C (F/R) @@ -1071,35 +688,56 @@ static WRITE8_HANDLER( pr8210_porta_w ) */ laserdisc_state *ld = find_pr8210(machine); ldplayer_data *player = ld->player; - if ((data & 0xfe) != (player->porta & 0xfe)) + UINT8 prev = player->port1; + int direction; + + /* set the new value */ + player->port1 = data; + + /* bit 7 selects the direction of slider movement for JUMP TRG and scanning */ + direction = (data & 0x80) ? 1 : -1; + + /* on the falling edge of bit 0, jump one track in either direction */ + if (!(data & 0x01) && (prev & 0x01)) { - int direction = (data & 0x80) ? 1 : -1; - - printf("%03X:porta_w = %02X", activecpu_get_pc(), data); - if (!(data & 0x01) && (player->porta & 0x01)) - ldcore_advance_slider(ld, direction); - if (!(data & 0x02)) - printf(" SCAN:%c:%c", (data & 0x80) ? 'F' : 'R', (data & 0x04) ? 'L' : 'H'); - if (!(data & 0x08)) printf(" /FOCUSON"); - if (!(data & 0x10)) printf(" /SPDLON"); - if (data & 0x20) printf(" VIDEOSQ"); - if (data & 0x40) printf(" AUDIOSQ"); - printf("\n"); - player->porta = data; - - ldcore_set_video_squelch(ld, (data & 0x20) != 0); - update_audio_squelch(ld); - if (!(data & 0x02)) + /* special override for the Simutrek, which takes over control of this is some situations */ + if (player->simutrek.cpunum == -1 || (player->simutrek.port2 & 0x04) != 0 || player->simutrek.jumphack) { - int delta = (data & 0x04) ? PR8210_SCAN_SPEED : PR8210_SEEK_FAST_SPEED; - ldcore_set_slider_speed(ld, delta * direction); + if (LOG_SIMUTREK) + printf("%3d:JUMP TRG\n", video_screen_get_vpos(ld->screen)); + ldcore_advance_slider(ld, direction); + player->simutrek.jumphack = 0; } - else - ldcore_set_slider_speed(ld, 0); + else if (LOG_SIMUTREK) + printf("%3d:Skipped JUMP TRG\n", video_screen_get_vpos(ld->screen)); } + + /* bit 1 low enables scanning */ + if (!(data & 0x02)) + { + /* bit 2 selects the speed */ + int delta = (data & 0x04) ? PR8210_SCAN_SPEED : PR8210_SEEK_FAST_SPEED; + ldcore_set_slider_speed(ld, delta * direction); + } + + /* bit 1 high stops scanning */ + else + ldcore_set_slider_speed(ld, 0); + + /* video squelch is controlled by bit 5 */ + update_video_squelch(ld); + + /* audio squelch is controlled by bit 6 */ + update_audio_squelch(ld); } -static WRITE8_HANDLER( pr8210_portb_w ) + +/*------------------------------------------------- + pr8210_port2_w - handle writes to the 8049 + port #2 +-------------------------------------------------*/ + +static WRITE8_HANDLER( pr8210_port2_w ) { /* $80 = (out) /CS on PIA @@ -1113,24 +751,28 @@ static WRITE8_HANDLER( pr8210_portb_w ) */ laserdisc_state *ld = find_pr8210(machine); ldplayer_data *player = ld->player; - cpunum_set_input_line(machine, player->cpunum, 0, (data & 0x40) ? CLEAR_LINE : ASSERT_LINE); - if ((data & 0x7f) != (player->portb & 0x7f)) - { - printf("%03X:portb_w = %02X", activecpu_get_pc(), data); - if (!(data & 0x01)) printf(" LASERON"); - if (!(data & 0x02)) printf(" ???"); - if (!(data & 0x04)) printf(" TP1"); - if (!(data & 0x08)) printf(" TP2"); - output_set_value("pr8210_standby", !(data & 0x10)); - if (!(data & 0x20)) printf(" SLOWTRG"); - if (!(data & 0x40)) printf(" IRQGEN"); -// if (data & 0x80) printf(" PIASEL"); - printf("\n"); - player->portb = data; - } + UINT8 prev = player->port2; + + /* set the new value */ + player->port2 = data; + + /* on the falling edge of bit 5, start the slow timer */ + if (!(data & 0x20) && (prev & 0x20)) + player->slowtrg = timer_get_time(); + + /* bit 6 when low triggers an IRQ on the MCU */ + cpunum_set_input_line(machine, player->cpunum, MCS48_INPUT_IRQ, (data & 0x40) ? CLEAR_LINE : ASSERT_LINE); + + /* standby LED is set accordingl to bit 4 */ + output_set_value("pr8210_standby", (data & 0x10) != 0); } +/*------------------------------------------------- + pr8210_t0_r - return the state of the 8049 + T0 input (connected to VSYNC) +-------------------------------------------------*/ + static READ8_HANDLER( pr8210_t0_r ) { /* returns VSYNC state */ @@ -1139,30 +781,286 @@ static READ8_HANDLER( pr8210_t0_r ) } +/*------------------------------------------------- + pr8210_t1_r - return the state of the 8049 + T1 input (pulled high) +-------------------------------------------------*/ + static READ8_HANDLER( pr8210_t1_r ) { - /* must return 1 or else it tries to jump to an external ROM */ return 1; } -static ADDRESS_MAP_START( pr8210_map, ADDRESS_SPACE_PROGRAM, 8 ) - AM_RANGE(0x000, 0x7ff) AM_ROM + +/*************************************************************************** + SIMUTREK ROM AND MACHINE INTERFACES +***************************************************************************/ + +static ADDRESS_MAP_START( simutrek_portmap, ADDRESS_SPACE_IO, 8 ) + AM_RANGE(0x00, 0xff) AM_READ(simutrek_data_r) + AM_RANGE(MCS48_PORT_P2, MCS48_PORT_P2) AM_READWRITE(simutrek_port2_r, simutrek_port2_w) + AM_RANGE(MCS48_PORT_T0, MCS48_PORT_T0) AM_READ(simutrek_t0_r) ADDRESS_MAP_END -static ADDRESS_MAP_START( pr8210_portmap, ADDRESS_SPACE_IO, 8 ) - AM_RANGE(0x00, 0xff) AM_READWRITE(pr8210_pia_r, pr8210_pia_w) - AM_RANGE(MCS48_PORT_BUS, MCS48_PORT_BUS) AM_READ(pr8210_bus_r) - AM_RANGE(MCS48_PORT_P1, MCS48_PORT_P1) AM_WRITE(pr8210_porta_w) - AM_RANGE(MCS48_PORT_P2, MCS48_PORT_P2) AM_WRITE(pr8210_portb_w) - AM_RANGE(MCS48_PORT_T0, MCS48_PORT_T0) AM_READ(pr8210_t0_r) - AM_RANGE(MCS48_PORT_T1, MCS48_PORT_T1) AM_READ(pr8210_t1_r) -ADDRESS_MAP_END +MACHINE_DRIVER_START( simutrek ) + MDRV_CPU_ADD("simutrek", I8748, XTAL_6MHz) + MDRV_CPU_IO_MAP(simutrek_portmap,0) - -MACHINE_DRIVER_START( pr8210 ) - MDRV_CPU_ADD("pr8210", I8049, XTAL_4_41MHz) - MDRV_CPU_PROGRAM_MAP(pr8210_map,0) - MDRV_CPU_IO_MAP(pr8210_portmap,0) + MDRV_IMPORT_FROM(pr8210) MACHINE_DRIVER_END + + +ROM_START( simutrek ) + ROM_REGION( 0x800, "pr8210", ROMREGION_LOADBYNAME ) + ROM_LOAD( "pr-8210_mcu_ud6005a.bin", 0x000, 0x800, CRC(120fa83b) SHA1(b514326ca1f52d6d89056868f9d17eabd4e3f31d) ) + + ROM_REGION( 0x400, "simutrek", ROMREGION_LOADBYNAME) + ROM_LOAD( "laser_player_interface_d8748_a308.bin", 0x0000, 0x0400, CRC(eed3e728) SHA1(1eb3467f1c41553375b2c21952cd593b167f5416) ) +ROM_END + + + +/*************************************************************************** + SIMUTREK PLAYER INTERFACE +***************************************************************************/ + +const ldplayer_interface simutrek_interface = +{ + LASERDISC_TYPE_SIMUTREK_SPECIAL, /* type of the player */ + sizeof(ldplayer_data), /* size of the state */ + "Simutrek Modified PR-8210", /* name of the player */ + rom_simutrek, /* pointer to ROM region information */ + machine_config_simutrek, /* pointer to machine configuration */ + simutrek_init, /* initialization callback */ + simutrek_vsync, /* vsync callback */ + simutrek_update, /* update callback */ + pr8210_overlay, /* overlay callback */ + simutrek_data_w, /* parallel data write */ + { /* single line write: */ + NULL, /* LASERDISC_LINE_ENTER */ + NULL /* LASERDISC_LINE_CONTROL */ + }, + NULL, /* parallel data read */ + { /* single line read: */ + simutrek_ready_r, /* LASERDISC_LINE_READY */ + simutrek_status_r, /* LASERDISC_LINE_STATUS */ + NULL, /* LASERDISC_LINE_COMMAND */ + NULL, /* LASERDISC_LINE_DATA_AVAIL */ + } +}; + + + +/*************************************************************************** + SIMUTREK IMPLEMENTATION +***************************************************************************/ + +/*------------------------------------------------- + simutrek_set_audio_squelch - Simutrek-specific + command to enable/disable audio squelch +-------------------------------------------------*/ + +void simutrek_set_audio_squelch(const device_config *device, int state) +{ + laserdisc_state *ld = ldcore_get_safe_token(device); + ldplayer_data *player = ld->player; + player->simutrek.audio_squelch = (state == 0); + update_audio_squelch(ld); +} + + +/*------------------------------------------------- + simutrek_init - Simutrek-specific + initialization +-------------------------------------------------*/ + +static void simutrek_init(laserdisc_state *ld) +{ + astring *tempstring = astring_alloc(); + + /* standard PR-8210 initialization */ + pr8210_init(ld); + + /* find the Simutrek CPU */ + astring_printf(tempstring, "%s:%s", ld->device->tag, "simutrek"); + ld->player->simutrek.cpunum = mame_find_cpu_index(ld->device->machine, astring_c(tempstring)); + astring_free(tempstring); +} + + +/*------------------------------------------------- + simutrek_vsync - VSYNC callback, called at the + start of the blanking period +-------------------------------------------------*/ + +static TIMER_CALLBACK( irq_off ) +{ + laserdisc_state *ld = ptr; + ldplayer_data *player = ld->player; + cpunum_set_input_line(ld->device->machine, player->simutrek.cpunum, MCS48_INPUT_IRQ, CLEAR_LINE); + if (LOG_SIMUTREK) + printf("%3d:**** Simutrek IRQ clear\n", video_screen_get_vpos(ld->screen)); +} + +static void simutrek_vsync(laserdisc_state *ld, const vbi_metadata *vbi, int fieldnum, attotime curtime) +{ + ldplayer_data *player = ld->player; + + if (LOG_SIMUTREK) + printf("%3d:VSYNC(%d)\n", video_screen_get_vpos(ld->screen), fieldnum); + pr8210_vsync(ld, vbi, fieldnum, curtime); + + if (player->simutrek.data_ready) + { + if (LOG_SIMUTREK) + printf("%3d:VSYNC IRQ\n", video_screen_get_vpos(ld->screen)); + cpunum_set_input_line(ld->device->machine, player->simutrek.cpunum, MCS48_INPUT_IRQ, ASSERT_LINE); + timer_set(video_screen_get_scan_period(ld->screen), ld, 0, irq_off); + } +} + + +/*------------------------------------------------- + simutrek_update - update callback, called on + the first visible line of the frame +-------------------------------------------------*/ + +static INT32 simutrek_update(laserdisc_state *ld, const vbi_metadata *vbi, int fieldnum, attotime curtime) +{ + return pr8210_update(ld, vbi, fieldnum, curtime); +} + + +/*------------------------------------------------- + simutrek_ready_r - read callback when the + READY line is read +-------------------------------------------------*/ + +static UINT8 simutrek_ready_r(laserdisc_state *ld) +{ + return !ld->player->simutrek.data_ready; +} + + +/*------------------------------------------------- + simutrek_status_r - read callback when the + STATUS line is read +-------------------------------------------------*/ + +static UINT8 simutrek_status_r(laserdisc_state *ld) +{ + return ((ld->player->simutrek.port2 & 0x03) == 0x03) ? ASSERT_LINE : CLEAR_LINE; +} + + +/*------------------------------------------------- + simutrek_data_w - write callback when the + parallel data port is written to +-------------------------------------------------*/ + +static void simutrek_data_w(laserdisc_state *ld, UINT8 prev, UINT8 data) +{ + timer_call_after_resynch(ld, data, simutrek_latched_data_w); + if (LOG_SIMUTREK) + printf("%03d:**** Simutrek Command = %02X\n", video_screen_get_vpos(ld->screen), data); +} + + +/*------------------------------------------------- + simutrek_latched_data_w - deferred write + callback for when data is written +-------------------------------------------------*/ + +static TIMER_CALLBACK( simutrek_latched_data_w ) +{ + laserdisc_state *ld = ptr; + ldplayer_data *player = ld->player; + + /* store the data and set the ready flag */ + player->simutrek.data = param; + player->simutrek.data_ready = TRUE; +} + + +/*------------------------------------------------- + simutrek_port2_r - handle reads from the 8748 + port #2 +-------------------------------------------------*/ + +static READ8_HANDLER( simutrek_port2_r ) +{ + laserdisc_state *ld = find_pr8210(machine); + ldplayer_data *player = ld->player; + + /* bit $80 is the pr8210 video squelch */ + return (player->port1 & 0x20) ? 0x00 : 0x80; +} + + +/*------------------------------------------------- + simutrek_port2_w - handle writes to the 8748 + port #2 +-------------------------------------------------*/ + +static WRITE8_HANDLER( simutrek_port2_w ) +{ + laserdisc_state *ld = find_pr8210(machine); + ldplayer_data *player = ld->player; + UINT8 prev = player->simutrek.port2; + + /* update stat */ + player->simutrek.port2 = data; + + /* bit $20 goes to the serial line */ + if ((data ^ prev) & 0x20) + pr8210_control_w(ld, (data & 0x20) ? ASSERT_LINE : CLEAR_LINE, (data & 0x20) ? CLEAR_LINE : ASSERT_LINE); + + /* bit $10 goes to JUMP TRG */ + /* bit $08 controls direction */ + if (!(data & 0x10) && (prev & 0x10)) + { + int direction = (data & 0x08) ? 1 : -1; + if (LOG_SIMUTREK) + printf("%3d:JUMP TRG (Simutrek PC=%03X)\n", video_screen_get_vpos(ld->screen), activecpu_get_pc()); + ldcore_advance_slider(ld, direction); + } + + /* bit $04 controls who owns the JUMP TRG command */ + if (!(data & 0x04) && (prev & 0x04)) + player->simutrek.jumphack = 1; + + /* bits $03 control something (status?) */ + if (LOG_SIMUTREK && ((data ^ prev) & 0x03)) + printf("Simutrek Status = %d\n", data & 0x03); +} + + +/*------------------------------------------------- + simutrek_data_r - handle external 8748 data + reads +-------------------------------------------------*/ + +static READ8_HANDLER( simutrek_data_r ) +{ + laserdisc_state *ld = find_pr8210(machine); + ldplayer_data *player = ld->player; + + /* acknowledge the read and clear the data ready flag */ + player->simutrek.data_ready = FALSE; + return player->simutrek.data; +} + + +/*------------------------------------------------- + simutrek_t0_r - return the status of the + 8748 T0 input +-------------------------------------------------*/ + +static READ8_HANDLER( simutrek_t0_r ) +{ + /* return 1 if data is waiting from main CPU */ + laserdisc_state *ld = find_pr8210(machine); + return ld->player->simutrek.data_ready; +} diff --git a/src/emu/romload.c b/src/emu/romload.c index 4601bc24396..63d3cfb1f83 100644 --- a/src/emu/romload.c +++ b/src/emu/romload.c @@ -631,7 +631,7 @@ static int open_rom_file(running_machine *machine, rom_load_data *romdata, const } /* if the region is load by name, load the ROM from there */ - if (regiontag != NULL) + if (romdata->file == NULL && regiontag != NULL) { astring *fname = astring_assemble_3(astring_alloc(), regiontag, PATH_SEPARATOR, ROM_GETNAME(romp)); if (has_crc) diff --git a/src/ldplayer/ldplayer.c b/src/ldplayer/ldplayer.c index e533ee485ae..4cd172ab23b 100644 --- a/src/ldplayer/ldplayer.c +++ b/src/ldplayer/ldplayer.c @@ -29,12 +29,18 @@ enum { CMD_SCAN_REVERSE, CMD_STEP_REVERSE, + CMD_SLOW_REVERSE, + CMD_FAST_REVERSE, CMD_SCAN_FORWARD, CMD_STEP_FORWARD, + CMD_SLOW_FORWARD, + CMD_FAST_FORWARD, CMD_PLAY, CMD_PAUSE, - CMD_DISPLAY_ON, - CMD_DISPLAY_OFF, + CMD_FRAME_TOGGLE, + CMD_CHAPTER_TOGGLE, + CMD_CH1_TOGGLE, + CMD_CH2_TOGGLE, CMD_0, CMD_1, CMD_2, @@ -60,7 +66,6 @@ static astring *filename; static input_port_value last_controls; static UINT8 playing; -static UINT8 displaying; static emu_timer *pr8210_bit_timer; static UINT32 pr8210_command_buffer_in, pr8210_command_buffer_out; @@ -81,51 +86,68 @@ static void process_commands(const device_config *laserdisc) input_port_value controls = input_port_read(laserdisc->machine, "controls"); int number; - /* scan/step backwards */ - if (playing) - { - if (controls & 0x01) - (*execute_command)(laserdisc, CMD_SCAN_REVERSE); - } - else - { - if (!(last_controls & 0x01) && (controls & 0x01)) - (*execute_command)(laserdisc, CMD_STEP_REVERSE); - } + /* step backwards */ + if (!(last_controls & 0x01) && (controls & 0x01)) + (*execute_command)(laserdisc, CMD_STEP_REVERSE); - /* scan/step forwards */ - if (playing) - { - if (controls & 0x02) - (*execute_command)(laserdisc, CMD_SCAN_FORWARD); - } - else - { - if (!(last_controls & 0x02) && (controls & 0x02)) - (*execute_command)(laserdisc, CMD_STEP_FORWARD); - } + /* step forwards */ + if (!(last_controls & 0x02) && (controls & 0x02)) + (*execute_command)(laserdisc, CMD_STEP_FORWARD); + + /* scan backwards */ + if (controls & 0x04) + (*execute_command)(laserdisc, CMD_SCAN_REVERSE); + + /* scan forwards */ + if (controls & 0x08) + (*execute_command)(laserdisc, CMD_SCAN_FORWARD); + + /* slow backwards */ + if (!(last_controls & 0x10) && (controls & 0x10)) + (*execute_command)(laserdisc, CMD_SLOW_REVERSE); + + /* slow forwards */ + if (!(last_controls & 0x20) && (controls & 0x20)) + (*execute_command)(laserdisc, CMD_SLOW_FORWARD); + + /* fast backwards */ + if (controls & 0x40) + (*execute_command)(laserdisc, CMD_FAST_REVERSE); + + /* fast forwards */ + if (controls & 0x80) + (*execute_command)(laserdisc, CMD_FAST_FORWARD); /* play/pause */ - if (!(last_controls & 0x10) && (controls & 0x10)) + if (!(last_controls & 0x100) && (controls & 0x100)) { playing = !playing; (*execute_command)(laserdisc, playing ? CMD_PLAY : CMD_PAUSE); } - /* toggle display */ - if (!(last_controls & 0x20) && (controls & 0x20)) - { - displaying = !displaying; - (*execute_command)(laserdisc, displaying ? CMD_DISPLAY_ON : CMD_DISPLAY_OFF); - } + /* toggle frame display */ + if (!(last_controls & 0x200) && (controls & 0x200)) + (*execute_command)(laserdisc, CMD_FRAME_TOGGLE); + + /* toggle chapter display */ + if (!(last_controls & 0x400) && (controls & 0x400)) + (*execute_command)(laserdisc, CMD_CHAPTER_TOGGLE); + + /* toggle left channel */ + if (!(last_controls & 0x800) && (controls & 0x800)) + (*execute_command)(laserdisc, CMD_CH1_TOGGLE); + + /* toggle right channel */ + if (!(last_controls & 0x1000) && (controls & 0x1000)) + (*execute_command)(laserdisc, CMD_CH2_TOGGLE); /* numbers */ for (number = 0; number < 10; number++) - if (!(last_controls & (0x100 << number)) && (controls & (0x100 << number))) + if (!(last_controls & (0x10000 << number)) && (controls & (0x10000 << number))) (*execute_command)(laserdisc, CMD_0 + number); /* enter */ - if (!(last_controls & 0x40000) && (controls & 0x40000)) + if (!(last_controls & 0x4000000) && (controls & 0x4000000)) (*execute_command)(laserdisc, CMD_SEARCH); last_controls = controls; @@ -162,7 +184,6 @@ static TIMER_CALLBACK( autoplay ) /* start playing */ (*execute_command)(laserdisc, CMD_PLAY); playing = TRUE; - displaying = FALSE; } @@ -185,7 +206,6 @@ static MACHINE_RESET( ldplayer ) INLINE void pr8210_add_command(UINT8 command) { - pr8210_command_buffer[pr8210_command_buffer_in++ % ARRAY_LENGTH(pr8210_command_buffer)] = (command & 0x1f) | 0x20; pr8210_command_buffer[pr8210_command_buffer_in++ % ARRAY_LENGTH(pr8210_command_buffer)] = (command & 0x1f) | 0x20; pr8210_command_buffer[pr8210_command_buffer_in++ % ARRAY_LENGTH(pr8210_command_buffer)] = 0x00 | 0x20; } @@ -252,7 +272,8 @@ static void pr8210_execute(const device_config *laserdisc, int command) switch (command) { case CMD_SCAN_REVERSE: - if (pr8210_command_buffer_in == pr8210_command_buffer_out) + if (pr8210_command_buffer_in == pr8210_command_buffer_out || + pr8210_command_buffer_in == (pr8210_command_buffer_out + 1) % ARRAY_LENGTH(pr8210_command_buffer)) { pr8210_add_command(0x1c); playing = TRUE; @@ -264,8 +285,23 @@ static void pr8210_execute(const device_config *laserdisc, int command) playing = FALSE; break; + case CMD_SLOW_REVERSE: + pr8210_add_command(0x02); + playing = TRUE; + break; + + case CMD_FAST_REVERSE: + if (pr8210_command_buffer_in == pr8210_command_buffer_out || + pr8210_command_buffer_in == (pr8210_command_buffer_out + 1) % ARRAY_LENGTH(pr8210_command_buffer)) + { + pr8210_add_command(0x0c); + playing = TRUE; + } + break; + case CMD_SCAN_FORWARD: - if (pr8210_command_buffer_in == pr8210_command_buffer_out) + if (pr8210_command_buffer_in == pr8210_command_buffer_out || + pr8210_command_buffer_in == (pr8210_command_buffer_out + 1) % ARRAY_LENGTH(pr8210_command_buffer)) { pr8210_add_command(0x08); playing = TRUE; @@ -277,6 +313,20 @@ static void pr8210_execute(const device_config *laserdisc, int command) playing = FALSE; break; + case CMD_SLOW_FORWARD: + pr8210_add_command(0x18); + playing = TRUE; + break; + + case CMD_FAST_FORWARD: + if (pr8210_command_buffer_in == pr8210_command_buffer_out || + pr8210_command_buffer_in == (pr8210_command_buffer_out + 1) % ARRAY_LENGTH(pr8210_command_buffer)) + { + pr8210_add_command(0x10); + playing = TRUE; + } + break; + case CMD_PLAY: pr8210_add_command(0x14); playing = TRUE; @@ -287,11 +337,22 @@ static void pr8210_execute(const device_config *laserdisc, int command) playing = FALSE; break; - case CMD_DISPLAY_ON: - case CMD_DISPLAY_OFF: + case CMD_FRAME_TOGGLE: pr8210_add_command(0x0b); break; + case CMD_CHAPTER_TOGGLE: + pr8210_add_command(0x06); + break; + + case CMD_CH1_TOGGLE: + pr8210_add_command(0x0e); + break; + + case CMD_CH2_TOGGLE: + pr8210_add_command(0x16); + break; + case CMD_0: case CMD_1: case CMD_2: @@ -355,13 +416,7 @@ static void ldv1000_execute(const device_config *laserdisc, int command) playing = FALSE; break; - case CMD_DISPLAY_ON: - laserdisc_data_w(laserdisc, digits[1]); - laserdisc_data_w(laserdisc, 0xf1); - break; - - case CMD_DISPLAY_OFF: - laserdisc_data_w(laserdisc, digits[0]); + case CMD_FRAME_TOGGLE: laserdisc_data_w(laserdisc, 0xf1); break; @@ -395,23 +450,30 @@ static void ldv1000_execute(const device_config *laserdisc, int command) static INPUT_PORTS_START( ldplayer ) PORT_START("controls") - PORT_BIT( 0x00001, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) - PORT_BIT( 0x00002, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) - PORT_BIT( 0x00004, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) - PORT_BIT( 0x00008, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) - PORT_BIT( 0x00010, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_CODE(KEYCODE_SPACE) - PORT_BIT( 0x00020, IP_ACTIVE_HIGH, IPT_BUTTON2 ) - PORT_BIT( 0x00100, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(2) PORT_CODE(KEYCODE_0_PAD) PORT_CODE(KEYCODE_0) - PORT_BIT( 0x00200, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_PLAYER(2) PORT_CODE(KEYCODE_1_PAD) PORT_CODE(KEYCODE_1) - PORT_BIT( 0x00400, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_PLAYER(2) PORT_CODE(KEYCODE_2_PAD) PORT_CODE(KEYCODE_2) - PORT_BIT( 0x00800, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_PLAYER(2) PORT_CODE(KEYCODE_3_PAD) PORT_CODE(KEYCODE_3) - PORT_BIT( 0x01000, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_PLAYER(2) PORT_CODE(KEYCODE_4_PAD) PORT_CODE(KEYCODE_4) - PORT_BIT( 0x02000, IP_ACTIVE_HIGH, IPT_BUTTON6 ) PORT_PLAYER(2) PORT_CODE(KEYCODE_5_PAD) PORT_CODE(KEYCODE_5) - PORT_BIT( 0x04000, IP_ACTIVE_HIGH, IPT_BUTTON7 ) PORT_PLAYER(2) PORT_CODE(KEYCODE_6_PAD) PORT_CODE(KEYCODE_6) - PORT_BIT( 0x08000, IP_ACTIVE_HIGH, IPT_BUTTON8 ) PORT_PLAYER(2) PORT_CODE(KEYCODE_7_PAD) PORT_CODE(KEYCODE_7) - PORT_BIT( 0x10000, IP_ACTIVE_HIGH, IPT_BUTTON9 ) PORT_PLAYER(2) PORT_CODE(KEYCODE_8_PAD) PORT_CODE(KEYCODE_8) - PORT_BIT( 0x20000, IP_ACTIVE_HIGH, IPT_BUTTON10 ) PORT_PLAYER(2) PORT_CODE(KEYCODE_9_PAD) PORT_CODE(KEYCODE_9) - PORT_BIT( 0x40000, IP_ACTIVE_HIGH, IPT_BUTTON11 ) PORT_PLAYER(2) PORT_CODE(KEYCODE_ENTER_PAD) PORT_CODE(KEYCODE_ENTER) + PORT_BIT( 0x0000001, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Step reverse") PORT_CODE(KEYCODE_LEFT) + PORT_BIT( 0x0000002, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Step forward") PORT_CODE(KEYCODE_RIGHT) + PORT_BIT( 0x0000004, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("Scan reverse") PORT_CODE(KEYCODE_UP) + PORT_BIT( 0x0000008, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_NAME("Scan forward") PORT_CODE(KEYCODE_DOWN) + PORT_BIT( 0x0000010, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Slow reverse") PORT_CODE(KEYCODE_OPENBRACE) + PORT_BIT( 0x0000020, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Slow forward") PORT_CODE(KEYCODE_CLOSEBRACE) + PORT_BIT( 0x0000040, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("Fast reverse") PORT_CODE(KEYCODE_COMMA) + PORT_BIT( 0x0000080, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_NAME("Fast forward") PORT_CODE(KEYCODE_STOP) + PORT_BIT( 0x0000100, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("Play/Pause") PORT_CODE(KEYCODE_SPACE) + PORT_BIT( 0x0000200, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Toggle frame display") PORT_CODE(KEYCODE_F) + PORT_BIT( 0x0000400, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("Toggle chapter display") PORT_CODE(KEYCODE_C) + PORT_BIT( 0x0000800, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("Toggle left channel") PORT_CODE(KEYCODE_L) + PORT_BIT( 0x0001000, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("Toggle right channel") PORT_CODE(KEYCODE_R) + PORT_BIT( 0x0010000, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_NAME("0") PORT_PLAYER(2) PORT_CODE(KEYCODE_0_PAD) PORT_CODE(KEYCODE_0) + PORT_BIT( 0x0020000, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("1") PORT_PLAYER(2) PORT_CODE(KEYCODE_1_PAD) PORT_CODE(KEYCODE_1) + PORT_BIT( 0x0040000, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("2") PORT_PLAYER(2) PORT_CODE(KEYCODE_2_PAD) PORT_CODE(KEYCODE_2) + PORT_BIT( 0x0080000, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_NAME("3") PORT_PLAYER(2) PORT_CODE(KEYCODE_3_PAD) PORT_CODE(KEYCODE_3) + PORT_BIT( 0x0100000, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_NAME("4") PORT_PLAYER(2) PORT_CODE(KEYCODE_4_PAD) PORT_CODE(KEYCODE_4) + PORT_BIT( 0x0200000, IP_ACTIVE_HIGH, IPT_BUTTON6 ) PORT_NAME("5") PORT_PLAYER(2) PORT_CODE(KEYCODE_5_PAD) PORT_CODE(KEYCODE_5) + PORT_BIT( 0x0400000, IP_ACTIVE_HIGH, IPT_BUTTON7 ) PORT_NAME("6") PORT_PLAYER(2) PORT_CODE(KEYCODE_6_PAD) PORT_CODE(KEYCODE_6) + PORT_BIT( 0x0800000, IP_ACTIVE_HIGH, IPT_BUTTON8 ) PORT_NAME("7") PORT_PLAYER(2) PORT_CODE(KEYCODE_7_PAD) PORT_CODE(KEYCODE_7) + PORT_BIT( 0x1000000, IP_ACTIVE_HIGH, IPT_BUTTON9 ) PORT_NAME("8") PORT_PLAYER(2) PORT_CODE(KEYCODE_8_PAD) PORT_CODE(KEYCODE_8) + PORT_BIT( 0x2000000, IP_ACTIVE_HIGH, IPT_BUTTON10 ) PORT_NAME("9") PORT_PLAYER(2) PORT_CODE(KEYCODE_9_PAD) PORT_CODE(KEYCODE_9) + PORT_BIT( 0x4000000, IP_ACTIVE_HIGH, IPT_BUTTON11 ) PORT_NAME("Enter") PORT_PLAYER(2) PORT_CODE(KEYCODE_ENTER_PAD) PORT_CODE(KEYCODE_ENTER) INPUT_PORTS_END diff --git a/src/lib/util/chd.h b/src/lib/util/chd.h index 7914a9161f9..d8c74dca5d9 100644 --- a/src/lib/util/chd.h +++ b/src/lib/util/chd.h @@ -130,6 +130,9 @@ #define AV_METADATA_TAG 0x41564156 /* 'AVAV' */ #define AV_METADATA_FORMAT "FPS:%d.%06d WIDTH:%d HEIGHT:%d INTERLACED:%d CHANNELS:%d SAMPLERATE:%d" +/* A/V laserdisc frame metadata */ +#define AV_LD_METADATA_TAG 0x41564C44 /* 'AVLD' */ + /* CHD open values */ #define CHD_OPEN_READ 1 #define CHD_OPEN_READWRITE 2 diff --git a/src/lib/util/vbiparse.c b/src/lib/util/vbiparse.c index 65f1061cf91..83a3331ca23 100644 --- a/src/lib/util/vbiparse.c +++ b/src/lib/util/vbiparse.c @@ -318,7 +318,7 @@ void vbi_parse_all(const UINT16 *source, int sourcerowpixels, int sourcewidth, i else { /* if both are frame numbers, and one is not valid BCD, pick the other */ - if ((vbi->line17 & 0xf80000) == 0xf80000 && (vbi->line18 & 0xf80000) == 0xf80000) + if ((vbi->line17 & VBI_MASK_CAV_PICTURE) == VBI_CODE_CAV_PICTURE && (vbi->line18 & VBI_MASK_CAV_PICTURE) == VBI_CODE_CAV_PICTURE) { if ((vbi->line17 & 0xf000) > 0x9000 || (vbi->line17 & 0xf00) > 0x900 || (vbi->line17 & 0xf0) > 0x90 || (vbi->line17 & 0xf) > 0x9) vbi->line1718 = vbi->line18; @@ -332,3 +332,46 @@ void vbi_parse_all(const UINT16 *source, int sourcerowpixels, int sourcewidth, i vbi->line1718 = (vbi->line1718 << 1) | ((bits[0][bitnum] > bits[1][bitnum]) ? (bits[0][bitnum] & 1) : (bits[1][bitnum] & 1)); } } + + +/*------------------------------------------------- + vbi_metadata_pack - pack the VBI data down + into a smaller form for storage +-------------------------------------------------*/ + +void vbi_metadata_pack(UINT8 *dest, UINT32 framenum, const vbi_metadata *vbi) +{ + dest[0] = framenum >> 16; + dest[1] = framenum >> 8; + dest[2] = framenum >> 0; + dest[3] = vbi->white; + dest[4] = vbi->line16 >> 16; + dest[5] = vbi->line16 >> 8; + dest[6] = vbi->line16 >> 0; + dest[7] = vbi->line17 >> 16; + dest[8] = vbi->line17 >> 8; + dest[9] = vbi->line17 >> 0; + dest[10] = vbi->line18 >> 16; + dest[11] = vbi->line18 >> 8; + dest[12] = vbi->line18 >> 0; + dest[13] = vbi->line1718 >> 16; + dest[14] = vbi->line1718 >> 8; + dest[15] = vbi->line1718 >> 0; +} + + +/*------------------------------------------------- + vbi_metadata_unpack - unpack the VBI data + from a smaller form into the full structure +-------------------------------------------------*/ + +void vbi_metadata_unpack(vbi_metadata *vbi, UINT32 *framenum, const UINT8 *source) +{ + *framenum = (source[0] << 16) | (source[1] << 8) | source[2]; + vbi->white = source[3]; + vbi->line16 = (source[4] << 16) | (source[5] << 8) | source[6]; + vbi->line17 = (source[7] << 16) | (source[8] << 8) | source[9]; + vbi->line18 = (source[10] << 16) | (source[11] << 8) | source[12]; + vbi->line1718 = (source[13] << 16) | (source[14] << 8) | source[15]; +} + diff --git a/src/lib/util/vbiparse.h b/src/lib/util/vbiparse.h index 5e4a594f6a1..02af240265d 100644 --- a/src/lib/util/vbiparse.h +++ b/src/lib/util/vbiparse.h @@ -15,6 +15,46 @@ #define __VBIPARSE_H__ +/*************************************************************************** + CONSTANTS +***************************************************************************/ + +/* size of packed VBI data */ +#define VBI_PACKED_BYTES 16 + +/* these codes are full 24-bit codes with no parameter data */ +#define VBI_CODE_LEADIN 0x88ffff +#define VBI_CODE_LEADOUT 0x80eeee +#define VBI_CODE_STOP 0x82cfff +#define VBI_CODE_CLV 0x87ffff + +/* these codes require a mask because some bits are parameters */ +#define VBI_MASK_CAV_PICTURE 0xf00000 +#define VBI_CODE_CAV_PICTURE 0xf00000 +#define VBI_MASK_CHAPTER 0xf00fff +#define VBI_CODE_CHAPTER 0x800ddd +#define VBI_MASK_CLV_TIME 0xf0ff00 +#define VBI_CODE_CLV_TIME 0xf0dd00 +#define VBI_MASK_STATUS_CX_ON 0xfff000 +#define VBI_CODE_STATUS_CX_ON 0x8dc000 +#define VBI_MASK_STATUS_CX_OFF 0xfff000 +#define VBI_CODE_STATUS_CX_OFF 0x8bc000 +#define VBI_MASK_USER 0xf0f000 +#define VBI_CODE_USER 0x80d000 +#define VBI_MASK_CLV_PICTURE 0xf0f000 +#define VBI_CODE_CLV_PICTURE 0x80e000 + + + +/*************************************************************************** + MACROS +***************************************************************************/ + +#define VBI_CAV_PICTURE(x) (((((x) >> 16) & 0x07) * 10000) + ((((x) >> 12) & 0x0f) * 1000) + ((((x) >> 8) & 0x0f) * 100) + ((((x) >> 4) & 0x0f) * 10) + ((((x) >> 0) & 0x0f) * 1)) +#define VBI_CHAPTER(x) (((((x) >> 16) & 0x07) * 10) + ((((x) >> 12) & 0x0f) * 1)) + + + /*************************************************************************** TYPE DEFINITIONS ***************************************************************************/ @@ -44,5 +84,11 @@ int vbi_parse_white_flag(const UINT16 *source, int sourcewidth, int sourceshift) /* parse everything from a video frame */ void vbi_parse_all(const UINT16 *source, int sourcerowpixels, int sourcewidth, int sourceshift, vbi_metadata *vbi); +/* pack the VBI data down into a smaller form for storage */ +void vbi_metadata_pack(UINT8 *dest, UINT32 framenum, const vbi_metadata *vbi); + +/* unpack the VBI data from a smaller form into the full structure */ +void vbi_metadata_unpack(vbi_metadata *vbi, UINT32 *framenum, const UINT8 *source); + #endif /* __VBIPARSE_H__ */ diff --git a/src/mame/drivers/cubeqst.c b/src/mame/drivers/cubeqst.c index dd6aba3e345..f42305e7896 100644 --- a/src/mame/drivers/cubeqst.c +++ b/src/mame/drivers/cubeqst.c @@ -5,7 +5,6 @@ driver by Phil Bennett TODO: - * Emulate D8478 laserdisc interface MCU * Accurate video timings - Derive from PROMs * More accurate line fill circuitry emulation @@ -61,7 +60,6 @@ static UINT8 io_latch; static UINT8 reset_latch; static int disk_on; -static int ldp_command_flag; static const device_config *laserdisc; /************************************* @@ -222,22 +220,16 @@ static INTERRUPT_GEN( vblank ) static WRITE16_HANDLER( laserdisc_w ) { - ldp_command_flag = 1; laserdisc_data_w(laserdisc, data & 0xff); } -/* Called by the player emulation to acknowledge the 68000 command */ -void ldp_ack_callback(void) -{ - ldp_command_flag = 0; -} - /* D0: Command acknowledge D1: Seek status (0 = searching, 1 = ready) */ static READ16_HANDLER( laserdisc_r ) { + int ldp_command_flag = (laserdisc_line_r(laserdisc, LASERDISC_LINE_READY) == ASSERT_LINE) ? 0 : 1; int ldp_seek_status = (laserdisc_line_r(laserdisc, LASERDISC_LINE_STATUS) == ASSERT_LINE) ? 1 : 0; return (ldp_seek_status << 1) | ldp_command_flag; @@ -444,16 +436,12 @@ ADDRESS_MAP_END static MACHINE_START( cubeqst ) { laserdisc = device_list_find_by_tag(machine->config->devicelist, LASERDISC, "laserdisc"); - simutrek_set_cmd_ack_callback(laserdisc, ldp_ack_callback); } static MACHINE_RESET( cubeqst ) { reset_latch = 0; - /* Do this until we sort out the laserdisc interface properly */ - ldp_command_flag = 0; - /* Auxillary CPUs are held in reset */ cpunum_set_input_line(machine, SOUND_CPU, INPUT_LINE_RESET, ASSERT_LINE); cpunum_set_input_line(machine, ROTATE_CPU, INPUT_LINE_RESET, ASSERT_LINE); @@ -558,12 +546,6 @@ static MACHINE_DRIVER_START( cubeqst ) MDRV_CPU_PROGRAM_MAP(line_sound_map, 0) MDRV_CPU_CONFIG(snd_config) -#if 0 - MDRV_CPU_ADD("ldp_cpu", I8048, XTAL_10MHz / 2) - MDRV_CPU_PROGRAM_MAP(ldp_mem, 0) - MDRV_CPU_IO_MAP(ldp_io, 0) -#endif - MDRV_INTERLEAVE(700) MDRV_MACHINE_START(cubeqst) @@ -580,6 +562,7 @@ static MACHINE_DRIVER_START( cubeqst ) MDRV_LASERDISC_OVERLAY(cubeqst, CUBEQST_HBLANK, CUBEQST_VCOUNT, BITMAP_FORMAT_INDEXED16) MDRV_LASERDISC_OVERLAY_CLIP(0, 320-1, 0, 256-8) MDRV_LASERDISC_OVERLAY_POSITION(0.002, -0.018) + MDRV_LASERDISC_OVERLAY_SCALE(1.0, 1.030) MDRV_SPEAKER_STANDARD_STEREO("left", "right") @@ -658,9 +641,6 @@ ROM_START( cubeqst ) ROMX_LOAD( "mother_sounds_82s129.9e", 0x4, 0x100, CRC(598687e7) SHA1(c5045ddaab7123ff0a4c8f4c2489f9d70b63fc76), ROM_NIBBLE | ROM_SHIFT_NIBBLE_HI | ROM_SKIP(7)) ROMX_LOAD( "mother_sounds_82s129.8e", 0x4, 0x100, CRC(68de17ed) SHA1(efefcb4ccdd012b767c4765304c6022b0c091066), ROM_NIBBLE | ROM_SHIFT_NIBBLE_LO | ROM_SKIP(7)) - ROM_REGION( 0x10000, "ldp_cpu", 0) - ROM_LOAD( "laser_player_interface_d8748_a308.bin", 0x0000, 0x0400, CRC(eed3e728) SHA1(1eb3467f1c41553375b2c21952cd593b167f5416) ) - ROM_REGION16_BE( 0x1000, "soundproms", 0) ROMX_LOAD( "mother_sounds_82s185.17f", 0x0, 0x800, CRC(0f49d40e) SHA1(40340833ab27ccb5b60baf44ad01930f204f5318), ROM_NIBBLE | ROM_SHIFT_NIBBLE_LO | ROM_SKIP(1) ) ROMX_LOAD( "mother_sounds_82s185.19f", 0x0, 0x800, CRC(a041ce92) SHA1(9bc92992de22b830e479933c50650c7dc23f5713), ROM_NIBBLE | ROM_SHIFT_NIBBLE_HI | ROM_SKIP(1) ) diff --git a/src/mame/drivers/gottlieb.c b/src/mame/drivers/gottlieb.c index 39b65454ee4..56228603943 100644 --- a/src/mame/drivers/gottlieb.c +++ b/src/mame/drivers/gottlieb.c @@ -680,7 +680,7 @@ static INTERRUPT_GEN( gottlieb_interrupt ) bitmap_t *dummy; /* set the "disc ready" bit, which basically indicates whether or not we have a proper video frame */ - if (laserdisc_get_video(laserdisc, &dummy) == 0) + if (!laserdisc_get_video(laserdisc, &dummy)) laserdisc_status &= ~0x20; else laserdisc_status |= 0x20; diff --git a/src/tools/chdman.c b/src/tools/chdman.c index f5af8f22b40..06765cb910a 100644 --- a/src/tools/chdman.c +++ b/src/tools/chdman.c @@ -15,6 +15,7 @@ #include "bitmap.h" #include "md5.h" #include "sha1.h" +#include "vbiparse.h" #include #include #include @@ -84,7 +85,8 @@ static clock_t lastprogress = 0; ***************************************************************************/ /*------------------------------------------------- - print_big_int - 64-bit int printing with commas + print_big_int - 64-bit int printing with + commas -------------------------------------------------*/ static void print_big_int(UINT64 intvalue, char *output) @@ -105,7 +107,8 @@ static void print_big_int(UINT64 intvalue, char *output) /*------------------------------------------------- - big_int_string - return a string for a big int + big_int_string - return a string for a big + integer -------------------------------------------------*/ static char *big_int_string(UINT64 intvalue) @@ -162,6 +165,7 @@ static int usage(void) printf(" or: chdman -merge parent.chd diff.chd output.chd\n"); printf(" or: chdman -diff parent.chd compare.chd diff.chd\n"); printf(" or: chdman -setchs inout.chd cylinders heads sectors\n"); + printf(" or: chdman -fixavdata inout.chd\n"); return 1; } @@ -755,6 +759,7 @@ static int do_createav(int argc, char *argv[], int param) const char *inputfile, *outputfile; bitmap_t *fullbitmap = NULL; const avi_movie_info *info; + UINT8 *ldframedata = NULL; const chd_header *header; chd_file *chd = NULL; avi_file *avi = NULL; @@ -837,6 +842,19 @@ static int do_createav(int argc, char *argv[], int param) firstframe *= 2; numframes *= 2; } + + /* allocate space for the frame data */ + if (height == 524/2 || height == 624/2) + { + ldframedata = malloc(numframes * VBI_PACKED_BYTES); + if (ldframedata == NULL) + { + fprintf(stderr, "Out of memory allocating frame metadata\n"); + err = CHDERR_OUT_OF_MEMORY; + goto cleanup; + } + memset(ldframedata, 0, numframes * VBI_PACKED_BYTES); + } /* determine the number of bytes per frame */ max_samples_per_frame = ((UINT64)rate * 1000000 + fps_times_1million - 1) / fps_times_1million; @@ -919,6 +937,15 @@ static int do_createav(int argc, char *argv[], int param) fprintf(stderr, "Error reading frame %d from AVI file: %s\n", effframe, avi_error_string(avierr)); err = CHDERR_COMPRESSION_ERROR; } + + /* update metadata for this frame */ + if (ldframedata != NULL) + { + /* parse the data and pack it */ + vbi_metadata vbi; + vbi_parse_all((const UINT16 *)avconfig.video->base, avconfig.video->rowpixels, avconfig.video->width, 8, &vbi); + vbi_metadata_pack(&ldframedata[framenum * VBI_PACKED_BYTES], framenum, &vbi); + } /* configure the compressor for this frame */ chd_codec_config(chd, AV_CODEC_COMPRESS_CONFIG, &avconfig); @@ -929,6 +956,17 @@ static int do_createav(int argc, char *argv[], int param) goto cleanup; } + /* write the final metadata */ + if (ldframedata != NULL) + { + err = chd_set_metadata(chd, AV_LD_METADATA_TAG, 0, ldframedata, numframes * VBI_PACKED_BYTES); + if (err != CHDERR_NONE) + { + fprintf(stderr, "Error adding AVLD metadata: %s\n", chd_error_string(err)); + goto cleanup; + } + } + /* finish compression */ err = chd_compress_finish(chd); if (err != CHDERR_NONE) @@ -947,6 +985,8 @@ cleanup: free(avconfig.audio[chnum]); if (fullbitmap != NULL) bitmap_free(fullbitmap); + if (ldframedata != NULL) + free(ldframedata); if (err != CHDERR_NONE) osd_rmfile(outputfile); return (err != CHDERR_NONE); @@ -1466,7 +1506,7 @@ static int do_extractav(int argc, char *argv[], int param) numframes = MIN(totalframes - firstframe, numframes); /* allocate a video buffer */ - fullbitmap = bitmap_alloc(width, height * (interlaced ? 2 : 1), BITMAP_FORMAT_YUY16); + fullbitmap = bitmap_alloc(width, height, BITMAP_FORMAT_YUY16); if (fullbitmap == NULL) { fprintf(stderr, "Out of memory allocating temporary bitmap\n"); @@ -1723,6 +1763,236 @@ cleanup: } +/*------------------------------------------------- + do_fixavdata - fix the AV metadata for an + A/V file +-------------------------------------------------*/ + +static int do_fixavdata(int argc, char *argv[], int param) +{ + int fps, fpsfrac, width, height, interlaced, channels, rate; + av_codec_decompress_config avconfig = { 0 }; + bitmap_t *fullbitmap = NULL; + const char *inputfile; + UINT8 *vbidata = NULL; + int writeable = FALSE; + chd_file *chd = NULL; + bitmap_t fakebitmap; + char metadata[256]; + chd_header header; + UINT32 actlength; + UINT32 framenum; + chd_error err; + int fixframes = 0; + int fixes = 0; + + /* require 3 args total */ + if (argc != 3) + return usage(); + + /* extract the data */ + inputfile = argv[2]; + + /* print some info */ + printf("Input file: %s\n", inputfile); + + /* get the header */ + err = chd_open(inputfile, CHD_OPEN_READ, NULL, &chd); + if (err != CHDERR_NONE) + { + fprintf(stderr, "Error opening CHD file '%s': %s\n", inputfile, chd_error_string(err)); + goto cleanup; + } + header = *chd_get_header(chd); + + /* get the metadata */ + err = chd_get_metadata(chd, AV_METADATA_TAG, 0, metadata, sizeof(metadata), NULL, NULL); + if (err != CHDERR_NONE) + { + fprintf(stderr, "Error getting A/V metadata: %s\n", chd_error_string(err)); + goto cleanup; + } + + /* extract the info */ + if (sscanf(metadata, AV_METADATA_FORMAT, &fps, &fpsfrac, &width, &height, &interlaced, &channels, &rate) != 7) + { + fprintf(stderr, "Improperly formatted metadata\n"); + err = CHDERR_INVALID_METADATA; + goto cleanup; + } + + /* allocate space for the frame data */ + if ((height != 524/2 && height != 624/2) || !interlaced) + { + fprintf(stderr, "This file does not need VBI metadata\n"); + err = CHDERR_INVALID_METADATA; + goto cleanup; + } + + /* allocate a video buffer */ + fullbitmap = bitmap_alloc(width, height, BITMAP_FORMAT_YUY16); + if (fullbitmap == NULL) + { + fprintf(stderr, "Out of memory allocating temporary bitmap\n"); + err = CHDERR_OUT_OF_MEMORY; + goto cleanup; + } + avconfig.video = &fakebitmap; + + /* allocate memory for VBI data */ + vbidata = malloc(header.totalhunks * VBI_PACKED_BYTES); + if (vbidata == NULL) + { + fprintf(stderr, "Out of memory allocating VBI data\n"); + err = CHDERR_OUT_OF_MEMORY; + goto cleanup; + } + + /* read the metadata */ + err = chd_get_metadata(chd, AV_LD_METADATA_TAG, 0, vbidata, header.totalhunks * VBI_PACKED_BYTES, &actlength, NULL); + if (err != CHDERR_NONE) + { + fprintf(stderr, "Error getting VBI metadata: %s\n", chd_error_string(err)); + memset(vbidata, 0, header.totalhunks * VBI_PACKED_BYTES); + fixes++; + } + if (actlength != header.totalhunks * VBI_PACKED_BYTES) + { + fprintf(stderr, "VBI metadata incorrect size\n"); + memset(vbidata, 0, header.totalhunks * VBI_PACKED_BYTES); + fixes++; + } + + /* loop over hunks, reading */ + for (framenum = 0; framenum < header.totalhunks; framenum++) + { + vbi_metadata origvbi; + vbi_metadata vbi; + UINT32 vbiframe; + + /* progress */ + progress(framenum == 0, "Processing hunk %d/%d... \r", framenum, header.totalhunks); + + /* set up the fake bitmap for this frame */ + *avconfig.video = *fullbitmap; + + /* configure the decompressor for this frame */ + chd_codec_config(chd, AV_CODEC_DECOMPRESS_CONFIG, &avconfig); + + /* read the hunk into the buffers */ + err = chd_read(chd, framenum, NULL); + if (err != CHDERR_NONE) + { + fprintf(stderr, "Error reading hunk %d from CHD file: %s\n", framenum, chd_error_string(err)); + goto cleanup; + } + + /* unpack the current data for this frame */ + vbi_metadata_unpack(&origvbi, &vbiframe, &vbidata[framenum * VBI_PACKED_BYTES]); + + /* parse the video data */ + vbi_parse_all((const UINT16 *)avconfig.video->base, avconfig.video->rowpixels, avconfig.video->width, 8, &vbi); + + /* verify the data */ + if (vbiframe != 0 || origvbi.white != 0 || origvbi.line16 != 0 || origvbi.line17 != 0 || origvbi.line18 != 0 || origvbi.line1718 != 0) + { + int errors = 0; + + if (vbiframe != framenum) + { + fprintf(stderr, "%d:Frame mismatch in VBI data (%d, should be %d)\n", framenum, vbiframe, framenum); + errors++; + } + if (vbi.white != origvbi.white) + { + fprintf(stderr, "%d:White flag mismatch in VBI data (%d, should be %d)\n", framenum, origvbi.white, vbi.white); + errors++; + } + if (vbi.line16 != origvbi.line16) + { + fprintf(stderr, "%d:Line 16 mismatch in VBI data (%06X, should be %06X)\n", framenum, origvbi.line16, vbi.line16); + errors++; + } + if (vbi.line17 != origvbi.line17) + { + fprintf(stderr, "%d:Line 17 mismatch in VBI data (%06X, should be %06X)\n", framenum, origvbi.line17, vbi.line17); + errors++; + } + if (vbi.line18 != origvbi.line18) + { + fprintf(stderr, "%d:Line 18 mismatch in VBI data (%06X, should be %06X)\n", framenum, origvbi.line18, vbi.line18); + errors++; + } + if (vbi.line1718 != origvbi.line1718) + { + fprintf(stderr, "%d:Line 17/18 mismatch in VBI data (%06X, should be %06X)\n", framenum, origvbi.line1718, vbi.line1718); + errors++; + } + fixes += errors; + fixframes += (errors != 0); + } + + /* pack the new data */ + vbi_metadata_pack(&vbidata[framenum * VBI_PACKED_BYTES], framenum, &vbi); + } + progress(TRUE, "Processing complete! \n"); + + /* print final results */ + if (fixes == 0) + printf("\nNo fixes required\n"); + else + printf("\nFound %d errors on %d frames\n", fixes, fixframes); + + /* close the drive */ + chd_close(chd); + chd = NULL; + + /* apply fixes */ + if (fixes > 0) + { + /* mark the CHD writeable */ + header.flags |= CHDFLAGS_IS_WRITEABLE; + err = chd_set_header(inputfile, &header); + if (err != CHDERR_NONE) + fprintf(stderr, "Error writing new header: %s\n", chd_error_string(err)); + header.flags &= ~CHDFLAGS_IS_WRITEABLE; + writeable = TRUE; + + /* open the file */ + err = chd_open(inputfile, CHD_OPEN_READWRITE, NULL, &chd); + if (err != CHDERR_NONE) + { + fprintf(stderr, "Error opening CHD file '%s': %s\n", inputfile, chd_error_string(err)); + goto cleanup; + } + + /* write new metadata */ + err = chd_set_metadata(chd, AV_LD_METADATA_TAG, 0, vbidata, header.totalhunks * VBI_PACKED_BYTES); + if (err != CHDERR_NONE) + { + fprintf(stderr, "Error adding AVLD metadata: %s\n", chd_error_string(err)); + goto cleanup; + } + else + printf("Updated metadata written successfully\n"); + + /* allow cleanup code to close the file and revert the header */ + } + +cleanup: + /* clean up our mess */ + if (fullbitmap != NULL) + bitmap_free(fullbitmap); + if (vbidata != NULL) + free(vbidata); + if (chd != NULL) + chd_close(chd); + if (writeable) + chd_set_header(inputfile, &header); + return (err != CHDERR_NONE); +} + + /*------------------------------------------------- do_info - dump the header information from a drive image @@ -2535,7 +2805,8 @@ int CLIB_DECL main(int argc, char **argv) { "-info", do_info, 0 }, { "-merge", do_merge_update_chomp, OPERATION_MERGE }, { "-diff", do_diff, 0 }, - { "-setchs", do_setchs, 0 } + { "-setchs", do_setchs, 0 }, + { "-fixavdata", do_fixavdata, 0 } }; extern char build_version[]; int i;