summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/sound/tms5220.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/sound/tms5220.cpp')
-rw-r--r--src/devices/sound/tms5220.cpp503
1 files changed, 291 insertions, 212 deletions
diff --git a/src/devices/sound/tms5220.cpp b/src/devices/sound/tms5220.cpp
index 4889cd85c60..77869045bea 100644
--- a/src/devices/sound/tms5220.cpp
+++ b/src/devices/sound/tms5220.cpp
@@ -312,7 +312,7 @@ Exidy's 'Victory' and 'Victor Banana' (need verify for both)
Pinball: Several (don't know names offhand, have not checked schematics; likely Zaccaria's 'Farfalla')
Home computer: Street Electronics Corp.'s Apple II 'Echo 2' Speech
synthesizer (later cards only); Texas Instruments' 'Speak and Learn'
-scanner wand unit.
+scanner wand unit; HP 27201A Speech Output Module (serial port connection).
TMS5220C AKA TSP5220C: (on stuff made from 1984 to 1992 or so)
Arcade: Atari's 'Indiana Jones and the Temple of Doom', '720',
@@ -366,41 +366,69 @@ emulating the tms5220 in MCU code). Look for a 16-pin chip at U6 labeled
one B cycle per interpolation step) */
#define FORCE_SUBC_RELOAD 1
-
/* *****debugging defines***** */
-// general, somewhat obsolete, catch all for debugs which don't fit elsewhere
-#define LOG_GENERAL (1 << 0)
-/* 5220 only; above dumps the data written to the tms52xx to stdout, useful
+/* 5220 only; above dumps the data written to the tms52xx to the error log, useful
for making logged data dumps for real hardware tests */
-#define LOG_DUMP_INPUT_DATA (1 << 1)
+#define LOG_DUMP_INPUT_DATA (1U << 1)
+
// 5220 only; above debugs FIFO stuff: writes, reads and flag updates
-#define LOG_FIFO (1 << 2)
+#define LOG_FIFO (1U << 2)
+
+// Show parsing events
+#define LOG_PARSE (1U << 3)
+
// dumps each speech frame as binary
-#define LOG_PARSE_FRAME_DUMP_BIN (1 << 3)
+#define LOG_PARSE_FRAME_DUMP_BIN (1U << 4)
+
// dumps each speech frame as hex
-#define LOG_PARSE_FRAME_DUMP_HEX (1 << 4)
+#define LOG_PARSE_FRAME_DUMP_HEX (1U << 5)
+
// dumps info if a frame ran out of data
-#define LOG_FRAME_ERRORS (1 << 6)
+#define LOG_FRAME_ERRORS (1U << 6)
+
// dumps all non-speech-data command writes
-#define LOG_COMMAND_DUMP (1 << 7)
-// dumps decoded info about command writes
-#define LOG_COMMAND_VERBOSE (1 << 8)
-// spams the errorlog with i/o ready messages whenever the ready or irq pin is read
-#define LOG_PIN_READS (1 << 9)
+#define LOG_COMMAND (1U << 7)
+
+// Additional information about command processing
+#define LOG_COMMAND_VERBOSE (1U << 8)
+
+// Show NOP command (tends to be noisy)
+#define LOG_COMMAND_NOP (1U << 9)
+
+// spams the error log with I/O ready messages whenever the ready or IRQ pin is read
+#define LOG_PIN_READS (1U << 10)
+
// dumps debug information related to the sample generation loop, i.e. whether interpolation is inhibited or not, and what the current and target values for each frame are.
-#define LOG_GENERATION (1 << 10)
+#define LOG_GENERATION (1U << 11)
+
// dumps MUCH MORE debug information related to the sample generation loop, namely the excitation, energy, pitch, k*, and output values for EVERY SINGLE SAMPLE during a frame.
-#define LOG_GENERATION_VERBOSE (1 << 11)
+#define LOG_GENERATION_VERBOSE (1U << 12)
+
// dumps the lattice filter state data each sample.
-#define LOG_LATTICE (1 << 12)
-// dumps info to stderr whenever the analog clip hardware is (or would be) clipping the signal.
-#define LOG_CLIP (1 << 13)
+#define LOG_LATTICE (1U << 13)
+
+// Show errors in matrix multiplication
+#define LOG_MULTIPLY (1U << 14)
+
+// dumps info to the error log whenever the analog clip hardware is (or would be) clipping the signal.
+#define LOG_CLIP (1U << 15)
+
// debugs the io ready callback timer
-#define LOG_IO_READY (1 << 14)
+#define LOG_IO_READY (1U << 16)
+
// debugs the tms5220_data_r and data_w access methods which actually respect rs and ws
-#define LOG_RS_WS (1 << 15)
+#define LOG_RS_WS (1U << 17)
+
+// Show the byte being written by the CPU to the VSP
+#define LOG_DATA_W (1U << 18)
+
+// Show interactions with speech rom
+#define LOG_VSM (1U << 19)
-//#define VERBOSE (LOG_GENERAL | LOG_DUMP_INPUT_DATA | LOG_FIFO | LOG_PARSE_FRAME_DUMP_HEX | LOG_FRAME_ERRORS | LOG_COMMAND_DUMP | LOG_COMMAND_VERBOSE | LOG_PIN_READS | LOG_GENERATION | LOG_GENERATION_VERBOSE | LOG_LATTICE | LOG_CLIP | LOG_IO_READY | LOG_RS_WS)
+// Show state changes
+#define LOG_STATE (1U << 20)
+
+//#define VERBOSE (LOG_FIFO | LOG_PARSE | LOG_FRAME_ERRORS | LOG_COMMAND | LOG_GENERATION | LOG_DATA_W | LOG_STATE)
#include "logmacro.h"
#define MAX_SAMPLE_CHUNK 512
@@ -440,6 +468,8 @@ emulating the tms5220 in MCU code). Look for a 16-pin chip at U6 labeled
static const uint8_t reload_table[4] = { 0, 2, 4, 6 }; //sample count reload for 5220c and cd2501ecd only; 5200 and 5220 always reload with 0; keep in mind this is loaded on IP=0 PC=12 subcycle=1 so it immediately will increment after one sample, effectively being 1,3,5,7 as in the comments above.
+#define NOCOMMAND 0xff
+
// Pull in the ROM tables
#include "tms5110r.hxx"
@@ -482,6 +512,9 @@ void tms5220_device::register_for_save_states()
save_item(NAME(m_irq_pin));
save_item(NAME(m_ready_pin));
+ save_item(NAME(m_command_register));
+ save_item(NAME(m_data_latched));
+
// current and previous frames
save_item(NAME(m_OLDE));
save_item(NAME(m_OLDP));
@@ -565,18 +598,15 @@ void tms5220_device::printbits(long data, int num)
/**********************************************************************************************
- tms5220_device::new_int_write -- wrap a write to the VSM
+ tms5220_device::vsm_write -- wrap a write sent to the VSM
***********************************************************************************************/
-void tms5220_device::new_int_write(uint8_t rc, uint8_t m0, uint8_t m1, uint8_t addr)
+void tms5220_device::vsm_write(uint8_t rc, uint8_t m0, uint8_t m1, uint8_t addr)
{
- if (!m_m0_cb.isnull())
- m_m0_cb(m0);
- if (!m_m1_cb.isnull())
- m_m1_cb(m1);
- if (!m_addr_cb.isnull())
- m_addr_cb((offs_t)0, addr);
- if (!m_romclk_cb.isnull())
+ m_m0_cb(m0);
+ m_m1_cb(m1);
+ m_addr_cb(offs_t(0), addr);
+ if (!m_romclk_cb.isunset())
{
//printf("rc %d\n", rc);
m_romclk_cb(rc);
@@ -585,32 +615,52 @@ void tms5220_device::new_int_write(uint8_t rc, uint8_t m0, uint8_t m1, uint8_t a
/**********************************************************************************************
- tms5220_device::new_int_write_addr -- wrap a 'load address' set of writes to the VSM
+ tms5220_device::vsm_write_addr -- wrap a 'load address' set of writes sent to the VSM
***********************************************************************************************/
-void tms5220_device::new_int_write_addr(uint8_t addr)
+void tms5220_device::vsm_write_addr(uint8_t addr)
{
- new_int_write(1, 0, 1, addr); // romclk 1, m0 0, m1 1, addr bus nybble = xxxx
- new_int_write(0, 0, 1, addr); // romclk 0, m0 0, m1 1, addr bus nybble = xxxx
- new_int_write(1, 0, 0, addr); // romclk 1, m0 0, m1 0, addr bus nybble = xxxx
- new_int_write(0, 0, 0, addr); // romclk 0, m0 0, m1 0, addr bus nybble = xxxx
+ vsm_write(1, 0, 1, addr); // romclk 1, m0 0, m1 1, addr bus nybble = xxxx
+ vsm_write(0, 0, 1, addr); // romclk 0, m0 0, m1 1, addr bus nybble = xxxx
+ vsm_write(1, 0, 0, addr); // romclk 1, m0 0, m1 0, addr bus nybble = xxxx
+ vsm_write(0, 0, 0, addr); // romclk 0, m0 0, m1 0, addr bus nybble = xxxx
}
/**********************************************************************************************
- tms5220_device::new_int_write_addr -- wrap a 'read bit' set of writes to the VSM
+ tms5220_device::vsm_read -- wrap a 'read bit' set of writes sent to the VSM
***********************************************************************************************/
-uint8_t tms5220_device::new_int_read()
+uint8_t tms5220_device::vsm_read()
{
- new_int_write(1, 1, 0, 0); // romclk 1, m0 1, m1 0, addr bus nybble = 0/open bus
- new_int_write(0, 1, 0, 0); // romclk 0, m0 1, m1 0, addr bus nybble = 0/open bus
- new_int_write(1, 0, 0, 0); // romclk 1, m0 0, m1 0, addr bus nybble = 0/open bus
- new_int_write(0, 0, 0, 0); // romclk 0, m0 0, m1 0, addr bus nybble = 0/open bus
- if (!m_data_cb.isnull())
- return m_data_cb();
- LOGMASKED(LOG_GENERAL, "WARNING: CALLBACK MISSING, RETURNING 0!\n");
- return 0;
+ vsm_write(1, 1, 0, 0); // romclk 1, m0 1, m1 0, addr bus nybble = 0/open bus
+ vsm_write(0, 1, 0, 0); // romclk 0, m0 1, m1 0, addr bus nybble = 0/open bus
+ vsm_write(1, 0, 0, 0); // romclk 1, m0 0, m1 0, addr bus nybble = 0/open bus
+ vsm_write(0, 0, 0, 0); // romclk 0, m0 0, m1 0, addr bus nybble = 0/open bus
+
+ uint8_t val = 0;
+
+ if (!m_data_cb.isunset())
+ val = m_data_cb();
+ else
+ LOGMASKED(LOG_VSM, "Warning: No speech memory attached, returning 0.\n");
+
+ return val;
+}
+
+/**********************************************************************************************
+
+ tms5220_device::vsm_read_and_branch -- wrap a 'read-and-branch' set of writes sent to the VSM
+
+***********************************************************************************************/
+void tms5220_device::vsm_read_and_branch()
+{
+ vsm_write(0, 1, 1, 0); // see tms5110.cpp
+ vsm_write(1, 1, 1, 0); // romclk 1, m0 1, m1 1, addr bus nybble = 0/open bus
+ vsm_write(0, 1, 1, 0); // romclk 0, m0 1, m1 1, addr bus nybble = 0/open bus
+ vsm_write(0, 0, 0, 0); // see tms5110.cpp
+ vsm_write(1, 0, 0, 0); // romclk 1, m0 0, m1 0, addr bus nybble = 0/open bus
+ vsm_write(0, 0, 0, 0); // romclk 0, m0 0, m1 0, addr bus nybble = 0/open bus
}
/**********************************************************************************************
@@ -632,7 +682,7 @@ void tms5220_device::data_write(int data)
m_fifo[m_fifo_tail] = data;
m_fifo_tail = (m_fifo_tail + 1) % FIFO_SIZE;
m_fifo_count++;
- LOGMASKED(LOG_FIFO, "data_write: Added byte to FIFO (current count=%2d)\n", m_fifo_count);
+ LOGMASKED(LOG_FIFO, "data_write: Added %02X byte to FIFO (current count=%2d)\n", data, m_fifo_count);
update_fifo_status_and_ints();
// if we just unset buffer low with that last write, and SPEN *was* zero (see circuit 251, sheet 12)
@@ -668,12 +718,14 @@ void tms5220_device::data_write(int data)
LOGMASKED(LOG_FIFO, "data_write: Ran out of room in the tms52xx FIFO! this should never happen!\n");
// at this point, /READY should remain HIGH/inactive until the FIFO has at least one byte open in it.
}
-
-
+ m_data_latched = false;
}
else //(! m_DDIS)
// R Nabet : we parse commands at once. It is necessary for such commands as read.
process_command(data);
+
+ if (!m_data_latched)
+ m_io_ready = true;
}
/**********************************************************************************************
@@ -741,21 +793,45 @@ void tms5220_device::update_fifo_status_and_ints()
// also, in this case, regardless if DDIS was set, unset it.
if (m_previous_talk_status && !talk_status())
{
- LOGMASKED(LOG_GENERAL, "Talk status WAS 1, is now 0, unsetting DDIS and firing an interrupt!\n");
+ LOGMASKED(LOG_STATE, "Talk status 1 -> 0, unsetting DDIS and firing an interrupt.\n");
set_interrupt_state(1);
m_DDIS = false;
+
+ m_previous_talk_status = false;
+
+ // Is there a command stuck in the command register due to speech output?
+ // Then resume it.
+ if (m_command_register != NOCOMMAND)
+ {
+ LOGMASKED(LOG_STATE, "Resume command execution: %02X\n", m_command_register);
+ process_command(m_command_register);
+
+ // Is there another data transfer pending? Resume it as well.
+ if (m_data_latched)
+ {
+ LOGMASKED(LOG_STATE, "Pending byte write: %02X\n", m_write_latch);
+ m_timer_io_ready->adjust(clocks_to_attotime(16), 1);
+ }
+ else
+ m_io_ready = true;
+
+ update_ready_state();
+ }
}
m_previous_talk_status = talk_status();
+ // update continuously when RSQ is held active low
+ if (m_rs_ws == 0x01 && !m_RDB_flag)
+ m_read_latch = status_read(false);
}
/**********************************************************************************************
- extract_bits -- extract a specific number of bits from the current input stream (FIFO or VSM)
+ read_bits -- read a specific number of bits from the current input stream (FIFO or VSM)
***********************************************************************************************/
-int tms5220_device::extract_bits(int count)
+int tms5220_device::read_bits(int count)
{
int val = 0;
@@ -778,32 +854,33 @@ int tms5220_device::extract_bits(int count)
}
else
{
-#ifndef USE_NEW_TMS6100_CODE
-/** TODO: get rid of this old code */
- // extract from VSM (speech ROM)
- if (m_speechrom)
- val = m_speechrom->read(count);
+ if (!m_m0_cb.isunset())
+ {
+ while (count--)
+ {
+ val = (val << 1) | vsm_read();
+ LOGMASKED(LOG_VSM, "bit read: %d\n", val&1);
+ }
+ }
else
- val = (1<<count)-1; // assume the input floats high if nothing is connected, so a spurious speak vsm command will eventually return a 0xF (STOP) frame which will halt speech
-#else
- while (count--)
{
- val = (val << 1) | new_int_read();
- LOGMASKED(LOG_GENERAL, "bit read: %d\n", val&1);
+ // assume the input floats high if nothing is connected, so a
+ // spurious speak vsm command will eventually return a 0xF (STOP)
+ // frame which will halt speech
+ val = (1<<count)-1;
}
-#endif
}
return val;
}
-/** TODO: dummy reads should be auto-done for tms52xx for the first read after an address load, but not tms51xx where they need to be done manually, if needed */
void tms5220_device::perform_dummy_read()
{
- if (m_schedule_dummy_read)
+ m_schedule_dummy_read = false;
+ if (!m_m0_cb.isunset())
{
- int data = new_int_read();
- LOGMASKED(LOG_GENERAL, "TMS5110 performing dummy read; value read = %1i\n", data & 1);
- m_schedule_dummy_read = false;
+ LOGMASKED(LOG_VSM, "TMS52xx performing dummy read\n");
+ vsm_write_addr(0);
+ vsm_read();
}
}
@@ -886,7 +963,7 @@ void tms5220_device::process(int16_t *buffer, unsigned int size)
int i, bitout;
int32_t this_sample;
- LOGMASKED(LOG_GENERAL, "process called with size of %d; IP=%d, PC=%d, subcycle=%d, m_SPEN=%d, m_TALK=%d, m_TALKD=%d\n", size, m_IP, m_PC, m_subcycle, m_SPEN, m_TALK, m_TALKD);
+ LOGMASKED(LOG_GENERATION, "process called with size of %d; IP=%d, PC=%d, subcycle=%d, m_SPEN=%d, m_TALK=%d, m_TALKD=%d\n", size, m_IP, m_PC, m_subcycle, m_SPEN, m_TALK, m_TALKD);
/* loop until the buffer is full or we've stopped speaking */
while (size > 0)
@@ -941,12 +1018,8 @@ void tms5220_device::process(int16_t *buffer, unsigned int size)
m_inhibit = false;
/* Debug info for current parsed frame */
- LOGMASKED(LOG_GENERATION, "OLDE: %d; NEWE: %d; OLDP: %d; NEWP: %d ", old_frame_silence_flag(), new_frame_silence_flag(), old_frame_unvoiced_flag(), new_frame_unvoiced_flag());
- LOGMASKED(LOG_GENERATION, "Processing new frame: ");
- if (!m_inhibit)
- LOGMASKED(LOG_GENERATION, "Normal Frame\n");
- else
- LOGMASKED(LOG_GENERATION, "Interpolation Inhibited\n");
+ LOGMASKED(LOG_GENERATION, "OLDE: %d; NEWE: %d; OLDP: %d; NEWP: %d\n", old_frame_silence_flag(), new_frame_silence_flag(), old_frame_unvoiced_flag(), new_frame_unvoiced_flag());
+ LOGMASKED(LOG_GENERATION, "Processing new frame: %s\n", !m_inhibit ? "Normal Frame" : "Interpolation Inhibited");
LOGMASKED(LOG_GENERATION, "*** current Energy, Pitch and Ks = %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d\n",m_current_energy, m_current_pitch, m_current_k[0], m_current_k[1], m_current_k[2], m_current_k[3], m_current_k[4], m_current_k[5], m_current_k[6], m_current_k[7], m_current_k[8], m_current_k[9]);
LOGMASKED(LOG_GENERATION, "*** target Energy(idx), Pitch, and Ks = %04d(%x),%04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d\n",
(m_coeff->energytable[m_new_frame_energy_idx] * (1-m_zpar)),
@@ -1219,8 +1292,8 @@ int32_t tms5220_device::matrix_multiply(int32_t a, int32_t b) const
while (b>16383) { b-=32768; }
while (b<-16384) { b+=32768; }
result = ((a*b)>>9); /** TODO: this isn't technically right to the chip, which truncates the lowest result bit, but it causes glitches otherwise. **/
- if (result>16383) LOGMASKED(LOG_GENERAL, "matrix multiplier overflowed! a: %x, b: %x, result: %x", a, b, result);
- if (result<-16384) LOGMASKED(LOG_GENERAL, "matrix multiplier underflowed! a: %x, b: %x, result: %x", a, b, result);
+ if (result>16383) LOGMASKED(LOG_MULTIPLY, "matrix multiplier overflowed! a: %x, b: %x, result: %x", a, b, result);
+ if (result<-16384) LOGMASKED(LOG_MULTIPLY, "matrix multiplier underflowed! a: %x, b: %x, result: %x", a, b, result);
return result;
}
@@ -1302,77 +1375,110 @@ int32_t tms5220_device::lattice_filter()
/**********************************************************************************************
- process_command -- extract a byte from the FIFO and interpret it as a command
+ process_command -- decode byte and run the command
+
+ During SPEAK, the address counter is locked against modification by
+ LOAD_ADDRESS. In that time, a LOAD_ADDRESS command does not terminate but
+ remains in the command register.
+ When another command is fed into the speech processor, the READY line is
+ lowered until the command register is cleared, then the new command is
+ loaded into the command register.
+
+ Note that the running SPEAK command does not block the command register;
+ it is a command that attempts to change the address during the SPEAK
+ process. [mz]
***********************************************************************************************/
-void tms5220_device::process_command(unsigned char cmd)
+void tms5220_device::process_command(uint8_t cmd)
{
- LOGMASKED(LOG_COMMAND_DUMP, "process_command called with parameter %02X\n", cmd);
+ m_command_register = cmd;
/* parse the command */
switch (cmd & 0x70)
{
case 0x10 : /* read byte */
- LOGMASKED(LOG_COMMAND_VERBOSE, "Read Byte command received\n");
+ LOGMASKED(LOG_COMMAND, "Read Byte command received (%02X)\n", cmd);
if (!talk_status()) /* TALKST must be clear for RDBY */
{
if (m_schedule_dummy_read)
{
m_schedule_dummy_read = false;
- if (m_speechrom)
- m_speechrom->read(1);
+ perform_dummy_read();
}
- if (m_speechrom)
- m_read_byte_register = m_speechrom->read(8); /* read one byte from speech ROM... */
+
+ m_read_byte_register = read_bits(8);
m_RDB_flag = true;
+ m_command_register = NOCOMMAND;
}
else
- LOGMASKED(LOG_COMMAND_VERBOSE, "Read Byte command received during TALK state, ignoring!\n");
+ LOGMASKED(LOG_COMMAND_VERBOSE, "Read Byte command received during TALK state, suspended.\n");
break;
- case 0x00: case 0x20: /* set rate (tms5220c and cd2501ecd only), otherwise NOP */
+ case 0x00:
+ case 0x20: /* set rate (tms5220c and cd2501ecd only), otherwise NOP */
if (TMS5220_HAS_RATE_CONTROL)
{
- LOGMASKED(LOG_COMMAND_VERBOSE, "Set Rate (or NOP) command received\n");
+ LOGMASKED(LOG_COMMAND_NOP, "Set Rate (or NOP) command received (%02X)\n", cmd);
m_c_variant_rate = cmd&0x0F;
}
else
- LOGMASKED(LOG_COMMAND_VERBOSE, "NOP command received\n");
+ LOGMASKED(LOG_COMMAND_NOP, "NOP command received (%02X)\n", cmd);
+
+ m_command_register = NOCOMMAND;
break;
case 0x30 : /* read and branch */
if (!talk_status()) /* TALKST must be clear for RB */
{
- LOGMASKED(LOG_COMMAND_VERBOSE, "Read and Branch command received\n");
+ LOGMASKED(LOG_COMMAND, "Read and Branch command received (%02X)\n", cmd);
m_RDB_flag = false;
- if (m_speechrom)
- m_speechrom->read_and_branch();
+
+ if (m_schedule_dummy_read)
+ {
+ m_schedule_dummy_read = false;
+ perform_dummy_read();
+ }
+
+ if (!m_m0_cb.isunset())
+ vsm_read_and_branch();
+ else
+ LOGMASKED(LOG_VSM, "WARNING: Read-and-branch: No memory attached to VSP\n");
+
+ m_command_register = NOCOMMAND;
}
break;
case 0x40 : /* load address */
- LOGMASKED(LOG_COMMAND_VERBOSE, "Load Address command received\n");
if (!talk_status()) /* TALKST must be clear for LA */
{
- /* tms5220 data sheet says that if we load only one 4-bit nibble, it won't work.
- This code does not care about this. */
- if (m_speechrom)
- m_speechrom->load_address(cmd & 0x0f);
+ LOGMASKED(LOG_COMMAND, "Load Address command received (%02X)\n", cmd);
+
+ // tms5220 data sheet says that if we load only one 4-bit nibble,
+ // it won't work. This code does not care about this.
+ if (!m_m0_cb.isunset())
+ vsm_write_addr(cmd & 0x0f);
+
m_schedule_dummy_read = true;
+ m_command_register = NOCOMMAND;
}
else
- LOGMASKED(LOG_COMMAND_VERBOSE, "Load Address command received during TALK state, ignoring!\n");
+ {
+ // The Load Address command is not ignored during speech output,
+ // as tests show. In fact, it is normally executed when the speech
+ // terminates.
+ LOGMASKED(LOG_COMMAND_VERBOSE, "Load Address command received during TALK state, suspended.\n");
+ }
break;
case 0x50 : /* speak */
- LOGMASKED(LOG_COMMAND_VERBOSE, "Speak (VSM) command received\n");
+ LOGMASKED(LOG_COMMAND, "Speak (VSM) command received (%02X)\n", cmd);
if (m_schedule_dummy_read)
{
m_schedule_dummy_read = false;
- if (m_speechrom)
- m_speechrom->read(1);
+ perform_dummy_read();
}
+
m_SPEN = 1;
#ifdef FAST_START_HACK
m_TALK = 1;
@@ -1395,10 +1501,12 @@ void tms5220_device::process_command(unsigned char cmd)
m_new_frame_k_idx[i] = 0xF;
for (int i = 7; i < m_coeff->num_k; i++)
m_new_frame_k_idx[i] = 0x7;
+
+ m_command_register = NOCOMMAND;
break;
case 0x60 : /* speak external */
- LOGMASKED(LOG_COMMAND_VERBOSE, "Speak External command received\n");
+ LOGMASKED(LOG_COMMAND, "Speak External command received (%02X)\n", cmd);
// SPKEXT going active asserts /SPKEE for 2 clocks, which clears the FIFO and its counters
std::fill(std::begin(m_fifo), std::end(m_fifo), 0);
@@ -1423,17 +1531,20 @@ void tms5220_device::process_command(unsigned char cmd)
for (int i = 7; i < m_coeff->num_k; i++)
m_new_frame_k_idx[i] = 0x7;
m_RDB_flag = false;
+
+ m_command_register = NOCOMMAND;
break;
case 0x70 : /* reset */
- LOGMASKED(LOG_COMMAND_VERBOSE, "Reset command received\n");
+ LOGMASKED(LOG_COMMAND, "Reset command received (%02X)\n", cmd);
if (m_schedule_dummy_read)
{
m_schedule_dummy_read = false;
- if (m_speechrom)
- m_speechrom->read(1);
+ perform_dummy_read();
}
+
reset();
+ m_command_register = NOCOMMAND;
break;
}
@@ -1474,7 +1585,7 @@ void tms5220_device::parse_frame()
has a 2 bit rate preceding it, grab two bits here and store them as the rate; */
if ((TMS5220_HAS_RATE_CONTROL) && (m_c_variant_rate & 0x04))
{
- i = extract_bits(2);
+ i = read_bits(2);
printbits(i, 2);
LOGMASKED(LOG_PARSE_FRAME_DUMP_BIN | LOG_PARSE_FRAME_DUMP_HEX, " ");
m_IP = reload_table[i];
@@ -1486,7 +1597,7 @@ void tms5220_device::parse_frame()
if (m_DDIS && m_buffer_empty) goto ranout;
// attempt to extract the energy index
- m_new_frame_energy_idx = extract_bits(m_coeff->energy_bits);
+ m_new_frame_energy_idx = read_bits(m_coeff->energy_bits);
printbits(m_new_frame_energy_idx, m_coeff->energy_bits);
LOGMASKED(LOG_PARSE_FRAME_DUMP_BIN | LOG_PARSE_FRAME_DUMP_HEX, " ");
update_fifo_status_and_ints();
@@ -1497,12 +1608,12 @@ void tms5220_device::parse_frame()
// attempt to extract the repeat flag
- rep_flag = extract_bits(1);
+ rep_flag = read_bits(1);
printbits(rep_flag, 1);
LOGMASKED(LOG_PARSE_FRAME_DUMP_BIN | LOG_PARSE_FRAME_DUMP_HEX, " ");
// attempt to extract the pitch
- m_new_frame_pitch_idx = extract_bits(m_coeff->pitch_bits);
+ m_new_frame_pitch_idx = read_bits(m_coeff->pitch_bits);
printbits(m_new_frame_pitch_idx, m_coeff->pitch_bits);
LOGMASKED(LOG_PARSE_FRAME_DUMP_BIN | LOG_PARSE_FRAME_DUMP_HEX, " ");
// if the new frame is unvoiced, be sure to zero out the k5-k10 parameters
@@ -1516,7 +1627,7 @@ void tms5220_device::parse_frame()
// extract first 4 K coefficients
for (i = 0; i < 4; i++)
{
- m_new_frame_k_idx[i] = extract_bits(m_coeff->kbits[i]);
+ m_new_frame_k_idx[i] = read_bits(m_coeff->kbits[i]);
printbits(m_new_frame_k_idx[i], m_coeff->kbits[i]);
LOGMASKED(LOG_PARSE_FRAME_DUMP_BIN | LOG_PARSE_FRAME_DUMP_HEX, " ");
update_fifo_status_and_ints();
@@ -1533,7 +1644,7 @@ void tms5220_device::parse_frame()
// If we got here, we need the remaining 6 K's
for (i = 4; i < m_coeff->num_k; i++)
{
- m_new_frame_k_idx[i] = extract_bits(m_coeff->kbits[i]);
+ m_new_frame_k_idx[i] = read_bits(m_coeff->kbits[i]);
printbits(m_new_frame_k_idx[i], m_coeff->kbits[i]);
LOGMASKED(LOG_PARSE_FRAME_DUMP_BIN | LOG_PARSE_FRAME_DUMP_HEX, " ");
update_fifo_status_and_ints();
@@ -1542,9 +1653,9 @@ void tms5220_device::parse_frame()
LOGMASKED(LOG_PARSE_FRAME_DUMP_BIN | LOG_PARSE_FRAME_DUMP_HEX, "\n");
if (m_DDIS)
- LOGMASKED(LOG_GENERAL, "Parsed a frame successfully in FIFO - %d bits remaining\n", (m_fifo_count*8)-(m_fifo_bits_taken));
+ LOGMASKED(LOG_PARSE, "Parsed a frame successfully in FIFO - %d bits remaining\n", (m_fifo_count*8)-(m_fifo_bits_taken));
else
- LOGMASKED(LOG_GENERAL, "Parsed a frame successfully in ROM\n");
+ LOGMASKED(LOG_PARSE, "Parsed a frame successfully in ROM\n");
return;
ranout:
@@ -1564,9 +1675,11 @@ void tms5220_device::set_interrupt_state(int state)
LOGMASKED(LOG_PIN_READS, "irq pin set to state %d\n", state);
- if (!m_irq_handler.isnull() && state != m_irq_pin)
+ if (state != m_irq_pin)
+ {
+ m_irq_pin = state;
m_irq_handler(!state);
- m_irq_pin = state;
+ }
}
/**********************************************************************************************
@@ -1581,8 +1694,7 @@ void tms5220_device::update_ready_state()
if (m_ready_pin != state)
{
LOGMASKED(LOG_PIN_READS, "ready pin set to state %d\n", state);
- if (!m_readyq_handler.isnull())
- m_readyq_handler(!state);
+ m_readyq_handler(!state);
m_ready_pin = state;
}
}
@@ -1594,19 +1706,6 @@ void tms5220_device::update_ready_state()
void tms5220_device::device_start()
{
- if (m_speechrom_tag)
- {
- m_speechrom = siblingdevice<speechrom_device>( m_speechrom_tag );
- if( !m_speechrom )
- {
- throw new emu_fatalerror("Error: %s '%s' can't find speechrom '%s'\n", shortname(), tag(), m_speechrom_tag );
- }
- }
- else
- {
- m_speechrom = nullptr;
- }
-
switch (m_variant)
{
case TMS5220_IS_TMC0281:
@@ -1639,19 +1738,10 @@ void tms5220_device::device_start()
fatalerror("Unknown variant in tms5220_set_variant\n");
}
- /* resolve callbacks */
- m_irq_handler.resolve();
- m_readyq_handler.resolve();
- m_m0_cb.resolve();
- m_m1_cb.resolve();
- m_romclk_cb.resolve();
- m_addr_cb.resolve();
- m_data_cb.resolve();
-
/* initialize a stream */
- m_stream = machine().sound().stream_alloc(*this, 0, 1, clock() / 80);
+ m_stream = stream_alloc(0, 1, clock() / 80);
- m_timer_io_ready = timer_alloc(0);
+ m_timer_io_ready = timer_alloc(FUNC(tms5220_device::set_io_ready), this);
/* not during reset which is called from within a write! */
m_io_ready = true;
@@ -1680,6 +1770,9 @@ void tms5220_device::device_reset()
update_ready_state();
m_buffer_empty = m_buffer_low = true;
+ m_command_register = NOCOMMAND;
+ m_data_latched = false;
+
m_RDB_flag = false;
/* initialize the energy/pitch/k states */
@@ -1703,18 +1796,8 @@ void tms5220_device::device_reset()
m_RNG = 0x1FFF;
std::fill(std::begin(m_u), std::end(m_u), 0);
std::fill(std::begin(m_x), std::end(m_x), 0);
- m_schedule_dummy_read = false;
- if (m_speechrom)
- {
- m_speechrom->load_address(0);
- // MZ: Do the dummy read immediately. The previous line will cause a
- // shift in the address pointer in the VSM. When the next command is a
- // load_address, no dummy read will occur, hence the address will be
- // incorrectly shifted.
- m_speechrom->read(1);
- m_schedule_dummy_read = false;
- }
+ perform_dummy_read();
// 5110 specific stuff
m_PDC = 0;
@@ -1724,68 +1807,68 @@ void tms5220_device::device_reset()
m_next_is_address = false;
m_addr_bit = 0;
m_CTL_buffer = 0;
-
}
-/**********************************************************************************************
-
- True timing
-
-***********************************************************************************************/
-
-void tms5220_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+TIMER_CALLBACK_MEMBER(tms5220_device::set_io_ready)
{
- switch(id)
+ /* bring up to date first */
+ m_stream->update();
+
+ LOGMASKED(LOG_IO_READY, "m_timer_io_ready timer fired, param = %02x, m_rs_ws = %02x\n", param, m_rs_ws);
+ if (param) // low->high ready state
{
- case 0: // m_timer_io_ready
- /* bring up to date first */
- m_stream->update();
- LOGMASKED(LOG_IO_READY, "m_timer_io_ready timer fired, param = %02x, m_rs_ws = %02x\n", param, m_rs_ws);
- if (param) // low->high ready state
+ switch (m_rs_ws)
{
- switch (m_rs_ws)
+ case 0x02:
+ /* Write */
+ LOGMASKED(LOG_IO_READY, "m_timer_io_ready: Attempting to service write...\n");
+ if ((m_fifo_count >= FIFO_SIZE) && m_DDIS) // if FIFO is full and we're in speak external mode
{
- case 0x02:
- /* Write */
- LOGMASKED(LOG_IO_READY, "m_timer_io_ready: Attempting to service write...\n");
- if ((m_fifo_count >= FIFO_SIZE) && m_DDIS) // if FIFO is full and we're in speak external mode
+ LOGMASKED(LOG_IO_READY, "m_timer_io_ready: in SPKEXT and FIFO was full! cannot service write now, delaying 16 cycles...\n");
+ m_timer_io_ready->adjust(clocks_to_attotime(16), 1);
+ break;
+ }
+ else
+ {
+ if (m_command_register != NOCOMMAND)
{
- LOGMASKED(LOG_IO_READY, "m_timer_io_ready: in SPKEXT and FIFO was full! cannot service write now, delaying 16 cycles...\n");
- m_timer_io_ready->adjust(clocks_to_attotime(16), 1);
- break;
+ // The command register is still busy; do not activate /READY
+ // and keep the data latch
+ LOGMASKED(LOG_IO_READY, "m_timer_io_ready: Command register not ready\n");
}
else
{
- LOGMASKED(LOG_IO_READY, "m_timer_io_ready: Serviced write: %02x\n", m_write_latch);
+ LOGMASKED(LOG_IO_READY, "m_timer_io_ready: Serviced write: %02X\n", m_write_latch);
+ m_data_latched = false;
data_write(m_write_latch);
- m_io_ready = param;
- break;
}
- case 0x01:
- /* Read */
- m_read_latch = status_read(true);
- LOGMASKED(LOG_IO_READY, "m_timer_io_ready: Serviced read, returning %02x\n", m_read_latch);
- m_io_ready = param;
- break;
- case 0x03:
- /* High Impedance */
- m_io_ready = param;
- case 0x00:
- /* illegal */
- m_io_ready = param;
+
break;
}
+ case 0x01:
+ /* Read */
+ m_read_latch = status_read(true);
+ LOGMASKED(LOG_IO_READY, "m_timer_io_ready: Serviced read, returning %02X\n", m_read_latch);
+ m_io_ready = true;
+ break;
+ case 0x03:
+ /* High Impedance */
+ m_io_ready = true;
+ break;
+ case 0x00:
+ /* illegal */
+ m_io_ready = true;
+ break;
}
-
- update_ready_state();
- break;
}
+
+ update_ready_state();
}
/*
* /RS line write handler
*/
-WRITE_LINE_MEMBER( tms5220_device::rsq_w )
+void tms5220_device::rsq_w(int state)
{
m_true_timing = true;
state &= 0x01;
@@ -1832,7 +1915,7 @@ WRITE_LINE_MEMBER( tms5220_device::rsq_w )
/*
* /WS line write handler
*/
-WRITE_LINE_MEMBER( tms5220_device::wsq_w )
+void tms5220_device::wsq_w(int state)
{
m_true_timing = true;
state &= 0x01;
@@ -1851,7 +1934,7 @@ WRITE_LINE_MEMBER( tms5220_device::wsq_w )
LOGMASKED(LOG_RS_WS, "tms5220_wsq_w: illegal\n");
return;
}
- else if ( new_val == 3)
+ else if (new_val == 3)
{
/* high impedance */
m_read_latch = 0xff;
@@ -1965,10 +2048,12 @@ void tms5220_device::combined_rsq_wsq_w(u8 data)
void tms5220_device::data_w(uint8_t data)
{
- LOGMASKED(LOG_RS_WS, "tms5220_write_data: data %02x\n", data);
+ LOGMASKED(LOG_DATA_W, "tms5220_write_data: data %02X\n", data);
/* bring up to date first */
m_stream->update();
m_write_latch = data;
+ m_data_latched = true;
+
if (!m_true_timing) // if we're in the default hacky mode where we don't bother with rsq_w and wsq_w...
data_write(m_write_latch); // ...force the write through instantly.
else
@@ -2019,7 +2104,7 @@ uint8_t tms5220_device::status_r()
***********************************************************************************************/
-READ_LINE_MEMBER( tms5220_device::readyq_r )
+int tms5220_device::readyq_r()
{
// prevent debugger from changing the internal state
if (!machine().side_effects_disabled())
@@ -2035,7 +2120,7 @@ READ_LINE_MEMBER( tms5220_device::readyq_r )
***********************************************************************************************/
-READ_LINE_MEMBER( tms5220_device::intq_r )
+int tms5220_device::intq_r()
{
// prevent debugger from changing the internal state
if (!machine().side_effects_disabled())
@@ -2055,24 +2140,19 @@ READ_LINE_MEMBER( tms5220_device::intq_r )
// sound_stream_update - handle a stream update
//-------------------------------------------------
-void tms5220_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples)
+void tms5220_device::sound_stream_update(sound_stream &stream)
{
int16_t sample_data[MAX_SAMPLE_CHUNK];
- stream_sample_t *buffer = outputs[0];
/* loop while we still have samples to generate */
- while (samples)
+ for (int sampindex = 0; sampindex < stream.samples(); )
{
- int length = (samples > MAX_SAMPLE_CHUNK) ? MAX_SAMPLE_CHUNK : samples;
- int index;
+ int length = (stream.samples() > MAX_SAMPLE_CHUNK) ? MAX_SAMPLE_CHUNK : stream.samples();
/* generate the samples and copy to the target buffer */
process(sample_data, length);
- for (index = 0; index < length; index++)
- *buffer++ = sample_data[index];
-
- /* account for the samples */
- samples -= length;
+ for (int index = 0; index < length; index++)
+ stream.put_int(0, sampindex++, sample_data[index], 32768);
}
}
@@ -2114,11 +2194,10 @@ tms5220_device::tms5220_device(const machine_config &mconfig, device_type type,
, m_variant(variant)
, m_irq_handler(*this)
, m_readyq_handler(*this)
- , m_speechrom_tag(nullptr)
, m_m0_cb(*this)
, m_m1_cb(*this)
, m_addr_cb(*this)
- , m_data_cb(*this)
+ , m_data_cb(*this, 0)
, m_romclk_cb(*this)
{
}