// license:BSD-3-Clause // copyright-holders:Aaron Giles /*************************************************************************** Hard Drivin' machine hardware ****************************************************************************/ #include "emu.h" #include "includes/slapstic.h" #include "includes/harddriv.h" /************************************* * * Constants and macros * *************************************/ #define DS3_TRIGGER 7777 #define DS3_STRIGGER 5555 /* debugging tools */ #define LOG_COMMANDS 0 #if 0 #pragma mark * DRIVER/MULTISYNC BOARD #endif /************************************* * * Initialization * *************************************/ void harddriv_state::device_start() { m_lamps.resolve(); //atarigen_state::machine_start(); /* predetermine memory regions */ m_adsp_pgm_memory_word = (uint16_t *)(reinterpret_cast(m_adsp_pgm_memory.target()) + 1); init_video(); } void harddriv_state::device_reset() { /* generic reset */ //atarigen_state::machine_reset(); if (m_slapstic_device.found()) m_slapstic_device->slapstic_reset(); /* halt several of the DSPs to start */ if (m_adsp.found()) m_adsp->set_input_line(INPUT_LINE_HALT, ASSERT_LINE); if (m_dsp32.found()) m_dsp32->set_input_line(INPUT_LINE_HALT, ASSERT_LINE); m_last_gsp_shiftreg = 0; m_m68k_adsp_buffer_bank = 0; /* reset IRQ states */ m_irq_state = m_gsp_irq_state = m_msp_irq_state = m_adsp_irq_state = m_duart_irq_state = 0; /* reset the ADSP/DSIII/DSIV boards */ m_adsp_halt = 1; m_adsp_br = 0; m_adsp_xflag = 0; if (m_ds3sdsp.found()) { m_ds3sdsp->load_boot_data(m_ds3sdsp_region->base(), m_ds3sdsp_pgm_memory); m_ds3sdsp_timer_en = 0; m_ds3sdsp_internal_timer->adjust(attotime::never); } if (m_ds3xdsp.found()) { m_ds3xdsp->load_boot_data(m_ds3xdsp_region->base(), m_ds3xdsp_pgm_memory); m_ds3xdsp_timer_en = 0; m_ds3xdsp_internal_timer->adjust(attotime::never); } } /************************************* * * 68000 interrupt handling * *************************************/ void harddriv_state::update_interrupts() { m_maincpu->set_input_line(1, m_msp_irq_state ? ASSERT_LINE : CLEAR_LINE); m_maincpu->set_input_line(2, m_adsp_irq_state ? ASSERT_LINE : CLEAR_LINE); m_maincpu->set_input_line(3, m_gsp_irq_state ? ASSERT_LINE : CLEAR_LINE); m_maincpu->set_input_line(4, m_sound_int_state ? ASSERT_LINE : CLEAR_LINE); /* /LINKIRQ on STUN Runner */ m_maincpu->set_input_line(5, m_irq_state ? ASSERT_LINE : CLEAR_LINE); m_maincpu->set_input_line(6, m_duart_irq_state ? ASSERT_LINE : CLEAR_LINE); } INTERRUPT_GEN_MEMBER(harddriv_state::hd68k_irq_gen) { m_irq_state = 1; update_interrupts(); } WRITE16_MEMBER( harddriv_state::hd68k_irq_ack_w ) { m_irq_state = 0; update_interrupts(); } WRITE_LINE_MEMBER(harddriv_state::hdgsp_irq_gen) { m_gsp_irq_state = state; update_interrupts(); } WRITE_LINE_MEMBER(harddriv_state::hdmsp_irq_gen) { m_msp_irq_state = state; update_interrupts(); } /************************************* * * 68000 access to GSP * *************************************/ READ16_MEMBER( harddriv_state::hd68k_gsp_io_r ) { uint16_t result; offset = (offset / 2) ^ 1; m_hd34010_host_access = true; result = m_gsp->host_r(offset); m_hd34010_host_access = false; return result; } WRITE16_MEMBER( harddriv_state::hd68k_gsp_io_w ) { offset = (offset / 2) ^ 1; m_hd34010_host_access = true; m_gsp->host_w(offset, data); m_hd34010_host_access = false; } /************************************* * * 68000 access to MSP * *************************************/ READ16_MEMBER( harddriv_state::hd68k_msp_io_r ) { uint16_t result; offset = (offset / 2) ^ 1; m_hd34010_host_access = true; result = m_msp.found() ? m_msp->host_r(offset) : 0xffff; m_hd34010_host_access = false; return result; } WRITE16_MEMBER( harddriv_state::hd68k_msp_io_w ) { offset = (offset / 2) ^ 1; if (m_msp.found()) { m_hd34010_host_access = true; m_msp->host_w(offset, data); m_hd34010_host_access = false; } } /************************************* * * 68000 input handlers * *************************************/ READ16_MEMBER( harddriv_state::hd68k_a80000_r ) { return m_a80000->read(); } READ16_MEMBER( harddriv_state::hd68k_port0_r ) { /* port is as follows: 0x0001 = DIAGN 0x0002 = /HSYNCB 0x0004 = /VSYNCB 0x0008 = EOC12 0x0010 = EOC8 0x0020 = SELF-TEST 0x0040 = COIN2 0x0080 = COIN1 0x0100 = SW1 #8 0x0200 = SW1 #7 ..... 0x8000 = SW1 #1 */ screen_device &scr = m_gsp->screen(); int temp = (m_sw1.read_safe(0xff) << 8) | m_in0->read(); if (get_hblank(scr)) temp ^= 0x0002; temp ^= 0x0008; /* 12-bit EOC always high for now */ return temp; } READ16_MEMBER( harddriv_state::hdc68k_port1_r ) { uint16_t result = m_a80000->read(); uint16_t diff = result ^ m_hdc68k_last_port1; /* if a new shifter position is selected, use it */ /* if it's the same shifter position as last time, go back to neutral */ if ((diff & 0x0100) && !(result & 0x0100)) m_hdc68k_shifter_state = (m_hdc68k_shifter_state == 1) ? 0 : 1; if ((diff & 0x0200) && !(result & 0x0200)) m_hdc68k_shifter_state = (m_hdc68k_shifter_state == 2) ? 0 : 2; if ((diff & 0x0400) && !(result & 0x0400)) m_hdc68k_shifter_state = (m_hdc68k_shifter_state == 4) ? 0 : 4; if ((diff & 0x0800) && !(result & 0x0800)) m_hdc68k_shifter_state = (m_hdc68k_shifter_state == 8) ? 0 : 8; /* merge in the new shifter value */ result = (result | 0x0f00) ^ (m_hdc68k_shifter_state << 8); /* merge in the wheel edge latch bit */ if (m_hdc68k_wheel_edge) result ^= 0x4000; m_hdc68k_last_port1 = result; return result; } READ16_MEMBER( harddriv_state::hda68k_port1_r ) { uint16_t result = m_a80000->read(); /* merge in the wheel edge latch bit */ if (m_hdc68k_wheel_edge) result ^= 0x4000; return result; } READ16_MEMBER( harddriv_state::hdc68k_wheel_r ) { /* grab the new wheel value */ uint16_t new_wheel = m_12badc[0].read_safe(0xffff); /* hack to display the wheel position */ if (machine().input().code_pressed(KEYCODE_LSHIFT)) popmessage("%04X", new_wheel); /* if we crossed the center line, latch the edge bit */ if ((m_hdc68k_last_wheel / 0xf00) != (new_wheel / 0xf00)) m_hdc68k_wheel_edge = 1; /* remember the last value and return the low 8 bits */ m_hdc68k_last_wheel = new_wheel; return (new_wheel << 8) | 0xff; } READ16_MEMBER( harddriv_state::hd68k_adc12_r ) { return m_adc12_byte ? ((m_adc12_data >> 8) & 0x0f) : (m_adc12_data & 0xff); } READ16_MEMBER( harddriv_state::hd68k_sound_reset_r ) { if (m_jsa.found()) m_jsa->reset(); return ~0; } /************************************* * * 68000 output handlers * *************************************/ WRITE16_MEMBER( harddriv_state::hd68k_adc_control_w ) { COMBINE_DATA(&m_adc_control); /* handle a write to the 8-bit ADC address select */ m_adc8->address_w(m_adc_control & 0x07); m_adc8->start_w(BIT(m_adc_control, 3)); /* handle a write to the 12-bit ADC address select */ if (m_adc_control & 0x40) { m_adc12_select = (m_adc_control >> 4) & 0x03; m_adc12_data = m_12badc[m_adc12_select].read_safe(0xffff); } /* bit 7 selects which byte of the 12 bit data to read */ m_adc12_byte = (m_adc_control >> 7) & 1; } WRITE16_MEMBER( harddriv_state::hd68k_wr0_write ) { /* bit 3 selects the value; data is ignored */ data = (offset >> 3) & 1; /* low 3 bits select the function */ offset &= 7; m_sel_select = 0; switch (offset) { case 1: /* SEL1 */ case 2: /* SEL2 */ case 3: /* SEL3 */ case 4: /* SEL4 */ m_sel_select = offset; break; case 6: /* CC1 */ case 7: /* CC2 */ machine().bookkeeping().coin_counter_w(offset - 6, data); break; default: /* just ignore */ break; } } WRITE16_MEMBER( harddriv_state::hd68k_wr1_write ) { if (offset == 0) { // logerror("Shifter Interface Latch = %02X\n", data); data = data >> 8; switch (m_sel_select) { case 1: /* SEL1 */ m_sel1_data = data; machine().output().set_value("SEL1", m_sel1_data); break; case 2: /* SEL2 */ m_sel2_data = data; machine().output().set_value("SEL2", m_sel2_data); break; case 3: /* SEL3 */ m_sel3_data = data; machine().output().set_value("SEL3", m_sel3_data); break; case 4: /* SEL4 */ m_sel4_data = data; machine().output().set_value("SEL4", m_sel4_data); break; } } else { logerror("/WR1(%04X)=%02X\n", offset, data); } } WRITE16_MEMBER( harddriv_state::hd68k_wr2_write ) { if (offset == 0) { // logerror("Steering Wheel Latch = %02X\n", data); machine().output().set_value("wheel", data >> 8); } else { logerror("/WR2(%04X)=%02X\n", offset, data); } } WRITE16_MEMBER( harddriv_state::hd68k_nwr_w ) { /* bit 3 selects the value; data is ignored */ data = (offset >> 3) & 1; /* low 3 bits select the function */ offset &= 7; switch (offset) { case 0: /* CR2 */ case 1: /* CR1 */ break; case 2: /* LC1 */ // used for seat locking on harddriv m_lamps[0] = data; break; case 3: /* LC2 */ // used for "abort" button lamp m_lamps[1] = data; break; case 4: /* ZP1 */ m_m68k_zp1 = data; break; case 5: /* ZP2 */ m_m68k_zp2 = data; break; case 6: /* /GSPRES */ logerror("Write to /GSPRES(%d)\n", data); if (m_gsp.found()) m_gsp->set_input_line(INPUT_LINE_RESET, data ? CLEAR_LINE : ASSERT_LINE); break; case 7: /* /MSPRES */ logerror("Write to /MSPRES(%d)\n", data); if (m_msp.found()) m_msp->set_input_line(INPUT_LINE_RESET, data ? CLEAR_LINE : ASSERT_LINE); break; } } WRITE16_MEMBER( harddriv_state::hdc68k_wheel_edge_reset_w ) { /* reset the edge latch */ m_hdc68k_wheel_edge = 0; } /************************************* * * 68000 ZRAM access * *************************************/ READ16_MEMBER( harddriv_state::hd68k_zram_r ) { uint16_t data = 0; if (ACCESSING_BITS_0_7) data |= m_210e->read(space, offset, mem_mask); if (ACCESSING_BITS_8_15) data |= m_200e->read(offset) << 8; return data; } WRITE16_MEMBER( harddriv_state::hd68k_zram_w ) { if (m_m68k_zp1 == 0 && m_m68k_zp2 == 1) { if (ACCESSING_BITS_0_7) m_210e->write(space, offset, data, mem_mask); if (ACCESSING_BITS_8_15) m_200e->write(offset, data >> 8); } } /************************************* * * 68681 DUART * *************************************/ WRITE_LINE_MEMBER(harddriv_state::harddriv_duart_irq_handler) { m_duart_irq_state = state; update_interrupts(); } /************************************* * * GSP I/O register writes * *************************************/ void harddriv_state::hdgsp_io_w(offs_t offset, u16 data, u16 mem_mask) { /* detect an enabling of the shift register and force yielding */ if (offset == REG_DPYCTL) { uint8_t new_shiftreg = (data >> 11) & 1; if (new_shiftreg != m_last_gsp_shiftreg) { m_last_gsp_shiftreg = new_shiftreg; if (new_shiftreg) m_gsp->yield(); } } screen_device &scr = m_gsp->screen(); /* detect changes to HEBLNK and HSBLNK and force an update before they change */ if ((offset == REG_HEBLNK || offset == REG_HSBLNK) && data != m_gsp->io_register_r(offset)) scr.update_partial(scr.vpos() - 1); } /************************************* * * GSP protection workarounds * *************************************/ WRITE16_MEMBER( harddriv_state::hdgsp_protection_w ) { /* this memory address is incremented whenever a protection check fails */ /* after it reaches a certain value, the GSP will randomly trash a */ /* register; we just prevent it from ever going above 0 */ *m_gsp_protection = 0; } #if 0 #pragma mark - #pragma mark * ADSP BOARD #endif /************************************* * * 68000 access to ADSP program memory * *************************************/ READ16_MEMBER( harddriv_state::hd68k_adsp_program_r ) { uint32_t word = m_adsp_pgm_memory[offset/2]; return (!(offset & 1)) ? (word >> 16) : (word & 0xffff); } WRITE16_MEMBER( harddriv_state::hd68k_adsp_program_w ) { uint32_t *base = &m_adsp_pgm_memory[offset/2]; uint32_t oldword = *base; uint16_t temp; if (!(offset & 1)) { temp = oldword >> 16; COMBINE_DATA(&temp); oldword = (oldword & 0x0000ffff) | (temp << 16); } else { temp = oldword & 0xffff; COMBINE_DATA(&temp); oldword = (oldword & 0xffff0000) | temp; } *base = oldword; } /************************************* * * 68000 access to ADSP data memory * *************************************/ READ16_MEMBER( harddriv_state::hd68k_adsp_data_r ) { return m_adsp_data_memory[offset]; } WRITE16_MEMBER( harddriv_state::hd68k_adsp_data_w ) { COMBINE_DATA(&m_adsp_data_memory[offset]); /* any write to $1FFF is taken to be a trigger; synchronize the CPUs */ if (offset == 0x1fff) { logerror("%06X:ADSP sync address written (%04X)\n", m_maincpu->pcbase(), data); machine().scheduler().synchronize(); m_adsp->signal_interrupt_trigger(); } else logerror("%06X:ADSP W@%04X (%04X)\n", m_maincpu->pcbase(), offset, data); } /************************************* * * 68000 access to ADSP output memory * *************************************/ READ16_MEMBER( harddriv_state::hd68k_adsp_buffer_r ) { /* logerror("hd68k_adsp_buffer_r(%04X)\n", offset);*/ return m_som_memory[m_m68k_adsp_buffer_bank * 0x2000 + offset]; } WRITE16_MEMBER( harddriv_state::hd68k_adsp_buffer_w ) { COMBINE_DATA(&m_som_memory[m_m68k_adsp_buffer_bank * 0x2000 + offset]); } /************************************* * * 68000 access to ADSP control regs * *************************************/ TIMER_CALLBACK_MEMBER(harddriv_state::deferred_adsp_bank_switch) { if (LOG_COMMANDS && m_m68k_adsp_buffer_bank != param && machine().input().code_pressed(KEYCODE_L)) { static FILE *commands; if (!commands) commands = fopen("commands.log", "w"); if (commands) { int16_t *base = (int16_t *)&m_som_memory[param * 0x2000]; int16_t *end = base + (uint16_t)*base; int16_t *current = base + 1; int16_t *table = base + 1 + (uint16_t)*current++; fprintf(commands, "\n---------------\n"); while ((current + 5) < table) { int offset = (int)(current - base); int c1 = *current++; int c2 = *current++; int c3 = *current++; int c4 = *current++; fprintf(commands, "Cmd @ %04X = %04X %d-%d @ %d\n", offset, c1, c2, c3, c4); while (current < table) { uint32_t rslope, lslope; rslope = (uint16_t)*current++, rslope |= *current++ << 16; if (rslope == 0xffffffff) { fprintf(commands, " (end)\n"); break; } lslope = (uint16_t)*current++, lslope |= *current++ << 16; fprintf(commands, " L=%08X R=%08X count=%d\n", (int)lslope, (int)rslope, (int)*current++); } } fprintf(commands, "\nTable:\n"); current = table; while (current < end) fprintf(commands, " %04X\n", *current++); } } m_m68k_adsp_buffer_bank = param; logerror("ADSP bank = %d\n", param); } WRITE16_MEMBER( harddriv_state::hd68k_adsp_control_w ) { /* bit 3 selects the value; data is ignored */ int val = (offset >> 3) & 1; /* low 3 bits select the function */ offset &= 7; switch (offset) { case 0: case 1: /* LEDs */ break; case 3: logerror("ADSP bank = %d (deferred)\n", val); machine().scheduler().synchronize(timer_expired_delegate(FUNC(harddriv_state::deferred_adsp_bank_switch),this), val); break; case 5: /* connected to the /BR (bus request) line; this effectively halts */ /* the ADSP at the next instruction boundary */ m_adsp_br = !val; logerror("ADSP /BR = %d\n", !m_adsp_br); if (m_adsp_br || m_adsp_halt) m_adsp->set_input_line(INPUT_LINE_HALT, ASSERT_LINE); else { m_adsp->set_input_line(INPUT_LINE_HALT, CLEAR_LINE); /* a yield in this case is not enough */ /* we would need to increase the interleaving otherwise */ /* note that this only affects the test mode */ m_maincpu->spin(); } break; case 6: /* connected to the /HALT line; this effectively halts */ /* the ADSP at the next instruction boundary */ m_adsp_halt = !val; logerror("ADSP /HALT = %d\n", !m_adsp_halt); if (m_adsp_br || m_adsp_halt) m_adsp->set_input_line(INPUT_LINE_HALT, ASSERT_LINE); else { m_adsp->set_input_line(INPUT_LINE_HALT, CLEAR_LINE); /* a yield in this case is not enough */ /* we would need to increase the interleaving otherwise */ /* note that this only affects the test mode */ m_maincpu->spin(); } break; case 7: logerror("ADSP reset = %d\n", val); m_adsp->set_input_line(INPUT_LINE_RESET, val ? CLEAR_LINE : ASSERT_LINE); m_maincpu->yield(); break; default: logerror("ADSP control %02X = %04X\n", offset, data); break; } } WRITE16_MEMBER( harddriv_state::hd68k_adsp_irq_clear_w ) { logerror("%06X:68k clears ADSP interrupt\n", m_maincpu->pcbase()); m_adsp_irq_state = 0; update_interrupts(); } READ16_MEMBER( harddriv_state::hd68k_adsp_irq_state_r ) { int result = 0xfffd; if (m_adsp_xflag) result ^= 2; if (m_adsp_irq_state) result ^= 1; logerror("%06X:68k reads ADSP interrupt state = %04x\n", m_maincpu->pcbase(), result); return result; } /************************************* * * ADSP memory-mapped I/O * *************************************/ READ16_MEMBER( harddriv_state::hdadsp_special_r ) { switch (offset & 7) { case 0: /* /SIMBUF */ if (m_adsp_eprom_base + m_adsp_sim_address < m_sim_memory.length()) return m_sim_memory[m_adsp_eprom_base + m_adsp_sim_address++]; else return 0xff; case 1: /* /SIMLD */ break; case 2: /* /SOMO */ break; case 3: /* /SOMLD */ break; default: logerror("%04X:hdadsp_special_r(%04X)\n", m_adsp->pcbase(), offset); break; } return 0; } WRITE16_MEMBER( harddriv_state::hdadsp_special_w ) { switch (offset & 7) { case 1: /* /SIMCLK */ m_adsp_sim_address = data; break; case 2: /* SOMLATCH */ m_som_memory[(m_m68k_adsp_buffer_bank ^ 1) * 0x2000 + (m_adsp_som_address++ & 0x1fff)] = data; break; case 3: /* /SOMCLK */ m_adsp_som_address = data; break; case 5: /* /XOUT */ m_adsp_xflag = data & 1; break; case 6: /* /GINT */ logerror("%04X:ADSP signals interrupt\n", m_adsp->pcbase()); m_adsp_irq_state = 1; update_interrupts(); break; case 7: /* /MP */ m_adsp_eprom_base = 0x10000 * data; break; default: logerror("%04X:hdadsp_special_w(%04X)=%04X\n", m_adsp->pcbase(), offset, data); break; } } #if 0 #pragma mark - #pragma mark * DS III BOARD #endif /************************************* * * General DS III I/O * *************************************/ void harddriv_state::update_ds3_irq() { /* update the IRQ2 signal to the ADSP2101 */ if (!(!m_ds3_g68flag && m_ds3_g68irqs) && !(m_ds3_gflag && m_ds3_gfirqs)) m_adsp->set_input_line(ADSP2100_IRQ2, ASSERT_LINE); else m_adsp->set_input_line(ADSP2100_IRQ2, CLEAR_LINE); } void harddriv_state::update_ds3_sirq() { /* update the IRQ2 signal to the ADSP2105 */ if (!(!m_ds3_s68flag && m_ds3_s68irqs) && !(m_ds3_sflag && m_ds3_sfirqs)) m_ds3sdsp->set_input_line(ADSP2105_IRQ2, ASSERT_LINE); else m_ds3sdsp->set_input_line(ADSP2105_IRQ2, CLEAR_LINE); } WRITE16_MEMBER( harddriv_state::hd68k_ds3_control_w ) { int val = (offset >> 3) & 1; switch (offset & 7) { case 0: /* SRES - reset sound CPU */ if (m_ds3sdsp.found()) { m_ds3sdsp->set_input_line(INPUT_LINE_RESET, val ? CLEAR_LINE : ASSERT_LINE); m_ds3sdsp->load_boot_data(m_ds3sdsp_region->base(), m_ds3sdsp_pgm_memory); if (val && !m_ds3_sreset) { m_ds3_sflag = 0; m_ds3_scmd = 0; m_ds3_sfirqs = 0; m_ds3_s68irqs = !m_ds3_sfirqs; update_ds3_sirq(); } m_ds3_sreset = val; m_maincpu->yield(); } break; case 1: /* XRES - reset sound helper CPU */ if (m_ds3xdsp.found()) { m_ds3xdsp->set_input_line(INPUT_LINE_RESET, val ? CLEAR_LINE : ASSERT_LINE); m_ds3xdsp->load_boot_data(m_ds3xdsp_region->base(), m_ds3xdsp_pgm_memory); } break; case 2: /* connected to the /BR (bus request) line; this effectively halts */ /* the ADSP at the next instruction boundary */ m_adsp_br = !val; if (m_adsp_br) m_adsp->set_input_line(INPUT_LINE_HALT, ASSERT_LINE); else { m_adsp->set_input_line(INPUT_LINE_HALT, CLEAR_LINE); /* a yield in this case is not enough */ /* we would need to increase the interleaving otherwise */ /* note that this only affects the test mode */ m_maincpu->spin(); } break; case 3: m_adsp->set_input_line(INPUT_LINE_RESET, val ? CLEAR_LINE : ASSERT_LINE); if (val && !m_ds3_reset) { m_ds3_gflag = 0; m_ds3_gcmd = 0; m_ds3_gfirqs = 0; m_ds3_g68irqs = !m_ds3_gfirqs; m_ds3_send = 0; update_ds3_irq(); } m_ds3_reset = val; m_maincpu->yield(); logerror("DS III reset = %d\n", val); break; case 7: /* LED */ break; default: logerror("DS III control %02X = %04X\n", offset, data); break; } } /************************************* * * DS III graphics I/O * *************************************/ READ16_MEMBER( harddriv_state::hd68k_ds3_girq_state_r ) { int result = 0x0fff; if (m_ds3_g68flag) result ^= 0x8000; if (m_ds3_gflag) result ^= 0x4000; if (m_ds3_g68irqs) result ^= 0x2000; if (!m_adsp_irq_state) result ^= 0x1000; return result; } READ16_MEMBER( harddriv_state::hd68k_ds3_gdata_r ) { offs_t pc = m_maincpu->pc(); m_ds3_gflag = 0; update_ds3_irq(); logerror("%06X:hd68k_ds3_gdata_r(%04X)\n", m_maincpu->pcbase(), m_ds3_gdata); /* attempt to optimize the transfer if conditions are right */ if (pc == m_ds3_transfer_pc && !(!m_ds3_g68flag && m_ds3_g68irqs) && !(m_ds3_gflag && m_ds3_gfirqs)) { uint32_t destaddr = m_maincpu->state_int(M68K_A1); uint16_t count68k = m_maincpu->state_int(M68K_D1); uint16_t mstat = m_adsp->state_int(ADSP2100_MSTAT); uint16_t i6 = m_adsp->state_int((mstat & 1) ? ADSP2100_MR0 : ADSP2100_MR0_SEC); uint16_t l6 = m_adsp->state_int(ADSP2100_L6) - 1; uint16_t m7 = m_adsp->state_int(ADSP2100_M7); auto &mspace = m_maincpu->space(AS_PROGRAM); logerror("%06X:optimizing 68k transfer, %d words\n", m_maincpu->pcbase(), count68k); while (count68k > 0 && m_adsp_data_memory[0x16e6] > 0) { mspace.write_word(destaddr, m_ds3_gdata); { m_adsp_data_memory[0x16e6]--; m_ds3_gdata = m_adsp_pgm_memory[i6] >> 8; i6 = (i6 & ~l6) | ((i6 + m7) & l6); } count68k--; } m_maincpu->set_state_int(M68K_D1, count68k); m_adsp->set_state_int((mstat & 1) ? ADSP2100_MR0 : ADSP2100_MR0_SEC, i6); m_adsp_speedup_count[1]++; } /* if we just cleared the IRQ, we are going to do some VERY timing critical reads */ /* it is important that all the CPUs be in sync before we continue, so spin a little */ /* while to let everyone else catch up */ m_maincpu->spin_until_trigger(DS3_TRIGGER); machine().scheduler().trigger(DS3_TRIGGER, attotime::from_usec(5)); return m_ds3_gdata; } WRITE16_MEMBER( harddriv_state::hd68k_ds3_gdata_w ) { logerror("%06X:hd68k_ds3_gdata_w(%04X)\n", m_maincpu->pcbase(), m_ds3_gdata); COMBINE_DATA(&m_ds3_g68data); m_ds3_g68flag = 1; m_ds3_gcmd = offset & 1; m_adsp->signal_interrupt_trigger(); update_ds3_irq(); } /************************************* * * DS III sound I/O * *************************************/ WRITE16_MEMBER( harddriv_state::hd68k_ds3_sirq_clear_w ) { logerror("%06X:68k clears ADSP interrupt\n", m_maincpu->pcbase()); m_sound_int_state = 0; update_interrupts(); } READ16_MEMBER( harddriv_state::hd68k_ds3_sirq_state_r ) { int result = 0x0fff; if (m_ds3_s68flag) result ^= 0x8000; if (m_ds3_sflag) result ^= 0x4000; if (m_ds3_s68irqs) result ^= 0x2000; if (!m_sound_int_state) result ^= 0x1000; return result; } READ16_MEMBER( harddriv_state::hd68k_ds3_sdata_r ) { m_ds3_sflag = 0; update_ds3_sirq(); /* if we just cleared the IRQ, we are going to do some VERY timing critical reads */ /* it is important that all the CPUs be in sync before we continue, so spin a little */ /* while to let everyone else catch up */ m_maincpu->spin_until_trigger(DS3_STRIGGER); machine().scheduler().trigger(DS3_STRIGGER, attotime::from_usec(5)); return m_ds3_sdata; } WRITE16_MEMBER( harddriv_state::hd68k_ds3_sdata_w ) { COMBINE_DATA(&m_ds3_s68data); m_ds3_s68flag = 1; m_ds3_scmd = offset & 1; m_ds3sdsp->signal_interrupt_trigger(); update_ds3_sirq(); } READ16_MEMBER( harddriv_state::hdds3_sdsp_special_r ) { int result; switch (offset & 7) { case 0: m_ds3_s68flag = 0; update_ds3_sirq(); return m_ds3_s68data; case 1: result = 0x0fff; if (m_ds3_scmd) result ^= 0x8000; if (m_ds3_s68flag) result ^= 0x4000; if (m_ds3_sflag) result ^= 0x2000; return result; case 4: if (m_ds3_sdata_address < m_ds3_sdata_memory_size) return m_ds3_sdata_memory[m_ds3_sdata_address]; else return 0xff; case 5: /* DS IV: sound ROM configuration */ return 1; case 7: /* SFWCLR */ break; default: return 0xff; } return 0; } WRITE16_MEMBER( harddriv_state::hdds3_sdsp_special_w ) { /* Note: DS IV is slightly different */ switch (offset & 7) { case 0: m_ds3_sdata = data; m_ds3_sflag = 1; update_ds3_sirq(); /* once we've written data, trigger the main CPU to wake up again */ machine().scheduler().trigger(DS3_STRIGGER); break; case 1: m_sound_int_state = (data >> 1) & 1; update_interrupts(); break; case 2: /* bit 0 = T1 (unused) */ break; case 3: m_ds3_sfirqs = (data >> 1) & 1; m_ds3_s68irqs = !m_ds3_sfirqs; update_ds3_sirq(); break; case 4: m_ldac->write(data); break; case 5: m_rdac->write(data); break; case 6: m_ds3_sdata_address = (m_ds3_sdata_address & 0xffff0000) | (data & 0xffff); break; case 7: m_ds3_sdata_address = (m_ds3_sdata_address & 0x0000ffff) | (data << 16); break; } } READ16_MEMBER( harddriv_state::hdds3_sdsp_control_r ) { switch (offset) { default: return m_ds3sdsp_regs[offset]; } } WRITE16_MEMBER( harddriv_state::hdds3_sdsp_control_w ) { switch (offset) { case 0x1b: // Scale data &= 0xff; if (m_ds3sdsp_regs[0x1b] != data) { m_ds3sdsp_regs[0x1b] = data; hdds3sdsp_reset_timer(); } break; case 0x1c: // Count if (m_ds3sdsp_regs[0x1c] != data) { m_ds3sdsp_regs[0x1c] = data; hdds3sdsp_reset_timer(); } break; case 0x1d: // Period m_ds3sdsp_regs[0x1d] = data; break; case 0x1e: m_ds3sdsp_regs[0x1e] = data; break; case 0x1f: /* are we asserting BFORCE? */ if (data & 0x200) { uint32_t page = (data >> 6) & 7; m_ds3sdsp->load_boot_data(m_ds3sdsp_region->base() + (0x2000 * page), m_ds3sdsp_pgm_memory); m_ds3sdsp->pulse_input_line(INPUT_LINE_RESET, attotime::zero); data &= ~0x200; } m_ds3sdsp_regs[0x1f] = data; break; default: m_ds3sdsp_regs[offset] = data; break; } } READ16_MEMBER( harddriv_state::hdds3_xdsp_control_r ) { switch (offset) { default: return m_ds3xdsp_regs[offset]; } // never executed //return 0xff; } WRITE16_MEMBER( harddriv_state::hdds3_xdsp_control_w ) { switch (offset) { default: m_ds3xdsp_regs[offset] = data; break; } } TIMER_DEVICE_CALLBACK_MEMBER( harddriv_state::ds3sdsp_internal_timer_callback ) { uint16_t period = m_ds3sdsp_regs[0x1d]; uint16_t scale = m_ds3sdsp_regs[0x1b] + 1; m_ds3sdsp_internal_timer->adjust(m_ds3sdsp->cycles_to_attotime(period * scale)); /* the IRQ line is edge triggered */ m_ds3sdsp->set_input_line(ADSP2105_TIMER, ASSERT_LINE); m_ds3sdsp->set_input_line(ADSP2105_TIMER, CLEAR_LINE); } void harddriv_state::hdds3sdsp_reset_timer() { if (!m_ds3sdsp_timer_en) return; uint16_t count = m_ds3sdsp_regs[0x1c]; uint16_t scale = m_ds3sdsp_regs[0x1b] + 1; m_ds3sdsp_internal_timer->adjust(m_ds3sdsp->cycles_to_attotime(count * scale)); } WRITE_LINE_MEMBER(harddriv_state::hdds3sdsp_timer_enable_callback) { m_ds3sdsp_timer_en = state; if (state) hdds3sdsp_reset_timer(); else m_ds3sdsp_internal_timer->adjust(attotime::never); } TIMER_DEVICE_CALLBACK_MEMBER( harddriv_state::ds3xdsp_internal_timer_callback ) { uint16_t period = m_ds3xdsp_regs[0x1d]; uint16_t scale = m_ds3xdsp_regs[0x1b] + 1; m_ds3xdsp_internal_timer->adjust(m_ds3xdsp->cycles_to_attotime(period * scale)); /* the IRQ line is edge triggered */ m_ds3xdsp->set_input_line(ADSP2105_TIMER, ASSERT_LINE); m_ds3xdsp->set_input_line(ADSP2105_TIMER, CLEAR_LINE); } void harddriv_state::hdds3xdsp_reset_timer() { if (!m_ds3xdsp_timer_en) return; uint16_t count = m_ds3xdsp_regs[0x1c]; uint16_t scale = m_ds3xdsp_regs[0x1b] + 1; m_ds3xdsp_internal_timer->adjust(m_ds3xdsp->cycles_to_attotime(count * scale)); } WRITE_LINE_MEMBER(harddriv_state::hdds3xdsp_timer_enable_callback) { m_ds3xdsp_timer_en = state; if (state) hdds3xdsp_reset_timer(); else m_ds3xdsp_internal_timer->adjust(attotime::never); } /* TODO: The following does not work correctly */ TIMER_CALLBACK_MEMBER(harddriv_state::xsdp_sport1_irq_off_callback) { m_ds3xdsp->set_input_line(ADSP2105_SPORT1_RX, CLEAR_LINE); } WRITE32_MEMBER(harddriv_state::hdds3sdsp_serial_tx_callback) { if ((m_ds3sdsp_regs[0x1f] & 0xc00) != 0xc00) return; m_ds3sdsp_sdata = data; m_ds3xdsp->set_input_line(ADSP2105_SPORT1_RX, ASSERT_LINE); machine().scheduler().timer_set(attotime::from_nsec(200), timer_expired_delegate(FUNC(harddriv_state::xsdp_sport1_irq_off_callback), this)); } READ32_MEMBER(harddriv_state::hdds3sdsp_serial_rx_callback) { if ((m_ds3sdsp_regs[0x1f] & 0xc00) != 0xc00) return 0xff; return m_ds3xdsp_sdata; } WRITE32_MEMBER(harddriv_state::hdds3xdsp_serial_tx_callback) { if ((m_ds3xdsp_regs[0x1f] & 0xc00) != 0xc00) return; m_ds3xdsp_sdata = data; } READ32_MEMBER(harddriv_state::hdds3xdsp_serial_rx_callback) { m_ds3xdsp->set_input_line(ADSP2105_SPORT1_RX, ASSERT_LINE); m_ds3xdsp->set_input_line(ADSP2105_SPORT1_RX, CLEAR_LINE); m_ds3xdsp->signal_interrupt_trigger(); return m_ds3sdsp_sdata; } /************************************* * * DS III internal I/O * *************************************/ READ16_MEMBER( harddriv_state::hdds3_special_r ) { int result; switch (offset & 7) { case 0: m_ds3_g68flag = 0; update_ds3_irq(); return m_ds3_g68data; case 1: result = 0x0fff; if (m_ds3_gcmd) result ^= 0x8000; if (m_ds3_g68flag) result ^= 0x4000; if (m_ds3_gflag) result ^= 0x2000; return result; case 6: logerror("ADSP r @ %04x\n", m_ds3_sim_address); if (m_ds3_sim_address < m_sim_memory.length()) return m_sim_memory[m_ds3_sim_address]; else return 0xff; } return 0; } WRITE16_MEMBER( harddriv_state::hdds3_special_w ) { /* IMPORTANT! these data values also write through to the underlying RAM */ m_adsp_data_memory[offset] = data; switch (offset & 7) { case 0: logerror("%s:ADSP sets gdata to %04X\n", machine().describe_context(), data); m_ds3_gdata = data; m_ds3_gflag = 1; update_ds3_irq(); /* once we've written data, trigger the main CPU to wake up again */ machine().scheduler().trigger(DS3_TRIGGER); break; case 1: logerror("%s:ADSP sets interrupt = %d\n", machine().describe_context(), (data >> 1) & 1); m_adsp_irq_state = (data >> 1) & 1; update_interrupts(); break; case 2: m_ds3_send = (data >> 0) & 1; break; case 3: m_ds3_gfirqs = (data >> 1) & 1; m_ds3_g68irqs = !m_ds3_gfirqs; update_ds3_irq(); break; case 4: m_ds3_sim_address = (m_ds3_sim_address & 0xffff0000) | (data & 0xffff); break; case 5: m_ds3_sim_address = (m_ds3_sim_address & 0xffff) | ((data << 16) & 0x00070000); break; } } READ16_MEMBER( harddriv_state::hdds3_control_r ) { logerror("adsp2101 control r @ %04X\n", 0x3fe0 + offset); return 0; } WRITE16_MEMBER( harddriv_state::hdds3_control_w ) { if (offset != 0x1e && offset != 0x1f) logerror("adsp2101 control w @ %04X = %04X\n", 0x3fe0 + offset, data); } /************************************* * * DS III program memory handlers * *************************************/ READ16_MEMBER( harddriv_state::hd68k_ds3_program_r ) { uint32_t *base = &m_adsp_pgm_memory[offset & 0x1fff]; uint32_t word = *base; return (!(offset & 0x2000)) ? (word >> 8) : (word & 0xff); } WRITE16_MEMBER( harddriv_state::hd68k_ds3_program_w ) { uint32_t *base = &m_adsp_pgm_memory[offset & 0x1fff]; uint32_t oldword = *base; uint16_t temp; if (!(offset & 0x2000)) { temp = oldword >> 8; COMBINE_DATA(&temp); oldword = (oldword & 0x000000ff) | (temp << 8); } else { temp = oldword & 0xff; COMBINE_DATA(&temp); oldword = (oldword & 0xffffff00) | (temp & 0xff); } *base = oldword; } #if 0 #pragma mark - #pragma mark * DSK BOARD #endif /************************************* * * DSK board IRQ generation * *************************************/ WRITE32_MEMBER(harddriv_state::hddsk_update_pif) { m_sound_int_state = ((data & DSP32_OUTPUT_PIF) != 0); update_interrupts(); } /************************************* * * DSK board control handlers * *************************************/ WRITE16_MEMBER( harddriv_state::hd68k_dsk_control_w ) { int val = (offset >> 3) & 1; switch (offset & 7) { case 0: /* DSPRESTN */ if (m_dsp32.found()) m_dsp32->set_input_line(INPUT_LINE_RESET, val ? CLEAR_LINE : ASSERT_LINE); break; case 1: /* DSPZN */ if (m_dsp32.found()) m_dsp32->set_input_line(INPUT_LINE_HALT, val ? CLEAR_LINE : ASSERT_LINE); break; case 2: /* ZW1 */ break; case 3: /* ZW2 */ break; case 4: /* ASIC65 reset */ m_asic65->reset_line(!val); break; case 7: /* LED */ break; default: logerror("hd68k_dsk_control_w(%d) = %d\n", offset & 7, val); break; } } /************************************* * * DSK board RAM/ZRAM/ROM handlers * *************************************/ READ16_MEMBER( harddriv_state::hd68k_dsk_ram_r ) { return m_dsk_ram[offset]; } WRITE16_MEMBER( harddriv_state::hd68k_dsk_ram_w ) { COMBINE_DATA(&m_dsk_ram[offset]); } READ16_MEMBER( harddriv_state::hd68k_dsk_small_rom_r ) { return m_dsk_rom[offset & 0x1ffff]; } READ16_MEMBER( harddriv_state::hd68k_dsk_rom_r ) { return m_dsk_rom[offset]; } /************************************* * * DSK board DSP32C I/O handlers * *************************************/ WRITE16_MEMBER( harddriv_state::hd68k_dsk_dsp32_w ) { m_dsk_pio_access = true; if (m_dsp32.found()) m_dsp32->pio_w(offset, data); m_dsk_pio_access = false; } READ16_MEMBER( harddriv_state::hd68k_dsk_dsp32_r ) { uint16_t result; m_dsk_pio_access = true; if (m_dsp32.found()) result = m_dsp32->pio_r(offset); else result = 0x00; m_dsk_pio_access = false; return result; } /************************************* * * DSP32C synchronization * *************************************/ TIMER_CALLBACK_MEMBER(harddriv_state::rddsp32_sync_cb) { *m_dataptr[param] = m_dataval[param]; } WRITE32_MEMBER( harddriv_state::rddsp32_sync0_w ) { if (m_dsk_pio_access) { uint32_t *dptr = &m_rddsp32_sync[0][offset]; uint32_t newdata = *dptr; COMBINE_DATA(&newdata); m_dataptr[m_next_msp_sync % MAX_MSP_SYNC] = dptr; m_dataval[m_next_msp_sync % MAX_MSP_SYNC] = newdata; machine().scheduler().synchronize(timer_expired_delegate(FUNC(harddriv_state::rddsp32_sync_cb),this), m_next_msp_sync++ % MAX_MSP_SYNC); } else COMBINE_DATA(&m_rddsp32_sync[0][offset]); } WRITE32_MEMBER( harddriv_state::rddsp32_sync1_w ) { if (m_dsk_pio_access) { uint32_t *dptr = &m_rddsp32_sync[1][offset]; uint32_t newdata = *dptr; COMBINE_DATA(&newdata); m_dataptr[m_next_msp_sync % MAX_MSP_SYNC] = dptr; m_dataval[m_next_msp_sync % MAX_MSP_SYNC] = newdata; machine().scheduler().synchronize(timer_expired_delegate(FUNC(harddriv_state::rddsp32_sync_cb),this), m_next_msp_sync++ % MAX_MSP_SYNC); } else COMBINE_DATA(&m_rddsp32_sync[1][offset]); } #if 0 #pragma mark - #pragma mark * DSPCOM BOARD #endif /************************************* * * DSPCOM control handlers * *************************************/ WRITE16_MEMBER( harddriv_state::hddspcom_control_w ) { int val = (offset >> 3) & 1; switch (offset & 7) { case 2: /* ASIC65 reset */ m_asic65->reset_line(!val); break; default: logerror("hddspcom_control_w(%d) = %d\n", offset & 7, val); break; } } #if 0 #pragma mark - #pragma mark * GAME-SPECIFIC PROTECTION #endif /************************************* * * Race Drivin' slapstic handling * *************************************/ WRITE16_MEMBER( harddriv_state::rd68k_slapstic_w ) { m_slapstic_device->slapstic_tweak(space, offset & 0x3fff); } READ16_MEMBER( harddriv_state::rd68k_slapstic_r ) { int bank = m_slapstic_device->slapstic_tweak(space, offset & 0x3fff) * 0x4000; return m_m68k_slapstic_base[bank + (offset & 0x3fff)]; } /************************************* * * Steel Talons SLOOP handling * *************************************/ int harddriv_state::st68k_sloop_tweak(offs_t offset) { static int last_offset; if (last_offset == 0) { switch (offset) { case 0x78e8: m_st68k_sloop_bank = 0; break; case 0x6ca4: m_st68k_sloop_bank = 1; break; case 0x15ea: m_st68k_sloop_bank = 2; break; case 0x6b28: m_st68k_sloop_bank = 3; break; } } last_offset = offset; return m_st68k_sloop_bank; } WRITE16_MEMBER( harddriv_state::st68k_sloop_w ) { st68k_sloop_tweak(offset & 0x3fff); } READ16_MEMBER( harddriv_state::st68k_sloop_r ) { int bank = st68k_sloop_tweak(offset) * 0x4000; return m_m68k_slapstic_base[bank + (offset & 0x3fff)]; } READ16_MEMBER( harddriv_state::st68k_sloop_alt_r ) { if (m_st68k_last_alt_sloop_offset == 0x00fe) { switch (offset*2) { case 0x22c: m_st68k_sloop_bank = 0; break; case 0x1e2: m_st68k_sloop_bank = 1; break; case 0x1fa: m_st68k_sloop_bank = 2; break; case 0x206: m_st68k_sloop_bank = 3; break; } } m_st68k_last_alt_sloop_offset = offset*2; return m_m68k_sloop_alt_base[offset]; } int harddriv_state::st68k_protosloop_tweak(offs_t offset) { static int last_offset; if (last_offset == 0) { switch (offset) { case 0x0001: m_st68k_sloop_bank = 0; break; case 0x0002: m_st68k_sloop_bank = 1; break; case 0x0003: m_st68k_sloop_bank = 2; break; case 0x0004: m_st68k_sloop_bank = 3; break; } } last_offset = offset; return m_st68k_sloop_bank; } WRITE16_MEMBER( harddriv_state::st68k_protosloop_w ) { st68k_protosloop_tweak(offset & 0x3fff); } READ16_MEMBER( harddriv_state::st68k_protosloop_r ) { int bank = st68k_protosloop_tweak(offset) * 0x4000; return m_m68k_slapstic_base[bank + (offset & 0x3fff)]; } #if 0 #pragma mark - #pragma mark * GSP OPTIMIZATIONS #endif /************************************* * * GSP Optimizations - case 1 * Works for: * Hard Drivin' * STUN Runner * *************************************/ READ16_MEMBER( harddriv_state::hdgsp_speedup_r ) { int result = m_gsp_speedup_addr[0][offset]; /* if both this address and the other important address are not $ffff */ /* then we can spin until something gets written */ if (result != 0xffff && m_gsp_speedup_addr[1][0] != 0xffff && m_gsp->pc() == m_gsp_speedup_pc) { m_gsp_speedup_count[0]++; m_gsp->spin_until_interrupt(); } return result; } WRITE16_MEMBER( harddriv_state::hdgsp_speedup1_w ) { COMBINE_DATA(&m_gsp_speedup_addr[0][offset]); /* if $ffff is written, send an "interrupt" trigger to break us out of the spin loop */ if (m_gsp_speedup_addr[0][offset] == 0xffff) m_gsp->signal_interrupt_trigger(); } WRITE16_MEMBER( harddriv_state::hdgsp_speedup2_w ) { COMBINE_DATA(&m_gsp_speedup_addr[1][offset]); /* if $ffff is written, send an "interrupt" trigger to break us out of the spin loop */ if (m_gsp_speedup_addr[1][offset] == 0xffff) m_gsp->signal_interrupt_trigger(); } /************************************* * * GSP Optimizations - case 2 * Works for: * Race Drivin' * *************************************/ READ16_MEMBER( harddriv_state::rdgsp_speedup1_r ) { uint16_t result = m_gsp_speedup_addr[0][offset]; /* if this address is equal to $f000, spin until something gets written */ if (m_gsp->pc() == m_gsp_speedup_pc && (uint8_t)result < m_gsp->state_int(TMS34010_A1)) { m_gsp_speedup_count[0]++; m_gsp->spin_until_interrupt(); } return result; } WRITE16_MEMBER( harddriv_state::rdgsp_speedup1_w ) { COMBINE_DATA(&m_gsp_speedup_addr[0][offset]); } #if 0 #pragma mark - #pragma mark * MSP OPTIMIZATIONS #endif /************************************* * * MSP Optimizations * *************************************/ READ16_MEMBER( harddriv_state::hdmsp_speedup_r ) { int data = m_msp_speedup_addr[offset]; if (data == 0 && m_msp->pc() == m_msp_speedup_pc) { m_msp_speedup_count[0]++; m_msp->spin_until_interrupt(); } return data; } WRITE16_MEMBER( harddriv_state::hdmsp_speedup_w ) { COMBINE_DATA(&m_msp_speedup_addr[offset]); if (offset == 0 && m_msp_speedup_addr[offset] != 0) m_msp->signal_interrupt_trigger(); } #if 0 #pragma mark - #pragma mark * ADSP OPTIMIZATIONS #endif /************************************* * * ADSP Optimizations * *************************************/ READ16_MEMBER( harddriv_state::hdadsp_speedup_r ) { int data = m_adsp_data_memory[0x1fff]; if (data == 0xffff && m_adsp->pc() <= 0x3b) { m_adsp_speedup_count[0]++; m_adsp->spin_until_interrupt(); } return data; } READ16_MEMBER( harddriv_state::hdds3_speedup_r ) { int data = *m_ds3_speedup_addr; if (data != 0 && m_adsp->pc() == m_ds3_speedup_pc) { m_adsp_speedup_count[2]++; m_adsp->spin_until_interrupt(); } return data; }