mirror of
https://github.com/mamedev/mame.git
synced 2024-11-16 07:48:32 +01:00
namco/namcos10.cpp: Add MP3 decoder support to the MEM(P3) board. (#11210)
* 3rdparty/minimp3: Update to latest source (afb604c06bc8beb145fecd42c0ceb5bda8795144). * sound/mp3_audio.cpp: Add helper class to decode MP3 frame data, abstracting away minimp3 from devices. * sound/lc82310.cpp: Added basic Sanyo LC82310 MP3 decoder emulation. * namco/namcos10.cpp: Fixed light gun inputs for Golgo 13: Juusei no Requiem. Systems promoted to working ------------------ Golgo 13: Juusei no Requiem (Japan, GLT1 VER.A) Tsukkomi Yousei Gips Nice Tsukkomi (NTK1 Ver.A) Seishun-Quiz Colorful High School (CHS1 Ver.A)
This commit is contained in:
parent
f64d568f8e
commit
fb2b5745d1
11 changed files with 833 additions and 1498 deletions
24
3rdparty/minimp3/minimp3.h
vendored
24
3rdparty/minimp3/minimp3.h
vendored
|
@ -881,12 +881,22 @@ static void L3_midside_stereo(float *left, int n)
|
|||
int i = 0;
|
||||
float *right = left + 576;
|
||||
#if HAVE_SIMD
|
||||
if (have_simd()) for (; i < n - 3; i += 4)
|
||||
if (have_simd())
|
||||
{
|
||||
f4 vl = VLD(left + i);
|
||||
f4 vr = VLD(right + i);
|
||||
VSTORE(left + i, VADD(vl, vr));
|
||||
VSTORE(right + i, VSUB(vl, vr));
|
||||
for (; i < n - 3; i += 4)
|
||||
{
|
||||
f4 vl = VLD(left + i);
|
||||
f4 vr = VLD(right + i);
|
||||
VSTORE(left + i, VADD(vl, vr));
|
||||
VSTORE(right + i, VSUB(vl, vr));
|
||||
}
|
||||
#ifdef __GNUC__
|
||||
/* Workaround for spurious -Waggressive-loop-optimizations warning from gcc.
|
||||
* For more info see: https://github.com/lieff/minimp3/issues/88
|
||||
*/
|
||||
if (__builtin_constant_p(n % 4 == 0) && n % 4 == 0)
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
#endif /* HAVE_SIMD */
|
||||
for (; i < n; i++)
|
||||
|
@ -1353,7 +1363,7 @@ static void mp3d_DCT_II(float *grbuf, int n)
|
|||
} else
|
||||
#endif /* HAVE_SIMD */
|
||||
#ifdef MINIMP3_ONLY_SIMD
|
||||
{}
|
||||
{} /* for HAVE_SIMD=1, MINIMP3_ONLY_SIMD=1 case we do not need non-intrinsic "else" branch */
|
||||
#else /* MINIMP3_ONLY_SIMD */
|
||||
for (; k < n; k++)
|
||||
{
|
||||
|
@ -1583,7 +1593,7 @@ static void mp3d_synth(float *xl, mp3d_sample_t *dstl, int nch, float *lins)
|
|||
} else
|
||||
#endif /* HAVE_SIMD */
|
||||
#ifdef MINIMP3_ONLY_SIMD
|
||||
{}
|
||||
{} /* for HAVE_SIMD=1, MINIMP3_ONLY_SIMD=1 case we do not need non-intrinsic "else" branch */
|
||||
#else /* MINIMP3_ONLY_SIMD */
|
||||
for (i = 14; i >= 0; i--)
|
||||
{
|
||||
|
|
1394
3rdparty/minimp3/minimp3_ex.h
vendored
1394
3rdparty/minimp3/minimp3_ex.h
vendored
File diff suppressed because it is too large
Load diff
|
@ -1286,6 +1286,18 @@ if (SOUNDS["MULTIPCM"]~=null) then
|
|||
}
|
||||
end
|
||||
|
||||
---------------------------------------------------
|
||||
-- MP3 AUDIO
|
||||
--@src/devices/sound/mp3_audio.h,SOUNDS["MP3_AUDIO"] = true
|
||||
---------------------------------------------------
|
||||
|
||||
if (SOUNDS["MP3_AUDIO"]~=null) then
|
||||
files {
|
||||
MAME_DIR .. "src/devices/sound/mp3_audio.cpp",
|
||||
MAME_DIR .. "src/devices/sound/mp3_audio.h",
|
||||
}
|
||||
end
|
||||
|
||||
---------------------------------------------------
|
||||
-- MPEG AUDIO
|
||||
--@src/devices/sound/mpeg_audio.h,SOUNDS["MPEG_AUDIO"] = true
|
||||
|
@ -1429,6 +1441,18 @@ if (SOUNDS["LC7535"]~=null) then
|
|||
}
|
||||
end
|
||||
|
||||
---------------------------------------------------
|
||||
-- Sanyo LC82310
|
||||
--@src/devices/sound/lc82310.h,SOUNDS["LC82310"] = true
|
||||
---------------------------------------------------
|
||||
|
||||
if (SOUNDS["LC82310"]~=null) then
|
||||
files {
|
||||
MAME_DIR .. "src/devices/sound/lc82310.cpp",
|
||||
MAME_DIR .. "src/devices/sound/lc82310.h",
|
||||
}
|
||||
end
|
||||
|
||||
---------------------------------------------------
|
||||
-- NEC uPD934G
|
||||
--@src/devices/sound/upd934g.h,SOUNDS["UPD934G"] = true
|
||||
|
|
|
@ -583,6 +583,10 @@ void tmp95c061_device::tlcs900_handle_ad()
|
|||
|
||||
m_int_reg[TMP95C061_INTE0AD] |= 0x80;
|
||||
m_check_irqs = 1;
|
||||
|
||||
/* AD repeat mode */
|
||||
if ( m_ad_mode & 0x20 )
|
||||
m_ad_cycles_left = ( m_ad_mode & 0x08 ) ? 320 : 160;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1397,7 +1401,7 @@ void tmp95c061_device::admod_w(uint8_t data)
|
|||
{
|
||||
data &= ~0x04;
|
||||
data |= 0x40;
|
||||
m_ad_cycles_left = ( data & 0x08 ) ? 640 : 320;
|
||||
m_ad_cycles_left = ( data & 0x08 ) ? 320 : 160;
|
||||
}
|
||||
|
||||
m_ad_mode = data;
|
||||
|
|
297
src/devices/sound/lc82310.cpp
Normal file
297
src/devices/sound/lc82310.cpp
Normal file
|
@ -0,0 +1,297 @@
|
|||
// license:BSD-3-Clause
|
||||
// copyright-holders:windyfairy
|
||||
/**********************************************************************
|
||||
|
||||
Sanyo LC82310 MP3 decoder
|
||||
|
||||
**********************************************************************/
|
||||
|
||||
#include "emu.h"
|
||||
#include "lc82310.h"
|
||||
#include "mp3_audio.h"
|
||||
|
||||
DEFINE_DEVICE_TYPE(LC82310, lc82310_device, "lc82310", "Sanyo LC82310 MP3 Decoder")
|
||||
|
||||
lc82310_device::lc82310_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
|
||||
: device_t(mconfig, LC82310, tag, owner, clock)
|
||||
, device_sound_interface(mconfig, *this)
|
||||
{
|
||||
}
|
||||
|
||||
void lc82310_device::device_start()
|
||||
{
|
||||
stream = stream_alloc(0, 2, 44100);
|
||||
mp3dec = std::make_unique<mp3_audio>(reinterpret_cast<const uint8_t *>(&mp3data[0]));
|
||||
|
||||
save_item(NAME(mp3data));
|
||||
save_item(NAME(samples));
|
||||
save_item(NAME(m_mp3data_count));
|
||||
save_item(NAME(m_sample_count));
|
||||
save_item(NAME(m_samples_idx));
|
||||
save_item(NAME(m_frame_channels));
|
||||
save_item(NAME(m_output_gain));
|
||||
|
||||
save_item(NAME(m_csctl));
|
||||
save_item(NAME(m_ckctl));
|
||||
save_item(NAME(m_dictl));
|
||||
save_item(NAME(m_doctl));
|
||||
save_item(NAME(m_ctl_bits));
|
||||
save_item(NAME(m_ctl_byte));
|
||||
save_item(NAME(m_ctl_out_byte));
|
||||
}
|
||||
|
||||
void lc82310_device::device_reset()
|
||||
{
|
||||
std::fill(std::begin(m_output_gain), std::end(m_output_gain), 0);
|
||||
|
||||
m_csctl = 0;
|
||||
m_ckctl = 0;
|
||||
m_dictl = 0;
|
||||
m_doctl = 0;
|
||||
m_ctl_bits = 0;
|
||||
m_ctl_byte = 0;
|
||||
m_ctl_out_byte = 0;
|
||||
m_ctl_state = ACCEPTING_CMD;
|
||||
|
||||
reset_playback();
|
||||
}
|
||||
|
||||
void lc82310_device::reset_playback()
|
||||
{
|
||||
std::fill(mp3data.begin(), mp3data.end(), 0);
|
||||
std::fill(samples.begin(), samples.end(), 0);
|
||||
|
||||
mp3dec->clear();
|
||||
m_mp3data_count = 0;
|
||||
m_sample_count = 0;
|
||||
m_samples_idx = 0;
|
||||
m_frame_channels = 2;
|
||||
}
|
||||
|
||||
WRITE_LINE_MEMBER(lc82310_device::zcsctl_w)
|
||||
{
|
||||
m_csctl = state;
|
||||
}
|
||||
|
||||
WRITE_LINE_MEMBER(lc82310_device::ckctl_w)
|
||||
{
|
||||
if (m_csctl == 0 && m_ckctl == 0 && state == 1)
|
||||
{
|
||||
m_ctl_byte |= m_dictl << m_ctl_bits;
|
||||
m_ctl_bits++;
|
||||
|
||||
if (m_ctl_bits > 7)
|
||||
{
|
||||
if (m_ctl_state == ACCEPTING_CMD)
|
||||
{
|
||||
// Expected to be able to read the return value while sending the second byte
|
||||
// Everything in the 0x80 range of commands seems to respond with a value
|
||||
// 0x80, 0x81, 0x82 return separate 8-bit values
|
||||
// 0x83 returns an 8-bit error status, bits 0 and 1 are used to signal errors, any non-0 bit is considered an error
|
||||
// 0x84 and 0x85 are used together to form a 16-bit value
|
||||
m_ctl_out_byte = 0;
|
||||
|
||||
m_ctl_cmd = m_ctl_byte;
|
||||
m_ctl_state = ACCEPTING_PARAM;
|
||||
}
|
||||
else if (m_ctl_state == ACCEPTING_PARAM)
|
||||
{
|
||||
handle_command(m_ctl_cmd, m_ctl_byte);
|
||||
m_ctl_state = ACCEPTING_CMD;
|
||||
}
|
||||
|
||||
m_ctl_byte = 0;
|
||||
m_ctl_bits = 0;
|
||||
}
|
||||
|
||||
m_doctl = m_ctl_out_byte & 1;
|
||||
m_ctl_out_byte >>= 1;
|
||||
}
|
||||
|
||||
m_ckctl = state;
|
||||
}
|
||||
|
||||
WRITE_LINE_MEMBER(lc82310_device::dictl_w)
|
||||
{
|
||||
m_dictl = state;
|
||||
}
|
||||
|
||||
READ_LINE_MEMBER(lc82310_device::doctl_r)
|
||||
{
|
||||
return m_doctl;
|
||||
}
|
||||
|
||||
READ_LINE_MEMBER(lc82310_device::demand_r)
|
||||
{
|
||||
return m_mp3data_count < mp3data.size();
|
||||
}
|
||||
|
||||
void lc82310_device::dimpg_w(uint8_t data)
|
||||
{
|
||||
if (m_mp3data_count >= mp3data.size())
|
||||
{
|
||||
// Drop a byte if the buffer is full and it's still trying to send data
|
||||
std::copy(mp3data.begin() + 1, mp3data.end(), mp3data.begin());
|
||||
m_mp3data_count--;
|
||||
}
|
||||
|
||||
mp3data[m_mp3data_count++] = data;
|
||||
}
|
||||
|
||||
void lc82310_device::handle_command(uint8_t cmd, uint8_t param)
|
||||
{
|
||||
if (cmd == CMD_UNK13_VOL || cmd == CMD_UNK15_VOL)
|
||||
{
|
||||
// These are calculated based on the configurable values in-game vs what is sent to the MP3 decoder
|
||||
constexpr float gain_table[] = {
|
||||
1.0, // 0
|
||||
30.0 / 31.0, // 1
|
||||
29.0 / 31.0, // 2
|
||||
28.0 / 31.0, // 3
|
||||
27.0 / 31.0, // 4
|
||||
26.0 / 31.0, // 5
|
||||
25.0 / 31.0, // 6
|
||||
24.0 / 31.0, // 7
|
||||
23.0 / 31.0, // 8
|
||||
22.0 / 31.0, // 9
|
||||
21.0 / 31.0, // 10
|
||||
20.0 / 31.0, // 11
|
||||
19.0 / 31.0, // 12
|
||||
18.0 / 31.0, // 13
|
||||
17.0 / 31.0, // 14
|
||||
16.0 / 31.0, // 15
|
||||
15.0 / 31.0, // 16
|
||||
14.0 / 31.0, // 17
|
||||
13.0 / 31.0, // 18
|
||||
12.0 / 31.0, // 19
|
||||
11.0 / 31.0, // 20
|
||||
10.0 / 31.0, // 21
|
||||
9.0 / 31.0, // 22
|
||||
8.5 / 31.0, // 23
|
||||
8.0 / 31.0, // 24
|
||||
7.0 / 31.0, // 25
|
||||
6.0 / 31.0, // 25
|
||||
5.5 / 31.0, // 27
|
||||
5.0 / 31.0, // 28
|
||||
4.5 / 31.0, // 29
|
||||
4.0 / 31.0, // 30
|
||||
3.5 / 31.0, // 31
|
||||
3.0 / 31.0, // 32
|
||||
2.75 / 31.0, // 33
|
||||
2.5 / 31.0, // 34
|
||||
2.25 / 31.0, // 35
|
||||
2.0 / 31.0, // 36
|
||||
(1.0 + (1.0 / 6) * 5) / 31.0, // 37
|
||||
(1.0 + (1.0 / 6) * 4) / 31.0, // 38
|
||||
(1.0 + (1.0 / 6) * 3) / 31.0, // 39
|
||||
(1.0 + (1.0 / 6) * 2) / 31.0, // 40
|
||||
(1.0 + (1.0 / 6) * 1) / 31.0, // 41
|
||||
1.0 / 31.0, // 42
|
||||
((1.0 / 34) * 33) / 31.0, // 43
|
||||
((1.0 / 34) * 32) / 31.0, // 44
|
||||
((1.0 / 34) * 31) / 31.0, // 45
|
||||
((1.0 / 34) * 30) / 31.0, // 46
|
||||
((1.0 / 34) * 29) / 31.0, // 47
|
||||
((1.0 / 34) * 28) / 31.0, // 48
|
||||
((1.0 / 34) * 27) / 31.0, // 49
|
||||
((1.0 / 34) * 26) / 31.0, // 50
|
||||
((1.0 / 34) * 25) / 31.0, // 51
|
||||
((1.0 / 34) * 24) / 31.0, // 52
|
||||
((1.0 / 34) * 23) / 31.0, // 53
|
||||
((1.0 / 34) * 22) / 31.0, // 54
|
||||
((1.0 / 34) * 21) / 31.0, // 55
|
||||
((1.0 / 34) * 20) / 31.0, // 56
|
||||
((1.0 / 34) * 19) / 31.0, // 57
|
||||
((1.0 / 34) * 18) / 31.0, // 58
|
||||
((1.0 / 34) * 17) / 31.0, // 59
|
||||
((1.0 / 34) * 16) / 31.0, // 60
|
||||
((1.0 / 34) * 15) / 31.0, // 61
|
||||
((1.0 / 34) * 14) / 31.0, // 62
|
||||
((1.0 / 34) * 13) / 31.0, // 63
|
||||
((1.0 / 34) * 12) / 31.0, // 64
|
||||
((1.0 / 34) * 11) / 31.0, // 65
|
||||
((1.0 / 34) * 10) / 31.0, // 66
|
||||
((1.0 / 34) * 9) / 31.0, // 67
|
||||
((1.0 / 34) * 8) / 31.0, // 68
|
||||
((1.0 / 34) * 7) / 31.0, // 69
|
||||
((1.0 / 34) * 6) / 31.0, // 70
|
||||
((1.0 / 34) * 5) / 31.0, // 71
|
||||
((1.0 / 34) * 4) / 31.0, // 72
|
||||
((1.0 / 34) * 3) / 31.0, // 73
|
||||
((1.0 / 34) * 2) / 31.0, // 74
|
||||
((1.0 / 34) * 1) / 31.0, // 75
|
||||
0.0, // 76
|
||||
};
|
||||
|
||||
int speaker_idx = cmd == CMD_UNK15_VOL ? 1 : 0; // guessed, both are set at the same time in current use cases
|
||||
m_output_gain[speaker_idx] = gain_table[std::min<uint8_t>(param, 0x4c)];
|
||||
|
||||
set_output_gain(speaker_idx, m_output_gain[speaker_idx]);
|
||||
}
|
||||
}
|
||||
|
||||
void lc82310_device::fill_buffer()
|
||||
{
|
||||
int pos = 0, frame_sample_rate = 0;
|
||||
bool decoded_frame = mp3dec->decode_buffer(pos, m_mp3data_count, &samples[0], m_sample_count, frame_sample_rate, m_frame_channels);
|
||||
m_samples_idx = 0;
|
||||
|
||||
if (!decoded_frame || m_sample_count <= 0)
|
||||
{
|
||||
// Frame decode failed
|
||||
if (m_mp3data_count >= mp3data.size())
|
||||
{
|
||||
std::copy(mp3data.begin() + 1, mp3data.end(), mp3data.begin());
|
||||
m_mp3data_count--;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
std::copy(mp3data.begin() + pos, mp3data.end(), mp3data.begin());
|
||||
m_mp3data_count -= pos;
|
||||
|
||||
stream->set_sample_rate(frame_sample_rate);
|
||||
}
|
||||
|
||||
void lc82310_device::append_buffer(std::vector<write_stream_view> &outputs, int &pos, int scount)
|
||||
{
|
||||
int s1 = std::min(scount - pos, m_sample_count);
|
||||
int words_per_sample = std::min(m_frame_channels, 2);
|
||||
|
||||
for (int i = 0; i < s1; i++)
|
||||
{
|
||||
outputs[0].put_int(pos, samples[m_samples_idx * words_per_sample], 32768);
|
||||
outputs[1].put_int(pos, samples[m_samples_idx * words_per_sample + (words_per_sample >> 1)], 32768);
|
||||
|
||||
m_samples_idx++;
|
||||
pos++;
|
||||
|
||||
if (m_samples_idx >= m_sample_count)
|
||||
{
|
||||
m_sample_count = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void lc82310_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
|
||||
{
|
||||
int csamples = outputs[0].samples();
|
||||
int pos = 0;
|
||||
|
||||
while (pos < csamples)
|
||||
{
|
||||
if (m_sample_count == 0)
|
||||
fill_buffer();
|
||||
|
||||
if (m_sample_count <= 0)
|
||||
{
|
||||
outputs[0].fill(0, pos);
|
||||
outputs[1].fill(0, pos);
|
||||
return;
|
||||
}
|
||||
|
||||
append_buffer(outputs, pos, csamples);
|
||||
}
|
||||
}
|
92
src/devices/sound/lc82310.h
Normal file
92
src/devices/sound/lc82310.h
Normal file
|
@ -0,0 +1,92 @@
|
|||
// license:BSD-3-Clause
|
||||
// copyright-holders:windyfairy
|
||||
/**********************************************************************
|
||||
|
||||
Sanyo LC82310 MP3 decoder
|
||||
|
||||
**********************************************************************/
|
||||
|
||||
#ifndef MAME_SOUND_LC82310_H
|
||||
#define MAME_SOUND_LC82310_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "mp3_audio.h"
|
||||
|
||||
class lc82310_device : public device_t,
|
||||
public device_sound_interface
|
||||
{
|
||||
public:
|
||||
lc82310_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock = 0);
|
||||
|
||||
DECLARE_WRITE_LINE_MEMBER(zcsctl_w);
|
||||
DECLARE_WRITE_LINE_MEMBER(ckctl_w);
|
||||
DECLARE_WRITE_LINE_MEMBER(dictl_w);
|
||||
DECLARE_READ_LINE_MEMBER(doctl_r);
|
||||
DECLARE_READ_LINE_MEMBER(demand_r);
|
||||
|
||||
void dimpg_w(uint8_t data);
|
||||
|
||||
void reset_playback();
|
||||
|
||||
protected:
|
||||
virtual void device_start() override;
|
||||
virtual void device_reset() override;
|
||||
|
||||
virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
|
||||
|
||||
private:
|
||||
enum : uint8_t
|
||||
{
|
||||
ACCEPTING_CMD,
|
||||
ACCEPTING_PARAM,
|
||||
};
|
||||
|
||||
enum : uint8_t
|
||||
{
|
||||
CMD_UNK10 = 0x10,
|
||||
CMD_UNK11 = 0x11,
|
||||
CMD_UNK12 = 0x12, // Set to 0 when writing data and 1 when not writing data
|
||||
CMD_UNK13_VOL = 0x13,
|
||||
CMD_UNK15_VOL = 0x15,
|
||||
CMD_UNK17 = 0x17,
|
||||
CMD_UNK18 = 0x18,
|
||||
CMD_SET_CONFIGURATION = 0x22, // has PLLOFF and SLEEP bits
|
||||
CMD_UNK80 = 0x80,
|
||||
CMD_UNK81 = 0x81,
|
||||
CMD_UNK82 = 0x82,
|
||||
CMD_GET_ERROR_STATUS = 0x83,
|
||||
CMD_UNK84 = 0x84,
|
||||
CMD_UNK85 = 0x85,
|
||||
};
|
||||
|
||||
void handle_command(uint8_t cmd, uint8_t param);
|
||||
|
||||
void fill_buffer();
|
||||
void append_buffer(std::vector<write_stream_view> &outputs, int &pos, int scount);
|
||||
|
||||
sound_stream *stream;
|
||||
std::unique_ptr<mp3_audio> mp3dec;
|
||||
|
||||
std::array<uint8_t, 0x1000> mp3data;
|
||||
std::array<short, 1152*2> samples;
|
||||
|
||||
uint32_t m_mp3data_count;
|
||||
int32_t m_sample_count, m_samples_idx;
|
||||
int32_t m_frame_channels;
|
||||
float m_output_gain[2];
|
||||
|
||||
uint8_t m_csctl;
|
||||
uint8_t m_ckctl;
|
||||
uint8_t m_dictl;
|
||||
uint8_t m_doctl;
|
||||
uint8_t m_ctl_state;
|
||||
uint8_t m_ctl_cmd;
|
||||
uint8_t m_ctl_bits;
|
||||
uint8_t m_ctl_byte;
|
||||
uint8_t m_ctl_out_byte;
|
||||
};
|
||||
|
||||
DECLARE_DEVICE_TYPE(LC82310, lc82310_device)
|
||||
|
||||
#endif // MAME_SOUND_LC82310_H
|
|
@ -13,13 +13,7 @@
|
|||
|
||||
#include "emu.h"
|
||||
#include "mas3507d.h"
|
||||
|
||||
#define MINIMP3_ONLY_MP3
|
||||
#define MINIMP3_NO_STDIO
|
||||
#define MINIMP3_IMPLEMENTATION
|
||||
#define MAX_FRAME_SYNC_MATCHES 3
|
||||
#include "minimp3/minimp3.h"
|
||||
#include "minimp3/minimp3_ex.h"
|
||||
#include "mp3_audio.h"
|
||||
|
||||
#define LOG_GENERAL (1 << 0)
|
||||
#define LOG_READ (1 << 1)
|
||||
|
@ -59,6 +53,7 @@ mas3507d_device::mas3507d_device(const machine_config &mconfig, const char *tag,
|
|||
void mas3507d_device::device_start()
|
||||
{
|
||||
stream = stream_alloc(0, 2, 44100);
|
||||
mp3dec = std::make_unique<mp3_audio>(reinterpret_cast<const uint8_t *>(&mp3data[0]));
|
||||
|
||||
cb_mpeg_frame_sync.resolve();
|
||||
cb_demand.resolve();
|
||||
|
@ -78,7 +73,6 @@ void mas3507d_device::device_start()
|
|||
|
||||
save_item(NAME(mp3data_count));
|
||||
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));
|
||||
|
@ -91,22 +85,8 @@ void mas3507d_device::device_start()
|
|||
save_item(NAME(i2c_io_val));
|
||||
save_item(NAME(i2c_sdao_data));
|
||||
save_item(NAME(playback_status));
|
||||
save_item(NAME(mp3_is_buffered));
|
||||
|
||||
// This should be removed in the future if/when native MP3 decoding is implemented in MAME
|
||||
save_item(NAME(mp3_dec.mdct_overlap));
|
||||
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));
|
||||
save_item(NAME(frame_channels));
|
||||
}
|
||||
|
||||
void mas3507d_device::device_reset()
|
||||
|
@ -121,6 +101,8 @@ void mas3507d_device::device_reset()
|
|||
is_muted = false;
|
||||
gain_ll = gain_rr = 0;
|
||||
|
||||
frame_channels = 2;
|
||||
|
||||
stream->set_sample_rate(44100);
|
||||
|
||||
reset_playback();
|
||||
|
@ -439,50 +421,43 @@ void mas3507d_device::sid_w(uint8_t byte)
|
|||
|
||||
mp3data[mp3data_count++] = byte;
|
||||
|
||||
if (!mp3_is_buffered) {
|
||||
// Only start the decoder when a full MP3 frame is found
|
||||
int free_format_bytes = 0, frame_size = 0;
|
||||
int frame_offset = mp3d_find_frame(static_cast<const uint8_t *>(&mp3data[0]), mp3data_count, &free_format_bytes, &frame_size);
|
||||
mp3_is_buffered = frame_size && frame_offset + frame_size < mp3data_count;
|
||||
}
|
||||
|
||||
cb_demand(!mp3_is_buffered || mp3data_count < mp3data.size());
|
||||
cb_demand(mp3data_count < mp3data.size());
|
||||
}
|
||||
|
||||
void mas3507d_device::fill_buffer()
|
||||
{
|
||||
cb_mpeg_frame_sync(0);
|
||||
|
||||
if (!mp3_is_buffered) {
|
||||
cb_demand(!mp3_is_buffered || mp3data_count < mp3data.size());
|
||||
return;
|
||||
}
|
||||
|
||||
memset(&mp3_info, 0, sizeof(mp3dec_frame_info_t));
|
||||
sample_count = mp3dec_decode_frame(&mp3_dec, static_cast<const uint8_t *>(&mp3data[0]), mp3data_count, static_cast<mp3d_sample_t *>(&samples[0]), &mp3_info);
|
||||
int pos = 0, frame_sample_rate = 0;
|
||||
bool decoded_frame = mp3dec->decode_buffer(pos, mp3data_count, &samples[0], sample_count, frame_sample_rate, frame_channels);
|
||||
samples_idx = 0;
|
||||
|
||||
if (sample_count == 0) {
|
||||
if (!decoded_frame || sample_count == 0) {
|
||||
// Frame decode failed
|
||||
reset_playback();
|
||||
if (mp3data_count >= mp3data.size()) {
|
||||
std::copy(mp3data.begin() + 1, mp3data.end(), mp3data.begin());
|
||||
mp3data_count--;
|
||||
}
|
||||
|
||||
cb_demand(mp3data_count < mp3data.size());
|
||||
return;
|
||||
}
|
||||
|
||||
std::copy(mp3data.begin() + mp3_info.frame_bytes, mp3data.end(), mp3data.begin());
|
||||
mp3data_count -= mp3_info.frame_bytes;
|
||||
std::copy(mp3data.begin() + pos, mp3data.end(), mp3data.begin());
|
||||
mp3data_count -= pos;
|
||||
|
||||
stream->set_sample_rate(mp3_info.hz);
|
||||
stream->set_sample_rate(frame_sample_rate);
|
||||
|
||||
decoded_frame_count++;
|
||||
cb_mpeg_frame_sync(1);
|
||||
|
||||
cb_demand(!mp3_is_buffered || mp3data_count < mp3data.size());
|
||||
cb_demand(mp3data_count < mp3data.size());
|
||||
}
|
||||
|
||||
void mas3507d_device::append_buffer(std::vector<write_stream_view> &outputs, int &pos, int scount)
|
||||
{
|
||||
int s1 = scount - pos;
|
||||
int bytes_per_sample = mp3_info.channels > 2 ? 2 : mp3_info.channels; // More than 2 channels is unsupported here
|
||||
int bytes_per_sample = std::min(frame_channels, 2); // More than 2 channels is unsupported here
|
||||
|
||||
if(s1 > sample_count)
|
||||
s1 = sample_count;
|
||||
|
@ -492,7 +467,6 @@ void mas3507d_device::append_buffer(std::vector<write_stream_view> &outputs, int
|
|||
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) {
|
||||
|
@ -504,20 +478,14 @@ void mas3507d_device::append_buffer(std::vector<write_stream_view> &outputs, int
|
|||
|
||||
void mas3507d_device::reset_playback()
|
||||
{
|
||||
if (mp3data_count != 0)
|
||||
std::fill(mp3data.begin(), mp3data.end(), 0);
|
||||
std::fill(mp3data.begin(), mp3data.end(), 0);
|
||||
std::fill(samples.begin(), samples.end(), 0);
|
||||
|
||||
if (sample_count != 0 || decoded_samples != 0)
|
||||
std::fill(samples.begin(), samples.end(), 0);
|
||||
|
||||
mp3dec_init(&mp3_dec);
|
||||
mp3dec->clear();
|
||||
mp3data_count = 0;
|
||||
sample_count = 0;
|
||||
decoded_frame_count = 0;
|
||||
decoded_samples = 0;
|
||||
samples_idx = 0;
|
||||
|
||||
mp3_is_buffered = false;
|
||||
}
|
||||
|
||||
void mas3507d_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
|
||||
|
|
|
@ -5,9 +5,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#define MINIMP3_ONLY_MP3
|
||||
#define MINIMP3_NO_STDIO
|
||||
#include "minimp3/minimp3.h"
|
||||
#include "mp3_audio.h"
|
||||
|
||||
class mas3507d_device : public device_t, public device_sound_interface
|
||||
{
|
||||
|
@ -72,13 +70,10 @@ private:
|
|||
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;
|
||||
std::array<short, 1152*2> samples;
|
||||
|
||||
bool i2c_scli, i2c_sclo, i2c_sdai, i2c_sdao;
|
||||
int i2c_bus_curbit;
|
||||
|
@ -87,15 +82,17 @@ private:
|
|||
uint32_t i2c_io_bank, i2c_io_adr, i2c_io_count, i2c_io_val;
|
||||
uint32_t i2c_sdao_data;
|
||||
|
||||
bool mp3_is_buffered;
|
||||
uint32_t mp3data_count;
|
||||
uint32_t decoded_frame_count, decoded_samples;
|
||||
uint32_t decoded_frame_count;
|
||||
int32_t sample_count, samples_idx;
|
||||
int32_t frame_channels;
|
||||
|
||||
bool is_muted;
|
||||
float gain_ll, gain_rr;
|
||||
|
||||
uint32_t playback_status;
|
||||
|
||||
std::unique_ptr<mp3_audio> mp3dec;
|
||||
};
|
||||
|
||||
|
||||
|
|
79
src/devices/sound/mp3_audio.cpp
Normal file
79
src/devices/sound/mp3_audio.cpp
Normal file
|
@ -0,0 +1,79 @@
|
|||
// license:BSD-3-Clause
|
||||
// copyright-holders:windyfairy
|
||||
/***************************************************************************
|
||||
|
||||
MP3 audio decoder
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
#include "emu.h"
|
||||
#include "mp3_audio.h"
|
||||
|
||||
#define MINIMP3_IMPLEMENTATION
|
||||
#define MAX_FRAME_SYNC_MATCHES 2
|
||||
#include "minimp3/minimp3.h"
|
||||
|
||||
// To avoid modifying minimp3.h, forward declare mp3dec_local_t in mp3_audio.h and then make it an mp3dec_t using inheritance
|
||||
struct mp3_audio::mp3dec_local_t : public mp3dec_t
|
||||
{
|
||||
};
|
||||
|
||||
mp3_audio::mp3_audio(const void *_base)
|
||||
: base((const uint8_t *)_base)
|
||||
{
|
||||
dec = std::make_unique<mp3dec_local_t>();
|
||||
clear();
|
||||
}
|
||||
|
||||
mp3_audio::~mp3_audio()
|
||||
{
|
||||
}
|
||||
|
||||
void mp3_audio::clear()
|
||||
{
|
||||
mp3dec_init(dec.get());
|
||||
m_found_stream = false;
|
||||
}
|
||||
|
||||
bool mp3_audio::decode_buffer(int &pos, int limit, short *output, int &output_samples, int &sample_rate, int &channels)
|
||||
{
|
||||
mp3dec_frame_info_t info = {};
|
||||
|
||||
if (!m_found_stream)
|
||||
{
|
||||
// Guarantee a specified number of frames are buffered before starting decoding to ensure it's not full of garbage that looks like a valid frame
|
||||
int free_format_bytes = 0;
|
||||
int frame_bytes = 0;
|
||||
int frame_offset = mp3d_find_frame(base, limit, &free_format_bytes, &frame_bytes);
|
||||
|
||||
if (frame_bytes && frame_offset + frame_bytes <= limit)
|
||||
{
|
||||
int i = 0, nmatch = 0;
|
||||
|
||||
for (i = frame_offset, nmatch = 0; nmatch < MAX_FRAME_SYNC_MATCHES; nmatch++)
|
||||
{
|
||||
i += hdr_frame_bytes(base + i, frame_bytes) + hdr_padding(base + i);
|
||||
if (i + HDR_SIZE > limit || !hdr_compare(base + frame_offset, base + i))
|
||||
break;
|
||||
}
|
||||
|
||||
m_found_stream = nmatch >= MAX_FRAME_SYNC_MATCHES;
|
||||
}
|
||||
|
||||
if (!m_found_stream)
|
||||
{
|
||||
output_samples = 0;
|
||||
sample_rate = 0;
|
||||
channels = 0;
|
||||
pos = 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
output_samples = mp3dec_decode_frame(dec.get(), base, limit, output, &info);
|
||||
sample_rate = info.hz;
|
||||
channels = info.channels;
|
||||
pos = info.frame_bytes;
|
||||
|
||||
return pos > 0 && output_samples > 0;
|
||||
}
|
35
src/devices/sound/mp3_audio.h
Normal file
35
src/devices/sound/mp3_audio.h
Normal file
|
@ -0,0 +1,35 @@
|
|||
// license:BSD-3-Clause
|
||||
// copyright-holders:windyfairy
|
||||
/***************************************************************************
|
||||
|
||||
MP3 audio decoder
|
||||
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef MAME_SOUND_MP3_AUDIO_H
|
||||
#define MAME_SOUND_MP3_AUDIO_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
class mp3_audio
|
||||
{
|
||||
public:
|
||||
mp3_audio(const void *base);
|
||||
~mp3_audio();
|
||||
|
||||
bool decode_buffer(int &pos, int limit, short *output, int &output_samples, int &sample_rate, int &channels);
|
||||
|
||||
void clear();
|
||||
|
||||
private:
|
||||
struct mp3dec_local_t;
|
||||
|
||||
const uint8_t *base;
|
||||
|
||||
std::unique_ptr<mp3dec_local_t> dec;
|
||||
bool m_found_stream;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -660,6 +660,7 @@ User data note:
|
|||
#include "machine/ram.h"
|
||||
#include "machine/ticket.h"
|
||||
#include "machine/timer.h"
|
||||
#include "sound/lc82310.h"
|
||||
#include "sound/spu.h"
|
||||
#include "video/psx.h"
|
||||
|
||||
|
@ -907,6 +908,7 @@ public:
|
|||
: namcos10_memn_state(mconfig, type, tag)
|
||||
, m_ram(*this, "maincpu:ram")
|
||||
, m_memp3_mcu(*this, "memp3_mcu")
|
||||
, m_lc82310(*this, "mp3_decoder")
|
||||
, m_mcu_ram(*this, "mcu_ram")
|
||||
, m_p3_analog(*this, "P3_ANALOG%u", 1U)
|
||||
{ }
|
||||
|
@ -926,26 +928,55 @@ private:
|
|||
void namcos10_memp3_map(address_map &map);
|
||||
void mcu_map(address_map &map);
|
||||
|
||||
template <int Port> uint8_t port_read(offs_t offset);
|
||||
template <int Port> void port_write(offs_t offset, uint8_t data);
|
||||
|
||||
void firmware_write_w(uint16_t data);
|
||||
|
||||
void ram_bank_w(uint16_t data);
|
||||
|
||||
uint16_t unk_status1_r();
|
||||
|
||||
uint16_t unk_status2_r();
|
||||
void mcu_int5_w(uint16_t data);
|
||||
uint16_t subcomm_busy_r();
|
||||
void subcomm_int_w(uint16_t data);
|
||||
|
||||
uint16_t ram_r(offs_t offset);
|
||||
void ram_w(offs_t offset, uint16_t data);
|
||||
|
||||
uint16_t io_analog_r(offs_t offset);
|
||||
void subcomm_ack_w(uint16_t data);
|
||||
|
||||
uint16_t mp3_unk_r();
|
||||
void mp3_unk_w(uint16_t data);
|
||||
|
||||
uint16_t mp3_unk2_r();
|
||||
void mp3_unk2_w(uint16_t data);
|
||||
|
||||
uint16_t mp3_unk3_r();
|
||||
void mp3_unk3_w(uint16_t data);
|
||||
|
||||
uint16_t decode_pld_ver_r();
|
||||
uint16_t dram_pld_ver_r();
|
||||
uint16_t nand_pld_ver_r();
|
||||
|
||||
uint16_t mp3_stream_status_r();
|
||||
void mp3_stream_status_w(uint16_t data);
|
||||
|
||||
void mp3_data_w(uint16_t data);
|
||||
|
||||
required_device<ram_device> m_ram;
|
||||
required_device<tmp95c061_device> m_memp3_mcu;
|
||||
required_device<lc82310_device> m_lc82310;
|
||||
required_shared_ptr<uint16_t> m_mcu_ram;
|
||||
optional_ioport_array<4> m_p3_analog;
|
||||
|
||||
uint16_t m_mcu_ram_bank;
|
||||
bool m_subcomm_busy;
|
||||
|
||||
uint16_t m_mp3_unk_val, m_mp3_unk2_val, m_mp3_unk3_val;
|
||||
uint8_t m_mp3_port7_data;
|
||||
uint8_t m_mp3_porta_data;
|
||||
bool m_mp3_stream_available;
|
||||
bool m_mp3_received_byte;
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -1258,8 +1289,6 @@ void namcos10_state::namcos10_exio(machine_config &config)
|
|||
|
||||
void namcos10_state::namcos10_map_exio_inner(address_map &map)
|
||||
{
|
||||
// TODO: Base registers are probably similar between EXIO and MGEXIO, fill in registers and rename if possible
|
||||
// TODO: Figure out what the commented out registers are actually used for
|
||||
map(0x06000, 0x0ffff).rw(m_exio, FUNC(namcos10_exio_device::ram_r), FUNC(namcos10_exio_device::ram_w));
|
||||
map(0x10000, 0x10003).rw(m_exio, FUNC(namcos10_exio_device::ctrl_r), FUNC(namcos10_exio_device::ctrl_w));
|
||||
map(0x18000, 0x18003).rw(m_exio, FUNC(namcos10_exio_device::bus_req_r), FUNC(namcos10_exio_device::bus_req_w));
|
||||
|
@ -1758,6 +1787,9 @@ void namcos10_memn_state::namcos10_memn_map_inner(address_map &map)
|
|||
map(0xf460000, 0xf460001).w(FUNC(namcos10_memn_state::nand_bank_w));
|
||||
map(0xf470000, 0xf470001).rw(FUNC(namcos10_memn_state::ctrl_reg_r), FUNC(namcos10_memn_state::ctrl_reg_w));
|
||||
//map(0xf480000, 0xf480001).w(); // 0xffff is written here after a nand write/erase?
|
||||
|
||||
map(0xfe40000, 0xfe40003).noprw();
|
||||
map(0xfe48000, 0xfe48003).noprw();
|
||||
}
|
||||
|
||||
void namcos10_memn_state::namcos10_memn_map(address_map &map)
|
||||
|
@ -2551,37 +2583,28 @@ void namcos10_memp3_state::ram_w(offs_t offset, uint16_t data)
|
|||
m_mcu_ram[m_mcu_ram_bank * 0x10000 + offset] = data;
|
||||
}
|
||||
|
||||
uint16_t namcos10_memp3_state::unk_status2_r()
|
||||
uint16_t namcos10_memp3_state::subcomm_busy_r()
|
||||
{
|
||||
// Some kind of status flag.
|
||||
// Game code loops until this is non-zero before writing to data to f30000c/320000.
|
||||
// Possibly related to MP3 decoder?
|
||||
return 1;
|
||||
// Before writing a new command this register is checked to make sure the
|
||||
// previous command has already been finished processing
|
||||
return !m_subcomm_busy;
|
||||
}
|
||||
|
||||
void namcos10_memp3_state::mcu_int5_w(uint16_t data)
|
||||
void namcos10_memp3_state::subcomm_int_w(uint16_t data)
|
||||
{
|
||||
// MP3 decoder commands will only be processed when INT5 is triggered.
|
||||
// 0 gets written to this register (only?) after the MP3 decoder commands are
|
||||
// written into the MCU's memory so I believe it's responsible for making the
|
||||
// INT5 get triggered.
|
||||
// MP3 decoder commands will only be processed when INT5 is triggered
|
||||
m_memp3_mcu->set_input_line(TLCS900_INT5, ASSERT_LINE);
|
||||
}
|
||||
|
||||
uint16_t namcos10_memp3_state::io_analog_r(offs_t offset)
|
||||
{
|
||||
return m_p3_analog[offset].read_safe(0);
|
||||
m_subcomm_busy = true;
|
||||
}
|
||||
|
||||
void namcos10_memp3_state::namcos10_memp3_map_inner(address_map &map)
|
||||
{
|
||||
map(0xf300000, 0xf300001).w(FUNC(namcos10_memp3_state::firmware_write_w));
|
||||
// 1f300004 unk
|
||||
map(0xf300006, 0xf300007).rw(FUNC(namcos10_memp3_state::unk_status2_r), FUNC(namcos10_memp3_state::mcu_int5_w));
|
||||
map(0xf300006, 0xf300007).rw(FUNC(namcos10_memp3_state::subcomm_busy_r), FUNC(namcos10_memp3_state::subcomm_int_w));
|
||||
map(0xf30000c, 0xf30000d).w(FUNC(namcos10_memp3_state::ram_bank_w));
|
||||
map(0xf30000e, 0xf30000f).r(FUNC(namcos10_memp3_state::unk_status1_r));
|
||||
map(0xf30000e, 0xf30000f).r(FUNC(namcos10_memp3_state::unk_status1_r)); // similar to 0xf300006, only used when writing firmware?
|
||||
map(0xf320000, 0xf33ffff).rw(FUNC(namcos10_memp3_state::ram_r), FUNC(namcos10_memp3_state::ram_w));
|
||||
map(0xf33fff0, 0xf33fff7).r(FUNC(namcos10_memp3_state::io_analog_r));
|
||||
}
|
||||
|
||||
void namcos10_memp3_state::namcos10_memp3_map(address_map &map)
|
||||
|
@ -2595,9 +2618,143 @@ void namcos10_memp3_state::namcos10_memp3_map(address_map &map)
|
|||
|
||||
void namcos10_memp3_state::mcu_map(address_map &map)
|
||||
{
|
||||
map(0x000000, 0x7fffff).ram().mirror(0x800000).share(m_mcu_ram);
|
||||
map(0x100000, 0x100001).rw(FUNC(namcos10_memp3_state::mp3_unk_r), FUNC(namcos10_memp3_state::mp3_unk_w));
|
||||
map(0x100002, 0x100003).portr("MEMP3_DIPSW");
|
||||
map(0x100004, 0x100005).rw(FUNC(namcos10_memp3_state::mp3_unk2_r), FUNC(namcos10_memp3_state::mp3_unk2_w));
|
||||
map(0x100008, 0x100009).w(FUNC(namcos10_memp3_state::subcomm_ack_w));
|
||||
map(0x10000e, 0x10000f).r(FUNC(namcos10_memp3_state::decode_pld_ver_r));
|
||||
map(0x100010, 0x100011).rw(FUNC(namcos10_memp3_state::mp3_unk3_r), FUNC(namcos10_memp3_state::mp3_unk3_w));
|
||||
map(0x10001e, 0x10002f).r(FUNC(namcos10_memp3_state::dram_pld_ver_r));
|
||||
map(0x10002e, 0x10002f).r(FUNC(namcos10_memp3_state::nand_pld_ver_r));
|
||||
map(0x100030, 0x100031).w(FUNC(namcos10_memp3_state::mp3_data_w));
|
||||
map(0x100032, 0x100033).rw(FUNC(namcos10_memp3_state::mp3_stream_status_r), FUNC(namcos10_memp3_state::mp3_stream_status_w));
|
||||
map(0x800000, 0xffffff).ram().share(m_mcu_ram);
|
||||
}
|
||||
|
||||
uint16_t namcos10_memp3_state::decode_pld_ver_r()
|
||||
{
|
||||
// DECODE PLD version, only seems to be read when sub CPU is in debug mode
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint16_t namcos10_memp3_state::dram_pld_ver_r()
|
||||
{
|
||||
// DRAM-C PLD version, only seems to be read when sub CPU is in debug mode
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint16_t namcos10_memp3_state::nand_pld_ver_r()
|
||||
{
|
||||
// NAND-C PLD version, only seems to be read when sub CPU is in debug mode
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint16_t namcos10_memp3_state::mp3_stream_status_r()
|
||||
{
|
||||
// The debug messages when debug mode are enabled checks this register
|
||||
// and if it's 0 then it prints "MUTE:ON" and if it's 1 it prints "MUTE:OFF"
|
||||
return m_mp3_stream_available;
|
||||
}
|
||||
|
||||
void namcos10_memp3_state::mp3_stream_status_w(uint16_t data)
|
||||
{
|
||||
// This gets set to 0 and then 1 every time a new MP3 stream is started
|
||||
bool is_available = BIT(data, 0);
|
||||
|
||||
if (m_mp3_stream_available && !is_available)
|
||||
m_lc82310->reset_playback();
|
||||
|
||||
m_mp3_stream_available = is_available;
|
||||
}
|
||||
|
||||
void namcos10_memp3_state::mp3_data_w(uint16_t data)
|
||||
{
|
||||
m_lc82310->dimpg_w(data & 0xff);
|
||||
m_mp3_received_byte = true;
|
||||
}
|
||||
|
||||
void namcos10_memp3_state::subcomm_ack_w(uint16_t data)
|
||||
{
|
||||
m_subcomm_busy = false;
|
||||
m_memp3_mcu->set_input_line(TLCS900_INT5, CLEAR_LINE);
|
||||
}
|
||||
|
||||
uint16_t namcos10_memp3_state::mp3_unk_r()
|
||||
{
|
||||
// Seems to be related to timer 3, something to do with the "L" LEDs maybe?
|
||||
// When the timer 3 value has ticked 10 times, set to 1 if value is 0 else shift value by 1
|
||||
// mp3_unk_w(mp3_unk_r() ? mp3_unk_r() << 1 : 1)
|
||||
return m_mp3_unk_val;
|
||||
}
|
||||
|
||||
void namcos10_memp3_state::mp3_unk_w(uint16_t data)
|
||||
{
|
||||
m_mp3_unk_val = data;
|
||||
}
|
||||
|
||||
uint16_t namcos10_memp3_state::mp3_unk2_r()
|
||||
{
|
||||
// The following are how the sub CPU program handles the bits.
|
||||
// It's unclear what writing to these registers actually does outside of the sub CPU.
|
||||
// Only available in g13jnr and nicetsuk, maybe some kind of debug controls?
|
||||
// bit 0 = Start playback
|
||||
// bit 1 = Stop playback
|
||||
// bit 2 = Fade out
|
||||
// bit 3 = Fade in
|
||||
// bit 4 = Decrease volume by 1
|
||||
// bit 5 = Increase volume by 1
|
||||
// bit 6 = Mute/Unmute (uses 0x100032)
|
||||
// bit 7 = Print state from MP3 decoder + state byte from sub CPU to the sub CPU's serial output
|
||||
return m_mp3_unk2_val;
|
||||
}
|
||||
|
||||
void namcos10_memp3_state::mp3_unk2_w(uint16_t data)
|
||||
{
|
||||
m_mp3_unk2_val = data;
|
||||
}
|
||||
|
||||
uint16_t namcos10_memp3_state::mp3_unk3_r()
|
||||
{
|
||||
// When the timer 3 value has ticked 10 times: mp3_unk3_w(3 - mp3_unk3_r())
|
||||
return m_mp3_unk3_val;
|
||||
}
|
||||
|
||||
void namcos10_memp3_state::mp3_unk3_w(uint16_t data)
|
||||
{
|
||||
m_mp3_unk3_val = data;
|
||||
}
|
||||
|
||||
template <int Port>
|
||||
void namcos10_memp3_state::port_write(offs_t offset, uint8_t data)
|
||||
{
|
||||
if (Port == 7) {
|
||||
m_lc82310->zcsctl_w(BIT(data, 3));
|
||||
m_lc82310->dictl_w(BIT(data, 1));
|
||||
m_lc82310->ckctl_w(BIT(data, 2));
|
||||
m_mp3_port7_data = data;
|
||||
} else if (Port == 10) {
|
||||
// bit 2 is toggled off/on after MP3 byte is written
|
||||
// bit 3 is set to 0 before writing data and then set to 1 after writing data
|
||||
m_mp3_porta_data = data;
|
||||
}
|
||||
}
|
||||
|
||||
template <int Port>
|
||||
uint8_t namcos10_memp3_state::port_read(offs_t offset)
|
||||
{
|
||||
uint8_t r = 0;
|
||||
|
||||
if (Port == 7) {
|
||||
r = (m_mp3_port7_data & ~1) | m_lc82310->doctl_r();
|
||||
} else if (Port == 10) { // Port A
|
||||
r = m_mp3_porta_data;
|
||||
} else if (Port == 11) { // Port B
|
||||
r = !m_mp3_received_byte && m_lc82310->demand_r();
|
||||
m_mp3_received_byte = false;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
void namcos10_memp3_state::namcos10_memp3_base(machine_config &config)
|
||||
{
|
||||
|
@ -2607,11 +2764,44 @@ void namcos10_memp3_state::namcos10_memp3_base(machine_config &config)
|
|||
m_maincpu->subdevice<psxdma_device>("dma")->install_write_handler(5, psxdma_device::write_delegate(&namcos10_memp3_state::pio_dma_read, this));
|
||||
m_maincpu->set_addrmap(AS_PROGRAM, &namcos10_memp3_state::namcos10_memp3_map);
|
||||
|
||||
TMP95C061(config, m_memp3_mcu, XTAL(16'934'400));
|
||||
TMP95C061(config, m_memp3_mcu, XTAL(101'491'200) / 2); // Measured
|
||||
m_memp3_mcu->set_addrmap(AS_PROGRAM, &namcos10_memp3_state::mcu_map);
|
||||
// Port 7 is used for communicating with the MP3 decoder chip
|
||||
|
||||
// LC82310 16.9344MHz
|
||||
m_memp3_mcu->port1_read().set(FUNC(namcos10_memp3_state::port_read<1>));
|
||||
m_memp3_mcu->port5_read().set(FUNC(namcos10_memp3_state::port_read<5>));
|
||||
m_memp3_mcu->port7_read().set(FUNC(namcos10_memp3_state::port_read<7>));
|
||||
m_memp3_mcu->port8_read().set(FUNC(namcos10_memp3_state::port_read<8>));
|
||||
m_memp3_mcu->port9_read().set(FUNC(namcos10_memp3_state::port_read<9>));
|
||||
m_memp3_mcu->porta_read().set(FUNC(namcos10_memp3_state::port_read<10>));
|
||||
m_memp3_mcu->portb_read().set(FUNC(namcos10_memp3_state::port_read<11>));
|
||||
m_memp3_mcu->port1_write().set(FUNC(namcos10_memp3_state::port_write<1>));
|
||||
m_memp3_mcu->port2_write().set(FUNC(namcos10_memp3_state::port_write<2>));
|
||||
m_memp3_mcu->port5_write().set(FUNC(namcos10_memp3_state::port_write<5>));
|
||||
m_memp3_mcu->port6_write().set(FUNC(namcos10_memp3_state::port_write<6>));
|
||||
m_memp3_mcu->port7_write().set(FUNC(namcos10_memp3_state::port_write<7>));
|
||||
m_memp3_mcu->port8_write().set(FUNC(namcos10_memp3_state::port_write<8>));
|
||||
m_memp3_mcu->porta_write().set(FUNC(namcos10_memp3_state::port_write<10>));
|
||||
m_memp3_mcu->portb_write().set(FUNC(namcos10_memp3_state::port_write<11>));
|
||||
|
||||
m_memp3_mcu->an_read<0>().set([this] () {
|
||||
return m_p3_analog[0].read_safe(0);
|
||||
});
|
||||
|
||||
m_memp3_mcu->an_read<1>().set([this] () {
|
||||
return m_p3_analog[1].read_safe(0);
|
||||
});
|
||||
|
||||
m_memp3_mcu->an_read<2>().set([this] () {
|
||||
return m_p3_analog[2].read_safe(0);
|
||||
});
|
||||
|
||||
m_memp3_mcu->an_read<3>().set([this] () {
|
||||
return m_p3_analog[3].read_safe(0);
|
||||
});
|
||||
|
||||
LC82310(config, m_lc82310, XTAL(16'934'400));
|
||||
m_lc82310->add_route(0, "lspeaker", 1.0);
|
||||
m_lc82310->add_route(1, "rspeaker", 1.0);
|
||||
}
|
||||
|
||||
void namcos10_memp3_state::machine_start()
|
||||
|
@ -2619,13 +2809,31 @@ void namcos10_memp3_state::machine_start()
|
|||
namcos10_memn_state::machine_start();
|
||||
|
||||
save_item(NAME(m_mcu_ram_bank));
|
||||
save_item(NAME(m_mp3_unk_val));
|
||||
save_item(NAME(m_mp3_unk2_val));
|
||||
save_item(NAME(m_mp3_unk3_val));
|
||||
save_item(NAME(m_mp3_port7_data));
|
||||
save_item(NAME(m_mp3_porta_data));
|
||||
save_item(NAME(m_mp3_stream_available));
|
||||
save_item(NAME(m_mp3_received_byte));
|
||||
save_item(NAME(m_subcomm_busy));
|
||||
}
|
||||
|
||||
void namcos10_memp3_state::machine_reset()
|
||||
{
|
||||
namcos10_memn_state::machine_reset();
|
||||
|
||||
std::fill(m_mcu_ram.begin(), m_mcu_ram.end(), 0);
|
||||
|
||||
m_mcu_ram_bank = 0;
|
||||
m_mp3_unk_val = m_mp3_unk3_val = 0;
|
||||
m_mp3_unk2_val = 0xffff;
|
||||
m_mp3_port7_data = 0;
|
||||
m_mp3_porta_data = 0;
|
||||
m_mp3_stream_available = false;
|
||||
m_mp3_received_byte = false;
|
||||
|
||||
m_subcomm_busy = false;
|
||||
|
||||
m_memp3_mcu->suspend(SUSPEND_REASON_HALT, 1);
|
||||
}
|
||||
|
@ -2774,6 +2982,18 @@ static INPUT_PORTS_START( namcos10 )
|
|||
|
||||
INPUT_PORTS_END
|
||||
|
||||
static INPUT_PORTS_START( memp3 )
|
||||
PORT_START("MEMP3_DIPSW")
|
||||
PORT_BIT( 0xfff0, IP_ACTIVE_LOW, IPT_UNUSED )
|
||||
PORT_DIPUNKNOWN_DIPLOC( 0x0001, IP_ACTIVE_LOW, "MEMP3_SW1:1" )
|
||||
PORT_DIPUNKNOWN_DIPLOC( 0x0002, IP_ACTIVE_LOW, "MEMP3_SW1:2" )
|
||||
PORT_DIPUNKNOWN_DIPLOC( 0x0004, IP_ACTIVE_LOW, "MEMP3_SW1:3" )
|
||||
PORT_DIPNAME( 0x0008, 0x0008, "Debug Mode" ) PORT_DIPLOCATION("MEMP3_SW1:4")
|
||||
PORT_DIPSETTING( 0x0008, DEF_STR( Off ) )
|
||||
PORT_DIPSETTING( 0x0000, DEF_STR( On ) )
|
||||
|
||||
INPUT_PORTS_END
|
||||
|
||||
static INPUT_PORTS_START( mrdrilr2 )
|
||||
PORT_INCLUDE(namcos10)
|
||||
|
||||
|
@ -2925,6 +3145,7 @@ INPUT_PORTS_END
|
|||
|
||||
static INPUT_PORTS_START( g13jnr )
|
||||
PORT_INCLUDE(namcos10)
|
||||
PORT_INCLUDE(memp3)
|
||||
|
||||
PORT_MODIFY("IN1")
|
||||
PORT_BIT( 0x071f0043, IP_ACTIVE_LOW, IPT_UNUSED )
|
||||
|
@ -2947,10 +3168,10 @@ static INPUT_PORTS_START( g13jnr )
|
|||
PORT_DIPSETTING( 0x00, DEF_STR( On ) )
|
||||
|
||||
PORT_START("P3_ANALOG1")
|
||||
PORT_BIT( 0xffff, 0x7fff, IPT_LIGHTGUN_Y ) PORT_CROSSHAIR(Y, -1.0, 0.0, 0) PORT_MINMAX(0x0000,0xffff) PORT_SENSITIVITY(100) PORT_KEYDELTA(100) PORT_PLAYER(1) PORT_REVERSE
|
||||
PORT_BIT( 0x3ff, 0, IPT_LIGHTGUN_Y ) PORT_CROSSHAIR(Y, -1.0, 0.0, 0) PORT_MINMAX(0x000,0x3ff) PORT_SENSITIVITY(100) PORT_KEYDELTA(50) PORT_PLAYER(1) PORT_REVERSE
|
||||
|
||||
PORT_START("P3_ANALOG2")
|
||||
PORT_BIT( 0xffff, 0x7fff, IPT_LIGHTGUN_X ) PORT_CROSSHAIR(X, 1.0, 0.0, 0) PORT_MINMAX(0x0000,0xffff) PORT_SENSITIVITY(100) PORT_KEYDELTA(100) PORT_PLAYER(1)
|
||||
PORT_BIT( 0x3ff, 0, IPT_LIGHTGUN_X ) PORT_CROSSHAIR(X, 1.0, 0.0, 0) PORT_MINMAX(0x000,0x3ff) PORT_SENSITIVITY(100) PORT_KEYDELTA(50) PORT_PLAYER(1)
|
||||
|
||||
INPUT_PORTS_END
|
||||
|
||||
|
@ -3022,6 +3243,7 @@ INPUT_PORTS_END
|
|||
|
||||
static INPUT_PORTS_START( nicetsuk )
|
||||
PORT_INCLUDE(namcos10)
|
||||
PORT_INCLUDE(memp3)
|
||||
|
||||
PORT_MODIFY("IN1")
|
||||
PORT_BIT( 0x0ff9ef40, IP_ACTIVE_LOW, IPT_UNUSED )
|
||||
|
@ -3041,6 +3263,7 @@ INPUT_PORTS_END
|
|||
|
||||
static INPUT_PORTS_START( squizchs )
|
||||
PORT_INCLUDE(namcos10)
|
||||
PORT_INCLUDE(memp3)
|
||||
|
||||
PORT_MODIFY("IN1")
|
||||
PORT_BIT( 0x0fff7070, IP_ACTIVE_LOW, IPT_UNUSED )
|
||||
|
@ -3675,6 +3898,6 @@ GAME( 2003, taiko5, 0, ns10_taiko5, taiko, namcos10_memn_sta
|
|||
GAME( 2004, taiko6, 0, ns10_taiko6, taiko, namcos10_memn_state, memn_driver_init, ROT0, "Namco", "Taiko no Tatsujin 6 (Japan, TK61 Ver.A)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )
|
||||
|
||||
// MEM(P3)
|
||||
GAME( 2001, g13jnr, 0, ns10_g13jnr, g13jnr, namcos10_memp3_state, memn_driver_init, ROT0, "Eighting / Raizing / Namco", "Golgo 13: Juusei no Requiem (Japan, GLT1 VER.A)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )
|
||||
GAME( 2002, nicetsuk, 0, ns10_nicetsuk, nicetsuk, namcos10_memp3_state, memn_driver_init, ROT0, "Namco / Metro", "Tsukkomi Yousei Gips Nice Tsukkomi (NTK1 Ver.A)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )
|
||||
GAME( 2003, squizchs, 0, ns10_squizchs, squizchs, namcos10_memp3_state, memn_driver_init, ROT0, "Namco", "Seishun-Quiz Colorful High School (CHS1 Ver.A)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_SOUND )
|
||||
GAME( 2001, g13jnr, 0, ns10_g13jnr, g13jnr, namcos10_memp3_state, memn_driver_init, ROT0, "Eighting / Raizing / Namco", "Golgo 13: Juusei no Requiem (Japan, GLT1 VER.A)", MACHINE_IMPERFECT_SOUND )
|
||||
GAME( 2002, nicetsuk, 0, ns10_nicetsuk, nicetsuk, namcos10_memp3_state, memn_driver_init, ROT0, "Namco / Metro", "Tsukkomi Yousei Gips Nice Tsukkomi (NTK1 Ver.A)", MACHINE_IMPERFECT_SOUND )
|
||||
GAME( 2003, squizchs, 0, ns10_squizchs, squizchs, namcos10_memp3_state, memn_driver_init, ROT0, "Namco", "Seishun-Quiz Colorful High School (CHS1 Ver.A)", MACHINE_IMPERFECT_SOUND )
|
||||
|
|
Loading…
Reference in a new issue