summaryrefslogtreecommitdiffstatshomepage
path: root/trunk/src/emu/machine/ldpr8210.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/src/emu/machine/ldpr8210.c')
-rw-r--r--trunk/src/emu/machine/ldpr8210.c1136
1 files changed, 1136 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..82ddd80d78c
--- /dev/null
+++ b/trunk/src/emu/machine/ldpr8210.c
@@ -0,0 +1,1136 @@
+/*************************************************************************
+
+ ldpr8210.c
+
+ Pioneer PR-8210 laserdisc emulation.
+
+****************************************************************************
+
+ Copyright Aaron Giles
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ * Neither the name 'MAME' nor the names of its contributors may be
+ used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+**************************************************************************
+
+ 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 "ldpr8210.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)
+
+
+
+//**************************************************************************
+// GLOBAL VARIABLES
+//**************************************************************************
+
+// devices
+const device_type PIONEER_PR8210 = &device_creator<pioneer_pr8210_device>;
+const device_type SIMUTREK_SPECIAL = &device_creator<simutrek_special_device>;
+
+
+// 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 }, // <backslash>
+ { 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 } // ?
+};
+
+
+
+//**************************************************************************
+// PR-8210 ROM AND MACHINE INTERFACES
+//**************************************************************************
+
+static ADDRESS_MAP_START( pr8210_portmap, AS_IO, 8, pioneer_pr8210_device )
+ AM_RANGE(0x00, 0xff) AM_READWRITE(i8049_pia_r, i8049_pia_w)
+ AM_RANGE(MCS48_PORT_BUS, MCS48_PORT_BUS) AM_READ(i8049_bus_r)
+ AM_RANGE(MCS48_PORT_P1, MCS48_PORT_P1) AM_WRITE(i8049_port1_w)
+ AM_RANGE(MCS48_PORT_P2, MCS48_PORT_P2) AM_WRITE(i8049_port2_w)
+ AM_RANGE(MCS48_PORT_T0, MCS48_PORT_T0) AM_READ(i8049_t0_r)
+ AM_RANGE(MCS48_PORT_T1, MCS48_PORT_T1) AM_READ(i8049_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
+
+
+
+//**************************************************************************
+// PIONEER PR-8210 IMPLEMENTATION
+//**************************************************************************
+
+//-------------------------------------------------
+// pioneer_pr8210_device - constructor
+//-------------------------------------------------
+
+pioneer_pr8210_device::pioneer_pr8210_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : laserdisc_device(mconfig, PIONEER_PR8210, "Pioneer PR-8210", "pr8210", tag, owner, clock),
+ m_control(0),
+ m_lastcommand(0),
+ m_accumulator(0),
+ m_lastcommandtime(attotime::zero),
+ m_lastbittime(attotime::zero),
+ m_firstbittime(attotime::zero),
+ m_i8049_cpu(*this, "pr8210"),
+ m_slowtrg(attotime::zero),
+ m_vsync(false),
+ m_i8049_port1(0),
+ m_i8049_port2(0)
+{
+}
+
+pioneer_pr8210_device::pioneer_pr8210_device(const machine_config &mconfig, device_type type, const char *name, const char *shortname, const char *tag, device_t *owner, UINT32 clock)
+ : laserdisc_device(mconfig, type, name, shortname, tag, owner, clock),
+ m_control(0),
+ m_lastcommand(0),
+ m_accumulator(0),
+ m_lastcommandtime(attotime::zero),
+ m_lastbittime(attotime::zero),
+ m_firstbittime(attotime::zero),
+ m_i8049_cpu(*this, "pr8210"),
+ m_slowtrg(attotime::zero),
+ m_vsync(false),
+ m_i8049_port1(0),
+ m_i8049_port2(0)
+{
+}
+
+
+//-------------------------------------------------
+// control_w - write callback when the CONTROL
+// line is toggled
+//-------------------------------------------------
+
+void pioneer_pr8210_device::control_w(UINT8 data)
+{
+ // set the new value and remember the last
+ UINT8 prev = m_control;
+ m_control = data;
+
+ // handle rising edge
+ if (prev != ASSERT_LINE && data == ASSERT_LINE)
+ {
+ // get the time difference from the last assert
+ // and update our internal command time
+ attotime curtime = machine().time();
+ attotime delta = curtime - m_lastbittime;
+ m_lastbittime = curtime;
+
+ // if we timed out since the first bit, reset the accumulator
+ attotime overalldelta = curtime - m_firstbittime;
+ if (overalldelta > SERIAL_MAX_WORD_TIME || delta > SERIAL_MAX_BIT_TIME)
+ {
+ m_firstbittime = curtime;
+ m_accumulator = 0x5555;
+ if (LOG_SERIAL)
+ printf("Reset accumulator\n");
+ }
+
+ // 0 bit delta is 1.05 msec, 1 bit delta is 2.11 msec
+ int longpulse = (delta < SERIAL_MIDPOINT_TIME) ? 0 : 1;
+ m_accumulator = (m_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, m_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 ((m_accumulator & 0x383) == 0x80)
+ {
+ // data is stored to the PIA in bit-reverse order
+ UINT8 newcommand = (m_accumulator >> 2) & 0x1f;
+ m_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
+ attotime rejectuntil = m_lastcommandtime + SERIAL_REJECT_DUPLICATE_TIME;
+ m_lastcommandtime = curtime;
+ if (m_pia.porta == m_lastcommand && curtime < rejectuntil)
+ m_pia.porta = 0x00;
+ else
+ m_lastcommand = m_pia.porta;
+
+ // log the command and wait for a keypress
+ if (LOG_SERIAL)
+ printf("--- Command = %02X\n", m_pia.porta >> 3);
+
+ // reset the first bit time so that the accumulator clears on the next write
+ m_firstbittime = curtime - SERIAL_MAX_WORD_TIME;
+ }
+ }
+}
+
+
+//-------------------------------------------------
+// device_start - device initialization
+//-------------------------------------------------
+
+void pioneer_pr8210_device::device_start()
+{
+ // pass through to the parent
+ laserdisc_device::device_start();
+}
+
+
+//-------------------------------------------------
+// device_reset - device reset
+//-------------------------------------------------
+
+void pioneer_pr8210_device::device_reset()
+{
+ // pass through to the parent
+ laserdisc_device::device_reset();
+
+ // reset our state
+ attotime curtime = machine().time();
+ m_lastcommandtime = curtime;
+ m_firstbittime = curtime;
+ m_lastbittime = curtime;
+ m_slowtrg = curtime;
+}
+
+
+//-------------------------------------------------
+// device_timer - handle timers set by this
+// device
+//-------------------------------------------------
+
+void pioneer_pr8210_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ switch (id)
+ {
+ // update the VBI data in the PIA as soon as it is ready;
+ // this must happen early in the frame because the player
+ // logic relies on fetching it here
+ case TID_VBI_DATA_FETCH:
+
+ // logging
+ if (LOG_VBLANK_VBI)
+ {
+ UINT32 line1718 = get_field_code(LASERDISC_CODE_LINE1718, FALSE);
+ if ((line1718 & VBI_MASK_CAV_PICTURE) == VBI_CODE_CAV_PICTURE)
+ printf("%3d:VBI(%05d)\n", screen().vpos(), VBI_CAV_PICTURE(line1718));
+ else
+ printf("%3d:VBI()\n", screen().vpos());
+ }
+
+ // update PIA registers based on vbi code
+ m_pia.vbi1 = 0xff;
+ m_pia.vbi2 = 0xff;
+ if (focus_on() && laser_on())
+ {
+ UINT32 line16 = get_field_code(LASERDISC_CODE_LINE16, FALSE);
+ UINT32 line1718 = get_field_code(LASERDISC_CODE_LINE1718, FALSE);
+ if (line1718 == VBI_CODE_LEADIN)
+ m_pia.vbi1 &= ~0x01;
+ if (line1718 == VBI_CODE_LEADOUT)
+ m_pia.vbi1 &= ~0x02;
+ if (line16 == VBI_CODE_STOP)
+ m_pia.vbi1 &= ~0x04;
+ // unsure what this bit means: m_pia.vbi1 &= ~0x08;
+ if ((line1718 & VBI_MASK_CAV_PICTURE) == VBI_CODE_CAV_PICTURE)
+ {
+ m_pia.vbi1 &= ~0x10;
+ m_pia.frame[2] = 0xf0 | ((line1718 >> 16) & 0x07);
+ m_pia.frame[3] = 0xf0 | ((line1718 >> 12) & 0x0f);
+ m_pia.frame[4] = 0xf0 | ((line1718 >> 8) & 0x0f);
+ m_pia.frame[5] = 0xf0 | ((line1718 >> 4) & 0x0f);
+ m_pia.frame[6] = 0xf0 | ((line1718 >> 0) & 0x0f);
+ }
+ if ((line1718 & VBI_MASK_CHAPTER) == VBI_CODE_CHAPTER)
+ {
+ m_pia.vbi2 &= ~0x01;
+ m_pia.frame[0] = 0xf0 | ((line1718 >> 16) & 0x07);
+ m_pia.frame[1] = 0xf0 | ((line1718 >> 12) & 0x0f);
+ }
+ }
+ break;
+
+ // clear the VSYNC flag
+ case TID_VSYNC_OFF:
+ m_vsync = false;
+ break;
+
+ // pass everything else onto the parent
+ default:
+ laserdisc_device::device_timer(timer, id, param, ptr);
+ break;
+ }
+}
+
+
+//-------------------------------------------------
+// device_rom_region - return a pointer to our
+// ROM region definitions
+//-------------------------------------------------
+
+const rom_entry *pioneer_pr8210_device::device_rom_region() const
+{
+ return ROM_NAME(pr8210);
+}
+
+
+//-------------------------------------------------
+// device_mconfig_additions - return a pointer to
+// our machine config fragment
+//-------------------------------------------------
+
+machine_config_constructor pioneer_pr8210_device::device_mconfig_additions() const
+{
+ return MACHINE_CONFIG_NAME(pr8210);
+}
+
+
+//-------------------------------------------------
+// player_vsync - VSYNC callback, called at the
+// start of the blanking period
+//-------------------------------------------------
+
+void pioneer_pr8210_device::player_vsync(const vbi_metadata &vbi, int fieldnum, attotime curtime)
+{
+ // logging
+ if (LOG_VBLANK_VBI)
+ {
+ if ((vbi.line1718 & VBI_MASK_CAV_PICTURE) == VBI_CODE_CAV_PICTURE)
+ printf("%3d:VSYNC(%d,%05d)\n", screen().vpos(), fieldnum, VBI_CAV_PICTURE(vbi.line1718));
+ else
+ printf("%3d:VSYNC(%d)\n", screen().vpos(), fieldnum);
+ }
+
+ // signal VSYNC and set a timer to turn it off
+ m_vsync = true;
+ timer_set(screen().scan_period() * 4, TID_VSYNC_OFF);
+
+ // also set a timer to fetch the VBI data when it is ready
+ timer_set(screen().time_until_pos(19*2), TID_VBI_DATA_FETCH);
+}
+
+
+//-------------------------------------------------
+// player_update - update callback, called on the
+// first visible line of the frame
+//-------------------------------------------------
+
+INT32 pioneer_pr8210_device::player_update(const vbi_metadata &vbi, int fieldnum, attotime curtime)
+{
+ // logging
+ if (LOG_VBLANK_VBI)
+ printf("%3d:Update(%d)\n", screen().vpos(), fieldnum);
+
+ // if the spindle is on, we advance by 1 track after completing field #1
+ return spdl_on() ? fieldnum : 0;
+}
+
+
+//-------------------------------------------------
+// player_overlay - overlay callback, called
+// during frame processing in update to overlay
+// player data
+//-------------------------------------------------
+
+void pioneer_pr8210_device::player_overlay(bitmap_yuy16 &bitmap)
+{
+ // custom display
+ if (m_pia.display)
+ {
+ overlay_draw_group(bitmap, &m_pia.text[2], 5, OVERLAY_GROUP1_X);
+ overlay_draw_group(bitmap, &m_pia.text[7], 5, OVERLAY_GROUP2_X);
+ overlay_draw_group(bitmap, &m_pia.text[12], 5, OVERLAY_GROUP3_X);
+ }
+
+ // chapter/frame display
+ else
+ {
+ // frame display
+ if (m_pia.latchdisplay & 2)
+ overlay_draw_group(bitmap, &m_pia.text[2], 5, OVERLAY_GROUP1_X);
+
+ // chapter overlay
+ if (m_pia.latchdisplay & 1)
+ overlay_draw_group(bitmap, &m_pia.text[0], 2, OVERLAY_GROUP0_X);
+ }
+ m_pia.latchdisplay = 0;
+}
+
+
+//-------------------------------------------------
+// i8049_pia_r - handle reads from the mystery
+// Pioneer PIA
+//-------------------------------------------------
+
+READ8_MEMBER( pioneer_pr8210_device::i8049_pia_r )
+{
+ 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 = m_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 = m_pia.porta;
+ break;
+
+ // (C0) VBI decoding state 1
+ case 0xc0:
+ if (LOG_VBLANK_VBI)
+ printf("%3d:PIA(C0)\n", screen().vpos());
+ result = m_pia.vbi1;
+ break;
+
+ // (E0) VBI decoding state 2
+ case 0xe0:
+ if (LOG_VBLANK_VBI)
+ printf("%3d:PIA(E0)\n", screen().vpos());
+ result = m_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;
+}
+
+
+//-------------------------------------------------
+// i8049_pia_w - handle writes to the mystery
+// Pioneer PIA
+//-------------------------------------------------
+
+WRITE8_MEMBER( pioneer_pr8210_device::i8049_pia_w )
+{
+ 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:
+ m_pia.text[offset - 0x20] = data;
+ break;
+
+ // (40) control lines
+ case 0x40:
+
+ // toggle bit 0 to latch chapter number into display area
+ if (!(data & 0x01) && (m_pia.control & 0x01))
+ {
+ memcpy(&m_pia.text[0], &m_pia.frame[0], 2);
+ m_pia.latchdisplay |= 1;
+ }
+
+ // toggle bit 1 to latch frame number into display area
+ if (!(data & 0x02) && (m_pia.control & 0x02))
+ {
+ memcpy(&m_pia.text[2], &m_pia.frame[2], 5);
+ m_pia.latchdisplay |= 2;
+ }
+ m_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));
+
+ m_pia.portb = data;
+ update_audio_squelch();
+ break;
+
+ // (80) display enable
+ case 0x80:
+ m_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;
+ }
+}
+
+
+//-------------------------------------------------
+// i8049_bus_r - handle reads from the 8049 BUS
+// input, which is enabled via the PIA above
+//-------------------------------------------------
+
+READ8_MEMBER( pioneer_pr8210_device::i8049_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
+ */
+
+ UINT8 result = 0x00;
+
+ // bus bit 6: slider position limit detector, inside and outside
+ slider_position sliderpos = get_slider_position();
+ 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;
+}
+
+
+//-------------------------------------------------
+// i8049_port1_w - handle writes to the 8049
+// port #1
+//-------------------------------------------------
+
+WRITE8_MEMBER( pioneer_pr8210_device::i8049_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)
+ */
+
+ // set the new value
+ UINT8 prev = m_i8049_port1;
+ m_i8049_port1 = data;
+
+ // bit 7 selects the direction of slider movement for JUMP TRG and scanning
+ int 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 (!override_control())
+ {
+ if (LOG_SIMUTREK)
+ printf("%3d:JUMP TRG\n", screen().vpos());
+ advance_slider(direction);
+ }
+ else if (LOG_SIMUTREK)
+ printf("%3d:Skipped JUMP TRG\n", screen().vpos());
+ }
+
+ // bit 1 low enables scanning
+ if (!(data & 0x02))
+ {
+ // bit 2 selects the speed
+ int delta = (data & 0x04) ? SCAN_SPEED : SEEK_FAST_SPEED;
+ set_slider_speed(delta * direction);
+ }
+
+ // bit 1 high stops scanning
+ else
+ set_slider_speed(0);
+
+ // video squelch is controlled by bit 5; audio squelch is controlled by bit 6
+ update_video_squelch();
+ update_audio_squelch();
+}
+
+
+//-------------------------------------------------
+// i8049_port2_w - handle writes to the 8049
+// port #2
+//-------------------------------------------------
+
+WRITE8_MEMBER( pioneer_pr8210_device::i8049_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
+ */
+
+ // set the new value
+ UINT8 prev = m_i8049_port2;
+ m_i8049_port2 = data;
+
+ // on the falling edge of bit 5, start the slow timer
+ if (!(data & 0x20) && (prev & 0x20))
+ m_slowtrg = machine().time();
+
+ // bit 6 when low triggers an IRQ on the MCU
+ m_i8049_cpu->set_input_line(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);
+}
+
+
+//-------------------------------------------------
+// i8049_t0_r - return the state of the 8049
+// T0 input (connected to VSYNC)
+//-------------------------------------------------
+
+READ8_MEMBER( pioneer_pr8210_device::i8049_t0_r )
+{
+ // returns VSYNC state
+ return !m_vsync;
+}
+
+
+//-------------------------------------------------
+// i8049_t1_r - return the state of the 8049
+// T1 input (pulled high)
+//-------------------------------------------------
+
+READ8_MEMBER( pioneer_pr8210_device::i8049_t1_r )
+{
+ return 1;
+}
+
+
+//-------------------------------------------------
+// overlay_draw_group - draw a single group of
+// characters
+//-------------------------------------------------
+
+void pioneer_pr8210_device::overlay_draw_group(bitmap_yuy16 &bitmap, const UINT8 *text, int count, float xstart)
+{
+ // rease the background
+ overlay_erase(bitmap, xstart, xstart + ((OVERLAY_X_PIXELS + 1) * count + 1) * OVERLAY_PIXEL_WIDTH);
+
+ // draw each character, suppressing leading 0's
+ bool skip = true;
+ for (int 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
+//-------------------------------------------------
+
+void pioneer_pr8210_device::overlay_erase(bitmap_yuy16 &bitmap, float xstart, float xend)
+{
+ UINT32 xmin = (UINT32)(xstart * 256.0f * float(bitmap.width()));
+ UINT32 xmax = (UINT32)(xend * 256.0f * float(bitmap.width()));
+
+ for (UINT32 y = OVERLAY_Y; y < (OVERLAY_Y + (OVERLAY_Y_PIXELS + 2) * OVERLAY_PIXEL_HEIGHT); y++)
+ {
+ UINT16 *dest = &bitmap.pix16(y, xmin >> 8);
+ UINT16 ymax = *dest >> 8;
+ UINT16 ymin = ymax * 3 / 8;
+ UINT16 yres = ymin + ((ymax - ymin) * (xmin & 0xff)) / 256;
+ *dest = (yres << 8) | (*dest & 0xff);
+ dest++;
+
+ for (UINT32 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
+//-------------------------------------------------
+
+void pioneer_pr8210_device::overlay_draw_char(bitmap_yuy16 &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()));
+
+ // iterate over pixels
+ const UINT8 *chdataptr = &text_bitmap[ch & 0x3f][0];
+ for (UINT32 y = 0; y < OVERLAY_Y_PIXELS; y++)
+ {
+ UINT8 chdata = *chdataptr++;
+
+ for (UINT32 x = 0; x < OVERLAY_X_PIXELS; x++, chdata <<= 1)
+ if (chdata & 0x80)
+ {
+ UINT32 xmin = xminbase + x * xsize;
+ UINT32 xmax = xmin + xsize;
+ for (UINT32 yy = 0; yy < OVERLAY_PIXEL_HEIGHT; yy++)
+ {
+ UINT16 *dest = &bitmap.pix16(OVERLAY_Y + (y + 1) * OVERLAY_PIXEL_HEIGHT + yy, xmin >> 8);
+ UINT16 ymax = 0xff;
+ UINT16 ymin = *dest >> 8;
+ UINT16 yres = ymin + ((ymax - ymin) * (~xmin & 0xff)) / 256;
+ *dest = (yres << 8) | (*dest & 0xff);
+ dest++;
+
+ for (UINT32 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, simutrek_special_device )
+ AM_RANGE(0x00, 0xff) AM_READ(i8748_data_r)
+ AM_RANGE(MCS48_PORT_P2, MCS48_PORT_P2) AM_READWRITE(i8748_port2_r, i8748_port2_w)
+ AM_RANGE(MCS48_PORT_T0, MCS48_PORT_T0) AM_READ(i8748_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 IMPLEMENTATION
+//**************************************************************************
+
+//-------------------------------------------------
+// simutrek_special_device - constructor
+//-------------------------------------------------
+
+simutrek_special_device::simutrek_special_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : pioneer_pr8210_device(mconfig, SIMUTREK_SPECIAL, "Simutrek Modified PR-8210", "simutrek", tag, owner, clock),
+ m_i8748_cpu(*this, "simutrek"),
+ m_audio_squelch(0),
+ m_data(0),
+ m_data_ready(false),
+ m_i8748_port2(0),
+ m_controlnext(0),
+ m_controlthis(0)
+{
+}
+
+
+//-------------------------------------------------
+// data_w - write callback when the parallel data
+// port is written to
+//-------------------------------------------------
+
+void simutrek_special_device::data_w(UINT8 data)
+{
+ synchronize(TID_LATCH_DATA, data);
+ if (LOG_SIMUTREK)
+ printf("%03d:**** Simutrek Command = %02X\n", screen().vpos(), data);
+}
+
+
+//-------------------------------------------------
+// set_external_audio_squelch - Simutrek-specific
+// command to enable/disable audio squelch
+//-------------------------------------------------
+
+void simutrek_special_device::set_external_audio_squelch(int state)
+{
+ if (LOG_SIMUTREK && m_audio_squelch != (state == 0))
+ printf("--> audio squelch = %d\n", state == 0);
+ m_audio_squelch = (state == 0);
+ update_audio_squelch();
+}
+
+
+//-------------------------------------------------
+// player_vsync - VSYNC callback, called at the
+// start of the blanking period
+//-------------------------------------------------
+
+void simutrek_special_device::player_vsync(const vbi_metadata &vbi, int fieldnum, attotime curtime)
+{
+ // latch the control state after the second field
+ if (fieldnum == 1)
+ {
+ m_controlthis = m_controlnext;
+ m_controlnext = 0;
+ }
+
+ // call the parent
+ if (LOG_SIMUTREK)
+ printf("%3d:VSYNC(%d)\n", screen().vpos(), fieldnum);
+ pioneer_pr8210_device::player_vsync(vbi, fieldnum, curtime);
+
+ // process data
+ if (m_data_ready)
+ {
+ if (LOG_SIMUTREK)
+ printf("%3d:VSYNC IRQ\n", screen().vpos());
+ m_i8748_cpu->set_input_line(MCS48_INPUT_IRQ, ASSERT_LINE);
+ timer_set(screen().scan_period(), TID_IRQ_OFF);
+ }
+}
+
+
+//-------------------------------------------------
+// device_start - device initialization
+//-------------------------------------------------
+
+void simutrek_special_device::device_start()
+{
+ // pass through to the parent
+ pioneer_pr8210_device::device_start();
+}
+
+
+//-------------------------------------------------
+// device_reset - device reset
+//-------------------------------------------------
+
+void simutrek_special_device::device_reset()
+{
+ // standard PR-8210 initialization
+ pioneer_pr8210_device::device_reset();
+
+ // initialize the Simutrek state
+ // for proper synchronization of initial attract mode, this needs to be set
+ m_data_ready = true;
+}
+
+
+//-------------------------------------------------
+// device_timer - handle timers set by this
+// device
+//-------------------------------------------------
+
+void simutrek_special_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ switch (id)
+ {
+ // clear the 8748 IRQ
+ case TID_IRQ_OFF:
+ m_i8748_cpu->set_input_line(MCS48_INPUT_IRQ, CLEAR_LINE);
+ break;
+
+ // latch data
+ case TID_LATCH_DATA:
+ m_data = param;
+ m_data_ready = true;
+ break;
+
+ // pass everything else onto the parent
+ default:
+ pioneer_pr8210_device::device_timer(timer, id, param, ptr);
+ break;
+ }
+}
+
+
+//-------------------------------------------------
+// device_rom_region - return a pointer to our
+// ROM region definitions
+//-------------------------------------------------
+
+const rom_entry *simutrek_special_device::device_rom_region() const
+{
+ return ROM_NAME(simutrek);
+}
+
+
+//-------------------------------------------------
+// device_mconfig_additions - return a pointer to
+// our machine config fragment
+//-------------------------------------------------
+
+machine_config_constructor simutrek_special_device::device_mconfig_additions() const
+{
+ return MACHINE_CONFIG_NAME(simutrek);
+}
+
+
+//-------------------------------------------------
+// i8748_port2_r - handle reads from the 8748
+// port #2
+//-------------------------------------------------
+
+READ8_MEMBER( simutrek_special_device::i8748_port2_r )
+{
+ // bit $80 is the pr8210 video squelch
+ return (m_i8049_port1 & 0x20) ? 0x00 : 0x80;
+}
+
+
+//-------------------------------------------------
+// i8748_port2_w - handle writes to the 8748
+// port #2
+//-------------------------------------------------
+
+WRITE8_MEMBER( simutrek_special_device::i8748_port2_w )
+{
+ // update stat
+ UINT8 prev = m_i8748_port2;
+ m_i8748_port2 = data;
+
+ // bit $20 goes to the serial line
+ if ((data ^ prev) & 0x20)
+ pioneer_pr8210_device::control_w((data & 0x20) ? ASSERT_LINE : CLEAR_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", screen().vpos(), cpu_get_pc(&space.device()));
+ advance_slider(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", screen().vpos(), (data >> 2) & 1, cpu_get_pc(&space.device()));
+ m_controlnext = (~data >> 2) & 1;
+
+ // bits $03 control something (status?)
+ if (LOG_SIMUTREK && ((data ^ prev) & 0x03))
+ printf("Simutrek Status = %d\n", data & 0x03);
+}
+
+
+//-------------------------------------------------
+// i8748_data_r - handle external 8748 data reads
+//-------------------------------------------------
+
+READ8_MEMBER( simutrek_special_device::i8748_data_r )
+{
+ // acknowledge the read and clear the data ready flag
+ m_data_ready = false;
+ return m_data;
+}
+
+
+//-------------------------------------------------
+// i8748_t0_r - return the status of the 8748
+// T0 input
+//-------------------------------------------------
+
+READ8_MEMBER( simutrek_special_device::i8748_t0_r )
+{
+ // return 1 if data is waiting from main CPU
+ return m_data_ready;
+}