From 232c10aef1d65140fa383930d70463ee9203ca53 Mon Sep 17 00:00:00 2001 From: 987123879113 <63495610+987123879113@users.noreply.github.com> Date: Wed, 13 Apr 2022 23:36:52 +0900 Subject: [PATCH] k573dio/k573fpga: Improve counters and status flags (#9554) --- src/devices/sound/mas3507d.cpp | 90 ++++++---- src/devices/sound/mas3507d.h | 24 ++- src/mame/machine/k573dio.cpp | 27 ++- src/mame/machine/k573dio.h | 4 +- src/mame/machine/k573fpga.cpp | 305 ++++++++++++++++++++++----------- src/mame/machine/k573fpga.h | 75 +++++--- 6 files changed, 335 insertions(+), 190 deletions(-) diff --git a/src/devices/sound/mas3507d.cpp b/src/devices/sound/mas3507d.cpp index bc4131739cd..23693bfa641 100644 --- a/src/devices/sound/mas3507d.cpp +++ b/src/devices/sound/mas3507d.cpp @@ -17,7 +17,7 @@ #define MINIMP3_ONLY_MP3 #define MINIMP3_NO_STDIO #define MINIMP3_IMPLEMENTATION -#define MAX_FRAME_SYNC_MATCHES 1 +#define MAX_FRAME_SYNC_MATCHES 3 #include "minimp3/minimp3.h" #include "minimp3/minimp3_ex.h" @@ -49,7 +49,7 @@ DEFINE_DEVICE_TYPE(MAS3507D, mas3507d_device, "mas3507d", "Micronas MAS 3507D MP mas3507d_device::mas3507d_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : device_t(mconfig, MAS3507D, tag, owner, clock) , device_sound_interface(mconfig, *this) - , cb_sample(*this) + , cb_mpeg_frame_sync(*this), cb_demand(*this) , i2c_bus_state(IDLE), i2c_bus_address(UNKNOWN), i2c_subdest(UNDEFINED), i2c_command(CMD_BAD) , i2c_scli(false), i2c_sclo(false), i2c_sdai(false), i2c_sdao(false) , i2c_bus_curbit(0), i2c_bus_curval(0), i2c_bytecount(0), i2c_io_bank(0), i2c_io_adr(0), i2c_io_count(0), i2c_io_val(0) @@ -58,9 +58,10 @@ mas3507d_device::mas3507d_device(const machine_config &mconfig, const char *tag, void mas3507d_device::device_start() { - current_rate = 44100; // TODO: related to clock/divider - stream = stream_alloc(0, 2, current_rate); - cb_sample.resolve(); + stream = stream_alloc(0, 2, 44100); + + cb_mpeg_frame_sync.resolve(); + cb_demand.resolve(); save_item(NAME(mp3data)); save_item(NAME(samples)); @@ -74,8 +75,8 @@ void mas3507d_device::device_start() save_item(NAME(i2c_sdao)); save_item(NAME(i2c_bus_curbit)); save_item(NAME(i2c_bus_curval)); + save_item(NAME(mp3data_count)); - save_item(NAME(current_rate)); save_item(NAME(decoded_frame_count)); save_item(NAME(decoded_samples)); save_item(NAME(sample_count)); @@ -90,6 +91,7 @@ void mas3507d_device::device_start() save_item(NAME(i2c_io_val)); save_item(NAME(i2c_sdao_data)); save_item(NAME(playback_status)); + save_item(NAME(mp3_is_buffered)); // This should be removed in the future if/when native MP3 decoding is implemented in MAME save_item(NAME(mp3_dec.mdct_overlap)); @@ -119,6 +121,8 @@ void mas3507d_device::device_reset() is_muted = false; gain_ll = gain_rr = 0; + stream->set_sample_rate(44100); + reset_playback(); } @@ -355,11 +359,11 @@ void mas3507d_device::i2c_device_got_stop() LOGOTHER("MAS I2C: got stop\n"); } -int gain_to_db(double val) { +int mas3507d_device::gain_to_db(double val) { return round(20 * log10((0x100000 - val) / 0x80000)); } -float gain_to_percentage(int val) { +float mas3507d_device::gain_to_percentage(int val) { if(val == 0) return 0; // Special case for muting it seems @@ -425,30 +429,53 @@ void mas3507d_device::run_program(uint32_t adr) } } -void mas3507d_device::fill_buffer() +void mas3507d_device::sid_w(uint8_t byte) { - while(mp3data_count + 2 < mp3data.size()) { - uint16_t v = cb_sample(); - mp3data[mp3data_count++] = v >> 8; - mp3data[mp3data_count++] = v; + if (mp3data_count >= mp3data.size()) { + std::copy(mp3data.begin() + 1, mp3data.end(), mp3data.begin()); + mp3data_count--; } + mp3data[mp3data_count++] = byte; + + if (!mp3_is_buffered) { + // Only start the decoder when a full MP3 frame is found + int free_format_bytes = 0, frame_size = 0; + int frame_offset = mp3d_find_frame(static_cast(&mp3data[0]), mp3data_count, &free_format_bytes, &frame_size); + mp3_is_buffered = frame_size && frame_offset + frame_size < mp3data_count; + } + + cb_demand(!mp3_is_buffered || mp3data_count < mp3data.size()); +} + +void mas3507d_device::fill_buffer() +{ + cb_mpeg_frame_sync(0); + + if (!mp3_is_buffered) { + cb_demand(!mp3_is_buffered || mp3data_count < mp3data.size()); + return; + } + + memset(&mp3_info, 0, sizeof(mp3dec_frame_info_t)); sample_count = mp3dec_decode_frame(&mp3_dec, static_cast(&mp3data[0]), mp3data_count, static_cast(&samples[0]), &mp3_info); samples_idx = 0; - playback_status = PLAYBACK_STATE_BUFFER_FULL; - if(sample_count == 0) + if (sample_count == 0) { + // Frame decode failed + reset_playback(); return; + } std::copy(mp3data.begin() + mp3_info.frame_bytes, mp3data.end(), mp3data.begin()); mp3data_count -= mp3_info.frame_bytes; - if(mp3_info.hz != current_rate) { - current_rate = mp3_info.hz; - stream->set_sample_rate(current_rate); - } + stream->set_sample_rate(mp3_info.hz); decoded_frame_count++; + cb_mpeg_frame_sync(1); + + cb_demand(!mp3_is_buffered || mp3data_count < mp3data.size()); } void mas3507d_device::append_buffer(std::vector &outputs, int &pos, int scount) @@ -459,8 +486,6 @@ void mas3507d_device::append_buffer(std::vector &outputs, int if(s1 > sample_count) s1 = sample_count; - playback_status = PLAYBACK_STATE_DEMAND_BUFFER; - for(int i = 0; i < s1; i++) { outputs[0].put_int(pos, samples[samples_idx * bytes_per_sample], 32768); outputs[1].put_int(pos, samples[samples_idx * bytes_per_sample + (bytes_per_sample >> 1)], 32768); @@ -478,22 +503,20 @@ void mas3507d_device::append_buffer(std::vector &outputs, int void mas3507d_device::reset_playback() { + if (mp3data_count != 0) + std::fill(mp3data.begin(), mp3data.end(), 0); + + if (sample_count != 0 || decoded_samples != 0) + std::fill(samples.begin(), samples.end(), 0); + mp3dec_init(&mp3_dec); mp3data_count = 0; sample_count = 0; decoded_frame_count = 0; decoded_samples = 0; - playback_status = PLAYBACK_STATE_IDLE; - is_started = false; samples_idx = 0; - std::fill(mp3data.begin(), mp3data.end(), 0); - std::fill(samples.begin(), samples.end(), 0); -} -void mas3507d_device::start_playback() -{ - reset_playback(); - is_started = true; + mp3_is_buffered = false; } void mas3507d_device::sound_stream_update(sound_stream &stream, std::vector const &inputs, std::vector &outputs) @@ -502,13 +525,10 @@ void mas3507d_device::sound_stream_update(sound_stream &stream, std::vectorupdate(); } void reset_playback(); - void start_playback(); - - bool is_started; protected: virtual void device_start() override; @@ -56,7 +47,11 @@ private: void fill_buffer(); void append_buffer(std::vector &outputs, int &pos, int scount); - devcb_read16 cb_sample; + int gain_to_db(double val); + float gain_to_percentage(int val); + + devcb_write_line cb_mpeg_frame_sync; + devcb_write_line cb_demand; enum { CMD_DEV_WRITE = 0x3a, @@ -92,7 +87,8 @@ private: uint32_t i2c_io_bank, i2c_io_adr, i2c_io_count, i2c_io_val; uint32_t i2c_sdao_data; - uint32_t mp3data_count, current_rate; + bool mp3_is_buffered; + uint32_t mp3data_count; uint32_t decoded_frame_count, decoded_samples; int32_t sample_count, samples_idx; diff --git a/src/mame/machine/k573dio.cpp b/src/mame/machine/k573dio.cpp index 0838ac404b1..f4c5df42ce0 100644 --- a/src/mame/machine/k573dio.cpp +++ b/src/mame/machine/k573dio.cpp @@ -82,6 +82,9 @@ DEFINE_DEVICE_TYPE(KONAMI_573_DIGITAL_IO_BOARD, k573dio_device, "k573_dio", "Kon void k573dio_device::amap(address_map &map) { + // TODO: Split address maps between DDR Solo Bass Mix's FPGA code and the normal FPGA code. + // For example, DDR Solo Bass Mix's FPGA code returns 0x7654 for unused registers like mp3_counter_high_r. + map(0x00, 0x01).r(FUNC(k573dio_device::a00_r)); map(0x02, 0x03).r(FUNC(k573dio_device::a02_r)); map(0x04, 0x05).r(FUNC(k573dio_device::a04_r)); @@ -95,7 +98,7 @@ void k573dio_device::amap(address_map &map) map(0xa2, 0xa3).rw(FUNC(k573dio_device::mpeg_start_adr_low_r), FUNC(k573dio_device::mpeg_start_adr_low_w)); map(0xa4, 0xa5).rw(FUNC(k573dio_device::mpeg_end_adr_high_r), FUNC(k573dio_device::mpeg_end_adr_high_w)); map(0xa6, 0xa7).rw(FUNC(k573dio_device::mpeg_end_adr_low_r), FUNC(k573dio_device::mpeg_end_adr_low_w)); - map(0xa8, 0xa9).rw(FUNC(k573dio_device::mpeg_key_1_r), FUNC(k573dio_device::mpeg_key_1_w)); + map(0xa8, 0xa9).rw(FUNC(k573dio_device::mpeg_frame_counter_r), FUNC(k573dio_device::mpeg_key_1_w)); map(0xaa, 0xab).r(FUNC(k573dio_device::mpeg_ctrl_r)); map(0xac, 0xad).rw(FUNC(k573dio_device::mas_i2c_r), FUNC(k573dio_device::mas_i2c_w)); map(0xae, 0xaf).rw(FUNC(k573dio_device::fpga_ctrl_r), FUNC(k573dio_device::fpga_ctrl_w)); @@ -144,6 +147,7 @@ void k573dio_device::device_start() save_item(NAME(output_data)); save_item(NAME(is_ddrsbm_fpga)); save_item(NAME(crypto_key1)); + save_item(NAME(fpga_counter)); save_item(NAME(network_id)); k573fpga->set_ddrsbm_fpga(is_ddrsbm_fpga); @@ -154,6 +158,8 @@ void k573dio_device::device_reset() ram_adr = 0; ram_read_adr = 0; crypto_key1 = 0; + fpga_counter = 0; + network_id = 0; std::fill(std::begin(output_data), std::end(output_data), 0); @@ -179,10 +185,6 @@ void k573dio_device::device_add_mconfig(machine_config &config) DS2401(config, digital_id); } -void k573dio_device::device_timer(emu_timer &timer, device_timer_id id, int param) -{ -} - uint16_t k573dio_device::a00_r() { LOGUNKNOWNREG("%s: a00_r (%s)\n", tag(), machine().describe_context()); @@ -244,9 +246,6 @@ void k573dio_device::mpeg_start_adr_low_w(uint16_t data) { LOGMP3("FPGA MPEG start address low %04x\n", data); k573fpga->set_mp3_start_addr((k573fpga->get_mp3_start_addr() & 0xffff0000) | data); // low - - if(is_ddrsbm_fpga) - k573fpga->set_crypto_key3(0); } uint16_t k573dio_device::mpeg_end_adr_high_r() @@ -271,10 +270,9 @@ void k573dio_device::mpeg_end_adr_low_w(uint16_t data) k573fpga->set_mp3_end_addr((k573fpga->get_mp3_end_addr() & 0xffff0000) | data); // low } -uint16_t k573dio_device::mpeg_key_1_r() +uint16_t k573dio_device::mpeg_frame_counter_r() { - // Dance Dance Revolution Solo Bass Mix reads this key before starting songs - return crypto_key1; + return k573fpga->get_mp3_frame_count(); } void k573dio_device::mpeg_key_1_w(uint16_t data) @@ -306,7 +304,7 @@ uint16_t k573dio_device::fpga_ctrl_r() void k573dio_device::fpga_ctrl_w(uint16_t data) { - k573fpga->set_mpeg_ctrl(data); + k573fpga->set_fpga_ctrl(data); } void k573dio_device::ram_write_adr_high_w(uint16_t data) @@ -348,12 +346,13 @@ void k573dio_device::ram_read_adr_low_w(uint16_t data) uint16_t k573dio_device::mp3_counter_high_r() { - return (k573fpga->get_counter() & 0xffff0000) >> 16; + return (fpga_counter & 0xffff0000) >> 16; } uint16_t k573dio_device::mp3_counter_low_r() { - return k573fpga->get_counter() & 0x0000ffff; + fpga_counter = k573fpga->get_counter(); + return fpga_counter & 0x0000ffff; } void k573dio_device::mp3_counter_low_w(uint16_t data) diff --git a/src/mame/machine/k573dio.h b/src/mame/machine/k573dio.h index 682b8095523..209b0b06587 100644 --- a/src/mame/machine/k573dio.h +++ b/src/mame/machine/k573dio.h @@ -34,7 +34,7 @@ public: void mpeg_end_adr_high_w(uint16_t data); uint16_t mpeg_end_adr_low_r(); void mpeg_end_adr_low_w(uint16_t data); - uint16_t mpeg_key_1_r(); + uint16_t mpeg_frame_counter_r(); void mpeg_key_1_w(uint16_t data); uint16_t mpeg_ctrl_r(); uint16_t mas_i2c_r(); @@ -75,7 +75,6 @@ protected: virtual void device_reset() override; virtual const tiny_rom_entry *device_rom_region() const override; virtual void device_add_mconfig(machine_config &config) override; - virtual void device_timer(emu_timer &timer, device_timer_id id, int param) override; private: memory_share_creator ram; @@ -90,6 +89,7 @@ private: bool is_ddrsbm_fpga; u16 crypto_key1; + uint32_t fpga_counter; uint16_t network_id; }; 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::reset_counter() +{ + is_mpeg_frame_synced = false; + counter_current = counter_base = machine().time(); + counter_value = 0; } -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; -} - -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_diff() { +uint32_t k573fpga_device::get_counter() +{ + update_counter(); + return counter_value * 44100; +} + +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; - - case mas3507d_device::PLAYBACK_STATE_DEMAND_BUFFER: - return PLAYBACK_STATE_DEMAND_BUFFER; - } - - return PLAYBACK_STATE_IDLE; + 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(); } -bool k573fpga_device::is_mp3_playing() +void k573fpga_device::set_mp3_start_addr(uint32_t v) { - return get_mpeg_ctrl() > PLAYBACK_STATE_IDLE; + mp3_start_addr = v; + update_mp3_decode_state(); +} + +void k573fpga_device::set_mp3_end_addr(uint32_t v) +{ + mp3_end_addr = v; + update_mp3_decode_state(); +} + +uint16_t k573fpga_device::get_mpeg_ctrl() +{ + 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(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_FRAME_COUNTER_ENABLE) && BIT(fpga_status, FPGA_FRAME_COUNTER_ENABLE)) { + mp3_frame_counter = 0; } + + 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(!is_streaming()) { - is_stream_active = false; - return 0; + if (!BIT(mpeg_status, PLAYBACK_STATE_DEMAND)) { + return; } - uint16_t src = ram[mp3_cur_addr >> 1]; - uint16_t result = use_ddrsbm_fpga ? decrypt_ddrsbm(src) : decrypt_default(src); - mp3_cur_addr += 2; + 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; + } - return result; + 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 (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; + } +} + +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); + } + + 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") diff --git a/src/mame/machine/k573fpga.h b/src/mame/machine/k573fpga.h index 1309c01501c..5fe76bb0e44 100644 --- a/src/mame/machine/k573fpga.h +++ b/src/mame/machine/k573fpga.h @@ -7,6 +7,7 @@ #include "sound/mas3507d.h" #include "machine/ds2401.h" +#include "machine/timer.h" DECLARE_DEVICE_TYPE(KONAMI_573_DIGITAL_FPGA, k573fpga_device) @@ -18,32 +19,33 @@ public: template void add_route(T &&... args) { subdevice("mpeg")->add_route(std::forward(args)...); } template void set_ram(T &&tag) { ram.set_tag(std::forward(tag)); } - void set_ddrsbm_fpga(bool flag) { use_ddrsbm_fpga = flag; } + void set_ddrsbm_fpga(bool flag) { is_ddrsbm_fpga = flag; } - uint16_t get_decrypted(); + DECLARE_WRITE_LINE_MEMBER(mpeg_frame_sync); + DECLARE_WRITE_LINE_MEMBER(mas3507d_demand); - void set_crypto_key1(uint16_t v) { crypto_key1 = v; } - void set_crypto_key2(uint16_t v) { crypto_key2 = v; } - void set_crypto_key3(uint8_t v) { crypto_key3 = v; } + void set_crypto_key1(uint16_t v); + void set_crypto_key2(uint16_t v); + void set_crypto_key3(uint8_t v); uint32_t get_mp3_start_addr() { return mp3_start_addr; } - void set_mp3_start_addr(uint32_t v) { mp3_start_addr = v; } + void set_mp3_start_addr(uint32_t v); uint32_t get_mp3_end_addr() { return mp3_end_addr; } - void set_mp3_end_addr(uint32_t v) { mp3_end_addr = v; } + void set_mp3_end_addr(uint32_t v); uint16_t mas_i2c_r(); void mas_i2c_w(uint16_t data); uint16_t get_fpga_ctrl(); - void set_mpeg_ctrl(uint16_t data); + void set_fpga_ctrl(uint16_t data); uint16_t get_mpeg_ctrl(); uint32_t get_counter(); uint32_t get_counter_diff(); + uint16_t get_mp3_frame_count(); - void status_update(); void reset_counter(); protected: @@ -52,33 +54,58 @@ protected: virtual void device_add_mconfig(machine_config &config) override; private: + TIMER_CALLBACK_MEMBER(update_stream); + void update_counter(); + void update_mp3_decode_state(); + uint16_t decrypt_default(uint16_t data); uint16_t decrypt_ddrsbm(uint16_t data); - bool is_mp3_playing(); - bool is_streaming(); + emu_timer* m_stream_timer; enum { - PLAYBACK_STATE_UNKNOWN = 0x8000, - PLAYBACK_STATE_ERROR = 0xa000, // Error? - PLAYBACK_STATE_IDLE = 0xb000, // Not playing - PLAYBACK_STATE_BUFFER_FULL = 0xc000, // Playing, demand pin = 0? - PLAYBACK_STATE_DEMAND_BUFFER = 0xd000 // Playing, demand pin = 1? + PLAYBACK_STATE_DEMAND = 12, + PLAYBACK_STATE_IDLE = 13, + PLAYBACK_STATE_PLAYING = 14, + PLAYBACK_STATE_ENABLED = 15, + }; + + enum { + // Allows MP3 data to be decrypted? + // If this is 0 then data won't be sent to the MAS3507D even if FPGA_STREAMING_ENABLE is 1. + FPGA_MP3_ENABLE = 13, + + // Allows data to be streamed to MAS3507D. + // This needs to be set before the register at 0x1f6400ae will return the streaming status. + FPGA_STREAMING_ENABLE = 14, + + // Allows frame counter to be incremented based on the MPEG frame sync pin from the MAS3507D. + // Setting this to 0 resets the frame counter register. + FPGA_FRAME_COUNTER_ENABLE = 15, }; required_shared_ptr ram; required_device mas3507d; - uint16_t crypto_key1 = 0, crypto_key2 = 0; - uint8_t crypto_key3 = 0; + bool is_ddrsbm_fpga; - uint32_t mp3_start_addr = 0, mp3_cur_addr = 0, mp3_end_addr = 0; - bool use_ddrsbm_fpga = false; + uint16_t mpeg_status, fpga_status; - bool is_stream_active = false, is_timer_active = false; - uint32_t counter_previous = 0, counter_offset = 0; - int32_t counter_current = 0; - uint32_t last_playback_status = 0; + uint16_t crypto_key1, crypto_key2; + uint8_t crypto_key3; + uint16_t crypto_key1_start, crypto_key2_start; + uint8_t crypto_key3_start; + + uint32_t mp3_start_addr, mp3_end_addr; + uint32_t mp3_cur_start_addr, mp3_cur_end_addr, mp3_cur_addr; + uint16_t mp3_data; + int mp3_remaining_bytes; + + bool is_mpeg_frame_synced; + uint32_t mp3_frame_counter; + + attotime counter_current, counter_base; + double counter_value; }; #endif // MAME_MACHINE_K573FPGA_H