sega/dsb2.cpp: add basic MPEG playback

This commit is contained in:
angelosa 2025-05-15 22:03:06 +02:00
parent 3103e0c799
commit 16cc6c9d80
2 changed files with 177 additions and 27 deletions

View File

@ -1,12 +1,10 @@
// license:BSD-3-Clause
// copyright-holders:
/***************************************************************************
/**************************************************************************************************
Sega Z80 Digital Sound Board
Sega Digital Sound Board 2
used for Model 1/2/3
***************************************************************************/
**************************************************************************************************/
#include "emu.h"
@ -28,14 +26,15 @@ dsb2_device::dsb2_device(const machine_config &mconfig, const char *tag, device_
m_mp_end(0),
m_mp_vol(0x7f),
m_mp_pan(0),
m_mp_state(0),
m_lp_start(0),
m_lp_end(0),
m_start(0),
m_end(0),
m_mp_pos(0),
m_audio_pos(0),
m_audio_avail(0)
m_audio_avail(0),
m_command(mpeg_command_t::IDLE),
m_player(mpeg_player_t::NOT_PLAYING)
{
}
@ -47,7 +46,7 @@ void dsb2_device::dsb2_map(address_map &map)
// map(0xd00001) system control?
// map(0xd20001) flsbeats reads here
// map(0xe00001) acknowledge FIFO writes?
// map(0xe00003) MPEG FIFO writes
map(0xe00003, 0xe00003).w(FUNC(dsb2_device::fifo_w));
// MPEG status
map(0xe80001, 0xe80001).lr8(NAME([] () { return 0x01; }));
map(0xf00000, 0xf1ffff).ram();
@ -56,7 +55,6 @@ void dsb2_device::dsb2_map(address_map &map)
void dsb2_device::device_add_mconfig(machine_config &config)
{
// TODO: unknown clocks
// TODO: 1 kHz timer for irq2
M68000(config, m_ourcpu, 8000000);
m_ourcpu->set_addrmap(AS_PROGRAM, &dsb2_device::dsb2_map);
@ -75,11 +73,14 @@ void dsb2_device::device_start()
m_decoder.reset(new mpeg_audio(&m_mpeg_rom[0], mpeg_audio::L2, false, 0));
stream_alloc(0, 2, 32000);
m_timer_1kHz = timer_alloc(FUNC(dsb2_device::timer_irq_cb), this);
save_item(NAME(m_mp_start));
save_item(NAME(m_mp_end));
save_item(NAME(m_mp_vol));
save_item(NAME(m_mp_pan));
save_item(NAME(m_mp_state));
// save_item(NAME(m_command));
// save_item(NAME(m_player));
save_item(NAME(m_lp_start));
save_item(NAME(m_lp_end));
save_item(NAME(m_start));
@ -95,11 +96,13 @@ void dsb2_device::device_reset()
m_audio_pos = m_audio_avail = 0;
std::fill(std::begin(m_audio_buf), std::end(m_audio_buf), 0);
m_mp_vol = 0x7f;
m_mp_state = 0;
m_command = IDLE;
m_player = NOT_PLAYING;
m_uart->write_cts(0);
}
m_timer_1kHz->adjust(attotime::from_hz(1000), 0, attotime::from_hz(1000));
}
void dsb2_device::device_stop()
{
@ -116,10 +119,144 @@ void dsb2_device::output_txd(int state)
m_rxd_handler(state);
}
TIMER_CALLBACK_MEMBER(dsb2_device::timer_irq_cb)
{
m_ourcpu->set_input_line(2, HOLD_LINE);
}
void dsb2_device::fifo_w(offs_t offset, u8 data)
{
switch(m_command)
{
case mpeg_command_t::START_ADDRESS_HI:
m_start &= 0x00ffff;
m_start |= (uint32_t)data << 16;
m_command = mpeg_command_t::START_ADDRESS_MD;
break;
case mpeg_command_t::START_ADDRESS_MD:
m_start &= 0xff00ff;
m_start |= (uint32_t)data << 8;
m_command = mpeg_command_t::START_ADDRESS_LO;
break;
case mpeg_command_t::START_ADDRESS_LO:
m_start &= 0xffff00;
m_start |= data;
m_mp_start = m_start;
m_command = mpeg_command_t::IDLE;
break;
case mpeg_command_t::END_ADDRESS_HI:
m_end &= 0x00ffff;
m_end |= (uint32_t)data << 16;
m_command = mpeg_command_t::END_ADDRESS_MD;
break;
case mpeg_command_t::END_ADDRESS_MD:
m_end &= 0xff00ff;
m_end |= (uint32_t)data << 8;
m_command = mpeg_command_t::END_ADDRESS_LO;
break;
case mpeg_command_t::END_ADDRESS_LO:
m_end &= 0xffff00;
m_end |= data;
m_mp_end = m_end;
m_command = mpeg_command_t::IDLE;
break;
case mpeg_command_t::IDLE:
default:
{
if ((data & 0xfe) == 0x14)
m_command = mpeg_command_t::START_ADDRESS_HI;
if ((data & 0xfe) == 0x24)
m_command = mpeg_command_t::END_ADDRESS_HI;
if ((data & 0xfe) == 0x74)
{
m_mp_pos = m_mp_start * 8;
m_player = mpeg_player_t::PLAYING;
}
if ((data & 0xfe) == 0x84)
{
m_player = mpeg_player_t::NOT_PLAYING;
m_audio_pos = m_audio_avail = 0;
}
}
}
}
void dsb2_device::sound_stream_update(sound_stream &stream)
{
//int samples = stream.samples();
//int sampindex = 0;
int samples = stream.samples();
int sampindex = 0;
for (;;)
{
while (samples && (m_audio_pos < m_audio_avail))
{
switch (m_mp_pan)
{
case 0: // stereo
stream.put_int(0, sampindex, m_audio_buf[m_audio_pos*2] * m_mp_vol, 32768 * 128);
stream.put_int(1, sampindex, m_audio_buf[m_audio_pos*2+1] * m_mp_vol, 32768 * 128);
sampindex++;
break;
// ...
case 1: // left only
stream.put_int(0, sampindex, m_audio_buf[m_audio_pos*2] * m_mp_vol, 32768 * 128);
stream.put_int(1, sampindex, m_audio_buf[m_audio_pos*2] * m_mp_vol, 32768 * 128);
sampindex++;
break;
case 2: // right only
stream.put_int(0, sampindex, m_audio_buf[m_audio_pos*2+1] * m_mp_vol, 32768 * 128);
stream.put_int(1, sampindex, m_audio_buf[m_audio_pos*2+1] * m_mp_vol, 32768 * 128);
sampindex++;
break;
}
m_audio_pos++;
samples--;
}
if (!samples)
{
break;
}
if (m_player == mpeg_player_t::NOT_PLAYING)
{
break;
}
else
{
int sample_rate, channel_count;
bool const ok = m_decoder->decode_buffer(m_mp_pos, m_mp_end * 8, m_audio_buf, m_audio_avail, sample_rate, channel_count);
if (ok)
{
m_audio_pos = 0;
}
else
{
//if (m_mp_state == 2)
//{
// if (m_mp_pos == m_lp_start * 8)
// {
// // We're looping on un-decodable crap, abort abort abort
// m_mp_state = 0;
// }
// m_mp_pos = m_lp_start * 8;
//
// if (m_lp_end)
// {
// m_mp_end = m_lp_end;
// }
//}
//else
{
m_player = mpeg_player_t::NOT_PLAYING;
}
}
}
}
}

