summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author 987123879113 <63495610+987123879113@users.noreply.github.com>2022-04-13 23:36:52 +0900
committer GitHub <noreply@github.com>2022-04-13 16:36:52 +0200
commit232c10aef1d65140fa383930d70463ee9203ca53 (patch)
tree1e2f635d10bbc48a6115b02c26e5afce62212ae4
parentc8ab7240564794057997f285cf38b93fa6f55d7e (diff)
k573dio/k573fpga: Improve counters and status flags (#9554)
-rw-r--r--src/devices/sound/mas3507d.cpp88
-rw-r--r--src/devices/sound/mas3507d.h24
-rw-r--r--src/mame/machine/k573dio.cpp27
-rw-r--r--src/mame/machine/k573dio.h4
-rw-r--r--src/mame/machine/k573fpga.cpp297
-rw-r--r--src/mame/machine/k573fpga.h75
6 files changed, 330 insertions, 185 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::sid_w(uint8_t byte)
+{
+ 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<const uint8_t *>(&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()
{
- while(mp3data_count + 2 < mp3data.size()) {
- uint16_t v = cb_sample();
- mp3data[mp3data_count++] = v >> 8;
- mp3data[mp3data_count++] = v;
+ 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<const uint8_t *>(&mp3data[0]), mp3data_count, static_cast<mp3d_sample_t *>(&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<write_stream_view> &outputs, int &pos, int scount)
@@ -459,8 +486,6 @@ void mas3507d_device::append_buffer(std::vector<write_stream_view> &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<write_stream_view> &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<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
@@ -502,13 +525,10 @@ void mas3507d_device::sound_stream_update(sound_stream &stream, std::vector<read
int pos = 0;
while(pos < csamples) {
- if(is_started && sample_count == 0)
+ if(sample_count == 0)
fill_buffer();
- if(!is_started || sample_count <= 0) {
- playback_status = PLAYBACK_STATE_IDLE;
- decoded_frame_count = 0;
- decoded_samples = 0;
+ if(sample_count <= 0) {
outputs[0].fill(0, pos);
outputs[1].fill(0, pos);
return;
diff --git a/src/devices/sound/mas3507d.h b/src/devices/sound/mas3507d.h
index 36f95de72db..52c2e924109 100644
--- a/src/devices/sound/mas3507d.h
+++ b/src/devices/sound/mas3507d.h
@@ -12,31 +12,22 @@
class mas3507d_device : public device_t, public device_sound_interface
{
public:
- enum {
- PLAYBACK_STATE_IDLE,
- PLAYBACK_STATE_BUFFER_FULL,
- PLAYBACK_STATE_DEMAND_BUFFER
- };
-
// construction/destruction
mas3507d_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0);
- auto sample_cb() { return cb_sample.bind(); }
+ auto mpeg_frame_sync_cb() { return cb_mpeg_frame_sync.bind(); }
+ auto demand_cb() { return cb_demand.bind(); }
int i2c_scl_r();
int i2c_sda_r();
void i2c_scl_w(bool line);
void i2c_sda_w(bool line);
- uint32_t get_samples() const { return decoded_samples; }
- uint32_t get_status() const { return playback_status; }
+ void sid_w(uint8_t byte);
void update_stream() { stream->update(); }
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<write_stream_view> &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<uint16_t> 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::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")
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 <typename... T> void add_route(T &&... args) { subdevice<mas3507d_device>("mpeg")->add_route(std::forward<T>(args)...); }
template <typename T> void set_ram(T &&tag) { ram.set_tag(std::forward<T>(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<uint16_t> ram;
required_device<mas3507d_device> mas3507d;
- uint16_t crypto_key1 = 0, crypto_key2 = 0;
- uint8_t crypto_key3 = 0;
+ bool is_ddrsbm_fpga;
+
+ uint16_t mpeg_status, fpga_status;
+
+ 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;
- uint32_t mp3_start_addr = 0, mp3_cur_addr = 0, mp3_end_addr = 0;
- bool use_ddrsbm_fpga = false;
+ bool is_mpeg_frame_synced;
+ uint32_t mp3_frame_counter;
- 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;
+ attotime counter_current, counter_base;
+ double counter_value;
};
#endif // MAME_MACHINE_K573FPGA_H