/* MPU4 hardware emulation for sets see mpu4.c */ /* Note 19/07/11 DH - added lots of sets these are mostly unsorted and need to be split into clones the original source of these was a mess, assume things to be mislabled, bad, duplicated, or otherwise badly organized. a lot of work is needed to sort them out, especially the Barcrest sets! Some of this stuff MIGHT be in the wrong driver, or missing roms (sound roms especially) */ /*********************************************************************************************************** Barcrest MPU4 highly preliminary driver by J.Wallace, and Anonymous. This is the core driver, no video specific stuff should go in here. This driver holds all the mechanical games. 06-2011: Fixed boneheaded interface glitch that was causing samples to not be cancelled correctly. Added the ability to read each segment of an LED display separately, this may be necessary for some games that use them as surrogate lamp lines. New persistence 'hack' to stop light flicker for the small extender. 05-2011: Add better OKI emulation 04-2011: More accurate gamball code, fixed ROM banking (Project Amber), added BwB CHR simulator (Amber) This is still a hard coded system, but significantly different to Barcrest's version. Started adding support for the Crystal Gaming program card, and the link keys for setting parameters. 03-2011: Lamp timing fixes, support for all known expansion cards added. 01-2011: Adding the missing 'OKI' sound card, and documented it, but it needs a 6376 rewrite. 09-2007: Haze: Added Deal 'Em video support. 03-08-2007: J Wallace: Removed audio filter for now, since sound is more accurate without them. Connect 4 now has the right sound. 03-07-2007: J Wallace: Several major changes, including input relabelling, and system timer improvements. 06-2007: Atari Ace, many cleanups and optimizations of I/O routines 09-06-2007: J Wallace: Fixed 50Hz detection circuit. 17-02-2007: J Wallace: Added Deal 'Em - still needs some work. 10-02-2007: J Wallace: Improved input timing. 30-01-2007: J Wallace: Characteriser rewritten to run the 'extra' data needed by some games. 24-01-2007: J Wallace: With thanks to Canonman and HIGHWAYMAN/System 80, I was able to confirm a seemingly ghastly misuse of a PIA is actually on the real hardware. This fixes the meters. See http://agemame.mameworld.info/techinfo/mpu4.php for Information. --- Board Setup --- The MPU4 BOARD is the driver board, originally designed to run Fruit Machines made by the Barcrest Group, but later licensed to other firms as a general purpose unit (even some old Photo-Me booths used the unit). This board uses a ~1.72 Mhz 6809B CPU, and a number of PIA6821 chips for multiplexing inputs and the like. To some extent, the hardware feels like a revision of the MPU3 design, integrating into the base unit features that were previously added through expansion ports. However, there is no backwards compatibility, and the entire memory map has been reworked. Like MPU3, a 6840PTM is used for internal timing, and other miscellaneous control functions, including as a crude analogue sound device (a square wave from the PTM being used as the alarm sound generator). However, the main sound functionality is provided by dedicated hardware (an AY8913). A MPU4 GAME CARD (cartridge) plugs into the MPU4 board containing the game, and a protection PAL (the 'characteriser'). This PAL, as well as protecting the games, also controlled some of the lamp address matrix for many games, and acted as an anti-tampering device which helped to prevent the hacking of certain titles in a manner which broke UK gaming laws. Like MPU3, over the years developers have added more capabilities through the spare inputs and outputs provided. These provided support for more reels, lamps and LEDs through daughtercards. Several solutions were released depending on the manufacturer of the machine, all are emulated here. In later revisions of the main board (MOD4 onwards), the AY8913 was removed entirely, as two official alternatives for sound had been produced. In one, a YM2413 is built into the gameboard, and in the other an OKI MSM6376 is interfaced with a PIA and PTM to allow sophisticated sampled sound. The lamping and input handling side of the machine rely entirely on a column by column 'strobe' system, with lights and LEDs selected in turn. In the inputs there are two orange connectors (sampled every 8ms) and two black ones (sampled every 16ms), giving 32 multiplexed inputs. In addition there are two auxiliary ports that can be accessed separately to these and are bidirectional --- Preliminary MPU4 Memorymap --- (NV) indicates an item which is not present on the video version, which has a Comms card instead. hex |r/w| D D D D D D D D | location | | 7 6 5 4 3 2 1 0 | function -----------+---+-----------------+-------------------------------------------------------------------------- 0000-07FF |R/W| D D D D D D D D | 2k RAM -----------+---+-----------------+-------------------------------------------------------------------------- 0800 |R/W| | Characteriser (Security PAL) (NV) -----------+---+-----------------+-------------------------------------------------------------------------- 0850 ? | W | ??????????????? | page latch (NV) -----------+---+-----------------+-------------------------------------------------------------------------- 0880 |R/W| D D D D D D D D | PIA6821 on soundboard (Oki MSM6376 clocked by 6840 (8C0)) | | | port A = ?? | | | port B (882) | | | b7 = NAR | | | b6 = 0 if OKI busy, 1 if OKI ready | | | b5 = volume control clock | | | b4 = volume control direction (0= up, 1 = down) | | | b3 = ?? | | | b2 = ?? | | | b1 = 2ch | | | b0 = ST -----------+---+-----------------+-------------------------------------------------------------------------- 08C0 | | | MC6840 on sound board -----------+---+-----------------+-------------------------------------------------------------------------- 0900- |R/W| D D D D D D D D | MC6840 PTM IC2 Clock1 <-------------------------------------- | | V | Output1 ---> Clock2 | | Output2 --+-> Clock3 | | | | Output3 ---> 'to audio amp' ?? | +--------> CA1 IC3 ( IRQ line connected to CPU -----------+---+-----------------+-------------------------------------------------------------------------- 0A00-0A03 |R/W| D D D D D D D D | PIA6821 IC3 port A Lamp Drives 1,2,3,4,6,7,8,9 (sic)(IC14) | | | | | | CA1 <= output2 from PTM6840 (IC2) | | | CA2 => alpha data | | | | | | port B Lamp Drives 10,11,12,13,14,15,16,17 (sic)(IC13) | | | | | | CB2 => alpha reset (clock on Dutch systems) | | | -----------+---+-----------------+-------------------------------------------------------------------------- 0B00-0B03 |R/W| D D D D D D D D | PIA6821 IC4 port A = data for 7seg leds (pins 10 - 17, via IC32) | | | | | | CA1 INPUT, 50 Hz input (used to generate IRQ) | | | CA2 OUTPUT, connected to pin2 74LS138 CE for multiplexer | | | (B on LED strobe multiplexer) | | | IRQA connected to IRQ of CPU | | | port B | | | PB7 = INPUT, serial port Receive data (Rx) | | | PB6 = INPUT, reel A sensor | | | PB5 = INPUT, reel B sensor | | | PB4 = INPUT, reel C sensor | | | PB3 = INPUT, reel D sensor | | | PB2 = INPUT, Connected to CA1 (50Hz signal) | | | PB1 = INPUT, undercurrent sense | | | PB0 = INPUT, overcurrent sense | | | | | | CB1 INPUT, used to generate IRQ on edge of serial input line | | | CB2 OUTPUT, enable signal for reel optics | | | IRQB connected to IRQ of CPU | | | -----------+---+-----------------+-------------------------------------------------------------------------- 0C00-0C03 |R/W| D D D D D D D D | PIA6821 IC5 port A | | | | | | PA0-PA7, INPUT AUX1 connector | | | | | | CA2 OUTPUT, serial port Transmit line | | | CA1 not connected | | | IRQA connected to IRQ of CPU | | | | | | port B | | | | | | PB0-PB7 INPUT, AUX2 connector | | | | | | CB1 INPUT, connected to PB7 (Aux2 connector pin 4) | | | | | | CB2 OUTPUT, AY8913 chip select line | | | IRQB connected to IRQ of CPU | | | -----------+---+-----------------+-------------------------------------------------------------------------- 0D00-0D03 |R/W| D D D D D D D D | PIA6821 IC6 | | | | | | port A | | | | | | PA0 - PA7 (INPUT/OUTPUT) data port AY8913 sound chip | | | | | | CA1 INPUT, not connected | | | CA2 OUTPUT, BC1 pin AY8913 sound chip | | | IRQA , connected to IRQ CPU | | | | | | port B | | | | | | PB0-PB3 OUTPUT, reel A | | | PB4-PB7 OUTPUT, reel B | | | | | | CB1 INPUT, not connected | | | CB2 OUTPUT, B01R pin AY8913 sound chip | | | IRQB , connected to IRQ CPU | | | -----------+---+-----------------+-------------------------------------------------------------------------- 0E00-0E03 |R/W| D D D D D D D D | PIA6821 IC7 | | | | | | port A | | | | | | PA0-PA3 OUTPUT, reel C | | | PA4-PA7 OUTPUT, reel D | | | CA1 INPUT, not connected | | | CA2 OUTPUT, A on LED strobe multiplexer | | | IRQA , connected to IRQ CPU | | | | | | port B | | | | | | PB0-PB6 OUTPUT mech meter 1-7 or reel E + F | | | PB7 Voltage drop sensor | | | CB1 INPUT, not connected | | | CB2 OUTPUT,mech meter 8 | | | IRQB , connected to IRQ CPU | | | -----------+---+-----------------+-------------------------------------------------------------------------- 0F00-0F03 |R/W| D D D D D D D D | PIA6821 IC8 | | | | | | port A | | | | | | PA0-PA7 INPUT multiplexed inputs data | | | | | | CA1 INPUT, not connected | | | CA2 OUTPUT, C on LED strobe multiplexer | | | IRQA connected to IRQ CPU | | | | | | port B | | | | | | PB0-PB7 OUTPUT triacs outputs connector PL6 | | | used for slides / hoppers | | | | | | CB1 INPUT, not connected | | | CB2 OUTPUT, pin1 alpha display PL7 (clock signal) | | | IRQB connected to IRQ CPU | | | -----------+---+-----------------+-------------------------------------------------------------------------- 1000-FFFF | R | D D D D D D D D | ROM (can be bank switched by 0x850 in 8 banks of 64 k ) (NV) -----------+---+-----------------+-------------------------------------------------------------------------- TODO: - Distinguish door switches using manual - Complete stubs for hoppers (needs slightly better 68681 emulation, and new 'hoppers' device emulation) - It seems that the MPU4 core program relies on some degree of persistence when switching strobes and handling writes to the various hardware ports. This explains the occasional lamping/LED blackout and switching bugs For now, we're ignoring any extra writes to strobes, as the alternative is to assign a timer to *everything* and start modelling the individual hysteresis curves of filament lamps. - Fix BwB characteriser, need to be able to calculate stabiliser bytes. Anyone fancy reading 6809 source? - Strange bug in Andy's Great Escape - Mystery nudge sound effect is not played, mpu4 latches in silence instead (?) ***********************************************************************************************************/ #include "emu.h" #include "includes/mpu4.h" #include "video/awpvid.h" //Fruit Machines Only #include "mpu4.lh" #include "mpu4ext.lh" static TIMER_CALLBACK( ic24_timeout ); /* LED Segments related to pins (5 is not connected): Unlike the controllers emulated in the layout code, each segment of an MPU4 LED can be set individually, even being used as individual lamps. However, we can get away with settings like this in the majority of cases. _9_ | | 3 8 | | _2_ | | 4 7 |_ _| 6 1 8 display enables (pins 10 - 17) */ static void lamp_extend_small(mpu4_state *state, int data) { int lamp_ext_data,column,i; column = data & 0x07; lamp_ext_data = 0x1f - ((data & 0xf8) >> 3);//remove the mux lines from the data if (state->m_lamp_strobe_ext_persistence == 0) //One write to reset the drive lines, one with the data, one to clear the lines, so only the 2nd write does anything //Once again, lamp persistences would take care of this, but we can't do that { for (i = 0; i < 5; i++) { output_set_lamp_value((8*column)+i+128,((lamp_ext_data & (1 << i)) != 0)); } } state->m_lamp_strobe_ext_persistence ++; if ((state->m_lamp_strobe_ext_persistence == 3)||(state->m_lamp_strobe_ext!=column)) { state->m_lamp_strobe_ext_persistence = 0; state->m_lamp_strobe_ext=column; } } static void lamp_extend_large(mpu4_state *state, int data,int column,int active) { int lampbase,i,bit7; state->m_lamp_sense = 0; bit7 = data & 0x80; if ( bit7 != state->m_last_b7 ) { state->m_card_live = 1; //depending on bit 7, we can access one of two 'blocks' of 64 lamps lampbase = bit7 ? 0 : 64; if ( data & 0x3f ) { state->m_lamp_sense = 1; } if ( active ) { if (state->m_lamp_strobe_ext != column) { for (i = 0; i < 8; i++) {//CHECK, this includes bit 7 output_set_lamp_value((8*column)+i+128+lampbase ,(data & (1 << i)) != 0); } state->m_lamp_strobe_ext = column; } } state->m_last_b7 = bit7; } else { state->m_card_live = 0; } } static void led_write_latch(mpu4_state *state, int latch, int data, int column) { int diff,i,j; diff = (latch ^ state->m_last_latch) & latch; column = 7 - column; // like main board, these are wired up in reverse data = ~data;//inverted drive lines? for(i=0; i<5; i++) { if (diff & (1<m_last_latch = diff; } static void update_meters(mpu4_state *state) { int meter; int data = ((state->m_mmtr_data & 0x7f) | state->m_remote_meter); switch (state->m_reel_mux) { case STANDARD_REEL: // Change nothing break; case FIVE_REEL_5TO8: stepper_update(4, ((data >> 4) & 0x0f)); data = (data & 0x0F); //Strip reel data from meter drives, leaving active elements awp_draw_reel(4); break; case FIVE_REEL_8TO5: stepper_update(4, (((data & 0x01) + ((data & 0x08) >> 2) + ((data & 0x20) >> 3) + ((data & 0x80) >> 4)) & 0x0f)) ; data = 0x00; //Strip all reel data from meter drives, nothing is connected awp_draw_reel(4); break; case FIVE_REEL_3TO6: stepper_update(4, ((data >> 2) & 0x0f)); data = 0x00; //Strip all reel data from meter drives awp_draw_reel(4); break; case SIX_REEL_1TO8: stepper_update(4, (data & 0x0f)); stepper_update(5, ((data >> 4) & 0x0f)); data = 0x00; //Strip all reel data from meter drives awp_draw_reel(4); awp_draw_reel(5); break; case SIX_REEL_5TO8: stepper_update(4, ((data >> 4) & 0x0f)); data = 0x00; //Strip all reel data from meter drives awp_draw_reel(4); break; case SEVEN_REEL: stepper_update(0, (((data & 0x01) + ((data & 0x08) >> 2) + ((data & 0x20) >> 3) + ((data & 0x80) >> 4)) & 0x0f)) ; data = 0x00; //Strip all reel data from meter drives awp_draw_reel(0); break; case FLUTTERBOX: //The backbox fan assembly fits in a reel unit sized box, wired to the remote meter pin, so we can handle it here output_set_value("flutterbox", data & 0x80); data &= ~0x80; //Strip flutterbox data from meter drives } MechMtr_update(7, (data & 0x80)); for (meter = 0; meter < 4; meter ++) { MechMtr_update(meter, (data & (1 << meter))); } if (state->m_reel_mux == STANDARD_REEL) { for (meter = 4; meter < 7; meter ++) { MechMtr_update(meter, (data & (1 << meter))); } } } /* called if board is reset */ void mpu4_stepper_reset(mpu4_state *state) { int pattern = 0,reel; for (reel = 0; reel < 6; reel++) { stepper_reset_position(reel); if(!state->m_reel_mux) { if (stepper_optic_state(reel)) pattern |= 1<m_optic_pattern = pattern; } MACHINE_RESET_MEMBER(mpu4_state,mpu4) { m_vfd->reset(); mpu4_stepper_reset(this); m_lamp_strobe = 0; m_lamp_strobe2 = 0; m_led_strobe = 0; m_mmtr_data = 0; m_remote_meter = 0; m_IC23GC = 0; m_IC23GB = 0; m_IC23GA = 0; m_IC23G1 = 1; m_IC23G2A = 0; m_IC23G2B = 0; m_prot_col = 0; m_chr_counter = 0; m_chr_value = 0; { UINT8 *rom = memregion("maincpu")->base(); size_t romsize = memregion("maincpu")->bytes(); if (romsize < 0x10000) fatalerror("maincpu ROM region is < 0x10000 bytes, check ROM\n"); int numbanks = romsize / 0x10000; m_bank1->configure_entries(0, 8, &rom[0x01000], 0x10000); // some Bwb games must default to the last bank, does anything not like this // behavior? // some Bwb games don't work anyway tho, they seem to dislike something else // about the way the regular banking behaves, not related to the CB2 stuff m_bank1->set_entry(numbanks-1); m_maincpu->reset(); } } /* 6809 IRQ handler */ WRITE_LINE_MEMBER(mpu4_state::cpu0_irq) { /* The PIA and PTM IRQ lines are all connected to a common PCB track, leading directly to the 6809 IRQ line. */ int combined_state = m_pia3->irq_a_state() | m_pia3->irq_b_state() | m_pia4->irq_a_state() | m_pia4->irq_b_state() | m_pia5->irq_a_state() | m_pia5->irq_b_state() | m_pia6->irq_a_state() | m_pia6->irq_b_state() | m_pia7->irq_a_state() | m_pia7->irq_b_state() | m_pia8->irq_a_state() | m_pia8->irq_b_state() | m_6840ptm->irq_state(); if (!m_link7a_connected) //7B = IRQ, 7A = FIRQ, both = NMI { m_maincpu->set_input_line(M6809_IRQ_LINE, combined_state ? ASSERT_LINE : CLEAR_LINE); LOG(("6809 int%d \n", combined_state)); } else { m_maincpu->set_input_line(M6809_FIRQ_LINE, combined_state ? ASSERT_LINE : CLEAR_LINE); LOG(("6809 fint%d \n", combined_state)); } } /* Bankswitching The MOD 4 ROM cards are set up to handle 8 separate ROM pages, arranged as 2 sets of 4. The bankswitch selects which of the 4 pages in the set is active, while the bankset switches between the sets. It appears that the cards were originally intended to be used in a 'half' page setup, where the two halves of the ROM space could be mixed and matched as appropriate. However, there is no evidence to suggest this was ever implemented. The controls for it exist however, in the form of the Soundboard PIA CB2 pin, which is used in some cabinets instead of the main control. */ WRITE8_MEMBER(mpu4_state::bankswitch_w) { // printf("bankswitch_w %02x\n", data); // m_pageset is never even set?? m_pageval = (data & 0x03); m_bank1->set_entry((m_pageval + (m_pageset ? 4 : 0)) & 0x07); } READ8_MEMBER(mpu4_state::bankswitch_r) { return m_bank1->entry(); } WRITE8_MEMBER(mpu4_state::bankset_w) { // printf("bankset_w %02x\n", data); // m_pageset is never even set?? m_pageval = (data - 2);//writes 2 and 3, to represent 0 and 1 - a hangover from the half page design? m_bank1->set_entry((m_pageval + (m_pageset ? 4 : 0)) & 0x07); } /* IC2 6840 PTM handler */ WRITE8_MEMBER(mpu4_state::ic2_o1_callback) { m_6840ptm->set_c2(data); /* copy output value to IC2 c2 this output is the clock for timer2 */ /* 1200Hz System interrupt timer */ } WRITE8_MEMBER(mpu4_state::ic2_o2_callback) { m_pia3->ca1_w(data); /* copy output value to IC3 ca1 */ /* the output from timer2 is the input clock for timer3 */ /* miscellaneous interrupts generated here */ m_6840ptm->set_c3(data); } WRITE8_MEMBER(mpu4_state::ic2_o3_callback) { /* the output from timer3 is used as a square wave for the alarm output and as an external clock source for timer 1! */ /* also runs lamp fade */ m_6840ptm->set_c1(data); } static const ptm6840_interface ptm_ic2_intf = { MPU4_MASTER_CLOCK / 4, { 0, 0, 0 }, { DEVCB_DRIVER_MEMBER(mpu4_state,ic2_o1_callback), DEVCB_DRIVER_MEMBER(mpu4_state,ic2_o2_callback), DEVCB_DRIVER_MEMBER(mpu4_state,ic2_o3_callback) }, DEVCB_DRIVER_LINE_MEMBER(mpu4_state,cpu0_irq) }; /* 6821 PIA handlers */ /* IC3, lamp data lines + alpha numeric display */ WRITE8_MEMBER(mpu4_state::pia_ic3_porta_w) { int i; LOG_IC3(("%s: IC3 PIA Port A Set to %2x (lamp strobes 1 - 9)\n", machine().describe_context(),data)); if(m_ic23_active) { if (m_lamp_strobe != m_input_strobe) { // Because of the nature of the lamping circuit, there is an element of persistance // As a consequence, the lamp column data can change before the input strobe without // causing the relevant lamps to black out. for (i = 0; i < 8; i++) { output_set_lamp_value((8*m_input_strobe)+i, ((data & (1 << i)) !=0)); } m_lamp_strobe = m_input_strobe; } } } WRITE8_MEMBER(mpu4_state::pia_ic3_portb_w) { int i; LOG_IC3(("%s: IC3 PIA Port B Set to %2x (lamp strobes 10 - 17)\n", machine().describe_context(),data)); if(m_ic23_active) { if (m_lamp_strobe2 != m_input_strobe) { for (i = 0; i < 8; i++) { output_set_lamp_value((8*m_input_strobe)+i+64, ((data & (1 << i)) !=0)); } m_lamp_strobe2 = m_input_strobe; } if (m_led_lamp) { /* Some games (like Connect 4) use 'programmable' LED displays, built from light display lines in section 2. */ /* These are mostly low-tech machines, where such wiring proved cheaper than an extender card */ /* TODO: replace this with 'segment' lamp masks, to make it more generic */ UINT8 pled_segs[2] = {0,0}; static const int lamps1[8] = { 106, 107, 108, 109, 104, 105, 110, 133 }; static const int lamps2[8] = { 114, 115, 116, 117, 112, 113, 118, 119 }; for (i = 0; i < 8; i++) { if (output_get_lamp_value(lamps1[i])) pled_segs[0] |= (1 << i); if (output_get_lamp_value(lamps2[i])) pled_segs[1] |= (1 << i); } output_set_digit_value(8,pled_segs[0]); output_set_digit_value(9,pled_segs[1]); } } } WRITE_LINE_MEMBER(mpu4_state::pia_ic3_ca2_w) { LOG_IC3(("%s: IC3 PIA Write CA2 (alpha data), %02X\n", machine().describe_context(),state)); m_alpha_data_line = state; } WRITE_LINE_MEMBER(mpu4_state::pia_ic3_cb2_w) { LOG_IC3(("%s: IC3 PIA Write CB (alpha reset), %02X\n",machine().describe_context(),state)); // DM Data pin A if ( !state ) { m_vfd->reset(); } } static const pia6821_interface pia_ic3_intf = { DEVCB_NULL, /* port A in */ DEVCB_NULL, /* port B in */ DEVCB_NULL, /* line CA1 in */ DEVCB_NULL, /* line CB1 in */ DEVCB_NULL, /* line CA2 in */ DEVCB_NULL, /* line CB2 in */ DEVCB_DRIVER_MEMBER(mpu4_state,pia_ic3_porta_w), /* port A out */ DEVCB_DRIVER_MEMBER(mpu4_state,pia_ic3_portb_w), /* port B out */ DEVCB_DRIVER_LINE_MEMBER(mpu4_state,pia_ic3_ca2_w), /* line CA2 out */ DEVCB_DRIVER_LINE_MEMBER(mpu4_state,pia_ic3_cb2_w), /* port CB2 out */ DEVCB_DRIVER_LINE_MEMBER(mpu4_state,cpu0_irq), /* IRQA */ DEVCB_DRIVER_LINE_MEMBER(mpu4_state,cpu0_irq) /* IRQB */ }; /* IC23 emulation IC23 is a 74LS138 1-of-8 Decoder It is used as a multiplexer for the LEDs, lamp selects and inputs.*/ static void ic23_update(mpu4_state *state) { if (!state->m_IC23G2A) { if (!state->m_IC23G2B) { if (state->m_IC23G1) { if ( state->m_IC23GA ) state->m_input_strobe |= 0x01; else state->m_input_strobe &= ~0x01; if ( state->m_IC23GB ) state->m_input_strobe |= 0x02; else state->m_input_strobe &= ~0x02; if ( state->m_IC23GC ) state->m_input_strobe |= 0x04; else state->m_input_strobe &= ~0x04; } } } else if ((state->m_IC23G2A)||(state->m_IC23G2B)) { state->m_input_strobe = 0x00; } } /* IC24 emulation IC24 is a 74LS122 pulse generator CLEAR and B2 are tied high and A1 and A2 tied low, meaning any pulse on B1 will give a low pulse on the output pin. */ static void ic24_output(mpu4_state *state, int data) { state->m_IC23G2A = data; ic23_update(state); } static void ic24_setup(mpu4_state *state) { if (state->m_IC23GA) { double duration = TIME_OF_74LS123((220*1000),(0.1*0.000001)); { state->m_ic23_active=1; ic24_output(state, 0); state->m_ic24_timer->adjust(attotime::from_double(duration)); } } } static TIMER_CALLBACK( ic24_timeout ) { mpu4_state *state = machine.driver_data(); state->m_ic23_active=0; ic24_output(state, 1); } /* IC4, 7 seg leds, 50Hz timer reel sensors, current sensors */ WRITE8_MEMBER(mpu4_state::pia_ic4_porta_w) { int i; if(m_ic23_active) { if (((m_lamp_extender == NO_EXTENDER)||(m_lamp_extender == SMALL_CARD)||(m_lamp_extender == LARGE_CARD_C))&& (m_led_extender == NO_EXTENDER)) { if(m_led_strobe != m_input_strobe) { for(i=0; i<8; i++) { output_set_indexed_value("mpu4led",((7 - m_input_strobe) * 8) +i,(data & (1 << i)) !=0); } output_set_digit_value(7 - m_input_strobe,data); } m_led_strobe = m_input_strobe; } } } WRITE8_MEMBER(mpu4_state::pia_ic4_portb_w) { if (m_reel_mux) { /* A write here connects one reel (and only one) to the optic test circuit. This allows 8 reels to be supported instead of 4. */ if (m_reel_mux == SEVEN_REEL) { m_active_reel= reel_mux_table7[(data >> 4) & 0x07]; } else m_active_reel= reel_mux_table[(data >> 4) & 0x07]; } } READ8_MEMBER(mpu4_state::pia_ic4_portb_r) { pia6821_device *pia = m_pia4; if ( m_serial_data ) { m_ic4_input_b |= 0x80; pia->cb1_w(1); } else { m_ic4_input_b &= ~0x80; pia->cb1_w(0); } if (!m_reel_mux) { if ( m_optic_pattern & 0x01 ) m_ic4_input_b |= 0x40; /* reel A tab */ else m_ic4_input_b &= ~0x40; if ( m_optic_pattern & 0x02 ) m_ic4_input_b |= 0x20; /* reel B tab */ else m_ic4_input_b &= ~0x20; if ( m_optic_pattern & 0x04 ) m_ic4_input_b |= 0x10; /* reel C tab */ else m_ic4_input_b &= ~0x10; if ( m_optic_pattern & 0x08 ) m_ic4_input_b |= 0x08; /* reel D tab */ else m_ic4_input_b &= ~0x08; } else { if (stepper_optic_state(m_active_reel)) { m_ic4_input_b |= 0x08; } else { m_ic4_input_b &= ~0x08; } } if ( m_signal_50hz ) m_ic4_input_b |= 0x04; /* 50 Hz */ else m_ic4_input_b &= ~0x04; if (m_ic4_input_b & 0x02) { m_ic4_input_b &= ~0x02; } else { m_ic4_input_b |= 0x02; //Pulse the overcurrent line with every read to show the CPU each lamp has lit } #ifdef UNUSED_FUNCTION if ( lamp_undercurrent ) m_ic4_input_b |= 0x01; #endif LOG_IC3(("%s: IC4 PIA Read of Port B %x\n",machine().describe_context(),m_ic4_input_b)); return m_ic4_input_b; } WRITE_LINE_MEMBER(mpu4_state::pia_ic4_ca2_w) { mpu4_state *drvstate = machine().driver_data(); LOG_IC3(("%s: IC4 PIA Write CA (input MUX strobe /LED B), %02X\n", machine().describe_context(),state)); m_IC23GB = state; ic23_update(drvstate); } WRITE_LINE_MEMBER(mpu4_state::pia_ic4_cb2_w) { LOG_IC3(("%s: IC4 PIA Write CA (input MUX strobe /LED B), %02X\n", machine().describe_context(),state)); m_reel_flag=state; } static const pia6821_interface pia_ic4_intf = { DEVCB_NULL, /* port A in */ DEVCB_DRIVER_MEMBER(mpu4_state,pia_ic4_portb_r), /* port B in */ DEVCB_NULL, /* line CA1 in */ DEVCB_NULL, /* line CB1 in */ DEVCB_NULL, /* line CA2 in */ DEVCB_NULL, /* line CB2 in */ DEVCB_DRIVER_MEMBER(mpu4_state,pia_ic4_porta_w), /* port A out */ DEVCB_DRIVER_MEMBER(mpu4_state,pia_ic4_portb_w), /* port B out */ DEVCB_DRIVER_LINE_MEMBER(mpu4_state,pia_ic4_ca2_w), /* line CA2 out */ DEVCB_DRIVER_LINE_MEMBER(mpu4_state,pia_ic4_cb2_w), /* line CB2 out */ DEVCB_DRIVER_LINE_MEMBER(mpu4_state,cpu0_irq), /* IRQA */ DEVCB_DRIVER_LINE_MEMBER(mpu4_state,cpu0_irq) /* IRQB */ }; /* IC5, AUX ports, coin lockouts and AY sound chip select (MODs below 4 only) */ READ8_MEMBER(mpu4_state::pia_ic5_porta_r) { if (m_lamp_extender == LARGE_CARD_A) { if (m_lamp_sense && m_ic23_active) { m_aux1_input |= 0x40; } else { m_aux1_input &= ~0x40; //Pulse the overcurrent line with every read to show the CPU each lamp has lit } } if (m_hopper == HOPPER_NONDUART_A) { /* if (hopper1_active) { m_aux1_input |= 0x04; } else { m_aux1_input &= ~0x04; }*/ } LOG(("%s: IC5 PIA Read of Port A (AUX1)\n",machine().describe_context())); return m_aux1_port->read()|m_aux1_input; } WRITE8_MEMBER(mpu4_state::pia_ic5_porta_w) { int i; mpu4_state *state = machine().driver_data(); pia6821_device *pia_ic4 = m_pia4; if (m_hopper == HOPPER_NONDUART_A) { //hopper1_drive_sensor(data&0x10); } switch (m_lamp_extender) { case NO_EXTENDER: if (m_led_extender == CARD_B) { led_write_latch(state, data & 0x1f, pia_ic4->a_output(),m_input_strobe); } else if ((m_led_extender != CARD_A)&&(m_led_extender != NO_EXTENDER)) { for(i=0; i<8; i++) { output_set_indexed_value("mpu4led",((m_input_strobe + 8) * 8) +i,(data & (1 << i)) !=0); } output_set_digit_value((m_input_strobe+8),data); } break; case SMALL_CARD: if(m_ic23_active) { lamp_extend_small(state,data); } break; case LARGE_CARD_A: lamp_extend_large(state,data,m_input_strobe,m_ic23_active); break; case LARGE_CARD_B: lamp_extend_large(state,data,m_input_strobe,m_ic23_active); if ((m_ic23_active) && m_card_live) { for(i=0; i<8; i++) { output_set_indexed_value("mpu4led",(((8*(m_last_b7 >>7))+ m_input_strobe) * 8) +i,(~data & (1 << i)) !=0); } output_set_digit_value(((8*(m_last_b7 >>7))+m_input_strobe),~data); } break; case LARGE_CARD_C: lamp_extend_large(state,data,m_input_strobe,m_ic23_active); break; } if (m_reel_mux == SIX_REEL_5TO8) { stepper_update(4, data&0x0F); stepper_update(5, (data >> 4)&0x0F); awp_draw_reel(4); awp_draw_reel(5); } else if (m_reel_mux == SEVEN_REEL) { stepper_update(1, data&0x0F); stepper_update(2, (data >> 4)&0x0F); awp_draw_reel(1); awp_draw_reel(2); } if (mame_stricmp(machine().system().name, "m4gambal") == 0) { /* The 'Gamball' device is a unique piece of mechanical equipment, designed to provide a truly fair hi-lo gamble for an AWP. Functionally, it consists of a ping-pong ball or similar enclosed in the machine's backbox, on a platform with 12 holes. When the low 4 bytes of AUX1 are triggered, this fires the ball out from the hole it's currently in, to land in another. Landing in the same hole causes the machine to refire the ball. The ball detection is done by the high 4 bytes of AUX1. Here we call the MAME RNG, once to pick a row, once to pick from the four pockets within it. We then trigger the switches corresponding to the correct number. This appears to be the best way of making the game fair, short of simulating the physics of a bouncing ball ;)*/ if (data & 0x0f) { switch ((machine().rand()>>5) % 0x3) { case 0x00: //Top row { switch (machine().rand() & 0x3) { case 0x00: //7 m_aux1_input = (m_aux1_input & 0x0f); m_aux1_input|= 0xa0; break; case 0x01://4 m_aux1_input = (m_aux1_input & 0x0f); m_aux1_input|= 0xb0; break; case 0x02://9 m_aux1_input = (m_aux1_input & 0x0f); m_aux1_input|= 0xc0; break; case 0x03://8 m_aux1_input = (m_aux1_input & 0x0f); m_aux1_input|= 0xd0; break; } } case 0x01: //Middle row - note switches don't match pattern { switch (machine().rand() & 0x3) { case 0x00://12 m_aux1_input = (m_aux1_input & 0x0f); m_aux1_input|= 0x40; break; case 0x01://1 m_aux1_input = (m_aux1_input & 0x0f); m_aux1_input|= 0x50; break; case 0x02://11 m_aux1_input = (m_aux1_input & 0x0f); m_aux1_input|= 0x80; break; case 0x03://2 m_aux1_input = (m_aux1_input & 0x0f); m_aux1_input|= 0x90; break; } } case 0x02: //Bottom row { switch (machine().rand() & 0x3) { case 0x00://5 m_aux1_input = (m_aux1_input & 0x0f); m_aux1_input|= 0x00; break; case 0x01://10 m_aux1_input = (m_aux1_input & 0x0f); m_aux1_input|= 0x10; break; case 0x02://3 m_aux1_input = (m_aux1_input & 0x0f); m_aux1_input|= 0x20; break; case 0x03://6 m_aux1_input = (m_aux1_input & 0x0f); m_aux1_input|= 0x30; break; } } } } } } WRITE8_MEMBER(mpu4_state::pia_ic5_portb_w) { if (m_hopper == HOPPER_NONDUART_B) { //hopper1_drive_motor(data &0x01) //hopper1_drive_sensor(data &0x08) } if (m_led_extender == CARD_A) { // led_write_latch(state, data & 0x07, pia_get_output_a(pia_ic4),m_input_strobe) } } READ8_MEMBER(mpu4_state::pia_ic5_portb_r) { pia6821_device *pia_ic5 = machine().device("pia_ic5"); if (m_hopper == HOPPER_NONDUART_B) {/* if (hopper1_active) { m_aux2_input |= 0x08; } else { m_aux2_input &= ~0x08; }*/ } LOG(("%s: IC5 PIA Read of Port B (coin input AUX2)\n",machine().describe_context())); coin_lockout_w(machine(), 0, (pia_ic5->b_output() & 0x01) ); coin_lockout_w(machine(), 1, (pia_ic5->b_output() & 0x02) ); coin_lockout_w(machine(), 2, (pia_ic5->b_output() & 0x04) ); coin_lockout_w(machine(), 3, (pia_ic5->b_output() & 0x08) ); return m_aux2_port->read() | m_aux2_input; } WRITE_LINE_MEMBER(mpu4_state::pia_ic5_ca2_w) { LOG(("%s: IC5 PIA Write CA2 (Serial Tx) %2x\n",machine().describe_context(),state)); m_serial_data = state; } /* --------------------------------------- AY Chip sound function selection - --------------------------------------- The databus of the AY sound chip is connected to IC6 Port A. Data is read from/written to the AY chip through this port. If this sounds familiar, Amstrad did something very similar with their home computers. The PSG function, defined by the BC1,BC2 and BDIR signals, is controlled by CA2 and CB2 of IC6. PSG function selection: ----------------------- BDIR = IC6 CB2 and BC1 = IC6 CA2 Pin | PSG Function BDIR BC1 | 0 0 | Inactive 0 1 | Read from selected PSG register. When function is set, the PSG will make the register data available to Port A. 1 0 | Write to selected PSG register. When set, the PSG will take the data at Port A and write it into the selected PSG register. 1 1 | Select PSG register. When set, the PSG will take the data at Port A and select a register. */ /* PSG function selected */ static void update_ay(device_t *device, address_space &space) { device_t *ay = device->machine().device("ay8913"); if (!ay) return; mpu4_state *state = device->machine().driver_data(); pia6821_device *pia = downcast(device); if (!pia->cb2_output()) { switch (state->m_ay8913_address) { case 0x00: { /* Inactive */ break; } case 0x01: { /* CA2 = 1 CB2 = 0? : Read from selected PSG register and make the register data available to Port A */ pia6821_device *pia_ic6 = device->machine().device("pia_ic6"); LOG(("AY8913 address = %d \n",pia_ic6->a_output()&0x0f)); break; } case 0x02: {/* CA2 = 0 CB2 = 1? : Write to selected PSG register and write data to Port A */ pia6821_device *pia_ic6 = device->machine().device("pia_ic6"); device_t *ay = device->machine().device("ay8913"); ay8910_data_w(ay, space, 0, pia_ic6->a_output()); LOG(("AY Chip Write \n")); break; } case 0x03: {/* CA2 = 1 CB2 = 1? : The register will now be selected and the user can read from or write to it. The register will remain selected until another is chosen.*/ pia6821_device *pia_ic6 = device->machine().device("pia_ic6"); device_t *ay = device->machine().device("ay8913"); ay8910_address_w(ay, space, 0, pia_ic6->a_output()); LOG(("AY Chip Select \n")); break; } default: { LOG(("AY Chip error \n")); } } } } WRITE_LINE_MEMBER(mpu4_state::pia_ic5_cb2_w) { device_t *device = machine().device("pia_ic5"); update_ay(device, generic_space()); } static const pia6821_interface pia_ic5_intf = { DEVCB_DRIVER_MEMBER(mpu4_state,pia_ic5_porta_r), /* port A in */ DEVCB_DRIVER_MEMBER(mpu4_state,pia_ic5_portb_r), /* port B in */ DEVCB_NULL, /* line CA1 in */ DEVCB_NULL, /* line CB1 in */ DEVCB_NULL, /* line CA2 in */ DEVCB_NULL, /* line CB2 in */ DEVCB_DRIVER_MEMBER(mpu4_state,pia_ic5_porta_w), /* port A out */ DEVCB_DRIVER_MEMBER(mpu4_state,pia_ic5_portb_w), /* port B out */ DEVCB_DRIVER_LINE_MEMBER(mpu4_state,pia_ic5_ca2_w), /* line CA2 out */ DEVCB_DRIVER_LINE_MEMBER(mpu4_state,pia_ic5_cb2_w), /* port CB2 out */ DEVCB_DRIVER_LINE_MEMBER(mpu4_state,cpu0_irq), /* IRQA */ DEVCB_DRIVER_LINE_MEMBER(mpu4_state,cpu0_irq) /* IRQB */ }; /* IC6, Reel A and B and AY registers (MODs below 4 only) */ WRITE8_MEMBER(mpu4_state::pia_ic6_portb_w) { LOG(("%s: IC6 PIA Port B Set to %2x (Reel A and B)\n", machine().describe_context(),data)); if (m_reel_mux == SEVEN_REEL) { stepper_update(3, data&0x0F); stepper_update(4, (data >> 4)&0x0F); awp_draw_reel(3); awp_draw_reel(4); } else if (m_reels) { stepper_update(0, data & 0x0F ); stepper_update(1, (data>>4) & 0x0F ); awp_draw_reel(0); awp_draw_reel(1); } if (m_reel_flag && (m_reel_mux == STANDARD_REEL) && m_reels) { if ( stepper_optic_state(0) ) m_optic_pattern |= 0x01; else m_optic_pattern &= ~0x01; if ( stepper_optic_state(1) ) m_optic_pattern |= 0x02; else m_optic_pattern &= ~0x02; } } WRITE8_MEMBER(mpu4_state::pia_ic6_porta_w) { device_t *device = machine().device("pia_ic6"); LOG(("%s: IC6 PIA Write A %2x\n", machine().describe_context(),data)); if (m_mod_number <4) { m_ay_data = data; update_ay(device, space); } } WRITE_LINE_MEMBER(mpu4_state::pia_ic6_ca2_w) { device_t *device = machine().device("pia_ic6"); LOG(("%s: IC6 PIA write CA2 %2x (AY8913 BC1)\n", machine().describe_context(),state)); if (m_mod_number <4) { if ( state ) m_ay8913_address |= 0x01; else m_ay8913_address &= ~0x01; update_ay(device, generic_space()); } } WRITE_LINE_MEMBER(mpu4_state::pia_ic6_cb2_w) { device_t *device = machine().device("pia_ic6"); LOG(("%s: IC6 PIA write CB2 %2x (AY8913 BCDIR)\n", machine().describe_context(),state)); if (m_mod_number <4) { if ( state ) m_ay8913_address |= 0x02; else m_ay8913_address &= ~0x02; update_ay(device, generic_space()); } } static const pia6821_interface pia_ic6_intf = { DEVCB_NULL, /* port A in */ DEVCB_NULL, /* port B in */ DEVCB_NULL, /* line CA1 in */ DEVCB_NULL, /* line CB1 in */ DEVCB_NULL, /* line CA2 in */ DEVCB_NULL, /* line CB2 in */ DEVCB_DRIVER_MEMBER(mpu4_state,pia_ic6_porta_w), /* port A out */ DEVCB_DRIVER_MEMBER(mpu4_state,pia_ic6_portb_w), /* port B out */ DEVCB_DRIVER_LINE_MEMBER(mpu4_state,pia_ic6_ca2_w), /* line CA2 out */ DEVCB_DRIVER_LINE_MEMBER(mpu4_state,pia_ic6_cb2_w), /* port CB2 out */ DEVCB_DRIVER_LINE_MEMBER(mpu4_state,cpu0_irq), /* IRQA */ DEVCB_DRIVER_LINE_MEMBER(mpu4_state,cpu0_irq) /* IRQB */ }; /* IC7 Reel C and D, mechanical meters/Reel E and F, input strobe bit A */ WRITE8_MEMBER(mpu4_state::pia_ic7_porta_w) { LOG(("%s: IC7 PIA Port A Set to %2x (Reel C and D)\n", machine().describe_context(),data)); if (m_reel_mux == SEVEN_REEL) { stepper_update(5, data&0x0F); stepper_update(6, (data >> 4)&0x0F); awp_draw_reel(5); awp_draw_reel(6); } else if (m_reels) { stepper_update(2, data & 0x0F ); stepper_update(3, (data>>4) & 0x0F ); awp_draw_reel(2); awp_draw_reel(3); } if (m_reel_flag && (m_reel_mux == STANDARD_REEL) && m_reels) { if ( stepper_optic_state(2) ) m_optic_pattern |= 0x04; else m_optic_pattern &= ~0x04; if ( stepper_optic_state(3) ) m_optic_pattern |= 0x08; else m_optic_pattern &= ~0x08; } } WRITE8_MEMBER(mpu4_state::pia_ic7_portb_w) { if (m_hopper == HOPPER_DUART_A) { //duart write data } else if (m_hopper == HOPPER_NONDUART_A) { //hoppr1_drive_motor(data & 0x10); } m_mmtr_data = data; } READ8_MEMBER(mpu4_state::pia_ic7_portb_r) { /* The meters are connected to a voltage drop sensor, where current flowing through them also passes through pin B7, meaning that when any meter is activated, pin B7 goes high. As for why they connected this to an output port rather than using CB1, no idea, although it proved of benefit when the reel multiplexer was designed as it allows a separate meter to be used when the rest of the port is blocked. This appears to have confounded the schematic drawer, who has assumed that all eight meters are driven from this port, giving the 8 line driver chip 9 connections in total. */ //This may be overkill, but the meter sensing is VERY picky int combined_meter = MechMtr_GetActivity(0) | MechMtr_GetActivity(1) | MechMtr_GetActivity(2) | MechMtr_GetActivity(3) | MechMtr_GetActivity(4) | MechMtr_GetActivity(5) | MechMtr_GetActivity(6) | MechMtr_GetActivity(7); if(combined_meter) { return 0x80; } else { return 0x00; } } WRITE_LINE_MEMBER(mpu4_state::pia_ic7_ca2_w) { mpu4_state *drvstate = machine().driver_data(); LOG(("%s: IC7 PIA write CA2 %2x (input strobe bit 0 / LED A)\n", machine().describe_context(),state)); m_IC23GA = state; ic24_setup(drvstate); ic23_update(drvstate); } WRITE_LINE_MEMBER(mpu4_state::pia_ic7_cb2_w) { m_remote_meter = state?0x80:0x00; } static const pia6821_interface pia_ic7_intf = { DEVCB_NULL, /* port A in */ DEVCB_DRIVER_MEMBER(mpu4_state,pia_ic7_portb_r), /* port B in */ DEVCB_NULL, /* line CA1 in */ DEVCB_NULL, /* line CB1 in */ DEVCB_NULL, /* line CA2 in */ DEVCB_NULL, /* line CB2 in */ DEVCB_DRIVER_MEMBER(mpu4_state,pia_ic7_porta_w), /* port A out */ DEVCB_DRIVER_MEMBER(mpu4_state,pia_ic7_portb_w), /* port B out */ DEVCB_DRIVER_LINE_MEMBER(mpu4_state,pia_ic7_ca2_w), /* line CA2 out */ DEVCB_DRIVER_LINE_MEMBER(mpu4_state,pia_ic7_cb2_w), /* line CB2 out */ DEVCB_DRIVER_LINE_MEMBER(mpu4_state,cpu0_irq), /* IRQA */ DEVCB_DRIVER_LINE_MEMBER(mpu4_state,cpu0_irq) /* IRQB */ }; /* IC8, Inputs, TRIACS, alpha clock */ READ8_MEMBER(mpu4_state::pia_ic8_porta_r) { ioport_port * portnames[] = { m_orange1_port, m_orange2_port, m_black1_port, m_black2_port, m_orange1_port, m_orange2_port, m_dil1_port, m_dil2_port }; LOG_IC8(("%s: IC8 PIA Read of Port A (MUX input data)\n", machine().describe_context())); /* The orange inputs are polled twice as often as the black ones, for reasons of efficiency. This is achieved via connecting every input line to an AND gate, thus allowing two strobes to represent each orange input bank (strobes are active low). */ m_pia5->cb1_w(m_aux2_port->read() & 0x80); return (portnames[m_input_strobe])->read(); } WRITE8_MEMBER(mpu4_state::pia_ic8_portb_w) { if (m_hopper == HOPPER_DUART_B) { // duart.drive_sensor(data & 0x04, data & 0x01, 0, 0); } else if (m_hopper == HOPPER_DUART_C) { // duart.drive_sensor(data & 0x04, data & 0x01, data & 0x04, data & 0x02); } int i; LOG_IC8(("%s: IC8 PIA Port B Set to %2x (OUTPUT PORT, TRIACS)\n", machine().describe_context(),data)); for (i = 0; i < 8; i++) { output_set_indexed_value("triac", i, data & (1 << i)); } } WRITE_LINE_MEMBER(mpu4_state::pia_ic8_ca2_w) { mpu4_state *drvstate = machine().driver_data(); LOG_IC8(("%s: IC8 PIA write CA2 (input_strobe bit 2 / LED C) %02X\n", machine().describe_context(), state & 0xFF)); m_IC23GC = state; ic23_update(drvstate); } WRITE_LINE_MEMBER(mpu4_state::pia_ic8_cb2_w) { LOG_IC8(("%s: IC8 PIA write CB2 (alpha clock) %02X\n", machine().describe_context(), state & 0xFF)); // DM Data pin B if (m_alpha_clock != state) { if (!m_alpha_clock)//falling edge { m_vfd->shift_data(m_alpha_data_line?0:1); } } m_alpha_clock = state; } static const pia6821_interface pia_ic8_intf = { DEVCB_DRIVER_MEMBER(mpu4_state,pia_ic8_porta_r), /* port A in */ DEVCB_NULL, /* port B in */ DEVCB_NULL, /* line CA1 in */ DEVCB_NULL, /* line CB1 in */ DEVCB_NULL, /* line CA2 in */ DEVCB_NULL, /* line CB2 in */ DEVCB_NULL, /* port A out */ DEVCB_DRIVER_MEMBER(mpu4_state,pia_ic8_portb_w), /* port B out */ DEVCB_DRIVER_LINE_MEMBER(mpu4_state,pia_ic8_ca2_w), /* line CA2 out */ DEVCB_DRIVER_LINE_MEMBER(mpu4_state,pia_ic8_cb2_w), /* port CB2 out */ DEVCB_DRIVER_LINE_MEMBER(mpu4_state,cpu0_irq), /* IRQA */ DEVCB_DRIVER_LINE_MEMBER(mpu4_state,cpu0_irq) /* IRQB */ }; // universal sampled sound program card PCB 683077 // Sampled sound card, using a PIA and PTM for timing and data handling WRITE8_MEMBER(mpu4_state::pia_gb_porta_w) { LOG_SS(("%s: GAMEBOARD: PIA Port A Set to %2x\n", machine().describe_context(),data)); okim6376_w(m_msm6376, space, 0, data); } WRITE8_MEMBER(mpu4_state::pia_gb_portb_w) { int changed = m_expansion_latch^data; LOG_SS(("%s: GAMEBOARD: PIA Port B Set to %2x\n", machine().describe_context(),data)); if ( changed & 0x20) { // digital volume clock line changed if ( !(data & 0x20) ) { // changed from high to low, if ( !(data & 0x10) )//down { if ( m_global_volume < 32 ) m_global_volume++; //steps unknown } else {//up if ( m_global_volume > 0 ) m_global_volume--; } { float percent = (32-m_global_volume)/32.0; m_msm6376->set_output_gain(0, percent); m_msm6376->set_output_gain(1, percent); } } } okim6376_ch2_w(m_msm6376,data&0x02); okim6376_st_w(m_msm6376,data&0x01); } READ8_MEMBER(mpu4_state::pia_gb_portb_r) { LOG_SS(("%s: GAMEBOARD: PIA Read of Port B\n",machine().describe_context())); int data=0; // b7 NAR - we can load another address into Channel 1 // b6, 1 = OKI ready, 0 = OKI busy // b5, vol clock // b4, 1 = Vol down, 0 = Vol up // if ( okim6376_nar_r(m_msm6376) ) data |= 0x80; else data &= ~0x80; if ( okim6376_busy_r(m_msm6376) ) data |= 0x40; else data &= ~0x40; return ( data | m_expansion_latch ); } WRITE_LINE_MEMBER(mpu4_state::pia_gb_ca2_w) { LOG_SS(("%s: GAMEBOARD: OKI RESET data = %02X\n", machine().describe_context(), state)); // reset line } WRITE_LINE_MEMBER(mpu4_state::pia_gb_cb2_w) { //Some BWB games use this to drive the bankswitching if (m_bwb_bank) { //printf("pia_gb_cb2_w %d\n", state); m_pageval = state; m_bank1->set_entry((m_pageval + (m_pageset ? 4 : 0)) & 0x07); } } static const pia6821_interface pia_ic4ss_intf = { DEVCB_NULL, /* port A in */ DEVCB_DRIVER_MEMBER(mpu4_state,pia_gb_portb_r), /* port B in */ DEVCB_NULL, /* line CA1 in */ DEVCB_NULL, /* line CB1 in */ DEVCB_NULL, /* line CA2 in */ DEVCB_NULL, /* line CB2 in */ DEVCB_DRIVER_MEMBER(mpu4_state,pia_gb_porta_w), /* port A out */ DEVCB_DRIVER_MEMBER(mpu4_state,pia_gb_portb_w), /* port B out */ DEVCB_DRIVER_LINE_MEMBER(mpu4_state,pia_gb_ca2_w), /* line CA2 out */ DEVCB_DRIVER_LINE_MEMBER(mpu4_state,pia_gb_cb2_w), /* line CB2 out */ DEVCB_NULL, /* IRQA */ DEVCB_NULL /* IRQB */ }; //Sampled sound timer /* The MSM6376 sound chip is configured in a slightly strange way, to enable dynamic sample rate changes (8Khz, 10.6 Khz, 16 KHz) by varying the clock. According to the BwB programmer's guide, the formula is: MSM6376 clock frequency:- freq = (1720000/((t3L+1)(t3H+1)))*[(t3H(T3L+1)+1)/(2(t1+1))] where [] means rounded up integer, t3L is the LSB of Clock 3, t3H is the MSB of Clock 3, and t1 is the initial value in clock 1. */ //O3 -> G1 O1 -> c2 o2 -> c1 WRITE8_MEMBER(mpu4_state::ic3ss_o1_callback) { device_t * device = machine().device("ptm_ic3ss"); downcast(device)->set_c2(data); } WRITE8_MEMBER(mpu4_state::ic3ss_o2_callback)//Generates 'beep' tone { device_t * device = machine().device("ptm_ic3ss"); downcast(device)->set_c1(data);//? } WRITE8_MEMBER(mpu4_state::ic3ss_o3_callback) { //downcast(device)->set_g1(data); /* this output is the clock for timer1 */ } /* This is a bit of a cheat - since we don't clock into the OKI chip directly, we need to calculate the oscillation frequency in advance. We're running the timer for interrupt purposes, but the frequency calculation is done by plucking the values out as they are written.*/ WRITE8_MEMBER(mpu4_state::ic3ss_w) { device_t *ic3ss = machine().device("ptm_ic3ss"); downcast(ic3ss)->write(offset,data); if (offset == 3) { m_t1 = data; } if (offset == 6) { m_t3h = data; } if (offset == 7) { m_t3l = data; } float num = (1720000/((m_t3l + 1)*(m_t3h + 1))); float denom1 = ((m_t3h *(m_t3l + 1)+ 1)/(2*(m_t1 + 1))); int denom2 = denom1 +0.5;//need to round up, this gives same precision as chip int freq=num*denom2; if (freq) { okim6376_set_frequency(m_msm6376, freq); } } static const ptm6840_interface ptm_ic3ss_intf = { MPU4_MASTER_CLOCK / 4, { 0, 0, 0 }, { DEVCB_DRIVER_MEMBER(mpu4_state,ic3ss_o1_callback), DEVCB_DRIVER_MEMBER(mpu4_state,ic3ss_o2_callback), DEVCB_DRIVER_MEMBER(mpu4_state,ic3ss_o3_callback) }, DEVCB_NULL//LINE(cpu1_ptm_irq) }; /* input ports for MPU4 board */ INPUT_PORTS_START( mpu4 ) PORT_START("ORANGE1") PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("00")// 20p level PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("01")// 100p level PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("02")// Token 1 level PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("03")// Token 2 level PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("04") PORT_CONFNAME( 0xE0, 0x00, "Stake Key" ) PORT_CONFSETTING( 0x00, "Not fitted / 5p" ) PORT_CONFSETTING( 0x20, "10p" ) PORT_CONFSETTING( 0x40, "20p" ) PORT_CONFSETTING( 0x60, "25p" ) PORT_CONFSETTING( 0x80, "30p" ) PORT_CONFSETTING( 0xA0, "40p" ) PORT_CONFSETTING( 0xC0, "50p" ) PORT_CONFSETTING( 0xE0, "1 GBP" ) PORT_START("ORANGE2") PORT_CONFNAME( 0x0F, 0x00, "Jackpot / Prize Key" ) PORT_CONFSETTING( 0x00, "Not fitted" ) PORT_CONFSETTING( 0x01, "3 GBP" ) PORT_CONFSETTING( 0x02, "4 GBP" ) PORT_CONFSETTING( 0x08, "5 GBP" ) PORT_CONFSETTING( 0x03, "6 GBP" ) PORT_CONFSETTING( 0x04, "6 GBP Token" ) PORT_CONFSETTING( 0x05, "8 GBP" ) PORT_CONFSETTING( 0x06, "8 GBP Token" ) PORT_CONFSETTING( 0x07, "10 GBP" ) PORT_CONFSETTING( 0x09, "15 GBP" ) PORT_CONFSETTING( 0x0A, "25 GBP" ) PORT_CONFSETTING( 0x0B, "25 GBP (Licensed Betting Office Profile)" ) PORT_CONFSETTING( 0x0C, "35 GBP" ) PORT_CONFSETTING( 0x0D, "70 GBP" ) PORT_CONFSETTING( 0x0E, "Reserved" ) PORT_CONFSETTING( 0x0F, "Reserved" ) PORT_CONFNAME( 0xF0, 0x00, "Percentage Key" ) PORT_CONFSETTING( 0x00, "Not fitted / 68% (Invalid for UK Games)" ) PORT_CONFSETTING( 0x10, "70" ) PORT_CONFSETTING( 0x20, "72" ) PORT_CONFSETTING( 0x30, "74" ) PORT_CONFSETTING( 0x40, "76" ) PORT_CONFSETTING( 0x50, "78" ) PORT_CONFSETTING( 0x60, "80" ) PORT_CONFSETTING( 0x70, "82" ) PORT_CONFSETTING( 0x80, "84" ) PORT_CONFSETTING( 0x90, "86" ) PORT_CONFSETTING( 0xA0, "88" ) PORT_CONFSETTING( 0xB0, "90" ) PORT_CONFSETTING( 0xC0, "92" ) PORT_CONFSETTING( 0xD0, "94" ) PORT_CONFSETTING( 0xE0, "96" ) PORT_CONFSETTING( 0xF0, "98" ) PORT_START("BLACK1") PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_NAME("Hi") PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_BUTTON2) PORT_NAME("Lo") PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("18") PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("19") PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("20") PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_SERVICE) PORT_NAME("Test Button") PORT_CODE(KEYCODE_W) PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_SERVICE) PORT_NAME("Refill Key") PORT_CODE(KEYCODE_R) PORT_TOGGLE PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_INTERLOCK) PORT_NAME("Cashbox (Back) Door") PORT_CODE(KEYCODE_Q) PORT_TOGGLE PORT_START("BLACK2") PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("24") PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("25") PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_BUTTON3) PORT_NAME("Cancel") PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_BUTTON4) PORT_NAME("Hold 1") PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_BUTTON5) PORT_NAME("Hold 2") PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_BUTTON6) PORT_NAME("Hold 3") PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_BUTTON7) PORT_NAME("Hold 4") PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_START1) PORT_START("DIL1") PORT_DIPNAME( 0x01, 0x00, DEF_STR( Unused ) ) PORT_DIPLOCATION("DIL1:01") PORT_DIPSETTING( 0x00, DEF_STR( Off ) ) PORT_DIPSETTING( 0x01, DEF_STR( On ) ) PORT_DIPNAME( 0x02, 0x00, DEF_STR( Unused ) ) PORT_DIPLOCATION("DIL1:02") PORT_DIPSETTING( 0x00, DEF_STR( Off ) ) PORT_DIPSETTING( 0x02, DEF_STR( On ) ) PORT_DIPNAME( 0x04, 0x00, DEF_STR( Unused ) ) PORT_DIPLOCATION("DIL1:03") PORT_DIPSETTING( 0x00, DEF_STR( Off ) ) PORT_DIPSETTING( 0x04, DEF_STR( On ) ) PORT_DIPNAME( 0x08, 0x00, DEF_STR( Unused ) ) PORT_DIPLOCATION("DIL1:04") PORT_DIPSETTING( 0x00, DEF_STR( Off ) ) PORT_DIPSETTING( 0x08, DEF_STR( On ) ) PORT_DIPNAME( 0xF0, 0x00, "Target Percentage (if key not fitted)" )PORT_DIPLOCATION("DIL1:05,06,07,08") PORT_DIPSETTING( 0x00, "Unset (Program Optimum)" ) PORT_DIPSETTING( 0x10, "70" ) PORT_DIPSETTING( 0x20, "72" ) PORT_DIPSETTING( 0x30, "74" ) PORT_DIPSETTING( 0x40, "76" ) PORT_DIPSETTING( 0x50, "78" ) PORT_DIPSETTING( 0x60, "80" ) PORT_DIPSETTING( 0x70, "82" ) PORT_DIPSETTING( 0x80, "84" ) PORT_DIPSETTING( 0x90, "86" ) PORT_DIPSETTING( 0xA0, "88" ) PORT_DIPSETTING( 0xB0, "90" ) PORT_DIPSETTING( 0xC0, "92" ) PORT_DIPSETTING( 0xD0, "94" ) PORT_DIPSETTING( 0xE0, "96" ) PORT_DIPSETTING( 0xF0, "98" ) PORT_START("DIL2") PORT_DIPNAME( 0x01, 0x00, "Token Lockout when full" ) PORT_DIPLOCATION("DIL2:01") PORT_DIPSETTING( 0x00, DEF_STR( Off ) ) PORT_DIPSETTING( 0x01, DEF_STR( On ) ) PORT_DIPNAME( 0x02, 0x00, DEF_STR( Unused )) PORT_DIPLOCATION("DIL2:02") PORT_DIPSETTING( 0x00, DEF_STR( Off ) ) PORT_DIPSETTING( 0x02, DEF_STR( On ) ) PORT_DIPNAME( 0x04, 0x00, "Scottish Coin Handling" ) PORT_DIPLOCATION("DIL2:03")//20p payout PORT_DIPSETTING( 0x00, DEF_STR( Off ) ) PORT_DIPSETTING( 0x04, DEF_STR( On ) ) PORT_DIPNAME( 0x08, 0x08, "Out of Credit Display Inhibit" ) PORT_DIPLOCATION("DIL2:04") // many games need this on to boot PORT_DIPSETTING( 0x00, DEF_STR( Off ) ) PORT_DIPSETTING( 0x08, DEF_STR( On ) ) PORT_DIPNAME( 0x10, 0x00, "OCD Audio Enable" ) PORT_DIPLOCATION("DIL2:05") PORT_DIPSETTING( 0x00, DEF_STR( Off ) ) PORT_DIPSETTING( 0x10, DEF_STR( On ) ) PORT_DIPNAME( 0x20, 0x00, "Coin Alarm Inhibit" ) PORT_DIPLOCATION("DIL2:06") PORT_DIPSETTING( 0x00, DEF_STR( Off ) ) PORT_DIPSETTING( 0x20, DEF_STR( On ) ) PORT_DIPNAME( 0x40, 0x00, "Token Refill Level Inhibit" ) PORT_DIPLOCATION("DIL2:07") PORT_DIPSETTING( 0x00, DEF_STR( Off ) ) PORT_DIPSETTING( 0x40, DEF_STR( On ) ) PORT_DIPNAME( 0x80, 0x00, "Single Credit Entry" ) PORT_DIPLOCATION("DIL2:08") PORT_DIPSETTING( 0x00, DEF_STR( Off ) ) PORT_DIPSETTING( 0x80, DEF_STR( On ) ) PORT_START("AUX1") PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("0") PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("1") PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("2") PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("3") PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("4") PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("5") PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("6") PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("7") PORT_START("AUX2") PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_SPECIAL) PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_SPECIAL) PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_SPECIAL) PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_SPECIAL) PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_COIN1) PORT_NAME("10p")//PORT_IMPULSE(5) PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_COIN2) PORT_NAME("20p")//PORT_IMPULSE(5) PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_COIN3) PORT_NAME("50p")//PORT_IMPULSE(5) PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_COIN4) PORT_NAME("100p")//PORT_IMPULSE(5) INPUT_PORTS_END INPUT_PORTS_START( mpu4jackpot8tkn ) PORT_INCLUDE( mpu4 ) PORT_MODIFY("ORANGE2") PORT_CONFNAME( 0x0F, 0x06, "Jackpot / Prize Key" ) PORT_CONFSETTING( 0x00, "Not fitted" ) PORT_CONFSETTING( 0x01, "3 GBP" ) PORT_CONFSETTING( 0x02, "4 GBP" ) PORT_CONFSETTING( 0x08, "5 GBP" ) PORT_CONFSETTING( 0x03, "6 GBP" ) PORT_CONFSETTING( 0x04, "6 GBP Token" ) PORT_CONFSETTING( 0x05, "8 GBP" ) PORT_CONFSETTING( 0x06, "8 GBP Token" ) PORT_CONFSETTING( 0x07, "10 GBP" ) PORT_CONFSETTING( 0x09, "15 GBP" ) PORT_CONFSETTING( 0x0A, "25 GBP" ) PORT_CONFSETTING( 0x0B, "25 GBP (Licensed Betting Office Profile)" ) PORT_CONFSETTING( 0x0C, "35 GBP" ) PORT_CONFSETTING( 0x0D, "70 GBP" ) PORT_CONFSETTING( 0x0E, "Reserved" ) PORT_CONFSETTING( 0x0F, "Reserved" ) INPUT_PORTS_END INPUT_PORTS_START( mpu4jackpot8per ) PORT_INCLUDE( mpu4 ) PORT_MODIFY("ORANGE2") PORT_CONFNAME( 0x0F, 0x06, "Jackpot / Prize Key" ) PORT_CONFSETTING( 0x00, "Not fitted" ) PORT_CONFSETTING( 0x01, "3 GBP" ) PORT_CONFSETTING( 0x02, "4 GBP" ) PORT_CONFSETTING( 0x08, "5 GBP" ) PORT_CONFSETTING( 0x03, "6 GBP" ) PORT_CONFSETTING( 0x04, "6 GBP Token" ) PORT_CONFSETTING( 0x05, "8 GBP" ) PORT_CONFSETTING( 0x06, "8 GBP Token" ) PORT_CONFSETTING( 0x07, "10 GBP" ) PORT_CONFSETTING( 0x09, "15 GBP" ) PORT_CONFSETTING( 0x0A, "25 GBP" ) PORT_CONFSETTING( 0x0B, "25 GBP (Licensed Betting Office Profile)" ) PORT_CONFSETTING( 0x0C, "35 GBP" ) PORT_CONFSETTING( 0x0D, "70 GBP" ) PORT_CONFSETTING( 0x0E, "Reserved" ) PORT_CONFSETTING( 0x0F, "Reserved" ) PORT_CONFNAME( 0xF0, 0x10, "Percentage Key" ) PORT_CONFSETTING( 0x00, "Not fitted / 68% (Invalid for UK Games)" ) PORT_CONFSETTING( 0x10, "70" ) PORT_CONFSETTING( 0x20, "72" ) PORT_CONFSETTING( 0x30, "74" ) PORT_CONFSETTING( 0x40, "76" ) PORT_CONFSETTING( 0x50, "78" ) PORT_CONFSETTING( 0x60, "80" ) PORT_CONFSETTING( 0x70, "82" ) PORT_CONFSETTING( 0x80, "84" ) PORT_CONFSETTING( 0x90, "86" ) PORT_CONFSETTING( 0xA0, "88" ) PORT_CONFSETTING( 0xB0, "90" ) PORT_CONFSETTING( 0xC0, "92" ) PORT_CONFSETTING( 0xD0, "94" ) PORT_CONFSETTING( 0xE0, "96" ) PORT_CONFSETTING( 0xF0, "98" ) INPUT_PORTS_END INPUT_PORTS_START( grtecp ) PORT_START("ORANGE1") PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("00")// 20p level PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("01")// 100p level PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("02")// Token 1 level PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("03")// Token 2 level PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("04") PORT_CONFNAME( 0xE0, 0x00, "Stake Key" ) PORT_CONFSETTING( 0x00, "Not fitted / 5p" ) PORT_CONFSETTING( 0x20, "10p" ) PORT_CONFSETTING( 0x40, "20p" ) PORT_CONFSETTING( 0x60, "25p" ) PORT_CONFSETTING( 0x80, "30p" ) PORT_CONFSETTING( 0xA0, "40p" ) PORT_CONFSETTING( 0xC0, "50p" ) PORT_CONFSETTING( 0xE0, "1 GBP" ) PORT_START("ORANGE2") PORT_CONFNAME( 0x0F, 0x00, "Jackpot / Prize Key" ) PORT_CONFSETTING( 0x00, "Not fitted" ) PORT_CONFSETTING( 0x01, "3 GBP" ) PORT_CONFSETTING( 0x02, "4 GBP" ) PORT_CONFSETTING( 0x08, "5 GBP" ) PORT_CONFSETTING( 0x03, "6 GBP" ) PORT_CONFSETTING( 0x04, "6 GBP Token" ) PORT_CONFSETTING( 0x05, "8 GBP" ) PORT_CONFSETTING( 0x06, "8 GBP Token" ) PORT_CONFSETTING( 0x07, "10 GBP" ) PORT_CONFSETTING( 0x09, "15 GBP" ) PORT_CONFSETTING( 0x0A, "25 GBP" ) PORT_CONFSETTING( 0x0B, "25 GBP (Licensed Betting Office Profile)" ) PORT_CONFSETTING( 0x0C, "35 GBP" ) PORT_CONFSETTING( 0x0D, "70 GBP" ) PORT_CONFSETTING( 0x0E, "Reserved" ) PORT_CONFSETTING( 0x0F, "Reserved" ) PORT_CONFNAME( 0xF0, 0x00, "Percentage Key" ) PORT_CONFSETTING( 0x00, "As Option Switches" ) PORT_CONFSETTING( 0x10, "70" ) PORT_CONFSETTING( 0x20, "72" ) PORT_CONFSETTING( 0x30, "74" ) PORT_CONFSETTING( 0x40, "76" ) PORT_CONFSETTING( 0x50, "78" ) PORT_CONFSETTING( 0x60, "80" ) PORT_CONFSETTING( 0x70, "82" ) PORT_CONFSETTING( 0x80, "84" ) PORT_CONFSETTING( 0x90, "86" ) PORT_CONFSETTING( 0xA0, "88" ) PORT_CONFSETTING( 0xB0, "90" ) PORT_CONFSETTING( 0xC0, "92" ) PORT_CONFSETTING( 0xD0, "94" ) PORT_CONFSETTING( 0xE0, "96" ) PORT_CONFSETTING( 0xF0, "98" ) PORT_START("BLACK1") PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_UNUSED) PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_UNUSED) PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_UNUSED) PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_UNUSED) PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_UNUSED) PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_SERVICE) PORT_NAME("Test Button") PORT_CODE(KEYCODE_W) PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_SERVICE) PORT_NAME("Refill Key") PORT_CODE(KEYCODE_R) PORT_TOGGLE PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_INTERLOCK) PORT_NAME("Cashbox (Back) Door") PORT_CODE(KEYCODE_Q) PORT_TOGGLE PORT_START("BLACK2") PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_NAME("Collect/Cancel") PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_BUTTON2) PORT_NAME("Hold 1") PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_BUTTON3) PORT_NAME("Hold 2") PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_BUTTON4) PORT_NAME("Hold 3") PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_BUTTON5) PORT_NAME("Hi") PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_BUTTON6) PORT_NAME("Lo") PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_BUTTON7) PORT_NAME("Exchange") PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_START1) PORT_START("DIL1") PORT_DIPNAME( 0x01, 0x00, DEF_STR( Unused ) ) PORT_DIPLOCATION("DIL1:01") PORT_DIPSETTING( 0x00, DEF_STR( Off ) ) PORT_DIPSETTING( 0x01, DEF_STR( On ) ) PORT_DIPNAME( 0x02, 0x00, DEF_STR( Unused ) ) PORT_DIPLOCATION("DIL1:02") PORT_DIPSETTING( 0x00, DEF_STR( Off ) ) PORT_DIPSETTING( 0x02, DEF_STR( On ) ) PORT_DIPNAME( 0x04, 0x00, DEF_STR( Unused ) ) PORT_DIPLOCATION("DIL1:03") PORT_DIPSETTING( 0x00, DEF_STR( Off ) ) PORT_DIPSETTING( 0x04, DEF_STR( On ) ) PORT_DIPNAME( 0x08, 0x00, DEF_STR( Unused ) ) PORT_DIPLOCATION("DIL1:04") PORT_DIPSETTING( 0x00, DEF_STR( Off ) ) PORT_DIPSETTING( 0x08, DEF_STR( On ) ) PORT_DIPNAME( 0xF0, 0x00, "Target Percentage (if key not fitted)" )PORT_DIPLOCATION("DIL1:05,06,07,08") PORT_DIPSETTING( 0x00, "Unset (Program Optimum)" ) PORT_DIPSETTING( 0x10, "70" ) PORT_DIPSETTING( 0x20, "72" ) PORT_DIPSETTING( 0x30, "74" ) PORT_DIPSETTING( 0x40, "76" ) PORT_DIPSETTING( 0x50, "78" ) PORT_DIPSETTING( 0x60, "80" ) PORT_DIPSETTING( 0x70, "82" ) PORT_DIPSETTING( 0x80, "84" ) PORT_DIPSETTING( 0x90, "86" ) PORT_DIPSETTING( 0xA0, "88" ) PORT_DIPSETTING( 0xB0, "90" ) PORT_DIPSETTING( 0xC0, "92" ) PORT_DIPSETTING( 0xD0, "94" ) PORT_DIPSETTING( 0xE0, "96" ) PORT_DIPSETTING( 0xF0, "98" ) PORT_START("DIL2") PORT_DIPNAME( 0x01, 0x00, "Token Lockout when full" ) PORT_DIPLOCATION("DIL2:01") PORT_DIPSETTING( 0x00, DEF_STR( Off ) ) PORT_DIPSETTING( 0x01, DEF_STR( On ) ) PORT_DIPNAME( 0x02, 0x00, DEF_STR( Unused )) PORT_DIPLOCATION("DIL2:02") PORT_DIPSETTING( 0x00, DEF_STR( Off ) ) PORT_DIPSETTING( 0x02, DEF_STR( On ) ) PORT_DIPNAME( 0x04, 0x00, "Scottish Coin Handling" ) PORT_DIPLOCATION("DIL2:03")//20p payout PORT_DIPSETTING( 0x00, DEF_STR( Off ) ) PORT_DIPSETTING( 0x04, DEF_STR( On ) ) PORT_DIPNAME( 0x08, 0x00, "Out of Credit Display Inhibit" ) PORT_DIPLOCATION("DIL2:04") PORT_DIPSETTING( 0x00, DEF_STR( Off ) ) PORT_DIPSETTING( 0x08, DEF_STR( On ) ) PORT_DIPNAME( 0x10, 0x00, "OCD Audio Enable" ) PORT_DIPLOCATION("DIL2:05") PORT_DIPSETTING( 0x00, DEF_STR( Off ) ) PORT_DIPSETTING( 0x10, DEF_STR( On ) ) PORT_DIPNAME( 0x20, 0x00, "Coin Alarm Inhibit" ) PORT_DIPLOCATION("DIL2:06") PORT_DIPSETTING( 0x00, DEF_STR( Off ) ) PORT_DIPSETTING( 0x20, DEF_STR( On ) ) PORT_DIPNAME( 0x40, 0x00, "Token Refill Level Inhibit" ) PORT_DIPLOCATION("DIL2:07") PORT_DIPSETTING( 0x00, DEF_STR( Off ) ) PORT_DIPSETTING( 0x40, DEF_STR( On ) ) PORT_DIPNAME( 0x80, 0x00, "Single Credit Entry" ) PORT_DIPLOCATION("DIL2:08") PORT_DIPSETTING( 0x00, DEF_STR( Off ) ) PORT_DIPSETTING( 0x80, DEF_STR( On ) ) PORT_START("AUX1") PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("0") PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("1") PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("2") PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("3") PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("4") PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("5") PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("6") PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_OTHER) PORT_NAME("7") PORT_START("AUX2") PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_SPECIAL) PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_SPECIAL) PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_SPECIAL) PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_SPECIAL) PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_COIN1) PORT_NAME("10p")//PORT_IMPULSE(5) PORT_BIT(0x20, IP_ACTIVE_HIGH, IPT_COIN2) PORT_NAME("20p")//PORT_IMPULSE(5) PORT_BIT(0x40, IP_ACTIVE_HIGH, IPT_COIN3) PORT_NAME("50p")//PORT_IMPULSE(5) PORT_BIT(0x80, IP_ACTIVE_HIGH, IPT_COIN4) PORT_NAME("100p")//PORT_IMPULSE(5) INPUT_PORTS_END static const stepper_interface barcrest_reel_interface = { BARCREST_48STEP_REEL, 1, 3, 0x00, 2 }; static const stepper_interface barcrest_opto1_interface = { BARCREST_48STEP_REEL, 4, 12, 0x00, 2 }; static const stepper_interface barcrest_opto2_interface = { BARCREST_48STEP_REEL, 92, 3, 0x00, 2 }; static const stepper_interface barcrest_opto3_interface = { BARCREST_48STEP_REEL, 0, 5, 0x00, 2 }; static const stepper_interface bwb_opto1_interface = { BARCREST_48STEP_REEL, 96, 3, 0x00, 2 }; /* Characteriser (CHR) As built, the CHR is a PAL which can perform basic bit manipulation according to an as yet unknown unique key. However, the programmers decided to best use this protection device in read/write/compare cycles, storing almost the entire 'hidden' data table in the ROMs in plain sight. Only later rebuilds by BwB avoided this 'feature' of the development kit, and will need a different setup. This information has been used to generate the CHR tables loaded by the programs, until a key can be determined. For most Barcrest games, the following method was used: The initial 'PALTEST' routine as found in the Barcrest programs simply writes the first 'call' to the CHR space, to read back the 'response'. There is no attempt to alter the order or anything else, just a simple runthrough of the entire data table. The only 'catch' in this is to note that the CHR chip always scans through the table starting at the last accessed data value, unless 00 is used to reset to the beginning. This is obviously a simplification, in fact the PAL does bit manipulation with some latching. However, a final 8 byte row, that controls the lamp matrix is not tested - to date, no-one outside of Barcrest knows how this is generated, and currently trial and error is the only sensible method. It is noted that the default, of all 00, is sometimes the correct answer, particularly in non-Barcrest use of the CHR chip, though when used normally, there are again fixed call values. */ WRITE8_MEMBER(mpu4_state::characteriser_w) { int x; int call=data; LOG_CHR_FULL(("%04x Characteriser write offset %02X data %02X", space.device().safe_pcbase(),offset,data)); if (!m_current_chr_table) { logerror("No Characteriser Table @ %04x\n", space.device().safe_pcbase()); return; } if (offset == 0) { { if (call == 0) { m_prot_col = 0; } else { for (x = m_prot_col; x < 64; x++) { if (m_current_chr_table[(x)].call == call) { m_prot_col = x; LOG_CHR(("Characteriser find column %02X\n",m_prot_col)); break; } } } } } else if (offset == 2) { LOG_CHR(("Characteriser write 2 data %02X\n",data)); switch (call) // Rather than the search strategy, we can map the calls directly here. Note that they are hex versions of the square number series { case 0x00: m_lamp_col = 0; break; case 0x01: m_lamp_col = 1; break; case 0x04: m_lamp_col = 2; break; case 0x09: m_lamp_col = 3; break; case 0x10: m_lamp_col = 4; break; case 0x19: m_lamp_col = 5; break; case 0x24: m_lamp_col = 6; break; case 0x31: m_lamp_col = 7; break; } LOG_CHR(("Characteriser find 2 column %02X\n",m_lamp_col)); } } READ8_MEMBER(mpu4_state::characteriser_r) { if (!m_current_chr_table) { logerror("No Characteriser Table @ %04x", space.device().safe_pcbase()); /* a cheat ... many early games use a standard check */ int addr = space.device().state().state_int(M6809_X); if ((addr>=0x800) && (addr<=0xfff)) return 0x00; // prevent recursion, only care about ram/rom areas for this cheat. UINT8 ret = space.read_byte(addr); logerror(" (returning %02x)",ret); logerror("\n"); return ret; } LOG_CHR(("Characteriser read offset %02X \n",offset)); if (offset == 0) { LOG_CHR(("Characteriser read data %02X \n",m_current_chr_table[m_prot_col].response)); return m_current_chr_table[m_prot_col].response; } if (offset == 3) { LOG_CHR(("Characteriser read data off 3 %02X \n",m_current_chr_table[m_lamp_col+64].response)); return m_current_chr_table[m_lamp_col+64].response; } return 0; } /* BwB Characteriser (CHR) The BwB method of protection is considerably different to the Barcrest one, with any incorrect behaviour manifesting in ridiculously large payouts. The hardware is the same, however the main weakness of the software has been eliminated. In fact, the software seems deliberately designed to mislead, but is (fortunately for us) prone to similar weaknesses that allow a per game solution. Project Amber performed a source analysis (available on request) which appears to make things work. Said weaknesses (A Cheats Guide according to Project Amber) The common initialisation sequence is "00 04 04 0C 0C 1C 14 2C 5C 2C" 0 1 2 3 4 5 6 7 8 Using debug search for the first read from said string (best to find it first). At this point, the X index on the CPU is at the magic number address. The subsequent calls for each can be found based on the magic address (0) = ( (BWBMagicAddress)) (1) = ( (BWBMagicAddress + 1)) (2) = ( (BWBMagicAddress + 2)) (3) = ( (BWBMagicAddress + 4)) (4) = ( (BWBMagicAddress - 5)) (5) = ( (BWBMagicAddress - 4)) (6) = ( (BWBMagicAddress - 3)) (7) = ( (BWBMagicAddress - 2)) (8) = ( (BWBMagicAddress - 1)) These return the standard init sequence as above. For ease of understanding, we use three tables, one holding the common responses and two holding the appropriate call and response pairs for the two stages of operation */ WRITE8_MEMBER(mpu4_state::bwb_characteriser_w) { int x; int call=data; LOG_CHR_FULL(("%04x Characteriser write offset %02X data %02X \n", space.device().safe_pcbase(),offset,data)); if (!m_current_chr_table) fatalerror("No Characteriser Table @ %04x\n", space.device().safe_pcbase()); if ((offset & 0x3f)== 0)//initialisation is always at 0x800 { if (!m_chr_state) { m_chr_state=1; m_chr_counter=0; } if (call == 0) { m_init_col ++; } else { m_init_col =0; } } m_chr_value = machine().rand(); for (x = 0; x < 4; x++) { if (m_current_chr_table[(x)].call == call) { if (x == 0) // reinit { m_bwb_return = 0; } m_chr_value = bwb_chr_table_common[(m_bwb_return)]; m_bwb_return++; break; } } } READ8_MEMBER(mpu4_state::bwb_characteriser_r) { LOG_CHR(("Characteriser read offset %02X \n",offset)); if (offset ==0) { switch (m_chr_counter) { case 6: case 13: case 20: case 27: case 34: { return m_bwb_chr_table1[(((m_chr_counter + 1) / 7) - 1)].response; } default: { if (m_chr_counter > 34) { m_chr_counter = 35; m_chr_state = 2; } m_chr_counter ++; return m_chr_value; } } } else { return m_chr_value; } } /* Common configurations */ WRITE8_MEMBER(mpu4_state::mpu4_ym2413_w) { device_t *ym = machine().device("ym2413"); if (ym) ym2413_w(ym,space,offset,data); } READ8_MEMBER(mpu4_state::mpu4_ym2413_r) { // device_t *ym = machine().device("ym2413"); // return ym2413_read(ym,offset); return 0xff; } void mpu4_install_mod4yam_space(address_space &space) { mpu4_state *state = space.machine().driver_data(); space.install_read_handler(0x0880, 0x0882, read8_delegate(FUNC(mpu4_state::mpu4_ym2413_r),state)); space.install_write_handler(0x0880, 0x0881, write8_delegate(FUNC(mpu4_state::mpu4_ym2413_w),state)); } void mpu4_install_mod4oki_space(address_space &space) { mpu4_state *state = space.machine().driver_data(); pia6821_device *pia_ic4ss = space.machine().device("pia_ic4ss"); ptm6840_device *ptm_ic3ss = space.machine().device("ptm_ic3ss"); space.install_readwrite_handler(0x0880, 0x0883, 0, 0, read8_delegate(FUNC(pia6821_device::read), pia_ic4ss), write8_delegate(FUNC(pia6821_device::write), pia_ic4ss)); space.install_read_handler(0x08c0, 0x08c7, 0, 0, read8_delegate(FUNC(ptm6840_device::read), ptm_ic3ss)); space.install_write_handler(0x08c0, 0x08c7, 0, 0, write8_delegate(FUNC(mpu4_state::ic3ss_w),state)); } void mpu4_install_mod4bwb_space(address_space &space) { mpu4_state *state = space.machine().driver_data(); space.install_readwrite_handler(0x0810, 0x0810, 0, 0, read8_delegate(FUNC(mpu4_state::bwb_characteriser_r),state),write8_delegate(FUNC(mpu4_state::bwb_characteriser_w),state)); mpu4_install_mod4oki_space(space); } void mpu4_config_common(running_machine &machine) { mpu4_state *state = machine.driver_data(); state->m_ic24_timer = machine.scheduler().timer_alloc(FUNC(ic24_timeout)); state->m_lamp_strobe_ext_persistence = 0; /* setup 8 mechanical meters */ MechMtr_config(machine,8); } static void mpu4_config_common_reels(running_machine &machine,int reels) { int n; /* setup n default 96 half step reels, using the standard optic flag */ for ( n = 0; n < reels; n++ ) { stepper_config(machine, n, &barcrest_reel_interface); } } MACHINE_START_MEMBER(mpu4_state,mod2) { mpu4_config_common(machine()); m_link7a_connected=0; m_mod_number=2; } MACHINE_START_MEMBER(mpu4_state,mpu4yam) { address_space &space = machine().device("maincpu")->memory().space(AS_PROGRAM); mpu4_config_common(machine()); m_link7a_connected=0; m_mod_number=4; mpu4_install_mod4yam_space(space); } MACHINE_START_MEMBER(mpu4_state,mpu4oki) { address_space &space = machine().device("maincpu")->memory().space(AS_PROGRAM); mpu4_config_common(machine()); m_link7a_connected=0; m_mod_number=4; mpu4_install_mod4oki_space(space); } MACHINE_START_MEMBER(mpu4_state,mpu4bwb) { address_space &space = machine().device("maincpu")->memory().space(AS_PROGRAM); mpu4_config_common(machine()); m_link7a_connected=0; m_mod_number=4; mpu4_install_mod4bwb_space(space); } MACHINE_START_MEMBER(mpu4_state,mpu4cry) { mpu4_config_common(machine()); m_link7a_connected=0; m_mod_number=4; } /* CHR Tables */ static mpu4_chr_table ccelbr_data[72] = { {0x00, 0x00},{0x1a, 0x84},{0x04, 0x8c},{0x10, 0xb8},{0x18, 0x74},{0x0f, 0x80},{0x13, 0x1c},{0x1b, 0xb4}, {0x03, 0xd8},{0x07, 0x74},{0x17, 0x00},{0x1d, 0xd4},{0x36, 0xc8},{0x35, 0x78},{0x2b, 0xa4},{0x28, 0x4c}, {0x39, 0xe0},{0x21, 0xdc},{0x22, 0xf4},{0x25, 0x88},{0x2c, 0x78},{0x29, 0x24},{0x31, 0x84},{0x34, 0xcc}, {0x0a, 0xb8},{0x1f, 0x74},{0x06, 0x90},{0x0e, 0x48},{0x1c, 0xa0},{0x12, 0x1c},{0x1e, 0x24},{0x0d, 0x94}, {0x14, 0xc8},{0x0a, 0xb8},{0x19, 0x74},{0x15, 0x00},{0x06, 0x94},{0x0f, 0x48},{0x08, 0x30},{0x1b, 0x90}, {0x1e, 0x08},{0x04, 0x60},{0x01, 0xd4},{0x0c, 0x58},{0x18, 0xf4},{0x1a, 0x18},{0x11, 0x74},{0x0b, 0x80}, {0x03, 0xdc},{0x17, 0x74},{0x10, 0xd0},{0x1d, 0x58},{0x0e, 0x24},{0x07, 0x94},{0x12, 0xd8},{0x09, 0x34}, {0x0d, 0x90},{0x1f, 0x58},{0x16, 0xf4},{0x05, 0x88},{0x13, 0x38},{0x1c, 0x24},{0x02, 0xd4},{0x00, 0x00}, {0x00, 0x00},{0x01, 0x50},{0x04, 0x00},{0x09, 0x50},{0x10, 0x10},{0x19, 0x40},{0x24, 0x04},{0x31, 0x00} }; static mpu4_chr_table gmball_data[72] = { {0x00, 0x00},{0x1a, 0x0c},{0x04, 0x50},{0x10, 0x90},{0x18, 0xb0},{0x0f, 0x38},{0x13, 0xd4},{0x1b, 0xa0}, {0x03, 0xbc},{0x07, 0xd4},{0x17, 0x30},{0x1d, 0x90},{0x36, 0x38},{0x35, 0xc4},{0x2b, 0xac},{0x28, 0x70}, {0x39, 0x98},{0x21, 0xdc},{0x22, 0xdc},{0x25, 0x54},{0x2c, 0x80},{0x29, 0xb4},{0x31, 0x38},{0x34, 0xcc}, {0x0a, 0xe8},{0x1f, 0xf8},{0x06, 0xd4},{0x0e, 0x30},{0x1c, 0x00},{0x12, 0x84},{0x1e, 0x2c},{0x0d, 0xc8}, {0x14, 0xf8},{0x0a, 0x4c},{0x19, 0x58},{0x15, 0xd4},{0x06, 0xa8},{0x0f, 0x78},{0x08, 0x44},{0x1b, 0x0c}, {0x1e, 0x48},{0x04, 0x50},{0x01, 0x98},{0x0c, 0xd4},{0x18, 0xb0},{0x1a, 0xa0},{0x11, 0xa4},{0x0b, 0x3c}, {0x03, 0xdc},{0x17, 0xd4},{0x10, 0xb8},{0x1d, 0xd4},{0x0e, 0x30},{0x07, 0x88},{0x12, 0xe0},{0x09, 0x24}, {0x0d, 0x8c},{0x1f, 0xf8},{0x16, 0xcc},{0x05, 0x70},{0x13, 0x90},{0x1c, 0x20},{0x02, 0x9c},{0x00, 0x00}, {0x00, 0x00},{0x01, 0x18},{0x04, 0x08},{0x09, 0x10},{0x10, 0x00},{0x19, 0x18},{0x24, 0x08},{0x31, 0x00} }; static mpu4_chr_table grtecp_data[72] = { {0x00, 0x00},{0x1a, 0x84},{0x04, 0xa4},{0x10, 0xac},{0x18, 0x70},{0x0f, 0x80},{0x13, 0x2c},{0x1b, 0xc0}, {0x03, 0xbc},{0x07, 0x5c},{0x17, 0x5c},{0x1d, 0x5c},{0x36, 0xdc},{0x35, 0x5c},{0x2b, 0xcc},{0x28, 0x68}, {0x39, 0xd0},{0x21, 0xb8},{0x22, 0xdc},{0x25, 0x54},{0x2c, 0x08},{0x29, 0x58},{0x31, 0x54},{0x34, 0x90}, {0x0a, 0xb8},{0x1f, 0x5c},{0x06, 0x5c},{0x0e, 0x44},{0x1c, 0x84},{0x12, 0xac},{0x1e, 0xe0},{0x0d, 0xbc}, {0x14, 0xcc},{0x0a, 0xe8},{0x19, 0x70},{0x15, 0x00},{0x06, 0x8c},{0x0f, 0x70},{0x08, 0x00},{0x1b, 0x84}, {0x1e, 0xa4},{0x04, 0xa4},{0x01, 0xbc},{0x0c, 0xdc},{0x18, 0x5c},{0x1a, 0xcc},{0x11, 0xe8},{0x0b, 0xe0}, {0x03, 0xbc},{0x17, 0x4c},{0x10, 0xc8},{0x1d, 0xf8},{0x0e, 0xd4},{0x07, 0xa8},{0x12, 0x68},{0x09, 0x40}, {0x0d, 0x0c},{0x1f, 0xd8},{0x16, 0xdc},{0x05, 0x54},{0x13, 0x98},{0x1c, 0x44},{0x02, 0x9c},{0x00, 0x00}, {0x00, 0x00},{0x01, 0x18},{0x04, 0x00},{0x09, 0x18},{0x10, 0x08},{0x19, 0x10},{0x24, 0x00},{0x31, 0x00} }; static mpu4_chr_table oldtmr_data[72] = { {0x00, 0x00},{0x1a, 0x90},{0x04, 0xc0},{0x10, 0x54},{0x18, 0xa4},{0x0f, 0xf0},{0x13, 0x64},{0x1b, 0x90}, {0x03, 0xe4},{0x07, 0xd4},{0x17, 0x60},{0x1d, 0xb4},{0x36, 0xc0},{0x35, 0x70},{0x2b, 0x80},{0x28, 0x74}, {0x39, 0xa4},{0x21, 0xf4},{0x22, 0xe4},{0x25, 0xd0},{0x2c, 0x64},{0x29, 0x10},{0x31, 0x20},{0x34, 0x90}, {0x0a, 0xe4},{0x1f, 0xf4},{0x06, 0xc4},{0x0e, 0x70},{0x1c, 0x00},{0x12, 0x14},{0x1e, 0x00},{0x0d, 0x14}, {0x14, 0xa0},{0x0a, 0xf0},{0x19, 0x64},{0x15, 0x10},{0x06, 0x84},{0x0f, 0x70},{0x08, 0x00},{0x1b, 0x90}, {0x1e, 0x40},{0x04, 0x90},{0x01, 0xe4},{0x0c, 0xf4},{0x18, 0x64},{0x1a, 0x90},{0x11, 0x64},{0x0b, 0x90}, {0x03, 0xe4},{0x17, 0x50},{0x10, 0x24},{0x1d, 0xb4},{0x0e, 0xe0},{0x07, 0xd4},{0x12, 0xe4},{0x09, 0x50}, {0x0d, 0x04},{0x1f, 0xb4},{0x16, 0xc0},{0x05, 0xd0},{0x13, 0x64},{0x1c, 0x90},{0x02, 0xe4},{0x00, 0x00}, {0x00, 0x00},{0x01, 0x00},{0x04, 0x00},{0x09, 0x00},{0x10, 0x00},{0x19, 0x10},{0x24, 0x00},{0x31, 0x00} }; static const bwb_chr_table blsbys_data1[5] = { //Magic number 724A // PAL Codes // 0 1 2 3 4 5 6 7 8 // ?? ?? 20 0F 24 3C 36 27 09 {0x67},{0x17},{0x0f},{0x24},{0x3c}, }; static mpu4_chr_table blsbys_data[8] = { {0xEF, 0x02},{0x81, 0x00},{0xCE, 0x00},{0x00, 0x2e}, {0x06, 0x20},{0xC6, 0x0f},{0xF8, 0x24},{0x8E, 0x3c}, }; // set percentage and other options. 2e 20 0f // PAL Codes // 0 1 2 3 4 5 6 7 8 // 42 2E 20 0F 24 3C 36 27 09 // 6 0 7 0 8 0 7 0 0 8 //request 36 42 27 42 09 42 27 42 42 09 //verify 00 04 04 0C 0C 1C 14 2C 5C 2C DRIVER_INIT_MEMBER(mpu4_state,m_oldtmr) { m_reel_mux=SIX_REEL_1TO8; m_reels = 6; stepper_config(machine(), 0, &barcrest_opto1_interface); stepper_config(machine(), 1, &barcrest_opto1_interface); stepper_config(machine(), 2, &barcrest_opto1_interface); stepper_config(machine(), 3, &barcrest_opto1_interface); stepper_config(machine(), 4, &barcrest_opto1_interface); stepper_config(machine(), 5, &barcrest_opto1_interface); m_current_chr_table = oldtmr_data; } DRIVER_INIT_MEMBER(mpu4_state,m4altreels) { m_reel_mux=SIX_REEL_1TO8; m_reels = 6; stepper_config(machine(), 0, &barcrest_opto1_interface); stepper_config(machine(), 1, &barcrest_opto1_interface); stepper_config(machine(), 2, &barcrest_opto1_interface); stepper_config(machine(), 3, &barcrest_opto1_interface); stepper_config(machine(), 4, &barcrest_opto1_interface); stepper_config(machine(), 5, &barcrest_opto1_interface); } DRIVER_INIT_MEMBER(mpu4_state,m_ccelbr) { m_reel_mux=STANDARD_REEL; m_reels = 4; // setup 4 default 96 half step reels /////////////////////////////////// mpu4_config_common_reels(machine(),4); m_current_chr_table = ccelbr_data; } DRIVER_INIT_MEMBER(mpu4_state,m4gambal) { m_reel_mux=STANDARD_REEL; m_reels = 4; // setup 4 default 96 half step reels /////////////////////////////////// mpu4_config_common_reels(machine(),4); m_current_chr_table = gmball_data; } DRIVER_INIT_MEMBER(mpu4_state,m_grtecp) { m_reel_mux=FIVE_REEL_5TO8; m_reels = 5; m_lamp_extender=SMALL_CARD; // setup 4 default 96 half step reels with the mux board mpu4_config_common_reels(machine(),5); m_current_chr_table = grtecp_data; } DRIVER_INIT_MEMBER(mpu4_state,m_blsbys) { m_bwb_bank=1; m_reel_mux=FIVE_REEL_5TO8; m_reels = 5; stepper_config(machine(), 0, &bwb_opto1_interface); stepper_config(machine(), 1, &bwb_opto1_interface); stepper_config(machine(), 2, &bwb_opto1_interface); stepper_config(machine(), 3, &bwb_opto1_interface); stepper_config(machine(), 4, &bwb_opto1_interface); m_bwb_chr_table1 = blsbys_data1; m_current_chr_table = blsbys_data; } DRIVER_INIT_MEMBER(mpu4_state,m4tst2) { m_reel_mux=STANDARD_REEL; m_reels = 4; mpu4_config_common_reels(machine(),4); } DRIVER_INIT_MEMBER(mpu4_state,m4tst) { m_reel_mux=STANDARD_REEL; m_reels = 4; mpu4_config_common_reels(machine(),4); } DRIVER_INIT_MEMBER(mpu4_state,m4default) { m_reel_mux=STANDARD_REEL; m_reels = 4; mpu4_config_common_reels(machine(),4); m_bwb_bank=0; } DRIVER_INIT_MEMBER(mpu4_state,m4default_alt) { m_reel_mux=STANDARD_REEL; m_reels = 8; stepper_config(machine(), 0, &barcrest_opto2_interface); stepper_config(machine(), 1, &barcrest_opto2_interface); stepper_config(machine(), 2, &barcrest_opto2_interface); stepper_config(machine(), 3, &barcrest_opto2_interface); stepper_config(machine(), 4, &barcrest_opto2_interface); stepper_config(machine(), 5, &barcrest_opto2_interface); stepper_config(machine(), 6, &barcrest_opto2_interface); stepper_config(machine(), 7, &barcrest_opto2_interface); m_bwb_bank=0; } DRIVER_INIT_MEMBER(mpu4_state,m4default_big) { address_space &space = machine().device("maincpu")->memory().space(AS_PROGRAM); DRIVER_INIT_CALL(m4default); int size = machine().root_device().memregion( "maincpu" )->bytes(); if (size<=0x10000) { printf("extended banking selected on set <=0x10000 in size, ignoring"); } else { m_bwb_bank=1; space.install_write_handler(0x0858, 0x0858, 0, 0, write8_delegate(FUNC(mpu4_state::bankswitch_w),this)); space.install_write_handler(0x0878, 0x0878, 0, 0, write8_delegate(FUNC(mpu4_state::bankset_w),this)); } } READ8_MEMBER(mpu4_state::crystal_sound_r) { return machine().rand(); } //this may be a YMZ280B WRITE8_MEMBER(mpu4_state::crystal_sound_w) { printf("crystal_sound_w %02x\n",data); } DRIVER_INIT_MEMBER(mpu4_state,m_frkstn) { address_space &space = machine().device("maincpu")->memory().space(AS_PROGRAM); DRIVER_INIT_CALL(m4default_big); space.install_read_handler(0x0880, 0x0880, 0, 0, read8_delegate(FUNC(mpu4_state::crystal_sound_r),this)); space.install_write_handler(0x0881, 0x0881, 0, 0, write8_delegate(FUNC(mpu4_state::crystal_sound_w),this)); } // thanks to Project Amber for descramble information void descramble_crystal( UINT8* region, int start, int end, UINT8 extra_xor) { for (int i=start;ibase(), 0x0000, 0x10000, 0x00); } DRIVER_INIT_MEMBER(mpu4_state,crystali) { DRIVER_INIT_CALL(m_frkstn); descramble_crystal(machine().root_device().memregion( "maincpu" )->base(), 0x0000, 0x10000, 0xff); // invert after decrypt?! } /* generate a 50 Hz signal (based on an RC time) */ TIMER_DEVICE_CALLBACK_MEMBER(mpu4_state::gen_50hz) { /* Although reported as a '50Hz' signal, the fact that both rising and falling edges of the pulse are used means the timer actually gives a 100Hz oscillating signal.*/ m_signal_50hz = m_signal_50hz?0:1; m_pia4->ca1_w(m_signal_50hz); /* signal is connected to IC4 CA1 */ update_meters(this);//run at 100Hz to sync with PIAs } static ADDRESS_MAP_START( mpu4_memmap, AS_PROGRAM, 8, mpu4_state ) AM_RANGE(0x0000, 0x07ff) AM_RAM AM_SHARE("nvram") AM_RANGE(0x0800, 0x0810) AM_READWRITE(characteriser_r,characteriser_w) AM_RANGE(0x0850, 0x0850) AM_READWRITE(bankswitch_r,bankswitch_w) /* write bank (rom page select) */ /* AM_RANGE(0x08e0, 0x08e7) AM_READWRITE_LEGACY(68681_duart_r,68681_duart_w) */ //Runs hoppers AM_RANGE(0x0900, 0x0907) AM_DEVREADWRITE("ptm_ic2", ptm6840_device, read, write)/* PTM6840 IC2 */ AM_RANGE(0x0a00, 0x0a03) AM_DEVREADWRITE("pia_ic3", pia6821_device, read, write) /* PIA6821 IC3 */ AM_RANGE(0x0b00, 0x0b03) AM_DEVREADWRITE("pia_ic4", pia6821_device, read, write) /* PIA6821 IC4 */ AM_RANGE(0x0c00, 0x0c03) AM_DEVREADWRITE("pia_ic5", pia6821_device, read, write) /* PIA6821 IC5 */ AM_RANGE(0x0d00, 0x0d03) AM_DEVREADWRITE("pia_ic6", pia6821_device, read, write) /* PIA6821 IC6 */ AM_RANGE(0x0e00, 0x0e03) AM_DEVREADWRITE("pia_ic7", pia6821_device, read, write) /* PIA6821 IC7 */ AM_RANGE(0x0f00, 0x0f03) AM_DEVREADWRITE("pia_ic8", pia6821_device, read, write) /* PIA6821 IC8 */ AM_RANGE(0x1000, 0xffff) AM_ROMBANK("bank1") /* 64k paged ROM (4 pages) */ ADDRESS_MAP_END const ay8910_interface ay8910_config = { AY8910_SINGLE_OUTPUT, {820,0,0}, DEVCB_NULL, DEVCB_NULL, DEVCB_NULL, DEVCB_NULL }; MACHINE_CONFIG_FRAGMENT( mpu4_common ) MCFG_TIMER_DRIVER_ADD_PERIODIC("50hz", mpu4_state, gen_50hz, attotime::from_hz(100)) MCFG_MSC1937_ADD("vfd",0,LEFT_TO_RIGHT) /* 6840 PTM */ MCFG_PTM6840_ADD("ptm_ic2", ptm_ic2_intf) MCFG_PIA6821_ADD("pia_ic3", pia_ic3_intf) MCFG_PIA6821_ADD("pia_ic4", pia_ic4_intf) MCFG_PIA6821_ADD("pia_ic5", pia_ic5_intf) MCFG_PIA6821_ADD("pia_ic6", pia_ic6_intf) MCFG_PIA6821_ADD("pia_ic7", pia_ic7_intf) MCFG_PIA6821_ADD("pia_ic8", pia_ic8_intf) MACHINE_CONFIG_END MACHINE_CONFIG_FRAGMENT( mpu4_common2 ) MCFG_PTM6840_ADD("ptm_ic3ss", ptm_ic3ss_intf) MCFG_PIA6821_ADD("pia_ic4ss", pia_ic4ss_intf) MACHINE_CONFIG_END /* machine driver for MOD 2 board */ MACHINE_CONFIG_START( mpu4base, mpu4_state ) MCFG_MACHINE_START_OVERRIDE(mpu4_state,mod2 ) MCFG_MACHINE_RESET_OVERRIDE(mpu4_state,mpu4) MCFG_CPU_ADD("maincpu", M6809, MPU4_MASTER_CLOCK/4) MCFG_CPU_PROGRAM_MAP(mpu4_memmap) MCFG_FRAGMENT_ADD(mpu4_common) MCFG_SPEAKER_STANDARD_STEREO("lspeaker", "rspeaker") MCFG_NVRAM_ADD_0FILL("nvram") MCFG_DEFAULT_LAYOUT(layout_mpu4) MACHINE_CONFIG_END MACHINE_CONFIG_DERIVED( mod2 , mpu4base ) MCFG_SOUND_ADD("ay8913",AY8913, MPU4_MASTER_CLOCK/4) MCFG_SOUND_CONFIG(ay8910_config) MCFG_SOUND_ROUTE(ALL_OUTPUTS, "lspeaker", 1.0) MCFG_SOUND_ROUTE(ALL_OUTPUTS, "rspeaker", 1.0) MACHINE_CONFIG_END MACHINE_CONFIG_DERIVED( mod4yam, mpu4base ) MCFG_MACHINE_START_OVERRIDE(mpu4_state,mpu4yam) MCFG_SOUND_ADD("ym2413", YM2413, MPU4_MASTER_CLOCK/4) MCFG_SOUND_ROUTE(ALL_OUTPUTS, "lspeaker", 1.0) MCFG_SOUND_ROUTE(ALL_OUTPUTS, "rspeaker", 1.0) MACHINE_CONFIG_END MACHINE_CONFIG_DERIVED( mod4oki, mpu4base ) MCFG_MACHINE_START_OVERRIDE(mpu4_state,mpu4oki) MCFG_FRAGMENT_ADD(mpu4_common2) MCFG_SOUND_ADD("msm6376", OKIM6376, 128000) //16KHz sample Can also be 85430 at 10.5KHz and 64000 at 8KHz MCFG_SOUND_ROUTE(ALL_OUTPUTS, "lspeaker", 1.0) MCFG_SOUND_ROUTE(ALL_OUTPUTS, "rspeaker", 1.0) MACHINE_CONFIG_END MACHINE_CONFIG_DERIVED( bwboki, mod4oki ) MCFG_MACHINE_START_OVERRIDE(mpu4_state,mpu4bwb) MACHINE_CONFIG_END MACHINE_CONFIG_DERIVED(mpu4crys, mod2 ) MCFG_MACHINE_START_OVERRIDE(mpu4_state,mpu4cry) MCFG_SOUND_ADD("upd", UPD7759, UPD7759_STANDARD_CLOCK) MCFG_SOUND_ROUTE(ALL_OUTPUTS, "lspeaker", 1.0) MCFG_SOUND_ROUTE(ALL_OUTPUTS, "rspeaker", 1.0) MACHINE_CONFIG_END