diff options
Diffstat (limited to 'src/mame/machine/k573fpga.cpp')
-rw-r--r-- | src/mame/machine/k573fpga.cpp | 297 |
1 files changed, 200 insertions, 97 deletions
diff --git a/src/mame/machine/k573fpga.cpp b/src/mame/machine/k573fpga.cpp index ad8134d9002..f11b80eebd6 100644 --- a/src/mame/machine/k573fpga.cpp +++ b/src/mame/machine/k573fpga.cpp @@ -15,14 +15,15 @@ k573fpga_device::k573fpga_device(const machine_config &mconfig, const char *tag, device_t(mconfig, KONAMI_573_DIGITAL_FPGA, tag, owner, clock), ram(*this, finder_base::DUMMY_TAG), mas3507d(*this, "mpeg"), - use_ddrsbm_fpga(false) + is_ddrsbm_fpga(false) { } void k573fpga_device::device_add_mconfig(machine_config &config) { MAS3507D(config, mas3507d); - mas3507d->sample_cb().set(*this, FUNC(k573fpga_device::get_decrypted)); + mas3507d->mpeg_frame_sync_cb().set(*this, FUNC(k573fpga_device::mpeg_frame_sync)); + mas3507d->demand_cb().set(*this, FUNC(k573fpga_device::mas3507d_demand)); } void k573fpga_device::device_start() @@ -30,74 +31,104 @@ void k573fpga_device::device_start() save_item(NAME(crypto_key1)); save_item(NAME(crypto_key2)); save_item(NAME(crypto_key3)); + save_item(NAME(crypto_key1_start)); + save_item(NAME(crypto_key2_start)); + save_item(NAME(crypto_key3_start)); save_item(NAME(mp3_start_addr)); - save_item(NAME(mp3_cur_addr)); save_item(NAME(mp3_end_addr)); - save_item(NAME(use_ddrsbm_fpga)); - save_item(NAME(is_stream_active)); - save_item(NAME(is_timer_active)); - save_item(NAME(counter_previous)); + save_item(NAME(mp3_cur_start_addr)); + save_item(NAME(mp3_cur_end_addr)); + save_item(NAME(mp3_cur_addr)); + save_item(NAME(mp3_data)); + save_item(NAME(mp3_remaining_bytes)); + save_item(NAME(is_ddrsbm_fpga)); + save_item(NAME(mpeg_status)); + save_item(NAME(fpga_status)); + save_item(NAME(mp3_frame_counter)); + save_item(NAME(counter_value)); save_item(NAME(counter_current)); - save_item(NAME(last_playback_status)); + save_item(NAME(counter_base)); + save_item(NAME(is_mpeg_frame_synced)); + + m_stream_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(k573fpga_device::update_stream), this)); + m_stream_timer->adjust(attotime::never); } void k573fpga_device::device_reset() { + fpga_status = 0; mp3_start_addr = 0; - mp3_cur_addr = 0; mp3_end_addr = 0; + mp3_cur_start_addr = 0; + mp3_cur_end_addr = 0; + mp3_cur_addr = 0; + mp3_data = 0; + mp3_remaining_bytes = 0; - crypto_key1 = 0; - crypto_key2 = 0; - crypto_key3 = 0; + crypto_key1_start = crypto_key1 = 0; + crypto_key2_start = crypto_key2 = 0; + crypto_key3_start = crypto_key3 = 0; - is_stream_active = false; - is_timer_active = false; + counter_current = counter_base = machine().time(); - counter_current = counter_previous = counter_offset = 0; + mpeg_status = 0; + mp3_frame_counter = 0; + counter_value = 0; + is_mpeg_frame_synced = false; mas3507d->reset_playback(); - last_playback_status = get_mpeg_ctrl(); } -void k573fpga_device::reset_counter() { - counter_current = counter_previous = counter_offset = 0; - status_update(); -} - -void k573fpga_device::status_update() { - auto cur_playback_status = get_mpeg_ctrl(); - is_timer_active = is_streaming() || ((cur_playback_status == last_playback_status && last_playback_status > PLAYBACK_STATE_IDLE) || cur_playback_status > last_playback_status); - last_playback_status = cur_playback_status; - - if(!is_timer_active) - counter_current = counter_previous = counter_offset = 0; +void k573fpga_device::reset_counter() +{ + is_mpeg_frame_synced = false; + counter_current = counter_base = machine().time(); + counter_value = 0; } -uint32_t k573fpga_device::get_counter() { - status_update(); - - counter_previous = counter_current; - - if(is_timer_active) { - mas3507d->update_stream(); - counter_current = mas3507d->get_samples() - counter_offset; +void k573fpga_device::update_counter() +{ + if (is_ddrsbm_fpga) { + // The counter for Solo Bass Mix is used differently than other games. + // DDR Solo Bass Mix will sync the internal playback timer to the first second of the MP3 using the MP3 frame counter. + // After that the playback timer is incremented using the difference between the last counter value and the current counter value. + // This counter register itself is always running even when no audio is playing. + // TODO: What happens when mp3_counter_low_w is written to on Solo Bass Mix? + counter_value = (machine().time() - counter_base).as_double(); + } + else if (is_mpeg_frame_synced) { + // Timer only seems to start when the first MPEG frame sync is encountered, so wait for that trigger + counter_base = counter_current; + counter_current = machine().time(); + counter_value += (counter_current - counter_base).as_double(); } +} - return counter_current; +uint32_t k573fpga_device::get_counter() +{ + update_counter(); + return counter_value * 44100; } -uint32_t k573fpga_device::get_counter_diff() { +uint32_t k573fpga_device::get_counter_diff() +{ // Delta playback time since last counter update. // I couldn't find any active usages of this register but it exists in some code paths. // The functionality was tested using custom code running on real hardware. // When this is called, it will return the difference between the current counter value // and the last read counter value, and then reset the counter back to the previously read counter's value. - auto diff = counter_current - counter_previous; - counter_current -= diff; - counter_previous = counter_current; - get_counter(); - return diff; + auto prev = counter_value; + update_counter(); + auto diff = counter_value - prev; + counter_value = prev; + return diff * 44100; +} + +uint16_t k573fpga_device::get_mp3_frame_count() +{ + // All games can read this but only DDR Solo Bass Mix actively uses it. + // Returns the same value as using a default read to get the frame counter from the MAS3507D over i2c. + return mp3_frame_counter & 0xffff; } uint16_t k573fpga_device::mas_i2c_r() @@ -113,72 +144,91 @@ void k573fpga_device::mas_i2c_w(uint16_t data) mas3507d->i2c_sda_w(data & 0x1000); } -uint16_t k573fpga_device::get_mpeg_ctrl() +void k573fpga_device::set_crypto_key1(uint16_t v) { - switch(mas3507d->get_status()) { - case mas3507d_device::PLAYBACK_STATE_IDLE: - return PLAYBACK_STATE_IDLE; - - case mas3507d_device::PLAYBACK_STATE_BUFFER_FULL: - return PLAYBACK_STATE_BUFFER_FULL; + crypto_key1_start = crypto_key1 = v; + update_mp3_decode_state(); +} +void k573fpga_device::set_crypto_key2(uint16_t v) +{ + crypto_key2_start = crypto_key2 = v; + update_mp3_decode_state(); +} +void k573fpga_device::set_crypto_key3(uint8_t v) +{ + crypto_key3_start = crypto_key3 = v; + update_mp3_decode_state(); +} - case mas3507d_device::PLAYBACK_STATE_DEMAND_BUFFER: - return PLAYBACK_STATE_DEMAND_BUFFER; - } +void k573fpga_device::set_mp3_start_addr(uint32_t v) +{ + mp3_start_addr = v; + update_mp3_decode_state(); +} - return PLAYBACK_STATE_IDLE; +void k573fpga_device::set_mp3_end_addr(uint32_t v) +{ + mp3_end_addr = v; + update_mp3_decode_state(); } -bool k573fpga_device::is_mp3_playing() +uint16_t k573fpga_device::get_mpeg_ctrl() { - return get_mpeg_ctrl() > PLAYBACK_STATE_IDLE; + return mpeg_status; } uint16_t k573fpga_device::get_fpga_ctrl() { // 0x0000 Not Streaming // 0x1000 Streaming - return is_streaming() << 12; + int is_streaming = BIT(fpga_status, FPGA_STREAMING_ENABLE) + && mp3_cur_addr >= mp3_cur_start_addr + && mp3_cur_addr < mp3_cur_end_addr; + return is_streaming << 12; } -bool k573fpga_device::is_streaming() -{ - return is_stream_active && mp3_cur_addr < mp3_end_addr; -} - -void k573fpga_device::set_mpeg_ctrl(uint16_t data) +void k573fpga_device::set_fpga_ctrl(uint16_t data) { LOG("FPGA MPEG control %c%c%c | %04x\n", - data & 0x8000 ? '#' : '.', - data & 0x4000 ? '#' : '.', // "Active" flag. The FPGA will never start streaming data without this bit set - data & 0x2000 ? '#' : '.', + BIT(data, FPGA_FRAME_COUNTER_ENABLE) ? '#' : '.', + BIT(data, FPGA_STREAMING_ENABLE) ? '#' : '.', + BIT(data, FPGA_MP3_ENABLE) ? '#' : '.', data); - mas3507d->reset_playback(); + if (!BIT(data, FPGA_FRAME_COUNTER_ENABLE) && BIT(fpga_status, FPGA_FRAME_COUNTER_ENABLE)) { + mp3_frame_counter = 0; + } - if(data == 0xa000) { - is_stream_active = false; - counter_current = counter_previous = 0; - status_update(); - } else if(data == 0xe000) { - is_stream_active = true; - mp3_cur_addr = mp3_start_addr; - - reset_counter(); - - if(!mas3507d->is_started) { - mas3507d->start_playback(); - mas3507d->update_stream(); - - // Audio should be buffered by this point. - // The assumption is that the number of samples actually played can be - // calculated by subtracting the base sample count when the song was started - // from the current sample count when the counter register is read. - // Otherwise, the sample count will always be ahead by the number of samples - // that were in the buffered frames. - counter_offset = mas3507d->get_samples(); - } + if ((BIT(data, FPGA_MP3_ENABLE) != BIT(fpga_status, FPGA_MP3_ENABLE)) + || (BIT(data, FPGA_STREAMING_ENABLE) != BIT(fpga_status, FPGA_STREAMING_ENABLE))) { + mas3507d->reset_playback(); + } + + fpga_status = data; +} + + +void k573fpga_device::update_mp3_decode_state() +{ + // HACK: The exact timing of when the internal state in the FPGA updates is still unknown + // so update the state any time one of the core settings (decryption keys or data start/stop addr) + // for a stream changes. + mp3_cur_addr = mp3_start_addr; + mp3_cur_start_addr = mp3_start_addr; + mp3_cur_end_addr = mp3_end_addr; + is_mpeg_frame_synced = false; + mp3_remaining_bytes = 0; + crypto_key1 = crypto_key1_start; + crypto_key2 = crypto_key2_start; + crypto_key3 = crypto_key3_start; + mp3_frame_counter = 0; + reset_counter(); + + if (is_ddrsbm_fpga) { + crypto_key3 = crypto_key3_start = 0; } + + mas3507d->reset_playback(); } uint16_t k573fpga_device::decrypt_default(uint16_t v) @@ -234,6 +284,11 @@ uint16_t k573fpga_device::decrypt_default(uint16_t v) uint16_t k573fpga_device::decrypt_ddrsbm(uint16_t data) { + // TODO: Work out the proper decryption algorithm. + // Similar to the other games, ddrsbm is capable of sending a pre-mutated key that is used to simulate seeking by starting MP3 playback from a non-zero offset. + // The MP3 seeking functionality doesn't appear to be used so the game doesn't break from lack of support from what I can tell. + // The proper key mutation found in game code is: crypto_key1 = rol(crypto_key1, offset & 0x0f) + uint8_t key[16] = {0}; uint16_t key_state = bitswap<16>( crypto_key1, @@ -273,18 +328,66 @@ uint16_t k573fpga_device::decrypt_ddrsbm(uint16_t data) return output_word; } -uint16_t k573fpga_device::get_decrypted() +TIMER_CALLBACK_MEMBER(k573fpga_device::update_stream) +{ + if (!BIT(mpeg_status, PLAYBACK_STATE_DEMAND)) { + return; + } + + if (!BIT(fpga_status, FPGA_MP3_ENABLE) + || !BIT(fpga_status, FPGA_STREAMING_ENABLE) + || mp3_cur_addr < mp3_cur_start_addr + || mp3_cur_addr >= mp3_cur_end_addr) { + return; + } + + if (mp3_remaining_bytes <= 0) { + uint16_t src = ram[mp3_cur_addr >> 1]; + mp3_data = is_ddrsbm_fpga ? decrypt_ddrsbm(src) : decrypt_default(src); + mp3_data = ((mp3_data >> 8) & 0xff) | ((mp3_data & 0xff) << 8); + mp3_cur_addr += 2; + mp3_remaining_bytes = 2; + } + + mas3507d->sid_w(mp3_data & 0xff); + mp3_data >>= 8; + mp3_remaining_bytes--; +} + +WRITE_LINE_MEMBER(k573fpga_device::mpeg_frame_sync) { - if(!is_streaming()) { - is_stream_active = false; - return 0; + if (state) { + mpeg_status &= ~(1 << PLAYBACK_STATE_IDLE); + mpeg_status |= 1 << PLAYBACK_STATE_PLAYING; + + if (!is_mpeg_frame_synced) { + reset_counter(); + is_mpeg_frame_synced = true; + } + + if (BIT(fpga_status, FPGA_FRAME_COUNTER_ENABLE)) { + mp3_frame_counter++; + } } + else { + mpeg_status &= ~(1 << PLAYBACK_STATE_PLAYING); + mpeg_status |= 1 << PLAYBACK_STATE_IDLE; + } +} - uint16_t src = ram[mp3_cur_addr >> 1]; - uint16_t result = use_ddrsbm_fpga ? decrypt_ddrsbm(src) : decrypt_default(src); - mp3_cur_addr += 2; +WRITE_LINE_MEMBER(k573fpga_device::mas3507d_demand) +{ + if (state && !BIT(mpeg_status, PLAYBACK_STATE_DEMAND)) { + mpeg_status |= 1 << PLAYBACK_STATE_DEMAND; + } + else if (!state && BIT(mpeg_status, PLAYBACK_STATE_DEMAND)) { + mpeg_status &= ~(1 << PLAYBACK_STATE_DEMAND); + m_stream_timer->adjust(attotime::never); + } - return result; + if (state && BIT(mpeg_status, PLAYBACK_STATE_DEMAND)) { + m_stream_timer->adjust(attotime::zero); + } } DEFINE_DEVICE_TYPE(KONAMI_573_DIGITAL_FPGA, k573fpga_device, "k573fpga", "Konami 573 Digital I/O FPGA") |