TMS5220: implemented talk status state machine properly as shown by patent. Got rid of m_target_* hack in favor of loading data from ROM as needed. Fixed ZPAR logic. Fixed pitch zeroing to match(?) patent. [Lord Nightmare]

This commit is contained in:
Lord-Nightmare 2015-08-27 19:08:56 -04:00
parent b6ca87aaf6
commit 12d07f9174
2 changed files with 221 additions and 192 deletions

View File

@ -361,9 +361,11 @@ void tms5220_device::register_for_save_states()
save_item(NAME(m_fifo_count)); save_item(NAME(m_fifo_count));
save_item(NAME(m_fifo_bits_taken)); save_item(NAME(m_fifo_bits_taken));
save_item(NAME(m_speaking_now)); save_item(NAME(m_previous_TALK_STATUS));
save_item(NAME(m_speak_external)); save_item(NAME(m_SPEN));
save_item(NAME(m_talk_status)); save_item(NAME(m_DDIS));
save_item(NAME(m_TALK));
save_item(NAME(m_TALKD));
save_item(NAME(m_buffer_low)); save_item(NAME(m_buffer_low));
save_item(NAME(m_buffer_empty)); save_item(NAME(m_buffer_empty));
save_item(NAME(m_irq_pin)); save_item(NAME(m_irq_pin));
@ -384,10 +386,6 @@ void tms5220_device::register_for_save_states()
save_item(NAME(m_current_pitch)); save_item(NAME(m_current_pitch));
save_item(NAME(m_current_k)); save_item(NAME(m_current_k));
save_item(NAME(m_target_energy));
save_item(NAME(m_target_pitch));
save_item(NAME(m_target_k));
save_item(NAME(m_previous_energy)); save_item(NAME(m_previous_energy));
save_item(NAME(m_subcycle)); save_item(NAME(m_subcycle));
@ -395,6 +393,9 @@ void tms5220_device::register_for_save_states()
save_item(NAME(m_PC)); save_item(NAME(m_PC));
save_item(NAME(m_IP)); save_item(NAME(m_IP));
save_item(NAME(m_inhibit)); save_item(NAME(m_inhibit));
save_item(NAME(m_uv_zpar));
save_item(NAME(m_zpar));
save_item(NAME(m_pitch_zero));
save_item(NAME(m_c_variant_rate)); save_item(NAME(m_c_variant_rate));
save_item(NAME(m_pitch_count)); save_item(NAME(m_pitch_count));
@ -465,10 +466,11 @@ static void printbits(long data, int num)
void tms5220_device::data_write(int data) void tms5220_device::data_write(int data)
{ {
int old_buffer_low = m_buffer_low;
#ifdef DEBUG_DUMP_INPUT_DATA #ifdef DEBUG_DUMP_INPUT_DATA
fprintf(stdout, "%c",data); fprintf(stdout, "%c",data);
#endif #endif
if (m_speak_external) // If we're in speak external mode if (m_DDIS) // If we're in speak external mode
{ {
// add this byte to the FIFO // add this byte to the FIFO
if (m_fifo_count < FIFO_SIZE) if (m_fifo_count < FIFO_SIZE)
@ -477,20 +479,26 @@ void tms5220_device::data_write(int data)
m_fifo_tail = (m_fifo_tail + 1) % FIFO_SIZE; m_fifo_tail = (m_fifo_tail + 1) % FIFO_SIZE;
m_fifo_count++; m_fifo_count++;
#ifdef DEBUG_FIFO #ifdef DEBUG_FIFO
logerror("data_write: Added byte to FIFO (current count=%2d)\n", m_fifo_count); fprintf(stderr,"data_write: Added byte to FIFO (current count=%2d)\n", m_fifo_count);
#endif #endif
update_fifo_status_and_ints(); update_fifo_status_and_ints();
if ((m_talk_status == 0) && (m_buffer_low == 0)) // we just unset buffer low with that last write, and talk status *was* zero... // if we just unset buffer low with that last write, and SPEN *was* zero (see circuit 251, sheet 12)
if ((m_SPEN == 0) && ((old_buffer_low == 1) && (m_buffer_low == 0))) // MUST HAVE EDGE DETECT
{ {
int i; int i;
#ifdef DEBUG_FIFO #ifdef DEBUG_FIFO
logerror("data_write triggered talk status to go active!\n"); fprintf(stderr,"data_write triggered SPEN to go active!\n");
#endif #endif
// ...then we now have enough bytes to start talking; clear out the new frame parameters (it will become old frame just before the first call to parse_frame() ) // ...then we now have enough bytes to start talking; set zpar and clear out the new frame parameters (it will become old frame just before the first call to parse_frame() )
// TODO: the 3 lines below (and others) are needed for victory to not fail its selftest due to a sample ending too late, may require additional investigation m_zpar = 1;
m_subcycle = m_subc_reload; m_uv_zpar = 1; // zero k4-k10 as well
m_PC = 0; m_OLDE = 1; // 'silence/zpar' frames are zero energy
m_IP = reload_table[m_c_variant_rate&0x3]; // is this correct? should this be always 7 instead, so that the new frame is loaded quickly? m_OLDP = 1; // 'silence/zpar' frames are zero pitch
#ifdef PERFECT_INTERPOLATION_HACK
m_old_zpar = 1; // zero all the old parameters
m_old_uv_zpar = 1; // zero old k4-k10 as well
#endif
m_SPEN = 1;
m_new_frame_energy_idx = 0; m_new_frame_energy_idx = 0;
m_new_frame_pitch_idx = 0; m_new_frame_pitch_idx = 0;
for (i = 0; i < 4; i++) for (i = 0; i < 4; i++)
@ -499,20 +507,20 @@ void tms5220_device::data_write(int data)
m_new_frame_k_idx[i] = 0xF; m_new_frame_k_idx[i] = 0xF;
for (i = 7; i < m_coeff->num_k; i++) for (i = 7; i < m_coeff->num_k; i++)
m_new_frame_k_idx[i] = 0x7; m_new_frame_k_idx[i] = 0x7;
m_talk_status = m_speaking_now = 1;
} }
} }
else else
{ {
#ifdef DEBUG_FIFO #ifdef DEBUG_FIFO
logerror("data_write: Ran out of room in the tms52xx FIFO! this should never happen!\n"); fprintf(stderr,"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. // at this point, /READY should remain HIGH/inactive until the fifo has at least one byte open in it.
#endif #endif
} }
} }
else //(! m_speak_external) else //(! m_DDIS)
// R Nabet : we parse commands at once. It is necessary for such commands as read. // R Nabet : we parse commands at once. It is necessary for such commands as read.
process_command(data); process_command(data);
} }
@ -560,31 +568,31 @@ void tms5220_device::update_fifo_status_and_ints()
m_buffer_low = 0; m_buffer_low = 0;
/* BE is set if neither byte 15 nor 14 of the fifo are in use; this /* BE is set if neither byte 15 nor 14 of the fifo are in use; this
translates to having fifo_count equal to exactly 0 */ translates to having fifo_count equal to exactly 0
*/
if (m_fifo_count == 0) if (m_fifo_count == 0)
{ {
// generate an interrupt if necessary; if /BE was inactive and is now active, set int. // generate an interrupt if necessary; if /BE was inactive and is now active, set int.
if (!m_buffer_empty) if (!m_buffer_empty)
set_interrupt_state(1); set_interrupt_state(1);
m_buffer_empty = 1; m_buffer_empty = 1;
m_TALK = m_SPEN = 0; // /BE being active clears the TALK(TCON) status which in turn clears SPEN
} }
else else
m_buffer_empty = 0; m_buffer_empty = 0;
/* TS is talk status and is set elsewhere in the fifo parser and in // generate an interrupt if /TS was active, and is now inactive.
the SPEAK command handler; however, if /BE is true during speak external // also, in this case, regardless if DDIS was set, unset it.
mode, it is immediately unset here. */ if (m_previous_TALK_STATUS == 1 && (TALK_STATUS == 0))
if ((m_speak_external == 1) && (m_buffer_empty == 1))
{ {
// generate an interrupt: /TS was active, and is now inactive. #ifdef VERBOSE
if (m_talk_status == 1) fprintf(stderr,"Talk status WAS 1, is now 0, unsetting DDIS and firing an interrupt!\n");
{ #endif
m_talk_status = m_speak_external = 0; set_interrupt_state(1);
set_interrupt_state(1); m_DDIS = 0;
}
} }
/* Note that TS being unset will also generate an interrupt when a STOP m_previous_TALK_STATUS = TALK_STATUS;
frame is encountered; this is handled in the sample generator code and not here */
} }
/********************************************************************************************** /**********************************************************************************************
@ -597,7 +605,7 @@ int tms5220_device::extract_bits(int count)
{ {
int val = 0; int val = 0;
if (m_speak_external) if (m_DDIS)
{ {
// extract from FIFO // extract from FIFO
while (count--) while (count--)
@ -643,10 +651,10 @@ int tms5220_device::status_read()
/* clear the interrupt pin on status read */ /* clear the interrupt pin on status read */
set_interrupt_state(0); set_interrupt_state(0);
#ifdef DEBUG_PIN_READS #ifdef DEBUG_PIN_READS
logerror("Status read: TS=%d BL=%d BE=%d\n", m_talk_status, m_buffer_low, m_buffer_empty); fprintf(stderr,"Status read: TS=%d BL=%d BE=%d\n", TALK_STATUS, m_buffer_low, m_buffer_empty);
#endif #endif
return (m_talk_status << 7) | (m_buffer_low << 6) | (m_buffer_empty << 5); return (TALK_STATUS << 7) | (m_buffer_low << 6) | (m_buffer_empty << 5);
} }
} }
@ -660,9 +668,9 @@ int tms5220_device::status_read()
int tms5220_device::ready_read() int tms5220_device::ready_read()
{ {
#ifdef DEBUG_PIN_READS #ifdef DEBUG_PIN_READS
logerror("ready_read: ready pin read, io_ready is %d, fifo count is %d\n", m_io_ready, m_fifo_count); fprintf(stderr,"ready_read: ready pin read, io_ready is %d, fifo count is %d, DDIS(speak external) is %d\n", m_io_ready, m_fifo_count, m_DDIS);
#endif #endif
return ((m_fifo_count < FIFO_SIZE)||(!m_speak_external)) && m_io_ready; return ((m_fifo_count < FIFO_SIZE)||(!m_DDIS)) && m_io_ready;
} }
@ -718,7 +726,7 @@ int tms5220_device::cycles_to_ready()
int tms5220_device::int_read() int tms5220_device::int_read()
{ {
#ifdef DEBUG_PIN_READS #ifdef DEBUG_PIN_READS
logerror("int_read: irq pin read, state is %d\n", m_irq_pin); fprintf(stderr,"int_read: irq pin read, state is %d\n", m_irq_pin);
#endif #endif
return m_irq_pin; return m_irq_pin;
} }
@ -733,34 +741,22 @@ int tms5220_device::int_read()
void tms5220_device::process(INT16 *buffer, unsigned int size) void tms5220_device::process(INT16 *buffer, unsigned int size)
{ {
int buf_count=0; int buf_count=0;
int i, bitout, zpar; int i, bitout;
INT32 this_sample; INT32 this_sample;
/* the following gotos are probably safe to remove */ /* the following gotos are probably safe to remove */
/* if we're empty and still not speaking, fill with nothingness */ /* if we're empty and still not speaking, fill with nothingness */
if (!m_speaking_now) if (!m_TALKD)
goto empty; goto empty;
/* if speak external is set, but talk status is not (yet) set, #ifdef VERBOSE
wait for buffer low to clear */ fprintf(stderr,"not empty 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);
if (!m_talk_status && m_speak_external && m_buffer_low) #endif
goto empty;
render:
/* loop until the buffer is full or we've stopped speaking */ /* loop until the buffer is full or we've stopped speaking */
while ((size > 0) && m_speaking_now) while ((size > 0) && m_TALKD)
{ {
/* if it is the appropriate time to update the old energy/pitch indices,
* i.e. when IP=7, PC=12, T=17, subcycle=2, do so. Since IP=7 PC=12 T=17
* is JUST BEFORE the transition to IP=0 PC=0 T=0 sybcycle=(0 or 1),
* which happens 4 T-cycles later), we change on the latter.
* The indices are updated here ~12 PCs before the new frame is applied.
*/
/** TODO: the patents 4331836, 4335277, and 4419540 disagree about the timing of this **/
if ((m_IP == 0) && (m_PC == 0) && (m_subcycle < 2))
{
m_OLDE = (m_new_frame_energy_idx == 0);
m_OLDP = (m_new_frame_pitch_idx == 0);
}
/* if we're ready for a new frame to be applied, i.e. when IP=0, PC=12, Sub=1 /* if we're ready for a new frame to be applied, i.e. when IP=0, PC=12, Sub=1
* (In reality, the frame was really loaded incrementally during the entire IP=0 * (In reality, the frame was really loaded incrementally during the entire IP=0
@ -783,48 +779,28 @@ void tms5220_device::process(INT16 *buffer, unsigned int size)
m_old_frame_k_idx[i] = m_new_frame_k_idx[i]; m_old_frame_k_idx[i] = m_new_frame_k_idx[i];
#endif #endif
/* if the talk status was clear last frame, halt speech now. */ /* Parse a new frame into the new_target_energy, new_target_pitch and new_target_k[] */
if (m_talk_status == 0) parse_frame();
{
#ifdef DEBUG_GENERATION // if the new frame is unvoiced (or silenced via ZPAR), be sure to zero out the k5-k10 parameters
fprintf(stderr,"tms5220_process: processing frame: talk status = 0 caused by stop frame or buffer empty, halting speech.\n"); // NOTE: this is probably the bug the tms5100/tmc0280 has, pre-rev D, I think.
// GUESS: Pre-rev D versions start zeroing k5-k10 immediately upon new frame load regardless of interpolation inhibit
// I.e. ZPAR = /TALKD || (PC>5&&P=0)
// GUESS: D and later versions only start or stop zeroing k5-k10 at the IP7->IP0 transition AFTER the frame
// I.e. ZPAR = /TALKD || (PC>5&&OLDP)
#ifdef PERFECT_INTERPOLATION_HACK
m_old_uv_zpar = m_uv_zpar;
m_old_zpar = m_zpar; // unset old zpar on new frame
#endif #endif
if (m_speaking_now == 1) // we're done, set all coeffs to idle state but keep going for a bit... m_zpar = 0;
{ //m_uv_zpar = (OLD_FRAME_UNVOICED_FLAG||m_zpar); // GUESS: fixed version in tmc0280d/tms5100a/cd280x/tms5110
/**TODO: should index clearing be done here, or elsewhere? **/ m_uv_zpar = (NEW_FRAME_UNVOICED_FLAG||m_zpar); // GUESS: buggy version in tmc0280/tms5100
m_new_frame_energy_idx = 0;
m_new_frame_pitch_idx = 0;
for (i = 0; i < 4; i++)
m_new_frame_k_idx[i] = 0;
for (i = 4; i < 7; i++)
m_new_frame_k_idx[i] = 0xF;
for (i = 7; i < m_coeff->num_k; i++)
m_new_frame_k_idx[i] = 0x7;
m_speaking_now = 2; // wait 8 extra interp periods before shutting down so we can interpolate everything to zero state
}
else // m_speaking_now == 2 // now we're really done.
{
m_speaking_now = 0; // finally halt speech
goto empty;
}
}
/* if the new frame is a stop frame, unset both TALK and SPEN (via TCON). TALKD remains active while the energy is ramping to 0. */
/* Parse a new frame into the new_target_energy, new_target_pitch and new_target_k[],
* but only if we're not just about to end speech */
if (m_speaking_now == 1) parse_frame();
#ifdef DEBUG_PARSE_FRAME_DUMP
fprintf(stderr,"\n");
#endif
/* if the new frame is a stop frame, set an interrupt and set talk status to 0 */
/** TODO: investigate this later! **/
if (NEW_FRAME_STOP_FLAG == 1) if (NEW_FRAME_STOP_FLAG == 1)
{ {
m_talk_status = m_speak_external = 0; m_TALK = m_SPEN = 0;
set_interrupt_state(1); }
update_fifo_status_and_ints();
}
/* in all cases where interpolation would be inhibited, set the inhibit flag; otherwise clear it. /* in all cases where interpolation would be inhibited, set the inhibit flag; otherwise clear it.
Interpolation inhibit cases: Interpolation inhibit cases:
@ -841,35 +817,31 @@ void tms5220_device::process(INT16 *buffer, unsigned int size)
else // normal frame, normal interpolation else // normal frame, normal interpolation
m_inhibit = 0; m_inhibit = 0;
/* load new frame targets from tables, using parsed indices */
m_target_energy = m_coeff->energytable[m_new_frame_energy_idx];
m_target_pitch = m_coeff->pitchtable[m_new_frame_pitch_idx];
zpar = NEW_FRAME_UNVOICED_FLAG; // find out if parameters k5-k10 should be zeroed
for (i = 0; i < 4; i++)
m_target_k[i] = m_coeff->ktable[i][m_new_frame_k_idx[i]];
for (i = 4; i < m_coeff->num_k; i++)
m_target_k[i] = (m_coeff->ktable[i][m_new_frame_k_idx[i]] * (1-zpar));
#ifdef DEBUG_GENERATION #ifdef DEBUG_GENERATION
/* Debug info for current parsed frame */ /* Debug info for current parsed frame */
fprintf(stderr, "OLDE: %d; OLDP: %d; ", m_OLDE, m_OLDP); fprintf(stderr, "OLDE: %d; OLDP: %d; ", m_OLDE, m_OLDP);
fprintf(stderr,"Processing frame: "); fprintf(stderr,"Processing new frame: ");
if (m_inhibit == 0) if (m_inhibit == 0)
fprintf(stderr, "Normal Frame\n"); fprintf(stderr, "Normal Frame\n");
else else
fprintf(stderr,"Interpolation Inhibited\n"); fprintf(stderr,"Interpolation Inhibited\n");
fprintf(stderr,"*** 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]); fprintf(stderr,"*** 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]);
fprintf(stderr,"*** target Energy(idx), Pitch, and Ks = %04d(%x),%04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d\n",m_target_energy, m_new_frame_energy_idx, m_target_pitch, m_target_k[0], m_target_k[1], m_target_k[2], m_target_k[3], m_target_k[4], m_target_k[5], m_target_k[6], m_target_k[7], m_target_k[8], m_target_k[9]); fprintf(stderr,"*** 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)),
m_new_frame_energy_idx,
(m_coeff->pitchtable[m_new_frame_pitch_idx] * (1-m_zpar)),
(m_coeff->ktable[0][m_new_frame_k_idx[0]] * (1-m_zpar)),
(m_coeff->ktable[1][m_new_frame_k_idx[1]] * (1-m_zpar)),
(m_coeff->ktable[2][m_new_frame_k_idx[2]] * (1-m_zpar)),
(m_coeff->ktable[3][m_new_frame_k_idx[3]] * (1-m_zpar)),
(m_coeff->ktable[4][m_new_frame_k_idx[4]] * (1-m_uv_zpar)),
(m_coeff->ktable[5][m_new_frame_k_idx[5]] * (1-m_uv_zpar)),
(m_coeff->ktable[6][m_new_frame_k_idx[6]] * (1-m_uv_zpar)),
(m_coeff->ktable[7][m_new_frame_k_idx[7]] * (1-m_uv_zpar)),
(m_coeff->ktable[8][m_new_frame_k_idx[8]] * (1-m_uv_zpar)),
(m_coeff->ktable[9][m_new_frame_k_idx[9]] * (1-m_uv_zpar)) );
#endif #endif
/* if TS is now 0, ramp the energy down to 0. Is this really correct to hardware? */
if (m_talk_status == 0)
{
#ifdef DEBUG_GENERATION
fprintf(stderr,"Talk status is 0, forcing target energy to 0\n");
#endif
m_target_energy = 0;
}
} }
else // Not a new frame, just interpolate the existing frame. else // Not a new frame, just interpolate the existing frame.
{ {
@ -878,30 +850,26 @@ void tms5220_device::process(INT16 *buffer, unsigned int size)
int samples_per_frame = m_subc_reload?175:266; // either (13 A cycles + 12 B cycles) * 7 interps for normal SPEAK/SPKEXT, or (13*2 A cycles + 12 B cycles) * 7 interps for SPKSLOW int samples_per_frame = m_subc_reload?175:266; // either (13 A cycles + 12 B cycles) * 7 interps for normal SPEAK/SPKEXT, or (13*2 A cycles + 12 B cycles) * 7 interps for SPKSLOW
//int samples_per_frame = m_subc_reload?200:304; // either (13 A cycles + 12 B cycles) * 8 interps for normal SPEAK/SPKEXT, or (13*2 A cycles + 12 B cycles) * 8 interps for SPKSLOW //int samples_per_frame = m_subc_reload?200:304; // either (13 A cycles + 12 B cycles) * 8 interps for normal SPEAK/SPKEXT, or (13*2 A cycles + 12 B cycles) * 8 interps for SPKSLOW
int current_sample = (m_subcycle - m_subc_reload)+(m_PC*(3-m_subc_reload))+((m_subc_reload?25:38)*((m_IP-1)&7)); int current_sample = (m_subcycle - m_subc_reload)+(m_PC*(3-m_subc_reload))+((m_subc_reload?25:38)*((m_IP-1)&7));
zpar = OLD_FRAME_UNVOICED_FLAG;
//fprintf(stderr, "CS: %03d", current_sample); //fprintf(stderr, "CS: %03d", current_sample);
// reset the current energy, pitch, etc to what it was at frame start // reset the current energy, pitch, etc to what it was at frame start
m_current_energy = m_coeff->energytable[m_old_frame_energy_idx]; m_current_energy = (m_coeff->energytable[m_old_frame_energy_idx] * (1-m_old_zpar));
m_current_pitch = m_coeff->pitchtable[m_old_frame_pitch_idx]; m_current_pitch = (m_coeff->pitchtable[m_old_frame_pitch_idx] * (1-m_old_zpar));
for (i = 0; i < 4; i++) for (i = 0; i < m_coeff->num_k; i++)
m_current_k[i] = m_coeff->ktable[i][m_old_frame_k_idx[i]]; m_current_k[i] = (m_coeff->ktable[i][m_old_frame_k_idx[i]] * (1-((i<4)?m_old_zpar:m_old_uv_zpar)));
for (i = 4; i < m_coeff->num_k; i++)
m_current_k[i] = (m_coeff->ktable[i][m_old_frame_k_idx[i]] * (1-zpar));
// now adjust each value to be exactly correct for each of the samples per frame // now adjust each value to be exactly correct for each of the samples per frame
if (m_IP != 0) // if we're still interpolating... if (m_IP != 0) // if we're still interpolating...
{ {
m_current_energy += (((m_target_energy - m_current_energy)*(1-inhibit_state))*current_sample)/samples_per_frame; m_current_energy = (m_current_energy + (((m_coeff->energytable[m_new_frame_energy_idx] - m_current_energy)*(1-inhibit_state))*current_sample)/samples_per_frame)*(1-m_zpar);
m_current_pitch += (((m_target_pitch - m_current_pitch)*(1-inhibit_state))*current_sample)/samples_per_frame; m_current_pitch = (m_current_pitch + (((m_coeff->pitchtable[m_new_frame_pitch_idx] - m_current_pitch)*(1-inhibit_state))*current_sample)/samples_per_frame)*(1-m_zpar);
for (i = 0; i < m_coeff->num_k; i++) for (i = 0; i < m_coeff->num_k; i++)
m_current_k[i] += (((m_target_k[i] - m_current_k[i])*(1-inhibit_state))*current_sample)/samples_per_frame; m_current_k[i] = (m_current_k[i] + (((m_coeff->ktable[i][m_new_frame_k_idx[i]] - m_current_k[i])*(1-inhibit_state))*current_sample)/samples_per_frame)*(1-((i<4)?m_zpar:m_uv_zpar));
} }
else // we're done, play this frame for 1/8 frame. else // we're done, play this frame for 1/8 frame.
{ {
m_current_energy = m_target_energy; m_current_energy = (m_coeff->energytable[m_new_frame_energy_idx] * (1-m_zpar));
m_current_pitch = m_target_pitch; m_current_pitch = (m_coeff->pitchtable[m_new_frame_pitch_idx] * (1-m_zpar));
for (i = 0; i < m_coeff->num_k; i++) for (i = 0; i < m_coeff->num_k; i++)
m_current_k[i] = m_target_k[i]; m_current_k[i] = (m_coeff->ktable[i][m_new_frame_k_idx[i]] * (1-((i<4)?m_zpar:m_uv_zpar)));
} }
#else #else
//Updates to parameters only happen on subcycle '2' (B cycle) of PCs. //Updates to parameters only happen on subcycle '2' (B cycle) of PCs.
@ -910,16 +878,17 @@ void tms5220_device::process(INT16 *buffer, unsigned int size)
switch(m_PC) switch(m_PC)
{ {
case 0: /* PC = 0, B cycle, write updated energy */ case 0: /* PC = 0, B cycle, write updated energy */
m_current_energy += (((m_target_energy - m_current_energy)*(1-inhibit_state)) INTERP_SHIFT); m_current_energy = (m_current_energy + (((m_coeff->energytable[m_new_frame_energy_idx] - m_current_energy)*(1-inhibit_state)) INTERP_SHIFT))*(1-m_zpar);
break; break;
case 1: /* PC = 1, B cycle, write updated pitch */ case 1: /* PC = 1, B cycle, write updated pitch */
m_current_pitch += (((m_target_pitch - m_current_pitch)*(1-inhibit_state)) INTERP_SHIFT); m_current_pitch = (m_current_pitch + (((m_coeff->pitchtable[m_new_frame_pitch_idx] - m_current_pitch)*(1-inhibit_state)) INTERP_SHIFT))*(1-m_zpar);
break; break;
case 2: case 3: case 4: case 5: case 6: case 7: case 8: case 9: case 10: case 11: case 2: case 3: case 4: case 5: case 6: case 7: case 8: case 9: case 10: case 11:
/* PC = 2 through 11, B cycle, write updated K1 through K10 */ /* PC = 2 through 11, B cycle, write updated K1 through K10 */
m_current_k[m_PC-2] += (((m_target_k[m_PC-2] - m_current_k[m_PC-2])*(1-inhibit_state)) INTERP_SHIFT); m_current_k[m_PC-2] = (m_current_k[m_PC-2] + (((m_coeff->ktable[m_PC-2][m_new_frame_k_idx[m_PC-2]] - m_current_k[m_PC-2])*(1-inhibit_state)) INTERP_SHIFT))*(((m_PC-2)>4)?(1-m_uv_zpar):(1-m_zpar));
break; break;
case 12: /* PC = 12, do nothing */ case 12: /* PC = 12 */
/* we should NEVER reach this point, PC=12 doesn't have a subcycle 2 */
break; break;
} }
} }
@ -967,7 +936,12 @@ void tms5220_device::process(INT16 *buffer, unsigned int size)
//fprintf(stderr,"X:%04d E:%03d P:%03d Pc:%03d ", m_excitation_data, m_current_energy, m_current_pitch, m_pitch_count); //fprintf(stderr,"X:%04d E:%03d P:%03d Pc:%03d ", m_excitation_data, m_current_energy, m_current_pitch, m_pitch_count);
for (i=0; i<10; i++) for (i=0; i<10; i++)
fprintf(stderr,"K%d:%04d ", i+1, m_current_k[i]); fprintf(stderr,"K%d:%04d ", i+1, m_current_k[i]);
fprintf(stderr,"Out:%06d", this_sample); fprintf(stderr,"Out:%06d ", this_sample);
//#ifdef PERFECT_INTERPOLATION_HACK
// fprintf(stderr,"%d%d%d%d",m_old_zpar,m_zpar,m_old_uv_zpar,m_uv_zpar);
//#else
// fprintf(stderr,"x%dx%d",m_zpar,m_uv_zpar);
//#endif
fprintf(stderr,"\n"); fprintf(stderr,"\n");
#endif #endif
/* next, force result to 14 bits (since its possible that the addition at the final (k1) stage of the lattice overflowed) */ /* next, force result to 14 bits (since its possible that the addition at the final (k1) stage of the lattice overflowed) */
@ -993,7 +967,7 @@ void tms5220_device::process(INT16 *buffer, unsigned int size)
// Update all counts // Update all counts
m_subcycle++; m_subcycle++;
if ((m_subcycle == 2) && (m_PC == 12)) if ((m_subcycle == 2) && (m_PC == 12)) // RESETF3
{ {
/* Circuit 412 in the patent acts a reset, resetting the pitch counter to 0 /* Circuit 412 in the patent acts a reset, resetting the pitch counter to 0
* if INHIBIT was true during the most recent frame transition. * if INHIBIT was true during the most recent frame transition.
@ -1003,7 +977,28 @@ void tms5220_device::process(INT16 *buffer, unsigned int size)
* (and hence INHIBIT itself 2 t-cycles later). We do it here because it is * (and hence INHIBIT itself 2 t-cycles later). We do it here because it is
* convenient and should make no difference in output. * convenient and should make no difference in output.
*/ */
if ((m_IP == 7)&&(m_inhibit==1)) m_pitch_count = 0; if ((m_IP == 7)&&(m_inhibit==1)) m_pitch_zero = 1;
if ((m_IP == 0)&&(m_pitch_zero==1)) m_pitch_zero = 0;
if (m_IP == 7) // RESETL4
{
// Latch OLDE and OLDP
OLD_FRAME_SILENCE_FLAG = NEW_FRAME_SILENCE_FLAG; // m_OLDE
OLD_FRAME_UNVOICED_FLAG = NEW_FRAME_UNVOICED_FLAG; // m_OLDP
/* if TALK was clear last frame, halt speech now, since TALKD (latched from TALK on new frame) just went inactive. */
#ifdef DEBUG_GENERATION
fprintf(stderr,"RESETL4, about to update status: IP=%d, PC=%d, subcycle=%d, m_SPEN=%d, m_TALK=%d, m_TALKD=%d\n", m_IP, m_PC, m_subcycle, m_SPEN, m_TALK, m_TALKD);
#endif
#ifdef DEBUG_GENERATION
if (m_TALK == 0)
fprintf(stderr,"tms5220_process: processing frame: TALKD = 0 caused by stop frame or buffer empty, halting speech.\n");
#endif
m_TALKD = m_TALK; // TALKD is latched from TALK
update_fifo_status_and_ints(); // to trigger an interrupt if TALK_STATUS is now inactive
m_TALK = m_SPEN; // TALK is latched from SPEN
#ifdef DEBUG_GENERATION
fprintf(stderr,"RESETL4, status updated: IP=%d, PC=%d, subcycle=%d, m_SPEN=%d, m_TALK=%d, m_TALKD=%d\n", m_IP, m_PC, m_subcycle, m_SPEN, m_TALK, m_TALKD);
#endif
}
m_subcycle = m_subc_reload; m_subcycle = m_subc_reload;
m_PC = 0; m_PC = 0;
m_IP++; m_IP++;
@ -1015,7 +1010,7 @@ void tms5220_device::process(INT16 *buffer, unsigned int size)
m_PC++; m_PC++;
} }
m_pitch_count++; m_pitch_count++;
if (m_pitch_count >= m_current_pitch) m_pitch_count = 0; if ((m_pitch_count >= m_current_pitch)||(m_pitch_zero == 1)) m_pitch_count = 0;
m_pitch_count &= 0x1FF; m_pitch_count &= 0x1FF;
buf_count++; buf_count++;
size--; size--;
@ -1023,15 +1018,24 @@ void tms5220_device::process(INT16 *buffer, unsigned int size)
empty: empty:
#ifdef VERBOSE
fprintf(stderr,"empty 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);
#endif
while (size > 0) while (size > 0)
{ {
m_subcycle++; m_subcycle++;
if ((m_subcycle == 2) && (m_PC == 12)) if ((m_subcycle == 2) && (m_PC == 12)) // RESETF3
{ {
if (m_IP == 7) // RESETL4
{
m_TALKD = m_TALK; // TALKD is latched from TALK
m_TALK = m_SPEN; // TALK is latched from SPEN
}
m_subcycle = m_subc_reload; m_subcycle = m_subc_reload;
m_PC = 0; m_PC = 0;
m_IP++; m_IP++;
m_IP&=0x7; m_IP&=0x7;
if (m_TALKD) goto render;
} }
else if (m_subcycle == 3) else if (m_subcycle == 3)
{ {
@ -1182,6 +1186,7 @@ INT32 tms5220_device::lattice_filter()
void tms5220_device::process_command(unsigned char cmd) void tms5220_device::process_command(unsigned char cmd)
{ {
int i;
#ifdef DEBUG_COMMAND_DUMP #ifdef DEBUG_COMMAND_DUMP
fprintf(stderr,"process_command called with parameter %02X\n",cmd); fprintf(stderr,"process_command called with parameter %02X\n",cmd);
#endif #endif
@ -1189,7 +1194,7 @@ void tms5220_device::process_command(unsigned char cmd)
switch (cmd & 0x70) switch (cmd & 0x70)
{ {
case 0x10 : /* read byte */ case 0x10 : /* read byte */
if (m_talk_status == 0) /* TALKST must be clear for RDBY */ if (TALK_STATUS == 0) /* TALKST must be clear for RDBY */
{ {
if (m_schedule_dummy_read) if (m_schedule_dummy_read)
{ {
@ -1211,10 +1216,10 @@ void tms5220_device::process_command(unsigned char cmd)
break; break;
case 0x30 : /* read and branch */ case 0x30 : /* read and branch */
if (m_talk_status == 0) /* TALKST must be clear for RB */ if (TALK_STATUS == 0) /* TALKST must be clear for RB */
{ {
#ifdef VERBOSE #ifdef VERBOSE
logerror("read and branch command received\n"); fprintf(stderr,"read and branch command received\n");
#endif #endif
m_RDB_flag = FALSE; m_RDB_flag = FALSE;
if (m_speechrom) if (m_speechrom)
@ -1223,7 +1228,7 @@ void tms5220_device::process_command(unsigned char cmd)
break; break;
case 0x40 : /* load address */ case 0x40 : /* load address */
if (m_talk_status == 0) /* TALKST must be clear for LA */ if (TALK_STATUS == 0) /* TALKST must be clear for LA */
{ {
/* tms5220 data sheet says that if we load only one 4-bit nibble, it won't work. /* tms5220 data sheet says that if we load only one 4-bit nibble, it won't work.
This code does not care about this. */ This code does not care about this. */
@ -1240,17 +1245,19 @@ void tms5220_device::process_command(unsigned char cmd)
if (m_speechrom) if (m_speechrom)
m_speechrom->read(1); m_speechrom->read(1);
} }
m_speaking_now = 1; m_SPEN = 1;
m_speak_external = 0; m_DDIS = 0;
m_talk_status = 1; /* start immediately */ m_zpar = 1; // zero all the parameters
/* clear out variables before speaking */ m_uv_zpar = 1; // zero k4-k10 as well
// TODO: similar to the victory case described above, but for VSM speech m_OLDE = 1; // 'silence/zpar' frames are zero energy
m_subcycle = m_subc_reload; m_OLDP = 1; // 'silence/zpar' frames are zero pitch
m_PC = 0; #ifdef PERFECT_INTERPOLATION_HACK
m_IP = reload_table[m_c_variant_rate&0x3]; m_old_zpar = 1; // zero all the old parameters
m_old_uv_zpar = 1; // zero old k4-k10 as well
#endif
// following is semi-hack but matches idle state observed on chip
m_new_frame_energy_idx = 0; m_new_frame_energy_idx = 0;
m_new_frame_pitch_idx = 0; m_new_frame_pitch_idx = 0;
int i;
for (i = 0; i < 4; i++) for (i = 0; i < 4; i++)
m_new_frame_k_idx[i] = 0; m_new_frame_k_idx[i] = 0;
for (i = 4; i < 7; i++) for (i = 4; i < 7; i++)
@ -1260,9 +1267,27 @@ void tms5220_device::process_command(unsigned char cmd)
break; break;
case 0x60 : /* speak external */ case 0x60 : /* speak external */
//SPKEXT going active activates SPKEE which clears the fifo // SPKEXT going active activates SPKEE which clears the fifo
m_fifo_head = m_fifo_tail = m_fifo_count = m_fifo_bits_taken = 0; m_fifo_head = m_fifo_tail = m_fifo_count = m_fifo_bits_taken = 0;
m_speak_external = 1; // SPEN is enabled when the fifo passes half full (falling edge of BL signal)
m_DDIS = 1;
m_zpar = 1; // zero all the parameters
m_uv_zpar = 1; // zero k4-k10 as well
m_OLDE = 1; // 'silence/zpar' frames are zero energy
m_OLDP = 1; // 'silence/zpar' frames are zero pitch
#ifdef PERFECT_INTERPOLATION_HACK
m_old_zpar = 1; // zero all the old parameters
m_old_uv_zpar = 1; // zero old k4-k10 as well
#endif
// following is semi-hack but matches idle state observed on chip
m_new_frame_energy_idx = 0;
m_new_frame_pitch_idx = 0;
for (i = 0; i < 4; i++)
m_new_frame_k_idx[i] = 0;
for (i = 4; i < 7; i++)
m_new_frame_k_idx[i] = 0xF;
for (i = 7; i < m_coeff->num_k; i++)
m_new_frame_k_idx[i] = 0x7;
m_RDB_flag = FALSE; m_RDB_flag = FALSE;
break; break;
@ -1308,7 +1333,7 @@ void tms5220_device::parse_frame()
m_IP = reload_table[m_c_variant_rate&0x3]; m_IP = reload_table[m_c_variant_rate&0x3];
update_fifo_status_and_ints(); update_fifo_status_and_ints();
if (!m_talk_status) goto ranout; if (m_DDIS && m_buffer_empty) goto ranout;
// attempt to extract the energy index // attempt to extract the energy index
m_new_frame_energy_idx = extract_bits(m_coeff->energy_bits); m_new_frame_energy_idx = extract_bits(m_coeff->energy_bits);
@ -1317,7 +1342,7 @@ void tms5220_device::parse_frame()
fprintf(stderr," "); fprintf(stderr," ");
#endif #endif
update_fifo_status_and_ints(); update_fifo_status_and_ints();
if (!m_talk_status) goto ranout; if (m_DDIS && m_buffer_empty) goto ranout;
// if the energy index is 0 or 15, we're done // if the energy index is 0 or 15, we're done
if ((m_new_frame_energy_idx == 0) || (m_new_frame_energy_idx == 15)) if ((m_new_frame_energy_idx == 0) || (m_new_frame_energy_idx == 15))
return; return;
@ -1337,7 +1362,7 @@ void tms5220_device::parse_frame()
fprintf(stderr," "); fprintf(stderr," ");
#endif #endif
update_fifo_status_and_ints(); update_fifo_status_and_ints();
if (!m_talk_status) goto ranout; if (m_DDIS && m_buffer_empty) goto ranout;
// if this is a repeat frame, just do nothing, it will reuse the old coefficients // if this is a repeat frame, just do nothing, it will reuse the old coefficients
if (rep_flag) if (rep_flag)
return; return;
@ -1351,7 +1376,7 @@ void tms5220_device::parse_frame()
fprintf(stderr," "); fprintf(stderr," ");
#endif #endif
update_fifo_status_and_ints(); update_fifo_status_and_ints();
if (!m_talk_status) goto ranout; if (m_DDIS && m_buffer_empty) goto ranout;
} }
// if the pitch index was zero, we only need 4 K's... // if the pitch index was zero, we only need 4 K's...
@ -1370,19 +1395,22 @@ void tms5220_device::parse_frame()
fprintf(stderr," "); fprintf(stderr," ");
#endif #endif
update_fifo_status_and_ints(); update_fifo_status_and_ints();
if (!m_talk_status) goto ranout; if (m_DDIS && m_buffer_empty) goto ranout;
} }
#ifdef DEBUG_PARSE_FRAME_DUMP
fprintf(stderr,"\n");
#endif
#ifdef VERBOSE #ifdef VERBOSE
if (m_speak_external) if (m_DDIS)
logerror("Parsed a frame successfully in FIFO - %d bits remaining\n", (m_fifo_count*8)-(m_fifo_bits_taken)); fprintf(stderr,"Parsed a frame successfully in FIFO - %d bits remaining\n", (m_fifo_count*8)-(m_fifo_bits_taken));
else else
logerror("Parsed a frame successfully in ROM\n"); fprintf(stderr,"Parsed a frame successfully in ROM\n");
#endif #endif
return; return;
ranout: ranout:
#ifdef DEBUG_FRAME_ERRORS #ifdef DEBUG_FRAME_ERRORS
logerror("Ran out of bits on a parse!\n"); fprintf(stderr,"Ran out of bits on a parse!\n");
#endif #endif
return; return;
} }
@ -1397,7 +1425,7 @@ void tms5220_device::set_interrupt_state(int state)
{ {
if (!TMS5220_IS_52xx) return; // bail out if not a 52xx chip, since there's no int pin if (!TMS5220_IS_52xx) return; // bail out if not a 52xx chip, since there's no int pin
#ifdef DEBUG_PIN_READS #ifdef DEBUG_PIN_READS
logerror("irq pin set to state %d\n", state); fprintf(stderr,"irq pin set to state %d\n", state);
#endif #endif
if (!m_irq_handler.isnull() && state != m_irq_pin) if (!m_irq_handler.isnull() && state != m_irq_pin)
m_irq_handler(!state); m_irq_handler(!state);
@ -1414,7 +1442,7 @@ void tms5220_device::update_ready_state()
{ {
int state = ready_read(); int state = ready_read();
#ifdef DEBUG_PIN_READS #ifdef DEBUG_PIN_READS
logerror("ready pin set to state %d\n", state); fprintf(stderr,"ready pin set to state %d\n", state);
#endif #endif
if (!m_readyq_handler.isnull() && state != m_ready_pin) if (!m_readyq_handler.isnull() && state != m_ready_pin)
m_readyq_handler(!state); m_readyq_handler(!state);
@ -1514,7 +1542,7 @@ void tms5220_device::device_reset()
/* initialize the chip state */ /* initialize the chip state */
/* Note that we do not actually clear IRQ on start-up : IRQ is even raised if m_buffer_empty or m_buffer_low are 0 */ /* Note that we do not actually clear IRQ on start-up : IRQ is even raised if m_buffer_empty or m_buffer_low are 0 */
m_speaking_now = m_speak_external = m_talk_status = m_irq_pin = m_ready_pin = 0; m_SPEN = m_DDIS = m_TALK = m_TALKD = m_previous_TALK_STATUS = m_irq_pin = m_ready_pin = 0;
set_interrupt_state(0); set_interrupt_state(0);
update_ready_state(); update_ready_state();
m_buffer_empty = m_buffer_low = 1; m_buffer_empty = m_buffer_low = 1;
@ -1525,12 +1553,13 @@ void tms5220_device::device_reset()
#ifdef PERFECT_INTERPOLATION_HACK #ifdef PERFECT_INTERPOLATION_HACK
m_old_frame_energy_idx = m_old_frame_pitch_idx = 0; m_old_frame_energy_idx = m_old_frame_pitch_idx = 0;
memset(m_old_frame_k_idx, 0, sizeof(m_old_frame_k_idx)); memset(m_old_frame_k_idx, 0, sizeof(m_old_frame_k_idx));
m_old_zpar = 0;
#endif #endif
m_new_frame_energy_idx = m_current_energy = m_target_energy = m_previous_energy = 0; m_new_frame_energy_idx = m_current_energy = m_previous_energy = 0;
m_new_frame_pitch_idx = m_current_pitch = m_target_pitch = 0; m_new_frame_pitch_idx = m_current_pitch = 0;
m_zpar = m_uv_zpar = 0;
memset(m_new_frame_k_idx, 0, sizeof(m_new_frame_k_idx)); memset(m_new_frame_k_idx, 0, sizeof(m_new_frame_k_idx));
memset(m_current_k, 0, sizeof(m_current_k)); memset(m_current_k, 0, sizeof(m_current_k));
memset(m_target_k, 0, sizeof(m_target_k));
/* initialize the sample generators */ /* initialize the sample generators */
m_inhibit = 1; m_inhibit = 1;
@ -1574,7 +1603,7 @@ void tms5220_device::device_timer(emu_timer &timer, device_timer_id id, int para
/* Write */ /* Write */
/* bring up to date first */ /* bring up to date first */
#ifdef DEBUG_IO_READY #ifdef DEBUG_IO_READY
logerror("Serviced write: %02x\n", m_write_latch); fprintf(stderr,"Serviced write: %02x\n", m_write_latch);
//fprintf(stderr, "Processed write data: %02X\n", m_write_latch); //fprintf(stderr, "Processed write data: %02X\n", m_write_latch);
#endif #endif
m_stream->update(); m_stream->update();
@ -1610,7 +1639,7 @@ WRITE_LINE_MEMBER( tms5220_device::rsq_w )
m_true_timing = 1; m_true_timing = 1;
state &= 0x01; state &= 0x01;
#ifdef DEBUG_RS_WS #ifdef DEBUG_RS_WS
logerror("/RS written with data: %d\n", state); fprintf(stderr,"/RS written with data: %d\n", state);
#endif #endif
new_val = (m_rs_ws & 0x01) | (state<<1); new_val = (m_rs_ws & 0x01) | (state<<1);
if (new_val != m_rs_ws) if (new_val != m_rs_ws)
@ -1623,7 +1652,7 @@ WRITE_LINE_MEMBER( tms5220_device::rsq_w )
#ifdef DEBUG_RS_WS #ifdef DEBUG_RS_WS
else else
/* illegal */ /* illegal */
logerror("tms5220_rs_w: illegal\n"); fprintf(stderr,"tms5220_rs_w: illegal\n");
#endif #endif
return; return;
} }
@ -1641,7 +1670,7 @@ WRITE_LINE_MEMBER( tms5220_device::rsq_w )
{ {
/* high to low - schedule ready cycle */ /* high to low - schedule ready cycle */
#ifdef DEBUG_RS_WS #ifdef DEBUG_RS_WS
logerror("Scheduling ready cycle for /RS...\n"); fprintf(stderr,"Scheduling ready cycle for /RS...\n");
#endif #endif
/* upon /RS being activated, /READY goes inactive after 100 nsec from data sheet, through 3 asynchronous gates on patent. This is effectively within one clock, so we immediately set io_ready to 0 and activate the callback. */ /* upon /RS being activated, /READY goes inactive after 100 nsec from data sheet, through 3 asynchronous gates on patent. This is effectively within one clock, so we immediately set io_ready to 0 and activate the callback. */
m_io_ready = 0; m_io_ready = 0;
@ -1662,7 +1691,7 @@ WRITE_LINE_MEMBER( tms5220_device::wsq_w )
m_true_timing = 1; m_true_timing = 1;
state &= 0x01; state &= 0x01;
#ifdef DEBUG_RS_WS #ifdef DEBUG_RS_WS
logerror("/WS written with data: %d\n", state); fprintf(stderr,"/WS written with data: %d\n", state);
#endif #endif
new_val = (m_rs_ws & 0x02) | (state<<0); new_val = (m_rs_ws & 0x02) | (state<<0);
if (new_val != m_rs_ws) if (new_val != m_rs_ws)
@ -1675,7 +1704,7 @@ WRITE_LINE_MEMBER( tms5220_device::wsq_w )
#ifdef DEBUG_RS_WS #ifdef DEBUG_RS_WS
else else
/* illegal */ /* illegal */
logerror("tms5220_ws_w: illegal\n"); fprintf(stderr,"tms5220_ws_w: illegal\n");
#endif #endif
return; return;
} }
@ -1693,7 +1722,7 @@ WRITE_LINE_MEMBER( tms5220_device::wsq_w )
{ {
/* high to low - schedule ready cycle */ /* high to low - schedule ready cycle */
#ifdef DEBUG_RS_WS #ifdef DEBUG_RS_WS
logerror("Scheduling ready cycle for /WS...\n"); fprintf(stderr,"Scheduling ready cycle for /WS...\n");
#endif #endif
/* upon /WS being activated, /READY goes inactive after 100 nsec from data sheet, through 3 asynchronous gates on patent. This is effectively within one clock, so we immediately set io_ready to 0 and activate the callback. */ /* upon /WS being activated, /READY goes inactive after 100 nsec from data sheet, through 3 asynchronous gates on patent. This is effectively within one clock, so we immediately set io_ready to 0 and activate the callback. */
m_io_ready = 0; m_io_ready = 0;
@ -1726,7 +1755,7 @@ WRITE8_MEMBER( tms5220_device::data_w )
if (space.debugger_access()) return; if (space.debugger_access()) return;
#ifdef DEBUG_RS_WS #ifdef DEBUG_RS_WS
logerror("tms5220_data_w: data %02x\n", data); fprintf(stderr,"tms5220_data_w: data %02x\n", data);
#endif #endif
if (!m_true_timing) if (!m_true_timing)
{ {
@ -1739,7 +1768,7 @@ WRITE8_MEMBER( tms5220_device::data_w )
/* actually in a write ? */ /* actually in a write ? */
#ifdef DEBUG_RS_WS #ifdef DEBUG_RS_WS
if (!(m_rs_ws == 0x02)) if (!(m_rs_ws == 0x02))
logerror("tms5220_data_w: data written outside ws, status: %02x!\n", m_rs_ws); fprintf(stderr,"tms5220_data_w: data written outside ws, status: %02x!\n", m_rs_ws);
#endif #endif
m_write_latch = data; m_write_latch = data;
} }
@ -1771,7 +1800,7 @@ READ8_MEMBER( tms5220_device::status_r )
return m_read_latch; return m_read_latch;
#ifdef DEBUG_RS_WS #ifdef DEBUG_RS_WS
else else
logerror("tms5220_status_r: data read outside rs!\n"); fprintf(stderr,"tms5220_status_r: data read outside rs!\n");
#endif #endif
return 0xff; return 0xff;
} }

View File

@ -105,9 +105,12 @@ private:
/* these contain global status bits */ /* these contain global status bits */
UINT8 m_speaking_now; /* True only if actual speech is being generated right now. Is set when a speak vsm command happens OR when speak external happens and buffer low becomes nontrue; Is cleared when speech halts after the last stop frame or the last frame after talk status is otherwise cleared.*/ UINT8 m_previous_TALK_STATUS; /* this is the OLD value of TALK_STATUS (i.e. previous value of m_SPEN|m_TALKD), needed for generating interrupts on a falling TALK_STATUS edge */
UINT8 m_speak_external; /* If 1, DDIS is 1, i.e. Speak External command in progress, writes go to FIFO. */ UINT8 m_SPEN; /* set on speak(or speak external and BL falling edge) command, cleared on stop command, reset command, or buffer out */
UINT8 m_talk_status; /* If 1, TS status bit is 1, i.e. speak or speak external is in progress and we have not encountered a stop frame yet; talk_status differs from speaking_now in that speaking_now is set as soon as a speak or speak external command is started; talk_status does NOT go active until after 8 bytes are written to the fifo on a speak external command, otherwise the two are the same. TS is cleared by 3 things: 1. when a STOP command has just been processed as a new frame in the speech stream; 2. if the fifo runs out in speak external mode; 3. on power-up/during a reset command; When it gets cleared, speak_external is also cleared, an interrupt is generated, and speaking_now will be cleared when the next frame starts. */ UINT8 m_DDIS; /* If 1, DDIS is 1, i.e. Speak External command in progress, writes go to FIFO. */
UINT8 m_TALK; /* set on SPEN & RESETL4(pc12->pc0 transition), cleared on stop command or reset command */
#define TALK_STATUS (m_SPEN|m_TALKD)
UINT8 m_TALKD; /* TALK(TCON) value, latched every RESETL4 */
UINT8 m_buffer_low; /* If 1, FIFO has less than 8 bytes in it */ UINT8 m_buffer_low; /* If 1, FIFO has less than 8 bytes in it */
UINT8 m_buffer_empty; /* If 1, FIFO is empty */ UINT8 m_buffer_empty; /* If 1, FIFO is empty */
UINT8 m_irq_pin; /* state of the IRQ pin (output) */ UINT8 m_irq_pin; /* state of the IRQ pin (output) */
@ -132,22 +135,16 @@ private:
INT16 m_current_energy; INT16 m_current_energy;
INT16 m_current_pitch; INT16 m_current_pitch;
INT16 m_current_k[10]; INT16 m_current_k[10];
INT16 m_target_energy;
INT16 m_target_pitch;
INT16 m_target_k[10];
#else #else
UINT8 m_old_frame_energy_idx; UINT8 m_old_frame_energy_idx;
UINT8 m_old_frame_pitch_idx; UINT8 m_old_frame_pitch_idx;
UINT8 m_old_frame_k_idx[10]; UINT8 m_old_frame_k_idx[10];
UINT8 m_old_zpar;
UINT8 m_old_uv_zpar;
INT32 m_current_energy; INT32 m_current_energy;
INT32 m_current_pitch; INT32 m_current_pitch;
INT32 m_current_k[10]; INT32 m_current_k[10];
INT32 m_target_energy;
INT32 m_target_pitch;
INT32 m_target_k[10];
#endif #endif
UINT16 m_previous_energy; /* needed for lattice filter to match patent */ UINT16 m_previous_energy; /* needed for lattice filter to match patent */
@ -155,16 +152,19 @@ private:
UINT8 m_subcycle; /* contains the current subcycle for a given PC: 0 is A' (only used on SPKSLOW mode on 51xx), 1 is A, 2 is B */ UINT8 m_subcycle; /* contains the current subcycle for a given PC: 0 is A' (only used on SPKSLOW mode on 51xx), 1 is A, 2 is B */
UINT8 m_subc_reload; /* contains 1 for normal speech, 0 when SPKSLOW is active */ UINT8 m_subc_reload; /* contains 1 for normal speech, 0 when SPKSLOW is active */
UINT8 m_PC; /* current parameter counter (what param is being interpolated), ranges from 0 to 12 */ UINT8 m_PC; /* current parameter counter (what param is being interpolated), ranges from 0 to 12 */
/* TODO/NOTE: the current interpolation period, counts 1,2,3,4,5,6,7,0 for divide by 8,8,8,4,4,2,2,1 */ /* NOTE: the interpolation period counts 1,2,3,4,5,6,7,0 for divide by 8,8,8,4,4,2,2,1 */
UINT8 m_IP; /* the current interpolation period */ UINT8 m_IP; /* the current interpolation period */
UINT8 m_inhibit; /* If 1, interpolation is inhibited until the DIV1 period */ UINT8 m_inhibit; /* If 1, interpolation is inhibited until the DIV1 period */
UINT8 m_uv_zpar; /* If 1, zero k5 thru k10 coefficients */
UINT8 m_zpar; /* If 1, zero ALL parameters. */
UINT8 m_pitch_zero; /* circuit 412; pitch is forced to zero under certain circumstances */
UINT8 m_c_variant_rate; /* only relevant for tms5220C's multi frame rate feature; is the actual 4 bit value written on a 0x2* or 0x0* command */ UINT8 m_c_variant_rate; /* only relevant for tms5220C's multi frame rate feature; is the actual 4 bit value written on a 0x2* or 0x0* command */
UINT16 m_pitch_count; /* pitch counter; provides chirp rom address */ UINT16 m_pitch_count; /* pitch counter; provides chirp rom address */
INT32 m_u[11]; INT32 m_u[11];
INT32 m_x[10]; INT32 m_x[10];
UINT16 m_RNG; /* the random noise generator configuration is: 1 + x + x^3 + x^4 + x^13 */ UINT16 m_RNG; /* the random noise generator configuration is: 1 + x + x^3 + x^4 + x^13 TODO: no it isn't */
INT16 m_excitation_data; INT16 m_excitation_data;
/* R Nabet : These have been added to emulate speech Roms */ /* R Nabet : These have been added to emulate speech Roms */