From 4c62c8a48f56402f1683a04e9510529899175a9e Mon Sep 17 00:00:00 2001 From: AJR Date: Tue, 12 Jun 2018 10:20:47 -0400 Subject: tms5220: Convert logging calls to logmacro.h standard (nw) This also reinstates the command logging improvements from 62fc2e8f068a67b84b324b1c2e9b55791850247e. --- src/devices/sound/tms5220.cpp | 596 +++++++++++++++++++----------------------- src/devices/sound/tms5220.h | 5 + 2 files changed, 268 insertions(+), 333 deletions(-) diff --git a/src/devices/sound/tms5220.cpp b/src/devices/sound/tms5220.cpp index b0cf48a271a..7d6ac1abc93 100644 --- a/src/devices/sound/tms5220.cpp +++ b/src/devices/sound/tms5220.cpp @@ -330,8 +330,6 @@ in MCU code). Look for a 16-pin chip at U6 labeled "ECHO-3 SN". #include "emu.h" #include "tms5220.h" -static int16_t clip_analog(int16_t cliptemp); - /* *****optional defines***** */ /* Hacky improvements which don't match patent: */ @@ -365,38 +363,41 @@ static int16_t clip_analog(int16_t cliptemp); /* *****debugging defines***** */ -#undef VERBOSE +#define LOG_GENERAL (1 << 0) // above is general, somewhat obsolete, catch all for debugs which don't fit elsewhere -#undef DEBUG_DUMP_INPUT_DATA +#define LOG_DUMP_INPUT_DATA (1 << 1) /* 5220 only; above dumps the data written to the tms52xx to stdout, useful for making logged data dumps for real hardware tests */ -#undef DEBUG_FIFO +#define LOG_FIFO (1 << 2) // 5220 only; above debugs fifo stuff: writes, reads and flag updates -#undef DEBUG_PARSE_FRAME_DUMP -// above dumps each frame to stderr: be sure to select one of the options below if you define it! -#undef DEBUG_PARSE_FRAME_DUMP_BIN +#define LOG_PARSE_FRAME_DUMP_BIN (1 << 3) // dumps each speech frame as binary -#undef DEBUG_PARSE_FRAME_DUMP_HEX +#define LOG_PARSE_FRAME_DUMP_HEX (1 << 4) // dumps each speech frame as hex -#undef DEBUG_FRAME_ERRORS +#define LOG_FRAME_ERRORS (1 << 6) // above dumps info if a frame ran out of data -#undef DEBUG_COMMAND_DUMP +#define LOG_COMMAND_DUMP (1 << 7) // above dumps all non-speech-data command writes -#undef DEBUG_PIN_READS +#define LOG_COMMAND_VERBOSE (1 << 8) +// above dumps decoded info about command writes +#define LOG_PIN_READS (1 << 9) // above spams the errorlog with i/o ready messages whenever the ready or irq pin is read -#undef DEBUG_GENERATION +#define LOG_GENERATION (1 << 10) // above 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. -#undef DEBUG_GENERATION_VERBOSE +#define LOG_GENERATION_VERBOSE (1 << 11) // above 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. -#undef DEBUG_LATTICE +#define LOG_LATTICE (1 << 12) // above dumps the lattice filter state data each sample. -#undef DEBUG_CLIP +#define LOG_CLIP (1 << 13) // above dumps info to stderr whenever the analog clip hardware is (or would be) clipping the signal. -#undef DEBUG_IO_READY -// above debugs the io ready callback -#undef DEBUG_RS_WS +#define LOG_IO_READY (1 << 14) +// above debugs the io ready callback timer +#define LOG_RS_WS (1 << 15) // above debugs the tms5220_data_r and data_w access methods which actually respect rs and ws +//#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) +#include "logmacro.h" + #define MAX_SAMPLE_CHUNK 512 /* 6+4 Variants, from tms5110r.inc */ @@ -522,37 +523,30 @@ void tms5220_device::register_for_save_states() ***********************************************************************************************/ -#ifdef DEBUG_PARSE_FRAME_DUMP_BIN -static void printbits(long data, int num) -{ - int i; - for (i=(num-1); i>=0; i--) - logerror("%0ld", (data>>i)&1); -} -#endif -#ifdef DEBUG_PARSE_FRAME_DUMP_HEX -static void printbits(long data, int num) +void tms5220_device::printbits(long data, int num) { - switch((num-1)&0xFC) + for (int i = num - 1; i >= 0; i--) + LOGMASKED(LOG_PARSE_FRAME_DUMP_BIN, "%0ld", (data>>i)&1); + + switch ((num - 1) & 0xfc) { - case 0: - logerror("%0lx", data); - break; - case 4: - logerror("%02lx", data); - break; - case 8: - logerror("%03lx", data); - break; - case 12: - logerror("%04lx", data); - break; - default: - logerror("%04lx", data); - break; + case 0: + LOGMASKED(LOG_PARSE_FRAME_DUMP_HEX, "%0lx", data); + break; + case 4: + LOGMASKED(LOG_PARSE_FRAME_DUMP_HEX, "%02lx", data); + break; + case 8: + LOGMASKED(LOG_PARSE_FRAME_DUMP_HEX, "%03lx", data); + break; + case 12: + LOGMASKED(LOG_PARSE_FRAME_DUMP_HEX, "%04lx", data); + break; + default: + LOGMASKED(LOG_PARSE_FRAME_DUMP_HEX, "%04lx", data); + break; } } -#endif /********************************************************************************************** @@ -600,9 +594,7 @@ uint8_t tms5220_device::new_int_read() 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(); -#ifdef VERBOSE - logerror("WARNING: CALLBACK MISSING, RETURNING 0!\n"); -#endif + LOGMASKED(LOG_GENERAL, "WARNING: CALLBACK MISSING, RETURNING 0!\n"); return 0; } @@ -615,9 +607,8 @@ uint8_t tms5220_device::new_int_read() void tms5220_device::data_write(int data) { int old_buffer_low = m_buffer_low; -#ifdef DEBUG_DUMP_INPUT_DATA - fprintf(stdout, "%c",data); -#endif + LOGMASKED(LOG_DUMP_INPUT_DATA, "%c", data); + if (m_DDIS) // If we're in speak external mode { // add this byte to the FIFO @@ -626,17 +617,13 @@ 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++; -#ifdef DEBUG_FIFO - logerror("data_write: Added byte to FIFO (current count=%2d)\n", m_fifo_count); -#endif + LOGMASKED(LOG_FIFO, "data_write: Added byte to FIFO (current count=%2d)\n", 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) if ((m_SPEN == 0) && ((old_buffer_low == 1) && (m_buffer_low == 0))) // MUST HAVE EDGE DETECT { - int i; -#ifdef DEBUG_FIFO - logerror("data_write triggered SPEN to go active!\n"); -#endif + LOGMASKED(LOG_FIFO, "data_write triggered SPEN to go active!\n"); // ...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 @@ -652,21 +639,19 @@ void tms5220_device::data_write(int data) #endif m_new_frame_energy_idx = 0; m_new_frame_pitch_idx = 0; - for (i = 0; i < 4; i++) + for (int i = 0; i < 4; i++) m_new_frame_k_idx[i] = 0; - for (i = 4; i < 7; i++) + for (int i = 4; i < 7; i++) m_new_frame_k_idx[i] = 0xF; - for (i = 7; i < m_coeff->num_k; i++) + for (int i = 7; i < m_coeff->num_k; i++) m_new_frame_k_idx[i] = 0x7; } } else { -#ifdef DEBUG_FIFO - logerror("data_write: Ran out of room in the tms52xx FIFO! this should never happen!\n"); + 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. -#endif } @@ -737,9 +722,7 @@ void tms5220_device::update_fifo_status_and_ints() // also, in this case, regardless if DDIS was set, unset it. if ((m_previous_TALK_STATUS == 1) && (TALK_STATUS() == 0)) { -#ifdef VERBOSE - logerror("Talk status WAS 1, is now 0, unsetting DDIS and firing an interrupt!\n"); -#endif + LOGMASKED(LOG_GENERAL, "Talk status WAS 1, is now 0, unsetting DDIS and firing an interrupt!\n"); set_interrupt_state(1); m_DDIS = 0; } @@ -785,9 +768,7 @@ int tms5220_device::extract_bits(int count) while (count--) { val = (val << 1) | new_int_read(); -#ifdef VERBOSE - logerror("bit read: %d\n", val&1); -#endif + LOGMASKED(LOG_GENERAL, "bit read: %d\n", val&1); } #endif } @@ -799,12 +780,8 @@ void tms5220_device::perform_dummy_read() { if (m_schedule_dummy_read) { -#ifdef VERBOSE int data = new_int_read(); - logerror("TMS5110 performing dummy read; value read = %1i\n", data & 1); -#else - new_int_read(); -#endif + LOGMASKED(LOG_GENERAL, "TMS5110 performing dummy read; value read = %1i\n", data & 1); m_schedule_dummy_read = false; } } @@ -826,9 +803,7 @@ int tms5220_device::status_read() { /* read status */ /* 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", TALK_STATUS(), m_buffer_low, m_buffer_empty); -#endif + LOGMASKED(LOG_PIN_READS, "Status read: TS=%d BL=%d BE=%d\n", TALK_STATUS(), m_buffer_low, m_buffer_empty); return (TALK_STATUS() << 7) | (m_buffer_low << 6) | (m_buffer_empty << 5); } } @@ -842,9 +817,8 @@ 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, DDIS(speak external) is %d\n", m_io_ready, m_fifo_count, m_DDIS); -#endif + LOGMASKED(LOG_PIN_READS, "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); + return ((m_fifo_count < FIFO_SIZE)||(!m_DDIS)) && m_io_ready; } @@ -900,9 +874,8 @@ 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); -#endif + LOGMASKED(LOG_PIN_READS, "int_read: irq pin read, state is %d\n", m_irq_pin); + return m_irq_pin; } @@ -919,9 +892,7 @@ void tms5220_device::process(int16_t *buffer, unsigned int size) int i, bitout; int32_t this_sample; -#ifdef VERBOSE - logerror("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); -#endif + 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); /* loop until the buffer is full or we've stopped speaking */ while (size > 0) @@ -975,16 +946,15 @@ void tms5220_device::process(int16_t *buffer, unsigned int size) else // normal frame, normal interpolation m_inhibit = 0; -#ifdef DEBUG_GENERATION /* Debug info for current parsed frame */ - logerror( "OLDE: %d; NEWE: %d; OLDP: %d; NEWP: %d ", OLD_FRAME_SILENCE_FLAG(), NEW_FRAME_SILENCE_FLAG(), OLD_FRAME_UNVOICED_FLAG(), NEW_FRAME_UNVOICED_FLAG()); - logerror("Processing new 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 == 0) - logerror( "Normal Frame\n"); + LOGMASKED(LOG_GENERATION, "Normal Frame\n"); else - logerror("Interpolation Inhibited\n"); - logerror("*** 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]); - logerror("*** target Energy(idx), Pitch, and Ks = %04d(%x),%04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d\n", + LOGMASKED(LOG_GENERATION, "Interpolation Inhibited\n"); + 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)), m_new_frame_energy_idx, (m_coeff->pitchtable[m_new_frame_pitch_idx] * (1-m_zpar)), @@ -998,8 +968,6 @@ void tms5220_device::process(int16_t *buffer, unsigned int size) (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 - } else // Not a new frame, just interpolate the existing frame. { @@ -1090,20 +1058,20 @@ void tms5220_device::process(int16_t *buffer, unsigned int size) m_RNG |= bitout; } this_sample = lattice_filter(); /* execute lattice filter */ -#ifdef DEBUG_GENERATION_VERBOSE - //logerror("C:%01d; ",m_subcycle); - logerror("IP:%01d PC:%02d X:%04d E:%03d P:%03d Pc:%03d ",m_IP, m_PC, m_excitation_data, m_current_energy, m_current_pitch, m_pitch_count); - //logerror("X:%04d E:%03d P:%03d Pc:%03d ", m_excitation_data, m_current_energy, m_current_pitch, m_pitch_count); + + //LOGMASKED(LOG_GENERATION_VERBOSE, "C:%01d; ",m_subcycle); + LOGMASKED(LOG_GENERATION_VERBOSE, "IP:%01d PC:%02d X:%04d E:%03d P:%03d Pc:%03d ",m_IP, m_PC, m_excitation_data, m_current_energy, m_current_pitch, m_pitch_count); + //LOGMASKED(LOG_GENERATION_VERBOSE, "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++) - logerror("K%d:%04d ", i+1, m_current_k[i]); - logerror("Out:%06d ", this_sample); + LOGMASKED(LOG_GENERATION_VERBOSE, "K%d:%04d ", i+1, m_current_k[i]); + LOGMASKED(LOG_GENERATION_VERBOSE, "Out:%06d ", this_sample); //#ifdef TMS5220_PERFECT_INTERPOLATION_HACK -// logerror("%d%d%d%d",m_old_zpar,m_zpar,m_old_uv_zpar,m_uv_zpar); +// LOGMASKED(LOG_GENERATION_VERBOSE, "%d%d%d%d",m_old_zpar,m_zpar,m_old_uv_zpar,m_uv_zpar); //#else -// logerror("x%dx%d",m_zpar,m_uv_zpar); +// LOGMASKED(LOG_GENERATION_VERBOSE, "x%dx%d",m_zpar,m_uv_zpar); //#endif - logerror("\n"); -#endif + LOGMASKED(LOG_GENERATION_VERBOSE, "\n"); + /* next, force result to 14 bits (since its possible that the addition at the final (k1) stage of the lattice overflowed) */ while (this_sample > 16383) this_sample -= 32768; while (this_sample < -16384) this_sample += 32768; @@ -1146,19 +1114,16 @@ void tms5220_device::process(int16_t *buffer, unsigned int size) OLD_FRAME_SILENCE_FLAG() = NEW_FRAME_SILENCE_FLAG() ? 1 : 0; // m_OLDE OLD_FRAME_UNVOICED_FLAG() = NEW_FRAME_UNVOICED_FLAG() ? 1 : 0; // 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 - logerror("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 + + LOGMASKED(LOG_GENERATION, "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); if ((!m_TALK) && (!m_SPEN)) - logerror("tms5220_process: processing frame: TALKD = 0 caused by stop frame or buffer empty, halting speech.\n"); -#endif + LOGMASKED(LOG_GENERATION, "tms5220_process: processing frame: TALKD = 0 caused by stop frame or buffer empty, halting speech.\n"); + m_TALKD = m_TALK; // TALKD is latched from TALK update_fifo_status_and_ints(); // to trigger an interrupt if TALK_STATUS has changed if ((!m_TALK) && m_SPEN) m_TALK = 1; // TALK is only activated if it wasn't already active, if m_SPEN is active, and if we're in RESETL4 (which we are). -#ifdef DEBUG_GENERATION - logerror("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 + + LOGMASKED(LOG_GENERATION, "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); } m_subcycle = m_subc_reload; m_PC = 0; @@ -1208,7 +1173,7 @@ void tms5220_device::process(int16_t *buffer, unsigned int size) ***********************************************************************************************/ -static int16_t clip_analog(int16_t cliptemp) +int16_t tms5220_device::clip_analog(int16_t cliptemp) const { /* clipping, just like the patent shows: * the top 10 bits of this result are visible on the digital output IO pin. @@ -1218,9 +1183,8 @@ static int16_t clip_analog(int16_t cliptemp) * 00 0bcd efgh xxxx -> 0b0bcdefgh * 0x xxxx xxxx xxxx -> 0b01111111 */ -#ifdef DEBUG_CLIP - if ((cliptemp > 2047) || (cliptemp < -2048)) logerror("clipping cliptemp to range; was %d\n", cliptemp); -#endif + if ((cliptemp > 2047) || (cliptemp < -2048)) + LOGMASKED(LOG_CLIP, "clipping cliptemp to range; was %d\n", cliptemp); if (cliptemp > 2047) cliptemp = 2047; else if (cliptemp < -2048) cliptemp = -2048; /* at this point the analog output is tapped */ @@ -1250,7 +1214,7 @@ static int16_t clip_analog(int16_t cliptemp) output, this makes almost no difference in the computation. **********************************************************************************************/ -static int32_t matrix_multiply(int32_t a, int32_t b) +int32_t tms5220_device::matrix_multiply(int32_t a, int32_t b) const { int32_t result; while (a>511) { a-=1024; } @@ -1258,10 +1222,8 @@ static int32_t matrix_multiply(int32_t a, int32_t b) 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. **/ -#ifdef VERBOSE - if (result>16383) logerror("matrix multiplier overflowed! a: %x, b: %x, result: %x", a, b, result); - if (result<-16384) logerror("matrix multiplier underflowed! a: %x, b: %x, result: %x", a, b, result); -#endif + 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); return result; } @@ -1307,9 +1269,7 @@ int32_t tms5220_device::lattice_filter() m_u[2] = m_u[3] - matrix_multiply(m_current_k[2], m_x[2]); m_u[1] = m_u[2] - matrix_multiply(m_current_k[1], m_x[1]); m_u[0] = m_u[1] - matrix_multiply(m_current_k[0], m_x[0]); -#ifdef DEBUG_LATTICE int32_t err = m_x[9] + matrix_multiply(m_current_k[9], m_u[9]); //x_10, real chip doesn't use or calculate this -#endif m_x[9] = m_x[8] + matrix_multiply(m_current_k[8], m_u[8]); m_x[8] = m_x[7] + matrix_multiply(m_current_k[7], m_u[7]); m_x[7] = m_x[6] + matrix_multiply(m_current_k[6], m_u[6]); @@ -1321,21 +1281,20 @@ int32_t tms5220_device::lattice_filter() m_x[1] = m_x[0] + matrix_multiply(m_current_k[0], m_u[0]); m_x[0] = m_u[0]; m_previous_energy = m_current_energy; -#ifdef DEBUG_LATTICE - int i; - logerror("V:%04d ", m_u[10]); - for (i = 9; i >= 0; i--) + + LOGMASKED(LOG_LATTICE, "V:%04d ", m_u[10]); + for (int i = 9; i >= 0; i--) { - logerror("Y%d:%04d ", i+1, m_u[i]); + LOGMASKED(LOG_LATTICE, "Y%d:%04d ", i+1, m_u[i]); } - logerror("\n"); - logerror("E:%04d ", err); - for (i = 9; i >= 0; i--) + LOGMASKED(LOG_LATTICE, "\n"); + LOGMASKED(LOG_LATTICE, "E:%04d ", err); + for (int i = 9; i >= 0; i--) { - logerror("b%d:%04d ", i+1, m_x[i]); + LOGMASKED(LOG_LATTICE, "b%d:%04d ", i+1, m_x[i]); } - logerror("\n"); -#endif + LOGMASKED(LOG_LATTICE, "\n"); + return m_u[0]; } @@ -1348,123 +1307,135 @@ int32_t tms5220_device::lattice_filter() void tms5220_device::process_command(unsigned char cmd) { - int i; -#ifdef DEBUG_COMMAND_DUMP - logerror("process_command called with parameter %02X\n",cmd); -#endif - /* parse the command */ - switch (cmd & 0x70) + LOGMASKED(LOG_COMMAND_DUMP, "process_command called with parameter %02X\n", cmd); + + /* parse the command */ + switch (cmd & 0x70) + { + case 0x10 : /* read byte */ + if (TALK_STATUS() == 0) /* TALKST must be clear for RDBY */ { - case 0x10 : /* read byte */ - if (TALK_STATUS() == 0) /* TALKST must be clear for RDBY */ + LOGMASKED(LOG_COMMAND_VERBOSE, "Read Byte command received\n"); + if (m_schedule_dummy_read) { - if (m_schedule_dummy_read) - { - m_schedule_dummy_read = false; - if (m_speechrom) - m_speechrom->read(1); - } + m_schedule_dummy_read = false; if (m_speechrom) - m_data_register = m_speechrom->read(8); /* read one byte from speech ROM... */ - m_RDB_flag = true; + m_speechrom->read(1); } + if (m_speechrom) + m_data_register = m_speechrom->read(8); /* read one byte from speech ROM... */ + m_RDB_flag = true; + } + else + LOGMASKED(LOG_COMMAND_VERBOSE, "Read Byte command received during TALK state, ignoring!\n"); break; - case 0x00: case 0x20: /* set rate (tms5220c and cd2501ecd only), otherwise NOP */ - if (TMS5220_HAS_RATE_CONTROL) - { - m_c_variant_rate = cmd&0x0F; - } + case 0x00: case 0x20: /* set rate (tms5220c and cd2501ecd only), otherwise NOP */ + if (TMS5220_HAS_RATE_CONTROL) + { + LOGMASKED(LOG_COMMAND_VERBOSE, "Set Rate command received\n"); + m_c_variant_rate = cmd&0x0F; + } + else + LOGMASKED(LOG_COMMAND_VERBOSE, "NOP command received\n"); break; - case 0x30 : /* read and branch */ - if (TALK_STATUS() == 0) /* TALKST must be clear for RB */ - { -#ifdef VERBOSE - logerror("read and branch command received\n"); -#endif - m_RDB_flag = false; - if (m_speechrom) - m_speechrom->read_and_branch(); - } - break; + case 0x30 : /* read and branch */ + if (TALK_STATUS() == 0) /* TALKST must be clear for RB */ + { + LOGMASKED(LOG_COMMAND_VERBOSE, "Read and Branch command received\n"); + m_RDB_flag = false; + if (m_speechrom) + m_speechrom->read_and_branch(); + } + else + LOGMASKED(LOG_COMMAND_VERBOSE, "Read and Branch command received during TALK state, ignoring!\n"); + break; - case 0x40 : /* load address */ - 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. */ - if (m_speechrom) - m_speechrom->load_address(cmd & 0x0f); - m_schedule_dummy_read = true; - } - break; + case 0x40 : /* load address */ + if (TALK_STATUS() == 0) /* TALKST must be clear for LA */ + { + LOGMASKED(LOG_COMMAND_VERBOSE, "Load Address command received\n"); - case 0x50 : /* speak */ - if (m_schedule_dummy_read) - { - m_schedule_dummy_read = false; - if (m_speechrom) - m_speechrom->read(1); - } - m_SPEN = 1; + /* 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); + m_schedule_dummy_read = true; + } + else + LOGMASKED(LOG_COMMAND_VERBOSE, "Load Address command received during TALK state, ignoring!\n"); + break; + + case 0x50 : /* speak */ + LOGMASKED(LOG_COMMAND_VERBOSE, "Speak (VSM) command received\n"); + if (m_schedule_dummy_read) + { + m_schedule_dummy_read = false; + if (m_speechrom) + m_speechrom->read(1); + } + m_SPEN = 1; #ifdef FAST_START_HACK - m_TALK = 1; + m_TALK = 1; #endif - 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 + 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 TMS5220_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; - break; - - case 0x60 : /* speak external */ - // SPKEXT going active activates SPKEE which clears the fifo - m_fifo_head = m_fifo_tail = m_fifo_count = m_fifo_bits_taken = 0; - // 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 + 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 (int i = 0; i < 4; i++) + m_new_frame_k_idx[i] = 0; + for (int i = 4; i < 7; i++) + m_new_frame_k_idx[i] = 0xF; + for (int i = 7; i < m_coeff->num_k; i++) + m_new_frame_k_idx[i] = 0x7; + break; + + case 0x60 : /* speak external */ + LOGMASKED(LOG_COMMAND_VERBOSE, "Speak External command received\n"); + + // SPKEXT going active activates SPKEE which clears the fifo + m_fifo_head = m_fifo_tail = m_fifo_count = m_fifo_bits_taken = 0; + // 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 TMS5220_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; + 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 (int i = 0; i < 4; i++) + m_new_frame_k_idx[i] = 0; + for (int i = 4; i < 7; i++) + 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_RDB_flag = false; + break; - case 0x70 : /* reset */ - if (m_schedule_dummy_read) - { - m_schedule_dummy_read = false; - if (m_speechrom) - m_speechrom->read(1); - } - reset(); - break; + case 0x70 : /* reset */ + LOGMASKED(LOG_COMMAND_VERBOSE, "Reset command received\n"); + if (m_schedule_dummy_read) + { + m_schedule_dummy_read = false; + if (m_speechrom) + m_speechrom->read(1); + } + reset(); + break; } /* update the buffer low state */ @@ -1495,10 +1466,8 @@ void tms5220_device::parse_frame() if ((TMS5220_HAS_RATE_CONTROL) && (m_c_variant_rate & 0x04)) { i = extract_bits(2); -#ifdef DEBUG_PARSE_FRAME_DUMP - printbits(i,2); - logerror(" "); -#endif + printbits(i, 2); + LOGMASKED(LOG_PARSE_FRAME_DUMP_BIN | LOG_PARSE_FRAME_DUMP_HEX, " "); m_IP = reload_table[i]; } else // non-5220C and 5220C in fixed rate mode @@ -1509,10 +1478,8 @@ void tms5220_device::parse_frame() // attempt to extract the energy index m_new_frame_energy_idx = extract_bits(m_coeff->energy_bits); -#ifdef DEBUG_PARSE_FRAME_DUMP - printbits(m_new_frame_energy_idx,m_coeff->energy_bits); - logerror(" "); -#endif + 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(); if (m_DDIS && m_buffer_empty) goto ranout; // if the energy index is 0 or 15, we're done @@ -1522,17 +1489,13 @@ void tms5220_device::parse_frame() // attempt to extract the repeat flag rep_flag = extract_bits(1); -#ifdef DEBUG_PARSE_FRAME_DUMP printbits(rep_flag, 1); - logerror(" "); -#endif + 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); -#ifdef DEBUG_PARSE_FRAME_DUMP - printbits(m_new_frame_pitch_idx,m_coeff->pitch_bits); - logerror(" "); -#endif + 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 m_uv_zpar = NEW_FRAME_UNVOICED_FLAG() ? 1 : 0; update_fifo_status_and_ints(); @@ -1545,10 +1508,8 @@ void tms5220_device::parse_frame() for (i = 0; i < 4; i++) { m_new_frame_k_idx[i] = extract_bits(m_coeff->kbits[i]); -#ifdef DEBUG_PARSE_FRAME_DUMP - printbits(m_new_frame_k_idx[i],m_coeff->kbits[i]); - logerror(" "); -#endif + 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(); if (m_DDIS && m_buffer_empty) goto ranout; } @@ -1564,28 +1525,21 @@ void tms5220_device::parse_frame() for (i = 4; i < m_coeff->num_k; i++) { m_new_frame_k_idx[i] = extract_bits(m_coeff->kbits[i]); -#ifdef DEBUG_PARSE_FRAME_DUMP - printbits(m_new_frame_k_idx[i],m_coeff->kbits[i]); - logerror(" "); -#endif + 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(); if (m_DDIS && m_buffer_empty) goto ranout; } -#ifdef DEBUG_PARSE_FRAME_DUMP - logerror("\n"); -#endif -#ifdef VERBOSE + LOGMASKED(LOG_PARSE_FRAME_DUMP_BIN | LOG_PARSE_FRAME_DUMP_HEX, "\n"); + if (m_DDIS) - logerror("Parsed a frame successfully in FIFO - %d bits remaining\n", (m_fifo_count*8)-(m_fifo_bits_taken)); + LOGMASKED(LOG_GENERAL, "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"); -#endif + LOGMASKED(LOG_GENERAL, "Parsed a frame successfully in ROM\n"); return; ranout: -#ifdef DEBUG_FRAME_ERRORS - logerror("Ran out of bits on a parse!\n"); -#endif + LOGMASKED(LOG_FRAME_ERRORS, "Ran out of bits on a parse!\n"); return; } @@ -1598,9 +1552,9 @@ void tms5220_device::parse_frame() 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); -#endif + + LOGMASKED(LOG_PIN_READS, "irq pin set to state %d\n", state); + if (!m_irq_handler.isnull() && state != m_irq_pin) m_irq_handler(!state); m_irq_pin = state; @@ -1615,9 +1569,9 @@ void tms5220_device::set_interrupt_state(int state) void tms5220_device::update_ready_state() { int state = ready_read(); -#ifdef DEBUG_PIN_READS - logerror("ready pin set to state %d\n", state); -#endif + + LOGMASKED(LOG_PIN_READS, "ready pin set to state %d\n", state); + if (!m_readyq_handler.isnull() && state != m_ready_pin) m_readyq_handler(!state); m_ready_pin = state; @@ -1780,10 +1734,8 @@ void tms5220_device::device_timer(emu_timer &timer, device_timer_id id, int para case 0x02: /* Write */ /* bring up to date first */ -#ifdef DEBUG_IO_READY - logerror("Serviced write: %02x\n", m_write_latch); - //logerror( "Processed write data: %02X\n", m_write_latch); -#endif + LOGMASKED(LOG_IO_READY, "Serviced write: %02x\n", m_write_latch); + //LOGMASKED(LOG_IO_READY, "Processed write data: %02X\n", m_write_latch); m_stream->update(); data_write(m_write_latch); break; @@ -1792,9 +1744,7 @@ void tms5220_device::device_timer(emu_timer &timer, device_timer_id id, int para /* bring up to date first */ m_stream->update(); m_read_latch = status_read(); -#ifdef DEBUG_IO_READY - logerror("Serviced read, returning %02x\n", m_read_latch); -#endif + LOGMASKED(LOG_IO_READY, "Serviced read, returning %02x\n", m_read_latch); break; case 0x03: /* High Impedance */ @@ -1815,14 +1765,11 @@ void tms5220_device::device_timer(emu_timer &timer, device_timer_id id, int para */ WRITE_LINE_MEMBER( tms5220_device::rsq_w ) { - uint8_t new_val; - m_true_timing = 1; state &= 0x01; -#ifdef DEBUG_RS_WS - logerror("/RS written with data: %d\n", state); -#endif - new_val = (m_rs_ws & 0x01) | (state<<1); + LOGMASKED(LOG_RS_WS, "/RS written with data: %d\n", state); + + uint8_t new_val = (m_rs_ws & 0x01) | (state<<1); if (new_val != m_rs_ws) { m_rs_ws = new_val; @@ -1830,11 +1777,9 @@ WRITE_LINE_MEMBER( tms5220_device::rsq_w ) { if (TMS5220_HAS_RATE_CONTROL) // correct for 5220c, ? for cd2501ecd reset(); -#ifdef DEBUG_RS_WS else /* illegal */ - logerror("tms5220_rsq_w: illegal\n"); -#endif + LOGMASKED(LOG_RS_WS, "tms5220_rsq_w: illegal\n"); return; } else if ( new_val == 3) @@ -1850,9 +1795,7 @@ WRITE_LINE_MEMBER( tms5220_device::rsq_w ) else { /* high to low - schedule ready cycle */ -#ifdef DEBUG_RS_WS - logerror("Scheduling ready cycle for /RS...\n"); -#endif + LOGMASKED(LOG_RS_WS, "Scheduling ready cycle for /RS...\n"); /* 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; update_ready_state(); @@ -1867,14 +1810,11 @@ WRITE_LINE_MEMBER( tms5220_device::rsq_w ) */ WRITE_LINE_MEMBER( tms5220_device::wsq_w ) { - uint8_t new_val; - m_true_timing = 1; state &= 0x01; -#ifdef DEBUG_RS_WS - logerror("/WS written with data: %d\n", state); -#endif - new_val = (m_rs_ws & 0x02) | (state<<0); + LOGMASKED(LOG_RS_WS, "/WS written with data: %d\n", state); + + uint8_t new_val = (m_rs_ws & 0x02) | (state<<0); if (new_val != m_rs_ws) { m_rs_ws = new_val; @@ -1882,11 +1822,9 @@ WRITE_LINE_MEMBER( tms5220_device::wsq_w ) { if (TMS5220_HAS_RATE_CONTROL) // correct for 5220c, probably also correct for cd2501ecd reset(); -#ifdef DEBUG_RS_WS else /* illegal */ - logerror("tms5220_wsq_w: illegal\n"); -#endif + LOGMASKED(LOG_RS_WS, "tms5220_wsq_w: illegal\n"); return; } else if ( new_val == 3) @@ -1902,9 +1840,8 @@ WRITE_LINE_MEMBER( tms5220_device::wsq_w ) else { /* high to low - schedule ready cycle */ -#ifdef DEBUG_RS_WS - logerror("Scheduling ready cycle for /WS...\n"); -#endif + LOGMASKED(LOG_RS_WS, "Scheduling ready cycle for /WS...\n"); + /* 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; update_ready_state(); @@ -1931,13 +1868,11 @@ WRITE_LINE_MEMBER( tms5220_device::wsq_w ) */ WRITE8_MEMBER( tms5220_device::combined_rsq_wsq_w ) { - uint8_t new_val; uint8_t falling_edges; m_true_timing = 1; -#ifdef DEBUG_RS_WS - logerror("/RS and /WS written with %d and %d respectively\n", (data&2)>>1, data&1); -#endif - new_val = data&0x03; + LOGMASKED(LOG_RS_WS, "/RS and /WS written with %d and %d respectively\n", (data&2)>>1, data&1); + + uint8_t new_val = data & 0x03; if (new_val != m_rs_ws) { falling_edges = ((m_rs_ws^new_val)&(~new_val)); @@ -1947,11 +1882,9 @@ WRITE8_MEMBER( tms5220_device::combined_rsq_wsq_w ) case 0: if (TMS5220_HAS_RATE_CONTROL) // correct for 5220c, probably also correct for cd2501ecd reset(); -#ifdef DEBUG_RS_WS else /* illegal */ - logerror("tms5220_combined_rsq_wsq_w: illegal\n"); -#endif + LOGMASKED(LOG_RS_WS, "tms5220_combined_rsq_wsq_w: illegal\n"); return; case 3: /* high impedance */ @@ -1960,13 +1893,14 @@ WRITE8_MEMBER( tms5220_device::combined_rsq_wsq_w ) case 2: // /WS active, /RS not /* check for falling or rising edge */ if (!(falling_edges&0x01)) return; /* low to high, do nothing */ + /* high to low - schedule ready cycle */ -#ifdef DEBUG_RS_WS - logerror("Scheduling ready cycle for /WS...\n"); -#endif + LOGMASKED(LOG_RS_WS, "Scheduling ready cycle for /WS...\n"); + /* 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; update_ready_state(); + /* Now comes the complicated part: how long does /READY stay inactive, when /WS is pulled low? This depends ENTIRELY on the command written, or whether the chip is in speak external mode or not... Speak external mode: ~16 cycles Command Mode: @@ -1983,13 +1917,14 @@ WRITE8_MEMBER( tms5220_device::combined_rsq_wsq_w ) case 1: // /RS active, /WS not /* check for falling or rising edge */ if (!(falling_edges&0x02)) return; /* low to high, do nothing */ + /* high to low - schedule ready cycle */ -#ifdef DEBUG_RS_WS - logerror("Scheduling ready cycle for /RS...\n"); -#endif + LOGMASKED(LOG_RS_WS, "Scheduling ready cycle for /RS...\n"); + /* 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; update_ready_state(); + /* How long does /READY stay inactive, when /RS is pulled low? I believe its almost always ~16 clocks (25 usec at 800khz as shown on the datasheet) */ m_timer_io_ready->adjust(clocks_to_attotime(16), 1); // this should take around 10-16 (closer to ~11?) cycles to complete return; @@ -2009,9 +1944,8 @@ void tms5220_device::write_data(uint8_t data) // prevent debugger from changing the internal state if (machine().side_effects_disabled()) return; -#ifdef DEBUG_RS_WS - logerror("tms5220_data_w: data %02x\n", data); -#endif + LOGMASKED(LOG_RS_WS, "tms5220_data_w: data %02x\n", data); + if (!m_true_timing) { /* bring up to date first */ @@ -2021,10 +1955,8 @@ void tms5220_device::write_data(uint8_t data) else { /* 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); -#endif + LOGMASKED(LOG_RS_WS, "tms5220_data_w: data written outside ws, status: %02x!\n", m_rs_ws); m_write_latch = data; } } @@ -2053,10 +1985,8 @@ uint8_t tms5220_device::read_status() /* actually in a read ? */ if (m_rs_ws == 0x01) return m_read_latch; -#ifdef DEBUG_RS_WS else - logerror("tms5220_status_r: data read outside rs!\n"); -#endif + LOGMASKED(LOG_RS_WS, "tms5220_status_r: data read outside rs!\n"); return 0xff; } } diff --git a/src/devices/sound/tms5220.h b/src/devices/sound/tms5220.h index 4cfab5491ef..b48ca8d9b9a 100644 --- a/src/devices/sound/tms5220.h +++ b/src/devices/sound/tms5220.h @@ -123,6 +123,8 @@ private: int cycles_to_ready(); int int_read(); void process(int16_t *buffer, unsigned int size); + int16_t clip_analog(int16_t cliptemp) const; + int32_t matrix_multiply(int32_t a, int32_t b) const; int32_t lattice_filter(); void process_command(unsigned char cmd); void parse_frame(); @@ -136,6 +138,9 @@ private: bool NEW_FRAME_SILENCE_FLAG() const { return m_new_frame_energy_idx == 0; } // ditto as above bool NEW_FRAME_UNVOICED_FLAG() const { return m_new_frame_pitch_idx == 0; } // ditto as above + // debugging helper + void printbits(long data, int num); + // internal state /* coefficient tables */ -- cgit v1.2.3