// license:BSD-3-Clause// copyright-holders:Aaron Giles/************************************************************************* ldpr8210.c Pioneer PR-8210 laserdisc emulation.************************************************************************** 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(455'000)#define SERIAL_0_BIT_TIME attotime::from_hz(SERIAL_CLOCK / 512)#define SERIAL_1_BIT_TIME attotime::from_hz(SERIAL_CLOCK / 1024)#define SERIAL_MIDPOINT_TIME attotime::from_hz(SERIAL_CLOCK / 600)#define SERIAL_MAX_BIT_TIME attotime::from_hz(SERIAL_CLOCK / 4096)#define SERIAL_MAX_WORD_TIME attotime::from_hz(SERIAL_CLOCK / 11520)#define SERIAL_REJECT_DUPLICATE_TIME attotime::from_hz(SERIAL_CLOCK / 11520 / 4)//**************************************************************************// GLOBAL VARIABLES//**************************************************************************// devicesDEFINE_DEVICE_TYPE(PIONEER_PR8210,pioneer_pr8210_device,"pr8210","Pioneer PR-8210")DEFINE_DEVICE_TYPE(SIMUTREK_SPECIAL,simutrek_special_device,"simutrek","Simutrek Modified PR-8210")// bitmaps for the charactersstaticconstuint8_ttext_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//**************************************************************************voidpioneer_pr8210_device::pr8210_portmap(address_map&map){map(0x00,0xff).rw(FUNC(pioneer_pr8210_device::i8049_pia_r),FUNC(pioneer_pr8210_device::i8049_pia_w));}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(constmachine_config&mconfig,constchar*tag,device_t*owner,uint32_tclock):pioneer_pr8210_device(mconfig,PIONEER_PR8210,tag,owner,clock){}pioneer_pr8210_device::pioneer_pr8210_device(constmachine_config&mconfig,device_typetype,constchar*tag,device_t*owner,uint32_tclock):laserdisc_device(mconfig,type,tag,owner,clock),m_audio1(*this,"pr8210_audio1"),m_audio2(*this,"pr8210_audio2"),m_clv(*this,"pr8210_clv"),m_cav(*this,"pr8210_cav"),m_srev(*this,"pr8210_srev"),m_sfwd(*this,"pr8210_sfwd"),m_play(*this,"pr8210_play"),m_step(*this,"pr8210_step"),m_pause(*this,"pr8210_pause"),m_standby(*this,"pr8210_standby"),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//-------------------------------------------------voidpioneer_pr8210_device::control_w(uint8_tdata){// set the new value and remember the lastuint8_tprev=m_control;m_control=data;// handle rising edgeif(prev!=ASSERT_LINE&&data==ASSERT_LINE){// get the time difference from the last assert// and update our internal command timeattotimecurtime=machine().time();attotimedelta=curtime-m_lastbittime;m_lastbittime=curtime;// if we timed out since the first bit, reset the accumulatorattotimeoveralldelta=curtime-m_firstbittime;if(overalldelta>SERIAL_MAX_WORD_TIME||delta>SERIAL_MAX_BIT_TIME){m_firstbittime=curtime;m_accumulator=0x5555;if(LOG_SERIAL)logerror("Reset accumulator\n");}// 0 bit delta is 1.05 msec, 1 bit delta is 2.11 msecintlongpulse=(delta<SERIAL_MIDPOINT_TIME)?0:1;m_accumulator=(m_accumulator<<1)|longpulse;// log the deltas for debuggingif(LOG_SERIAL){intusecdiff=(int)(delta.attoseconds()/ATTOSECONDS_IN_USEC(1));logerror("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,0if((m_accumulator&0x383)==0x80){// data is stored to the PIA in bit-reverse orderuint8_tnewcommand=(m_accumulator>>2)&0x1f;m_pia.porta=bitswap<8>(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 resultsattotimerejectuntil=m_lastcommandtime+SERIAL_REJECT_DUPLICATE_TIME;m_lastcommandtime=curtime;if(m_pia.porta==m_lastcommand&&curtime<rejectuntil)m_pia.porta=0x00;elsem_lastcommand=m_pia.porta;// log the command and wait for a keypressif(LOG_SERIAL)logerror("--- Command = %02X\n",m_pia.porta>>3);// reset the first bit time so that the accumulator clears on the next writem_firstbittime=curtime-SERIAL_MAX_WORD_TIME;}}}//-------------------------------------------------// device_start - device initialization//-------------------------------------------------voidpioneer_pr8210_device::device_start(){// alocate timersm_process_vbi_timer=timer_alloc(FUNC(pioneer_pr8210_device::process_vbi_data),this);m_vsync_off_timer=timer_alloc(FUNC(pioneer_pr8210_device::vsync_off),this);// resolve outputsm_audio1.resolve();m_audio2.resolve();m_clv.resolve();m_cav.resolve();m_srev.resolve();m_sfwd.resolve();m_play.resolve();m_step.resolve();m_pause.resolve();m_standby.resolve();// pass through to the parentlaserdisc_device::device_start();}//-------------------------------------------------// device_reset - device reset//-------------------------------------------------voidpioneer_pr8210_device::device_reset(){// pass through to the parentlaserdisc_device::device_reset();// reset our stateattotimecurtime=machine().time();m_lastcommandtime=curtime;m_firstbittime=curtime;m_lastbittime=curtime;m_slowtrg=curtime;}//-------------------------------------------------// process_vbi_data - process VBI data which was// fetched by the parent device//-------------------------------------------------TIMER_CALLBACK_MEMBER(pioneer_pr8210_device::process_vbi_data){// 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// loggingif(LOG_VBLANK_VBI){uint32_tline1718=get_field_code(LASERDISC_CODE_LINE1718,false);if((line1718&VBI_MASK_CAV_PICTURE)==VBI_CODE_CAV_PICTURE)logerror("%3d:VBI(%05d)\n",screen().vpos(),VBI_CAV_PICTURE(line1718));elselogerror("%3d:VBI()\n",screen().vpos());}// update PIA registers based on vbi codem_pia.vbi1=0xff;m_pia.vbi2=0xff;if(focus_on()&&laser_on()){uint32_tline16=get_field_code(LASERDISC_CODE_LINE16,false);uint32_tline1718=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);}}}//-------------------------------------------------// vsync_off - clear the VSYNC flag//-------------------------------------------------TIMER_CALLBACK_MEMBER(pioneer_pr8210_device::vsync_off){m_vsync=false;}//-------------------------------------------------// device_rom_region - return a pointer to our// ROM region definitions//-------------------------------------------------consttiny_rom_entry*pioneer_pr8210_device::device_rom_region()const{returnROM_NAME(pr8210);}//-------------------------------------------------// device_add_mconfig - add device configuration//-------------------------------------------------voidpioneer_pr8210_device::device_add_mconfig(machine_config&config){I8049(config,m_i8049_cpu,XTAL(4'410'000));m_i8049_cpu->set_addrmap(AS_IO,&pioneer_pr8210_device::pr8210_portmap);m_i8049_cpu->bus_in_cb().set(FUNC(pioneer_pr8210_device::i8049_bus_r));m_i8049_cpu->p1_out_cb().set(FUNC(pioneer_pr8210_device::i8049_port1_w));m_i8049_cpu->p2_out_cb().set(FUNC(pioneer_pr8210_device::i8049_port2_w));m_i8049_cpu->t0_in_cb().set(FUNC(pioneer_pr8210_device::i8049_t0_r));m_i8049_cpu->t1_in_cb().set(FUNC(pioneer_pr8210_device::i8049_t1_r));}//-------------------------------------------------// player_vsync - VSYNC callback, called at the// start of the blanking period//-------------------------------------------------voidpioneer_pr8210_device::player_vsync(constvbi_metadata&vbi,intfieldnum,constattotime&curtime){// loggingif(LOG_VBLANK_VBI){if((vbi.line1718&VBI_MASK_CAV_PICTURE)==VBI_CODE_CAV_PICTURE)logerror("%3d:VSYNC(%d,%05d)\n",screen().vpos(),fieldnum,VBI_CAV_PICTURE(vbi.line1718));elselogerror("%3d:VSYNC(%d)\n",screen().vpos(),fieldnum);}// signal VSYNC and set a timer to turn it offm_vsync=true;m_vsync_off_timer->adjust(screen().scan_period()*4);// also set a timer to fetch the VBI data when it is readym_process_vbi_timer->adjust(screen().time_until_pos(19*2));}//-------------------------------------------------// player_update - update callback, called on the// first visible line of the frame//-------------------------------------------------int32_tpioneer_pr8210_device::player_update(constvbi_metadata&vbi,intfieldnum,constattotime&curtime){// loggingif(LOG_VBLANK_VBI)logerror("%3d:Update(%d)\n",screen().vpos(),fieldnum);// if the spindle is on, we advance by 1 track after completing field #1returnspdl_on()?fieldnum:0;}//-------------------------------------------------// player_overlay - overlay callback, called// during frame processing in update to overlay// player data//-------------------------------------------------voidpioneer_pr8210_device::player_overlay(bitmap_yuy16&bitmap){// custom displayif(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 displayelse{// frame displayif(m_pia.latchdisplay&2)overlay_draw_group(bitmap,&m_pia.text[2],5,OVERLAY_GROUP1_X);// chapter overlayif(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//-------------------------------------------------uint8_tpioneer_pr8210_device::i8049_pia_r(offs_toffset){uint8_tresult=0xff;switch(offset){// (20-26) 7 characters for the chapter/framecase0x20:case0x21:case0x22:case0x23:case0x24:case0x25:case0x26:result=m_pia.frame[offset-0x20];break;// (1D-1F,27) invalid read but normalcase0x1d:case0x1e:case0x1f:case0x27:break;// (A0) port A value (from serial decoder)case0xa0:result=m_pia.porta;break;// (C0) VBI decoding state 1case0xc0:if(LOG_VBLANK_VBI)logerror("%3d:PIA(C0)\n",screen().vpos());result=m_pia.vbi1;break;// (E0) VBI decoding state 2case0xe0:if(LOG_VBLANK_VBI)logerror("%3d:PIA(E0)\n",screen().vpos());result=m_pia.vbi2;break;default:logerror("%s Unknown PR-8210 PIA read from offset %02X\n",machine().describe_context(),offset);break;}returnresult;}//-------------------------------------------------// i8049_pia_w - handle writes to the mystery// Pioneer PIA//-------------------------------------------------voidpioneer_pr8210_device::i8049_pia_w(offs_toffset,uint8_tdata){uint8_tvalue;switch(offset){// (20-30) 17 characters for the displaycase0x20:case0x21:case0x22:case0x23:case0x24:case0x25:case0x26:case0x27:case0x28:case0x29:case0x2a:case0x2b:case0x2c:case0x2d:case0x2e:case0x2f:case0x30:m_pia.text[offset-0x20]=data;break;// (40) control linescase0x40:// toggle bit 0 to latch chapter number into display areaif(!(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 areaif(!(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)case0x60:// these 4 are direct-connectm_audio1=BIT(data,0);m_audio2=BIT(data,1);m_clv=BIT(data,2);m_cav=BIT(data,3);// remaining 3 bits select one of 5 LEDs via a muxvalue=((data&0x40)>>6)|((data&0x20)>>4)|((data&0x10)>>2);m_srev=(value==0);m_sfwd=(value==1);m_play=(value==2);m_step=(value==3);m_pause=(value==4);m_pia.portb=data;update_audio_squelch();break;// (80) display enablecase0x80:m_pia.display=data&0x01;break;// no other writes knowndefault:logerror("%s Unknown PR-8210 PIA write to offset %02X = %02X\n",machine().describe_context(),offset,data);break;}}//-------------------------------------------------// i8049_bus_r - handle reads from the 8049 BUS// input, which is enabled via the PIA above//-------------------------------------------------uint8_tpioneer_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_tresult=0x00;// bus bit 6: slider position limit detector, inside and outsideslider_positionsliderpos=get_slider_position();if(sliderpos!=SLIDER_MINIMUM&&sliderpos!=SLIDER_MAXIMUM)result|=0x40;// bus bit 4: /FOCUS LOCKif(!focus_on())result|=0x10;// bus bit 3: /SPDL LOCKif(!spdl_on())result|=0x08;// bus bit 1: spindle motor stop detectorif(!spdl_on())result|=0x02;// bus bit 0: SLOW TIMER OUT// loop at beginning waits for $40=0, $02=1returnresult;}//-------------------------------------------------// i8049_port1_w - handle writes to the 8049// port #1//-------------------------------------------------voidpioneer_pr8210_device::i8049_port1_w(uint8_tdata){/* $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 valueuint8_tprev=m_i8049_port1;m_i8049_port1=data;// bit 7 selects the direction of slider movement for JUMP TRG and scanningintdirection=(data&0x80)?1:-1;// on the falling edge of bit 0, jump one track in either directionif(!(data&0x01)&&(prev&0x01)){// special override for the Simutrek, which takes over control of this is some situationsif(!override_control()){if(LOG_SIMUTREK)logerror("%3d:JUMP TRG\n",screen().vpos());advance_slider(direction);}elseif(LOG_SIMUTREK)logerror("%3d:Skipped JUMP TRG\n",screen().vpos());}// bit 1 low enables scanningif(!(data&0x02)){// bit 2 selects the speedintdelta=(data&0x04)?SCAN_SPEED:SEEK_FAST_SPEED;set_slider_speed(delta*direction);}// bit 1 high stops scanningelseset_slider_speed(0);// video squelch is controlled by bit 5; audio squelch is controlled by bit 6update_video_squelch();update_audio_squelch();}//-------------------------------------------------// i8049_port2_w - handle writes to the 8049// port #2//-------------------------------------------------voidpioneer_pr8210_device::i8049_port2_w(uint8_tdata){/* $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 valueuint8_tprev=m_i8049_port2;m_i8049_port2=data;// on the falling edge of bit 5, start the slow timerif(!BIT(data,5)&&BIT(prev,5))m_slowtrg=machine().time();// bit 6 when low triggers an IRQ on the MCUm_i8049_cpu->set_input_line(MCS48_INPUT_IRQ,BIT(data,6)?CLEAR_LINE:ASSERT_LINE);// standby LED is set accordingly to bit 4m_standby=BIT(data,4);}//-------------------------------------------------// i8049_t0_r - return the state of the 8049// T0 input (connected to VSYNC)//-------------------------------------------------intpioneer_pr8210_device::i8049_t0_r(){// returns VSYNC statereturn!m_vsync;}//-------------------------------------------------// i8049_t1_r - return the state of the 8049// T1 input (pulled high)//-------------------------------------------------intpioneer_pr8210_device::i8049_t1_r(){return1;}//-------------------------------------------------// overlay_draw_group - draw a single group of// characters//-------------------------------------------------voidpioneer_pr8210_device::overlay_draw_group(bitmap_yuy16&bitmap,constuint8_t*text,intcount,floatxstart){// rease the backgroundoverlay_erase(bitmap,xstart,xstart+((OVERLAY_X_PIXELS+1)*count+1)*OVERLAY_PIXEL_WIDTH);// draw each character, suppressing leading 0'sboolskip=true;for(intx=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//-------------------------------------------------voidpioneer_pr8210_device::overlay_erase(bitmap_yuy16&bitmap,floatxstart,floatxend){uint32_txmin=uint32_t(xstart*256.0f*float(bitmap.width()));uint32_txmax=uint32_t(xend*256.0f*float(bitmap.width()));for(uint32_ty=OVERLAY_Y;y<(OVERLAY_Y+(OVERLAY_Y_PIXELS+2)*OVERLAY_PIXEL_HEIGHT);y++){uint16_t*dest=&bitmap.pix(y,xmin>>8);uint16_tymax=*dest>>8;uint16_tymin=ymax*3/8;uint16_tyres=ymin+((ymax-ymin)*(xmin&0xff))/256;*dest=(yres<<8)|(*dest&0xff);dest++;for(uint32_tx=(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//-------------------------------------------------voidpioneer_pr8210_device::overlay_draw_char(bitmap_yuy16&bitmap,uint8_tch,floatxstart){uint32_txminbase=uint32_t(xstart*256.0f*float(bitmap.width()));uint32_txsize=uint32_t(OVERLAY_PIXEL_WIDTH*256.0f*float(bitmap.width()));// iterate over pixelsconstuint8_t*chdataptr=&text_bitmap[ch&0x3f][0];for(uint32_ty=0;y<OVERLAY_Y_PIXELS;y++){uint8_tchdata=*chdataptr++;for(uint32_tx=0;x<OVERLAY_X_PIXELS;x++,chdata<<=1)if(chdata&0x80){uint32_txmin=xminbase+x*xsize;uint32_txmax=xmin+xsize;for(uint32_tyy=0;yy<OVERLAY_PIXEL_HEIGHT;yy++){uint16_t*dest=&bitmap.pix(OVERLAY_Y+(y+1)*OVERLAY_PIXEL_HEIGHT+yy,xmin>>8);uint16_tymax=0xff;uint16_tymin=*dest>>8;uint16_tyres=ymin+((ymax-ymin)*(~xmin&0xff))/256;*dest=(yres<<8)|(*dest&0xff);dest++;for(uint32_txx=(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//**************************************************************************voidsimutrek_special_device::simutrek_portmap(address_map&map){map(0x00,0xff).r(FUNC(simutrek_special_device::i8748_data_r));}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(constmachine_config&mconfig,constchar*tag,device_t*owner,uint32_tclock):pioneer_pr8210_device(mconfig,SIMUTREK_SPECIAL,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//-------------------------------------------------voidsimutrek_special_device::data_w(uint8_tdata){m_latch_data_timer->adjust(attotime::zero,data);if(LOG_SIMUTREK)logerror("%03d:**** Simutrek Command = %02X\n",screen().vpos(),data);}//-------------------------------------------------// set_external_audio_squelch - Simutrek-specific// command to enable/disable audio squelch//-------------------------------------------------voidsimutrek_special_device::set_external_audio_squelch(intstate){if(LOG_SIMUTREK&&m_audio_squelch!=(state==0))logerror("--> 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//-------------------------------------------------voidsimutrek_special_device::player_vsync(constvbi_metadata&vbi,intfieldnum,constattotime&curtime){// latch the control state after the second fieldif(fieldnum==1){m_controlthis=m_controlnext;m_controlnext=0;}// call the parentif(LOG_SIMUTREK)logerror("%3d:VSYNC(%d)\n",screen().vpos(),fieldnum);pioneer_pr8210_device::player_vsync(vbi,fieldnum,curtime);// process dataif(m_data_ready){if(LOG_SIMUTREK)logerror("%3d:VSYNC IRQ\n",screen().vpos());m_i8748_cpu->set_input_line(MCS48_INPUT_IRQ,ASSERT_LINE);m_irq_off_timer->adjust(screen().scan_period());}}//-------------------------------------------------// device_start - device initialization//-------------------------------------------------voidsimutrek_special_device::device_start(){// alocate timersm_irq_off_timer=timer_alloc(FUNC(simutrek_special_device::irq_off),this);m_latch_data_timer=timer_alloc(FUNC(simutrek_special_device::latch_data),this);// pass through to the parentpioneer_pr8210_device::device_start();}//-------------------------------------------------// device_reset - device reset//-------------------------------------------------voidsimutrek_special_device::device_reset(){// standard PR-8210 initializationpioneer_pr8210_device::device_reset();// initialize the Simutrek state// for proper synchronization of initial attract mode, this needs to be setm_data_ready=true;}//-------------------------------------------------// irq_off - clear the 8748 IRQ//-------------------------------------------------TIMER_CALLBACK_MEMBER(simutrek_special_device::irq_off){m_i8748_cpu->set_input_line(MCS48_INPUT_IRQ,CLEAR_LINE);}//-------------------------------------------------// latch_data - perform delayed latching of data//-------------------------------------------------TIMER_CALLBACK_MEMBER(simutrek_special_device::latch_data){m_data=param;m_data_ready=true;}//-------------------------------------------------// device_rom_region - return a pointer to our// ROM region definitions//-------------------------------------------------consttiny_rom_entry*simutrek_special_device::device_rom_region()const{returnROM_NAME(simutrek);}//-------------------------------------------------// device_add_mconfig - add device configuration//-------------------------------------------------voidsimutrek_special_device::device_add_mconfig(machine_config&config){i8748_device&special(I8748(config,"simutrek",XTAL(6'000'000)));special.set_addrmap(AS_IO,&simutrek_special_device::simutrek_portmap);special.p2_in_cb().set(FUNC(simutrek_special_device::i8748_port2_r));special.p2_out_cb().set(FUNC(simutrek_special_device::i8748_port2_w));special.t0_in_cb().set(FUNC(simutrek_special_device::i8748_t0_r));pioneer_pr8210_device::device_add_mconfig(config);}//-------------------------------------------------// i8748_port2_r - handle reads from the 8748// port #2//-------------------------------------------------uint8_tsimutrek_special_device::i8748_port2_r(){// bit $80 is the pr8210 video squelchreturn(m_i8049_port1&0x20)?0x00:0x80;}//-------------------------------------------------// i8748_port2_w - handle writes to the 8748// port #2//-------------------------------------------------voidsimutrek_special_device::i8748_port2_w(uint8_tdata){// update statuint8_tprev=m_i8748_port2;m_i8748_port2=data;// bit $20 goes to the serial lineif((data^prev)&0x20)pioneer_pr8210_device::control_w((data&0x20)?ASSERT_LINE:CLEAR_LINE);// bit $10 goes to JUMP TRG// bit $08 controls directionif(!(data&0x10)&&(prev&0x10)){intdirection=(data&0x08)?1:-1;if(LOG_SIMUTREK)logerror("%3d:JUMP TRG %s\n",screen().vpos(),machine().describe_context());advance_slider(direction);}// bit $04 controls who owns the JUMP TRG commandif(LOG_SIMUTREK&&((data^prev)&0x04))logerror("%3d:Simutrek ownership line = %d %s\n",screen().vpos(),(data>>2)&1,machine().describe_context());m_controlnext=(~data>>2)&1;// bits $03 control something (status?)if(LOG_SIMUTREK&&((data^prev)&0x03))logerror("Simutrek Status = %d\n",data&0x03);}//-------------------------------------------------// i8748_data_r - handle external 8748 data reads//-------------------------------------------------uint8_tsimutrek_special_device::i8748_data_r(){// acknowledge the read and clear the data ready flagm_data_ready=false;returnm_data;}//-------------------------------------------------// i8748_t0_r - return the status of the 8748// T0 input//-------------------------------------------------intsimutrek_special_device::i8748_t0_r(){// return 1 if data is waiting from main CPUreturnm_data_ready;}