// license:BSD-3-Clause
// copyright-holders:Aaron Giles
/***************************************************************************
bsmt2000.cpp
BSMT2000 device emulator.
****************************************************************************
Chip is actually a TMS320C15 DSP with embedded mask rom
Trivia: BSMT stands for "Brian Schmidt's Mouse Trap"
***************************************************************************/
#include "emu.h"
#include "bsmt2000.h"
// device type definition
DEFINE_DEVICE_TYPE(BSMT2000, bsmt2000_device, "bsmt2000", "BSMT2000")
//**************************************************************************
// GLOBAL VARIABLES
//**************************************************************************
// program map for the DSP (points to internal ROM)
void bsmt2000_device::tms_program_map(address_map &map)
{
map.unmap_value_high();
map(0x000, 0xfff).rom();
}
// I/O map for the DSP
void bsmt2000_device::tms_io_map(address_map &map)
{
map(0, 0).rw(FUNC(bsmt2000_device::tms_register_r), FUNC(bsmt2000_device::tms_rom_addr_w));
map(1, 1).rw(FUNC(bsmt2000_device::tms_data_r), FUNC(bsmt2000_device::tms_rom_bank_w));
map(2, 2).r(FUNC(bsmt2000_device::tms_rom_r));
map(3, 3).w(FUNC(bsmt2000_device::tms_left_w));
map(7, 7).w(FUNC(bsmt2000_device::tms_right_w));
}
// ROM definition for the BSMT2000 program ROM
ROM_START( bsmt2000 )
ROM_REGION( 0x2000, "bsmt2000", 0 )
ROM_LOAD16_WORD( "bsmt2000.bin", 0x0000, 0x2000, CRC(c2a265af) SHA1(6ec9eb038fb8eb842c5482aebe1d149daf49f2e6) )
ROM_END
//**************************************************************************
// LIVE DEVICE
//**************************************************************************
//-------------------------------------------------
// bsmt2000_device - constructor
//-------------------------------------------------
bsmt2000_device::bsmt2000_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: device_t(mconfig, BSMT2000, tag, owner, clock)
, device_sound_interface(mconfig, *this)
, device_rom_interface(mconfig, *this, 32)
, m_ready_callback()
, m_stream(nullptr)
, m_cpu(*this, "bsmt2000")
, m_register_select(0)
, m_write_data(0)
, m_rom_address(0)
, m_rom_bank(0)
, m_left_data(0)
, m_right_data(0)
, m_write_pending(false)
{
}
//-------------------------------------------------
// rom_region - return a pointer to the device's
// internal ROM region
//-------------------------------------------------
const tiny_rom_entry *bsmt2000_device::device_rom_region() const
{
return ROM_NAME( bsmt2000 );
}
//-------------------------------------------------
// device_add_mconfig - add device configuration
//-------------------------------------------------
MACHINE_CONFIG_START(bsmt2000_device::device_add_mconfig)
MCFG_DEVICE_ADD("bsmt2000", TMS32015, DERIVED_CLOCK(1,1))
MCFG_DEVICE_PROGRAM_MAP(tms_program_map)
// data map is internal to the CPU
MCFG_DEVICE_IO_MAP(tms_io_map)
MCFG_TMS32010_BIO_IN_CB(READLINE(*this, bsmt2000_device, tms_write_pending_r))
MACHINE_CONFIG_END
//-------------------------------------------------
// device_start - device-specific startup
//-------------------------------------------------
void bsmt2000_device::device_start()
{
m_ready_callback.bind_relative_to(*owner());
// create the stream; BSMT typically runs at 24MHz and writes to a DAC, so
// in theory we should generate a 24MHz stream, but that's certainly overkill
// internally at 24MHz the max output sample rate is 32kHz
// divided by 128 gives us 6x the max output rate which is plenty for oversampling
m_stream = stream_alloc(0, 2, clock() / 128);
// register for save states
save_item(NAME(m_register_select));
save_item(NAME(m_write_data));
save_item(NAME(m_rom_address));
save_item(NAME(m_rom_bank));
save_item(NAME(m_left_data));
save_item(NAME(m_right_data));
save_item(NAME(m_write_pending));
}
//-------------------------------------------------
// device_reset - device-specific reset
//-------------------------------------------------
void bsmt2000_device::device_reset()
{
synchronize(TIMER_ID_RESET);
}
//-------------------------------------------------
// device_timer - handle deferred writes and
// resets as a timer callback
//-------------------------------------------------
void bsmt2000_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
{
switch (id)
{
// deferred reset
case TIMER_ID_RESET:
m_stream->update();
m_cpu->reset();
break;
// deferred register write
case TIMER_ID_REG_WRITE:
m_register_select = param & 0xffff;
break;
// deferred data write
case TIMER_ID_DATA_WRITE:
m_write_data = param & 0xffff;
if (m_write_pending) logerror("BSMT2000: Missed data\n");
m_write_pending = true;
break;
}
}
//-------------------------------------------------
// sound_stream_update - handle update requests
// for our sound stream
//-------------------------------------------------
void bsmt2000_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
{
// just fill with current left/right values
for (int samp = 0; samp < samples; samp++)
{
outputs[0][samp] = m_left_data * 16;
outputs[1][samp] = m_right_data * 16;
}
}
//-------------------------------------------------
// rom_bank_updated - the rom bank has changed
//-------------------------------------------------
void bsmt2000_device::rom_bank_updated()
{
m_stream->update();
}
//-------------------------------------------------
// read_status - return the write pending status
//-------------------------------------------------
uint16_t bsmt2000_device::read_status()
{
return m_write_pending ? 0 : 1;
}
//-------------------------------------------------
// write_reg - handle writes to the BSMT2000
// register select interface
//-------------------------------------------------
void bsmt2000_device::write_reg(uint16_t data)
{
synchronize(TIMER_ID_REG_WRITE, data);
}
//-------------------------------------------------
// write_data - handle writes to the BSMT2000
// data port
//-------------------------------------------------
void bsmt2000_device::write_data(uint16_t data)
{
synchronize(TIMER_ID_DATA_WRITE, data);
// boost the interleave on a write so that the caller detects the status more accurately
machine().scheduler().boost_interleave(attotime::from_usec(1), attotime::from_usec(10));
}
//-------------------------------------------------
// tms_register_r - return the value written to
// the register select port
//-------------------------------------------------
READ16_MEMBER( bsmt2000_device::tms_register_r )
{
return m_register_select;
}
//-------------------------------------------------
// tms_data_r - return the value written to the
// data port
//-------------------------------------------------
READ16_MEMBER( bsmt2000_device::tms_data_r )
{
// also implicitly clear the write pending flag
m_write_pending = false;
if (!m_ready_callback.isnull())
m_ready_callback();
return m_write_data;
}
//-------------------------------------------------
// tms_rom_r - read a byte from the currently
// selected ROM bank and address
//-------------------------------------------------
READ16_MEMBER( bsmt2000_device::tms_rom_r )
{
// underlying logic assumes this is a sign-extended value
return (int8_t)read_byte((m_rom_bank << 16) + m_rom_address);
}
//-------------------------------------------------
// tms_rom_addr_w - selects which byte within the
// current ROM bank to access
//-------------------------------------------------
WRITE16_MEMBER( bsmt2000_device::tms_rom_addr_w )
{
m_rom_address = data;
}
//-------------------------------------------------
// tms_rom_bank_w - selects which bank of ROM to
// access
//-------------------------------------------------
WRITE16_MEMBER( bsmt2000_device::tms_rom_bank_w )
{
m_rom_bank = data;
}
//-------------------------------------------------
// tms_left_w - handle writes to the left channel
// DAC
//-------------------------------------------------
WRITE16_MEMBER( bsmt2000_device::tms_left_w )
{
m_stream->update();
m_left_data = data;
}
//-------------------------------------------------
// tms_right_w - handle writes to the right
// channel DAC
//-------------------------------------------------
WRITE16_MEMBER( bsmt2000_device::tms_right_w )
{
m_stream->update();
m_right_data = data;
}
//-------------------------------------------------
// tms_write_pending_r - return whether a write
// is pending; this data is fed into the BIO line
// on the TMS32015
//-------------------------------------------------
READ_LINE_MEMBER( bsmt2000_device::tms_write_pending_r )
{
return m_write_pending ? 1 : 0;
}