// license:BSD-3-Clause // copyright-holders:Frank Palazzolo, Aaron Giles, Jonathan Gevaryahu, Raphael Nabet, Couriersud, Michael Zapf /* spchroms.c - This is an emulator for "typical" speech ROMs from TI, as used by TI99/4(a). In order to support its speech processor, TI designed some ROMs with a 1-bit data bus and 4-bit address bus (multiplexed 5 times to provide a 18-bit address). A fairly complete description of such a ROM (tms6100) is found in the tms5220 datasheet. One notable thing is that the address is a byte address (*NOT* a bit address). This file is designed to be interfaced with the tms5220 core. Interfacing it with the tms5110 would make sense, too. TODO: Create separate devices for TMS6100 & TMS6125 Implement the serial protocol */ #include "emu.h" #include "spchrom.h" #define TMS5220_ADDRESS_MASK 0x3FFFFUL /* 18-bit mask for tms5220 address */ // device type definition const device_type SPEECHROM = &device_creator; speechrom_device::speechrom_device(const machine_config &mconfig, std::string tag, device_t *owner, UINT32 clock) : device_t(mconfig, SPEECHROM, "SPEECHROM", tag, owner, clock, "speechrom", __FILE__), m_speechrom_data(nullptr), m_speechROMlen(0), m_speechROMaddr(0), m_load_pointer(0), m_ROM_bits_count(0), m_reverse(false) { } /* Read 'count' bits serially from speech ROM Actually, the ROM is expected to have reversed bit order, but there are many dumps with normal bit order. compatibility mode: 01234567 01234567 01234567 ... correct mode: 76543210 76543210 76543210 ... */ int speechrom_device::read(int count) { int val; int spchbyte; int pos; if (m_load_pointer) { /* first read after load address is ignored */ m_load_pointer = 0; count--; } if (m_speechROMaddr < m_speechROMlen) { val = 0; pos = 8 - m_ROM_bits_count; spchbyte = (m_reverse? (m_speechrom_data[m_speechROMaddr] >> pos) : (m_speechrom_data[m_speechROMaddr] << pos)) & 0xff; while (count > 0) { val = val << 1; if ((spchbyte & (m_reverse? 0x01:0x80))!=0) val |= 1; spchbyte = m_reverse? (spchbyte >> 1) : (spchbyte << 1); count--; if (pos == 7) { pos = 0; m_speechROMaddr = (m_speechROMaddr + 1) & TMS5220_ADDRESS_MASK; if (m_speechROMaddr >= m_speechROMlen) count = 0; else spchbyte = m_speechrom_data[m_speechROMaddr]; } else pos++; } m_ROM_bits_count = 8 - pos; } else { val = 0; } return val; } /* Write an address nibble to speech ROM */ void speechrom_device::load_address(int data) { /* tms5220 data sheet says that if we load only one 4-bit nibble, it won't work. This code does not care about this. */ m_speechROMaddr = ( (m_speechROMaddr & ~(0xf << m_load_pointer)) | (((unsigned long) (data & 0xf)) << m_load_pointer) ) & TMS5220_ADDRESS_MASK; m_load_pointer += 4; m_ROM_bits_count = 8; } /* Perform a read and branch command */ void speechrom_device::read_and_branch() { /* tms5220 data sheet says that if more than one speech ROM (tms6100) is present, there is a bus contention. This code does not care about this. */ if (m_speechROMaddr < m_speechROMlen-1) m_speechROMaddr = (m_speechROMaddr & 0x3c000UL) | (((((unsigned long) m_speechrom_data[m_speechROMaddr]) << 8) | m_speechrom_data[m_speechROMaddr+1]) & 0x3fffUL); else if (m_speechROMaddr == m_speechROMlen-1) m_speechROMaddr = (m_speechROMaddr & 0x3c000UL) | ((((unsigned long) m_speechrom_data[m_speechROMaddr]) << 8) & 0x3fffUL); else m_speechROMaddr = (m_speechROMaddr & 0x3c000UL); m_ROM_bits_count = 8; } void speechrom_device::device_start() { memory_region *region = memregion(tag()); if (region == nullptr) { throw emu_fatalerror("No region for device '%s'\n", tag().c_str()); } m_speechrom_data = region->base(); m_speechROMlen = region->bytes(); save_item(NAME(m_speechROMaddr)); save_item(NAME(m_load_pointer)); save_item(NAME(m_ROM_bits_count)); }