mirror of
https://github.com/mamedev/mame.git
synced 2024-11-16 07:48:32 +01:00
-Improved accuracy of System 573's digital I/O audio emulation. (#7664)
* Refactored mas3507d and k573dio/fpga to be more accurate to real hardware. -3rdparty/minimp3: Updated to latest master and removed local changes.
This commit is contained in:
parent
9b030d87dc
commit
e6d3720985
9 changed files with 1798 additions and 443 deletions
214
3rdparty/minimp3/minimp3.h
vendored
214
3rdparty/minimp3/minimp3.h
vendored
|
@ -12,7 +12,7 @@
|
|||
|
||||
typedef struct
|
||||
{
|
||||
int frame_bytes, channels, hz, layer, bitrate_kbps;
|
||||
int frame_bytes, frame_offset, channels, hz, layer, bitrate_kbps;
|
||||
} mp3dec_frame_info_t;
|
||||
|
||||
typedef struct
|
||||
|
@ -86,7 +86,7 @@ int mp3dec_decode_frame(mp3dec_t *dec, const uint8_t *mp3, int mp3_bytes, mp3d_s
|
|||
|
||||
#if !defined(MINIMP3_NO_SIMD)
|
||||
|
||||
#if !defined(MINIMP3_ONLY_SIMD) && (defined(_M_X64) || defined(_M_ARM64) || defined(__x86_64__) || defined(__aarch64__))
|
||||
#if !defined(MINIMP3_ONLY_SIMD) && (defined(_M_X64) || defined(__x86_64__) || defined(__aarch64__) || defined(_M_ARM64))
|
||||
/* x64 always have SSE2, arm64 always have neon, no need for generic code */
|
||||
#define MINIMP3_ONLY_SIMD
|
||||
#endif /* SIMD checks... */
|
||||
|
@ -136,7 +136,7 @@ static __inline__ __attribute__((always_inline)) void minimp3_cpuid(int CPUInfo[
|
|||
#endif /* defined(__PIC__)*/
|
||||
}
|
||||
#endif /* defined(_MSC_VER) || defined(MINIMP3_ONLY_SIMD) */
|
||||
static int have_simd()
|
||||
static int have_simd(void)
|
||||
{
|
||||
#ifdef MINIMP3_ONLY_SIMD
|
||||
return 1;
|
||||
|
@ -161,8 +161,9 @@ end:
|
|||
return g_have_simd - 1;
|
||||
#endif /* MINIMP3_ONLY_SIMD */
|
||||
}
|
||||
#elif defined(__ARM_NEON) || defined(__aarch64__)
|
||||
#elif defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64)
|
||||
#include <arm_neon.h>
|
||||
#define HAVE_SSE 0
|
||||
#define HAVE_SIMD 1
|
||||
#define VSTORE vst1q_f32
|
||||
#define VLD vld1q_f32
|
||||
|
@ -180,6 +181,7 @@ static int have_simd()
|
|||
return 1;
|
||||
}
|
||||
#else /* SIMD checks... */
|
||||
#define HAVE_SSE 0
|
||||
#define HAVE_SIMD 0
|
||||
#ifdef MINIMP3_ONLY_SIMD
|
||||
#error MINIMP3_ONLY_SIMD used, but SSE/NEON not enabled
|
||||
|
@ -189,6 +191,18 @@ static int have_simd()
|
|||
#define HAVE_SIMD 0
|
||||
#endif /* !defined(MINIMP3_NO_SIMD) */
|
||||
|
||||
#if defined(__ARM_ARCH) && (__ARM_ARCH >= 6) && !defined(__aarch64__) && !defined(_M_ARM64)
|
||||
#define HAVE_ARMV6 1
|
||||
static __inline__ __attribute__((always_inline)) int32_t minimp3_clip_int16_arm(int32_t a)
|
||||
{
|
||||
int32_t x = 0;
|
||||
__asm__ ("ssat %0, #16, %1" : "=r"(x) : "r"(a));
|
||||
return x;
|
||||
}
|
||||
#else
|
||||
#define HAVE_ARMV6 0
|
||||
#endif
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const uint8_t *buf;
|
||||
|
@ -767,41 +781,72 @@ static void L3_huffman(float *dst, bs_t *bs, const L3_gr_info_t *gr_info, const
|
|||
int sfb_cnt = gr_info->region_count[ireg++];
|
||||
const int16_t *codebook = tabs + tabindex[tab_num];
|
||||
int linbits = g_linbits[tab_num];
|
||||
do
|
||||
if (linbits)
|
||||
{
|
||||
np = *sfb++ / 2;
|
||||
pairs_to_decode = MINIMP3_MIN(big_val_cnt, np);
|
||||
one = *scf++;
|
||||
do
|
||||
{
|
||||
int j, w = 5;
|
||||
int leaf = codebook[PEEK_BITS(w)];
|
||||
while (leaf < 0)
|
||||
np = *sfb++ / 2;
|
||||
pairs_to_decode = MINIMP3_MIN(big_val_cnt, np);
|
||||
one = *scf++;
|
||||
do
|
||||
{
|
||||
FLUSH_BITS(w);
|
||||
w = leaf & 7;
|
||||
leaf = codebook[PEEK_BITS(w) - (leaf >> 3)];
|
||||
}
|
||||
FLUSH_BITS(leaf >> 8);
|
||||
|
||||
for (j = 0; j < 2; j++, dst++, leaf >>= 4)
|
||||
{
|
||||
int lsb = leaf & 0x0F;
|
||||
if (lsb == 15 && linbits)
|
||||
int j, w = 5;
|
||||
int leaf = codebook[PEEK_BITS(w)];
|
||||
while (leaf < 0)
|
||||
{
|
||||
lsb += PEEK_BITS(linbits);
|
||||
FLUSH_BITS(linbits);
|
||||
CHECK_BITS;
|
||||
*dst = one*L3_pow_43(lsb)*((int32_t)bs_cache < 0 ? -1: 1);
|
||||
} else
|
||||
{
|
||||
*dst = g_pow43[16 + lsb - 16*(bs_cache >> 31)]*one;
|
||||
FLUSH_BITS(w);
|
||||
w = leaf & 7;
|
||||
leaf = codebook[PEEK_BITS(w) - (leaf >> 3)];
|
||||
}
|
||||
FLUSH_BITS(lsb ? 1 : 0);
|
||||
}
|
||||
CHECK_BITS;
|
||||
} while (--pairs_to_decode);
|
||||
} while ((big_val_cnt -= np) > 0 && --sfb_cnt >= 0);
|
||||
FLUSH_BITS(leaf >> 8);
|
||||
|
||||
for (j = 0; j < 2; j++, dst++, leaf >>= 4)
|
||||
{
|
||||
int lsb = leaf & 0x0F;
|
||||
if (lsb == 15)
|
||||
{
|
||||
lsb += PEEK_BITS(linbits);
|
||||
FLUSH_BITS(linbits);
|
||||
CHECK_BITS;
|
||||
*dst = one*L3_pow_43(lsb)*((int32_t)bs_cache < 0 ? -1: 1);
|
||||
} else
|
||||
{
|
||||
*dst = g_pow43[16 + lsb - 16*(bs_cache >> 31)]*one;
|
||||
}
|
||||
FLUSH_BITS(lsb ? 1 : 0);
|
||||
}
|
||||
CHECK_BITS;
|
||||
} while (--pairs_to_decode);
|
||||
} while ((big_val_cnt -= np) > 0 && --sfb_cnt >= 0);
|
||||
} else
|
||||
{
|
||||
do
|
||||
{
|
||||
np = *sfb++ / 2;
|
||||
pairs_to_decode = MINIMP3_MIN(big_val_cnt, np);
|
||||
one = *scf++;
|
||||
do
|
||||
{
|
||||
int j, w = 5;
|
||||
int leaf = codebook[PEEK_BITS(w)];
|
||||
while (leaf < 0)
|
||||
{
|
||||
FLUSH_BITS(w);
|
||||
w = leaf & 7;
|
||||
leaf = codebook[PEEK_BITS(w) - (leaf >> 3)];
|
||||
}
|
||||
FLUSH_BITS(leaf >> 8);
|
||||
|
||||
for (j = 0; j < 2; j++, dst++, leaf >>= 4)
|
||||
{
|
||||
int lsb = leaf & 0x0F;
|
||||
*dst = g_pow43[16 + lsb - 16*(bs_cache >> 31)]*one;
|
||||
FLUSH_BITS(lsb ? 1 : 0);
|
||||
}
|
||||
CHECK_BITS;
|
||||
} while (--pairs_to_decode);
|
||||
} while ((big_val_cnt -= np) > 0 && --sfb_cnt >= 0);
|
||||
}
|
||||
}
|
||||
|
||||
for (np = 1 - big_val_cnt;; dst += 4)
|
||||
|
@ -1374,10 +1419,16 @@ static void mp3d_DCT_II(float *grbuf, int n)
|
|||
#ifndef MINIMP3_FLOAT_OUTPUT
|
||||
static int16_t mp3d_scale_pcm(float sample)
|
||||
{
|
||||
#if HAVE_ARMV6
|
||||
int32_t s32 = (int32_t)(sample + .5f);
|
||||
s32 -= (s32 < 0);
|
||||
int16_t s = (int16_t)minimp3_clip_int16_arm(s32);
|
||||
#else
|
||||
if (sample >= 32766.5) return (int16_t) 32767;
|
||||
if (sample <= -32767.5) return (int16_t)-32768;
|
||||
int16_t s = (int16_t)(sample + .5f);
|
||||
s -= (s < 0); /* away from zero, to be compliant */
|
||||
#endif
|
||||
return s;
|
||||
}
|
||||
#else /* MINIMP3_FLOAT_OUTPUT */
|
||||
|
@ -1641,7 +1692,7 @@ static int mp3d_find_frame(const uint8_t *mp3, int mp3_bytes, int *free_format_b
|
|||
}
|
||||
}
|
||||
*ptr_frame_bytes = 0;
|
||||
return i;
|
||||
return mp3_bytes;
|
||||
}
|
||||
|
||||
void mp3dec_init(mp3dec_t *dec)
|
||||
|
@ -1670,7 +1721,7 @@ int mp3dec_decode_frame(mp3dec_t *dec, const uint8_t *mp3, int mp3_bytes, mp3d_s
|
|||
i = mp3d_find_frame(mp3, mp3_bytes, &dec->free_format_bytes, &frame_size);
|
||||
if (!frame_size || i + frame_size > mp3_bytes)
|
||||
{
|
||||
info->frame_bytes = 0;
|
||||
info->frame_bytes = i;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -1678,6 +1729,7 @@ int mp3dec_decode_frame(mp3dec_t *dec, const uint8_t *mp3, int mp3_bytes, mp3d_s
|
|||
hdr = mp3 + i;
|
||||
memcpy(dec->header, hdr, HDR_SIZE);
|
||||
info->frame_bytes = i + frame_size;
|
||||
info->frame_offset = i;
|
||||
info->channels = HDR_IS_MONO(hdr) ? 1 : 2;
|
||||
info->hz = hdr_sample_rate_hz(hdr);
|
||||
info->layer = 4 - HDR_GET_LAYER(hdr);
|
||||
|
@ -1746,60 +1798,56 @@ int mp3dec_decode_frame(mp3dec_t *dec, const uint8_t *mp3, int mp3_bytes, mp3d_s
|
|||
#ifdef MINIMP3_FLOAT_OUTPUT
|
||||
void mp3dec_f32_to_s16(const float *in, int16_t *out, int num_samples)
|
||||
{
|
||||
if(num_samples > 0)
|
||||
{
|
||||
int i = 0;
|
||||
int i = 0;
|
||||
#if HAVE_SIMD
|
||||
int aligned_count = num_samples & ~7;
|
||||
|
||||
for(;i < aligned_count;i+=8)
|
||||
{
|
||||
static const f4 g_scale = { 32768.0f, 32768.0f, 32768.0f, 32768.0f };
|
||||
f4 a = VMUL(VLD(&in[i ]), g_scale);
|
||||
f4 b = VMUL(VLD(&in[i+4]), g_scale);
|
||||
int aligned_count = num_samples & ~7;
|
||||
for(; i < aligned_count; i += 8)
|
||||
{
|
||||
static const f4 g_scale = { 32768.0f, 32768.0f, 32768.0f, 32768.0f };
|
||||
f4 a = VMUL(VLD(&in[i ]), g_scale);
|
||||
f4 b = VMUL(VLD(&in[i+4]), g_scale);
|
||||
#if HAVE_SSE
|
||||
static const f4 g_max = { 32767.0f, 32767.0f, 32767.0f, 32767.0f };
|
||||
static const f4 g_min = { -32768.0f, -32768.0f, -32768.0f, -32768.0f };
|
||||
__m128i pcm8 = _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, g_max), g_min)),
|
||||
_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, g_max), g_min)));
|
||||
out[i ] = _mm_extract_epi16(pcm8, 0);
|
||||
out[i+1] = _mm_extract_epi16(pcm8, 1);
|
||||
out[i+2] = _mm_extract_epi16(pcm8, 2);
|
||||
out[i+3] = _mm_extract_epi16(pcm8, 3);
|
||||
out[i+4] = _mm_extract_epi16(pcm8, 4);
|
||||
out[i+5] = _mm_extract_epi16(pcm8, 5);
|
||||
out[i+6] = _mm_extract_epi16(pcm8, 6);
|
||||
out[i+7] = _mm_extract_epi16(pcm8, 7);
|
||||
static const f4 g_max = { 32767.0f, 32767.0f, 32767.0f, 32767.0f };
|
||||
static const f4 g_min = { -32768.0f, -32768.0f, -32768.0f, -32768.0f };
|
||||
__m128i pcm8 = _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, g_max), g_min)),
|
||||
_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, g_max), g_min)));
|
||||
out[i ] = _mm_extract_epi16(pcm8, 0);
|
||||
out[i+1] = _mm_extract_epi16(pcm8, 1);
|
||||
out[i+2] = _mm_extract_epi16(pcm8, 2);
|
||||
out[i+3] = _mm_extract_epi16(pcm8, 3);
|
||||
out[i+4] = _mm_extract_epi16(pcm8, 4);
|
||||
out[i+5] = _mm_extract_epi16(pcm8, 5);
|
||||
out[i+6] = _mm_extract_epi16(pcm8, 6);
|
||||
out[i+7] = _mm_extract_epi16(pcm8, 7);
|
||||
#else /* HAVE_SSE */
|
||||
int16x4_t pcma, pcmb;
|
||||
a = VADD(a, VSET(0.5f));
|
||||
b = VADD(b, VSET(0.5f));
|
||||
pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, VSET(0)))));
|
||||
pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, VSET(0)))));
|
||||
vst1_lane_s16(out+i , pcma, 0);
|
||||
vst1_lane_s16(out+i+1, pcma, 1);
|
||||
vst1_lane_s16(out+i+2, pcma, 2);
|
||||
vst1_lane_s16(out+i+3, pcma, 3);
|
||||
vst1_lane_s16(out+i+4, pcmb, 0);
|
||||
vst1_lane_s16(out+i+5, pcmb, 1);
|
||||
vst1_lane_s16(out+i+6, pcmb, 2);
|
||||
vst1_lane_s16(out+i+7, pcmb, 3);
|
||||
int16x4_t pcma, pcmb;
|
||||
a = VADD(a, VSET(0.5f));
|
||||
b = VADD(b, VSET(0.5f));
|
||||
pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, VSET(0)))));
|
||||
pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, VSET(0)))));
|
||||
vst1_lane_s16(out+i , pcma, 0);
|
||||
vst1_lane_s16(out+i+1, pcma, 1);
|
||||
vst1_lane_s16(out+i+2, pcma, 2);
|
||||
vst1_lane_s16(out+i+3, pcma, 3);
|
||||
vst1_lane_s16(out+i+4, pcmb, 0);
|
||||
vst1_lane_s16(out+i+5, pcmb, 1);
|
||||
vst1_lane_s16(out+i+6, pcmb, 2);
|
||||
vst1_lane_s16(out+i+7, pcmb, 3);
|
||||
#endif /* HAVE_SSE */
|
||||
}
|
||||
}
|
||||
#endif /* HAVE_SIMD */
|
||||
for(; i < num_samples; i++)
|
||||
for(; i < num_samples; i++)
|
||||
{
|
||||
float sample = in[i] * 32768.0f;
|
||||
if (sample >= 32766.5)
|
||||
out[i] = (int16_t) 32767;
|
||||
else if (sample <= -32767.5)
|
||||
out[i] = (int16_t)-32768;
|
||||
else
|
||||
{
|
||||
float sample = in[i] * 32768.0f;
|
||||
if (sample >= 32766.5)
|
||||
out[i] = (int16_t) 32767;
|
||||
else if (sample <= -32767.5)
|
||||
out[i] = (int16_t)-32768;
|
||||
else
|
||||
{
|
||||
int16_t s = (int16_t)(sample + .5f);
|
||||
s -= (s < 0); /* away from zero, to be compliant */
|
||||
out[i] = s;
|
||||
}
|
||||
int16_t s = (int16_t)(sample + .5f);
|
||||
s -= (s < 0); /* away from zero, to be compliant */
|
||||
out[i] = s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
1238
3rdparty/minimp3/minimp3_ex.h
vendored
1238
3rdparty/minimp3/minimp3_ex.h
vendored
File diff suppressed because it is too large
Load diff
|
@ -14,6 +14,28 @@
|
|||
#include "minimp3/minimp3.h"
|
||||
#include "minimp3/minimp3_ex.h"
|
||||
|
||||
#define LOG_GENERAL (1 << 0)
|
||||
#define LOG_READ (1 << 1)
|
||||
#define LOG_WRITE (1 << 2)
|
||||
#define LOG_REGISTER (1 << 3)
|
||||
#define LOG_CONFIG (1 << 4)
|
||||
#define LOG_OTHER (1 << 5)
|
||||
// #define VERBOSE (LOG_GENERAL | LOG_READ | LOG_WRITE | LOG_REGISTER | LOG_CONFIG | LOG_OTHER)
|
||||
// #define LOG_OUTPUT_STREAM std::cout
|
||||
|
||||
#include "logmacro.h"
|
||||
|
||||
#define LOGREAD(...) LOGMASKED(LOG_READ, __VA_ARGS__)
|
||||
#define LOGWRITE(...) LOGMASKED(LOG_WRITE, __VA_ARGS__)
|
||||
#define LOGREGISTER(...) LOGMASKED(LOG_REGISTER, __VA_ARGS__)
|
||||
#define LOGCONFIG(...) LOGMASKED(LOG_CONFIG, __VA_ARGS__)
|
||||
#define LOGOTHER(...) LOGMASKED(LOG_OTHER, __VA_ARGS__)
|
||||
|
||||
ALLOW_SAVE_TYPE(mas3507d_device::i2c_bus_state_t)
|
||||
ALLOW_SAVE_TYPE(mas3507d_device::i2c_bus_address_t)
|
||||
ALLOW_SAVE_TYPE(mas3507d_device::i2c_subdest_t)
|
||||
ALLOW_SAVE_TYPE(mas3507d_device::i2c_command_t)
|
||||
|
||||
// device type definition
|
||||
DEFINE_DEVICE_TYPE(MAS3507D, mas3507d_device, "mas3507d", "MAS 3507D MPEG decoder")
|
||||
|
||||
|
@ -21,8 +43,9 @@ mas3507d_device::mas3507d_device(const machine_config &mconfig, const char *tag,
|
|||
: device_t(mconfig, MAS3507D, tag, owner, clock)
|
||||
, device_sound_interface(mconfig, *this)
|
||||
, cb_sample(*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)
|
||||
, 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)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -31,6 +54,50 @@ void mas3507d_device::device_start()
|
|||
current_rate = 44100;
|
||||
stream = stream_alloc(0, 2, current_rate);
|
||||
cb_sample.resolve();
|
||||
|
||||
save_item(NAME(mp3data));
|
||||
save_item(NAME(samples));
|
||||
save_item(NAME(i2c_bus_state));
|
||||
save_item(NAME(i2c_bus_address));
|
||||
save_item(NAME(i2c_subdest));
|
||||
save_item(NAME(i2c_command));
|
||||
save_item(NAME(i2c_scli));
|
||||
save_item(NAME(i2c_sclo));
|
||||
save_item(NAME(i2c_sdai));
|
||||
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));
|
||||
save_item(NAME(samples_idx));
|
||||
save_item(NAME(is_muted));
|
||||
save_item(NAME(gain_ll));
|
||||
save_item(NAME(gain_rr));
|
||||
save_item(NAME(i2c_bytecount));
|
||||
save_item(NAME(i2c_io_bank));
|
||||
save_item(NAME(i2c_io_adr));
|
||||
save_item(NAME(i2c_io_count));
|
||||
save_item(NAME(i2c_io_val));
|
||||
save_item(NAME(i2c_sdao_data));
|
||||
save_item(NAME(playback_status));
|
||||
|
||||
// This should be removed in the future if/when native MP3 decoding is implemented in MAME
|
||||
save_item(NAME(mp3_dec.mdct_overlap));
|
||||
save_item(NAME(mp3_dec.qmf_state));
|
||||
save_item(NAME(mp3_dec.reserv));
|
||||
save_item(NAME(mp3_dec.free_format_bytes));
|
||||
save_item(NAME(mp3_dec.header));
|
||||
save_item(NAME(mp3_dec.reserv_buf));
|
||||
|
||||
save_item(NAME(mp3_info.frame_bytes));
|
||||
save_item(NAME(mp3_info.frame_offset));
|
||||
save_item(NAME(mp3_info.channels));
|
||||
save_item(NAME(mp3_info.hz));
|
||||
save_item(NAME(mp3_info.layer));
|
||||
save_item(NAME(mp3_info.bitrate_kbps));
|
||||
}
|
||||
|
||||
void mas3507d_device::device_reset()
|
||||
|
@ -42,13 +109,10 @@ void mas3507d_device::device_reset()
|
|||
i2c_bus_curbit = -1;
|
||||
i2c_bus_curval = 0;
|
||||
|
||||
mp3dec_init(&mp3_dec);
|
||||
memset(mp3data.data(), 0, mp3data.size());
|
||||
memset(samples.data(), 0, samples.size());
|
||||
mp3_count = 0;
|
||||
sample_count = 0;
|
||||
total_frame_count = 0;
|
||||
buffered_frame_count = 0;
|
||||
is_muted = false;
|
||||
gain_ll = gain_rr = 0;
|
||||
|
||||
reset_playback();
|
||||
}
|
||||
|
||||
void mas3507d_device::i2c_scl_w(bool line)
|
||||
|
@ -61,6 +125,14 @@ void mas3507d_device::i2c_scl_w(bool line)
|
|||
if(i2c_bus_state == STARTED) {
|
||||
if(i2c_sdai)
|
||||
i2c_bus_curval |= 1 << i2c_bus_curbit;
|
||||
|
||||
if(i2c_subdest == DATA_READ)
|
||||
i2c_sdao = BIT(i2c_sdao_data, i2c_bus_curbit + (i2c_bytecount * 8));
|
||||
else {
|
||||
i2c_sdao_data = 0;
|
||||
i2c_sdao = false;
|
||||
}
|
||||
|
||||
i2c_bus_curbit --;
|
||||
if(i2c_bus_curbit == -1) {
|
||||
if(i2c_bus_address == UNKNOWN) {
|
||||
|
@ -86,7 +158,7 @@ void mas3507d_device::i2c_scl_w(bool line)
|
|||
i2c_bus_state = STARTED;
|
||||
i2c_bus_curbit = 7;
|
||||
i2c_bus_curval = 0;
|
||||
i2c_sdao = true;
|
||||
i2c_sdao = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -131,24 +203,30 @@ int mas3507d_device::i2c_sda_r()
|
|||
|
||||
bool mas3507d_device::i2c_device_got_address(uint8_t address)
|
||||
{
|
||||
if (address == 0x3b) {
|
||||
if(address == CMD_DEV_READ)
|
||||
i2c_subdest = DATA_READ;
|
||||
} else {
|
||||
else
|
||||
i2c_subdest = UNDEFINED;
|
||||
}
|
||||
|
||||
return (address & 0xfe) == 0x3a;
|
||||
return (address & 0xfe) == CMD_DEV_WRITE;
|
||||
}
|
||||
|
||||
void mas3507d_device::i2c_device_got_byte(uint8_t byte)
|
||||
{
|
||||
switch(i2c_subdest) {
|
||||
case UNDEFINED:
|
||||
if(byte == 0x68)
|
||||
if(byte == CMD_DATA_WRITE)
|
||||
i2c_subdest = DATA_WRITE;
|
||||
else if(byte == 0x69)
|
||||
else if(byte == CMD_DATA_READ) {
|
||||
i2c_subdest = DATA_READ;
|
||||
else if(byte == 0x6a)
|
||||
|
||||
// Default read, returns the current frame count
|
||||
i2c_sdao_data = ((decoded_frame_count >> 8) & 0xff)
|
||||
| ((decoded_frame_count & 0xff) << 8)
|
||||
| (((decoded_frame_count >> 24) & 0xff) << 16)
|
||||
| (((decoded_frame_count >> 16) & 0xff) << 24);
|
||||
}
|
||||
else if(byte == CMD_CONTROL_WRITE)
|
||||
i2c_subdest = CONTROL;
|
||||
else
|
||||
i2c_subdest = BAD;
|
||||
|
@ -159,20 +237,20 @@ void mas3507d_device::i2c_device_got_byte(uint8_t byte)
|
|||
break;
|
||||
|
||||
case BAD:
|
||||
logerror("MAS I2C: Dropping byte %02x\n", byte);
|
||||
LOGOTHER("MAS I2C: Dropping byte %02x\n", byte);
|
||||
break;
|
||||
|
||||
case DATA_READ:
|
||||
// Default Read
|
||||
// This should return the current MPEGFrameCount value when called
|
||||
switch(i2c_bytecount) {
|
||||
case 0: i2c_io_val = byte; break;
|
||||
case 1: i2c_io_val |= byte << 8; break;
|
||||
case 2: i2c_nak(); return;
|
||||
}
|
||||
|
||||
LOGREAD("MAS I2C: DATA_READ %d %02x %08x\n", i2c_bytecount, byte, i2c_io_val);
|
||||
|
||||
// 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:
|
||||
|
@ -184,7 +262,7 @@ void mas3507d_device::i2c_device_got_byte(uint8_t byte)
|
|||
break;
|
||||
case 3:
|
||||
i2c_command = CMD_READ_CTRL;
|
||||
logerror("MAS I2C: READ_CTRL\n");
|
||||
LOGWRITE("MAS I2C: READ_CTRL\n");
|
||||
break;
|
||||
case 9:
|
||||
i2c_io_adr = (byte & 15) << 4;
|
||||
|
@ -196,22 +274,22 @@ void mas3507d_device::i2c_device_got_byte(uint8_t byte)
|
|||
break;
|
||||
case 0xd:
|
||||
i2c_command = CMD_READ_REG;
|
||||
logerror("MAS I2C: READ_REG\n");
|
||||
LOGWRITE("MAS I2C: READ_REG\n");
|
||||
break;
|
||||
case 0xe: case 0xf:
|
||||
i2c_io_bank = (byte >> 4) & 1;
|
||||
i2c_command = CMD_READ_MEM;
|
||||
logerror("MAS I2C: READ_MEM\n");
|
||||
LOGWRITE("MAS I2C: READ_MEM\n");
|
||||
break;
|
||||
default:
|
||||
i2c_command = CMD_BAD;
|
||||
logerror("MAS I2C: BAD\n");
|
||||
LOGWRITE("MAS I2C: BAD\n");
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch(i2c_command) {
|
||||
default:
|
||||
logerror("MAS I2C: Ignoring byte %02x\n", byte);
|
||||
LOGWRITE("MAS I2C: Ignoring byte %02x\n", byte);
|
||||
break;
|
||||
|
||||
case CMD_WRITE_REG:
|
||||
|
@ -260,14 +338,14 @@ void mas3507d_device::i2c_device_got_byte(uint8_t byte)
|
|||
break;
|
||||
|
||||
case CONTROL:
|
||||
logerror("MAS I2C: Control byte %02x\n", byte);
|
||||
LOGOTHER("MAS I2C: Control byte %02x\n", byte);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void mas3507d_device::i2c_device_got_stop()
|
||||
{
|
||||
logerror("MAS I2C: got stop\n");
|
||||
LOGOTHER("MAS I2C: got stop\n");
|
||||
}
|
||||
|
||||
int gain_to_db(double val) {
|
||||
|
@ -275,9 +353,8 @@ int gain_to_db(double val) {
|
|||
}
|
||||
|
||||
float gain_to_percentage(int val) {
|
||||
if (val == 0) {
|
||||
if(val == 0)
|
||||
return 0; // Special case for muting it seems
|
||||
}
|
||||
|
||||
double db = gain_to_db(val);
|
||||
|
||||
|
@ -287,113 +364,129 @@ float gain_to_percentage(int val) {
|
|||
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 0x0032d: LOGCONFIG("MAS3507D: PLLOffset48 = %05x\n", val); break;
|
||||
case 0x0032e: LOGCONFIG("MAS3507D: PLLOffset44 = %05x\n", val); break;
|
||||
case 0x0032f: LOGCONFIG("MAS3507D: OutputConfig = %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));
|
||||
set_output_gain(0, gain_to_percentage(val));
|
||||
gain_ll = gain_to_percentage(val);
|
||||
LOGCONFIG("MAS3507D: left->left gain = %05x (%d dB, %f%%)\n", val, gain_to_db(val), gain_ll);
|
||||
|
||||
if(!is_muted) {
|
||||
set_output_gain(0, gain_ll);
|
||||
}
|
||||
break;
|
||||
case 0x107f9:
|
||||
logerror("MAS3507D: left->right gain = %05x (%d dB, %f%%)\n", val, gain_to_db(val), gain_to_percentage(val));
|
||||
LOGCONFIG("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));
|
||||
LOGCONFIG("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));
|
||||
set_output_gain(1, gain_to_percentage(val));
|
||||
gain_rr = gain_to_percentage(val);
|
||||
LOGCONFIG("MAS3507D: right->right gain = %05x (%d dB, %f%%)\n", val, gain_to_db(val), gain_rr);
|
||||
|
||||
if(!is_muted) {
|
||||
set_output_gain(1, gain_rr);
|
||||
}
|
||||
break;
|
||||
default: logerror("MAS3507D: %d:%04x = %05x\n", bank, adr, val); break;
|
||||
default: LOGCONFIG("MAS3507D: %d:%04x = %05x\n", bank, adr, val); break;
|
||||
}
|
||||
}
|
||||
|
||||
void mas3507d_device::reg_write(uint32_t adr, uint32_t val)
|
||||
{
|
||||
switch(adr) {
|
||||
case 0x8e: logerror("MAS3507D: DCCF = %05x\n", val); break;
|
||||
case 0xaa: logerror("MAS3507D: Mute/bypass = %05x\n", val); break;
|
||||
case 0xe6: logerror("MAS3507D: StartupConfig = %05x\n", val); break;
|
||||
case 0xe7: logerror("MAS3507D: Kprescale = %05x\n", val); break;
|
||||
case 0x6b: logerror("MAS3507D: Kbass = %05x\n", val); break;
|
||||
case 0x6f: logerror("MAS3507D: Ktreble = %05x\n", val); break;
|
||||
default: logerror("MAS3507D: reg %02x = %05x\n", adr, val); break;
|
||||
case 0x8e: LOGCONFIG("MAS3507D: DCCF = %05x\n", val); break;
|
||||
case 0xaa:
|
||||
LOGCONFIG("MAS3507D: Mute/bypass = %05x\n", val);
|
||||
set_output_gain(0, val == 1 ? 0 : gain_ll);
|
||||
set_output_gain(1, val == 1 ? 0 : gain_rr);
|
||||
break;
|
||||
case 0xe6: LOGCONFIG("MAS3507D: StartupConfig = %05x\n", val); break;
|
||||
case 0xe7: LOGCONFIG("MAS3507D: Kprescale = %05x\n", val); break;
|
||||
case 0x6b: LOGCONFIG("MAS3507D: Kbass = %05x\n", val); break;
|
||||
case 0x6f: LOGCONFIG("MAS3507D: Ktreble = %05x\n", val); break;
|
||||
default: LOGCONFIG("MAS3507D: reg %02x = %05x\n", adr, val); break;
|
||||
}
|
||||
}
|
||||
|
||||
void mas3507d_device::run_program(uint32_t adr)
|
||||
{
|
||||
switch(adr) {
|
||||
case 0xfcb: logerror("MAS3507D: validate OutputConfig\n"); break;
|
||||
default: logerror("MAS3507D: run %04x\n", adr); break;
|
||||
case 0xfcb: LOGCONFIG("MAS3507D: validate OutputConfig\n"); break;
|
||||
default: LOGCONFIG("MAS3507D: run %04x\n", adr); break;
|
||||
}
|
||||
}
|
||||
|
||||
void mas3507d_device::fill_buffer()
|
||||
{
|
||||
while(mp3_count + 2 < mp3data.size()) {
|
||||
u16 v = cb_sample();
|
||||
mp3data[mp3_count++] = v >> 8;
|
||||
mp3data[mp3_count++] = v;
|
||||
while(mp3data_count + 2 < mp3data.size()) {
|
||||
uint16_t v = cb_sample();
|
||||
mp3data[mp3data_count++] = v >> 8;
|
||||
mp3data[mp3data_count++] = v;
|
||||
}
|
||||
|
||||
int scount = mp3dec_decode_frame(&mp3_dec, static_cast<const uint8_t *>(&mp3data[0]), mp3_count, static_cast<mp3d_sample_t *>(&samples[0]), &mp3_info);
|
||||
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(!scount) {
|
||||
int to_drop = mp3_info.frame_bytes;
|
||||
// At 1MHz, we can transfer around 2082 bytes/video frame. So
|
||||
// that puts a boundary on how much we're ready to drop
|
||||
if(to_drop > 2082 || !to_drop)
|
||||
to_drop = 2082;
|
||||
std::copy(mp3data.begin() + to_drop, mp3data.end(), mp3data.begin());
|
||||
mp3_count -= to_drop;
|
||||
if(sample_count == 0)
|
||||
return;
|
||||
}
|
||||
|
||||
std::copy(mp3data.begin() + mp3_info.frame_bytes, mp3data.end(), mp3data.begin());
|
||||
mp3_count -= mp3_info.frame_bytes;
|
||||
|
||||
sample_count = scount;
|
||||
mp3data_count -= mp3_info.frame_bytes;
|
||||
|
||||
if(mp3_info.hz != current_rate) {
|
||||
current_rate = mp3_info.hz;
|
||||
stream->set_sample_rate(current_rate);
|
||||
}
|
||||
|
||||
decoded_frame_count++;
|
||||
}
|
||||
|
||||
void mas3507d_device::append_buffer(std::vector<write_stream_view> &outputs, int &pos, int scount)
|
||||
{
|
||||
buffered_frame_count = scount;
|
||||
|
||||
int s1 = scount - pos;
|
||||
int bytes_per_sample = mp3_info.channels > 2 ? 2 : mp3_info.channels; // More than 2 channels is unsupported here
|
||||
|
||||
if(s1 > sample_count)
|
||||
s1 = sample_count;
|
||||
|
||||
if(mp3_info.channels == 1) {
|
||||
for(int i=0; i<s1; i++) {
|
||||
outputs[0].put_int(i+pos, samples[i], 32768);
|
||||
outputs[1].put_int(i+pos, samples[i], 32768);
|
||||
}
|
||||
} else {
|
||||
for(int i=0; i<s1; i++) {
|
||||
outputs[0].put_int(i+pos, samples[i*2], 32768);
|
||||
outputs[1].put_int(i+pos, samples[i*2+1], 32768);
|
||||
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);
|
||||
|
||||
samples_idx++;
|
||||
decoded_samples++;
|
||||
pos++;
|
||||
|
||||
if(samples_idx >= sample_count) {
|
||||
sample_count = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(s1 == sample_count) {
|
||||
pos += s1;
|
||||
sample_count = 0;
|
||||
total_frame_count += s1;
|
||||
return;
|
||||
}
|
||||
void mas3507d_device::reset_playback()
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
if(mp3_info.channels == 1)
|
||||
std::copy(samples.begin() + s1, samples.begin() + sample_count, samples.begin());
|
||||
else
|
||||
std::copy(samples.begin() + s1*2, samples.begin() + sample_count*2, samples.begin());
|
||||
|
||||
pos += s1;
|
||||
sample_count -= s1;
|
||||
total_frame_count += s1;
|
||||
void mas3507d_device::start_playback()
|
||||
{
|
||||
reset_playback();
|
||||
is_started = true;
|
||||
}
|
||||
|
||||
void mas3507d_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
|
||||
|
@ -401,25 +494,19 @@ void mas3507d_device::sound_stream_update(sound_stream &stream, std::vector<read
|
|||
int csamples = outputs[0].samples();
|
||||
int pos = 0;
|
||||
|
||||
append_buffer(outputs, pos, csamples);
|
||||
for(;;) {
|
||||
if(pos == csamples)
|
||||
return;
|
||||
fill_buffer();
|
||||
if(!sample_count) {
|
||||
// In the case of a bad frame or no frames being around, reset the state of the decoder
|
||||
mp3dec_init(&mp3_dec);
|
||||
memset(mp3data.data(), 0, mp3data.size());
|
||||
memset(samples.data(), 0, samples.size());
|
||||
mp3_count = 0;
|
||||
sample_count = 0;
|
||||
total_frame_count = 0;
|
||||
buffered_frame_count = 0;
|
||||
while(pos < csamples) {
|
||||
if(is_started && sample_count == 0)
|
||||
fill_buffer();
|
||||
|
||||
if(!is_started || sample_count <= 0) {
|
||||
playback_status = PLAYBACK_STATE_IDLE;
|
||||
decoded_frame_count = 0;
|
||||
decoded_samples = 0;
|
||||
outputs[0].fill(0, pos);
|
||||
outputs[1].fill(0, pos);
|
||||
return;
|
||||
}
|
||||
|
||||
append_buffer(outputs, pos, csamples);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,12 @@
|
|||
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);
|
||||
|
||||
|
@ -22,7 +28,15 @@ public:
|
|||
void i2c_scl_w(bool line);
|
||||
void i2c_sda_w(bool line);
|
||||
|
||||
u32 get_frame_count() const { return total_frame_count - buffered_frame_count; }
|
||||
uint32_t get_samples() const { return decoded_samples; }
|
||||
uint32_t get_status() const { return playback_status; }
|
||||
|
||||
void update_stream() { stream->update(); }
|
||||
|
||||
void reset_playback();
|
||||
void start_playback();
|
||||
|
||||
bool is_started;
|
||||
|
||||
protected:
|
||||
virtual void device_start() override;
|
||||
|
@ -30,40 +44,62 @@ protected:
|
|||
virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
|
||||
|
||||
private:
|
||||
devcb_read16 cb_sample;
|
||||
|
||||
enum { IDLE, STARTED, NAK, ACK, ACK2 } i2c_bus_state;
|
||||
enum { UNKNOWN, VALIDATED, WRONG } i2c_bus_address;
|
||||
|
||||
std::array<u8, 0xe00> mp3data;
|
||||
std::array<mp3d_sample_t, MINIMP3_MAX_SAMPLES_PER_FRAME> samples;
|
||||
bool i2c_scli, i2c_sclo, i2c_sdai, i2c_sdao;
|
||||
int i2c_bus_curbit;
|
||||
uint8_t i2c_bus_curval;
|
||||
int mp3_count, sample_count, current_rate;
|
||||
u32 total_frame_count, buffered_frame_count;
|
||||
|
||||
mp3dec_t mp3_dec;
|
||||
mp3dec_frame_info_t mp3_info;
|
||||
|
||||
sound_stream *stream;
|
||||
|
||||
void i2c_nak();
|
||||
bool i2c_device_got_address(uint8_t address);
|
||||
void i2c_device_got_byte(uint8_t byte);
|
||||
void i2c_device_got_stop();
|
||||
|
||||
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);
|
||||
|
||||
void fill_buffer();
|
||||
void append_buffer(std::vector<write_stream_view> &outputs, int &pos, int samples);
|
||||
void append_buffer(std::vector<write_stream_view> &outputs, int &pos, int scount);
|
||||
|
||||
devcb_read16 cb_sample;
|
||||
|
||||
enum {
|
||||
CMD_DEV_WRITE = 0x3a,
|
||||
CMD_DEV_READ = 0x3b,
|
||||
|
||||
CMD_DATA_WRITE = 0x68,
|
||||
CMD_DATA_READ = 0x69,
|
||||
CMD_CONTROL_WRITE = 0x6a
|
||||
};
|
||||
|
||||
enum i2c_bus_state_t : uint8_t { IDLE = 0, STARTED, NAK, ACK, ACK2 };
|
||||
enum i2c_bus_address_t : uint8_t { UNKNOWN = 0, VALIDATED, WRONG };
|
||||
enum i2c_subdest_t : uint8_t { UNDEFINED = 0, CONTROL, DATA_READ, DATA_WRITE, BAD };
|
||||
enum i2c_command_t : uint8_t { CMD_BAD = 0, CMD_RUN, CMD_READ_CTRL, CMD_WRITE_REG, CMD_WRITE_MEM, CMD_READ_REG, CMD_READ_MEM };
|
||||
|
||||
i2c_bus_state_t i2c_bus_state;
|
||||
i2c_bus_address_t i2c_bus_address;
|
||||
i2c_subdest_t i2c_subdest;
|
||||
i2c_command_t i2c_command;
|
||||
|
||||
mp3dec_t mp3_dec;
|
||||
mp3dec_frame_info_t mp3_info;
|
||||
|
||||
sound_stream *stream;
|
||||
|
||||
std::array<uint8_t, 0xe00> mp3data;
|
||||
std::array<mp3d_sample_t, MINIMP3_MAX_SAMPLES_PER_FRAME> samples;
|
||||
|
||||
bool i2c_scli, i2c_sclo, i2c_sdai, i2c_sdao;
|
||||
int i2c_bus_curbit;
|
||||
uint8_t i2c_bus_curval;
|
||||
int i2c_bytecount;
|
||||
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;
|
||||
uint32_t decoded_frame_count, decoded_samples;
|
||||
int32_t sample_count, samples_idx;
|
||||
|
||||
bool is_muted;
|
||||
float gain_ll, gain_rr;
|
||||
|
||||
uint32_t playback_status;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -794,7 +794,7 @@ template <unsigned Count> using required_memory_bank_array = memory_bank_array_f
|
|||
///
|
||||
/// Creates a memory bank or finds an existing one instantiated via an
|
||||
/// address map.
|
||||
class memory_bank_creator : finder_base
|
||||
class memory_bank_creator : public finder_base
|
||||
{
|
||||
public:
|
||||
/// \brief Memory bank creator constructor
|
||||
|
@ -1249,7 +1249,7 @@ template <typename PointerType, unsigned Count> using required_shared_ptr_array
|
|||
/// share. If an existing memory share is found, it is an error if it
|
||||
/// doesn't match the requested width, length and endianness.
|
||||
template <typename PointerType>
|
||||
class memory_share_creator : finder_base
|
||||
class memory_share_creator : public finder_base
|
||||
{
|
||||
public:
|
||||
/// \brief Memory share creator constructor
|
||||
|
|
|
@ -3,6 +3,19 @@
|
|||
#include "emu.h"
|
||||
#include "k573dio.h"
|
||||
|
||||
#define LOG_GENERAL (1 << 0)
|
||||
#define LOG_FPGA (1 << 1)
|
||||
#define LOG_MP3 (1 << 2)
|
||||
#define LOG_UNKNOWNREG (1 << 3)
|
||||
// #define VERBOSE (LOG_GENERAL | LOG_FPGA | LOG_MP3 | LOG_UNKNOWNREG)
|
||||
// #define LOG_OUTPUT_STREAM std::cout
|
||||
|
||||
#include "logmacro.h"
|
||||
|
||||
#define LOGFPGA(...) LOGMASKED(LOG_FPGA, __VA_ARGS__)
|
||||
#define LOGMP3(...) LOGMASKED(LOG_MP3, __VA_ARGS__)
|
||||
#define LOGUNKNOWNREG(...) LOGMASKED(LOG_UNKNOWNREG, __VA_ARGS__)
|
||||
|
||||
/*
|
||||
Digital I/O PCB
|
||||
---------------
|
||||
|
@ -82,16 +95,17 @@ void k573dio_device::amap(address_map &map)
|
|||
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).rw(FUNC(k573dio_device::mpeg_key_1_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::mpeg_ctrl_r), FUNC(k573dio_device::mpeg_ctrl_w));
|
||||
map(0xae, 0xaf).rw(FUNC(k573dio_device::fpga_ctrl_r), FUNC(k573dio_device::fpga_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).r(FUNC(k573dio_device::mp3_frame_count_high_r));
|
||||
map(0xcc, 0xcd).r(FUNC(k573dio_device::mp3_frame_count_low_r));
|
||||
map(0xce, 0xcf).r(FUNC(k573dio_device::mp3_unk_r));
|
||||
map(0xca, 0xcb).r(FUNC(k573dio_device::mp3_counter_high_r));
|
||||
map(0xcc, 0xcd).rw(FUNC(k573dio_device::mp3_counter_low_r), FUNC(k573dio_device::mp3_counter_low_w));
|
||||
map(0xce, 0xcf).r(FUNC(k573dio_device::mp3_counter_diff_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));
|
||||
|
@ -108,9 +122,9 @@ 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),
|
||||
ram(*this, "ram", 0x2000000, ENDIANNESS_LITTLE),
|
||||
k573fpga(*this, "k573fpga"),
|
||||
digital_id(*this, "digital_id"),
|
||||
mas3507d(*this, "mpeg"),
|
||||
output_cb(*this),
|
||||
is_ddrsbm_fpga(false)
|
||||
{
|
||||
|
@ -120,10 +134,12 @@ void k573dio_device::device_start()
|
|||
{
|
||||
output_cb.resolve_safe();
|
||||
|
||||
ram = std::make_unique<uint16_t[]>(0x2000000/2);
|
||||
save_pointer(NAME(ram), 0x2000000/2 );
|
||||
save_item(NAME(ram_adr));
|
||||
save_item(NAME(ram_read_adr));
|
||||
save_item(NAME(output_data));
|
||||
save_item(NAME(is_ddrsbm_fpga));
|
||||
save_item(NAME(crypto_key1));
|
||||
|
||||
k573fpga->set_ram(ram.get());
|
||||
k573fpga->set_ddrsbm_fpga(is_ddrsbm_fpga);
|
||||
}
|
||||
|
||||
|
@ -131,8 +147,9 @@ void k573dio_device::device_reset()
|
|||
{
|
||||
ram_adr = 0;
|
||||
ram_read_adr = 0;
|
||||
crypto_key1 = 0;
|
||||
|
||||
memset(output_data, 0, sizeof(output_data));
|
||||
std::fill(std::begin(output_data), std::end(output_data), 0);
|
||||
}
|
||||
|
||||
ROM_START( k573dio )
|
||||
|
@ -148,11 +165,11 @@ const tiny_rom_entry *k573dio_device::device_rom_region() const
|
|||
void k573dio_device::device_add_mconfig(machine_config &config)
|
||||
{
|
||||
KONAMI_573_DIGITAL_FPGA(config, k573fpga);
|
||||
k573fpga->set_ram(ram);
|
||||
k573fpga->add_route(0, ":lspeaker", 1.0);
|
||||
k573fpga->add_route(1, ":rspeaker", 1.0);
|
||||
|
||||
DS2401(config, digital_id);
|
||||
MAS3507D(config, mas3507d);
|
||||
mas3507d->sample_cb().set(k573fpga, FUNC(k573fpga_device::get_decrypted));
|
||||
mas3507d->add_route(0, ":lspeaker", 1.0);
|
||||
mas3507d->add_route(1, ":rspeaker", 1.0);
|
||||
}
|
||||
|
||||
void k573dio_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
|
||||
|
@ -161,61 +178,61 @@ void k573dio_device::device_timer(emu_timer &timer, device_timer_id id, int para
|
|||
|
||||
uint16_t k573dio_device::a00_r()
|
||||
{
|
||||
logerror("%s: a00_r (%s)\n", tag(), machine().describe_context());
|
||||
LOGUNKNOWNREG("%s: a00_r (%s)\n", tag(), machine().describe_context());
|
||||
return 0x0000;
|
||||
}
|
||||
|
||||
uint16_t k573dio_device::a02_r()
|
||||
{
|
||||
logerror("%s: a02_r (%s)\n", tag(), machine().describe_context());
|
||||
LOGUNKNOWNREG("%s: a02_r (%s)\n", tag(), machine().describe_context());
|
||||
return 0x0001;
|
||||
}
|
||||
|
||||
uint16_t k573dio_device::a04_r()
|
||||
{
|
||||
logerror("%s: a04_r (%s)\n", tag(), machine().describe_context());
|
||||
LOGUNKNOWNREG("%s: a04_r (%s)\n", tag(), machine().describe_context());
|
||||
return 0x0000;
|
||||
}
|
||||
|
||||
uint16_t k573dio_device::a06_r()
|
||||
{
|
||||
logerror("%s: a06_r (%s)\n", tag(), machine().describe_context());
|
||||
LOGUNKNOWNREG("%s: a06_r (%s)\n", tag(), machine().describe_context());
|
||||
return 0x0000;
|
||||
}
|
||||
|
||||
uint16_t k573dio_device::a0a_r()
|
||||
{
|
||||
logerror("%s: a0a_r (%s)\n", tag(), machine().describe_context());
|
||||
LOGUNKNOWNREG("%s: a0a_r (%s)\n", tag(), machine().describe_context());
|
||||
return 0x0000;
|
||||
}
|
||||
|
||||
void k573dio_device::a10_w(uint16_t data)
|
||||
{
|
||||
logerror("%s: a10_w (%s)\n", tag(), machine().describe_context());
|
||||
LOGUNKNOWNREG("%s: a10_w: %04x (%s)\n", tag(), data, machine().describe_context());
|
||||
}
|
||||
|
||||
uint16_t k573dio_device::ac4_r()
|
||||
{
|
||||
// What is this?
|
||||
LOGUNKNOWNREG("%s: ac4_r (%s)\n", tag(), machine().describe_context());
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16_t k573dio_device::a80_r()
|
||||
{
|
||||
logerror("%s: a80_r (%s)\n", tag(), machine().describe_context());
|
||||
LOGUNKNOWNREG("%s: a80_r (%s)\n", tag(), machine().describe_context());
|
||||
return 0x1234;
|
||||
}
|
||||
|
||||
void k573dio_device::mpeg_start_adr_high_w(uint16_t data)
|
||||
{
|
||||
logerror("FPGA MPEG start address high %04x\n", data);
|
||||
k573fpga->set_mp3_cur_adr((k573fpga->get_mp3_cur_adr() & 0x0000ffff) | (data << 16)); // high
|
||||
LOGMP3("FPGA MPEG start address high %04x\n", data);
|
||||
k573fpga->set_mp3_start_addr((k573fpga->get_mp3_start_addr() & 0x0000ffff) | (data << 16)); // high
|
||||
}
|
||||
|
||||
void k573dio_device::mpeg_start_adr_low_w(uint16_t data)
|
||||
{
|
||||
logerror("FPGA MPEG start address low %04x\n", data);
|
||||
k573fpga->set_mp3_cur_adr((k573fpga->get_mp3_cur_adr() & 0xffff0000) | data); // low
|
||||
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);
|
||||
|
@ -223,14 +240,14 @@ void k573dio_device::mpeg_start_adr_low_w(uint16_t data)
|
|||
|
||||
void k573dio_device::mpeg_end_adr_high_w(uint16_t data)
|
||||
{
|
||||
logerror("FPGA MPEG end address high %04x\n", data);
|
||||
k573fpga->set_mp3_end_adr((k573fpga->get_mp3_end_adr() & 0x0000ffff) | (data << 16)); // high
|
||||
LOGMP3("FPGA MPEG end address high %04x\n", data);
|
||||
k573fpga->set_mp3_end_addr((k573fpga->get_mp3_end_addr() & 0x0000ffff) | (data << 16)); // high
|
||||
}
|
||||
|
||||
void k573dio_device::mpeg_end_adr_low_w(uint16_t data)
|
||||
{
|
||||
logerror("FPGA MPEG end address low %04x\n", data);
|
||||
k573fpga->set_mp3_end_adr((k573fpga->get_mp3_end_adr() & 0xffff0000) | data); // low
|
||||
LOGMP3("FPGA MPEG end address low %04x\n", data);
|
||||
k573fpga->set_mp3_end_addr((k573fpga->get_mp3_end_addr() & 0xffff0000) | data); // low
|
||||
}
|
||||
|
||||
uint16_t k573dio_device::mpeg_key_1_r()
|
||||
|
@ -241,36 +258,32 @@ uint16_t k573dio_device::mpeg_key_1_r()
|
|||
|
||||
void k573dio_device::mpeg_key_1_w(uint16_t data)
|
||||
{
|
||||
logerror("FPGA MPEG key 1/3 %04x\n", data);
|
||||
LOGMP3("FPGA MPEG key 1/3 %04x\n", data);
|
||||
crypto_key1 = data;
|
||||
k573fpga->set_crypto_key1(data);
|
||||
}
|
||||
|
||||
uint16_t k573dio_device::mas_i2c_r()
|
||||
{
|
||||
int scl = mas3507d->i2c_scl_r() << 13;
|
||||
int sda = mas3507d->i2c_sda_r() << 12;
|
||||
|
||||
return scl | sda;
|
||||
return k573fpga->mas_i2c_r();
|
||||
}
|
||||
|
||||
void k573dio_device::mas_i2c_w(uint16_t data)
|
||||
{
|
||||
mas3507d->i2c_scl_w(data & 0x2000);
|
||||
mas3507d->i2c_sda_w(data & 0x1000);
|
||||
k573fpga->mas_i2c_w(data);
|
||||
}
|
||||
|
||||
uint16_t k573dio_device::mpeg_ctrl_r()
|
||||
{
|
||||
if (k573fpga->get_mpeg_ctrl() == 0x1000 && !k573fpga->is_playing()) {
|
||||
// Set the FPGA to stop mode so that data won't be sent anymore
|
||||
k573fpga->set_mpeg_ctrl(0xa000);
|
||||
}
|
||||
|
||||
return k573fpga->get_mpeg_ctrl();
|
||||
}
|
||||
|
||||
void k573dio_device::mpeg_ctrl_w(uint16_t data)
|
||||
uint16_t k573dio_device::fpga_ctrl_r()
|
||||
{
|
||||
return k573fpga->get_fpga_ctrl();
|
||||
}
|
||||
|
||||
void k573dio_device::fpga_ctrl_w(uint16_t data)
|
||||
{
|
||||
k573fpga->set_mpeg_ctrl(data);
|
||||
}
|
||||
|
@ -312,14 +325,25 @@ void k573dio_device::ram_read_adr_low_w(uint16_t data)
|
|||
ram_read_adr = ((ram_read_adr & 0xffff0000) | data) & 0x1ffffff;
|
||||
}
|
||||
|
||||
uint16_t k573dio_device::mp3_frame_count_high_r()
|
||||
uint16_t k573dio_device::mp3_counter_high_r()
|
||||
{
|
||||
return (mas3507d->get_frame_count() & 0xffff0000) >> 16;
|
||||
return (k573fpga->get_counter() & 0xffff0000) >> 16;
|
||||
}
|
||||
|
||||
uint16_t k573dio_device::mp3_frame_count_low_r()
|
||||
uint16_t k573dio_device::mp3_counter_low_r()
|
||||
{
|
||||
return mas3507d->get_frame_count() & 0x0000ffff;
|
||||
return k573fpga->get_counter() & 0x0000ffff;
|
||||
}
|
||||
|
||||
void k573dio_device::mp3_counter_low_w(uint16_t data)
|
||||
{
|
||||
LOGMP3("mp3_counter_low_w %04x\n", data);
|
||||
k573fpga->reset_counter();
|
||||
}
|
||||
|
||||
uint16_t k573dio_device::mp3_counter_diff_r()
|
||||
{
|
||||
return k573fpga->get_counter_diff() & 0x0000ffff;
|
||||
}
|
||||
|
||||
void k573dio_device::output_1_w(uint16_t data)
|
||||
|
@ -344,13 +368,13 @@ void k573dio_device::output_7_w(uint16_t data)
|
|||
|
||||
void k573dio_device::mpeg_key_2_w(uint16_t data)
|
||||
{
|
||||
logerror("FPGA MPEG key 2/3 %04x\n", data);
|
||||
LOGMP3("FPGA MPEG key 2/3 %04x\n", data);
|
||||
k573fpga->set_crypto_key2(data);
|
||||
}
|
||||
|
||||
void k573dio_device::mpeg_key_3_w(uint16_t data)
|
||||
{
|
||||
logerror("FPGA MPEG key 3/3 %04x\n", data);
|
||||
LOGMP3("FPGA MPEG key 3/3 %04x\n", data);
|
||||
k573fpga->set_crypto_key3(data);
|
||||
}
|
||||
|
||||
|
@ -366,7 +390,7 @@ void k573dio_device::digital_id_w(uint16_t data)
|
|||
|
||||
uint16_t k573dio_device::fpga_status_r()
|
||||
{
|
||||
//logerror("%s: fpga_status_r (%s)\n", tag(), machine().describe_context());
|
||||
LOGFPGA("%s: fpga_status_r (%s)\n", tag(), machine().describe_context());
|
||||
|
||||
// fpga/digital board status checks
|
||||
// wants & c000 = 8000 (just after program upload?)
|
||||
|
@ -400,11 +424,6 @@ void k573dio_device::output_2_w(uint16_t data)
|
|||
output(2, data);
|
||||
}
|
||||
|
||||
uint16_t k573dio_device::mp3_unk_r()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void k573dio_device::output(int offset, uint16_t data)
|
||||
{
|
||||
data = (data >> 12) & 0x0f;
|
||||
|
|
|
@ -33,18 +33,21 @@ public:
|
|||
void mpeg_end_adr_low_w(uint16_t data);
|
||||
uint16_t mpeg_key_1_r();
|
||||
void mpeg_key_1_w(uint16_t data);
|
||||
uint16_t mpeg_ctrl_r();
|
||||
uint16_t mas_i2c_r();
|
||||
void mas_i2c_w(uint16_t data);
|
||||
uint16_t mpeg_ctrl_r();
|
||||
void mpeg_ctrl_w(uint16_t data);
|
||||
uint16_t fpga_ctrl_r();
|
||||
void fpga_ctrl_w(uint16_t data);
|
||||
void ram_write_adr_high_w(uint16_t data);
|
||||
void ram_write_adr_low_w(uint16_t data);
|
||||
uint16_t ram_r();
|
||||
void ram_w(uint16_t data);
|
||||
void ram_read_adr_high_w(uint16_t data);
|
||||
void ram_read_adr_low_w(uint16_t data);
|
||||
uint16_t mp3_frame_count_high_r();
|
||||
uint16_t mp3_frame_count_low_r();
|
||||
uint16_t mp3_counter_high_r();
|
||||
uint16_t mp3_counter_low_r();
|
||||
void mp3_counter_low_w(uint16_t data);
|
||||
uint16_t mp3_counter_diff_r();
|
||||
void output_0_w(uint16_t data);
|
||||
void output_1_w(uint16_t data);
|
||||
void output_7_w(uint16_t data);
|
||||
|
@ -58,7 +61,6 @@ public:
|
|||
void output_4_w(uint16_t data);
|
||||
void output_2_w(uint16_t data);
|
||||
void output_5_w(uint16_t data);
|
||||
uint16_t mp3_unk_r();
|
||||
|
||||
protected:
|
||||
virtual void device_start() override;
|
||||
|
@ -68,12 +70,11 @@ protected:
|
|||
virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override;
|
||||
|
||||
private:
|
||||
memory_share_creator<uint16_t> ram;
|
||||
required_device<k573fpga_device> k573fpga;
|
||||
required_device<ds2401_device> digital_id;
|
||||
required_device<mas3507d_device> mas3507d;
|
||||
devcb_write8 output_cb;
|
||||
|
||||
std::unique_ptr<uint16_t[]> ram;
|
||||
uint32_t ram_adr, ram_read_adr;
|
||||
uint8_t output_data[8];
|
||||
|
||||
|
|
|
@ -5,50 +5,185 @@
|
|||
|
||||
#include "k573fpga.h"
|
||||
|
||||
#define LOG_GENERAL (1 << 0)
|
||||
#define VERBOSE (LOG_GENERAL)
|
||||
// #define LOG_OUTPUT_STREAM std::cout
|
||||
|
||||
k573fpga_device::k573fpga_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) :
|
||||
#include "logmacro.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),
|
||||
ram(*this, finder_base::DUMMY_TAG),
|
||||
mas3507d(*this, "mpeg"),
|
||||
use_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));
|
||||
}
|
||||
|
||||
void k573fpga_device::device_start()
|
||||
{
|
||||
save_item(NAME(crypto_key1));
|
||||
save_item(NAME(crypto_key2));
|
||||
save_item(NAME(crypto_key3));
|
||||
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(counter_current));
|
||||
save_item(NAME(last_playback_status));
|
||||
}
|
||||
|
||||
void k573fpga_device::device_reset()
|
||||
{
|
||||
mp3_cur_adr = 0;
|
||||
mp3_end_adr = 0;
|
||||
mp3_start_addr = 0;
|
||||
mp3_cur_addr = 0;
|
||||
mp3_end_addr = 0;
|
||||
|
||||
crypto_key1 = 0;
|
||||
crypto_key2 = 0;
|
||||
crypto_key3 = 0;
|
||||
|
||||
is_stream_active = false;
|
||||
is_timer_active = false;
|
||||
|
||||
counter_current = counter_previous = counter_offset = 0;
|
||||
|
||||
mas3507d->reset_playback();
|
||||
last_playback_status = get_mpeg_ctrl();
|
||||
}
|
||||
|
||||
u16 k573fpga_device::get_mpeg_ctrl()
|
||||
{
|
||||
if ((mpeg_ctrl_flag & 0xe000) == 0xe000) {
|
||||
// This has been tested with real hardware, but this flag is always held 0x1000 when the audio is being played
|
||||
return 0x1000;
|
||||
void k573fpga_device::reset_counter() {
|
||||
counter_current = counter_previous = counter_offset = 0;
|
||||
status_update();
|
||||
}
|
||||
|
||||
void k573fpga_device::status_update() {
|
||||
auto cur_playback_status = get_mpeg_ctrl();
|
||||
is_timer_active = is_streaming() || ((cur_playback_status == last_playback_status && last_playback_status > PLAYBACK_STATE_IDLE) || cur_playback_status > last_playback_status);
|
||||
last_playback_status = cur_playback_status;
|
||||
|
||||
if(!is_timer_active)
|
||||
counter_current = counter_previous = counter_offset = 0;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
return 0x0000;
|
||||
return counter_current;
|
||||
}
|
||||
|
||||
void k573fpga_device::set_mpeg_ctrl(u16 data)
|
||||
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;
|
||||
}
|
||||
|
||||
uint16_t k573fpga_device::mas_i2c_r()
|
||||
{
|
||||
logerror("FPGA MPEG control %c%c%c | %08x %08x\n",
|
||||
uint16_t scl = mas3507d->i2c_scl_r() << 13;
|
||||
uint16_t sda = mas3507d->i2c_sda_r() << 12;
|
||||
return scl | sda;
|
||||
}
|
||||
|
||||
void k573fpga_device::mas_i2c_w(uint16_t data)
|
||||
{
|
||||
mas3507d->i2c_scl_w(data & 0x2000);
|
||||
mas3507d->i2c_sda_w(data & 0x1000);
|
||||
}
|
||||
|
||||
uint16_t k573fpga_device::get_mpeg_ctrl()
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
bool k573fpga_device::is_mp3_playing()
|
||||
{
|
||||
return get_mpeg_ctrl() > PLAYBACK_STATE_IDLE;
|
||||
}
|
||||
|
||||
uint16_t k573fpga_device::get_fpga_ctrl()
|
||||
{
|
||||
// 0x0000 Not Streaming
|
||||
// 0x1000 Streaming
|
||||
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)
|
||||
{
|
||||
LOG("FPGA MPEG control %c%c%c | %04x\n",
|
||||
data & 0x8000 ? '#' : '.',
|
||||
data & 0x4000 ? '#' : '.',
|
||||
data & 0x4000 ? '#' : '.', // "Active" flag. The FPGA will never start streaming data without this bit set
|
||||
data & 0x2000 ? '#' : '.',
|
||||
mp3_cur_adr, mp3_end_adr);
|
||||
data);
|
||||
|
||||
mpeg_ctrl_flag = 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
u16 k573fpga_device::decrypt_default(u16 v)
|
||||
uint16_t k573fpga_device::decrypt_default(uint16_t v)
|
||||
{
|
||||
u16 m = crypto_key1 ^ crypto_key2;
|
||||
uint16_t m = crypto_key1 ^ crypto_key2;
|
||||
|
||||
v = bitswap<16>(
|
||||
v,
|
||||
|
@ -80,7 +215,7 @@ u16 k573fpga_device::decrypt_default(u16 v)
|
|||
(BIT(m, 0x0) << 0);
|
||||
|
||||
v ^= bitswap<16>(
|
||||
(u16)crypto_key3,
|
||||
(uint16_t)crypto_key3,
|
||||
7, 0, 6, 1,
|
||||
5, 2, 4, 3,
|
||||
3, 4, 2, 5,
|
||||
|
@ -97,10 +232,10 @@ u16 k573fpga_device::decrypt_default(u16 v)
|
|||
return v;
|
||||
}
|
||||
|
||||
u16 k573fpga_device::decrypt_ddrsbm(u16 data)
|
||||
uint16_t k573fpga_device::decrypt_ddrsbm(uint16_t data)
|
||||
{
|
||||
u8 key[16] = {0};
|
||||
u16 key_state = bitswap<16>(
|
||||
uint8_t key[16] = {0};
|
||||
uint16_t key_state = bitswap<16>(
|
||||
crypto_key1,
|
||||
13, 11, 9, 7,
|
||||
5, 3, 1, 15,
|
||||
|
@ -114,7 +249,7 @@ u16 k573fpga_device::decrypt_ddrsbm(u16 data)
|
|||
key_state = ((key_state & 0x8080) >> 7) | ((key_state & 0x7f7f) << 1);
|
||||
}
|
||||
|
||||
u16 output_word = 0;
|
||||
uint16_t output_word = 0;
|
||||
for(int cur_bit = 0; cur_bit < 8; cur_bit++) {
|
||||
int even_bit_shift = cur_bit * 2;
|
||||
int odd_bit_shift = cur_bit * 2 + 1;
|
||||
|
@ -138,15 +273,16 @@ u16 k573fpga_device::decrypt_ddrsbm(u16 data)
|
|||
return output_word;
|
||||
}
|
||||
|
||||
u16 k573fpga_device::get_decrypted()
|
||||
uint16_t k573fpga_device::get_decrypted()
|
||||
{
|
||||
if(mp3_cur_adr >= mp3_end_adr || (mpeg_ctrl_flag & 0xe000) != 0xe000) {
|
||||
if(!is_streaming()) {
|
||||
is_stream_active = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
u16 src = ram[mp3_cur_adr >> 1];
|
||||
u16 result = use_ddrsbm_fpga ? decrypt_ddrsbm(src) : decrypt_default(src);
|
||||
mp3_cur_adr += 2;
|
||||
uint16_t src = ram[mp3_cur_addr >> 1];
|
||||
uint16_t result = use_ddrsbm_fpga ? decrypt_ddrsbm(src) : decrypt_default(src);
|
||||
mp3_cur_addr += 2;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -13,46 +13,72 @@ 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, u32 clock = 0);
|
||||
k573fpga_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0);
|
||||
|
||||
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_ram(u16 *v) { ram = v; }
|
||||
u16 get_decrypted();
|
||||
uint16_t get_decrypted();
|
||||
|
||||
void set_crypto_key1(u16 v) { crypto_key1 = v; }
|
||||
void set_crypto_key2(u16 v) { crypto_key2 = v; }
|
||||
void set_crypto_key3(u8 v) { crypto_key3 = v; }
|
||||
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; }
|
||||
|
||||
uint32_t get_mp3_cur_adr() { return mp3_cur_adr; }
|
||||
void set_mp3_cur_adr(u32 v) { mp3_cur_adr = v; }
|
||||
uint32_t get_mp3_start_addr() { return mp3_start_addr; }
|
||||
void set_mp3_start_addr(uint32_t v) { mp3_start_addr = v; }
|
||||
|
||||
uint32_t get_mp3_end_adr() { return mp3_end_adr; }
|
||||
void set_mp3_end_adr(u32 v) { mp3_end_adr = v; }
|
||||
uint32_t get_mp3_end_addr() { return mp3_end_addr; }
|
||||
void set_mp3_end_addr(uint32_t v) { mp3_end_addr = v; }
|
||||
|
||||
u16 i2c_read();
|
||||
void i2c_write(u16 data);
|
||||
uint16_t mas_i2c_r();
|
||||
void mas_i2c_w(uint16_t data);
|
||||
|
||||
u16 get_mpeg_ctrl();
|
||||
void set_mpeg_ctrl(u16 data);
|
||||
uint16_t get_fpga_ctrl();
|
||||
void set_mpeg_ctrl(uint16_t data);
|
||||
|
||||
bool is_playing() { return (mpeg_ctrl_flag & 0xe000) == 0xe000 && mp3_cur_adr < mp3_end_adr; }
|
||||
uint16_t get_mpeg_ctrl();
|
||||
|
||||
uint32_t get_counter();
|
||||
uint32_t get_counter_diff();
|
||||
|
||||
void status_update();
|
||||
void reset_counter();
|
||||
|
||||
protected:
|
||||
virtual void device_start() override;
|
||||
virtual void device_reset() override;
|
||||
virtual void device_add_mconfig(machine_config &config) override;
|
||||
|
||||
private:
|
||||
u16 *ram;
|
||||
uint16_t decrypt_default(uint16_t data);
|
||||
uint16_t decrypt_ddrsbm(uint16_t data);
|
||||
|
||||
u16 crypto_key1, crypto_key2;
|
||||
u8 crypto_key3;
|
||||
bool is_mp3_playing();
|
||||
bool is_streaming();
|
||||
|
||||
u32 mp3_cur_adr, mp3_end_adr, mpeg_ctrl_flag;
|
||||
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?
|
||||
};
|
||||
|
||||
required_shared_ptr<uint16_t> ram;
|
||||
required_device<mas3507d_device> mas3507d;
|
||||
|
||||
uint16_t crypto_key1, crypto_key2;
|
||||
uint8_t crypto_key3;
|
||||
|
||||
uint32_t mp3_start_addr, mp3_cur_addr, mp3_end_addr;
|
||||
bool use_ddrsbm_fpga;
|
||||
|
||||
u16 decrypt_default(u16 data);
|
||||
u16 decrypt_ddrsbm(u16 data);
|
||||
bool is_stream_active, is_timer_active;
|
||||
uint32_t counter_previous, counter_offset;
|
||||
int32_t counter_current;
|
||||
uint32_t last_playback_status;
|
||||
};
|
||||
|
||||
#endif // MAME_MACHINE_K573FPGA_H
|
||||
|
|
Loading…
Reference in a new issue