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