summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/sound/tms5220.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/emu/sound/tms5220.c')
-rw-r--r--src/emu/sound/tms5220.c1140
1 files changed, 1140 insertions, 0 deletions
diff --git a/src/emu/sound/tms5220.c b/src/emu/sound/tms5220.c
new file mode 100644
index 00000000000..c6907163d40
--- /dev/null
+++ b/src/emu/sound/tms5220.c
@@ -0,0 +1,1140 @@
+/**********************************************************************************************
+
+ TMS5220 simulator
+
+ Written for MAME by Frank Palazzolo
+ With help from Neill Corlett
+ Additional tweaking by Aaron Giles
+ TMS6100 Speech Rom support added by Raphael Nabet
+ PRNG code by Jarek Burczynski backported from tms5110.c by Lord Nightmare
+ Chirp/excitation table fixes by Lord Nightmare
+ Various fixes by Lord Nightmare
+ Modularization by Lord Nightmare
+
+***********************************************************************************************/
+
+#include "sndintrf.h"
+#include "tms5220.h"
+
+
+/* Pull in the ROM tables */
+#include "tms5220r.c"
+
+/*
+ Changes by R. Nabet
+ * added Speech ROM support
+ * modified code so that the beast only start speaking at the start of next frame, like the data
+ sheet says
+*/
+#define USE_OBSOLETE_HACK 0
+
+
+#ifndef TRUE
+ #define TRUE 1
+#endif
+#ifndef FALSE
+ #define FALSE 0
+#endif
+
+
+struct tms5220
+{
+ /* these contain data that describes the 128-bit data FIFO */
+ #define FIFO_SIZE 16
+ UINT8 fifo[FIFO_SIZE];
+ UINT8 fifo_head;
+ UINT8 fifo_tail;
+ UINT8 fifo_count;
+ UINT8 fifo_bits_taken;
+
+
+ /* these contain global status bits */
+ /*
+ R Nabet : speak_external is only set when a speak external command is going on.
+ tms5220_speaking is set whenever a speak or speak external command is going on.
+ Note that we really need to do anything in tms5220_process and play samples only when
+ tms5220_speaking is true. Else, we can play nothing as well, which is a
+ speed-up...
+ */
+ UINT8 tms5220_speaking; /* Speak or Speak External command in progress */
+ UINT8 speak_external; /* Speak External command in progress */
+ #if USE_OBSOLETE_HACK
+ UINT8 speak_delay_frames;
+ #endif
+ UINT8 talk_status; /* tms5220 is really currently speaking */
+ UINT8 first_frame; /* we have just started speaking, and we are to parse the first frame */
+ UINT8 last_frame; /* we are doing the frame of sound */
+ UINT8 buffer_low; /* FIFO has less than 8 bytes in it */
+ UINT8 buffer_empty; /* FIFO is empty*/
+ UINT8 irq_pin; /* state of the IRQ pin (output) */
+
+ void (*irq_func)(int state); /* called when the state of the IRQ pin changes */
+
+
+ /* these contain data describing the current and previous voice frames */
+ UINT16 old_energy;
+ UINT16 old_pitch;
+ INT32 old_k[10];
+
+ UINT16 new_energy;
+ UINT16 new_pitch;
+ INT32 new_k[10];
+
+
+ /* these are all used to contain the current state of the sound generation */
+ UINT16 current_energy;
+ UINT16 current_pitch;
+ INT32 current_k[10];
+
+ UINT16 target_energy;
+ UINT16 target_pitch;
+ INT32 target_k[10];
+
+ UINT16 previous_energy; /* needed for lattice filter to match patent */
+
+ UINT8 interp_count; /* number of interp periods (0-7) */
+ UINT8 sample_count; /* sample number within interp (0-24) */
+ UINT16 pitch_count;
+
+ INT32 u[11];
+ INT32 x[10];
+
+ INT32 RNG; /* the random noise generator configuration is: 1 + x + x^3 + x^4 + x^13 */
+ INT8 excitation_data;
+
+ /* R Nabet : These have been added to emulate speech Roms */
+ int (*read_callback)(int count);
+ void (*load_address_callback)(int data);
+ void (*read_and_branch_callback)(void);
+ UINT8 schedule_dummy_read; /* set after each load address, so that next read operation
+ is preceded by a dummy read */
+
+ UINT8 data_register; /* data register, used by read command */
+ UINT8 RDB_flag; /* whether we should read data register or status register */
+
+ /* flag for variant tms0285/tms5200 emulation */
+ /* The TMC0285 AKA TMS5200 is an early variant of the TMS5220 used in
+ the early releases of the Speech Module for the TI-99/4(a) computer,
+ in Zaccaria's 'Money Money', and in a few other places.
+ The TMS5200 has a bug in its state machine PLA which causes it to
+ incorrectly use the LPC K3 parameter table for both K3 and K4.
+ (the chip has the unused K4 parameter in its internal ROM, but it is
+ never accessed due to the PLA bug)
+ The bug was fixed in the quickly-released TMS5220.
+ TI may have sold the remaining stocks of TMS5200s at a discount, and
+ apparently provided a special encoder to work around the bug.
+ Other than the bug, the two chips are identical.
+ Another variant of the TMS5220 is the TMS5220C/TSP5220C, which adds
+ an additional opcode (to select the data rate).
+ */
+ tms5220_variant variant;
+ /* The TMS52xx has two different ways of providing output data: the
+ analog speaker pin (which was usually used) and the Digital I/O pin.
+ The internal DAC used to feed the analog pin is only 8 bits, while the
+ digital pin gives full 12? bit resolution to the output data.
+ */
+ UINT8 digital_select;
+};
+
+
+/* Static function prototypes */
+static void process_command(struct tms5220 *tms);
+static int extract_bits(struct tms5220 *tms, int count);
+static int parse_frame(struct tms5220 *tms, int the_first_frame);
+static void check_buffer_low(struct tms5220 *tms);
+static void set_interrupt_state(struct tms5220 *tms, int state);
+static INT16 lattice_filter(void *chip);
+static INT16 clip_and_wrap(INT16 cliptemp);
+
+
+#define DEBUG_5220 0
+
+
+void *tms5220_create(int index)
+{
+ struct tms5220 *tms;
+
+ tms = malloc_or_die(sizeof(*tms));
+ memset(tms, 0, sizeof(*tms));
+
+ state_save_register_item_array("tms5220", index, tms->fifo);
+ state_save_register_item("tms5220", index, tms->fifo_head);
+ state_save_register_item("tms5220", index, tms->fifo_tail);
+ state_save_register_item("tms5220", index, tms->fifo_count);
+ state_save_register_item("tms5220", index, tms->fifo_bits_taken);
+
+ state_save_register_item("tms5220", index, tms->tms5220_speaking);
+ state_save_register_item("tms5220", index, tms->speak_external);
+ state_save_register_item("tms5220", index, tms->talk_status);
+ state_save_register_item("tms5220", index, tms->first_frame);
+ state_save_register_item("tms5220", index, tms->last_frame);
+ state_save_register_item("tms5220", index, tms->buffer_low);
+ state_save_register_item("tms5220", index, tms->buffer_empty);
+ state_save_register_item("tms5220", index, tms->irq_pin);
+
+ state_save_register_item("tms5220", index, tms->old_energy);
+ state_save_register_item("tms5220", index, tms->old_pitch);
+ state_save_register_item_array("tms5220", index, tms->old_k);
+
+ state_save_register_item("tms5220", index, tms->new_energy);
+ state_save_register_item("tms5220", index, tms->new_pitch);
+ state_save_register_item_array("tms5220", index, tms->new_k);
+
+ state_save_register_item("tms5220", index, tms->current_energy);
+ state_save_register_item("tms5220", index, tms->current_pitch);
+ state_save_register_item_array("tms5220", index, tms->current_k);
+
+ state_save_register_item("tms5220", index, tms->target_energy);
+ state_save_register_item("tms5220", index, tms->target_pitch);
+ state_save_register_item_array("tms5220", index, tms->target_k);
+
+ state_save_register_item("tms5220", index, tms->previous_energy);
+
+ state_save_register_item("tms5220", index, tms->interp_count);
+ state_save_register_item("tms5220", index, tms->sample_count);
+ state_save_register_item("tms5220", index, tms->pitch_count);
+
+ state_save_register_item_array("tms5220", index, tms->u);
+ state_save_register_item_array("tms5220", index, tms->x);
+
+ state_save_register_item("tms5220", index, tms->RNG);
+ state_save_register_item("tms5220", index, tms->excitation_data);
+
+ state_save_register_item("tms5220", index, tms->schedule_dummy_read);
+ state_save_register_item("tms5220", index, tms->data_register);
+ state_save_register_item("tms5220", index, tms->RDB_flag);
+ state_save_register_item("tms5220", index, tms->digital_select);
+
+ return tms;
+}
+
+
+void tms5220_destroy(void *chip)
+{
+ free(chip);
+}
+
+/**********************************************************************************************
+
+ tms5220_reset -- resets the TMS5220
+
+***********************************************************************************************/
+
+void tms5220_reset_chip(void *chip)
+{
+ struct tms5220 *tms = chip;
+
+ /* initialize the FIFO */
+ /*memset(tms->fifo, 0, sizeof(tms->fifo));*/
+ tms->fifo_head = tms->fifo_tail = tms->fifo_count = tms->fifo_bits_taken = 0;
+
+ /* initialize the chip state */
+ /* Note that we do not actually clear IRQ on start-up : IRQ is even raised if tms->buffer_empty or tms->buffer_low are 0 */
+ tms->tms5220_speaking = tms->speak_external = tms->talk_status = tms->first_frame = tms->last_frame = tms->irq_pin = 0;
+#if USE_OBSOLETE_HACK
+ tms->speak_delay_frames = 0;
+#endif
+ if (tms->irq_func) tms->irq_func(0);
+ tms->buffer_empty = tms->buffer_low = 1;
+
+ tms->RDB_flag = FALSE;
+
+ /* initialize the energy/pitch/k states */
+ tms->old_energy = tms->new_energy = tms->current_energy = tms->target_energy = 0;
+ tms->old_pitch = tms->new_pitch = tms->current_pitch = tms->target_pitch = 0;
+ memset(tms->old_k, 0, sizeof(tms->old_k));
+ memset(tms->new_k, 0, sizeof(tms->new_k));
+ memset(tms->current_k, 0, sizeof(tms->current_k));
+ memset(tms->target_k, 0, sizeof(tms->target_k));
+
+ /* initialize the sample generators */
+ tms->interp_count = tms->sample_count = tms->pitch_count = 0;
+ tms->RNG = 0x1FFF;
+ memset(tms->u, 0, sizeof(tms->u));
+ memset(tms->x, 0, sizeof(tms->x));
+
+ if (tms->load_address_callback)
+ (*tms->load_address_callback)(0);
+
+ tms->schedule_dummy_read = TRUE;
+}
+
+
+
+/**********************************************************************************************
+
+ tms5220_set_irq -- sets the interrupt handler
+
+***********************************************************************************************/
+
+void tms5220_set_irq(void *chip, void (*func)(int))
+{
+ struct tms5220 *tms = chip;
+ tms->irq_func = func;
+}
+
+
+/**********************************************************************************************
+
+ tms5220_set_read -- sets the speech ROM read handler
+
+***********************************************************************************************/
+
+void tms5220_set_read(void *chip, int (*func)(int))
+{
+ struct tms5220 *tms = chip;
+ tms->read_callback = func;
+}
+
+
+/**********************************************************************************************
+
+ tms5220_set_load_address -- sets the speech ROM load address handler
+
+***********************************************************************************************/
+
+void tms5220_set_load_address(void *chip, void (*func)(int))
+{
+ struct tms5220 *tms = chip;
+ tms->load_address_callback = func;
+}
+
+
+/**********************************************************************************************
+
+ tms5220_set_read_and_branch -- sets the speech ROM read and branch handler
+
+***********************************************************************************************/
+
+void tms5220_set_read_and_branch(void *chip, void (*func)(void))
+{
+ struct tms5220 *tms = chip;
+ tms->read_and_branch_callback = func;
+}
+
+
+/**********************************************************************************************
+
+ tms5220_set_variant -- sets the tms5220 core to emulate its buggy forerunner, the tms0285
+
+***********************************************************************************************/
+
+void tms5220_set_variant(void *chip, tms5220_variant new_variant)
+{
+ struct tms5220 *tms = chip;
+ tms->variant = new_variant;
+}
+
+
+/**********************************************************************************************
+
+ tms5220_data_write -- handle a write to the TMS5220
+
+***********************************************************************************************/
+
+void tms5220_data_write(void *chip, int data)
+{
+ struct tms5220 *tms = chip;
+
+ /* add this byte to the FIFO */
+ if (tms->fifo_count < FIFO_SIZE)
+ {
+ tms->fifo[tms->fifo_tail] = data;
+ tms->fifo_tail = (tms->fifo_tail + 1) % FIFO_SIZE;
+ tms->fifo_count++;
+
+ /* if we were speaking, then we're no longer empty */
+ if (tms->speak_external)
+ tms->buffer_empty = 0;
+
+ if (DEBUG_5220) logerror("Added byte to FIFO (size=%2d)\n", tms->fifo_count);
+ }
+ else
+ {
+ if (DEBUG_5220) logerror("Ran out of room in the FIFO!\n");
+ }
+
+ /* update the buffer low state */
+ check_buffer_low(tms);
+
+ if (! tms->speak_external)
+ /* R Nabet : we parse commands at once. It is necessary for such commands as read. */
+ process_command (tms/*data*/);
+}
+
+
+/**********************************************************************************************
+
+ tms5220_status_read -- read status or data from the TMS5220
+
+ From the data sheet:
+ bit 0 = TS - Talk Status is active (high) when the VSP is processing speech data.
+ Talk Status goes active at the initiation of a Speak command or after nine
+ bytes of data are loaded into the FIFO following a Speak External command. It
+ goes inactive (low) when the stop code (Energy=1111) is processed, or
+ immediately by a buffer empty condition or a reset command.
+ bit 1 = BL - Buffer Low is active (high) when the FIFO buffer is more than half empty.
+ Buffer Low is set when the "Last-In" byte is shifted down past the half-full
+ boundary of the stack. Buffer Low is cleared when data is loaded to the stack
+ so that the "Last-In" byte lies above the half-full boundary and becomes the
+ ninth data byte of the stack.
+ bit 2 = BE - Buffer Empty is active (high) when the FIFO buffer has run out of data
+ while executing a Speak External command. Buffer Empty is set when the last bit
+ of the "Last-In" byte is shifted out to the Synthesis Section. This causes
+ Talk Status to be cleared. Speed is terminated at some abnormal point and the
+ Speak External command execution is terminated.
+
+***********************************************************************************************/
+
+int tms5220_status_read(void *chip)
+{
+ struct tms5220 *tms = chip;
+
+ if (tms->RDB_flag)
+ { /* if last command was read, return data register */
+ tms->RDB_flag = FALSE;
+ return(tms->data_register);
+ }
+ else
+ { /* read status */
+
+ /* clear the interrupt pin */
+ set_interrupt_state(tms, 0);
+
+ if (DEBUG_5220) logerror("Status read: TS=%d BL=%d BE=%d\n", tms->talk_status, tms->buffer_low, tms->buffer_empty);
+
+ return (tms->talk_status << 7) | (tms->buffer_low << 6) | (tms->buffer_empty << 5);
+ }
+}
+
+
+
+/**********************************************************************************************
+
+ tms5220_ready_read -- returns the ready state of the TMS5220
+
+***********************************************************************************************/
+
+int tms5220_ready_read(void *chip)
+{
+ struct tms5220 *tms = chip;
+ return (tms->fifo_count < FIFO_SIZE-1);
+}
+
+
+/**********************************************************************************************
+
+ tms5220_ready_read -- returns the number of cycles until ready is asserted
+
+***********************************************************************************************/
+
+int tms5220_cycles_to_ready(void *chip)
+{
+ struct tms5220 *tms = chip;
+ int answer;
+
+
+ if (tms5220_ready_read(tms))
+ answer = 0;
+ else
+ {
+ int val;
+
+ answer = 200-tms->sample_count+8;
+
+ /* total number of bits available in current byte is (8 - tms->fifo_bits_taken) */
+ /* if more than 4 are available, we need to check the energy */
+ if (tms->fifo_bits_taken < 4)
+ {
+ /* read energy */
+ val = (tms->fifo[tms->fifo_head] >> tms->fifo_bits_taken) & 0xf;
+ if (val == 0)
+ /* 0 -> silence frame: we will only read 4 bits, and we will
+ therefore need to read another frame before the FIFO is not
+ full any more */
+ answer += 200;
+ /* 15 -> stop frame, we will only read 4 bits, but the FIFO will
+ we cleared */
+ /* otherwise, we need to parse the repeat flag (1 bit) and the
+ pitch (6 bits), so everything will be OK. */
+ }
+ }
+
+ return answer;
+}
+
+
+/**********************************************************************************************
+
+ tms5220_int_read -- returns the interrupt state of the TMS5220
+
+***********************************************************************************************/
+
+int tms5220_int_read(void *chip)
+{
+ struct tms5220 *tms = chip;
+ return tms->irq_pin;
+}
+
+
+
+/**********************************************************************************************
+
+ tms5220_process -- fill the buffer with a specific number of samples
+
+***********************************************************************************************/
+
+void tms5220_process(void *chip, INT16 *buffer, unsigned int size)
+{
+ struct tms5220 *tms = chip;
+ int buf_count=0;
+ int i, interp_period, bitout;
+
+tryagain:
+
+ /* if we're not speaking, parse commands */
+ /*while (!tms->speak_external && tms->fifo_count > 0)
+ process_command(tms);*/
+
+ /* if we're empty and still not speaking, fill with nothingness */
+ if ((!tms->tms5220_speaking) && (!tms->last_frame))
+ goto empty;
+
+ /* if we're to speak, but haven't started, wait for the 9th byte */
+ if (!tms->talk_status && tms->speak_external)
+ {
+ if (tms->fifo_count < 9)
+ goto empty;
+
+ tms->talk_status = 1;
+ tms->first_frame = 1; /* will cause the first frame to be parsed */
+ tms->buffer_empty = 0;
+ }
+
+#if 0
+ /* we are to speak, yet we fill with 0s until start of next frame */
+ if (tms->first_frame)
+ {
+ while ((size > 0) && ((tms->sample_count != 0) || (tms->interp_count != 0)))
+ {
+ tms->sample_count = (tms->sample_count + 1) % 200;
+ tms->interp_count = (tms->interp_count + 1) % 25;
+ buffer[buf_count] = 0x00; /* should be (-1 << 8) ??? (cf note in data sheet, p 10, table 4) */
+ buf_count++;
+ size--;
+ }
+ }
+#endif
+
+#if USE_OBSOLETE_HACK
+ /* apply some delay before we actually consume data; Victory requires this */
+ if (tms->speak_delay_frames)
+ {
+ if (size <= tms->speak_delay_frames)
+ {
+ tms->speak_delay_frames -= size;
+ size = 0;
+ }
+ else
+ {
+ size -= tms->speak_delay_frames;
+ tms->speak_delay_frames = 0;
+ }
+ }
+#endif
+
+ /* loop until the buffer is full or we've stopped speaking */
+ while ((size > 0) && tms->talk_status)
+ {
+
+ /* if we're ready for a new frame */
+ if ((tms->interp_count == 0) && (tms->sample_count == 0))
+ {
+ /* Parse a new frame */
+ if (!parse_frame(tms, tms->first_frame))
+ break;
+ tms->first_frame = 0;
+
+ /* Set old target as new start of frame */
+ tms->current_energy = tms->old_energy;
+ tms->current_pitch = tms->old_pitch;
+ for (i = 0; i < 10; i++)
+ tms->current_k[i] = tms->old_k[i];
+
+ /* is this a zero energy frame? */
+ if (tms->current_energy == 0)
+ {
+ /*mame_printf_debug("processing frame: zero energy\n");*/
+ tms->target_energy = 0;
+ tms->target_pitch = tms->current_pitch;
+ for (i = 0; i < 10; i++)
+ tms->target_k[i] = tms->current_k[i];
+ }
+
+ /* is this a stop frame? */
+ else if (tms->current_energy == (energytable[15] >> 6))
+ {
+ /*mame_printf_debug("processing frame: stop frame\n");*/
+ tms->current_energy = energytable[0] >> 6;
+ tms->target_energy = tms->current_energy;
+ /*tms->interp_count = tms->sample_count =*/ tms->pitch_count = 0;
+ tms->last_frame = 0;
+ if (tms->tms5220_speaking)
+ /* new speech command in progress */
+ tms->first_frame = 1;
+ else
+ {
+ /* really stop speaking */
+ tms->talk_status = 0;
+
+ /* generate an interrupt if necessary */
+ set_interrupt_state(tms, 1);
+ }
+
+ /* try to fetch commands again */
+ goto tryagain;
+ }
+ else
+ {
+ /* is this the ramp down frame? */
+ if (tms->new_energy == (energytable[15] >> 6))
+ {
+ /*mame_printf_debug("processing frame: ramp down\n");*/
+ tms->target_energy = 0;
+ tms->target_pitch = tms->current_pitch;
+ for (i = 0; i < 10; i++)
+ tms->target_k[i] = tms->current_k[i];
+ }
+ /* Reset the step size */
+ else
+ {
+ /*mame_printf_debug("processing frame: Normal\n");*/
+ /*mame_printf_debug("*** Energy = %d\n",tms->current_energy);*/
+ /*mame_printf_debug("proc: %d %d\n",last_fbuf_head,fbuf_head);*/
+
+ tms->target_energy = tms->new_energy;
+ tms->target_pitch = tms->new_pitch;
+
+ for (i = 0; i < 4; i++)
+ tms->target_k[i] = tms->new_k[i];
+ if (tms->current_pitch == 0)
+ for (i = 4; i < 10; i++)
+ {
+ tms->target_k[i] = tms->current_k[i] = 0;
+ }
+ else
+ for (i = 4; i < 10; i++)
+ tms->target_k[i] = tms->new_k[i];
+ }
+ }
+ }
+ else if (tms->interp_count == 0)
+ {
+ /* Update values based on step values */
+ /*mame_printf_debug("\n");*/
+
+ interp_period = tms->sample_count / 25;
+ tms->current_energy += ((tms->target_energy - tms->current_energy) >> interp_coeff[interp_period]);
+ if (tms->old_pitch != 0)
+ tms->current_pitch += ((tms->target_pitch - tms->current_pitch) >> interp_coeff[interp_period]);
+
+ /*mame_printf_debug("*** Energy = %d\n",tms->current_energy);*/
+
+ for (i = 0; i < 10; i++)
+ {
+ tms->current_k[i] += ((tms->target_k[i] - tms->current_k[i]) >> interp_coeff[interp_period]);
+ }
+ }
+
+ if (tms->old_energy == 0)
+ {
+ /* generate silent samples here */
+ tms->excitation_data = 0x00; /* I'm not sure if this is actually RIGHT, the current_energy may be forced to zero when we've just passed a zero energy frame. However, this does work too. May be removed later. */
+ }
+ else if (tms->old_pitch == 0)
+ {
+ /* generate unvoiced samples here */
+ if (tms->RNG & 1)
+ tms->excitation_data = -0x40; /* according to the patent it is (either + or -) half of the maximum value in the chirp table, so +-64 */
+ else
+ tms->excitation_data = 0x40;
+ }
+ else
+ {
+ /* generate voiced samples here */
+ tms->excitation_data = chirptable[tms->pitch_count%sizeof(chirptable)];
+ }
+
+ /* Update LFSR every clock, like patent shows */
+ bitout = ((tms->RNG >> 12) & 1) ^
+ ((tms->RNG >> 10) & 1) ^
+ ((tms->RNG >> 9) & 1) ^
+ ((tms->RNG >> 0) & 1);
+ tms->RNG >>= 1;
+ tms->RNG |= bitout << 12;
+
+ buffer[buf_count] = clip_and_wrap(lattice_filter(tms)); /* execute lattice filter and clipping/wrapping */
+
+ if (tms->digital_select == 0) /* if digital is NOT selected... */
+ buffer[buf_count] &= 0xff00; /* mask out all but the 8 dac bits */
+
+ /* Update all counts */
+
+ size--;
+ tms->sample_count = (tms->sample_count + 1) % 200;
+
+ if (tms->current_pitch != 0)
+ tms->pitch_count = (tms->pitch_count + 1) % tms->current_pitch;
+ else
+ tms->pitch_count = 0;
+
+ tms->interp_count = (tms->interp_count + 1) % 25;
+ buf_count++;
+ }
+
+empty:
+
+ while (size > 0)
+ {
+ tms->sample_count = (tms->sample_count + 1) % 200;
+ tms->interp_count = (tms->interp_count + 1) % 25;
+ buffer[buf_count] = 0x00; /* should be (-1 << 8) ??? (cf note in data sheet, p 10, table 4) */
+ buf_count++;
+ size--;
+ }
+}
+
+/**********************************************************************************************
+
+ clip_and_wrap -- clips and wraps the 14 bit return value from the lattice filter to its final 10 bit value (-512 to 511), and upshifts this to 16 bits
+
+***********************************************************************************************/
+
+static INT16 clip_and_wrap(INT16 cliptemp)
+{
+ /* clipping & wrapping, just like the patent shows */
+
+ if (cliptemp > 2047) cliptemp = -2048 + (cliptemp-2047);
+ else if (cliptemp < -2048) cliptemp = 2047 - (cliptemp+2048);
+
+ if (cliptemp > 511) { mame_printf_debug ("cliptemp > 511\n");
+ return 127<<8; }
+ else if (cliptemp < -512) { mame_printf_debug ("cliptemp < -512\n");
+ return -128<<8; }
+ else
+ return cliptemp << 6;
+}
+
+
+/**********************************************************************************************
+
+ lattice_filter -- executes one 'full run' of the lattice filter on a specific byte of
+ excitation data, and specific values of all the current k constants, and returns the
+ resulting sample.
+ Note: the current_k processing here by dividing the result by 32768 is necessary, as the stored
+ parameters in the lookup table are the 10 bit coefficients but are pre-multiplied by 512 for
+ ease of storage. This is undone on the real chip by a shifter here, after the multiply.
+
+***********************************************************************************************/
+
+static INT16 lattice_filter(void *chip)
+{
+ struct tms5220 *tms = chip;
+ /* Lattice filter here */
+ /* Aug/05/07: redone as unrolled loop, for clarity - LN*/
+ /* Copied verbatim from table I in US patent 4,209,804:
+ notation equivalencies from table:
+ Yn(i) == tms->u[n-1]
+ Kn = tms->current_k[n-1]
+ bn = tms->x[n-1]
+ */
+ tms->u[10] = (tms->excitation_data * tms->previous_energy) >> 8; /* Y(11) */
+ tms->u[9] = tms->u[10] - ((tms->current_k[9] * tms->x[9]) / 32768);
+ tms->u[8] = tms->u[9] - ((tms->current_k[8] * tms->x[8]) / 32768);
+ tms->x[9] = tms->x[8] + ((tms->current_k[8] * tms->u[8]) / 32768);
+ tms->u[7] = tms->u[8] - ((tms->current_k[7] * tms->x[7]) / 32768);
+ tms->x[8] = tms->x[7] + ((tms->current_k[7] * tms->u[7]) / 32768);
+ tms->u[6] = tms->u[7] - ((tms->current_k[6] * tms->x[6]) / 32768);
+ tms->x[7] = tms->x[6] + ((tms->current_k[6] * tms->u[6]) / 32768);
+ tms->u[5] = tms->u[6] - ((tms->current_k[5] * tms->x[5]) / 32768);
+ tms->x[6] = tms->x[5] + ((tms->current_k[5] * tms->u[5]) / 32768);
+ tms->u[4] = tms->u[5] - ((tms->current_k[4] * tms->x[4]) / 32768);
+ tms->x[5] = tms->x[4] + ((tms->current_k[4] * tms->u[4]) / 32768);
+ tms->u[3] = tms->u[4] - ((tms->current_k[3] * tms->x[3]) / 32768);
+ tms->x[4] = tms->x[3] + ((tms->current_k[3] * tms->u[3]) / 32768);
+ tms->u[2] = tms->u[3] - ((tms->current_k[2] * tms->x[2]) / 32768);
+ tms->x[3] = tms->x[2] + ((tms->current_k[2] * tms->u[2]) / 32768);
+ tms->u[1] = tms->u[2] - ((tms->current_k[1] * tms->x[1]) / 32768);
+ tms->x[2] = tms->x[1] + ((tms->current_k[1] * tms->u[1]) / 32768);
+ tms->u[0] = tms->u[1] - ((tms->current_k[0] * tms->x[0]) / 32768);
+ tms->x[1] = tms->x[0] + ((tms->current_k[0] * tms->u[0]) / 32768);
+ tms->x[0] = tms->u[0];
+ tms->previous_energy = tms->current_energy;
+ return tms->u[0];
+}
+
+
+/**********************************************************************************************
+
+ process_command -- extract a byte from the FIFO and interpret it as a command
+
+***********************************************************************************************/
+
+static void process_command(struct tms5220 *tms)
+{
+ unsigned char cmd;
+
+ /* if there are stray bits, ignore them */
+ if (tms->fifo_bits_taken)
+ {
+ tms->fifo_bits_taken = 0;
+ tms->fifo_count--;
+ tms->fifo_head = (tms->fifo_head + 1) % FIFO_SIZE;
+ }
+
+ /* grab a full byte from the FIFO */
+ if (tms->fifo_count > 0)
+ {
+ cmd = tms->fifo[tms->fifo_head];
+ tms->fifo_count--;
+ tms->fifo_head = (tms->fifo_head + 1) % FIFO_SIZE;
+
+ /* parse the command */
+ switch (cmd & 0x70)
+ {
+ case 0x10 : /* read byte */
+ if (tms->schedule_dummy_read)
+ {
+ tms->schedule_dummy_read = FALSE;
+ if (tms->read_callback)
+ (*tms->read_callback)(1);
+ }
+ if (tms->read_callback)
+ tms->data_register = (*tms->read_callback)(8); /* read one byte from speech ROM... */
+ tms->RDB_flag = TRUE;
+ break;
+
+ case 0x30 : /* read and branch */
+ if (DEBUG_5220) logerror("read and branch command received\n");
+ tms->RDB_flag = FALSE;
+ if (tms->read_and_branch_callback)
+ (*tms->read_and_branch_callback)();
+ break;
+
+ case 0x40 : /* load address */
+ /* 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 (tms->load_address_callback)
+ (*tms->load_address_callback)(cmd & 0x0f);
+ tms->schedule_dummy_read = TRUE;
+ break;
+
+ case 0x50 : /* speak */
+ if (tms->schedule_dummy_read)
+ {
+ tms->schedule_dummy_read = FALSE;
+ if (tms->read_callback)
+ (*tms->read_callback)(1);
+ }
+ tms->tms5220_speaking = 1;
+ tms->speak_external = 0;
+ if (! tms->last_frame)
+ {
+ tms->first_frame = 1;
+ }
+ tms->talk_status = 1; /* start immediately */
+ break;
+
+ case 0x60 : /* speak external */
+ tms->tms5220_speaking = tms->speak_external = 1;
+#if USE_OBSOLETE_HACK
+ tms->speak_delay_frames = 10;
+#endif
+
+ tms->RDB_flag = FALSE;
+
+ /* according to the datasheet, this will cause an interrupt due to a BE condition */
+ if (!tms->buffer_empty)
+ {
+ tms->buffer_empty = 1;
+ set_interrupt_state(tms, 1);
+ }
+
+ tms->talk_status = 0; /* wait to have 8 bytes in buffer before starting */
+ break;
+
+ case 0x70 : /* reset */
+ if (tms->schedule_dummy_read)
+ {
+ tms->schedule_dummy_read = FALSE;
+ if (tms->read_callback)
+ (*tms->read_callback)(1);
+ }
+ tms5220_reset_chip(tms);
+ break;
+ }
+ }
+
+ /* update the buffer low state */
+ check_buffer_low(tms);
+}
+
+
+
+/**********************************************************************************************
+
+ extract_bits -- extract a specific number of bits from the FIFO
+
+***********************************************************************************************/
+
+static int extract_bits(struct tms5220 *tms, int count)
+{
+ int val = 0;
+
+ if (tms->speak_external)
+ {
+ /* extract from FIFO */
+ while (count--)
+ {
+ val = (val << 1) | ((tms->fifo[tms->fifo_head] >> tms->fifo_bits_taken) & 1);
+ tms->fifo_bits_taken++;
+ if (tms->fifo_bits_taken >= 8)
+ {
+ tms->fifo_count--;
+ tms->fifo_head = (tms->fifo_head + 1) % FIFO_SIZE;
+ tms->fifo_bits_taken = 0;
+ }
+ }
+ }
+ else
+ {
+ /* extract from speech ROM */
+ if (tms->read_callback)
+ val = (* tms->read_callback)(count);
+ }
+
+ return val;
+}
+
+
+
+/**********************************************************************************************
+
+ parse_frame -- parse a new frame's worth of data; returns 0 if not enough bits in buffer
+
+***********************************************************************************************/
+
+static int parse_frame(struct tms5220 *tms, int the_first_frame)
+{
+ int bits = 0; /* number of bits in FIFO (speak external only) */
+ int indx, i, rep_flag;
+
+ if (! the_first_frame)
+ {
+ /* remember previous frame */
+ tms->old_energy = tms->new_energy;
+ tms->old_pitch = tms->new_pitch;
+ for (i = 0; i < 10; i++)
+ tms->old_k[i] = tms->new_k[i];
+ }
+
+ /* clear out the new frame */
+ tms->new_energy = 0;
+ tms->new_pitch = 0;
+ for (i = 0; i < 10; i++)
+ tms->new_k[i] = 0;
+
+ /* if the previous frame was a stop frame, don't do anything */
+ if ((! the_first_frame) && (tms->old_energy == (energytable[15] >> 6)))
+ /*return 1;*/
+ {
+ tms->buffer_empty = 1;
+ return 1;
+ }
+
+ if (tms->speak_external)
+ /* count the total number of bits available */
+ bits = tms->fifo_count * 8 - tms->fifo_bits_taken;
+
+ /* attempt to extract the energy index */
+ if (tms->speak_external)
+ {
+ bits -= 4;
+ if (bits < 0)
+ goto ranout;
+ }
+ indx = extract_bits(tms, 4);
+ tms->new_energy = energytable[indx] >> 6;
+
+ /* if the index is 0 or 15, we're done */
+ if (indx == 0 || indx == 15)
+ {
+ if (DEBUG_5220) logerror(" (4-bit energy=%d frame)\n",tms->new_energy);
+
+ /* clear tms->fifo if stop frame encountered */
+ if (indx == 15)
+ {
+ tms->fifo_head = tms->fifo_tail = tms->fifo_count = tms->fifo_bits_taken = 0;
+ tms->speak_external = tms->tms5220_speaking = 0;
+ tms->last_frame = 1;
+ }
+ goto done;
+ }
+
+ /* attempt to extract the repeat flag */
+ if (tms->speak_external)
+ {
+ bits -= 1;
+ if (bits < 0)
+ goto ranout;
+ }
+ rep_flag = extract_bits(tms, 1);
+
+ /* attempt to extract the pitch */
+ if (tms->speak_external)
+ {
+ bits -= 6;
+ if (bits < 0)
+ goto ranout;
+ }
+ indx = extract_bits(tms, 6);
+ tms->new_pitch = pitchtable[indx] / 256;
+
+ /* if this is a repeat frame, just copy the k's */
+ if (rep_flag)
+ {
+ for (i = 0; i < 10; i++)
+ tms->new_k[i] = tms->old_k[i];
+
+ if (DEBUG_5220) logerror(" (11-bit energy=%d pitch=%d rep=%d frame)\n", tms->new_energy, tms->new_pitch, rep_flag);
+ goto done;
+ }
+
+ /* if the pitch index was zero, we need 4 k's */
+ if (indx == 0)
+ {
+ /* attempt to extract 4 K's */
+ if (tms->speak_external)
+ {
+ bits -= 18;
+ if (bits < 0)
+ goto ranout;
+ }
+ tms->new_k[0] = k1table[extract_bits(tms, 5)];
+ tms->new_k[1] = k2table[extract_bits(tms, 5)];
+ tms->new_k[2] = k3table[extract_bits(tms, 4)];
+ if (tms->variant == variant_tms0285)
+ tms->new_k[3] = k3table[extract_bits(tms, 4)]; /* ??? */
+ else
+ tms->new_k[3] = k4table[extract_bits(tms, 4)];
+
+ if (DEBUG_5220) logerror(" (29-bit energy=%d pitch=%d rep=%d 4K frame)\n", tms->new_energy, tms->new_pitch, rep_flag);
+ goto done;
+ }
+
+ /* else we need 10 K's */
+ if (tms->speak_external)
+ {
+ bits -= 39;
+ if (bits < 0)
+ goto ranout;
+ }
+
+ tms->new_k[0] = k1table[extract_bits(tms, 5)];
+ tms->new_k[1] = k2table[extract_bits(tms, 5)];
+ tms->new_k[2] = k3table[extract_bits(tms, 4)];
+ if (tms->variant == variant_tms0285)
+ tms->new_k[3] = k3table[extract_bits(tms, 4)]; /* ??? */
+ else
+ tms->new_k[3] = k4table[extract_bits(tms, 4)];
+ tms->new_k[4] = k5table[extract_bits(tms, 4)];
+ tms->new_k[5] = k6table[extract_bits(tms, 4)];
+ tms->new_k[6] = k7table[extract_bits(tms, 4)];
+ tms->new_k[7] = k8table[extract_bits(tms, 3)];
+ tms->new_k[8] = k9table[extract_bits(tms, 3)];
+ tms->new_k[9] = k10table[extract_bits(tms, 3)];
+
+ if (DEBUG_5220) logerror(" (50-bit energy=%d pitch=%d rep=%d 10K frame)\n", tms->new_energy, tms->new_pitch, rep_flag);
+
+done:
+ if (DEBUG_5220)
+ {
+ if (tms->speak_external)
+ logerror("Parsed a frame successfully in FIFO - %d bits remaining\n", bits);
+ else
+ logerror("Parsed a frame successfully in ROM\n");
+ }
+
+ if (the_first_frame)
+ {
+ /* if this is the first frame, no previous frame to take as a starting point */
+ tms->old_energy = tms->new_energy;
+ tms->old_pitch = tms->new_pitch;
+ for (i = 0; i < 10; i++)
+ tms->old_k[i] = tms->new_k[i];
+ }
+
+ /* update the tms->buffer_low status */
+ check_buffer_low(tms);
+ return 1;
+
+ranout:
+
+ if (DEBUG_5220) logerror("Ran out of bits on a parse!\n");
+
+ /* this is an error condition; mark the buffer empty and turn off speaking */
+ tms->buffer_empty = 1;
+ tms->talk_status = tms->speak_external = tms->tms5220_speaking = the_first_frame = tms->last_frame = 0;
+ tms->fifo_count = tms->fifo_head = tms->fifo_tail = 0;
+
+ tms->RDB_flag = FALSE;
+
+ /* generate an interrupt if necessary */
+ set_interrupt_state(tms, 1);
+ return 0;
+}
+
+
+
+/**********************************************************************************************
+
+ check_buffer_low -- check to see if the buffer low flag should be on or off
+
+***********************************************************************************************/
+
+static void check_buffer_low(struct tms5220 *tms)
+{
+ /* did we just become low? */
+ if (tms->fifo_count <= 8)
+ {
+ /* generate an interrupt if necessary */
+ if (!tms->buffer_low)
+ set_interrupt_state(tms, 1);
+ tms->buffer_low = 1;
+
+ if (DEBUG_5220) logerror("Buffer low set\n");
+ }
+
+ /* did we just become full? */
+ else
+ {
+ tms->buffer_low = 0;
+
+ if (DEBUG_5220) logerror("Buffer low cleared\n");
+ }
+}
+
+
+
+/**********************************************************************************************
+
+ set_interrupt_state -- generate an interrupt
+
+***********************************************************************************************/
+
+static void set_interrupt_state(struct tms5220 *tms, int state)
+{
+ if (tms->irq_func && state != tms->irq_pin)
+ tms->irq_func(state);
+ tms->irq_pin = state;
+}