Partial support for encrypted audio in k573dio (Konami System 573 Digital I/O) (#5055)

* Add support for pcnfrk2m - Percussion Freaks 2nd Mix (GE912 VER. KAA)

* WIP audio for k573dio

* WIP

* Move 3rd party library to 3rdparty folder

* Use MAME's BIT and bitswap

* Fix regression which caused songs to stutter/lag when they should have been read completely in one shot

* Replace gain_to_db switch with equivalent math
This commit is contained in:
Windy Fairy 2019-05-16 09:17:00 +09:00 committed by R. Belmont
parent 17ff476852
commit ab8dbd3db0
14 changed files with 3273 additions and 38 deletions

2
3rdparty/README.md vendored
View File

@ -38,6 +38,8 @@ luafilesystem - [The MIT License (MIT)](http://opensource.org/licenses/MIT)
lzma - [The GNU Lesser General Public License](http://opensource.org/licenses/LGPL-2.1)
minimp3 - [Creative Commons Zero v1.0 Universal](https://creativecommons.org/publicdomain/zero/1.0/)
nanosvg - [zlib license](http://opensource.org/licenses/Zlib)
portaudio - [The MIT License (MIT)](http://opensource.org/licenses/MIT) explanation at [their site](http://www.portaudio.com/license.html)

1807
3rdparty/minimp3/minimp3.h vendored Normal file

File diff suppressed because it is too large Load Diff

392
3rdparty/minimp3/minimp3_ex.h vendored Normal file
View File

@ -0,0 +1,392 @@
#ifndef MINIMP3_EXT_H
#define MINIMP3_EXT_H
/*
https://github.com/lieff/minimp3
To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring rights to this software to the public domain worldwide.
This software is distributed without any warranty.
See <http://creativecommons.org/publicdomain/zero/1.0/>.
*/
#include "minimp3.h"
#define MP3D_SEEK_TO_BYTE 0
#define MP3D_SEEK_TO_SAMPLE 1
#define MP3D_SEEK_TO_SAMPLE_INDEXED 2
typedef struct
{
mp3d_sample_t *buffer;
size_t samples; /* channels included, byte size = samples*sizeof(int16_t) */
int channels, hz, layer, avg_bitrate_kbps, frame_bytes;
} mp3dec_file_info_t;
typedef struct
{
const uint8_t *buffer;
size_t size;
} mp3dec_map_info_t;
typedef struct
{
mp3dec_t mp3d;
mp3dec_map_info_t file;
int seek_method;
#ifndef MINIMP3_NO_STDIO
int is_file;
#endif
} mp3dec_ex_t;
typedef int (*MP3D_ITERATE_CB)(void *user_data, const uint8_t *frame, int frame_size, size_t offset, mp3dec_frame_info_t *info);
typedef int (*MP3D_PROGRESS_CB)(void *user_data, size_t file_size, size_t offset, mp3dec_frame_info_t *info);
#ifdef __cplusplus
extern "C" {
#endif
size_t mp3dec_skip_id3v2(const uint8_t *buf, size_t buf_size);
/* decode whole buffer block */
void mp3dec_load_buf(mp3dec_t *dec, const uint8_t *buf, size_t buf_size, mp3dec_file_info_t *info, MP3D_PROGRESS_CB progress_cb, void *user_data);
/* iterate through frames with optional decoding */
void mp3dec_iterate_buf(const uint8_t *buf, size_t buf_size, MP3D_ITERATE_CB callback, void *user_data);
/* decoder with seeking capability */
int mp3dec_ex_open_buf(mp3dec_ex_t *dec, const uint8_t *buf, size_t buf_size, int seek_method);
void mp3dec_ex_close(mp3dec_ex_t *dec);
void mp3dec_ex_seek(mp3dec_ex_t *dec, size_t position);
int mp3dec_ex_read(mp3dec_ex_t *dec, int16_t *buf, int samples);
#ifndef MINIMP3_NO_STDIO
/* stdio versions with file pre-load */
int mp3dec_load(mp3dec_t *dec, const char *file_name, mp3dec_file_info_t *info, MP3D_PROGRESS_CB progress_cb, void *user_data);
int mp3dec_iterate(const char *file_name, MP3D_ITERATE_CB callback, void *user_data);
int mp3dec_ex_open(mp3dec_ex_t *dec, const char *file_name, int seek_method);
#endif
#ifdef __cplusplus
}
#endif
#endif /*MINIMP3_EXT_H*/
#ifdef MINIMP3_IMPLEMENTATION
size_t mp3dec_skip_id3v2(const uint8_t *buf, size_t buf_size)
{
if (buf_size > 10 && !strncmp((char *)buf, "ID3", 3))
{
return (((buf[6] & 0x7f) << 21) | ((buf[7] & 0x7f) << 14) |
((buf[8] & 0x7f) << 7) | (buf[9] & 0x7f)) + 10;
}
return 0;
}
void mp3dec_load_buf(mp3dec_t *dec, const uint8_t *buf, size_t buf_size, mp3dec_file_info_t *info, MP3D_PROGRESS_CB progress_cb, void *user_data)
{
size_t orig_buf_size = buf_size;
mp3d_sample_t pcm[MINIMP3_MAX_SAMPLES_PER_FRAME];
mp3dec_frame_info_t frame_info;
memset(info, 0, sizeof(*info));
memset(&frame_info, 0, sizeof(frame_info));
/* skip id3v2 */
size_t id3v2size = mp3dec_skip_id3v2(buf, buf_size);
if (id3v2size > buf_size)
return;
buf += id3v2size;
buf_size -= id3v2size;
/* try to make allocation size assumption by first frame */
mp3dec_init(dec);
int samples;
do
{
samples = mp3dec_decode_frame(dec, buf, buf_size, pcm, &frame_info);
buf += frame_info.frame_bytes;
buf_size -= frame_info.frame_bytes;
if (samples) {
info->frame_bytes += frame_info.frame_bytes + id3v2size;
break;
}
} while (frame_info.frame_bytes);
if (!samples)
return;
samples *= frame_info.channels;
size_t allocated = (buf_size/frame_info.frame_bytes)*samples*sizeof(mp3d_sample_t) + MINIMP3_MAX_SAMPLES_PER_FRAME*sizeof(mp3d_sample_t);
info->buffer = (mp3d_sample_t*)malloc(allocated);
if (!info->buffer)
return;
info->samples = samples;
memcpy(info->buffer, pcm, info->samples*sizeof(mp3d_sample_t));
/* save info */
info->channels = frame_info.channels;
info->hz = frame_info.hz;
info->layer = frame_info.layer;
size_t avg_bitrate_kbps = frame_info.bitrate_kbps;
size_t frames = 1;
/* decode rest frames */
int frame_bytes;
do
{
if ((allocated - info->samples*sizeof(mp3d_sample_t)) < MINIMP3_MAX_SAMPLES_PER_FRAME*sizeof(mp3d_sample_t))
{
allocated *= 2;
info->buffer = (mp3d_sample_t*)realloc(info->buffer, allocated);
}
samples = mp3dec_decode_frame(dec, buf, buf_size, info->buffer + info->samples, &frame_info);
frame_bytes = frame_info.frame_bytes;
buf += frame_bytes;
buf_size -= frame_bytes;
if (samples)
{
info->frame_bytes += frame_info.frame_bytes;
if (info->hz != frame_info.hz || info->layer != frame_info.layer)
break;
if (info->channels && info->channels != frame_info.channels)
#ifdef MINIMP3_ALLOW_MONO_STEREO_TRANSITION
info->channels = 0; /* mark file with mono-stereo transition */
#else
break;
#endif
info->samples += samples*frame_info.channels;
avg_bitrate_kbps += frame_info.bitrate_kbps;
frames++;
if (progress_cb)
progress_cb(user_data, orig_buf_size, orig_buf_size - buf_size, &frame_info);
}
} while (frame_bytes);
/* reallocate to normal buffer size */
if (allocated != info->samples*sizeof(mp3d_sample_t))
info->buffer = (mp3d_sample_t*)realloc(info->buffer, info->samples*sizeof(mp3d_sample_t));
info->avg_bitrate_kbps = avg_bitrate_kbps/frames;
}
void mp3dec_iterate_buf(const uint8_t *buf, size_t buf_size, MP3D_ITERATE_CB callback, void *user_data)
{
if (!callback)
return;
mp3dec_frame_info_t frame_info;
memset(&frame_info, 0, sizeof(frame_info));
/* skip id3v2 */
size_t id3v2size = mp3dec_skip_id3v2(buf, buf_size);
if (id3v2size > buf_size)
return;
const uint8_t *orig_buf = buf;
buf += id3v2size;
buf_size -= id3v2size;
do
{
int free_format_bytes = 0, frame_size = 0;
int i = mp3d_find_frame(buf, buf_size, &free_format_bytes, &frame_size);
buf += i;
buf_size -= i;
if (i && !frame_size)
continue;
if (!frame_size)
break;
const uint8_t *hdr = buf;
frame_info.channels = HDR_IS_MONO(hdr) ? 1 : 2;
frame_info.hz = hdr_sample_rate_hz(hdr);
frame_info.layer = 4 - HDR_GET_LAYER(hdr);
frame_info.bitrate_kbps = hdr_bitrate_kbps(hdr);
frame_info.frame_bytes = frame_size;
if (callback(user_data, hdr, frame_size, hdr - orig_buf, &frame_info))
break;
buf += frame_size;
buf_size -= frame_size;
} while (1);
}
int mp3dec_ex_open_buf(mp3dec_ex_t *dec, const uint8_t *buf, size_t buf_size, int seek_method)
{
memset(dec, 0, sizeof(*dec));
dec->file.buffer = buf;
dec->file.size = buf_size;
dec->seek_method = seek_method;
mp3dec_init(&dec->mp3d);
return 0;
}
/*void mp3dec_ex_seek(mp3dec_ex_t *dec, size_t position)
{
}
int mp3dec_ex_read(mp3dec_ex_t *dec, int16_t *buf, int samples)
{
return 0;
}*/
#ifndef MINIMP3_NO_STDIO
#if defined(__linux__) || defined(__FreeBSD__)
#include <errno.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#if !defined(MAP_POPULATE) && defined(__linux__)
#define MAP_POPULATE 0x08000
#elif !defined(MAP_POPULATE)
#define MAP_POPULATE 0
#endif
static void mp3dec_close_file(mp3dec_map_info_t *map_info)
{
if (map_info->buffer && MAP_FAILED != map_info->buffer)
munmap((void *)map_info->buffer, map_info->size);
map_info->buffer = 0;
map_info->size = 0;
}
static int mp3dec_open_file(const char *file_name, mp3dec_map_info_t *map_info)
{
int file;
struct stat st;
memset(map_info, 0, sizeof(*map_info));
retry_open:
file = open(file_name, O_RDONLY);
if (file < 0 && (errno == EAGAIN || errno == EINTR))
goto retry_open;
if (file < 0 || fstat(file, &st) < 0)
{
close(file);
return -1;
}
map_info->size = st.st_size;
retry_mmap:
map_info->buffer = (const uint8_t *)mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE | MAP_POPULATE, file, 0);
if (MAP_FAILED == map_info->buffer && (errno == EAGAIN || errno == EINTR))
goto retry_mmap;
close(file);
if (MAP_FAILED == map_info->buffer)
return -1;
return 0;
}
#elif defined(_WIN32)
#include <windows.h>
static void mp3dec_close_file(mp3dec_map_info_t *map_info)
{
if (map_info->buffer)
UnmapViewOfFile(map_info->buffer);
map_info->buffer = 0;
map_info->size = 0;
}
static int mp3dec_open_file(const char *file_name, mp3dec_map_info_t *map_info)
{
memset(map_info, 0, sizeof(*map_info));
HANDLE file = CreateFileA(file_name, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
if (INVALID_HANDLE_VALUE == file)
return -1;
LARGE_INTEGER s;
s.LowPart = GetFileSize(file, (DWORD*)&s.HighPart);
if (s.LowPart == INVALID_FILE_SIZE && GetLastError() != NO_ERROR)
goto error;
map_info->size = s.QuadPart;
HANDLE mapping = CreateFileMapping(file, NULL, PAGE_READONLY, 0, 0, NULL);
if (!mapping)
goto error;
map_info->buffer = MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, s.QuadPart);
CloseHandle(mapping);
if (!map_info->buffer)
goto error;
CloseHandle(file);
return 0;
error:
mp3dec_close_file(map_info);
CloseHandle(file);
return -1;
}
#else
#include <stdio.h>
static void mp3dec_close_file(mp3dec_map_info_t *map_info)
{
if (map_info->buffer)
free((void *)map_info->buffer);
map_info->buffer = 0;
map_info->size = 0;
}
static int mp3dec_open_file(const char *file_name, mp3dec_map_info_t *map_info)
{
memset(map_info, 0, sizeof(*map_info));
FILE *file = fopen(file_name, "rb");
if (!file)
return -1;
long size = -1;
if (fseek(file, 0, SEEK_END))
goto error;
size = ftell(file);
if (size < 0)
goto error;
map_info->size = (size_t)size;
if (fseek(file, 0, SEEK_SET))
goto error;
map_info->buffer = (uint8_t *)malloc(map_info->size);
if (!map_info->buffer)
goto error;
if (fread((void *)map_info->buffer, 1, map_info->size, file) != map_info->size)
goto error;
fclose(file);
return 0;
error:
mp3dec_close_file(map_info);
fclose(file);
return -1;
}
#endif
int mp3dec_load(mp3dec_t *dec, const char *file_name, mp3dec_file_info_t *info, MP3D_PROGRESS_CB progress_cb, void *user_data)
{
int ret;
mp3dec_map_info_t map_info;
if ((ret = mp3dec_open_file(file_name, &map_info)))
return ret;
mp3dec_load_buf(dec, map_info.buffer, map_info.size, info, progress_cb, user_data);
mp3dec_close_file(&map_info);
return 0;
}
int mp3dec_iterate(const char *file_name, MP3D_ITERATE_CB callback, void *user_data)
{
int ret;
mp3dec_map_info_t map_info;
if ((ret = mp3dec_open_file(file_name, &map_info)))
return ret;
mp3dec_iterate_buf(map_info.buffer, map_info.size, callback, user_data);
mp3dec_close_file(&map_info);
return 0;
}
void mp3dec_ex_close(mp3dec_ex_t *dec)
{
if (dec->is_file)
mp3dec_close_file(&dec->file);
else
free((void *)dec->file.buffer);
memset(dec, 0, sizeof(*dec));
}
int mp3dec_ex_open(mp3dec_ex_t *dec, const char *file_name, int seek_method)
{
int ret;
memset(dec, 0, sizeof(*dec));
if ((ret = mp3dec_open_file(file_name, &dec->file)))
return ret;
dec->seek_method = seek_method;
dec->is_file = 1;
mp3dec_init(&dec->mp3d);
return 0;
}
#else
void mp3dec_ex_close(mp3dec_ex_t *dec)
{
free((void*)dec->file.buffer);
memset(dec, 0, sizeof(*dec));
}
#endif
#endif /*MINIMP3_IMPLEMENTATION*/

View File

@ -2393,6 +2393,8 @@ files {
MAME_DIR .. "src/mame/machine/k573cass.h",
MAME_DIR .. "src/mame/machine/k573dio.cpp",
MAME_DIR .. "src/mame/machine/k573dio.h",
MAME_DIR .. "src/mame/machine/k573fpga.cpp",
MAME_DIR .. "src/mame/machine/k573fpga.h",
MAME_DIR .. "src/mame/machine/k573mcr.cpp",
MAME_DIR .. "src/mame/machine/k573mcr.h",
MAME_DIR .. "src/mame/machine/k573msu.cpp",

View File

@ -15,6 +15,7 @@ mas3507d_device::mas3507d_device(const machine_config &mconfig, const char *tag,
, device_sound_interface(mconfig, *this)
, i2c_bus_state(), i2c_bus_address(), i2c_scli(false), i2c_sclo(false), i2c_sdai(false), i2c_sdao(false)
, i2c_bus_curbit(0), i2c_bus_curval(0), i2c_subdest(), i2c_command(), i2c_bytecount(0), i2c_io_bank(0), i2c_io_adr(0), i2c_io_count(0), i2c_io_val(0)
, m_samples(nullptr)
{
}
@ -48,6 +49,7 @@ void mas3507d_device::i2c_scl_w(bool line)
if(i2c_device_got_address(i2c_bus_curval)) {
i2c_bus_state = ACK;
i2c_bus_address = VALIDATED;
i2c_bus_curval = 0;
} else {
i2c_bus_state = NAK;
i2c_bus_address = WRONG;
@ -111,7 +113,12 @@ int mas3507d_device::i2c_sda_r()
bool mas3507d_device::i2c_device_got_address(uint8_t address)
{
i2c_subdest = UNDEFINED;
if (address == 0x3b) {
i2c_subdest = DATA_READ;
} else {
i2c_subdest = UNDEFINED;
}
return (address & 0xfe) == 0x3a;
}
@ -120,19 +127,37 @@ void mas3507d_device::i2c_device_got_byte(uint8_t byte)
switch(i2c_subdest) {
case UNDEFINED:
if(byte == 0x68)
i2c_subdest = DATA;
i2c_subdest = DATA_WRITE;
else if(byte == 0x69)
i2c_subdest = DATA;
i2c_subdest = DATA_READ;
else if(byte == 0x6a)
i2c_subdest = CONTROL;
else
i2c_subdest = BAD;
i2c_bytecount = 0;
i2c_io_val = 0;
break;
case BAD:
logerror("MAS I2C: Dropping byte %02x\n", byte);
break;
case DATA:
case DATA_READ:
// Default Read
// This should return the current MPEGFrameCount value when called
// TODO: Figure out how to use this data exactly (chip docs are a little unclear to me)
i2c_io_val <<= 8;
i2c_io_val |= byte;
i2c_bytecount++;
logerror("MAS I2C: DATA_READ %d %08x\n", i2c_bytecount, i2c_io_val);
break;
case DATA_WRITE:
if(!i2c_bytecount) {
switch(byte >> 4) {
case 0: case 1:
@ -215,6 +240,7 @@ void mas3507d_device::i2c_device_got_byte(uint8_t byte)
i2c_bytecount++;
break;
case CONTROL:
logerror("MAS I2C: Control byte %02x\n", byte);
break;
@ -226,14 +252,46 @@ void mas3507d_device::i2c_device_got_stop()
logerror("MAS I2C: got stop\n");
}
int gain_to_db(double val) {
return round(20 * log10((0x100000 - val) / 0x80000));
}
float gain_to_percentage(int val) {
double db = gain_to_db(val);
if (db == 0) {
return 0; // Special case for muting it seems
}
return powf(10.0, (db + 6) / 20.0);
}
void mas3507d_device::mem_write(int bank, uint32_t adr, uint32_t val)
{
switch(adr | (bank ? 0x10000 : 0)) {
case 0x0032f: logerror("MAS3507D: OutputConfig = %05x\n", val); break;
case 0x107f8: logerror("MAS3507D: left->left gain = %05x\n", val); break;
case 0x107f9: logerror("MAS3507D: left->right gain = %05x\n", val); break;
case 0x107fa: logerror("MAS3507D: right->left gain = %05x\n", val); break;
case 0x107fb: logerror("MAS3507D: right->right gain = %05x\n", val); break;
case 0x107f8:
logerror("MAS3507D: left->left gain = %05x (%d dB, %f%%)\n", val, gain_to_db(val), gain_to_percentage(val));
if (m_samples != nullptr) {
m_samples->set_volume(0, gain_to_percentage(val));
}
break;
case 0x107f9:
logerror("MAS3507D: left->right gain = %05x (%d dB, %f%%)\n", val, gain_to_db(val), gain_to_percentage(val));
break;
case 0x107fa:
logerror("MAS3507D: right->left gain = %05x (%d dB, %f%%)\n", val, gain_to_db(val), gain_to_percentage(val));
break;
case 0x107fb:
logerror("MAS3507D: right->right gain = %05x (%d dB, %f%%)\n", val, gain_to_db(val), gain_to_percentage(val));
if (m_samples != nullptr) {
m_samples->set_volume(1, gain_to_percentage(val));
}
break;
default: logerror("MAS3507D: %d:%04x = %05x\n", bank, adr, val); break;
}
}
@ -261,4 +319,4 @@ void mas3507d_device::run_program(uint32_t adr)
void mas3507d_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
{
}
}

View File

@ -5,6 +5,9 @@
#pragma once
#include <queue>
#include "sound/samples.h"
//**************************************************************************
// TYPE DEFINITIONS
@ -21,6 +24,8 @@ public:
void i2c_scl_w(bool line);
void i2c_sda_w(bool line);
void set_samples(samples_device *sample) { m_samples = sample; }
protected:
virtual void device_start() override;
virtual void device_reset() override;
@ -40,15 +45,16 @@ private:
void i2c_device_got_stop();
enum { UNDEFINED, CONTROL, DATA, BAD } i2c_subdest;
enum { UNDEFINED, CONTROL, DATA_READ, DATA_WRITE, BAD } i2c_subdest;
enum { CMD_BAD, CMD_RUN, CMD_READ_CTRL, CMD_WRITE_REG, CMD_WRITE_MEM, CMD_READ_REG, CMD_READ_MEM } i2c_command;
int i2c_bytecount;
uint32_t i2c_io_bank, i2c_io_adr, i2c_io_count, i2c_io_val;
void mem_write(int bank, uint32_t adr, uint32_t val);
void run_program(uint32_t adr);
void reg_write(uint32_t adr, uint32_t val);
samples_device *m_samples;
};

View File

@ -119,6 +119,27 @@ void samples_device::start_raw(uint8_t channel, const int16_t *sampledata, uint3
chan.loop = loop;
}
void samples_device::update_raw(uint8_t channel, const int16_t *sampledata, uint32_t samples, uint32_t frequency, bool loop)
{
assert(channel < m_channels);
// update the parameters
channel_t &chan = m_channel[channel];
bool is_update = chan.source != nullptr;
chan.source = sampledata;
chan.source_length = samples;
chan.basefreq = frequency;
chan.step = (int64_t(chan.basefreq) << FRAC_BITS) / machine().sample_rate();
chan.loop = loop;
if (!is_update) {
chan.source_num = -1;
chan.pos = 0;
chan.frac = 0;
}
}
//-------------------------------------------------
// set_frequency - set the playback frequency of
@ -321,8 +342,13 @@ void samples_device::device_post_load()
void samples_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
{
if (!m_samples_update_cb.isnull()) {
m_samples_update_cb();
}
// find the channel with this stream
for (int channel = 0; channel < m_channels; channel++)
{
if (&stream == m_channel[channel].stream)
{
channel_t &chan = m_channel[channel];
@ -355,7 +381,9 @@ void samples_device::sound_stream_update(sound_stream &stream, stream_sample_t *
if (pos >= sample_length)
{
if (chan.loop)
{
pos %= sample_length;
}
else
{
chan.source = nullptr;
@ -375,6 +403,7 @@ void samples_device::sound_stream_update(sound_stream &stream, stream_sample_t *
memset(buffer, 0, samples * sizeof(*buffer));
break;
}
}
}
@ -638,3 +667,16 @@ bool samples_device::load_samples()
}
return ok;
}
uint32_t samples_device::get_position(uint8_t channel)
{
assert(channel < m_channels);
channel_t &chan = m_channel[channel];
if (chan.source == nullptr) {
return 0;
}
return chan.pos;
}

View File

@ -26,6 +26,7 @@ DECLARE_DEVICE_TYPE(SAMPLES, samples_device)
//**************************************************************************
#define SAMPLES_START_CB_MEMBER(_name) void _name()
#define SAMPLES_UPDATE_CB_MEMBER(_name) void _name()
// ======================> samples_device
@ -34,6 +35,7 @@ class samples_device : public device_t,
{
public:
typedef device_delegate<void ()> start_cb_delegate;
typedef device_delegate<void ()> update_cb_delegate;
// construction/destruction
samples_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0);
@ -53,13 +55,30 @@ public:
set_samples_start_callback(start_cb_delegate(callback, name, nullptr, static_cast<FunctionClass *>(nullptr)));
}
// update callback helpers
void set_samples_update_callback(update_cb_delegate callback) {
m_samples_update_cb = callback;
m_samples_update_cb.bind_relative_to(*owner());
}
template <class FunctionClass> void set_samples_update_callback(const char *devname, void (FunctionClass::*callback)(), const char *name)
{
set_samples_update_callback(update_cb_delegate(callback, name, devname, static_cast<FunctionClass *>(nullptr)));
}
template <class FunctionClass> void set_samples_update_callback(void (FunctionClass::*callback)(), const char *name)
{
set_samples_update_callback(update_cb_delegate(callback, name, nullptr, static_cast<FunctionClass *>(nullptr)));
}
// getters
bool playing(uint8_t channel) const;
uint32_t base_frequency(uint8_t channel) const;
uint32_t get_position(uint8_t channel);
// start/stop helpers
void start(uint8_t channel, uint32_t samplenum, bool loop = false);
void start_raw(uint8_t channel, const int16_t *sampledata, uint32_t samples, uint32_t frequency, bool loop = false);
void update_raw(uint8_t channel, const int16_t *sampledata, uint32_t samples, uint32_t frequency, bool loop = false);
void pause(uint8_t channel, bool pause = true);
void stop(uint8_t channel);
void stop_all();
@ -116,6 +135,7 @@ protected:
bool load_samples();
start_cb_delegate m_samples_start_cb; // optional callback
start_cb_delegate m_samples_update_cb; // optional callback
// internal state
std::vector<channel_t> m_channel;

View File

@ -413,6 +413,7 @@ public:
void hyperbbc(machine_config &config);
void pnchmn2(machine_config &config);
void ddrsolo(machine_config &config);
void ddrsbm(machine_config &config);
void ddr3mp(machine_config &config);
void dsftkd(machine_config &config);
void dsfdcta(machine_config &config);
@ -431,6 +432,7 @@ public:
void gtrfrks(machine_config &config);
void gchgchmp(machine_config &config);
void ddr5m(machine_config &config);
void ddrmax(machine_config &config);
void drmn4m(machine_config &config);
void fbaitbc(machine_config &config);
void ddr4ms(machine_config &config);
@ -2293,6 +2295,16 @@ void ksys573_state::ddr5m(machine_config &config)
casszi(config);
}
void ksys573_state::ddrmax(machine_config &config)
{
k573d(config);
subdevice<k573dio_device>("k573dio")->output_callback().set(FUNC(ksys573_state::ddr_output_callback));
subdevice<k573dio_device>("k573dio")->set_buffer_speed(0x4000);
pccard2_32mb(config);
casszi(config);
}
// Dancing Stage
void ksys573_state::dsfdcta(machine_config &config)
@ -2334,6 +2346,15 @@ void ksys573_state::ddrsolo(machine_config &config)
cassyi(config);
}
void ksys573_state::ddrsbm(machine_config &config)
{
k573d(config);
subdevice<k573dio_device>("k573dio")->output_callback().set(FUNC(ksys573_state::ddrsolo_output_callback));
subdevice<k573dio_device>("k573dio")->set_fake_fpga(true);
cassyi(config);
}
void ksys573_state::ddrs2k(machine_config &config)
{
k573d(config);
@ -2416,6 +2437,8 @@ void ksys573_state::gtrfrk7m(machine_config &config)
void ksys573_state::gtfrk10mb(machine_config &config)
{
gtrfrk7m(config);
subdevice<k573dio_device>("k573dio")->set_mp3_dynamic_base(0x00500000);
KONAMI_573_NETWORK_PCB_UNIT(config, "k573npu", 0);
}
@ -4760,6 +4783,25 @@ ROM_START( powyakex )
DISK_IMAGE_READONLY( "802jab02", 0, SHA1(460cc9f0b2514ec1da06b0a1d7b52fe43220d181) )
ROM_END
ROM_START( pcnfrk2m )
SYS573_BIOS_A
ROM_REGION( 0x0000224, "cassette:install:eeprom", 0 )
ROM_LOAD( "ge912ka.u1", 0x000000, 0x000224, BAD_DUMP CRC(b3d5ca9a) SHA1(3dd9034e1a3a78a03bef975186b7ac6b01e3131a) )
ROM_REGION( 0x0001014, "cassette:game:eeprom", 0 )
ROM_LOAD( "gn912ka.u1", 0x000000, 0x001014, BAD_DUMP CRC(c6af0c1a) SHA1(042d23bcfabc2a7fb6d7a038978805968e229395) )
ROM_REGION( 0x000008, "cassette:install:id", 0 )
ROM_LOAD( "ge912ka.u6", 0x000000, 0x000008, BAD_DUMP CRC(af09e43c) SHA1(d8372f2d6e0ae07061b496a2242a63e5bc2e54dc) )
ROM_REGION( 0x000008, "cassette:game:id", 0 )
ROM_LOAD( "gn912ka.u6", 0x000000, 0x000008, BAD_DUMP CRC(ce84419e) SHA1(839e8ee080ecfc79021a06417d930e8b32dfc6a1) )
DISK_REGION( "cdrom0" )
DISK_IMAGE_READONLY( "912kaa02", 0, BAD_DUMP SHA1(176425d4be6809ede0333dd617056c7e8e3aa13a) )
ROM_END
ROM_START( pcnfrk3m )
SYS573_BIOS_A
@ -4981,7 +5023,7 @@ GAME( 2000, ddr3mk, sys573, ddr3m, ddr, ksys573_state, empty_ini
GAME( 2000, ddr3mka, ddr3mk, ddr3m, ddr, ksys573_state, empty_init, ROT0, "Konami", "Dance Dance Revolution 3rd Mix - Ver.Korea (GN887 VER. KAA)", MACHINE_IMPERFECT_SOUND | MACHINE_NOT_WORKING ) /* BOOT VER 1.3 */
GAME( 1999, ddr3ma, ddr3mk, ddr3m, ddr, ksys573_state, empty_init, ROT0, "Konami", "Dance Dance Revolution 3rd Mix (GN887 VER. AAA)", MACHINE_IMPERFECT_SOUND | MACHINE_NOT_WORKING ) /* BOOT VER 1.1 */
GAME( 1999, ddr3mj, ddr3mk, ddr3m, ddr, ksys573_state, empty_init, ROT0, "Konami", "Dance Dance Revolution 3rd Mix (GN887 VER. JAA)", MACHINE_IMPERFECT_SOUND | MACHINE_NOT_WORKING ) /* BOOT VER 1.0 */
GAME( 1999, ddrsbm, sys573, ddrsolo, ddrsolo, ksys573_state, empty_init, ROT0, "Konami", "Dance Dance Revolution Solo Bass Mix (GQ894 VER. JAA)", MACHINE_IMPERFECT_SOUND | MACHINE_NOT_WORKING )
GAME( 1999, ddrsbm, sys573, ddrsbm, ddrsolo, ksys573_state, empty_init, ROT0, "Konami", "Dance Dance Revolution Solo Bass Mix (GQ894 VER. JAA)", MACHINE_IMPERFECT_SOUND | MACHINE_NOT_WORKING )
GAME( 1999, ddrs2k, sys573, ddrs2k, ddrsolo, ksys573_state, empty_init, ROT0, "Konami", "Dance Dance Revolution Solo 2000 (GC905 VER. AAA)", MACHINE_IMPERFECT_SOUND | MACHINE_NOT_WORKING ) /* BOOT VER 1.3 */
GAME( 1999, ddrs2kj, ddrs2k, ddrs2k, ddrsolo, ksys573_state, empty_init, ROT0, "Konami", "Dance Dance Revolution Solo 2000 (GC905 VER. JAA)", MACHINE_IMPERFECT_SOUND | MACHINE_NOT_WORKING ) /* BOOT VER 1.2 */
GAME( 1999, hypbbc2p, sys573, hypbbc2p, hypbbc2p, ksys573_state, init_hyperbbc, ROT0, "Konami", "Hyper Bishi Bashi Champ - 2 Player (GX908 1999/08/24 VER. JAA)", MACHINE_IMPERFECT_SOUND )
@ -4991,6 +5033,7 @@ GAME( 1999, dsfdcta, dsfdct, dsfdcta, ddr, ksys573_state, init_ddr,
GAME( 1999, drmn2m, sys573, drmn2m, drmn, ksys573_state, empty_init, ROT0, "Konami", "DrumMania 2nd Mix (GE912 VER. JAB)", MACHINE_IMPERFECT_SOUND | MACHINE_NOT_WORKING ) /* BOOT VER 1.5 */
GAME( 1999, drmn2mpu, drmn2m, drmn2m, drmn, ksys573_state, empty_init, ROT0, "Konami", "DrumMania 2nd Mix Session Power Up Kit (GE912 VER. JAB)", MACHINE_IMPERFECT_SOUND | MACHINE_NOT_WORKING ) /* BOOT VER 1.5 */
GAME( 1999, stepchmp, sys573, salarymc, hyperbbc, ksys573_state, init_salarymc, ROT0, "Konami", "Step Champ (GQ930 VER. JA)", MACHINE_NO_SOUND )
GAME( 2000, pcnfrk2m, sys573, drmn2m, drmn, ksys573_state, empty_init, ROT0, "Konami", "Percussion Freaks 2nd Mix (GE912 VER. KAA)", MACHINE_IMPERFECT_SOUND | MACHINE_NOT_WORKING ) /* BOOT VER 1.5 */
GAME( 2000, dncfrks, sys573, dmx, dmx, ksys573_state, empty_init, ROT0, "Konami", "Dance Freaks (G*874 VER. KAA)", MACHINE_IMPERFECT_SOUND | MACHINE_NOT_WORKING ) /* BOOT VER 1.6 */
GAME( 2000, dmx, dncfrks, dmx, dmx, ksys573_state, empty_init, ROT0, "Konami", "Dance Maniax (G*874 VER. JAA)", MACHINE_IMPERFECT_SOUND | MACHINE_NOT_WORKING ) /* BOOT VER 1.6 */
GAME( 2000, gunmania, sys573, gunmania, gunmania, ksys573_state, empty_init, ROT0, "Konami", "GunMania (GL906 VER. JAA)", MACHINE_IMPERFECT_SOUND | MACHINE_NOT_WORKING )
@ -5031,8 +5074,8 @@ GAME( 2001, drmn5m, pcnfrk5m, drmn4m, drmn, ksys573_state, empty_ini
GAME( 2001, gtrfrk6m, sys573, gtrfrk5m, gtrfrks, ksys573_state, empty_init, ROT0, "Konami", "Guitar Freaks 6th Mix (G*B06 VER. JAA)", MACHINE_IMPERFECT_SOUND | MACHINE_NOT_WORKING ) /* BOOT VER 1.9 */
GAME( 2001, drmn6m, sys573, drmn4m, drmn, ksys573_state, empty_init, ROT0, "Konami", "DrumMania 6th Mix (G*B16 VER. JAA)", MACHINE_IMPERFECT_SOUND | MACHINE_NOT_WORKING ) /* BOOT VER 1.95 */
GAME( 2001, gtrfrk7m, sys573, gtrfrk7m, gtrfrks, ksys573_state, empty_init, ROT0, "Konami", "Guitar Freaks 7th Mix (G*B17 VER. JAA)", MACHINE_IMPERFECT_SOUND | MACHINE_NOT_WORKING ) /* BOOT VER 1.95 */
GAME( 2001, ddrmax, sys573, ddr5m, ddr, ksys573_state, empty_init, ROT0, "Konami", "DDR Max - Dance Dance Revolution 6th Mix (G*B19 VER. JAA)", MACHINE_IMPERFECT_SOUND | MACHINE_NOT_WORKING ) /* BOOT VER 1.9 */
GAME( 2002, ddrmax2, sys573, ddr5m, ddr, ksys573_state, empty_init, ROT0, "Konami", "DDR Max 2 - Dance Dance Revolution 7th Mix (G*B20 VER. JAA)", MACHINE_IMPERFECT_SOUND | MACHINE_NOT_WORKING ) /* BOOT VER 1.95 */
GAME( 2001, ddrmax, sys573, ddrmax, ddr, ksys573_state, empty_init, ROT0, "Konami", "DDR Max - Dance Dance Revolution 6th Mix (G*B19 VER. JAA)", MACHINE_IMPERFECT_SOUND | MACHINE_NOT_WORKING ) /* BOOT VER 1.9 */
GAME( 2002, ddrmax2, sys573, ddrmax, ddr, ksys573_state, empty_init, ROT0, "Konami", "DDR Max 2 - Dance Dance Revolution 7th Mix (G*B20 VER. JAA)", MACHINE_IMPERFECT_SOUND | MACHINE_NOT_WORKING ) /* BOOT VER 1.95 */
GAME( 2002, mrtlbeat, sys573, ddr5m, ddr, ksys573_state, empty_init, ROT0, "Konami", "Martial Beat (G*B47 VER. JBA)", MACHINE_IMPERFECT_SOUND | MACHINE_NOT_WORKING ) /* BOOT VER 1.9 */
GAME( 2002, drmn7m, sys573, drmn4m, drmn, ksys573_state, empty_init, ROT0, "Konami", "DrumMania 7th Mix power-up ver. (G*C07 VER. JBA)", MACHINE_IMPERFECT_SOUND | MACHINE_NOT_WORKING ) /* BOOT VER 1.95 */
GAME( 2002, drmn7ma, drmn7m, drmn4m, drmn, ksys573_state, empty_init, ROT0, "Konami", "DrumMania 7th Mix (G*C07 VER. JAA)", MACHINE_IMPERFECT_SOUND | MACHINE_NOT_WORKING ) /* BOOT VER 1.95 */
@ -5047,4 +5090,4 @@ GAME( 2003, gtfrk10m, sys573, gtrfrk7m, gtrfrks, ksys573_state, empty_ini
GAME( 2003, gtfrk10ma, gtfrk10m, gtrfrk7m, gtrfrks, ksys573_state, empty_init, ROT0, "Konami", "Guitar Freaks 10th Mix (G*D10 VER. JAA)", MACHINE_IMPERFECT_SOUND | MACHINE_NOT_WORKING ) /* BOOT VER 1.95 */
GAME( 2003, gtfrk10mb, gtfrk10m, gtfrk10mb, gtrfrks, ksys573_state, empty_init, ROT0, "Konami", "Guitar Freaks 10th Mix eAmusement (G*D10 VER. JBA)", MACHINE_IMPERFECT_SOUND | MACHINE_NOT_WORKING ) /* BOOT VER 1.95 */
GAME( 2004, gtfrk11m, sys573, gtrfrk7m, gtrfrks, ksys573_state, empty_init, ROT0, "Konami", "Guitar Freaks 11th Mix (G*D39 VER. JAA)", MACHINE_IMPERFECT_SOUND | MACHINE_NOT_WORKING ) /* BOOT VER 1.95 */
GAME( 2004, drmn10m, sys573, drmn4m, drmn, ksys573_state, empty_init, ROT0, "Konami", "DrumMania 10th Mix (G*D40 VER. JAA)", MACHINE_IMPERFECT_SOUND | MACHINE_NOT_WORKING ) /* BOOT VER 1.95 */
GAME( 2004, drmn10m, sys573, drmn4m, drmn, ksys573_state, empty_init, ROT0, "Konami", "DrumMania 10th Mix (G*D40 VER. JAA)", MACHINE_IMPERFECT_SOUND | MACHINE_NOT_WORKING ) /* BOOT VER 1.95 */

View File

@ -74,19 +74,25 @@ void k573dio_device::amap(address_map &map)
map(0x04, 0x05).r(FUNC(k573dio_device::a04_r));
map(0x06, 0x07).r(FUNC(k573dio_device::a06_r));
map(0x0a, 0x0b).r(FUNC(k573dio_device::a0a_r));
map(0x10, 0x11).w(FUNC(k573dio_device::a10_w));
map(0x80, 0x81).r(FUNC(k573dio_device::a80_r));
map(0xa8, 0xa9).r(FUNC(k573dio_device::aa8_r));
map(0xc4, 0xc5).r(FUNC(k573dio_device::ac4_r));
map(0xa0, 0xa1).w(FUNC(k573dio_device::mpeg_start_adr_high_w));
map(0xa2, 0xa3).w(FUNC(k573dio_device::mpeg_start_adr_low_w));
map(0xa4, 0xa5).w(FUNC(k573dio_device::mpeg_end_adr_high_w));
map(0xa6, 0xa7).w(FUNC(k573dio_device::mpeg_end_adr_low_w));
map(0xa8, 0xa9).w(FUNC(k573dio_device::mpeg_key_1_w));
map(0xac, 0xad).rw(FUNC(k573dio_device::mas_i2c_r), FUNC(k573dio_device::mas_i2c_w));
map(0xae, 0xaf).w(FUNC(k573dio_device::mpeg_ctrl_w));
map(0xae, 0xaf).rw(FUNC(k573dio_device::mpeg_ctrl_r), FUNC(k573dio_device::mpeg_ctrl_w));
map(0xb0, 0xb1).w(FUNC(k573dio_device::ram_write_adr_high_w));
map(0xb2, 0xb3).w(FUNC(k573dio_device::ram_write_adr_low_w));
map(0xb4, 0xb5).rw(FUNC(k573dio_device::ram_r), FUNC(k573dio_device::ram_w));
map(0xb6, 0xb7).w(FUNC(k573dio_device::ram_read_adr_high_w));
map(0xb8, 0xb9).w(FUNC(k573dio_device::ram_read_adr_low_w));
map(0xca, 0xcb).rw(FUNC(k573dio_device::mp3_playback_high_r), FUNC(k573dio_device::mp3_playback_high_w));
map(0xcc, 0xcd).rw(FUNC(k573dio_device::mp3_playback_low_r), FUNC(k573dio_device::mp3_playback_low_w));
map(0xce, 0xcf).r(FUNC(k573dio_device::mp3_unk_r));
map(0xe0, 0xe1).w(FUNC(k573dio_device::output_1_w));
map(0xe2, 0xe3).w(FUNC(k573dio_device::output_0_w));
map(0xe4, 0xe5).w(FUNC(k573dio_device::output_3_w));
@ -103,22 +109,34 @@ void k573dio_device::amap(address_map &map)
k573dio_device::k573dio_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: device_t(mconfig, KONAMI_573_DIGITAL_IO_BOARD, tag, owner, clock),
mas3507d(*this, "mpeg"),
k573fpga(*this, "k573fpga"),
digital_id(*this, "digital_id"),
output_cb(*this)
output_cb(*this),
is_fake_fpga(false),
buffer_speed(0x400)
{
}
void k573dio_device::device_start()
{
output_cb.resolve_safe();
ram = std::make_unique<uint16_t[]>(12 * 1024 * 1024 );
save_pointer(NAME(ram), 12 * 1024 * 1024 );
ram = std::make_unique<uint16_t[]>(32 * 1024 * 1024);
save_pointer(NAME(ram), 32 * 1024 * 1024 );
k573fpga->set_ram(ram.get());
k573fpga->set_fake_fpga(is_fake_fpga);
k573fpga->set_buffer_speed(buffer_speed);
k573fpga->set_mp3_dynamic_base(mp3_dynamic_base);
device_reset();
}
void k573dio_device::device_reset()
{
ram_adr = 0;
ram_read_adr = 0;
memset(output_data, 0, sizeof(output_data));
}
@ -134,8 +152,8 @@ const tiny_rom_entry *k573dio_device::device_rom_region() const
void k573dio_device::device_add_mconfig(machine_config &config)
{
MAS3507D(config, "mpeg");
DS2401( config, "digital_id" );
KONAMI_573_DIGITAL_FPGA(config, "k573fpga");
DS2401(config, "digital_id");
}
void k573dio_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
@ -172,6 +190,23 @@ READ16_MEMBER(k573dio_device::a0a_r)
return 0x0000;
}
WRITE16_MEMBER(k573dio_device::a10_w)
{
logerror("%s: a10_w (%s)\n", tag(), machine().describe_context());
}
READ16_MEMBER(k573dio_device::aa8_r)
{
logerror("%s: aa8_r (%s)\n", tag(), machine().describe_context());
return 1;
}
READ16_MEMBER(k573dio_device::ac4_r)
{
// What is this?
return 0;
}
READ16_MEMBER(k573dio_device::a80_r)
{
logerror("%s: a80_r (%s)\n", tag(), machine().describe_context());
@ -181,45 +216,51 @@ READ16_MEMBER(k573dio_device::a80_r)
WRITE16_MEMBER(k573dio_device::mpeg_start_adr_high_w)
{
logerror("FPGA MPEG start address high %04x\n", data);
k573fpga->set_mp3_start_adr((k573fpga->get_mp3_start_adr() & 0x0000ffff) | (data << 16)); // high
}
WRITE16_MEMBER(k573dio_device::mpeg_start_adr_low_w)
{
logerror("FPGA MPEG start address low %04x\n", data);
k573fpga->set_mp3_start_adr((k573fpga->get_mp3_start_adr() & 0xffff0000) | data); // low
}
WRITE16_MEMBER(k573dio_device::mpeg_end_adr_high_w)
{
logerror("FPGA MPEG end address high %04x\n", data);
k573fpga->set_mp3_end_adr((k573fpga->get_mp3_end_adr() & 0x0000ffff) | (data << 16)); // high
}
WRITE16_MEMBER(k573dio_device::mpeg_end_adr_low_w)
{
logerror("FPGA MPEG end address low %04x\n", data);
k573fpga->set_mp3_end_adr((k573fpga->get_mp3_end_adr() & 0xffff0000) | data); // low
}
WRITE16_MEMBER(k573dio_device::mpeg_key_1_w)
{
logerror("FPGA MPEG key 1/3 %04x\n", data);
k573fpga->set_crypto_key1(data);
}
READ16_MEMBER(k573dio_device::mas_i2c_r)
{
return (mas3507d->i2c_scl_r() << 13) | (mas3507d->i2c_sda_r() << 12);
return k573fpga->i2c_read();
}
WRITE16_MEMBER(k573dio_device::mas_i2c_w)
{
mas3507d->i2c_scl_w(data & 0x2000);
mas3507d->i2c_sda_w(data & 0x1000);
k573fpga->i2c_write(data);
}
READ16_MEMBER(k573dio_device::mpeg_ctrl_r)
{
return k573fpga->get_mpeg_ctrl();
}
WRITE16_MEMBER(k573dio_device::mpeg_ctrl_w)
{
logerror("FPGA MPEG control %c%c%c\n",
data & 0x8000 ? '#' : '.',
data & 0x4000 ? '#' : '.',
data & 0x2000 ? '#' : '.');
k573fpga->set_mpeg_ctrl(data);
}
WRITE16_MEMBER(k573dio_device::ram_write_adr_high_w)
@ -236,8 +277,8 @@ WRITE16_MEMBER(k573dio_device::ram_write_adr_low_w)
READ16_MEMBER(k573dio_device::ram_r)
{
uint16_t res = ram[ram_adr >> 1];
ram_adr += 2;
uint16_t res = ram[ram_read_adr >> 1];
ram_read_adr += 2;
return res;
}
@ -250,13 +291,37 @@ WRITE16_MEMBER(k573dio_device::ram_w)
WRITE16_MEMBER(k573dio_device::ram_read_adr_high_w)
{
// read and write address are shared
ram_adr = (ram_adr & 0x0000ffff) | (data << 16);
ram_read_adr = (ram_read_adr & 0x0000ffff) | (data << 16);
}
WRITE16_MEMBER(k573dio_device::ram_read_adr_low_w)
{
// read and write address are shared
ram_adr = (ram_adr & 0xffff0000) | data;
ram_read_adr = (ram_read_adr & 0xffff0000) | data;
}
READ16_MEMBER(k573dio_device::mp3_playback_high_r)
{
//logerror("mp3_playback_high_r: %08x (%08x)\n", mp3_playback & 0xffff0000, mp3_playback);
return (k573fpga->get_mp3_playback() & 0xffff0000) >> 16;
}
WRITE16_MEMBER(k573dio_device::mp3_playback_high_w)
{
//mp3_playback = (mp3_playback & 0x0000ffff) | (data << 16);
//logerror("mp3_playback_low_w: %08x\n", mp3_playback);
}
READ16_MEMBER(k573dio_device::mp3_playback_low_r)
{
//logerror("mp3_playback_low_r: %08x (%08x) %08x %08x\n", mp3_playback & 0x0000ffff, mp3_playback, m_samples->get_position(0), m_samples->get_position(1));
return k573fpga->get_mp3_playback() & 0x0000ffff;
}
WRITE16_MEMBER(k573dio_device::mp3_playback_low_w)
{
//mp3_playback = (mp3_playback & 0xffff0000) | data;
//logerror("mp3_playback_low_w: %08x\n", mp3_playback & 0x0000ffff);
}
WRITE16_MEMBER(k573dio_device::output_1_w)
@ -282,11 +347,13 @@ WRITE16_MEMBER(k573dio_device::output_7_w)
WRITE16_MEMBER(k573dio_device::mpeg_key_2_w)
{
logerror("FPGA MPEG key 2/3 %04x\n", data);
k573fpga->set_crypto_key2(data);
}
WRITE16_MEMBER(k573dio_device::mpeg_key_3_w)
{
logerror("FPGA MPEG key 3/3 %04x\n", data);
k573fpga->set_crypto_key3(data);
}
READ16_MEMBER(k573dio_device::digital_id_r)
@ -301,7 +368,7 @@ WRITE16_MEMBER(k573dio_device::digital_id_w)
READ16_MEMBER(k573dio_device::fpga_status_r)
{
logerror("%s: fpga_status_r (%s)\n", tag(), machine().describe_context());
//logerror("%s: fpga_status_r (%s)\n", tag(), machine().describe_context());
// fpga/digital board status checks
// wants & c000 = 8000 (just after program upload?)
@ -335,6 +402,11 @@ WRITE16_MEMBER(k573dio_device::output_2_w)
output(2, data);
}
READ16_MEMBER(k573dio_device::mp3_unk_r)
{
return 0;
}
void k573dio_device::output(int offset, uint16_t data)
{
data = (data >> 12) & 0x0f;

View File

@ -5,10 +5,9 @@
#pragma once
#include "sound/mas3507d.h"
#include "machine/k573fpga.h"
#include "machine/ds2401.h"
class k573dio_device : public device_t
{
public:
@ -16,17 +15,24 @@ public:
auto output_callback() { return output_cb.bind(); }
required_device<mas3507d_device> mas3507d;
required_device<k573fpga_device> k573fpga;
required_device<ds2401_device> digital_id;
void amap(address_map &map);
void set_fake_fpga(bool flag) { is_fake_fpga = flag; }
void set_buffer_speed(uint32_t speed) { buffer_speed = speed; }
void set_mp3_dynamic_base(uint32_t base) { mp3_dynamic_base = base; }
DECLARE_READ16_MEMBER(a00_r);
DECLARE_READ16_MEMBER(a02_r);
DECLARE_READ16_MEMBER(a04_r);
DECLARE_READ16_MEMBER(a06_r);
DECLARE_READ16_MEMBER(a0a_r);
DECLARE_WRITE16_MEMBER(a10_w);
DECLARE_READ16_MEMBER(a80_r);
DECLARE_READ16_MEMBER(aa8_r);
DECLARE_READ16_MEMBER(ac4_r);
DECLARE_WRITE16_MEMBER(mpeg_start_adr_high_w);
DECLARE_WRITE16_MEMBER(mpeg_start_adr_low_w);
DECLARE_WRITE16_MEMBER(mpeg_end_adr_high_w);
@ -34,6 +40,7 @@ public:
DECLARE_WRITE16_MEMBER(mpeg_key_1_w);
DECLARE_READ16_MEMBER(mas_i2c_r);
DECLARE_WRITE16_MEMBER(mas_i2c_w);
DECLARE_READ16_MEMBER(mpeg_ctrl_r);
DECLARE_WRITE16_MEMBER(mpeg_ctrl_w);
DECLARE_WRITE16_MEMBER(ram_write_adr_high_w);
DECLARE_WRITE16_MEMBER(ram_write_adr_low_w);
@ -41,6 +48,10 @@ public:
DECLARE_WRITE16_MEMBER(ram_w);
DECLARE_WRITE16_MEMBER(ram_read_adr_high_w);
DECLARE_WRITE16_MEMBER(ram_read_adr_low_w);
DECLARE_READ16_MEMBER(mp3_playback_high_r);
DECLARE_WRITE16_MEMBER(mp3_playback_high_w);
DECLARE_READ16_MEMBER(mp3_playback_low_r);
DECLARE_WRITE16_MEMBER(mp3_playback_low_w);
DECLARE_WRITE16_MEMBER(output_0_w);
DECLARE_WRITE16_MEMBER(output_1_w);
DECLARE_WRITE16_MEMBER(output_7_w);
@ -54,6 +65,7 @@ public:
DECLARE_WRITE16_MEMBER(output_4_w);
DECLARE_WRITE16_MEMBER(output_2_w);
DECLARE_WRITE16_MEMBER(output_5_w);
DECLARE_READ16_MEMBER(mp3_unk_r);
protected:
virtual void device_start() override;
@ -66,10 +78,13 @@ private:
devcb_write8 output_cb;
std::unique_ptr<uint16_t[]> ram;
uint32_t ram_adr;
uint32_t ram_adr, ram_read_adr;
uint8_t output_data[8];
void output(int offset, uint16_t data);
bool is_fake_fpga;
uint32_t buffer_speed, mp3_dynamic_base;
};
DECLARE_DEVICE_TYPE(KONAMI_573_DIGITAL_IO_BOARD, k573dio_device)

120
src/mame/machine/k573enc.h Normal file
View File

@ -0,0 +1,120 @@
// license:BSD-3-Clause
// copyright-holders:windyfairy
#ifndef MAME_MACHINE_K573ENC_H
#define MAME_MACHINE_K573ENC_H
#pragma once
#include <map>
std::unordered_map<uint16_t, uint32_t> k573enc_lookup({
{ 0x031a, 0 },
{ 0x038e, 1 },
{ 0x0aa7, 2 },
{ 0x0c51, 3 },
{ 0x0e34, 4 },
{ 0x14d9, 5 },
{ 0x154d, 6 },
{ 0x18a0, 7 },
{ 0x18da, 8 },
{ 0x1c67, 9 },
{ 0x20dc, 10 },
{ 0x21d6, 11 },
{ 0x2698, 12 },
{ 0x28b6, 13 },
{ 0x2a5f, 14 },
{ 0x2c7c, 15 },
{ 0x3009, 16 },
{ 0x3395, 17 },
{ 0x3505, 18 },
{ 0x3722, 19 },
{ 0x38cb, 20 },
{ 0x3aaf, 21 },
{ 0x3e3b, 22 },
{ 0x41c8, 23 },
{ 0x4371, 24 },
{ 0x451b, 25 },
{ 0x4555, 26 },
{ 0x46c4, 27 },
{ 0x48e1, 28 },
{ 0x4c6e, 29 },
{ 0x4fc0, 30 },
{ 0x4ffa, 31 },
{ 0x5387, 32 },
{ 0x5530, 33 },
{ 0x56b4, 34 },
{ 0x5714, 35 },
{ 0x5c10, 36 },
{ 0x5c83, 37 },
{ 0x5dcd, 38 },
{ 0x5fd6, 39 },
{ 0x6010, 40 },
{ 0x617f, 41 },
{ 0x6546, 42 },
{ 0x6729, 43 },
{ 0x685f, 44 },
{ 0x6dcf, 45 },
{ 0x6e43, 46 },
{ 0x6fec, 47 },
{ 0x7195, 48 },
{ 0x755c, 49 },
{ 0x78e8, 50 },
{ 0x7c3b, 51 }
});
uint8_t k573enc_keys[52][16] = {
{ 0x14, 0x26, 0x28, 0x4c, 0x50, 0x98, 0xa0, 0x31, 0x41, 0x62, 0x82, 0xc4, 0x05, 0x89, 0x0a, 0x13 },
{ 0x12, 0x36, 0x24, 0x6c, 0x48, 0xd8, 0x90, 0xb1, 0x21, 0x63, 0x42, 0xc6, 0x84, 0x8d, 0x09, 0x1b },
{ 0x03, 0x7a, 0x06, 0xf4, 0x0c, 0xe9, 0x18, 0xd3, 0x30, 0xa7, 0x60, 0x4f, 0xc0, 0x9e, 0x81, 0x3d },
{ 0x2d, 0x40, 0x5a, 0x80, 0xb4, 0x01, 0x69, 0x02, 0xd2, 0x04, 0xa5, 0x08, 0x4b, 0x10, 0x96, 0x20 },
{ 0x26, 0x68, 0x4c, 0xd0, 0x98, 0xa1, 0x31, 0x43, 0x62, 0x86, 0xc4, 0x0d, 0x89, 0x1a, 0x13, 0x34 },
{ 0x6d, 0x14, 0xda, 0x28, 0xb5, 0x50, 0x6b, 0xa0, 0xd6, 0x41, 0xad, 0x82, 0x5b, 0x05, 0xb6, 0x0a },
{ 0x7b, 0x04, 0xf6, 0x08, 0xed, 0x10, 0xdb, 0x20, 0xb7, 0x40, 0x6f, 0x80, 0xde, 0x01, 0xbd, 0x02 },
{ 0x40, 0x58, 0x80, 0xb0, 0x01, 0x61, 0x02, 0xc2, 0x04, 0x85, 0x08, 0x0b, 0x10, 0x16, 0x20, 0x2c },
{ 0x4c, 0x56, 0x98, 0xac, 0x31, 0x59, 0x62, 0xb2, 0xc4, 0x65, 0x89, 0xca, 0x13, 0x95, 0x26, 0x2b },
{ 0x6b, 0x4a, 0xd6, 0x94, 0xad, 0x29, 0x5b, 0x52, 0xb6, 0xa4, 0x6d, 0x49, 0xda, 0x92, 0xb5, 0x25 },
{ 0x0e, 0x94, 0x1c, 0x29, 0x38, 0x52, 0x70, 0xa4, 0xe0, 0x49, 0xc1, 0x92, 0x83, 0x25, 0x07, 0x4a },
{ 0x1e, 0x92, 0x3c, 0x25, 0x78, 0x4a, 0xf0, 0x94, 0xe1, 0x29, 0xc3, 0x52, 0x87, 0xa4, 0x0f, 0x49 },
{ 0x24, 0xb4, 0x48, 0x69, 0x90, 0xd2, 0x21, 0xa5, 0x42, 0x4b, 0x84, 0x96, 0x09, 0x2d, 0x12, 0x5a },
{ 0x06, 0xda, 0x0c, 0xb5, 0x18, 0x6b, 0x30, 0xd6, 0x60, 0xad, 0xc0, 0x5b, 0x81, 0xb6, 0x03, 0x6d },
{ 0x0f, 0xe6, 0x1e, 0xcd, 0x3c, 0x9b, 0x78, 0x37, 0xf0, 0x6e, 0xe1, 0xdc, 0xc3, 0xb9, 0x87, 0x73 },
{ 0x2e, 0xcc, 0x5c, 0x99, 0xb8, 0x33, 0x71, 0x66, 0xe2, 0xcc, 0xc5, 0x99, 0x8b, 0x33, 0x17, 0x66 },
{ 0x41, 0x84, 0x82, 0x09, 0x05, 0x12, 0x0a, 0x24, 0x14, 0x48, 0x28, 0x90, 0x50, 0x21, 0xa0, 0x42 },
{ 0x57, 0xb0, 0xae, 0x61, 0x5d, 0xc2, 0xba, 0x85, 0x75, 0x0b, 0xea, 0x16, 0xd5, 0x2c, 0xab, 0x58 },
{ 0x73, 0x80, 0xe6, 0x01, 0xcd, 0x02, 0x9b, 0x04, 0x37, 0x08, 0x6e, 0x10, 0xdc, 0x20, 0xb9, 0x40 },
{ 0x70, 0xaa, 0xe0, 0x55, 0xc1, 0xaa, 0x83, 0x55, 0x07, 0xaa, 0x0e, 0x55, 0x1c, 0xaa, 0x38, 0x55 },
{ 0x49, 0xd6, 0x92, 0xad, 0x25, 0x5b, 0x4a, 0xb6, 0x94, 0x6d, 0x29, 0xda, 0x52, 0xb5, 0xa4, 0x6b },
{ 0x43, 0xfe, 0x86, 0xfd, 0x0d, 0xfb, 0x1a, 0xf7, 0x34, 0xef, 0x68, 0xdf, 0xd0, 0xbf, 0xa1, 0x7f },
{ 0x65, 0xee, 0xca, 0xdd, 0x95, 0xbb, 0x2b, 0x77, 0x56, 0xee, 0xac, 0xdd, 0x59, 0xbb, 0xb2, 0x77 },
{ 0x98, 0x14, 0x31, 0x28, 0x62, 0x50, 0xc4, 0xa0, 0x89, 0x41, 0x13, 0x82, 0x26, 0x05, 0x4c, 0x0a },
{ 0x9d, 0x28, 0x3b, 0x50, 0x76, 0xa0, 0xec, 0x41, 0xd9, 0x82, 0xb3, 0x05, 0x67, 0x0a, 0xce, 0x14 },
{ 0xb5, 0x06, 0x6b, 0x0c, 0xd6, 0x18, 0xad, 0x30, 0x5b, 0x60, 0xb6, 0xc0, 0x6d, 0x81, 0xda, 0x03 },
{ 0xbf, 0x00, 0x7f, 0x00, 0xfe, 0x00, 0xfd, 0x00, 0xfb, 0x00, 0xf7, 0x00, 0xef, 0x00, 0xdf, 0x00 },
{ 0xaa, 0x30, 0x55, 0x60, 0xaa, 0xc0, 0x55, 0x81, 0xaa, 0x03, 0x55, 0x06, 0xaa, 0x0c, 0x55, 0x18 },
{ 0x89, 0x58, 0x13, 0xb0, 0x26, 0x61, 0x4c, 0xc2, 0x98, 0x85, 0x31, 0x0b, 0x62, 0x16, 0xc4, 0x2c },
{ 0xaa, 0x4e, 0x55, 0x9c, 0xaa, 0x39, 0x55, 0x72, 0xaa, 0xe4, 0x55, 0xc9, 0xaa, 0x93, 0x55, 0x27 },
{ 0xb8, 0x70, 0x71, 0xe0, 0xe2, 0xc1, 0xc5, 0x83, 0x8b, 0x07, 0x17, 0x0e, 0x2e, 0x1c, 0x5c, 0x38 },
{ 0xbc, 0x7e, 0x79, 0xfc, 0xf2, 0xf9, 0xe5, 0xf3, 0xcb, 0xe7, 0x97, 0xcf, 0x2f, 0x9f, 0x5e, 0x3f },
{ 0xd3, 0x32, 0xa7, 0x64, 0x4f, 0xc8, 0x9e, 0x91, 0x3d, 0x23, 0x7a, 0x46, 0xf4, 0x8c, 0xe9, 0x19 },
{ 0xf4, 0x08, 0xe9, 0x10, 0xd3, 0x20, 0xa7, 0x40, 0x4f, 0x80, 0x9e, 0x01, 0x3d, 0x02, 0x7a, 0x04 },
{ 0xe6, 0x38, 0xcd, 0x70, 0x9b, 0xe0, 0x37, 0xc1, 0x6e, 0x83, 0xdc, 0x07, 0xb9, 0x0e, 0x73, 0x1c },
{ 0xf6, 0x20, 0xed, 0x40, 0xdb, 0x80, 0xb7, 0x01, 0x6f, 0x02, 0xde, 0x04, 0xbd, 0x08, 0x7b, 0x10 },
{ 0xe4, 0x40, 0xc9, 0x80, 0x93, 0x01, 0x27, 0x02, 0x4e, 0x04, 0x9c, 0x08, 0x39, 0x10, 0x72, 0x20 },
{ 0xe1, 0x52, 0xc3, 0xa4, 0x87, 0x49, 0x0f, 0x92, 0x1e, 0x25, 0x3c, 0x4a, 0x78, 0x94, 0xf0, 0x29 },
{ 0xfb, 0x54, 0xf7, 0xa8, 0xef, 0x51, 0xdf, 0xa2, 0xbf, 0x45, 0x7f, 0x8a, 0xfe, 0x15, 0xfd, 0x2a },
{ 0xfe, 0x72, 0xfd, 0xe4, 0xfb, 0xc9, 0xf7, 0x93, 0xef, 0x27, 0xdf, 0x4e, 0xbf, 0x9c, 0x7f, 0x39 },
{ 0x84, 0x80, 0x09, 0x01, 0x12, 0x02, 0x24, 0x04, 0x48, 0x08, 0x90, 0x10, 0x21, 0x20, 0x42, 0x40 },
{ 0x9f, 0x8e, 0x3f, 0x1d, 0x7e, 0x3a, 0xfc, 0x74, 0xf9, 0xe8, 0xf3, 0xd1, 0xe7, 0xa3, 0xcf, 0x47 },
{ 0xba, 0x82, 0x75, 0x05, 0xea, 0x0a, 0xd5, 0x14, 0xab, 0x28, 0x57, 0x50, 0xae, 0xa0, 0x5d, 0x41 },
{ 0xb1, 0xac, 0x63, 0x59, 0xc6, 0xb2, 0x8d, 0x65, 0x1b, 0xca, 0x36, 0x95, 0x6c, 0x2b, 0xd8, 0x56 },
{ 0x8f, 0xc6, 0x1f, 0x8d, 0x3e, 0x1b, 0x7c, 0x36, 0xf8, 0x6c, 0xf1, 0xd8, 0xe3, 0xb1, 0xc7, 0x63 },
{ 0xbb, 0xd6, 0x77, 0xad, 0xee, 0x5b, 0xdd, 0xb6, 0xbb, 0x6d, 0x77, 0xda, 0xee, 0xb5, 0xdd, 0x6b },
{ 0xa9, 0xe2, 0x53, 0xc5, 0xa6, 0x8b, 0x4d, 0x17, 0x9a, 0x2e, 0x35, 0x5c, 0x6a, 0xb8, 0xd4, 0x71 },
{ 0xba, 0xfc, 0x75, 0xf9, 0xea, 0xf3, 0xd5, 0xe7, 0xab, 0xcf, 0x57, 0x9f, 0xae, 0x3f, 0x5d, 0x7e },
{ 0xd7, 0x90, 0xaf, 0x21, 0x5f, 0x42, 0xbe, 0x84, 0x7d, 0x09, 0xfa, 0x12, 0xf5, 0x24, 0xeb, 0x48 },
{ 0xfe, 0x84, 0xfd, 0x09, 0xfb, 0x12, 0xf7, 0x24, 0xef, 0x48, 0xdf, 0x90, 0xbf, 0x21, 0x7f, 0x42 },
{ 0xc8, 0xdc, 0x91, 0xb9, 0x23, 0x73, 0x46, 0xe6, 0x8c, 0xcd, 0x19, 0x9b, 0x32, 0x37, 0x64, 0x6e },
{ 0xe5, 0xce, 0xcb, 0x9d, 0x97, 0x3b, 0x2f, 0x76, 0x5e, 0xec, 0xbc, 0xd9, 0x79, 0xb3, 0xf2, 0x67 }
};
#endif // MAME_MACHINE_K573ENC_H

View File

@ -0,0 +1,566 @@
// license:BSD-3-Clause
// copyright-holders:windyfairy
#include "emu.h"
#include "speaker.h"
#include "k573fpga.h"
#include "k573enc.h"
// TODO: Check if this is optimal
#define MINIMUM_BUFFER 1
// TODO: Is this really needed? Confirm
#define MAX_FRAME_SYNC_MATCHES 1
#define MINIMP3_ONLY_MP3
#define MINIMP3_NO_STDIO
#define MINIMP3_IMPLEMENTATION
#include "minimp3/minimp3.h"
#include "minimp3/minimp3_ex.h"
k573fpga_device::k573fpga_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
device_t(mconfig, KONAMI_573_DIGITAL_FPGA, tag, owner, clock),
mas3507d(*this, "mpeg"),
m_samples(*this, "samples"),
use_fake_fpga(false)
{
}
void k573fpga_device::device_start()
{
ram_swap = std::make_unique<uint16_t[]>(32 * 1024 * 1024);
save_pointer(NAME(ram_swap), 32 * 1024 * 1024 );
device_reset();
}
void k573fpga_device::device_reset()
{
mp3_start_adr = 0;
mp3_end_adr = 0;
crypto_key1 = 0;
crypto_key2 = 0;
crypto_key3 = 0;
mp3_last_frame = 0;
mp3_last_adr = 0;
mp3_last_decrypt_adr = 0;
mp3_next_sync = 0;
last_copied_samples = 0;
decrypt_finished = false;
if (!channel_l_pcm) {
free(channel_l_pcm);
}
channel_l_pcm = nullptr;
last_buffer_size_channel_l = 0;
if (!channel_r_pcm) {
free(channel_r_pcm);
}
channel_r_pcm = nullptr;
last_buffer_size_channel_r = 0;
memset((void*)ram_swap.get(), 0, 12 * 1024 * 1024 * 2);
memset(&mp3_info, 0, sizeof(mp3_info));
mas3507d->set_samples(m_samples);
last_position_update = 0;
position_diff = 0;
}
static const char *const k573fpga_sample_names[] =
{
"*k573fpga",
"audio",
nullptr
};
void k573fpga_device::device_add_mconfig(machine_config &config)
{
MAS3507D(config, "mpeg");
/* sound hardware */
SPEAKER(config, "lspeaker").front_left();
SPEAKER(config, "rspeaker").front_right();
SAMPLES(config, m_samples);
m_samples->set_channels(2);
m_samples->set_samples_names(k573fpga_sample_names);
m_samples->set_samples_update_callback(FUNC(k573fpga_device::k573fpga_stream_update));
m_samples->add_route(0, "lspeaker", 1.0);
m_samples->add_route(1, "rspeaker", 1.0);
}
uint32_t k573fpga_device::get_mp3_playback() {
if (mp3_start_adr == 0) {
if (m_samples->get_position(0) == 0 && decrypt_finished) {
// The game is still requesting position updates after the song has ended.
// This happens in some games like DDR 4th Mix Plus where it uses
// the position in song for actual game engine timings.
// The counter will properly end when the game signals to the FPGA to stop playback.
last_position_update += position_diff;
} else {
position_diff = m_samples->get_position(0) - last_position_update;
last_position_update = m_samples->get_position(0);
}
last_position_update = m_samples->get_position(0);
return last_position_update;
}
return 0;
}
uint16_t k573fpga_device::i2c_read()
{
int scl = mas3507d->i2c_scl_r() << 13;
int sda = mas3507d->i2c_sda_r() << 12;
return scl | sda;
}
void k573fpga_device::i2c_write(uint16_t data)
{
mas3507d->i2c_scl_w(data & 0x2000);
mas3507d->i2c_sda_w(data & 0x1000);
}
uint16_t k573fpga_device::get_mpeg_ctrl()
{
if (mpeg_ctrl_flag == 0xe000 || (m_samples->get_position(0) > 0 && mp3_decrypt_mode)) {
// This has been tested with real hardware, but this flag is always held 0x1000 when the audio is being played
return mpeg_ctrl_flag | 0x1000;
}
return mpeg_ctrl_flag;
}
void k573fpga_device::set_mpeg_ctrl(uint16_t data)
{
logerror("FPGA MPEG control %c%c%c | %08x %08x\n",
data & 0x8000 ? '#' : '.',
data & 0x4000 ? '#' : '.',
data & 0x2000 ? '#' : '.',
mp3_start_adr, mp3_end_adr);
mpeg_ctrl_flag = data;
if (data == 0xa000) {
// Stop playback
m_samples->stop(0);
m_samples->stop(1);
}
if ((data & 0xa000) == 0xa000) {
// Reset playback state for start and stop events
mp3_last_frame = 0;
mp3_next_sync = 0;
mp3_last_adr = mp3_start_adr;
mp3_last_decrypt_adr = mp3_start_adr;
last_copied_samples = 0;
last_buffer_size_channel_l = 0;
last_buffer_size_channel_r = 0;
last_position_update = 0;
position_diff = 0;
decrypt_finished = false;
if (channel_l_pcm) {
free(channel_l_pcm);
channel_l_pcm = nullptr;
}
if (channel_r_pcm) {
free(channel_r_pcm);
channel_r_pcm = nullptr;
}
}
if ((data & 0xe000) == 0xe000) {
mp3_decrypt_mode = true;
} else {
mp3_decrypt_mode = false;
}
}
int32_t k573fpga_device::find_enc_key()
{
if (k573enc_lookup.find(crypto_key1) == k573enc_lookup.end()) {
return -1;
}
return k573enc_lookup[crypto_key1];
}
inline uint16_t k573fpga_device::fpga_decrypt_byte_real(uint16_t v)
{
uint16_t m = crypto_key1 ^ crypto_key2;
v = bitswap<16>(
v,
15 - BIT(m, 0xF),
14 + BIT(m, 0xF),
13 - BIT(m, 0xE),
12 + BIT(m, 0xE),
11 - BIT(m, 0xB),
10 + BIT(m, 0xB),
9 - BIT(m, 0x9),
8 + BIT(m, 0x9),
7 - BIT(m, 0x8),
6 + BIT(m, 0x8),
5 - BIT(m, 0x5),
4 + BIT(m, 0x5),
3 - BIT(m, 0x3),
2 + BIT(m, 0x3),
1 - BIT(m, 0x2),
0 + BIT(m, 0x2)
);
v ^= (BIT(m, 0xD) << 14) ^
(BIT(m, 0xC) << 12) ^
(BIT(m, 0xA) << 10) ^
(BIT(m, 0x7) << 8) ^
(BIT(m, 0x6) << 6) ^
(BIT(m, 0x4) << 4) ^
(BIT(m, 0x1) << 2) ^
(BIT(m, 0x0) << 0);
v ^= bitswap<16>(
(uint16_t)crypto_key3,
7, 0, 6, 1,
5, 2, 4, 3,
3, 4, 2, 5,
1, 6, 0, 7
);
return (v >> 8) | ((v & 0xff) << 8);
}
inline uint16_t k573fpga_device::fpga_decrypt_byte_fake(uint16_t data, uint32_t crypto_idx)
{
// Not the real algorithm. Only used for DDR Solo Bass Mix (ddrsbm)
int32_t crypto_enc_idx = find_enc_key();
if (crypto_enc_idx == -1) {
return data;
}
uint8_t *key = k573enc_keys[crypto_enc_idx];
uint16_t output_word = 0;
for (int cur_bit = 0; cur_bit < 8; cur_bit++) {
int even_bit_shift = (cur_bit * 2) & 0xff;
int odd_bit_shift = (cur_bit * 2 + 1) & 0xff;
int is_even_bit_set = (data & (1 << even_bit_shift)) != 0;
int is_odd_bit_set = (data & (1 << odd_bit_shift)) != 0;
int is_key_bit_set = (key[crypto_idx % 16] & (1 << cur_bit)) != 0;
int is_scramble_bit_set = (key[(crypto_idx - 1) % 16] & (1 << cur_bit)) != 0;
if (is_scramble_bit_set == 1) {
int t = is_even_bit_set;
is_even_bit_set = is_odd_bit_set;
is_odd_bit_set = t;
}
if (((is_even_bit_set ^ is_key_bit_set)) == 1) {
output_word |= 1 << even_bit_shift;
}
if ((is_odd_bit_set) == 1) {
output_word |= 1 << odd_bit_shift;
}
}
return (output_word >> 8) | ((output_word & 0xff) << 8);
}
SAMPLES_UPDATE_CB_MEMBER(k573fpga_device::k573fpga_stream_update)
{
if ((mpeg_ctrl_flag & 0xe000) != 0xe000 || mp3_last_adr >= mp3_end_adr || (m_samples->get_position(0) < mp3_next_sync && mp3_next_sync != 0)) {
return;
}
int new_samples = 0;
if (!use_fake_fpga && mp3_start_adr != mp3_dynamic_base) {
// Base addresses are kind of a "hack" and should be dealt with eventually.
// Files that are loaded at he base address are dynamic, meaning the data constantly changes.
// Data will not get loaded outside of the base address region past the boot sequence, however.
// As such, it's possible to decrypt and play the full file if it's not at the designated base address.
// Only 4 games seem to use a base address that is non-0: GF11DM10 (0x00540000) and GF10DM9 (0x00500000).
// This way of doing things seems kind of arbitrary, so it most likely doesn't need to be handled in a
// specific case like this, but this leads to much better performance overall since the data is usually
// small (<1mb).
// Another interesting thing about base addresses is that non-base address files are almost always looped
// in-game, with the exception of the Konami logo sound. Without looping non-base address sounds, things
// like system BGMs and song wheel previews will stop looping.
crypto_key1 = orig_crypto_key1;
crypto_key2 = orig_crypto_key2;
crypto_key3 = orig_crypto_key3;
for (int32_t cur_idx = mp3_start_adr; cur_idx < mp3_end_adr; cur_idx += 2) {
if (use_fake_fpga) {
ram_swap[cur_idx >> 1] = fpga_decrypt_byte_fake(ram[cur_idx >> 1], (cur_idx - mp3_start_adr) / 2);
} else {
ram_swap[cur_idx >> 1] = fpga_decrypt_byte_real(ram[cur_idx >> 1]);
}
if (!use_fake_fpga) {
crypto_key1 = (crypto_key1 & 0x8000) | ((crypto_key1 << 1) & 0x7FFE) | ((crypto_key1 >> 14) & 1);
if ((((crypto_key1 >> 15) ^ crypto_key1) & 1) != 0) {
crypto_key2 = (crypto_key2 << 1) | (crypto_key2 >> 15);
}
}
crypto_key3++;
}
// Decrypt entire block of memory
if (mp3_info.buffer != NULL) {
free(mp3_info.buffer);
}
mp3dec_load_buf(&mp3_dec, ((uint8_t*)ram_swap.get()) + mp3_start_adr, mp3_end_adr - mp3_start_adr, &mp3_info, NULL, NULL);
new_samples = mp3_info.samples;
last_copied_samples = 0;
mp3_last_adr += mp3_end_adr - mp3_start_adr;
} else {
// For base address files, decrypt in a streaming manner.
// By the time the play flag is set, there's usually enough data to decode the first frame or two.
// Some files (specifcially GFDM's audio) has a large 0x600 byte ID3 header, so in order to guarantee that
// the first frame is found, I've found it best to decrypt 0x2000 bytes in the first go and then a smaller
// amount after that for every subsequent update.
new_samples = mp3_info.samples;
int decrypt_buffer = MINIMUM_BUFFER;
int decrypt_buffer_speed = buffer_speed;
if (mp3_next_sync == 0) {
crypto_key1 = orig_crypto_key1;
crypto_key2 = orig_crypto_key2;
crypto_key3 = orig_crypto_key3;
mp3_last_decrypt_adr = mp3_start_adr;
// Cover a large chunk of ID3 or junk at the beginning of a file when decrypting first frame
decrypt_buffer = 1;
decrypt_buffer_speed = 0x2000;
last_position_update = 0;
position_diff = 0;
decrypt_finished = false;
}
// Decrypt chunk of data
if (mp3_last_decrypt_adr < mp3_end_adr && mp3_last_adr + decrypt_buffer_speed * decrypt_buffer >= mp3_last_decrypt_adr) {
int32_t cur_idx;
for (cur_idx = mp3_last_decrypt_adr; cur_idx - mp3_last_decrypt_adr < decrypt_buffer_speed * decrypt_buffer && cur_idx < mp3_end_adr; cur_idx += 2) {
if (use_fake_fpga) {
ram_swap[cur_idx >> 1] = fpga_decrypt_byte_fake(ram[cur_idx >> 1], (cur_idx - mp3_start_adr) / 2);
} else {
ram_swap[cur_idx >> 1] = fpga_decrypt_byte_real(ram[cur_idx >> 1]);
}
if (!use_fake_fpga) {
crypto_key1 = (crypto_key1 & 0x8000) | ((crypto_key1 << 1) & 0x7FFE) | ((crypto_key1 >> 14) & 1);
if ((((crypto_key1 >> 15) ^ crypto_key1) & 1) != 0) {
crypto_key2 = (crypto_key2 << 1) | (crypto_key2 >> 15);
}
}
crypto_key3++;
}
mp3_last_decrypt_adr = cur_idx;
}
int samples;
int free_format_bytes = 0, frame_size = 0;
int buf_size = decrypt_buffer_speed * decrypt_buffer;
uint8_t *buf = (uint8_t*)ram_swap.get() + mp3_last_adr;
// Detect first frame in MP3 data
if (mp3_next_sync == 0) {
buf_size = 0x2000;
// Everything on the System 573 has a header 0x600 or less from what I've seen, but just ot be sure, check the first 0x2000 bytes
uint32_t first_frame = mp3d_find_frame(buf, buf_size, &free_format_bytes, &frame_size);
buf += first_frame;
mp3_last_adr += first_frame;
buf_size -= first_frame;
}
// Skip x amount of samples previously read (this fixes some weird glitches)
for (int frame_skip = 0; frame_skip < mp3_last_frame && buf < (uint8_t*)ram_swap.get() + mp3_end_adr; frame_skip++) {
mp3d_find_frame(buf, buf_size, &free_format_bytes, &frame_size);
buf += frame_size;
mp3_last_adr += frame_size;
}
mp3_last_frame = 0;
if (mp3_next_sync == 0) {
// For the first iteration, we need to set up the MP3 state and such
if (mp3_info.buffer != NULL) {
free(mp3_info.buffer);
}
memset(&mp3_info, 0, sizeof(mp3_info));
memset(&mp3_frame_info, 0, sizeof(mp3_frame_info));
new_samples = 0;
/* try to make allocation size assumption by first frame */
mp3dec_init(&mp3_dec);
samples = mp3dec_decode_frame(&mp3_dec, buf, buf_size, mp3_pcm, &mp3_frame_info);
if (!samples) {
return;
}
samples *= mp3_frame_info.channels;
mp3_allocated = (buf_size / mp3_frame_info.frame_bytes) * samples * sizeof(mp3d_sample_t) + MINIMP3_MAX_SAMPLES_PER_FRAME * sizeof(mp3d_sample_t);
mp3_info.buffer = (mp3d_sample_t*)malloc(mp3_allocated);
if (!mp3_info.buffer) {
return;
}
mp3_info.samples = samples;
memcpy(mp3_info.buffer, mp3_pcm, mp3_info.samples * sizeof(mp3d_sample_t));
/* save info */
mp3_info.channels = mp3_frame_info.channels;
mp3_info.hz = mp3_frame_info.hz;
mp3_info.layer = mp3_frame_info.layer;
mp3_last_frame = 0;
decrypt_buffer = 8;
}
/* decode rest frames */
buf_size = decrypt_buffer_speed * 2;
for (int frame_skip = 0; frame_skip < decrypt_buffer && buf < (uint8_t*)ram_swap.get() + mp3_end_adr; frame_skip++) {
if (buf + buf_size > (uint8_t*)ram_swap.get() + mp3_end_adr) {
buf_size = ((uint8_t*)ram_swap.get() + mp3_end_adr) - buf;
}
if ((mp3_allocated - mp3_info.samples * sizeof(mp3d_sample_t)) < MINIMP3_MAX_SAMPLES_PER_FRAME * sizeof(mp3d_sample_t)) {
mp3_allocated *= 2;
mp3_info.buffer = (mp3d_sample_t*)realloc(mp3_info.buffer, mp3_allocated);
}
samples = mp3dec_decode_frame(&mp3_dec, buf, buf_size, mp3_info.buffer + mp3_info.samples, &mp3_frame_info);
if (buf + buf_size >= (uint8_t*)ram_swap.get() + mp3_end_adr) {
// Some songs have a weird frame at the very end of the song which can be skipped usually.
mp3_last_adr += buf_size;
break;
}
if (!samples) {
mp3_last_decrypt_adr = mp3_start_adr; // Force it to redecrypt everything in case the previous decrypt was too fast to find a full frame
crypto_key1 = orig_crypto_key1;
crypto_key2 = orig_crypto_key2;
crypto_key3 = orig_crypto_key3;
break;
}
if (mp3_info.hz != mp3_frame_info.hz || mp3_info.layer != mp3_frame_info.layer) {
break;
}
if (mp3_info.channels && mp3_info.channels != mp3_frame_info.channels) {
break;
}
buf += mp3_frame_info.frame_bytes;
mp3_info.samples += samples * mp3_frame_info.channels;
mp3_last_frame++;
}
new_samples = mp3_info.samples - new_samples;
}
if (new_samples > 0) {
size_t buffer_size = 0;
if (mp3_info.channels == 1) {
buffer_size = mp3_info.samples;
} else {
buffer_size = mp3_info.samples / 2;
}
if (channel_l_pcm == nullptr) {
last_buffer_size_channel_l = buffer_size * 2;
if (last_buffer_size_channel_l == 0) {
last_buffer_size_channel_l = 0x1000;
}
channel_l_pcm = (mp3d_sample_t*)calloc(last_buffer_size_channel_l, sizeof(mp3d_sample_t));
} else if (buffer_size > last_buffer_size_channel_l) {
// realloc
last_buffer_size_channel_l = buffer_size * 2;
if (last_buffer_size_channel_l == 0) {
last_buffer_size_channel_l = 0x1000;
}
channel_l_pcm = (mp3d_sample_t*)realloc(channel_l_pcm, last_buffer_size_channel_l * sizeof(mp3d_sample_t));
}
if (channel_r_pcm == nullptr) {
last_buffer_size_channel_r = buffer_size * 2;
if (last_buffer_size_channel_r == 0) {
last_buffer_size_channel_r = 0x1000;
}
channel_r_pcm = (mp3d_sample_t*)calloc(last_buffer_size_channel_r, sizeof(mp3d_sample_t));
} else if (buffer_size > last_buffer_size_channel_r) {
// realloc
last_buffer_size_channel_r = buffer_size * 2;
if (last_buffer_size_channel_r == 0) {
last_buffer_size_channel_r = 0x1000;
}
channel_r_pcm = (mp3d_sample_t*)realloc(channel_r_pcm, last_buffer_size_channel_r * sizeof(mp3d_sample_t));
}
if (mp3_info.channels == 1) {
memcpy(((int8_t*)channel_l_pcm) + (last_copied_samples * sizeof(mp3d_sample_t)), ((int8_t*)mp3_info.buffer) + (last_copied_samples * sizeof(mp3d_sample_t)), (buffer_size - last_copied_samples) * sizeof(mp3d_sample_t));
memcpy(((int8_t*)channel_r_pcm) + (last_copied_samples * sizeof(mp3d_sample_t)), ((int8_t*)mp3_info.buffer) + (last_copied_samples * sizeof(mp3d_sample_t)), (buffer_size - last_copied_samples) * sizeof(mp3d_sample_t));
} else {
for (size_t i = last_copied_samples; i <= buffer_size; i++) {
channel_l_pcm[i] = mp3_info.buffer[i * sizeof(mp3d_sample_t)];
channel_r_pcm[i] = mp3_info.buffer[i * sizeof(mp3d_sample_t) + 1];
}
}
last_copied_samples = buffer_size;
m_samples->update_raw(0, channel_l_pcm, buffer_size, mp3_info.hz, mp3_start_adr != mp3_dynamic_base);
m_samples->update_raw(1, channel_r_pcm, buffer_size, mp3_info.hz, mp3_start_adr != mp3_dynamic_base);
mp3_next_sync = buffer_size - (buffer_size / 3); // Grab more data sometime before the current buffer ends. This is arbitrary and kinda hacky, but it worked best between various games in my testing.
}
if (mp3_last_adr >= mp3_end_adr) {
decrypt_finished = true;
}
}
DEFINE_DEVICE_TYPE(KONAMI_573_DIGITAL_FPGA, k573fpga_device, "k573fpga", "Konami 573 Digital I/O FPGA")

View File

@ -0,0 +1,90 @@
// license:BSD-3-Clause
// copyright-holders:windyfairy
#ifndef MAME_MACHINE_K573FPGA_H
#define MAME_MACHINE_K573FPGA_H
#pragma once
#include "sound/mas3507d.h"
#include "machine/ds2401.h"
#include "sound/samples.h"
#define MINIMP3_ONLY_MP3
#define MINIMP3_NO_STDIO
#include "minimp3/minimp3.h"
#include "minimp3/minimp3_ex.h"
DECLARE_DEVICE_TYPE(KONAMI_573_DIGITAL_FPGA, k573fpga_device)
class k573fpga_device : public device_t
{
public:
k573fpga_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0);
required_device<mas3507d_device> mas3507d;
required_device<samples_device> m_samples;
void set_fake_fpga(bool flag) { use_fake_fpga = flag; }
void set_ram(uint16_t *v) { ram = v; }
void set_crypto_key1(uint16_t v) { orig_crypto_key1 = crypto_key1 = v; }
void set_crypto_key2(uint16_t v) { orig_crypto_key2 = crypto_key2 = v; }
void set_crypto_key3(uint8_t v) { orig_crypto_key3 = crypto_key3 = v; }
uint32_t get_mp3_start_adr() { return mp3_start_adr; }
void set_mp3_start_adr(uint32_t v) { mp3_start_adr = v; }
uint32_t get_mp3_end_adr() { return mp3_end_adr; }
void set_mp3_end_adr(uint32_t v) { mp3_end_adr = v; }
uint32_t get_mp3_playback();
uint16_t i2c_read();
void i2c_write(uint16_t data);
uint16_t get_mpeg_ctrl();
void set_mpeg_ctrl(uint16_t data);
void set_buffer_speed(uint32_t speed) { buffer_speed = speed; }
void set_mp3_dynamic_base(uint32_t base) { mp3_dynamic_base = base; }
protected:
virtual void device_start() override;
virtual void device_reset() override;
virtual void device_add_mconfig(machine_config &config) override;
private:
uint16_t *ram;
std::unique_ptr<uint16_t[]> ram_swap;
uint16_t crypto_key1, crypto_key2, orig_crypto_key1, orig_crypto_key2;
uint8_t crypto_key3, orig_crypto_key3;
uint32_t mp3_start_adr, mp3_end_adr, mpeg_ctrl_flag;
bool use_fake_fpga;
uint32_t mp3_last_frame, mp3_last_adr, mp3_next_sync, mp3_last_decrypt_adr;
int16_t *channel_l_pcm, *channel_r_pcm;
size_t last_buffer_size_channel_l, last_buffer_size_channel_r, last_copied_samples;
uint32_t last_position_update, position_diff;
bool mp3_decrypt_mode, decrypt_finished;
mp3d_sample_t mp3_pcm[MINIMP3_MAX_SAMPLES_PER_FRAME];
mp3dec_file_info_t mp3_info;
mp3dec_t mp3_dec;
mp3dec_frame_info_t mp3_frame_info;
size_t mp3_allocated;
uint32_t buffer_speed, mp3_dynamic_base;
int32_t find_enc_key();
uint16_t fpga_decrypt_byte_real(uint16_t data);
uint16_t fpga_decrypt_byte_fake(uint16_t data, uint32_t crypto_idx);
SAMPLES_UPDATE_CB_MEMBER(k573fpga_stream_update);
};
#endif // MAME_MACHINE_K573FPGA_H