mirror of
https://github.com/holub/mame
synced 2025-04-16 05:24:54 +03:00
k573dio/k573fpga: Improve counters and status flags (#9554)
This commit is contained in:
parent
c8ab724056
commit
232c10aef1
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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")
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user