View File

@ -13,10 +13,6 @@
#include <memory>
//**************************************************************************
// TYPE DEFINITIONS
//**************************************************************************
class dsb2_device : public device_t, public device_sound_interface
{
public:
@ -47,19 +43,36 @@ private:
std::unique_ptr<mpeg_audio> m_decoder;
int16_t m_audio_buf[1152*2];
uint32_t m_mp_start, m_mp_end, m_mp_vol, m_mp_pan, m_mp_state, m_lp_start, m_lp_end, m_start, m_end;
uint32_t m_mp_start, m_mp_end, m_mp_vol, m_mp_pan, m_lp_start, m_lp_end, m_start, m_end;
int32_t m_mp_pos, m_audio_pos, m_audio_avail;
emu_timer *m_timer_1kHz;
TIMER_CALLBACK_MEMBER(timer_irq_cb);
void output_txd(int state);
// void mpeg_trigger_w(uint8_t data);
// void mpeg_start_w(offs_t offset, uint8_t data);
// void mpeg_end_w(offs_t offset, uint8_t data);
// void mpeg_volume_w(uint8_t data);
// void mpeg_stereo_w(uint8_t data);
// uint8_t mpeg_pos_r(offs_t offset);
void dsb2_map(address_map &map) ATTR_COLD;
enum mpeg_command_t : u8 {
IDLE,
START_ADDRESS_HI,
START_ADDRESS_MD,
START_ADDRESS_LO,
END_ADDRESS_HI,
END_ADDRESS_MD,
END_ADDRESS_LO
};
enum mpeg_player_t : u8 {
NOT_PLAYING,
PLAYING
};
mpeg_command_t m_command;
mpeg_player_t m_player;
void fifo_w(offs_t offset, u8 data);
};