/************************************************************************* Driver for Williams/Midway Wolf-unit games. **************************************************************************/ #include "driver.h" #include "cpu/tms34010/tms34010.h" #include "cpu/m6809/m6809.h" #include "audio/dcs.h" #include "midwunit.h" #include "midwayic.h" /* code-related variables */ UINT8 * midwunit_decode_memory; /* CMOS-related variables */ static UINT8 cmos_write_enable; /* I/O-related variables */ static UINT16 iodata[8]; static UINT8 ioshuffle[16]; static UINT8 midxunit_analog_port; /* UART-related variables */ static UINT8 uart[8]; static UINT8 security_bits; /* prototype */ static READ16_HANDLER( midwunit_sound_state_r ); static void midxunit_dcs_output_full(int state); /************************************* * * State saving * *************************************/ static void register_state_saving(void) { state_save_register_global(cmos_write_enable); state_save_register_global_array(iodata); state_save_register_global_array(ioshuffle); state_save_register_global(midxunit_analog_port); state_save_register_global_array(uart); state_save_register_global(security_bits); } /************************************* * * CMOS reads/writes * *************************************/ WRITE16_HANDLER( midwunit_cmos_enable_w ) { cmos_write_enable = 1; } WRITE16_HANDLER( midwunit_cmos_w ) { if (cmos_write_enable) { COMBINE_DATA(generic_nvram16+offset); cmos_write_enable = 0; } else { logerror("%08X:Unexpected CMOS W @ %05X\n", activecpu_get_pc(), offset); popmessage("Bad CMOS write"); } } WRITE16_HANDLER( midxunit_cmos_w ) { COMBINE_DATA(generic_nvram16+offset); } READ16_HANDLER( midwunit_cmos_r ) { return generic_nvram16[offset]; } /************************************* * * General I/O writes * *************************************/ WRITE16_HANDLER( midwunit_io_w ) { int oldword, newword; offset %= 8; oldword = iodata[offset]; newword = oldword; COMBINE_DATA(&newword); switch (offset) { case 1: logerror("%08X:Control W @ %05X = %04X\n", activecpu_get_pc(), offset, data); /* bit 4 reset sound CPU */ dcs_reset_w(newword & 0x10); /* bit 5 (active low) reset security chip */ midway_serial_pic_reset_w(newword & 0x20); break; case 3: /* watchdog reset */ /* MK3 resets with this enabled */ /* watchdog_reset_w(0,0);*/ break; default: logerror("%08X:Unknown I/O write to %d = %04X\n", activecpu_get_pc(), offset, data); break; } iodata[offset] = newword; } WRITE16_HANDLER( midxunit_io_w ) { int oldword, newword; offset = (offset / 2) % 8; oldword = iodata[offset]; newword = oldword; COMBINE_DATA(&newword); switch (offset) { case 2: /* watchdog reset */ // watchdog_reset_w(0,0); break; default: logerror("%08X:I/O write to %d = %04X\n", activecpu_get_pc(), offset, data); // logerror("%08X:Unknown I/O write to %d = %04X\n", activecpu_get_pc(), offset, data); break; } iodata[offset] = newword; } WRITE16_HANDLER( midxunit_unknown_w ) { int offs = offset / 0x40000; if (ACCESSING_LSB && offset % 0x40000 == 0) logerror("%08X:midxunit_unknown_w @ %d = %02X\n", activecpu_get_pc(), offs, data & 0xff); } /************************************* * * General I/O reads * *************************************/ READ16_HANDLER( midwunit_io_r ) { /* apply I/O shuffling */ offset = ioshuffle[offset % 16]; switch (offset) { case 0: case 1: case 2: case 3: return readinputport(offset); case 4: return (midway_serial_pic_status_r() << 12) | midwunit_sound_state_r(0,0); default: logerror("%08X:Unknown I/O read from %d\n", activecpu_get_pc(), offset); break; } return ~0; } READ16_HANDLER( midxunit_io_r ) { offset = (offset / 2) % 8; switch (offset) { case 0: case 1: case 2: case 3: return readinputport(offset); default: logerror("%08X:Unknown I/O read from %d\n", activecpu_get_pc(), offset); break; } return ~0; } READ16_HANDLER( midxunit_analog_r ) { return readinputport(midxunit_analog_port); } WRITE16_HANDLER( midxunit_analog_select_w ) { if (offset == 0 && ACCESSING_LSB) midxunit_analog_port = data - 8 + 4; } READ16_HANDLER( midxunit_status_r ) { /* low bit indicates whether the ADC is done reading the current input */ return (midway_serial_pic_status_r() << 1) | 1; } /************************************* * * Revolution X UART * *************************************/ void midxunit_dcs_output_full(int state) { /* only signal if not in loopback state */ if (uart[1] != 0x66) cpunum_set_input_line(1, 1, state ? ASSERT_LINE : CLEAR_LINE); } READ16_HANDLER( midxunit_uart_r ) { int result = 0; /* convert to a byte offset */ if (offset & 1) return 0; offset /= 2; /* switch off the offset */ switch (offset) { case 0: /* register 0 must return 0x13 in order to pass the self test */ result = 0x13; break; case 1: /* register 1 contains the status */ /* loopback case: data always ready, and always ok to send */ if (uart[1] == 0x66) result |= 5; /* non-loopback case: bit 0 means data ready, bit 2 means ok to send */ else { int temp = midwunit_sound_state_r(0, 0); result |= (temp & 0x800) >> 9; result |= (~temp & 0x400) >> 10; timer_call_after_resynch(0, 0); } break; case 3: /* register 3 contains the data read */ /* loopback case: feed back last data wrtten */ if (uart[1] == 0x66) result = uart[3]; /* non-loopback case: read from the DCS system */ else result = midwunit_sound_r(0, 0); break; case 5: /* register 5 seems to be like 3, but with in/out swapped */ /* loopback case: data always ready, and always ok to send */ if (uart[1] == 0x66) result |= 5; /* non-loopback case: bit 0 means data ready, bit 2 means ok to send */ else { int temp = midwunit_sound_state_r(0, 0); result |= (temp & 0x800) >> 11; result |= (~temp & 0x400) >> 8; timer_call_after_resynch(0, 0); } break; default: /* everyone else reads themselves */ result = uart[offset]; break; } /* logerror("%08X:UART R @ %X = %02X\n", activecpu_get_pc(), offset, result);*/ return result; } WRITE16_HANDLER( midxunit_uart_w ) { /* convert to a byte offset, ignoring MSB writes */ if ((offset & 1) || !ACCESSING_LSB) return; offset /= 2; data &= 0xff; /* switch off the offset */ switch (offset) { case 3: /* register 3 contains the data to be sent */ /* loopback case: don't feed through */ if (uart[1] == 0x66) uart[3] = data; /* non-loopback case: send to the DCS system */ else midwunit_sound_w(0, data, mem_mask); break; case 5: /* register 5 write seems to reset things */ dcs_data_r(); break; default: /* everyone else just stores themselves */ uart[offset] = data; break; } /* logerror("%08X:UART W @ %X = %02X\n", activecpu_get_pc(), offset, data);*/ } /************************************* * * Generic driver init * *************************************/ static void init_wunit_generic(void) { UINT8 *base; int i, j; /* register for state saving */ register_state_saving(); /* load the graphics ROMs -- quadruples */ midyunit_gfx_rom = base = memory_region(REGION_GFX1); for (i = 0; i < memory_region_length(REGION_GFX1) / 0x400000; i++) { memcpy(midwunit_decode_memory, base, 0x400000); for (j = 0; j < 0x100000; j++) { *base++ = midwunit_decode_memory[0x000000 + j]; *base++ = midwunit_decode_memory[0x100000 + j]; *base++ = midwunit_decode_memory[0x200000 + j]; *base++ = midwunit_decode_memory[0x300000 + j]; } } /* init sound */ dcs_init(); } /************************************* * * Wolf-unit init (DCS) * * music: ADSP2101 * *************************************/ /********************** Mortal Kombat 3 **********************/ static void init_mk3_common(void) { /* common init */ init_wunit_generic(); /* serial prefixes 439, 528 */ midway_serial_pic_init(528); } DRIVER_INIT( mk3 ) { init_mk3_common(); } DRIVER_INIT( mk3r20 ) { init_mk3_common(); } DRIVER_INIT( mk3r10 ) { init_mk3_common(); } DRIVER_INIT( umk3 ) { init_mk3_common(); } DRIVER_INIT( umk3r11 ) { init_mk3_common(); } /********************** 2 On 2 Open Ice Challenge **********************/ DRIVER_INIT( openice ) { /* common init */ init_wunit_generic(); /* serial prefixes 438, 528 */ midway_serial_pic_init(528); } /********************** NBA Hangtime & NBA Maximum Hangtime **********************/ DRIVER_INIT( nbahangt ) { /* common init */ init_wunit_generic(); /* serial prefixes 459, 470, 528 */ midway_serial_pic_init(528); } /********************** WWF Wrestlemania **********************/ static WRITE16_HANDLER( wwfmania_io_0_w ) { int i; /* start with the originals */ for (i = 0; i < 16; i++) ioshuffle[i] = i % 8; /* based on the data written, shuffle */ switch (data) { case 0: break; case 1: ioshuffle[4] = 0; ioshuffle[8] = 1; ioshuffle[1] = 2; ioshuffle[9] = 3; ioshuffle[2] = 4; break; case 2: ioshuffle[8] = 0; ioshuffle[2] = 1; ioshuffle[4] = 2; ioshuffle[6] = 3; ioshuffle[1] = 4; break; case 3: ioshuffle[1] = 0; ioshuffle[8] = 1; ioshuffle[2] = 2; ioshuffle[10] = 3; ioshuffle[5] = 4; break; case 4: ioshuffle[2] = 0; ioshuffle[4] = 1; ioshuffle[1] = 2; ioshuffle[7] = 3; ioshuffle[8] = 4; break; } logerror("Changed I/O swiching to %d\n", data); } DRIVER_INIT( wwfmania ) { /* common init */ init_wunit_generic(); /* enable I/O shuffling */ memory_install_write16_handler(0, ADDRESS_SPACE_PROGRAM, 0x01800000, 0x0180000f, 0, 0, wwfmania_io_0_w); /* serial prefixes 430, 528 */ midway_serial_pic_init(528); } /********************** Rampage World Tour **********************/ DRIVER_INIT( rmpgwt ) { /* common init */ init_wunit_generic(); /* serial prefixes 465, 528 */ midway_serial_pic_init(528); } /********************** Revolution X **********************/ DRIVER_INIT( revx ) { UINT8 *base; int i, j; /* register for state saving */ register_state_saving(); /* load the graphics ROMs -- quadruples */ midyunit_gfx_rom = base = memory_region(REGION_GFX1); for (i = 0; i < memory_region_length(REGION_GFX1) / 0x200000; i++) { memcpy(midwunit_decode_memory, base, 0x200000); for (j = 0; j < 0x80000; j++) { *base++ = midwunit_decode_memory[0x000000 + j]; *base++ = midwunit_decode_memory[0x080000 + j]; *base++ = midwunit_decode_memory[0x100000 + j]; *base++ = midwunit_decode_memory[0x180000 + j]; } } /* init sound */ dcs_init(); /* serial prefixes 419, 420 */ midway_serial_pic_init(419); } /************************************* * * Machine init * *************************************/ MACHINE_RESET( midwunit ) { int i; /* reset sound */ dcs_reset_w(1); dcs_reset_w(0); /* reset I/O shuffling */ for (i = 0; i < 16; i++) ioshuffle[i] = i % 8; } MACHINE_RESET( midxunit ) { machine_reset_midwunit(machine); dcs_set_io_callbacks(midxunit_dcs_output_full, NULL); } /************************************* * * Security chip I/O * *************************************/ READ16_HANDLER( midwunit_security_r ) { return midway_serial_pic_r(); } WRITE16_HANDLER( midwunit_security_w ) { if (offset == 0 && ACCESSING_LSB) midway_serial_pic_w(data); } WRITE16_HANDLER( midxunit_security_w ) { if (ACCESSING_LSB) security_bits = data & 0x0f; } WRITE16_HANDLER( midxunit_security_clock_w ) { if (offset == 0 && ACCESSING_LSB) midway_serial_pic_w(((~data & 2) << 3) | security_bits); } /************************************* * * Sound write handlers * *************************************/ READ16_HANDLER( midwunit_sound_r ) { logerror("%08X:Sound read\n", activecpu_get_pc()); return dcs_data_r() & 0xff; } READ16_HANDLER( midwunit_sound_state_r ) { return dcs_control_r(); } WRITE16_HANDLER( midwunit_sound_w ) { /* check for out-of-bounds accesses */ if (offset) { logerror("%08X:Unexpected write to sound (hi) = %04X\n", activecpu_get_pc(), data); return; } /* call through based on the sound type */ if (ACCESSING_LSB) { logerror("%08X:Sound write = %04X\n", activecpu_get_pc(), data); dcs_data_w(data & 0xff); } }