// license:BSD-3-Clause // copyright-holders:hap, Couriersud /********************************************************************************************** Texas Instruments TMS6100 Voice Synthesis Memory (VSM) References: - TMS 6100 Voice Synthesis Memory Data Manual - TMS 6125 Voice Synthesis Memory Data Manual - Speak & Spell patent US4189779 for low-level documentation - 1982 Mitsubishi Data Book (M58819S section) TODO: - implement clock pin(CLK) properly, xtal/timer - command processing timing is not accurate, on the real chip it will take a few microseconds - current implementation does not regard multi-chip configuration and pretends it is 1 chip, this will work fine under normal circumstances since CS would be disabled on invalid address - implement chip addressing (0-15 mask programmed, see above) - M58819S pins SL(PROM expansion input), POC(reset) ***********************************************************************************************/ #include "emu.h" #include "tms6100.h" // device definitions const device_type TMS6100 = &device_creator; tms6100_device::tms6100_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, u32 clock, const char *shortname, const char *source) : device_t(mconfig, type, name, tag, owner, clock, shortname, source), m_rom(*this, DEVICE_SELF), m_reverse_bits(false), m_4bit_mode(false) { } tms6100_device::tms6100_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) : device_t(mconfig, TMS6100, "TMS6100", tag, owner, clock, "tms6100", __FILE__), m_rom(*this, DEVICE_SELF), m_reverse_bits(false), m_4bit_mode(false) { } const device_type M58819 = &device_creator; m58819_device::m58819_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) : tms6100_device(mconfig, M58819, "M58819S", tag, owner, clock, "m58819s", __FILE__) { } //------------------------------------------------- // device_start - device-specific startup //------------------------------------------------- void tms6100_device::device_start() { m_rommask = m_rom.bytes() - 1; // zerofill m_address = 0; m_sa = 0; m_count = 0; m_prev_cmd = 0; m_prev_m = 0; m_add = 0; m_data = 0; m_m0 = 0; m_m1 = 0; m_cs = 1; m_clk = 0; m_rck = 0; // register for savestates save_item(NAME(m_address)); save_item(NAME(m_sa)); save_item(NAME(m_count)); save_item(NAME(m_prev_cmd)); save_item(NAME(m_prev_m)); save_item(NAME(m_add)); save_item(NAME(m_data)); save_item(NAME(m_m0)); save_item(NAME(m_m1)); save_item(NAME(m_cs)); save_item(NAME(m_clk)); save_item(NAME(m_rck)); } void m58819_device::device_start() { tms6100_device::device_start(); m_reverse_bits = true; // m58819 'vsm-emulator' chip expects ROM bit order backwards } // external i/o WRITE_LINE_MEMBER(tms6100_device::m0_w) { m_m0 = (state) ? 1 : 0; } WRITE_LINE_MEMBER(tms6100_device::m1_w) { m_m1 = (state) ? 1 : 0; } WRITE_LINE_MEMBER(tms6100_device::cs_w) { // chip select pin m_cs = (state) ? 1 : 0; } WRITE_LINE_MEMBER(tms6100_device::rck_w) { // gate/mask for clk m_rck = (state) ? 1 : 0; } WRITE8_MEMBER(tms6100_device::add_w) { m_add = data & 0xf; } READ8_MEMBER(tms6100_device::data_r) { return m_data & 0xf; } READ_LINE_MEMBER(tms6100_device::data_line_r) { // DATA/ADD8 return (m_data & 8) ? 1 : 0; } WRITE_LINE_MEMBER(tms6100_device::clk_w) { // process on falling edge if (m_clk && !m_rck && !state) { if (m_cs) { // new command enabled on rising edge of m0/m1 u8 m = m_m1 << 1 | m_m0; if ((m & ~m_prev_m & 1) || (m & ~m_prev_m & 2)) handle_command(m); m_prev_m = m; } } m_clk = (state) ? 1 : 0; } // m0/m1 commands void tms6100_device::handle_command(u8 cmd) { enum { M_NOP = 0, M_TB, M_LA, M_RB }; switch (cmd) { // TB: transfer bit (read) case M_TB: if (m_prev_cmd == M_LA) { // dummy read after LA m_count = 0; } else { // load new data from rom if (m_count == 0) { m_sa = m_rom[m_address & m_rommask]; // M58819S reads serial data reversed if (m_reverse_bits) m_sa = BITSWAP8(m_sa,0,1,2,3,4,5,6,7); } else { // or shift(rotate) right m_sa = (m_sa >> 1) | (m_sa << 7 & 0x80); } // output to DATA pin(s) if (!m_4bit_mode) { // 1-bit mode: SA0 to ADD8/DATA m_data = m_sa << 3 & 8; } else { // 4-bit mode: SA0-3 or SA3-6(!) to DATA if (m_count & 1) m_data = m_sa >> 3 & 0xf; else m_data = m_sa & 0xf; } // 8 bits in 1-bit mode, otherwise 2 nybbles m_count = (m_count + 1) & (m_4bit_mode ? 1 : 7); // TB8 if (m_count == 0) m_address++; // CS bits too } break; // LA: load address case M_LA: if (m_prev_cmd == M_TB) { // start LA after TB m_address = (m_address & ~0xf) | m_add; m_count = 0; } else if (m_prev_cmd == M_LA) { // load consecutive address bits (including CS bits) // the 8-step counter PLA is shared between LA and TB if (m_count < 4) { const u8 shift = 4 * (m_count+1); m_address = (m_address & ~(0xf << shift)) | (m_add << shift); } m_count = (m_count + 1) & 7; } break; // RB: read and branch case M_RB: // process RB after LA or TB8 if (m_prev_cmd == M_LA || (m_prev_cmd == M_TB && m_count == 0)) { m_count = 0; // load new address bits (14 bits on TMS6100) u16 rb = m_rom[m_address & m_rommask]; m_address++; rb |= (m_rom[m_address & m_rommask] << 8); m_address = (m_address & ~0x3fff) | (rb & 0x3fff); } break; default: break; } m_prev_cmd = cmd; }