diff options
Diffstat (limited to 'src/mame/machine/leland.c')
-rw-r--r-- | src/mame/machine/leland.c | 1494 |
1 files changed, 1494 insertions, 0 deletions
diff --git a/src/mame/machine/leland.c b/src/mame/machine/leland.c new file mode 100644 index 00000000000..bf0dfe6d6d2 --- /dev/null +++ b/src/mame/machine/leland.c @@ -0,0 +1,1494 @@ +/*************************************************************************** + + Cinemat/Leland driver + + driver by Aaron Giles and Paul Leaman + +***************************************************************************/ + +#include "driver.h" +#include "machine/eeprom.h" +#include "cpu/z80/z80.h" +#include "leland.h" +#include "sound/ay8910.h" + + +/************************************* + * + * Debugging + * + *************************************/ + +/* define these to 0 to disable, or to 1 to enable */ +#define LOG_KEYCARDS 0 +#define LOG_KEYCARDS_FULL 0 +#define LOG_BANKSWITCHING_M 0 +#define LOG_BANKSWITCHING_S 0 +#define LOG_SOUNDPORT 0 +#define LOG_EEPROM 0 +#define LOG_BATTERY_RAM 0 +#define LOG_XROM 0 + + + +/************************************* + * + * Global variables + * + *************************************/ + +UINT8 leland_dac_control; + +static UINT8 leland_gfx_control; +static UINT8 wcol_enable; +static emu_timer *master_int_timer; + +static UINT8 *master_base; +static UINT8 *slave_base; +static UINT8 *xrom_base; +static UINT32 master_length; +static UINT32 slave_length; +static UINT32 xrom_length; + +static int dangerz_x, dangerz_y; +static UINT8 analog_result; +static UINT8 dial_last_input[4]; +static UINT8 dial_last_result[4]; + +static UINT8 keycard_shift; +static UINT8 keycard_bit; +static UINT8 keycard_state; +static UINT8 keycard_clock; +static UINT8 keycard_command[3]; + +static UINT8 top_board_bank; +static UINT8 sound_port_bank; +static UINT8 alternate_bank; +static UINT8 master_bank; +void (*leland_update_master_bank)(void); + +static UINT32 xrom1_addr; +static UINT32 xrom2_addr; + +static UINT8 battery_ram_enable; +static UINT8 *battery_ram; + +static UINT8 *extra_tram; + +static UINT8 eeprom_data[128*2]; + + +/* Internal routines */ +static TIMER_CALLBACK( leland_interrupt_callback ); +static TIMER_CALLBACK( ataxx_interrupt_callback ); + + + +/************************************* + * + * Generic dial encoding + * + *************************************/ + +static int dial_compute_value(int new_val, int indx) +{ + int delta = new_val - (int)dial_last_input[indx]; + UINT8 result = dial_last_result[indx] & 0x80; + + dial_last_input[indx] = new_val; + + if (delta > 0x80) + delta -= 0x100; + else if (delta < -0x80) + delta += 0x100; + + if (delta < 0) + { + result = 0x80; + delta = -delta; + } + else if (delta > 0) + result = 0x00; + + if (delta > 0x1f) + delta = 0x1f; + result |= (dial_last_result[indx] + delta) & 0x1f; + + dial_last_result[indx] = result; + return result; +} + + + +/************************************* + * + * Cerberus inputs + * + *************************************/ + +READ8_HANDLER( cerberus_dial_1_r ) +{ + int original = readinputport(0); + int modified = dial_compute_value(readinputport(4), 0); + return (original & 0xc0) | ((modified & 0x80) >> 2) | (modified & 0x1f); +} + + +READ8_HANDLER( cerberus_dial_2_r ) +{ + int original = readinputport(2); + int modified = dial_compute_value(readinputport(5), 1); + return (original & 0xc0) | ((modified & 0x80) >> 2) | (modified & 0x1f); +} + + + +/************************************* + * + * Alley Master inputs + * + *************************************/ + +UINT8 *alleymas_kludge_mem; + +WRITE8_HANDLER( alleymas_joystick_kludge ) +{ + /* catch the case where they clear this memory location at PC $1827 and change */ + /* the value written to be a 1 */ + if (safe_activecpu_get_pc() == 0x1827) + *alleymas_kludge_mem = 1; + else + *alleymas_kludge_mem = data; + + /* while we're here, make sure the first 3 characters in battery RAM are a */ + /* valid name; otherwise, it will crash if you start a game and don't enter */ + /* your name */ + if (battery_ram[0] == 0) + { + battery_ram[0] = 'C'; + battery_ram[1] = 'I'; + battery_ram[2] = 'N'; + } +} + + + +/************************************* + * + * Danger Zone inputs + * + *************************************/ + +static void update_dangerz_xy(void) +{ + UINT8 newy = readinputport(4); + UINT8 newx = readinputport(5); + int deltay = newy - dial_last_input[0]; + int deltax = newx - dial_last_input[1]; + + if (deltay <= -128) deltay += 256; + else if (deltay >= 128) deltay -= 256; + if (deltax <= -128) deltax += 256; + else if (deltax >= 128) deltax -= 256; + + dangerz_y += deltay; + dangerz_x += deltax; + if (dangerz_y < 0) dangerz_y = 0; + else if (dangerz_y >= 1024) dangerz_y = 1023; + if (dangerz_x < 0) dangerz_x = 0; + else if (dangerz_x >= 1024) dangerz_x = 1023; + + dial_last_input[0] = newy; + dial_last_input[1] = newx; +} + + +READ8_HANDLER( dangerz_input_y_r ) +{ + update_dangerz_xy(); + return dangerz_y & 0xff; +} + + +READ8_HANDLER( dangerz_input_x_r ) +{ + update_dangerz_xy(); + return dangerz_x & 0xff; +} + + +READ8_HANDLER( dangerz_input_upper_r ) +{ + update_dangerz_xy(); + return ((dangerz_y >> 2) & 0xc0) | ((dangerz_x >> 8) & 0x03); +} + + + +/************************************* + * + * Red Line Racer inputs + * + *************************************/ + +static const UINT8 redline_pedal_value[8] = { 0xf0, 0xe0, 0xc0, 0xd0, 0x90, 0xb0, 0x30, 0x70 }; + +READ8_HANDLER( redline_pedal_1_r ) +{ + int pedal = readinputport(0); + return redline_pedal_value[pedal >> 5] | 0x0f; +} + + +READ8_HANDLER( redline_pedal_2_r ) +{ + int pedal = readinputport(2); + return redline_pedal_value[pedal >> 5] | 0x0f; +} + + +READ8_HANDLER( redline_wheel_1_r ) +{ + return dial_compute_value(readinputport(4), 0); +} + + +READ8_HANDLER( redline_wheel_2_r ) +{ + return dial_compute_value(readinputport(5), 1); +} + + + +/************************************* + * + * Super Offroad inputs + * + *************************************/ + +READ8_HANDLER( offroad_wheel_1_r ) +{ + return dial_compute_value(readinputport(7), 0); +} + + +READ8_HANDLER( offroad_wheel_2_r ) +{ + return dial_compute_value(readinputport(8), 1); +} + + +READ8_HANDLER( offroad_wheel_3_r ) +{ + return dial_compute_value(readinputport(9), 2); +} + + + +/************************************* + * + * Ataxx inputs + * + *************************************/ + +READ8_HANDLER( ataxx_trackball_r ) +{ + return dial_compute_value(readinputport(3 + offset), offset); +} + + + +/************************************* + * + * Indy Heat inputs + * + *************************************/ + +READ8_HANDLER( indyheat_wheel_r ) +{ + return dial_compute_value(readinputport(3 + offset), offset); +} + + +READ8_HANDLER( indyheat_analog_r ) +{ + switch (offset) + { + case 0: + return 0; + + case 1: + return analog_result; + + case 2: + return 0; + + case 3: + logerror("Unexpected analog read(%02X)\n", 8 + offset); + break; + } + return 0xff; +} + + +WRITE8_HANDLER( indyheat_analog_w ) +{ + switch (offset) + { + case 3: + analog_result = readinputport(6 + data); + break; + + case 0: + case 1: + case 2: + logerror("Unexpected analog write(%02X) = %02X\n", 8 + offset, data); + break; + } +} + + + +/************************************* + * + * Machine initialization + * + *************************************/ + +MACHINE_START( leland ) +{ + /* allocate extra stuff */ + battery_ram = auto_malloc(LELAND_BATTERY_RAM_SIZE); + + /* start scanline interrupts going */ + master_int_timer = timer_alloc(leland_interrupt_callback); +} + + +MACHINE_RESET( leland ) +{ + timer_adjust(master_int_timer, video_screen_get_time_until_pos(0, 8, 0), 8, attotime_zero); + + /* reset globals */ + leland_gfx_control = 0x00; + leland_sound_port_w(0, 0xff); + wcol_enable = 0; + + dangerz_x = 512; + dangerz_y = 512; + analog_result = 0xff; + memset(dial_last_input, 0, sizeof(dial_last_input)); + memset(dial_last_result, 0, sizeof(dial_last_result)); + + keycard_shift = 0; + keycard_bit = 0; + keycard_state = 0; + keycard_clock = 0; + memset(keycard_command, 0, sizeof(keycard_command)); + + top_board_bank = 0; + sound_port_bank = 0; + alternate_bank = 0; + + /* initialize the master banks */ + master_length = memory_region_length(REGION_CPU1); + master_base = memory_region(REGION_CPU1); + (*leland_update_master_bank)(); + + /* initialize the slave banks */ + slave_length = memory_region_length(REGION_CPU2); + slave_base = memory_region(REGION_CPU2); + if (slave_length > 0x10000) + memory_set_bankptr(3, &slave_base[0x10000]); + + /* if we have an I80186 CPU, reset it */ + if (machine->drv->cpu[2].type == CPU_I80186) + leland_80186_sound_init(); +} + + +MACHINE_START( ataxx ) +{ + /* set the odd data banks */ + battery_ram = auto_malloc(LELAND_BATTERY_RAM_SIZE); + extra_tram = auto_malloc(ATAXX_EXTRA_TRAM_SIZE); + + /* start scanline interrupts going */ + master_int_timer = timer_alloc(ataxx_interrupt_callback); +} + + +MACHINE_RESET( ataxx ) +{ + memset(extra_tram, 0, ATAXX_EXTRA_TRAM_SIZE); + timer_adjust(master_int_timer, video_screen_get_time_until_pos(0, 8, 0), 8, attotime_zero); + + /* initialize the XROM */ + xrom_length = memory_region_length(REGION_USER1); + xrom_base = memory_region(REGION_USER1); + xrom1_addr = 0; + xrom2_addr = 0; + + /* reset globals */ + wcol_enable = 0; + + analog_result = 0xff; + memset(dial_last_input, 0, sizeof(dial_last_input)); + memset(dial_last_result, 0, sizeof(dial_last_result)); + + master_bank = 0; + + /* initialize the master banks */ + master_length = memory_region_length(REGION_CPU1); + master_base = memory_region(REGION_CPU1); + ataxx_bankswitch(); + + /* initialize the slave banks */ + slave_length = memory_region_length(REGION_CPU2); + slave_base = memory_region(REGION_CPU2); + if (slave_length > 0x10000) + memory_set_bankptr(3, &slave_base[0x10000]); + + /* reset the 80186 */ + leland_80186_sound_init(); +} + + + +/************************************* + * + * Master CPU interrupt handling + * + *************************************/ + +static TIMER_CALLBACK( leland_interrupt_callback ) +{ + int scanline = param; + extern UINT8 leland_last_scanline_int; + leland_last_scanline_int = scanline; + + /* interrupts generated on the VA10 line, which is every */ + /* 16 scanlines starting with scanline #8 */ + cpunum_set_input_line(0, 0, HOLD_LINE); + + /* set a timer for the next one */ + scanline += 16; + if (scanline > 248) + scanline = 8; + timer_adjust(master_int_timer, video_screen_get_time_until_pos(0, scanline, 0), scanline, attotime_zero); +} + + +static TIMER_CALLBACK( ataxx_interrupt_callback ) +{ + int scanline = param; + extern UINT8 leland_last_scanline_int; + leland_last_scanline_int = scanline; + + /* interrupts generated according to the interrupt control register */ + cpunum_set_input_line(0, 0, HOLD_LINE); + + /* set a timer for the next one */ + timer_adjust(master_int_timer, video_screen_get_time_until_pos(0, scanline, 0), scanline, attotime_zero); +} + + +INTERRUPT_GEN( leland_master_interrupt ) +{ + /* check for coins here */ + if ((readinputport(1) & 0x0e) != 0x0e) + cpunum_set_input_line(0, INPUT_LINE_NMI, ASSERT_LINE); +} + + + +/************************************* + * + * Master CPU bankswitch handlers + * + *************************************/ + +WRITE8_HANDLER( leland_master_alt_bankswitch_w ) +{ + /* update any bankswitching */ + if (LOG_BANKSWITCHING_M) + if ((alternate_bank ^ data) & 0x0f) + logerror("%04X:alternate_bank = %02X\n", safe_activecpu_get_pc(), data & 0x0f); + alternate_bank = data & 15; + (*leland_update_master_bank)(); + + /* sound control is in the rest */ + leland_80186_control_w(offset, data); +} + + +/* bankswitching for Cerberus */ +void cerberus_bankswitch(void) +{ + /* no bankswitching */ +} + + +/* bankswitching for Mayhem 2002, Power Play, World Series Baseball, and Alley Master */ +void mayhem_bankswitch(void) +{ + UINT8 *address; + + battery_ram_enable = ((sound_port_bank & 0x24) == 0); + + address = (!(sound_port_bank & 0x04)) ? &master_base[0x10000] : &master_base[0x1c000]; + memory_set_bankptr(1, address); + + address = battery_ram_enable ? battery_ram : &address[0x8000]; + memory_set_bankptr(2, address); +} + + +/* bankswitching for Danger Zone */ +void dangerz_bankswitch(void) +{ + UINT8 *address; + + battery_ram_enable = ((top_board_bank & 0x80) != 0); + + address = (!(alternate_bank & 1)) ? &master_base[0x02000] : &master_base[0x12000]; + memory_set_bankptr(1, address); + + address = battery_ram_enable ? battery_ram : &address[0x8000]; + memory_set_bankptr(2, address); +} + + +/* bankswitching for Baseball the Season II, Super Baseball, and Strike Zone */ +void basebal2_bankswitch(void) +{ + UINT8 *address; + + battery_ram_enable = (top_board_bank & 0x80); + + if (!battery_ram_enable) + address = (!(sound_port_bank & 0x04)) ? &master_base[0x10000] : &master_base[0x1c000]; + else + address = (!(top_board_bank & 0x40)) ? &master_base[0x28000] : &master_base[0x30000]; + memory_set_bankptr(1, address); + + address = battery_ram_enable ? battery_ram : &address[0x8000]; + memory_set_bankptr(2, address); +} + + +/* bankswitching for Red Line Racer */ +void redline_bankswitch(void) +{ + static const UINT32 bank_list[] = { 0x10000, 0x18000, 0x02000, 0x02000 }; + UINT8 *address; + + battery_ram_enable = ((alternate_bank & 3) == 1); + + address = &master_base[bank_list[alternate_bank & 3]]; + memory_set_bankptr(1, address); + + address = battery_ram_enable ? battery_ram : &master_base[0xa000]; + memory_set_bankptr(2, address); +} + + +/* bankswitching for Viper, Quarterback, Team Quarterback, and All American Football */ +void viper_bankswitch(void) +{ + static const UINT32 bank_list[] = { 0x02000, 0x10000, 0x18000, 0x02000 }; + UINT8 *address; + + battery_ram_enable = ((alternate_bank & 0x04) != 0); + + address = &master_base[bank_list[alternate_bank & 3]]; + if (bank_list[alternate_bank & 3] >= master_length) + { + logerror("%04X:Master bank %02X out of range!\n", safe_activecpu_get_pc(), alternate_bank & 3); + address = &master_base[bank_list[0]]; + } + memory_set_bankptr(1, address); + + address = battery_ram_enable ? battery_ram : &master_base[0xa000]; + memory_set_bankptr(2, address); +} + + +/* bankswitching for Super Offroad, Super Offroad Track Pack, and Pig Out */ +void offroad_bankswitch(void) +{ + static const UINT32 bank_list[] = { 0x02000, 0x02000, 0x10000, 0x18000, 0x20000, 0x28000, 0x30000, 0x38000 }; + UINT8 *address; + + battery_ram_enable = ((alternate_bank & 7) == 1); + + address = &master_base[bank_list[alternate_bank & 7]]; + if (bank_list[alternate_bank & 7] >= master_length) + { + logerror("%04X:Master bank %02X out of range!\n", safe_activecpu_get_pc(), alternate_bank & 7); + address = &master_base[bank_list[0]]; + } + memory_set_bankptr(1, address); + + address = battery_ram_enable ? battery_ram : &master_base[0xa000]; + memory_set_bankptr(2, address); +} + + +/* bankswitching for Ataxx, WSF, Indy Heat, and Brute Force */ +void ataxx_bankswitch(void) +{ + static const UINT32 bank_list[] = + { + 0x02000, 0x18000, 0x20000, 0x28000, 0x30000, 0x38000, 0x40000, 0x48000, + 0x50000, 0x58000, 0x60000, 0x68000, 0x70000, 0x78000, 0x80000, 0x88000 + }; + UINT8 *address; + + battery_ram_enable = ((master_bank & 0x30) == 0x10); + + address = &master_base[bank_list[master_bank & 15]]; + if (bank_list[master_bank & 15] >= master_length) + { + logerror("%04X:Master bank %02X out of range!\n", safe_activecpu_get_pc(), master_bank & 15); + address = &master_base[bank_list[0]]; + } + memory_set_bankptr(1, address); + + if (battery_ram_enable) + address = battery_ram; + else if ((master_bank & 0x30) == 0x20) + address = &ataxx_qram[(master_bank & 0xc0) << 8]; + else + address = &master_base[0xa000]; + memory_set_bankptr(2, address); + + wcol_enable = ((master_bank & 0x30) == 0x30); +} + + + +/************************************* + * + * EEPROM handling (64 x 16bits) + * + *************************************/ + +void leland_init_eeprom(UINT8 default_val, const UINT16 *data, UINT8 serial_offset, UINT8 serial_type) +{ + static struct EEPROM_interface eeprom_interface = + { + 6, + 16, + "110", + "101", + "111" + }; + UINT8 xorval = (serial_type == SERIAL_TYPE_ADD_XOR || serial_type == SERIAL_TYPE_ENCRYPT_XOR) ? 0xff : 0x00; + UINT32 serial; + + /* initialize everything to the default value */ + memset(eeprom_data, default_val, sizeof(eeprom_data)); + + /* fill in the preset data */ + while (*data != 0xffff) + { + int offset = *data++; + int value = *data++; + eeprom_data[offset * 2 + 0] = value >> 8; + eeprom_data[offset * 2 + 1] = value & 0xff; + } + + /* pick a serial number -- examples of real serial numbers: + + Team QB: 21101957 + AAFB: 26101119 and 26101039 + */ + serial = 0x12345678; + + /* switch off the serial number type */ + switch (serial_type) + { + case SERIAL_TYPE_ADD: + case SERIAL_TYPE_ADD_XOR: + { + int i; + for (i = 0; i < 10; i++) + { + int digit; + + if (i >= 8) + digit = 0; + else + digit = ((serial << (i * 4)) >> 28) & 15; + digit = ('0' + digit) * 2; + + eeprom_data[serial_offset * 2 + 0 + (i ^ 1)] = (digit / 3) ^ xorval; + eeprom_data[serial_offset * 2 + 10 + (i ^ 1)] = (digit / 3) ^ xorval; + eeprom_data[serial_offset * 2 + 20 + (i ^ 1)] = (digit - (2 * (digit / 3))) ^ xorval; + } + break; + } + + case SERIAL_TYPE_ENCRYPT: + case SERIAL_TYPE_ENCRYPT_XOR: + { + int d, e, h, l; + + /* break the serial number out into pieces */ + l = (serial >> 24) & 0xff; + h = (serial >> 16) & 0xff; + e = (serial >> 8) & 0xff; + d = serial & 0xff; + + /* decrypt the data */ + h = ((h ^ 0x2a ^ l) ^ 0xff) + 5; + d = ((d + 0x2a) ^ e) ^ 0xff; + l ^= e; + e ^= 0x2a; + + /* store the bytes */ + eeprom_data[serial_offset * 2 + 0] = h ^ xorval; + eeprom_data[serial_offset * 2 + 1] = l ^ xorval; + eeprom_data[serial_offset * 2 + 2] = d ^ xorval; + eeprom_data[serial_offset * 2 + 3] = e ^ xorval; + break; + } + } + + EEPROM_init(&eeprom_interface); +} + + + +/************************************* + * + * EEPROM handling (128 x 16bits) + * + *************************************/ + +void ataxx_init_eeprom(UINT8 default_val, const UINT16 *data, UINT8 serial_offset) +{ + static struct EEPROM_interface eeprom_interface = + { + 7, + 16, + "000001100", + "000001010", + 0, + "0000010000000000", + "0000010011000000", + 1 + }; + UINT32 serial; + + /* initialize everything to the default value */ + memset(eeprom_data, default_val, sizeof(eeprom_data)); + + /* fill in the preset data */ + while (*data != 0xffff) + { + int offset = *data++; + int value = *data++; + eeprom_data[offset * 2 + 0] = value >> 8; + eeprom_data[offset * 2 + 1] = value & 0xff; + } + + /* pick a serial number -- examples of real serial numbers: + + WSF: 30101190 + Indy Heat: 31201339 + */ + serial = 0x12345678; + + /* encrypt the serial number */ + { + int d, e, h, l; + + /* break the serial number out into pieces */ + l = (serial >> 24) & 0xff; + h = (serial >> 16) & 0xff; + e = (serial >> 8) & 0xff; + d = serial & 0xff; + + /* decrypt the data */ + h = ((h ^ 0x2a ^ l) ^ 0xff) + 5; + d = ((d + 0x2a) ^ e) ^ 0xff; + l ^= e; + e ^= 0x2a; + + /* store the bytes */ + eeprom_data[serial_offset * 2 + 0] = h; + eeprom_data[serial_offset * 2 + 1] = l; + eeprom_data[serial_offset * 2 + 2] = d; + eeprom_data[serial_offset * 2 + 3] = e; + } + + /* compute the checksum */ + { + int i, sum = 0; + for (i = 0; i < 0x7f * 2; i++) + sum += eeprom_data[i]; + sum ^= 0xffff; + eeprom_data[0x7f * 2 + 0] = (sum >> 8) & 0xff; + eeprom_data[0x7f * 2 + 1] = sum & 0xff; + + EEPROM_init(&eeprom_interface); + } +} + + + +/************************************* + * + * Ataxx EEPROM interfacing + * + *************************************/ + +READ8_HANDLER( ataxx_eeprom_r ) +{ + int port = readinputport(2); + if (LOG_EEPROM) logerror("%04X:EE read\n", safe_activecpu_get_pc()); + return (port & ~0x01) | EEPROM_read_bit(); +} + + +WRITE8_HANDLER( ataxx_eeprom_w ) +{ + if (LOG_EEPROM) logerror("%04X:EE write %d%d%d\n", safe_activecpu_get_pc(), + (data >> 6) & 1, (data >> 5) & 1, (data >> 4) & 1); + EEPROM_write_bit ((data & 0x10) >> 4); + EEPROM_set_clock_line((data & 0x20) ? ASSERT_LINE : CLEAR_LINE); + EEPROM_set_cs_line ((~data & 0x40) ? ASSERT_LINE : CLEAR_LINE); +} + + + +/************************************* + * + * Battery backed RAM + * + *************************************/ + +WRITE8_HANDLER( leland_battery_ram_w ) +{ + if (battery_ram_enable) + { + if (LOG_BATTERY_RAM) logerror("%04X:BatteryW@%04X=%02X\n", safe_activecpu_get_pc(), offset, data); + battery_ram[offset] = data; + } + else + logerror("%04X:BatteryW@%04X (invalid!)\n", safe_activecpu_get_pc(), offset); +} + + +WRITE8_HANDLER( ataxx_battery_ram_w ) +{ + if (battery_ram_enable) + { + if (LOG_BATTERY_RAM) logerror("%04X:BatteryW@%04X=%02X\n", safe_activecpu_get_pc(), offset, data); + battery_ram[offset] = data; + } + else if ((master_bank & 0x30) == 0x20) + ataxx_qram[((master_bank & 0xc0) << 8) + offset] = data; + else + logerror("%04X:BatteryW@%04X (invalid!)\n", safe_activecpu_get_pc(), offset); +} + + +NVRAM_HANDLER( leland ) +{ + if (read_or_write) + { + EEPROM_save(file); + mame_fwrite(file, battery_ram, LELAND_BATTERY_RAM_SIZE); + } + else if (file) + { + EEPROM_load(file); + mame_fread(file, battery_ram, LELAND_BATTERY_RAM_SIZE); + } + else + { + EEPROM_set_data(eeprom_data, 64*2); + memset(battery_ram, 0x00, LELAND_BATTERY_RAM_SIZE); + } +} + + +NVRAM_HANDLER( ataxx ) +{ + if (read_or_write) + { + EEPROM_save(file); + mame_fwrite(file, battery_ram, LELAND_BATTERY_RAM_SIZE); + } + else if (file) + { + EEPROM_load(file); + mame_fread(file, battery_ram, LELAND_BATTERY_RAM_SIZE); + } + else + { + EEPROM_set_data(eeprom_data, 128*2); + memset(battery_ram, 0x00, LELAND_BATTERY_RAM_SIZE); + } +} + + +/************************************* + * + * Master CPU keycard I/O + * + ************************************/ + +/*-------------------------------------------------------------------- + + A note about keycards: + + These were apparently static programmable ROMs that could be + inserted into certain games. The games would then save/load + statistics on them. + + The data is accessed via a serial protocol, which is + implemented below. There are two known commands that are + written; each command is 3 bytes and accesses 128 bytes of + data from the keycard: + + 62 00 80 + 9D 00 80 + + the last byte appears to specify the length of data to transfer. + + The format of the data on the card is pretty heavily encrypted. + The first 7 bytes read serves as a header: + + D5 43 49 4E 2A xx yy + + where xx is a game-specific key, and yy is the complement of the + key. For example, World Series Baseball uses 04/FB. Alley Master + uses 05/FA. + + The last 112 bytes of data is encrypted via the following method: + + for (i = 16, b = 0x70, r = 0x08; i < 128; i++, b--, r += 0x10) + { + UINT8 a = original_data[i] ^ 0xff; + a = (a >> 3) | (a << 5); + a = (((a ^ r) + 1 + b) ^ b) - b; + encrypted_data[i] = a; + } + + The data that is encrypted is stored alternating with a checksum + byte. The checksum for a value A is ((A ^ 0xa5) + 0x27) ^ 0x34. + +--------------------------------------------------------------------*/ + +static int keycard_r(void) +{ + int result = 0; + + if (LOG_KEYCARDS_FULL) logerror(" (%04X:keycard_r)\n", safe_activecpu_get_pc()); + + /* if we have a valid keycard read state, we're reading from the keycard */ + if (keycard_state & 0x80) + { + /* clock in new data */ + if (keycard_bit == 1) + { + keycard_shift = 0xff; /* no data, but this is where we would clock it in */ + if (LOG_KEYCARDS) logerror(" (clocked in %02X)\n", keycard_shift); + } + + /* clock in the bit */ + result = (~keycard_shift & 1) << ((keycard_state >> 4) & 3); + if (LOG_KEYCARDS) logerror(" (read %02X)\n", result); + } + return result; +} + +static void keycard_w(int data) +{ + int new_state = data & 0xb0; + int new_clock = data & 0x40; + + if (LOG_KEYCARDS_FULL) logerror(" (%04X:keycard_w=%02X)\n", safe_activecpu_get_pc(), data); + + /* check for going active */ + if (!keycard_state && new_state) + { + keycard_command[0] = keycard_command[1] = keycard_command[2] = 0; + if (LOG_KEYCARDS) logerror("keycard going active (state=%02X)\n", new_state); + } + + /* check for going inactive */ + else if (keycard_state && !new_state) + { + keycard_command[0] = keycard_command[1] = keycard_command[2] = 0; + if (LOG_KEYCARDS) logerror("keycard going inactive\n"); + } + + /* check for clocks */ + else if (keycard_state == new_state) + { + /* work off of falling edge */ + if (!new_clock && keycard_clock) + { + keycard_shift >>= 1; + keycard_bit = (keycard_bit + 1) & 7; + } + + /* look for a bit write */ + else if (!new_clock && !keycard_clock && !(data & 0x80)) + { + if (LOG_KEYCARDS) logerror(" (write %02X)\n", data); + + keycard_shift &= ~0x80; + if (data & (1 << ((new_state >> 4) & 3))) + keycard_shift |= 0x80; + + /* clock out the data on the last bit */ + if (keycard_bit == 7) + { + if (LOG_KEYCARDS) logerror(" (clocked out %02X)\n", keycard_shift); + keycard_command[0] = keycard_command[1]; + keycard_command[1] = keycard_command[2]; + keycard_command[2] = keycard_shift; + if (keycard_command[0] == 0x62 && keycard_command[1] == 0x00 && keycard_command[2] == 0x80) + { + if (LOG_KEYCARDS) logerror(" (got command $62)\n"); + } + } + } + } + + /* error case */ + else + { + /* only an error if the selected bit changes; read/write transitions are okay */ + if ((new_state & 0x30) != (keycard_state & 0x30)) + if (LOG_KEYCARDS) logerror("ERROR: Caught keycard state transition %02X -> %02X\n", keycard_state, new_state); + } + + keycard_state = new_state; + keycard_clock = new_clock; +} + + + +/************************************* + * + * Master CPU analog and keycard I/O + * + *************************************/ + +READ8_HANDLER( leland_master_analog_key_r ) +{ + int result = 0; + + switch (offset) + { + case 0x00: /* FD = analog data read */ + result = analog_result; + break; + + case 0x01: /* FE = analog status read */ + /* bit 7 indicates the analog input is busy for some games */ + result = 0x00; + break; + + case 0x02: /* FF = keycard serial data read */ + result = keycard_r(); + + /* bit 7 indicates the analog input is busy for some games */ + result &= ~0x80; + break; + } + return result; +} + + + +WRITE8_HANDLER( leland_master_analog_key_w ) +{ + switch (offset) + { + case 0x00: /* FD = analog port trigger */ + break; + + case 0x01: /* FE = analog port select/bankswitch */ + analog_result = readinputport((data & 15) + 4); + + /* update top board banking for some games */ + if (LOG_BANKSWITCHING_M) + if ((top_board_bank ^ data) & 0xc0) + logerror("%04X:top_board_bank = %02X\n", safe_activecpu_get_pc(), data & 0xc0); + top_board_bank = data & 0xc0; + (*leland_update_master_bank)(); + break; + + case 0x02: /* FF = keycard data write */ + keycard_w(data); + break; + } +} + + + +/************************************* + * + * Master CPU internal I/O + * + *************************************/ + +READ8_HANDLER( leland_master_input_r ) +{ + int result = 0xff; + + switch (offset) + { + case 0x00: /* /GIN0 */ + result = readinputport(0); + break; + + case 0x01: /* /GIN1 */ + result = readinputport(1); + if (cpunum_get_reg(1, Z80_HALT)) + result ^= 0x01; + break; + + case 0x02: /* /GIN2 */ + case 0x12: + cpunum_set_input_line(0, INPUT_LINE_NMI, CLEAR_LINE); + break; + + case 0x03: /* /IGID */ + case 0x13: + result = AY8910_read_port_0_r(offset); + break; + + case 0x10: /* /GIN0 */ + result = readinputport(2); + break; + + case 0x11: /* /GIN1 */ + result = readinputport(3); + if (LOG_EEPROM) logerror("%04X:EE read\n", safe_activecpu_get_pc()); + result = (result & ~0x01) | EEPROM_read_bit(); + break; + + default: + logerror("Master I/O read offset %02X\n", offset); + break; + } + return result; +} + + +WRITE8_HANDLER( leland_master_output_w ) +{ + switch (offset) + { + case 0x09: /* /MCONT */ + cpunum_set_input_line(1, INPUT_LINE_RESET, (data & 0x01) ? CLEAR_LINE : ASSERT_LINE); + wcol_enable = (data & 0x02); + cpunum_set_input_line(1, INPUT_LINE_NMI, (data & 0x04) ? CLEAR_LINE : ASSERT_LINE); + cpunum_set_input_line (1, 0, (data & 0x08) ? CLEAR_LINE : ASSERT_LINE); + + if (LOG_EEPROM) logerror("%04X:EE write %d%d%d\n", safe_activecpu_get_pc(), + (data >> 6) & 1, (data >> 5) & 1, (data >> 4) & 1); + EEPROM_write_bit ((data & 0x10) >> 4); + EEPROM_set_clock_line((data & 0x20) ? ASSERT_LINE : CLEAR_LINE); + EEPROM_set_cs_line ((~data & 0x40) ? ASSERT_LINE : CLEAR_LINE); + break; + + case 0x0a: /* /OGIA */ + AY8910_control_port_0_w(0, data); + break; + + case 0x0b: /* /OGID */ + AY8910_write_port_0_w(0, data); + break; + + case 0x0c: /* /BKXL */ + case 0x0d: /* /BKXH */ + case 0x0e: /* /BKYL */ + case 0x0f: /* /BKYH */ + leland_gfx_port_w(offset - 0x0c, data); + break; + + default: + logerror("Master I/O write offset %02X=%02X\n", offset, data); + break; + } +} + + +READ8_HANDLER( ataxx_master_input_r ) +{ + int result = 0xff; + + switch (offset) + { + case 0x06: /* /GIN0 */ + result = readinputport(0); + break; + + case 0x07: /* /SLVBLK */ + result = readinputport(1); + if (cpunum_get_reg(1, Z80_HALT)) + result ^= 0x01; + break; + + default: + logerror("Master I/O read offset %02X\n", offset); + break; + } + return result; +} + + +WRITE8_HANDLER( ataxx_master_output_w ) +{ + switch (offset) + { + case 0x00: /* /BKXL */ + case 0x01: /* /BKXH */ + case 0x02: /* /BKYL */ + case 0x03: /* /BKYH */ + leland_gfx_port_w(offset, data); + break; + + case 0x04: /* /MBNK */ + if (LOG_BANKSWITCHING_M) + if ((master_bank ^ data) & 0xff) + logerror("%04X:master_bank = %02X\n", safe_activecpu_get_pc(), data & 0xff); + master_bank = data; + ataxx_bankswitch(); + break; + + case 0x05: /* /SLV0 */ + cpunum_set_input_line(1, 0, (data & 0x01) ? CLEAR_LINE : ASSERT_LINE); + cpunum_set_input_line(1, INPUT_LINE_NMI, (data & 0x04) ? CLEAR_LINE : ASSERT_LINE); + cpunum_set_input_line(1, INPUT_LINE_RESET, (data & 0x10) ? CLEAR_LINE : ASSERT_LINE); + break; + + case 0x08: /* */ + timer_adjust(master_int_timer, video_screen_get_time_until_pos(0, data + 1, 0), data + 1, attotime_zero); + break; + + default: + logerror("Master I/O write offset %02X=%02X\n", offset, data); + break; + } +} + + + +/************************************* + * + * Master CPU palette gates + * + *************************************/ + +WRITE8_HANDLER( leland_gated_paletteram_w ) +{ + if (wcol_enable) + paletteram_BBGGGRRR_w(offset, data); +} + + +READ8_HANDLER( leland_gated_paletteram_r ) +{ + if (wcol_enable) + return paletteram_r(offset); + return 0xff; +} + + +WRITE8_HANDLER( ataxx_paletteram_and_misc_w ) +{ + if (wcol_enable) + paletteram_xxxxRRRRGGGGBBBB_le_w(offset, data); + else if (offset == 0x7f8 || offset == 0x7f9) + leland_master_video_addr_w(offset - 0x7f8, data); + else if (offset == 0x7fc) + { + xrom1_addr = (xrom1_addr & 0xff00) | (data & 0x00ff); + if (LOG_XROM) logerror("%04X:XROM1 address low write = %02X (addr=%04X)\n", safe_activecpu_get_pc(), data, xrom1_addr); + } + else if (offset == 0x7fd) + { + xrom1_addr = (xrom1_addr & 0x00ff) | ((data << 8) & 0xff00); + if (LOG_XROM) logerror("%04X:XROM1 address high write = %02X (addr=%04X)\n", safe_activecpu_get_pc(), data, xrom1_addr); + } + else if (offset == 0x7fe) + { + xrom2_addr = (xrom2_addr & 0xff00) | (data & 0x00ff); + if (LOG_XROM) logerror("%04X:XROM2 address low write = %02X (addr=%04X)\n", safe_activecpu_get_pc(), data, xrom2_addr); + } + else if (offset == 0x7ff) + { + xrom2_addr = (xrom2_addr & 0x00ff) | ((data << 8) & 0xff00); + if (LOG_XROM) logerror("%04X:XROM2 address high write = %02X (addr=%04X)\n", safe_activecpu_get_pc(), data, xrom2_addr); + } + else + extra_tram[offset] = data; +} + + +READ8_HANDLER( ataxx_paletteram_and_misc_r ) +{ + if (wcol_enable) + return paletteram_r(offset); + else if (offset == 0x7fc || offset == 0x7fd) + { + int result = xrom_base[0x00000 | xrom1_addr | ((offset & 1) << 16)]; + if (LOG_XROM) logerror("%04X:XROM1 read(%d) = %02X (addr=%04X)\n", safe_activecpu_get_pc(), offset - 0x7fc, result, xrom1_addr); + return result; + } + else if (offset == 0x7fe || offset == 0x7ff) + { + int result = xrom_base[0x20000 | xrom2_addr | ((offset & 1) << 16)]; + if (LOG_XROM) logerror("%04X:XROM2 read(%d) = %02X (addr=%04X)\n", safe_activecpu_get_pc(), offset - 0x7fc, result, xrom2_addr); + return result; + } + else + return extra_tram[offset]; +} + + + +/************************************* + * + * AY8910-controlled graphics latch + * + *************************************/ + +READ8_HANDLER( leland_sound_port_r ) +{ + return leland_gfx_control; +} + + +WRITE8_HANDLER( leland_sound_port_w ) +{ + int gfx_banks = Machine->gfx[0]->total_elements / 0x400; + int gfx_bank_mask = (gfx_banks - 1) << 4; + int diff = data ^ leland_gfx_control; + + /* update the graphics banking if necessary */ + if ((diff & 0x08) || (diff & gfx_bank_mask)) + leland_gfx_port_w(-1, data); + + /* set the new value */ + leland_gfx_control = data; + leland_dac_control &= data & 3; + + /* some bankswitching occurs here */ + if (LOG_BANKSWITCHING_M) + if ((sound_port_bank ^ data) & 0x24) + logerror("%04X:sound_port_bank = %02X\n", safe_activecpu_get_pc(), data & 0x24); + sound_port_bank = data & 0x24; + (*leland_update_master_bank)(); +} + + + +/************************************* + * + * Slave CPU bankswitching + * + *************************************/ + +WRITE8_HANDLER( leland_slave_small_banksw_w ) +{ + int bankaddress = 0x10000 + 0xc000 * (data & 1); + + if (bankaddress >= slave_length) + { + logerror("%04X:Slave bank %02X out of range!", safe_activecpu_get_pc(), data & 1); + bankaddress = 0x10000; + } + memory_set_bankptr(3, &slave_base[bankaddress]); + + if (LOG_BANKSWITCHING_S) logerror("%04X:Slave bank = %02X (%05X)\n", safe_activecpu_get_pc(), data & 1, bankaddress); +} + + +WRITE8_HANDLER( leland_slave_large_banksw_w ) +{ + int bankaddress = 0x10000 + 0x8000 * (data & 15); + + if (bankaddress >= slave_length) + { + logerror("%04X:Slave bank %02X out of range!", safe_activecpu_get_pc(), data & 15); + bankaddress = 0x10000; + } + memory_set_bankptr(3, &slave_base[bankaddress]); + + if (LOG_BANKSWITCHING_S) logerror("%04X:Slave bank = %02X (%05X)\n", safe_activecpu_get_pc(), data & 15, bankaddress); +} + + +WRITE8_HANDLER( ataxx_slave_banksw_w ) +{ + int bankaddress, bank = data & 15; + + if (bank == 0) + bankaddress = 0x2000; + else + { + bankaddress = 0x10000 * bank + 0x8000 * ((data >> 4) & 1); + if (slave_length > 0x100000) + bankaddress += 0x100000 * ((data >> 5) & 1); + } + + if (bankaddress >= slave_length) + { + logerror("%04X:Slave bank %02X out of range!", safe_activecpu_get_pc(), data & 0x3f); + bankaddress = 0x2000; + } + memory_set_bankptr(3, &slave_base[bankaddress]); + + if (LOG_BANKSWITCHING_S) logerror("%04X:Slave bank = %02X (%05X)\n", safe_activecpu_get_pc(), data, bankaddress); +} + + + +/************************************* + * + * Slave CPU I/O + * + *************************************/ + +READ8_HANDLER( leland_raster_r ) +{ + return video_screen_get_vpos(0); +} + + + +/************************************* + * + * Driver initialization + * + *************************************/ + +/* also called by Ataxx */ +void leland_rotate_memory(int cpunum) +{ + int startaddr = 0x10000; + int banks = (memory_region_length(REGION_CPU1 + cpunum) - startaddr) / 0x8000; + UINT8 *ram = memory_region(REGION_CPU1 + cpunum); + UINT8 temp[0x2000]; + int i; + + for (i = 0; i < banks; i++) + { + memmove(temp, &ram[startaddr + 0x0000], 0x2000); + memmove(&ram[startaddr + 0x0000], &ram[startaddr + 0x2000], 0x6000); + memmove(&ram[startaddr + 0x6000], temp, 0x2000); + startaddr += 0x8000; + } +} |