k573dio/k573fpga: Improve counters and status flags (#9554)

This commit is contained in:
987123879113 2022-04-13 23:36:52 +09:00 committed by GitHub
parent c8ab724056
commit 232c10aef1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 335 additions and 190 deletions

View File

@ -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<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()
{
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;

View File

@ -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;

View File

@ -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)

View File

@ -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;
};

View File

@ -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")

View File

@ -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;
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