// 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(*this) , 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 //------------------------------------------------- void bsmt2000_device::device_add_mconfig(machine_config &config) { tms32015_device &tms(TMS32015(config, "bsmt2000", DERIVED_CLOCK(1,1))); tms.set_addrmap(AS_PROGRAM, &bsmt2000_device::tms_program_map); // data map is internal to the CPU tms.set_addrmap(AS_IO, &bsmt2000_device::tms_io_map); tms.bio().set(FUNC(bsmt2000_device::tms_write_pending_r)); } //------------------------------------------------- // device_start - device-specific startup //------------------------------------------------- void bsmt2000_device::device_start() { m_ready_callback.resolve(); // 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; outputs[1][samp] = m_right_data; } } //------------------------------------------------- // 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 ) { // DSP code expects a 16-bit value with the data in the high byte return (int16_t)(read_byte((m_rom_bank << 16) + m_rom_address) << 8); } //------------------------------------------------- // 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; }