diff options
author | 2015-08-27 19:08:56 -0400 | |
---|---|---|
committer | 2015-08-27 19:08:56 -0400 | |
commit | 12d07f91741615bb811e122ffc15ce8cf39f6266 (patch) | |
tree | aa97e91e467f7839effea138d11ede1fbaa52aee | |
parent | b6ca87aaf6e7c0a8d7ee643a12df2859a907030c (diff) |
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]
-rw-r--r-- | src/emu/sound/tms5220.c | 385 | ||||
-rw-r--r-- | src/emu/sound/tms5220.h | 26 |
2 files changed, 220 insertions, 191 deletions
diff --git a/src/emu/sound/tms5220.c b/src/emu/sound/tms5220.c index 8b8f5190de3..db65f36d614 100644 --- a/src/emu/sound/tms5220.c +++ b/src/emu/sound/tms5220.c @@ -361,9 +361,11 @@ void tms5220_device::register_for_save_states() save_item(NAME(m_fifo_count)); save_item(NAME(m_fifo_bits_taken)); - save_item(NAME(m_speaking_now)); - save_item(NAME(m_speak_external)); - save_item(NAME(m_talk_status)); + save_item(NAME(m_previous_TALK_STATUS)); + save_item(NAME(m_SPEN)); + 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_empty)); 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_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_subcycle)); @@ -395,6 +393,9 @@ void tms5220_device::register_for_save_states() save_item(NAME(m_PC)); save_item(NAME(m_IP)); 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_pitch_count)); @@ -465,10 +466,11 @@ static void printbits(long data, int num) void tms5220_device::data_write(int data) { + int old_buffer_low = m_buffer_low; #ifdef DEBUG_DUMP_INPUT_DATA fprintf(stdout, "%c",data); #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 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_count++; #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 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 - logerror("data_write triggered talk status to go active!\n"); + fprintf(stderr,"data_write triggered SPEN to go active!\n"); +#endif + // ...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() ) + m_zpar = 1; + 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 - // ...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() ) - // 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_subcycle = m_subc_reload; - m_PC = 0; - 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_SPEN = 1; m_new_frame_energy_idx = 0; m_new_frame_pitch_idx = 0; for (i = 0; i < 4; i++) @@ -499,20 +507,20 @@ void tms5220_device::data_write(int data) m_new_frame_k_idx[i] = 0xF; for (i = 7; i < m_coeff->num_k; i++) m_new_frame_k_idx[i] = 0x7; - m_talk_status = m_speaking_now = 1; + } } else { #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. #endif } } - else //(! m_speak_external) + else //(! m_DDIS) // R Nabet : we parse commands at once. It is necessary for such commands as read. process_command(data); } @@ -560,31 +568,31 @@ void tms5220_device::update_fifo_status_and_ints() m_buffer_low = 0; /* 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) { // generate an interrupt if necessary; if /BE was inactive and is now active, set int. if (!m_buffer_empty) set_interrupt_state(1); m_buffer_empty = 1; + m_TALK = m_SPEN = 0; // /BE being active clears the TALK(TCON) status which in turn clears SPEN } else m_buffer_empty = 0; - /* TS is talk status and is set elsewhere in the fifo parser and in - the SPEAK command handler; however, if /BE is true during speak external - mode, it is immediately unset here. */ - if ((m_speak_external == 1) && (m_buffer_empty == 1)) + // generate an interrupt if /TS was active, and is now inactive. + // also, in this case, regardless if DDIS was set, unset it. + if (m_previous_TALK_STATUS == 1 && (TALK_STATUS == 0)) { - // generate an interrupt: /TS was active, and is now inactive. - if (m_talk_status == 1) - { - m_talk_status = m_speak_external = 0; - set_interrupt_state(1); - } +#ifdef VERBOSE + fprintf(stderr,"Talk status WAS 1, is now 0, unsetting DDIS and firing an interrupt!\n"); +#endif + set_interrupt_state(1); + m_DDIS = 0; } - /* Note that TS being unset will also generate an interrupt when a STOP - frame is encountered; this is handled in the sample generator code and not here */ + m_previous_TALK_STATUS = TALK_STATUS; + } /********************************************************************************************** @@ -597,7 +605,7 @@ int tms5220_device::extract_bits(int count) { int val = 0; - if (m_speak_external) + if (m_DDIS) { // extract from FIFO while (count--) @@ -643,10 +651,10 @@ int tms5220_device::status_read() /* clear the interrupt pin on status read */ set_interrupt_state(0); #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 - 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() { #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 - 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() { #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 return m_irq_pin; } @@ -733,34 +741,22 @@ int tms5220_device::int_read() void tms5220_device::process(INT16 *buffer, unsigned int size) { int buf_count=0; - int i, bitout, zpar; + int i, bitout; INT32 this_sample; /* the following gotos are probably safe to remove */ /* if we're empty and still not speaking, fill with nothingness */ - if (!m_speaking_now) + if (!m_TALKD) goto empty; - /* if speak external is set, but talk status is not (yet) set, - wait for buffer low to clear */ - if (!m_talk_status && m_speak_external && m_buffer_low) - goto empty; +#ifdef VERBOSE + 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); +#endif +render: /* 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 * (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]; #endif - /* if the talk status was clear last frame, halt speech now. */ - if (m_talk_status == 0) - { -#ifdef DEBUG_GENERATION - fprintf(stderr,"tms5220_process: processing frame: talk status = 0 caused by stop frame or buffer empty, halting speech.\n"); -#endif - if (m_speaking_now == 1) // we're done, set all coeffs to idle state but keep going for a bit... - { - /**TODO: should index clearing be done here, or elsewhere? **/ - 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; - } - } - + /* Parse a new frame into the new_target_energy, new_target_pitch and new_target_k[] */ + parse_frame(); - /* 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"); + // if the new frame is unvoiced (or silenced via ZPAR), be sure to zero out the k5-k10 parameters + // 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 + m_zpar = 0; + //m_uv_zpar = (OLD_FRAME_UNVOICED_FLAG||m_zpar); // GUESS: fixed version in tmc0280d/tms5100a/cd280x/tms5110 + m_uv_zpar = (NEW_FRAME_UNVOICED_FLAG||m_zpar); // GUESS: buggy version in tmc0280/tms5100 - /* if the new frame is a stop frame, set an interrupt and set talk status to 0 */ - /** TODO: investigate this later! **/ + /* 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. */ if (NEW_FRAME_STOP_FLAG == 1) - { - m_talk_status = m_speak_external = 0; - set_interrupt_state(1); - update_fifo_status_and_ints(); - } + { + m_TALK = m_SPEN = 0; + } /* in all cases where interpolation would be inhibited, set the inhibit flag; otherwise clear it. Interpolation inhibit cases: @@ -841,35 +817,31 @@ void tms5220_device::process(INT16 *buffer, unsigned int size) else // normal frame, normal interpolation 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 /* Debug info for current parsed frame */ fprintf(stderr, "OLDE: %d; OLDP: %d; ", m_OLDE, m_OLDP); - fprintf(stderr,"Processing frame: "); + fprintf(stderr,"Processing new frame: "); if (m_inhibit == 0) fprintf(stderr, "Normal Frame\n"); else 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,"*** 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 - /* 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. { @@ -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?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)); - - zpar = OLD_FRAME_UNVOICED_FLAG; //fprintf(stderr, "CS: %03d", current_sample); // 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_pitch = m_coeff->pitchtable[m_old_frame_pitch_idx]; - for (i = 0; i < 4; i++) - m_current_k[i] = m_coeff->ktable[i][m_old_frame_k_idx[i]]; - 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)); + 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] * (1-m_old_zpar)); + for (i = 0; i < m_coeff->num_k; 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))); // now adjust each value to be exactly correct for each of the samples per frame 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_pitch += (((m_target_pitch - m_current_pitch)*(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_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++) - 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. { - m_current_energy = m_target_energy; - m_current_pitch = m_target_pitch; + m_current_energy = (m_coeff->energytable[m_new_frame_energy_idx] * (1-m_zpar)); + m_current_pitch = (m_coeff->pitchtable[m_new_frame_pitch_idx] * (1-m_zpar)); 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 //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) { 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; 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; 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 */ - 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; - 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; } } @@ -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); for (i=0; i<10; 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"); #endif /* 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 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 * 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 * 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_PC = 0; m_IP++; @@ -1015,7 +1010,7 @@ void tms5220_device::process(INT16 *buffer, unsigned int size) m_PC++; } 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; buf_count++; size--; @@ -1023,15 +1018,24 @@ void tms5220_device::process(INT16 *buffer, unsigned int size) 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) { 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_PC = 0; m_IP++; m_IP&=0x7; + if (m_TALKD) goto render; } else if (m_subcycle == 3) { @@ -1182,6 +1186,7 @@ INT32 tms5220_device::lattice_filter() void tms5220_device::process_command(unsigned char cmd) { + int i; #ifdef DEBUG_COMMAND_DUMP fprintf(stderr,"process_command called with parameter %02X\n",cmd); #endif @@ -1189,7 +1194,7 @@ void tms5220_device::process_command(unsigned char cmd) switch (cmd & 0x70) { 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) { @@ -1211,10 +1216,10 @@ void tms5220_device::process_command(unsigned char cmd) break; 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 - logerror("read and branch command received\n"); + fprintf(stderr,"read and branch command received\n"); #endif m_RDB_flag = FALSE; if (m_speechrom) @@ -1223,7 +1228,7 @@ void tms5220_device::process_command(unsigned char cmd) break; 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. This code does not care about this. */ @@ -1240,17 +1245,19 @@ void tms5220_device::process_command(unsigned char cmd) if (m_speechrom) m_speechrom->read(1); } - m_speaking_now = 1; - m_speak_external = 0; - m_talk_status = 1; /* start immediately */ - /* clear out variables before speaking */ - // TODO: similar to the victory case described above, but for VSM speech - m_subcycle = m_subc_reload; - m_PC = 0; - m_IP = reload_table[m_c_variant_rate&0x3]; + m_SPEN = 1; + m_DDIS = 0; + 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; - int i; for (i = 0; i < 4; i++) m_new_frame_k_idx[i] = 0; for (i = 4; i < 7; i++) @@ -1260,9 +1267,27 @@ void tms5220_device::process_command(unsigned char cmd) break; 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_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; break; @@ -1308,7 +1333,7 @@ void tms5220_device::parse_frame() m_IP = reload_table[m_c_variant_rate&0x3]; 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 m_new_frame_energy_idx = extract_bits(m_coeff->energy_bits); @@ -1317,7 +1342,7 @@ void tms5220_device::parse_frame() fprintf(stderr," "); #endif 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 ((m_new_frame_energy_idx == 0) || (m_new_frame_energy_idx == 15)) return; @@ -1337,7 +1362,7 @@ void tms5220_device::parse_frame() fprintf(stderr," "); #endif 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 (rep_flag) return; @@ -1351,7 +1376,7 @@ void tms5220_device::parse_frame() fprintf(stderr," "); #endif 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... @@ -1370,19 +1395,22 @@ void tms5220_device::parse_frame() fprintf(stderr," "); #endif 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 - if (m_speak_external) - logerror("Parsed a frame successfully in FIFO - %d bits remaining\n", (m_fifo_count*8)-(m_fifo_bits_taken)); + if (m_DDIS) + fprintf(stderr,"Parsed a frame successfully in FIFO - %d bits remaining\n", (m_fifo_count*8)-(m_fifo_bits_taken)); else - logerror("Parsed a frame successfully in ROM\n"); + fprintf(stderr,"Parsed a frame successfully in ROM\n"); #endif return; ranout: #ifdef DEBUG_FRAME_ERRORS - logerror("Ran out of bits on a parse!\n"); + fprintf(stderr,"Ran out of bits on a parse!\n"); #endif 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 #ifdef DEBUG_PIN_READS - logerror("irq pin set to state %d\n", state); + fprintf(stderr,"irq pin set to state %d\n", state); #endif if (!m_irq_handler.isnull() && state != m_irq_pin) m_irq_handler(!state); @@ -1414,7 +1442,7 @@ void tms5220_device::update_ready_state() { int state = ready_read(); #ifdef DEBUG_PIN_READS - logerror("ready pin set to state %d\n", state); + fprintf(stderr,"ready pin set to state %d\n", state); #endif if (!m_readyq_handler.isnull() && state != m_ready_pin) m_readyq_handler(!state); @@ -1514,7 +1542,7 @@ void tms5220_device::device_reset() /* 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 */ - 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); update_ready_state(); m_buffer_empty = m_buffer_low = 1; @@ -1525,12 +1553,13 @@ void tms5220_device::device_reset() #ifdef PERFECT_INTERPOLATION_HACK m_old_frame_energy_idx = m_old_frame_pitch_idx = 0; memset(m_old_frame_k_idx, 0, sizeof(m_old_frame_k_idx)); + m_old_zpar = 0; #endif - m_new_frame_energy_idx = m_current_energy = m_target_energy = m_previous_energy = 0; - m_new_frame_pitch_idx = m_current_pitch = m_target_pitch = 0; + m_new_frame_energy_idx = m_current_energy = m_previous_energy = 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_current_k, 0, sizeof(m_current_k)); - memset(m_target_k, 0, sizeof(m_target_k)); /* initialize the sample generators */ m_inhibit = 1; @@ -1574,7 +1603,7 @@ void tms5220_device::device_timer(emu_timer &timer, device_timer_id id, int para /* Write */ /* bring up to date first */ #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); #endif m_stream->update(); @@ -1610,7 +1639,7 @@ WRITE_LINE_MEMBER( tms5220_device::rsq_w ) m_true_timing = 1; state &= 0x01; #ifdef DEBUG_RS_WS - logerror("/RS written with data: %d\n", state); + fprintf(stderr,"/RS written with data: %d\n", state); #endif new_val = (m_rs_ws & 0x01) | (state<<1); if (new_val != m_rs_ws) @@ -1623,7 +1652,7 @@ WRITE_LINE_MEMBER( tms5220_device::rsq_w ) #ifdef DEBUG_RS_WS else /* illegal */ - logerror("tms5220_rs_w: illegal\n"); + fprintf(stderr,"tms5220_rs_w: illegal\n"); #endif return; } @@ -1641,7 +1670,7 @@ WRITE_LINE_MEMBER( tms5220_device::rsq_w ) { /* high to low - schedule ready cycle */ #ifdef DEBUG_RS_WS - logerror("Scheduling ready cycle for /RS...\n"); + fprintf(stderr,"Scheduling ready cycle for /RS...\n"); #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. */ m_io_ready = 0; @@ -1662,7 +1691,7 @@ WRITE_LINE_MEMBER( tms5220_device::wsq_w ) m_true_timing = 1; state &= 0x01; #ifdef DEBUG_RS_WS - logerror("/WS written with data: %d\n", state); + fprintf(stderr,"/WS written with data: %d\n", state); #endif new_val = (m_rs_ws & 0x02) | (state<<0); if (new_val != m_rs_ws) @@ -1675,7 +1704,7 @@ WRITE_LINE_MEMBER( tms5220_device::wsq_w ) #ifdef DEBUG_RS_WS else /* illegal */ - logerror("tms5220_ws_w: illegal\n"); + fprintf(stderr,"tms5220_ws_w: illegal\n"); #endif return; } @@ -1693,7 +1722,7 @@ WRITE_LINE_MEMBER( tms5220_device::wsq_w ) { /* high to low - schedule ready cycle */ #ifdef DEBUG_RS_WS - logerror("Scheduling ready cycle for /WS...\n"); + fprintf(stderr,"Scheduling ready cycle for /WS...\n"); #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. */ m_io_ready = 0; @@ -1726,7 +1755,7 @@ WRITE8_MEMBER( tms5220_device::data_w ) if (space.debugger_access()) return; #ifdef DEBUG_RS_WS - logerror("tms5220_data_w: data %02x\n", data); + fprintf(stderr,"tms5220_data_w: data %02x\n", data); #endif if (!m_true_timing) { @@ -1739,7 +1768,7 @@ WRITE8_MEMBER( tms5220_device::data_w ) /* actually in a write ? */ #ifdef DEBUG_RS_WS 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 m_write_latch = data; } @@ -1771,7 +1800,7 @@ READ8_MEMBER( tms5220_device::status_r ) return m_read_latch; #ifdef DEBUG_RS_WS else - logerror("tms5220_status_r: data read outside rs!\n"); + fprintf(stderr,"tms5220_status_r: data read outside rs!\n"); #endif return 0xff; } diff --git a/src/emu/sound/tms5220.h b/src/emu/sound/tms5220.h index 177f82bdecf..12edc66e5c3 100644 --- a/src/emu/sound/tms5220.h +++ b/src/emu/sound/tms5220.h @@ -105,9 +105,12 @@ private: /* 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_speak_external; /* If 1, DDIS is 1, i.e. Speak External command in progress, writes go to FIFO. */ - 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_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_SPEN; /* set on speak(or speak external and BL falling edge) command, cleared on stop command, reset command, or buffer out */ + 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_empty; /* If 1, FIFO is empty */ UINT8 m_irq_pin; /* state of the IRQ pin (output) */ @@ -132,22 +135,16 @@ private: INT16 m_current_energy; INT16 m_current_pitch; INT16 m_current_k[10]; - - INT16 m_target_energy; - INT16 m_target_pitch; - INT16 m_target_k[10]; #else UINT8 m_old_frame_energy_idx; UINT8 m_old_frame_pitch_idx; UINT8 m_old_frame_k_idx[10]; + UINT8 m_old_zpar; + UINT8 m_old_uv_zpar; INT32 m_current_energy; INT32 m_current_pitch; INT32 m_current_k[10]; - - INT32 m_target_energy; - INT32 m_target_pitch; - INT32 m_target_k[10]; #endif 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_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 */ - /* 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_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 */ UINT16 m_pitch_count; /* pitch counter; provides chirp rom address */ INT32 m_u[11]; 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; /* R Nabet : These have been added to emulate speech Roms */ |