diff options
author | 2013-05-14 10:21:05 +0000 | |
---|---|---|
committer | 2013-05-14 10:21:05 +0000 | |
commit | d700c1f372a451a08cd7737be70372efdbdb1fa8 (patch) | |
tree | eecbe8dfb684f585736c17c902e9b84cb00abef5 /src/emu | |
parent | 25cb3c0cdaaaffeee62bd8e5528a9c9ecfc48219 (diff) |
modernised TMS5220 [smf]
Diffstat (limited to 'src/emu')
-rw-r--r-- | src/emu/machine/spchrom.c | 115 | ||||
-rw-r--r-- | src/emu/machine/spchrom.h | 37 | ||||
-rw-r--r-- | src/emu/sound/sound.mak | 2 | ||||
-rw-r--r-- | src/emu/sound/tms5220.c | 2575 | ||||
-rw-r--r-- | src/emu/sound/tms5220.h | 404 |
5 files changed, 829 insertions, 2304 deletions
diff --git a/src/emu/machine/spchrom.c b/src/emu/machine/spchrom.c new file mode 100644 index 00000000000..27f1960e172 --- /dev/null +++ b/src/emu/machine/spchrom.c @@ -0,0 +1,115 @@ +/* + spchroms.c - This is an emulator for "typical" speech ROMs from TI, as used by TI99/4(a). + + In order to support its speech processor, TI designed some ROMs with a 1-bit data bus + and 4-bit address bus (multiplexed 5 times to provide a 18-bit address). + A fairly complete description of such a ROM (tms6100) is found in the tms5220 datasheet. + + One notable thing is that the address is a byte address (*NOT* a bit address). + + This file is designed to be interfaced with the tms5220 core. + Interfacing it with the tms5110 would make sense, too. +*/ + +#include "emu.h" +#include "spchrom.h" + +#define TMS5220_ADDRESS_MASK 0x3FFFFUL /* 18-bit mask for tms5220 address */ + +// device type definition +const device_type SPEECHROM = &device_creator<speechrom_device>; + +speechrom_device::speechrom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : device_t(mconfig, SPEECHROM, "SPEECHROM", tag, owner, clock), + m_speechROMaddr(0), + m_load_pointer(0), + m_ROM_bits_count(0) +{ +} + +/* + Read 'count' bits serially from speech ROM +*/ +int speechrom_device::read(int count) +{ + int val; + + if (m_load_pointer) + { /* first read after load address is ignored */ + m_load_pointer = 0; + count--; + } + + if (m_speechROMaddr < m_speechROMlen) + if (count < m_ROM_bits_count) + { + m_ROM_bits_count -= count; + val = (m_speechrom_data[m_speechROMaddr] >> m_ROM_bits_count) & (0xFF >> (8 - count)); + } + else + { + val = ((int) m_speechrom_data[m_speechROMaddr]) << 8; + + m_speechROMaddr = (m_speechROMaddr + 1) & TMS5220_ADDRESS_MASK; + + if (m_speechROMaddr < m_speechROMlen) + val |= m_speechrom_data[m_speechROMaddr]; + + m_ROM_bits_count += 8 - count; + + val = (val >> m_ROM_bits_count) & (0xFF >> (8 - count)); + } + else + val = 0; + + return val; +} + +/* + Write an address nibble to speech ROM +*/ +void speechrom_device::load_address(int data) +{ + /* tms5220 data sheet says that if we load only one 4-bit nibble, it won't work. + This code does not care about this. */ + m_speechROMaddr = ( (m_speechROMaddr & ~(0xf << m_load_pointer)) + | (((unsigned long) (data & 0xf)) << m_load_pointer) ) & TMS5220_ADDRESS_MASK; + m_load_pointer += 4; + m_ROM_bits_count = 8; +} + +/* + Perform a read and branch command +*/ +void speechrom_device::read_and_branch() +{ + /* tms5220 data sheet says that if more than one speech ROM (tms6100) is present, + there is a bus contention. This code does not care about this. */ + if (m_speechROMaddr < m_speechROMlen-1) + m_speechROMaddr = (m_speechROMaddr & 0x3c000UL) + | (((((unsigned long) m_speechrom_data[m_speechROMaddr]) << 8) + | m_speechrom_data[m_speechROMaddr+1]) & 0x3fffUL); + else if (m_speechROMaddr == m_speechROMlen-1) + m_speechROMaddr = (m_speechROMaddr & 0x3c000UL) + | ((((unsigned long) m_speechrom_data[m_speechROMaddr]) << 8) & 0x3fffUL); + else + m_speechROMaddr = (m_speechROMaddr & 0x3c000UL); + + m_ROM_bits_count = 8; +} + +void speechrom_device::device_start() +{ + memory_region *region = memregion(tag()); + if (region == NULL) + { + throw emu_fatalerror("No region for device '%s'\n", tag()); + } + + m_speechrom_data = region->base(); + m_speechROMlen = region->bytes(); + + save_item(NAME(m_speechROMaddr)); + save_item(NAME(m_load_pointer)); + save_item(NAME(m_ROM_bits_count)); +} diff --git a/src/emu/machine/spchrom.h b/src/emu/machine/spchrom.h new file mode 100644 index 00000000000..c9401379e19 --- /dev/null +++ b/src/emu/machine/spchrom.h @@ -0,0 +1,37 @@ +/* + * Voice Synthesis Memory + * + */ + +#ifndef __SPCHROMS_H +#define __SPCHROMS_H + +#include "emu.h" + +class speechrom_device : public device_t +{ +public: + // construction/destruction + speechrom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + /// TODO: implement bus behaviour + int read(int count); + void load_address(int data); + void read_and_branch(); + + // device-level overrides + virtual void device_start(); + +private: + UINT8 *m_speechrom_data; /* pointer to speech ROM data */ + unsigned long m_speechROMlen; /* length of data pointed by speechrom_data, from 0 to 2^18 */ + unsigned long m_speechROMaddr; /* 18 bit pointer in ROM */ + int m_load_pointer; /* which 4-bit nibble will be affected by load address */ + int m_ROM_bits_count; /* current bit position in ROM */ +}; + + +// device type definition +extern const device_type SPEECHROM; + +#endif diff --git a/src/emu/sound/sound.mak b/src/emu/sound/sound.mak index f162cac0444..e60a5fa9842 100644 --- a/src/emu/sound/sound.mak +++ b/src/emu/sound/sound.mak @@ -644,7 +644,7 @@ $(SOUNDOBJ)/tms5110.o: $(SOUNDSRC)/tms5110r.c # Texas Instruments TMS5200-series speech synthesizers #------------------------------------------------- ifneq ($(filter TMS5220,$(SOUNDS)),) -SOUNDOBJS += $(SOUNDOBJ)/tms5220.o +SOUNDOBJS += $(SOUNDOBJ)/tms5220.o $(EMUMACHINE)/spchrom.o endif $(SOUNDOBJ)/tms5220.o: $(SOUNDSRC)/tms5110r.c diff --git a/src/emu/sound/tms5220.c b/src/emu/sound/tms5220.c index 68e6c611f41..03123d28a5e 100644 --- a/src/emu/sound/tms5220.c +++ b/src/emu/sound/tms5220.c @@ -251,6 +251,8 @@ in MCU code). Look for a 16-pin chip at U6 labeled "ECHO-3 SN". #include "emu.h" #include "tms5220.h" +static INT16 clip_analog(INT16 cliptemp); + /* *****optional defines***** */ /* Hacky improvements which don't match patent: */ @@ -258,8 +260,8 @@ in MCU code). Look for a 16-pin chip at U6 labeled "ECHO-3 SN". * One of the following two lines should be used, and the other commented * The second line is more accurate mathematically but not accurate to the patent */ -#define INTERP_SHIFT >> tms->coeff->interp_coeff[tms->IP] -//define INTERP_SHIFT / (1<<tms->coeff->interp_coeff[tms->IP]) +#define INTERP_SHIFT >> m_coeff->interp_coeff[m_IP] +//define INTERP_SHIFT / (1<<m_coeff->interp_coeff[m_IP]) /* Other hacks */ /* HACK?: if defined, outputs the low 4 bits of the lattice filter to the i/o @@ -267,9 +269,6 @@ in MCU code). Look for a 16-pin chip at U6 labeled "ECHO-3 SN". * ...actually the tms5220c might legitamately do this! */ #undef ALLOW_4_LSB -/* HACK: if defined, uses impossibly perfect 'straight line' interpolation */ -#undef PERFECT_INTERPOLATION_HACK - /* *****configuration of chip connection stuff***** */ /* must be defined; if 0, output the waveform as if it was tapped on the speaker pin as usual, if 1, output the waveform as if it was tapped on the i/o pin (volume is much lower in the latter case) */ @@ -323,222 +322,85 @@ in MCU code). Look for a 16-pin chip at U6 labeled "ECHO-3 SN". static const UINT8 reload_table[4] = { 0, 2, 4, 6 }; //sample count reload for 5220c only; 5200 and 5220 always reload with 0; keep in mind this is loaded on IP=0 PC=12 subcycle=1 so it immediately will increment after one sample, effectively being 1,3,5,7 as in the comments above. -struct tms5220_state -{ - /* coefficient tables */ - int variant; /* Variant of the 5xxx - see tms5110r.h */ - - /* coefficient tables */ - const struct tms5100_coeffs *coeff; - - /* callbacks */ - devcb_resolved_write_line irq_func; - devcb_resolved_write_line readyq_func; - - /* these contain data that describes the 128-bit data FIFO */ - UINT8 fifo[FIFO_SIZE]; - UINT8 fifo_head; - UINT8 fifo_tail; - UINT8 fifo_count; - UINT8 fifo_bits_taken; - - - /* these contain global status bits */ - UINT8 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 speak_external; /* If 1, DDIS is 1, i.e. Speak External command in progress, writes go to FIFO. */ - UINT8 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 buffer_low; /* If 1, FIFO has less than 8 bytes in it */ - UINT8 buffer_empty; /* If 1, FIFO is empty */ - UINT8 irq_pin; /* state of the IRQ pin (output) */ - UINT8 ready_pin; /* state of the READY pin (output) */ - - /* these contain data describing the current and previous voice frames */ -#define OLD_FRAME_SILENCE_FLAG tms->OLDE // 1 if E=0, 0 otherwise. -#define OLD_FRAME_UNVOICED_FLAG tms->OLDP // 1 if P=0 (unvoiced), 0 if voiced - UINT8 OLDE; - UINT8 OLDP; - -#define NEW_FRAME_STOP_FLAG (tms->new_frame_energy_idx == 0xF) // 1 if this is a stop (Energy = 0xF) frame -#define NEW_FRAME_SILENCE_FLAG (tms->new_frame_energy_idx == 0) // ditto as above -#define NEW_FRAME_UNVOICED_FLAG (tms->new_frame_pitch_idx == 0) // ditto as above - UINT8 new_frame_energy_idx; - UINT8 new_frame_pitch_idx; - UINT8 new_frame_k_idx[10]; - - - /* these are all used to contain the current state of the sound generation */ -#ifndef PERFECT_INTERPOLATION_HACK - INT16 current_energy; - INT16 current_pitch; - INT16 current_k[10]; - - INT16 target_energy; - INT16 target_pitch; - INT16 target_k[10]; -#else - UINT8 old_frame_energy_idx; - UINT8 old_frame_pitch_idx; - UINT8 old_frame_k_idx[10]; - - INT32 current_energy; - INT32 current_pitch; - INT32 current_k[10]; - - INT32 target_energy; - INT32 target_pitch; - INT32 target_k[10]; -#endif - - UINT16 previous_energy; /* needed for lattice filter to match patent */ - - UINT8 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 subc_reload; /* contains 1 for normal speech, 0 when SPKSLOW is active */ - UINT8 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 */ - UINT8 IP; /* the current interpolation period */ - UINT8 inhibit; /* If 1, interpolation is inhibited until the DIV1 period */ - UINT8 tms5220c_rate; /* only relevant for tms5220C's multi frame rate feature; is the actual 4 bit value written on a 0x2* or 0x0* command */ - UINT16 pitch_count; /* pitch counter; provides chirp rom address */ - - INT32 u[11]; - INT32 x[10]; - - UINT16 RNG; /* the random noise generator configuration is: 1 + x + x^3 + x^4 + x^13 */ - INT16 excitation_data; - - /* R Nabet : These have been added to emulate speech Roms */ - 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 */ - - /* io_ready: page 3 of the datasheet specifies that READY will be asserted until - * data is available or processed by the system. - */ - UINT8 io_ready; - - /* flag for "true" timing involving rs/ws */ - UINT8 true_timing; - - /* rsws - state, rs bit 1, ws bit 0 */ - UINT8 rs_ws; - UINT8 read_latch; - UINT8 write_latch; - - /* 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, and has the - funny clipping/clamping logic, while the digital pin gives full 10 bit - resolution of the output data. - TODO: add a way to set/reset this other than the FORCE_DIGITAL define - */ - UINT8 digital_select; - device_t *device; - - const tms5220_interface *intf; - sound_stream *stream; - int clock; -}; - - // Pull in the ROM tables #include "tms5110r.c" -INLINE tms5220_state *get_safe_token(device_t *device) -{ - assert(device != NULL); - assert(device->type() == TMS5220 || - device->type() == TMS5220C || - device->type() == TMC0285 || - device->type() == TMS5200); - return (tms5220_state *)downcast<tms5220_device *>(device)->token(); -} - -/* Static function prototypes */ -static void process_command(tms5220_state *tms, unsigned char data); -static void parse_frame(tms5220_state *tms); -static void update_status_and_ints(tms5220_state *tms); -static void set_interrupt_state(tms5220_state *tms, int state); -static INT32 lattice_filter(tms5220_state *tms); -static INT16 clip_analog(INT16 clip); -static void update_ready_state(tms5220_state *tms); -static STREAM_UPDATE( tms5220_update ); - -static void tms5220_set_variant(tms5220_state *tms, int variant) +void tms5220_device::set_variant(int variant) { switch (variant) { case TMS5220_IS_5200: - tms->coeff = &tms5200_coeff; + m_coeff = &tms5200_coeff; break; case TMS5220_IS_5220C: case TMS5220_IS_5220: - tms->coeff = &tms5220_coeff; + m_coeff = &tms5220_coeff; break; default: fatalerror("Unknown variant in tms5220_set_variant\n"); } - tms->variant = variant; + m_variant = variant; } -static void register_for_save_states(tms5220_state *tms) +void tms5220_device::register_for_save_states() { - tms->device->save_item(NAME(tms->fifo)); - tms->device->save_item(NAME(tms->fifo_head)); - tms->device->save_item(NAME(tms->fifo_tail)); - tms->device->save_item(NAME(tms->fifo_count)); - tms->device->save_item(NAME(tms->fifo_bits_taken)); - - tms->device->save_item(NAME(tms->speaking_now)); - tms->device->save_item(NAME(tms->speak_external)); - tms->device->save_item(NAME(tms->talk_status)); - tms->device->save_item(NAME(tms->buffer_low)); - tms->device->save_item(NAME(tms->buffer_empty)); - tms->device->save_item(NAME(tms->irq_pin)); - tms->device->save_item(NAME(tms->ready_pin)); - - tms->device->save_item(NAME(tms->OLDE)); - tms->device->save_item(NAME(tms->OLDP)); - - tms->device->save_item(NAME(tms->new_frame_energy_idx)); - tms->device->save_item(NAME(tms->new_frame_pitch_idx)); - tms->device->save_item(NAME(tms->new_frame_k_idx)); + save_item(NAME(m_fifo)); + save_item(NAME(m_fifo_head)); + save_item(NAME(m_fifo_tail)); + 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_buffer_low)); + save_item(NAME(m_buffer_empty)); + save_item(NAME(m_irq_pin)); + save_item(NAME(m_ready_pin)); + + save_item(NAME(m_OLDE)); + save_item(NAME(m_OLDP)); + + save_item(NAME(m_new_frame_energy_idx)); + save_item(NAME(m_new_frame_pitch_idx)); + save_item(NAME(m_new_frame_k_idx)); #ifdef PERFECT_INTERPOLATION_HACK - tms->device->save_item(NAME(tms->old_frame_energy_idx)); - tms->device->save_item(NAME(tms->old_frame_pitch_idx)); - tms->device->save_item(NAME(tms->old_frame_k_idx)); + save_item(NAME(m_old_frame_energy_idx)); + save_item(NAME(m_old_frame_pitch_idx)); + save_item(NAME(m_old_frame_k_idx)); #endif - tms->device->save_item(NAME(tms->current_energy)); - tms->device->save_item(NAME(tms->current_pitch)); - tms->device->save_item(NAME(tms->current_k)); + save_item(NAME(m_current_energy)); + save_item(NAME(m_current_pitch)); + save_item(NAME(m_current_k)); - tms->device->save_item(NAME(tms->target_energy)); - tms->device->save_item(NAME(tms->target_pitch)); - tms->device->save_item(NAME(tms->target_k)); + save_item(NAME(m_target_energy)); + save_item(NAME(m_target_pitch)); + save_item(NAME(m_target_k)); - tms->device->save_item(NAME(tms->previous_energy)); + save_item(NAME(m_previous_energy)); - tms->device->save_item(NAME(tms->subcycle)); - tms->device->save_item(NAME(tms->subc_reload)); - tms->device->save_item(NAME(tms->PC)); - tms->device->save_item(NAME(tms->IP)); - tms->device->save_item(NAME(tms->inhibit)); - tms->device->save_item(NAME(tms->tms5220c_rate)); - tms->device->save_item(NAME(tms->pitch_count)); + save_item(NAME(m_subcycle)); + save_item(NAME(m_subc_reload)); + save_item(NAME(m_PC)); + save_item(NAME(m_IP)); + save_item(NAME(m_inhibit)); + save_item(NAME(m_tms5220c_rate)); + save_item(NAME(m_pitch_count)); - tms->device->save_item(NAME(tms->u)); - tms->device->save_item(NAME(tms->x)); + save_item(NAME(m_u)); + save_item(NAME(m_x)); - tms->device->save_item(NAME(tms->RNG)); - tms->device->save_item(NAME(tms->excitation_data)); + save_item(NAME(m_RNG)); + save_item(NAME(m_excitation_data)); - tms->device->save_item(NAME(tms->schedule_dummy_read)); - tms->device->save_item(NAME(tms->data_register)); - tms->device->save_item(NAME(tms->RDB_flag)); - tms->device->save_item(NAME(tms->digital_select)); + save_item(NAME(m_schedule_dummy_read)); + save_item(NAME(m_data_register)); + save_item(NAME(m_RDB_flag)); + save_item(NAME(m_digital_select)); - tms->device->save_item(NAME(tms->io_ready)); + save_item(NAME(m_io_ready)); } @@ -586,24 +448,24 @@ static void printbits(long data, int num) ***********************************************************************************************/ -static void tms5220_data_write(tms5220_state *tms, int data) +void tms5220_device::data_write(int data) { #ifdef DEBUG_DUMP_INPUT_DATA fprintf(stdout, "%c",data); #endif - if (tms->speak_external) // If we're in speak external mode + if (m_speak_external) // If we're in speak external mode { // add this byte to the FIFO - if (tms->fifo_count < FIFO_SIZE) + if (m_fifo_count < FIFO_SIZE) { - tms->fifo[tms->fifo_tail] = data; - tms->fifo_tail = (tms->fifo_tail + 1) % FIFO_SIZE; - tms->fifo_count++; + 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", tms->fifo_count); + logerror("data_write: Added byte to FIFO (current count=%2d)\n", m_fifo_count); #endif - update_status_and_ints(tms); - if ((tms->talk_status == 0) && (tms->buffer_low == 0)) // we just unset buffer low with that last write, and talk status *was* zero... + update_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... { int i; #ifdef DEBUG_FIFO @@ -611,18 +473,18 @@ static void tms5220_data_write(tms5220_state *tms, int data) #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 - tms->subcycle = tms->subc_reload; - tms->PC = 0; - tms->IP = reload_table[tms->tms5220c_rate&0x3]; // is this correct? should this be always 7 instead, so that the new frame is loaded quickly? - tms->new_frame_energy_idx = 0; - tms->new_frame_pitch_idx = 0; + m_subcycle = m_subc_reload; + m_PC = 0; + m_IP = reload_table[m_tms5220c_rate&0x3]; // is this correct? should this be always 7 instead, so that the new frame is loaded quickly? + m_new_frame_energy_idx = 0; + m_new_frame_pitch_idx = 0; for (i = 0; i < 4; i++) - tms->new_frame_k_idx[i] = 0; + m_new_frame_k_idx[i] = 0; for (i = 4; i < 7; i++) - tms->new_frame_k_idx[i] = 0xF; - for (i = 7; i < tms->coeff->num_k; i++) - tms->new_frame_k_idx[i] = 0x7; - tms->talk_status = tms->speaking_now = 1; + 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 @@ -635,9 +497,9 @@ static void tms5220_data_write(tms5220_state *tms, int data) } - else //(! tms->speak_external) + else //(! m_speak_external) // R Nabet : we parse commands at once. It is necessary for such commands as read. - process_command(tms,data); + process_command(data); } /********************************************************************************************** @@ -663,47 +525,47 @@ static void tms5220_data_write(tms5220_state *tms, int data) ***********************************************************************************************/ -static void update_status_and_ints(tms5220_state *tms) +void tms5220_device::update_status_and_ints() { /* update flags and set ints if needed */ - update_ready_state(tms); + update_ready_state(); /* BL is set if neither byte 9 nor 8 of the fifo are in use; this translates to having fifo_count (which ranges from 0 bytes in use to 16 bytes used) being less than or equal to 8. Victory/Victorba depends on this. */ - if (tms->fifo_count <= 8) + if (m_fifo_count <= 8) { // generate an interrupt if necessary; if /BL was inactive and is now active, set int. - if (!tms->buffer_low) - set_interrupt_state(tms, 1); - tms->buffer_low = 1; + if (!m_buffer_low) + set_interrupt_state(1); + m_buffer_low = 1; } else - tms->buffer_low = 0; + 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 */ - if (tms->fifo_count == 0) + if (m_fifo_count == 0) { // generate an interrupt if necessary; if /BE was inactive and is now active, set int. - if (!tms->buffer_empty) - set_interrupt_state(tms, 1); - tms->buffer_empty = 1; + if (!m_buffer_empty) + set_interrupt_state(1); + m_buffer_empty = 1; } else - tms->buffer_empty = 0; + 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 ((tms->speak_external == 1) && (tms->buffer_empty == 1)) + if ((m_speak_external == 1) && (m_buffer_empty == 1)) { // generate an interrupt: /TS was active, and is now inactive. - if (tms->talk_status == 1) + if (m_talk_status == 1) { - tms->talk_status = tms->speak_external = 0; - set_interrupt_state(tms, 1); + m_talk_status = m_speak_external = 0; + set_interrupt_state(1); } } /* Note that TS being unset will also generate an interrupt when a STOP @@ -716,32 +578,32 @@ static void update_status_and_ints(tms5220_state *tms) ***********************************************************************************************/ -static int extract_bits(tms5220_state *tms, int count) +int tms5220_device::extract_bits(int count) { int val = 0; - if (tms->speak_external) + if (m_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) + val = (val << 1) | ((m_fifo[m_fifo_head] >> m_fifo_bits_taken) & 1); + m_fifo_bits_taken++; + if (m_fifo_bits_taken >= 8) { - tms->fifo_count--; - tms->fifo[tms->fifo_head] = 0; // zero the newly depleted fifo head byte - tms->fifo_head = (tms->fifo_head + 1) % FIFO_SIZE; - tms->fifo_bits_taken = 0; - update_status_and_ints(tms); + m_fifo_count--; + m_fifo[m_fifo_head] = 0; // zero the newly depleted fifo head byte + m_fifo_head = (m_fifo_head + 1) % FIFO_SIZE; + m_fifo_bits_taken = 0; + update_status_and_ints(); } } } else { // extract from VSM (speech ROM) - if (tms->intf->read) - val = (* tms->intf->read)(tms->device, count); + if (m_speechrom) + val = m_speechrom->read(count); } return val; @@ -753,23 +615,23 @@ static int extract_bits(tms5220_state *tms, int count) ***********************************************************************************************/ -static int tms5220_status_read(tms5220_state *tms) +int tms5220_device::status_read() { - if (tms->RDB_flag) + if (m_RDB_flag) { /* if last command was read, return data register */ - tms->RDB_flag = FALSE; - return(tms->data_register); + m_RDB_flag = FALSE; + return(m_data_register); } else { /* read status */ /* clear the interrupt pin on status read */ - set_interrupt_state(tms, 0); + set_interrupt_state(0); #ifdef DEBUG_PIN_READS - logerror("Status read: TS=%d BL=%d BE=%d\n", tms->talk_status, tms->buffer_low, tms->buffer_empty); + logerror("Status read: TS=%d BL=%d BE=%d\n", m_talk_status, m_buffer_low, m_buffer_empty); #endif - return (tms->talk_status << 7) | (tms->buffer_low << 6) | (tms->buffer_empty << 5); + return (m_talk_status << 7) | (m_buffer_low << 6) | (m_buffer_empty << 5); } } @@ -780,12 +642,12 @@ static int tms5220_status_read(tms5220_state *tms) ***********************************************************************************************/ -static int tms5220_ready_read(tms5220_state *tms) +int tms5220_device::ready_read() { #ifdef DEBUG_PIN_READS - logerror("ready_read: ready pin read, io_ready is %d, fifo count is %d\n", tms->io_ready, tms->fifo_count); + logerror("ready_read: ready pin read, io_ready is %d, fifo count is %d\n", m_io_ready, m_fifo_count); #endif - return ((tms->fifo_count < FIFO_SIZE)||(!tms->speak_external)) && tms->io_ready; + return ((m_fifo_count < FIFO_SIZE)||(!m_speak_external)) && m_io_ready; } @@ -797,31 +659,31 @@ static int tms5220_ready_read(tms5220_state *tms) ***********************************************************************************************/ -static int tms5220_cycles_to_ready(tms5220_state *tms) +int tms5220_device::cycles_to_ready() { int answer; - if (tms5220_ready_read(tms)) + if (ready_read()) answer = 0; else { int val; - int samples_per_frame = tms->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 = ((tms->PC*(3-tms->subc_reload))+((tms->subc_reload?38:25)*tms->IP)); + 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_PC*(3-m_subc_reload))+((m_subc_reload?38:25)*m_IP)); answer = samples_per_frame-current_sample+8; - // total number of bits available in current byte is (8 - tms->fifo_bits_taken) + // total number of bits available in current byte is (8 - m_fifo_bits_taken) // if more than 4 are available, we need to check the energy - if (tms->fifo_bits_taken < 4) + if (m_fifo_bits_taken < 4) { // read energy - val = (tms->fifo[tms->fifo_head] >> tms->fifo_bits_taken) & 0xf; + val = (m_fifo[m_fifo_head] >> m_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 += tms->subc_reload?200:304; + answer += m_subc_reload?200:304; /* 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. */ @@ -838,12 +700,12 @@ static int tms5220_cycles_to_ready(tms5220_state *tms) ***********************************************************************************************/ -static int tms5220_int_read(tms5220_state *tms) +int tms5220_device::int_read() { #ifdef DEBUG_PIN_READS - logerror("int_read: irq pin read, state is %d\n", tms->irq_pin); + logerror("int_read: irq pin read, state is %d\n", m_irq_pin); #endif - return tms->irq_pin; + return m_irq_pin; } @@ -853,7 +715,7 @@ static int tms5220_int_read(tms5220_state *tms) ***********************************************************************************************/ -static void tms5220_process(tms5220_state *tms, INT16 *buffer, unsigned int size) +void tms5220_device::process(INT16 *buffer, unsigned int size) { int buf_count=0; int i, bitout, zpar; @@ -861,61 +723,61 @@ static void tms5220_process(tms5220_state *tms, INT16 *buffer, unsigned int size /* the following gotos are probably safe to remove */ /* if we're empty and still not speaking, fill with nothingness */ - if (!tms->speaking_now) + if (!m_speaking_now) goto empty; /* if speak external is set, but talk status is not (yet) set, wait for buffer low to clear */ - if (!tms->talk_status && tms->speak_external && tms->buffer_low) + if (!m_talk_status && m_speak_external && m_buffer_low) goto empty; /* loop until the buffer is full or we've stopped speaking */ - while ((size > 0) && tms->speaking_now) + while ((size > 0) && m_speaking_now) { /* if it is the appropriate time to update the old energy/pitch idxes, * 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.*/ - if ((tms->IP == 0) && (tms->PC == 0) && (tms->subcycle < 2)) + if ((m_IP == 0) && (m_PC == 0) && (m_subcycle < 2)) { - tms->OLDE = (tms->new_frame_energy_idx == 0); - tms->OLDP = (tms->new_frame_pitch_idx == 0); + 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 * PC=x time period, but it doesn't affect anything until IP=0 PC=12 happens) */ - if ((tms->IP == 0) && (tms->PC == 12) && (tms->subcycle == 1)) + if ((m_IP == 0) && (m_PC == 12) && (m_subcycle == 1)) { // HACK for regression testing, be sure to comment out before release! - //tms->RNG = 0x1234; + //m_RNG = 0x1234; // end HACK /* appropriately override the interp count if needed; this will be incremented after the frame parse! */ - tms->IP = reload_table[tms->tms5220c_rate&0x3]; + m_IP = reload_table[m_tms5220c_rate&0x3]; #ifdef PERFECT_INTERPOLATION_HACK /* remember previous frame energy, pitch, and coefficients */ - tms->old_frame_energy_idx = tms->new_frame_energy_idx; - tms->old_frame_pitch_idx = tms->new_frame_pitch_idx; - for (i = 0; i < tms->coeff->num_k; i++) - tms->old_frame_k_idx[i] = tms->new_frame_k_idx[i]; + m_old_frame_energy_idx = m_new_frame_energy_idx; + m_old_frame_pitch_idx = m_new_frame_pitch_idx; + for (i = 0; i < m_coeff->num_k; i++) + 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 (tms->talk_status == 0) + 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 - tms->speaking_now = 0; // finally halt speech + 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(tms); + parse_frame(); #ifdef DEBUG_PARSE_FRAME_DUMP fprintf(stderr,"\n"); #endif @@ -923,9 +785,9 @@ static void tms5220_process(tms5220_state *tms, INT16 *buffer, unsigned int size /* if the new frame is a stop frame, set an interrupt and set talk status to 0 */ if (NEW_FRAME_STOP_FLAG == 1) { - tms->talk_status = tms->speak_external = 0; - set_interrupt_state(tms, 1); - update_status_and_ints(tms); + m_talk_status = m_speak_external = 0; + set_interrupt_state(1); + update_status_and_ints(); } /* in all cases where interpolation would be inhibited, set the inhibit flag; otherwise clear it. @@ -937,87 +799,87 @@ static void tms5220_process(tms5220_state *tms, INT16 *buffer, unsigned int size if ( ((OLD_FRAME_UNVOICED_FLAG == 0) && (NEW_FRAME_UNVOICED_FLAG == 1)) || ((OLD_FRAME_UNVOICED_FLAG == 1) && (NEW_FRAME_UNVOICED_FLAG == 0)) /* this line needs further investigation, starwars tie fighters may sound better without it */ || ((OLD_FRAME_SILENCE_FLAG == 1) && (NEW_FRAME_SILENCE_FLAG == 0)) ) - tms->inhibit = 1; + m_inhibit = 1; else // normal frame, normal interpolation - tms->inhibit = 0; + m_inhibit = 0; /* load new frame targets from tables, using parsed indices */ - tms->target_energy = tms->coeff->energytable[tms->new_frame_energy_idx]; - tms->target_pitch = tms->coeff->pitchtable[tms->new_frame_pitch_idx]; + 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++) - tms->target_k[i] = tms->coeff->ktable[i][tms->new_frame_k_idx[i]]; - for (i = 4; i < tms->coeff->num_k; i++) - tms->target_k[i] = (tms->coeff->ktable[i][tms->new_frame_k_idx[i]] * (1-zpar)); + 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; ", tms->OLDE, tms->OLDP); + fprintf(stderr, "OLDE: %d; OLDP: %d; ", m_OLDE, m_OLDP); fprintf(stderr,"Processing frame: "); - if (tms->inhibit == 0) + 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",tms->current_energy, tms->current_pitch, tms->current_k[0], tms->current_k[1], tms->current_k[2], tms->current_k[3], tms->current_k[4], tms->current_k[5], tms->current_k[6], tms->current_k[7], tms->current_k[8], tms->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",tms->target_energy, tms->new_frame_energy_idx, tms->target_pitch, tms->target_k[0], tms->target_k[1], tms->target_k[2], tms->target_k[3], tms->target_k[4], tms->target_k[5], tms->target_k[6], tms->target_k[7], tms->target_k[8], tms->target_k[9]); + fprintf(stderr,"*** current Energy, Pitch and Ks = %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d\n",m_current_energy, m_current_pitch, m_current_k[0], m_current_k[1], m_current_k[2], m_current_k[3], m_current_k[4], m_current_k[5], m_current_k[6], m_current_k[7], m_current_k[8], m_current_k[9]); + fprintf(stderr,"*** target Energy(idx), Pitch, and Ks = %04d(%x),%04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d, %04d\n",m_target_energy, m_new_frame_energy_idx, m_target_pitch, m_target_k[0], m_target_k[1], m_target_k[2], m_target_k[3], m_target_k[4], m_target_k[5], m_target_k[6], m_target_k[7], m_target_k[8], m_target_k[9]); #endif /* if TS is now 0, ramp the energy down to 0. Is this really correct to hardware? */ - if (tms->talk_status == 0) + if (m_talk_status == 0) { #ifdef DEBUG_GENERATION fprintf(stderr,"Talk status is 0, forcing target energy to 0\n"); #endif - tms->target_energy = 0; + m_target_energy = 0; } } else // Not a new frame, just interpolate the existing frame. { - int inhibit_state = ((tms->inhibit==1)&&(tms->IP != 0)); // disable inhibit when reaching the last interp period, but don't overwrite the tms->inhibit value + int inhibit_state = ((m_inhibit==1)&&(m_IP != 0)); // disable inhibit when reaching the last interp period, but don't overwrite the m_inhibit value #ifdef PERFECT_INTERPOLATION_HACK - int samples_per_frame = tms->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 = tms->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 = (tms->subcycle - tms->subc_reload)+(tms->PC*(3-tms->subc_reload))+((tms->subc_reload?25:38)*((tms->IP-1)&7)); + 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 - tms->current_energy = tms->coeff->energytable[tms->old_frame_energy_idx]; - tms->current_pitch = tms->coeff->pitchtable[tms->old_frame_pitch_idx]; + 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++) - tms->current_k[i] = tms->coeff->ktable[i][tms->old_frame_k_idx[i]]; - for (i = 4; i < tms->coeff->num_k; i++) - tms->current_k[i] = (tms->coeff->ktable[i][tms->old_frame_k_idx[i]] * (1-zpar)); + 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)); // now adjust each value to be exactly correct for each of the samples per frame - if (tms->IP != 0) // if we're still interpolating... + if (m_IP != 0) // if we're still interpolating... { - tms->current_energy += (((tms->target_energy - tms->current_energy)*(1-inhibit_state))*current_sample)/samples_per_frame; - tms->current_pitch += (((tms->target_pitch - tms->current_pitch)*(1-inhibit_state))*current_sample)/samples_per_frame; - for (i = 0; i < tms->coeff->num_k; i++) - tms->current_k[i] += (((tms->target_k[i] - tms->current_k[i])*(1-inhibit_state))*current_sample)/samples_per_frame; + 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; + 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; } else // we're done, play this frame for 1/8 frame. { - tms->current_energy = tms->target_energy; - tms->current_pitch = tms->target_pitch; - for (i = 0; i < tms->coeff->num_k; i++) - tms->current_k[i] = tms->target_k[i]; + m_current_energy = m_target_energy; + m_current_pitch = m_target_pitch; + for (i = 0; i < m_coeff->num_k; i++) + m_current_k[i] = m_target_k[i]; } #else //Updates to parameters only happen on subcycle '2' (B cycle) of PCs. - if (tms->subcycle == 2) + if (m_subcycle == 2) { - switch(tms->PC) + switch(m_PC) { case 0: /* PC = 0, B cycle, write updated energy */ - tms->current_energy += (((tms->target_energy - tms->current_energy)*(1-inhibit_state)) INTERP_SHIFT); + m_current_energy += (((m_target_energy - m_current_energy)*(1-inhibit_state)) INTERP_SHIFT); break; case 1: /* PC = 1, B cycle, write updated pitch */ - tms->current_pitch += (((tms->target_pitch - tms->current_pitch)*(1-inhibit_state)) INTERP_SHIFT); + m_current_pitch += (((m_target_pitch - m_current_pitch)*(1-inhibit_state)) INTERP_SHIFT); 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 */ - tms->current_k[tms->PC-2] += (((tms->target_k[tms->PC-2] - tms->current_k[tms->PC-2])*(1-inhibit_state)) INTERP_SHIFT); + m_current_k[m_PC-2] += (((m_target_k[m_PC-2] - m_current_k[m_PC-2])*(1-inhibit_state)) INTERP_SHIFT); break; case 12: /* PC = 12, do nothing */ break; @@ -1030,10 +892,10 @@ static void tms5220_process(tms5220_state *tms, INT16 *buffer, unsigned int size if (OLD_FRAME_UNVOICED_FLAG == 1) { // generate unvoiced samples here - if (tms->RNG & 1) - tms->excitation_data = ~0x3F; /* according to the patent it is (either + or -) half of the maximum value in the chirp table, so either 01000000(0x40) or 11000000(0xC0)*/ + if (m_RNG & 1) + m_excitation_data = ~0x3F; /* according to the patent it is (either + or -) half of the maximum value in the chirp table, so either 01000000(0x40) or 11000000(0xC0)*/ else - tms->excitation_data = 0x40; + m_excitation_data = 0x40; } else /* (OLD_FRAME_UNVOICED_FLAG == 0) */ { @@ -1044,36 +906,36 @@ static void tms5220_process(tms5220_state *tms, INT16 *buffer, unsigned int size * and if the address reaches that point the ADDRESS incrementer is * disabled, forcing all samples beyond 51d to be == 51d */ - if (tms->pitch_count >= 51) - tms->excitation_data = tms->coeff->chirptable[51]; - else /*tms->pitch_count < 51*/ - tms->excitation_data = tms->coeff->chirptable[tms->pitch_count]; + if (m_pitch_count >= 51) + m_excitation_data = m_coeff->chirptable[51]; + else /*m_pitch_count < 51*/ + m_excitation_data = m_coeff->chirptable[m_pitch_count]; } // Update LFSR *20* times every sample (once per T cycle), like patent shows for (i=0; i<20; i++) { - bitout = ((tms->RNG >> 12) & 1) ^ - ((tms->RNG >> 3) & 1) ^ - ((tms->RNG >> 2) & 1) ^ - ((tms->RNG >> 0) & 1); - tms->RNG <<= 1; - tms->RNG |= bitout; + bitout = ((m_RNG >> 12) & 1) ^ + ((m_RNG >> 3) & 1) ^ + ((m_RNG >> 2) & 1) ^ + ((m_RNG >> 0) & 1); + m_RNG <<= 1; + m_RNG |= bitout; } - this_sample = lattice_filter(tms); /* execute lattice filter */ + this_sample = lattice_filter(); /* execute lattice filter */ #ifdef DEBUG_GENERATION_VERBOSE - //fprintf(stderr,"C:%01d; ",tms->subcycle); - fprintf(stderr,"IP:%01d PC:%02d X:%04d E:%03d P:%03d Pc:%03d ",tms->IP, tms->PC, tms->excitation_data, tms->current_energy, tms->current_pitch, tms->pitch_count); - //fprintf(stderr,"X:%04d E:%03d P:%03d Pc:%03d ", tms->excitation_data, tms->current_energy, tms->current_pitch, tms->pitch_count); + //fprintf(stderr,"C:%01d; ",m_subcycle); + fprintf(stderr,"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); + //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, tms->current_k[i]); + fprintf(stderr,"K%d:%04d ", i+1, m_current_k[i]); fprintf(stderr,"Out:%06d", this_sample); 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) */ while (this_sample > 16383) this_sample -= 32768; while (this_sample < -16384) this_sample += 32768; - if (tms->digital_select == 0) // analog SPK pin output is only 8 bits, with clipping + if (m_digital_select == 0) // analog SPK pin output is only 8 bits, with clipping buffer[buf_count] = clip_analog(this_sample); else // digital I/O pin output is 12 bits { @@ -1092,31 +954,31 @@ static void tms5220_process(tms5220_state *tms, INT16 *buffer, unsigned int size } // Update all counts - tms->subcycle++; - if ((tms->subcycle == 2) && (tms->PC == 12)) + m_subcycle++; + if ((m_subcycle == 2) && (m_PC == 12)) { /* Circuit 412 in the patent acts a reset, resetting the pitch counter to 0 * if INHIBIT was true during the most recent frame transition. * The exact time this occurs is betwen IP=7, PC=12 sub=0, T=t12 - * and tms->IP = 0, PC=0 sub=0, T=t12, a period of exactly 20 cycles, + * and m_IP = 0, PC=0 sub=0, T=t12, a period of exactly 20 cycles, * which overlaps the time OLDE and OLDP are updated at IP=7 PC=12 T17 * (and hence INHIBIT itself 2 t-cycles later). We do it here because it is * convenient and should make no difference in output. */ - if ((tms->IP == 7)&&(tms->inhibit==1)) tms->pitch_count = 0; - tms->subcycle = tms->subc_reload; - tms->PC = 0; - tms->IP++; - tms->IP&=0x7; + if ((m_IP == 7)&&(m_inhibit==1)) m_pitch_count = 0; + m_subcycle = m_subc_reload; + m_PC = 0; + m_IP++; + m_IP&=0x7; } - else if (tms->subcycle == 3) + else if (m_subcycle == 3) { - tms->subcycle = tms->subc_reload; - tms->PC++; + m_subcycle = m_subc_reload; + m_PC++; } - tms->pitch_count++; - if (tms->pitch_count >= tms->current_pitch) tms->pitch_count = 0; - tms->pitch_count &= 0x1FF; + m_pitch_count++; + if (m_pitch_count >= m_current_pitch) m_pitch_count = 0; + m_pitch_count &= 0x1FF; buf_count++; size--; } @@ -1125,18 +987,18 @@ empty: while (size > 0) { - tms->subcycle++; - if ((tms->subcycle == 2) && (tms->PC == 12)) + m_subcycle++; + if ((m_subcycle == 2) && (m_PC == 12)) { - tms->subcycle = tms->subc_reload; - tms->PC = 0; - tms->IP++; - tms->IP&=0x7; + m_subcycle = m_subc_reload; + m_PC = 0; + m_IP++; + m_IP&=0x7; } - else if (tms->subcycle == 3) + else if (m_subcycle == 3) { - tms->subcycle = tms->subc_reload; - tms->PC++; + m_subcycle = m_subc_reload; + m_PC++; } buffer[buf_count] = -1; /* should be just -1; actual chip outputs -1 every idle sample; (cf note in data sheet, p 10, table 4) */ buf_count++; @@ -1215,49 +1077,49 @@ static INT32 matrix_multiply(INT32 a, INT32 b) ***********************************************************************************************/ -static INT32 lattice_filter(tms5220_state *tms) +INT32 tms5220_device::lattice_filter() { // Lattice filter here // Aug/05/07: redone as unrolled loop, for clarity - LN /* Originally Copied verbatim from table I in US patent 4,209,804, now updated to be in same order as the actual chip does it, not that it matters. notation equivalencies from table: - Yn(i) == tms->u[n-1] - Kn = tms->current_k[n-1] - bn = tms->x[n-1] + Yn(i) == m_u[n-1] + Kn = m_current_k[n-1] + bn = m_x[n-1] */ - tms->u[10] = matrix_multiply(tms->previous_energy, (tms->excitation_data<<6)); //Y(11) - tms->u[9] = tms->u[10] - matrix_multiply(tms->current_k[9], tms->x[9]); - tms->u[8] = tms->u[9] - matrix_multiply(tms->current_k[8], tms->x[8]); - tms->u[7] = tms->u[8] - matrix_multiply(tms->current_k[7], tms->x[7]); - tms->u[6] = tms->u[7] - matrix_multiply(tms->current_k[6], tms->x[6]); - tms->u[5] = tms->u[6] - matrix_multiply(tms->current_k[5], tms->x[5]); - tms->u[4] = tms->u[5] - matrix_multiply(tms->current_k[4], tms->x[4]); - tms->u[3] = tms->u[4] - matrix_multiply(tms->current_k[3], tms->x[3]); - tms->u[2] = tms->u[3] - matrix_multiply(tms->current_k[2], tms->x[2]); - tms->u[1] = tms->u[2] - matrix_multiply(tms->current_k[1], tms->x[1]); - tms->u[0] = tms->u[1] - matrix_multiply(tms->current_k[0], tms->x[0]); - tms->x[9] = tms->x[8] + matrix_multiply(tms->current_k[8], tms->u[8]); - tms->x[8] = tms->x[7] + matrix_multiply(tms->current_k[7], tms->u[7]); - tms->x[7] = tms->x[6] + matrix_multiply(tms->current_k[6], tms->u[6]); - tms->x[6] = tms->x[5] + matrix_multiply(tms->current_k[5], tms->u[5]); - tms->x[5] = tms->x[4] + matrix_multiply(tms->current_k[4], tms->u[4]); - tms->x[4] = tms->x[3] + matrix_multiply(tms->current_k[3], tms->u[3]); - tms->x[3] = tms->x[2] + matrix_multiply(tms->current_k[2], tms->u[2]); - tms->x[2] = tms->x[1] + matrix_multiply(tms->current_k[1], tms->u[1]); - tms->x[1] = tms->x[0] + matrix_multiply(tms->current_k[0], tms->u[0]); - tms->x[0] = tms->u[0]; - tms->previous_energy = tms->current_energy; + m_u[10] = matrix_multiply(m_previous_energy, (m_excitation_data<<6)); //Y(11) + m_u[9] = m_u[10] - matrix_multiply(m_current_k[9], m_x[9]); + m_u[8] = m_u[9] - matrix_multiply(m_current_k[8], m_x[8]); + m_u[7] = m_u[8] - matrix_multiply(m_current_k[7], m_x[7]); + m_u[6] = m_u[7] - matrix_multiply(m_current_k[6], m_x[6]); + m_u[5] = m_u[6] - matrix_multiply(m_current_k[5], m_x[5]); + m_u[4] = m_u[5] - matrix_multiply(m_current_k[4], m_x[4]); + m_u[3] = m_u[4] - matrix_multiply(m_current_k[3], m_x[3]); + 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]); + 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]); + m_x[6] = m_x[5] + matrix_multiply(m_current_k[5], m_u[5]); + m_x[5] = m_x[4] + matrix_multiply(m_current_k[4], m_u[4]); + m_x[4] = m_x[3] + matrix_multiply(m_current_k[3], m_u[3]); + m_x[3] = m_x[2] + matrix_multiply(m_current_k[2], m_u[2]); + m_x[2] = m_x[1] + matrix_multiply(m_current_k[1], m_u[1]); + 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; - fprintf(stderr,"V:%04d ", tms->u[10]); + fprintf(stderr,"V:%04d ", m_u[10]); for (i = 9; i >= 0; i--) { - fprintf(stderr,"Y%d:%04d ", i+1, tms->u[i]); - fprintf(stderr,"b%d:%04d ", i+1, tms->x[i]); + fprintf(stderr,"Y%d:%04d ", i+1, m_u[i]); + fprintf(stderr,"b%d:%04d ", i+1, m_x[i]); if ((i % 5) == 0) fprintf(stderr,"\n"); } #endif - return tms->u[0]; + return m_u[0]; } @@ -1267,7 +1129,7 @@ static INT32 lattice_filter(tms5220_state *tms) ***********************************************************************************************/ -static void process_command(tms5220_state *tms, unsigned char cmd) +void tms5220_device::process_command(unsigned char cmd) { #ifdef DEBUG_COMMAND_DUMP fprintf(stderr,"process_command called with parameter %02X\n",cmd); @@ -1276,96 +1138,96 @@ static void process_command(tms5220_state *tms, unsigned char cmd) switch (cmd & 0x70) { case 0x10 : /* read byte */ - if (tms->talk_status == 0) /* TALKST must be clear for RDBY */ + if (m_talk_status == 0) /* TALKST must be clear for RDBY */ { - if (tms->schedule_dummy_read) + if (m_schedule_dummy_read) { - tms->schedule_dummy_read = FALSE; - if (tms->intf->read) - (*tms->intf->read)(tms->device, 1); + m_schedule_dummy_read = FALSE; + if (m_speechrom) + m_speechrom->read(1); } - if (tms->intf->read) - tms->data_register = (*tms->intf->read)(tms->device, 8); /* read one byte from speech ROM... */ - tms->RDB_flag = TRUE; + if (m_speechrom) + m_data_register = m_speechrom->read(8); /* read one byte from speech ROM... */ + m_RDB_flag = TRUE; } break; case 0x00: case 0x20: /* set rate (tms5220c only), otherwise NOP */ - if (tms->variant == TMS5220_IS_5220C) + if (m_variant == TMS5220_IS_5220C) { - tms->tms5220c_rate = cmd&0x0F; + m_tms5220c_rate = cmd&0x0F; } break; case 0x30 : /* read and branch */ - if (tms->talk_status == 0) /* TALKST must be clear for RB */ + if (m_talk_status == 0) /* TALKST must be clear for RB */ { #ifdef VERBOSE logerror("read and branch command received\n"); #endif - tms->RDB_flag = FALSE; - if (tms->intf->read_and_branch) - (*tms->intf->read_and_branch)(tms->device); + m_RDB_flag = FALSE; + if (m_speechrom) + m_speechrom->read_and_branch(); } break; case 0x40 : /* load address */ - if (tms->talk_status == 0) /* TALKST must be clear for LA */ + if (m_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 (tms->intf->load_address) - (*tms->intf->load_address)(tms->device, cmd & 0x0f); - tms->schedule_dummy_read = TRUE; + if (m_speechrom) + m_speechrom->load_address(cmd & 0x0f); + m_schedule_dummy_read = TRUE; } break; case 0x50 : /* speak */ - if (tms->schedule_dummy_read) + if (m_schedule_dummy_read) { - tms->schedule_dummy_read = FALSE; - if (tms->intf->read) - (*tms->intf->read)(tms->device, 1); + m_schedule_dummy_read = FALSE; + if (m_speechrom) + m_speechrom->read(1); } - tms->speaking_now = 1; - tms->speak_external = 0; - tms->talk_status = 1; /* start immediately */ + 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 - tms->subcycle = tms->subc_reload; - tms->PC = 0; - tms->IP = reload_table[tms->tms5220c_rate&0x3]; - tms->new_frame_energy_idx = 0; - tms->new_frame_pitch_idx = 0; + m_subcycle = m_subc_reload; + m_PC = 0; + m_IP = reload_table[m_tms5220c_rate&0x3]; + m_new_frame_energy_idx = 0; + m_new_frame_pitch_idx = 0; int i; for (i = 0; i < 4; i++) - tms->new_frame_k_idx[i] = 0; + m_new_frame_k_idx[i] = 0; for (i = 4; i < 7; i++) - tms->new_frame_k_idx[i] = 0xF; - for (i = 7; i < tms->coeff->num_k; i++) - tms->new_frame_k_idx[i] = 0x7; + 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 - tms->fifo_head = tms->fifo_tail = tms->fifo_count = tms->fifo_bits_taken = 0; - tms->speak_external = 1; - tms->RDB_flag = FALSE; + m_fifo_head = m_fifo_tail = m_fifo_count = m_fifo_bits_taken = 0; + m_speak_external = 1; + m_RDB_flag = FALSE; break; case 0x70 : /* reset */ - if (tms->schedule_dummy_read) + if (m_schedule_dummy_read) { - tms->schedule_dummy_read = FALSE; - if (tms->intf->read) - (*tms->intf->read)(tms->device, 1); + m_schedule_dummy_read = FALSE; + if (m_speechrom) + m_speechrom->read(1); } - tms->device->reset(); + reset(); break; } /* update the buffer low state */ - update_status_and_ints(tms); + update_status_and_ints(); } /****************************************************************************************** @@ -1374,7 +1236,7 @@ static void process_command(tms5220_state *tms, unsigned char cmd) ******************************************************************************************/ -static void parse_frame(tms5220_state *tms) +void tms5220_device::parse_frame() { int indx, i, rep_flag; @@ -1382,49 +1244,49 @@ static void parse_frame(tms5220_state *tms) /* if the chip is a tms5220C, and the rate mode is set to that each frame (0x04 bit set) has a 2 bit rate preceding it, grab two bits here and store them as the rate; */ - if ((tms->variant == TMS5220_IS_5220C) && (tms->tms5220c_rate & 0x04)) + if ((m_variant == TMS5220_IS_5220C) && (m_tms5220c_rate & 0x04)) { - indx = extract_bits(tms, 2); + indx = extract_bits(2); #ifdef DEBUG_PARSE_FRAME_DUMP printbits(indx,2); fprintf(stderr," "); #endif - tms->IP = reload_table[indx]; + m_IP = reload_table[indx]; } else // non-5220C and 5220C in fixed rate mode - tms->IP = reload_table[tms->tms5220c_rate&0x3]; + m_IP = reload_table[m_tms5220c_rate&0x3]; - update_status_and_ints(tms); - if (!tms->talk_status) goto ranout; + update_status_and_ints(); + if (!m_talk_status) goto ranout; // attempt to extract the energy index - tms->new_frame_energy_idx = extract_bits(tms,tms->coeff->energy_bits); + m_new_frame_energy_idx = extract_bits(m_coeff->energy_bits); #ifdef DEBUG_PARSE_FRAME_DUMP - printbits(tms->new_frame_energy_idx,tms->coeff->energy_bits); + printbits(m_new_frame_energy_idx,m_coeff->energy_bits); fprintf(stderr," "); #endif - update_status_and_ints(tms); - if (!tms->talk_status) goto ranout; + update_status_and_ints(); + if (!m_talk_status) goto ranout; // if the energy index is 0 or 15, we're done - if ((tms->new_frame_energy_idx == 0) || (tms->new_frame_energy_idx == 15)) + if ((m_new_frame_energy_idx == 0) || (m_new_frame_energy_idx == 15)) return; // attempt to extract the repeat flag - rep_flag = extract_bits(tms,1); + rep_flag = extract_bits(1); #ifdef DEBUG_PARSE_FRAME_DUMP printbits(rep_flag, 1); fprintf(stderr," "); #endif // attempt to extract the pitch - tms->new_frame_pitch_idx = extract_bits(tms,tms->coeff->pitch_bits); + m_new_frame_pitch_idx = extract_bits(m_coeff->pitch_bits); #ifdef DEBUG_PARSE_FRAME_DUMP - printbits(tms->new_frame_pitch_idx,tms->coeff->pitch_bits); + printbits(m_new_frame_pitch_idx,m_coeff->pitch_bits); fprintf(stderr," "); #endif - update_status_and_ints(tms); - if (!tms->talk_status) goto ranout; + update_status_and_ints(); + if (!m_talk_status) goto ranout; // if this is a repeat frame, just do nothing, it will reuse the old coefficients if (rep_flag) return; @@ -1432,36 +1294,36 @@ static void parse_frame(tms5220_state *tms) // extract first 4 K coefficients for (i = 0; i < 4; i++) { - tms->new_frame_k_idx[i] = extract_bits(tms,tms->coeff->kbits[i]); + m_new_frame_k_idx[i] = extract_bits(m_coeff->kbits[i]); #ifdef DEBUG_PARSE_FRAME_DUMP - printbits(tms->new_frame_k_idx[i],tms->coeff->kbits[i]); + printbits(m_new_frame_k_idx[i],m_coeff->kbits[i]); fprintf(stderr," "); #endif - update_status_and_ints(tms); - if (!tms->talk_status) goto ranout; + update_status_and_ints(); + if (!m_talk_status) goto ranout; } // if the pitch index was zero, we only need 4 K's... - if (tms->new_frame_pitch_idx == 0) + if (m_new_frame_pitch_idx == 0) { /* and the rest of the coefficients are zeroed, but that's done in the generator code */ return; } // If we got here, we need the remaining 6 K's - for (i = 4; i < tms->coeff->num_k; i++) + for (i = 4; i < m_coeff->num_k; i++) { - tms->new_frame_k_idx[i] = extract_bits(tms, tms->coeff->kbits[i]); + m_new_frame_k_idx[i] = extract_bits(m_coeff->kbits[i]); #ifdef DEBUG_PARSE_FRAME_DUMP - printbits(tms->new_frame_k_idx[i],tms->coeff->kbits[i]); + printbits(m_new_frame_k_idx[i],m_coeff->kbits[i]); fprintf(stderr," "); #endif - update_status_and_ints(tms); - if (!tms->talk_status) goto ranout; + update_status_and_ints(); + if (!m_talk_status) goto ranout; } #ifdef VERBOSE - if (tms->speak_external) - logerror("Parsed a frame successfully in FIFO - %d bits remaining\n", (tms->fifo_count*8)-(tms->fifo_bits_taken)); + if (m_speak_external) + logerror("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 @@ -1480,14 +1342,14 @@ static void parse_frame(tms5220_state *tms) ***********************************************************************************************/ -static void set_interrupt_state(tms5220_state *tms, int state) +void tms5220_device::set_interrupt_state(int state) { #ifdef DEBUG_PIN_READS logerror("irq pin set to state %d\n", state); #endif - if (!tms->irq_func.isnull() && state != tms->irq_pin) - tms->irq_func(!state); - tms->irq_pin = state; + if (!m_irq_handler.isnull() && state != m_irq_pin) + m_irq_handler(!state); + m_irq_pin = state; } /********************************************************************************************** @@ -1496,15 +1358,15 @@ static void set_interrupt_state(tms5220_state *tms, int state) ***********************************************************************************************/ -static void update_ready_state(tms5220_state *tms) +void tms5220_device::update_ready_state() { - int state = tms5220_ready_read(tms); + int state = ready_read(); #ifdef DEBUG_PIN_READS logerror("ready pin set to state %d\n", state); #endif - if (!tms->readyq_func.isnull() && state != tms->ready_pin) - tms->readyq_func(!state); - tms->ready_pin = state; + if (!m_readyq_handler.isnull() && state != m_ready_pin) + m_readyq_handler(!state); + m_ready_pin = state; } @@ -1514,113 +1376,121 @@ static void update_ready_state(tms5220_state *tms) ***********************************************************************************************/ -static DEVICE_START( tms5220 ) -{ - static const tms5220_interface dummy = { DEVCB_NULL }; - tms5220_state *tms = get_safe_token(device); - - tms->intf = device->static_config() ? (const tms5220_interface *)device->static_config() : &dummy; - //tms->table = *device->region(); +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- - tms->device = device; - tms5220_set_variant(tms, TMS5220_IS_5220); - tms->clock = device->clock(); +void tms5220_device::device_start() +{ + if (m_speechrom_tag) + { + m_speechrom = siblingdevice<speechrom_device>( m_speechrom_tag ); + if( !m_speechrom ) + { + throw new emu_fatalerror("Error: %s '%s' can't find speechrom '%s'\n", shortname(), tag(), m_speechrom_tag ); + } + } + else + { + m_speechrom = NULL; + } - assert_always(tms != NULL, "Error creating TMS5220 chip"); + set_variant(TMS5220_IS_5220); + m_clock = clock(); /* resolve irq and readyq line */ - tms->irq_func.resolve(tms->intf->irq_func, *device); - tms->readyq_func.resolve(tms->intf->readyq_func, *device); + m_irq_handler.resolve(); + m_readyq_handler.resolve(); /* initialize a stream */ - tms->stream = device->machine().sound().stream_alloc(*device, 0, 1, device->clock() / 80, tms, tms5220_update); + m_stream = machine().sound().stream_alloc(*this, 0, 1, clock() / 80); - /*if (tms->table == NULL) - { - assert_always(tms->intf->M0_callback != NULL, "Missing _mandatory_ 'M0_callback' function pointer in the TMS5110 interface\n This function is used by TMS5220 to call for a new single bit\n needed to generate the speech when in VSM mode\n Aborting startup...\n"); - tms->M0_callback = tms->intf->M0_callback; - tms->set_load_address = tms->intf->load_address; - } - else - { - tms->M0_callback = speech_rom_read_bit; - tms->set_load_address = speech_rom_set_addr; - }*/ + m_timer_io_ready = timer_alloc(0); /* not during reset which is called frm within a write! */ - tms->io_ready = 1; - tms->true_timing = 0; - tms->rs_ws = 0x03; // rs and ws are assumed to be inactive on device startup + m_io_ready = 1; + m_true_timing = 0; + m_rs_ws = 0x03; // rs and ws are assumed to be inactive on device startup - register_for_save_states(tms); + register_for_save_states(); } -static DEVICE_START( tms5220c ) +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void tms5220c_device::device_start() { - tms5220_state *tms = get_safe_token(device); - DEVICE_START_CALL( tms5220 ); - tms5220_set_variant(tms, TMS5220_IS_5220C); + tms5220_device::device_start(); + set_variant(TMS5220_IS_5220C); } -static DEVICE_START( tmc0285 ) +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void tmc0285_device::device_start() { - tms5220_state *tms = get_safe_token(device); - DEVICE_START_CALL( tms5220 ); - tms5220_set_variant(tms, TMS5220_IS_TMC0285); + tms5220_device::device_start(); + set_variant(TMS5220_IS_TMC0285); } +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- -static DEVICE_START( tms5200 ) +void tms5200_device::device_start() { - tms5220_state *tms = get_safe_token(device); - DEVICE_START_CALL( tms5220 ); - tms5220_set_variant(tms, TMS5220_IS_5200); + tms5220_device::device_start(); + set_variant(TMS5220_IS_5200); } -static DEVICE_RESET( tms5220 ) -{ - tms5220_state *tms = get_safe_token(device); +//------------------------------------------------- +// device_reset - device-specific reset +//------------------------------------------------- - tms->digital_select = FORCE_DIGITAL; // assume analog output +void tms5220_device::device_reset() +{ + m_digital_select = FORCE_DIGITAL; // assume analog output /* initialize the FIFO */ - /*memset(tms->fifo, 0, sizeof(tms->fifo));*/ - tms->fifo_head = tms->fifo_tail = tms->fifo_count = tms->fifo_bits_taken = 0; + /*memset(m_fifo, 0, sizeof(m_fifo));*/ + m_fifo_head = m_fifo_tail = m_fifo_count = m_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->speaking_now = tms->speak_external = tms->talk_status = tms->irq_pin = tms->ready_pin = 0; - set_interrupt_state(tms, 0); - update_ready_state(tms); - tms->buffer_empty = tms->buffer_low = 1; + /* 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; + set_interrupt_state(0); + update_ready_state(); + m_buffer_empty = m_buffer_low = 1; - tms->RDB_flag = FALSE; + m_RDB_flag = FALSE; /* initialize the energy/pitch/k states */ #ifdef PERFECT_INTERPOLATION_HACK - tms->old_frame_energy_idx = tms->old_frame_pitch_idx = 0; - memset(tms->old_frame_k_idx, 0, sizeof(tms->old_frame_k_idx)); + m_old_frame_energy_idx = m_old_frame_pitch_idx = 0; + memset(m_old_frame_k_idx, 0, sizeof(m_old_frame_k_idx)); #endif - tms->new_frame_energy_idx = tms->current_energy = tms->target_energy = 0; - tms->new_frame_pitch_idx = tms->current_pitch = tms->target_pitch = 0; - memset(tms->new_frame_k_idx, 0, sizeof(tms->new_frame_k_idx)); - memset(tms->current_k, 0, sizeof(tms->current_k)); - memset(tms->target_k, 0, sizeof(tms->target_k)); + m_new_frame_energy_idx = m_current_energy = m_target_energy = 0; + m_new_frame_pitch_idx = m_current_pitch = m_target_pitch = 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 */ - tms->inhibit = 1; - tms->subcycle = tms->tms5220c_rate = tms->pitch_count = tms->PC = 0; - tms->subc_reload = FORCE_SUBC_RELOAD; - tms->OLDE = tms->OLDP = 1; - tms->IP = reload_table[tms->tms5220c_rate&0x3]; - tms->RNG = 0x1FFF; - memset(tms->u, 0, sizeof(tms->u)); - memset(tms->x, 0, sizeof(tms->x)); - - if (tms->intf->load_address) - (*tms->intf->load_address)(tms->device, 0); - - tms->schedule_dummy_read = TRUE; + m_inhibit = 1; + m_subcycle = m_tms5220c_rate = m_pitch_count = m_PC = 0; + m_subc_reload = FORCE_SUBC_RELOAD; + m_OLDE = m_OLDP = 1; + m_IP = reload_table[m_tms5220c_rate&0x3]; + m_RNG = 0x1FFF; + memset(m_u, 0, sizeof(m_u)); + memset(m_x, 0, sizeof(m_x)); + + if (m_speechrom) + m_speechrom->load_address(0); + + m_schedule_dummy_read = TRUE; } /********************************************************************************************** @@ -1629,61 +1499,65 @@ static DEVICE_RESET( tms5220 ) ***********************************************************************************************/ -static TIMER_CALLBACK( io_ready_cb ) +void tms5220_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) { - tms5220_state *tms = (tms5220_state *) ptr; - if (param) + switch(id) { - switch (tms->rs_ws) + case 0: + if (param) { - case 0x02: - /* Write */ - /* bring up to date first */ + switch (m_rs_ws) + { + case 0x02: + /* Write */ + /* bring up to date first */ #ifdef DEBUG_IO_READY - logerror("Serviced write: %02x\n", tms->write_latch); - //fprintf(stderr, "Processed write data: %02X\n", tms->write_latch); -#endif - tms->stream->update(); - tms5220_data_write(tms, tms->write_latch); - break; - case 0x01: - /* Read */ - /* bring up to date first */ - tms->stream->update(); - tms->read_latch = tms5220_status_read(tms); - break; - case 0x03: - /* High Impedance */ - case 0x00: - /* illegal */ - break; + logerror("Serviced write: %02x\n", m_write_latch); + //fprintf(stderr, "Processed write data: %02X\n", m_write_latch); +#endif + m_stream->update(); + data_write(m_write_latch); + break; + case 0x01: + /* Read */ + /* bring up to date first */ + m_stream->update(); + m_read_latch = status_read(); + break; + case 0x03: + /* High Impedance */ + case 0x00: + /* illegal */ + break; + } } + + m_io_ready = param; + update_ready_state(); + break; } - tms->io_ready = param; - update_ready_state(tms); } /* * /RS line write handler */ -WRITE_LINE_DEVICE_HANDLER( tms5220_rsq_w ) +WRITE_LINE_MEMBER( tms5220_device::rsq_w ) { - tms5220_state *tms = get_safe_token(device); UINT8 new_val; - tms->true_timing = 1; + m_true_timing = 1; state &= 0x01; #ifdef DEBUG_RS_WS logerror("/RS written with data: %d\n", state); #endif - new_val = (tms->rs_ws & 0x01) | (state<<1); - if (new_val != tms->rs_ws) + new_val = (m_rs_ws & 0x01) | (state<<1); + if (new_val != m_rs_ws) { - tms->rs_ws = new_val; + m_rs_ws = new_val; if (new_val == 0) { - if (tms->variant == TMS5220_IS_5220C) - device->reset(); + if (m_variant == TMS5220_IS_5220C) + reset(); #ifdef DEBUG_RS_WS else /* illegal */ @@ -1694,7 +1568,7 @@ WRITE_LINE_DEVICE_HANDLER( tms5220_rsq_w ) else if ( new_val == 3) { /* high impedance */ - tms->read_latch = 0xff; + m_read_latch = 0xff; return; } if (state) @@ -1708,10 +1582,10 @@ WRITE_LINE_DEVICE_HANDLER( tms5220_rsq_w ) logerror("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. */ - tms->io_ready = 0; - update_ready_state(tms); + 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) */ - tms->device->machine().scheduler().timer_set(attotime::from_hz(device->clock()/16), FUNC(io_ready_cb), 1, tms); // this should take around 10-16 (closer to ~11?) cycles to complete + m_timer_io_ready->adjust(attotime::from_hz(clock()/16), 1); // this should take around 10-16 (closer to ~11?) cycles to complete } } } @@ -1719,24 +1593,23 @@ WRITE_LINE_DEVICE_HANDLER( tms5220_rsq_w ) /* * /WS line write handler */ -WRITE_LINE_DEVICE_HANDLER( tms5220_wsq_w ) +WRITE_LINE_MEMBER( tms5220_device::wsq_w ) { - tms5220_state *tms = get_safe_token(device); UINT8 new_val; - tms->true_timing = 1; + m_true_timing = 1; state &= 0x01; #ifdef DEBUG_RS_WS logerror("/WS written with data: %d\n", state); #endif - new_val = (tms->rs_ws & 0x02) | (state<<0); - if (new_val != tms->rs_ws) + new_val = (m_rs_ws & 0x02) | (state<<0); + if (new_val != m_rs_ws) { - tms->rs_ws = new_val; + m_rs_ws = new_val; if (new_val == 0) { - if (tms->variant == TMS5220_IS_5220C) - device->reset(); + if (m_variant == TMS5220_IS_5220C) + reset(); #ifdef DEBUG_RS_WS else /* illegal */ @@ -1747,7 +1620,7 @@ WRITE_LINE_DEVICE_HANDLER( tms5220_wsq_w ) else if ( new_val == 3) { /* high impedance */ - tms->read_latch = 0xff; + m_read_latch = 0xff; return; } if (state) @@ -1761,8 +1634,8 @@ WRITE_LINE_DEVICE_HANDLER( tms5220_wsq_w ) logerror("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. */ - tms->io_ready = 0; - update_ready_state(tms); + m_io_ready = 0; + update_ready_state(); /* Now comes the complicated part: 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: @@ -1774,7 +1647,7 @@ WRITE_LINE_DEVICE_HANDLER( tms5220_wsq_w ) SET RATE (5220C only): ? cycles (probably ~16) */ // TODO: actually HANDLE the timing differences! currently just assuming always 16 cycles - tms->device->machine().scheduler().timer_set(attotime::from_hz(device->clock()/16), FUNC(io_ready_cb), 1, tms); // this should take around 10-16 (closer to ~15) cycles to complete for fifo writes, TODO: but actually depends on what command is written if in command mode + m_timer_io_ready->adjust(attotime::from_hz(clock()/16), 1); // this should take around 10-16 (closer to ~15) cycles to complete for fifo writes, TODO: but actually depends on what command is written if in command mode } } } @@ -1785,26 +1658,25 @@ WRITE_LINE_DEVICE_HANDLER( tms5220_wsq_w ) ***********************************************************************************************/ -WRITE8_DEVICE_HANDLER( tms5220_data_w ) +WRITE8_MEMBER( tms5220_device::data_w ) { - tms5220_state *tms = get_safe_token(device); #ifdef DEBUG_RS_WS logerror("tms5220_data_w: data %02x\n", data); #endif - if (!tms->true_timing) + if (!m_true_timing) { /* bring up to date first */ - tms->stream->update(); - tms5220_data_write(tms, data); + m_stream->update(); + data_write(data); } else { /* actually in a write ? */ #ifdef DEBUG_RS_WS - if (!(tms->rs_ws == 0x02)) - logerror("tms5220_data_w: data written outside ws, status: %02x!\n", tms->rs_ws); + if (!(m_rs_ws == 0x02)) + logerror("tms5220_data_w: data written outside ws, status: %02x!\n", m_rs_ws); #endif - tms->write_latch = data; + m_write_latch = data; } } @@ -1816,20 +1688,19 @@ WRITE8_DEVICE_HANDLER( tms5220_data_w ) ***********************************************************************************************/ -READ8_DEVICE_HANDLER( tms5220_status_r ) +READ8_MEMBER( tms5220_device::status_r ) { - tms5220_state *tms = get_safe_token(device); - if (!tms->true_timing) + if (!m_true_timing) { /* bring up to date first */ - tms->stream->update(); - return tms5220_status_read(tms); + m_stream->update(); + return status_read(); } else { /* actually in a read ? */ - if (tms->rs_ws == 0x01) - return tms->read_latch; + if (m_rs_ws == 0x01) + return m_read_latch; #ifdef DEBUG_RS_WS else logerror("tms5220_status_r: data read outside rs!\n"); @@ -1846,12 +1717,11 @@ READ8_DEVICE_HANDLER( tms5220_status_r ) ***********************************************************************************************/ -READ_LINE_DEVICE_HANDLER( tms5220_readyq_r ) +READ_LINE_MEMBER( tms5220_device::readyq_r ) { - tms5220_state *tms = get_safe_token(device); /* bring up to date first */ - tms->stream->update(); - return !tms5220_ready_read(tms); + m_stream->update(); + return !ready_read(); } @@ -1862,15 +1732,14 @@ READ_LINE_DEVICE_HANDLER( tms5220_readyq_r ) ***********************************************************************************************/ -double tms5220_time_to_ready(device_t *device) +double tms5220_device::time_to_ready() { - tms5220_state *tms = get_safe_token(device); double cycles; /* bring up to date first */ - tms->stream->update(); - cycles = tms5220_cycles_to_ready(tms); - return cycles * 80.0 / tms->clock; + m_stream->update(); + cycles = cycles_to_ready(); + return cycles * 80.0 / m_clock; } @@ -1881,12 +1750,11 @@ double tms5220_time_to_ready(device_t *device) ***********************************************************************************************/ -READ_LINE_DEVICE_HANDLER( tms5220_intq_r ) +READ_LINE_MEMBER( tms5220_device::intq_r ) { - tms5220_state *tms = get_safe_token(device); /* bring up to date first */ - tms->stream->update(); - return !tms5220_int_read(tms); + m_stream->update(); + return !int_read(); } @@ -1897,9 +1765,12 @@ READ_LINE_DEVICE_HANDLER( tms5220_intq_r ) ***********************************************************************************************/ -static STREAM_UPDATE( tms5220_update ) +//------------------------------------------------- +// sound_stream_update - handle a stream update +//------------------------------------------------- + +void tms5220_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) { - tms5220_state *tms = (tms5220_state *)param; INT16 sample_data[MAX_SAMPLE_CHUNK]; stream_sample_t *buffer = outputs[0]; @@ -1910,7 +1781,7 @@ static STREAM_UPDATE( tms5220_update ) int index; /* generate the samples and copy to the target buffer */ - tms5220_process(tms, sample_data, length); + process(sample_data, length); for (index = 0; index < length; index++) *buffer++ = sample_data[index]; @@ -1927,11 +1798,10 @@ static STREAM_UPDATE( tms5220_update ) ***********************************************************************************************/ -void tms5220_set_frequency(device_t *device, int frequency) +void tms5220_device::set_frequency(int frequency) { - tms5220_state *tms = get_safe_token(device); - tms->stream->set_sample_rate(frequency / 80); - tms->clock = frequency; + m_stream->set_sample_rate(frequency / 80); + m_clock = frequency; } const device_type TMS5220C = &device_creator<tms5220c_device>; @@ -1941,39 +1811,25 @@ tms5220c_device::tms5220c_device(const machine_config &mconfig, const char *tag, { } -//------------------------------------------------- -// device_start - device-specific startup -//------------------------------------------------- - -void tms5220c_device::device_start() -{ - DEVICE_START_NAME( tms5220c )(this); -} - -//------------------------------------------------- -// sound_stream_update - handle a stream update -//------------------------------------------------- - -void tms5220c_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) -{ - // should never get here - fatalerror("sound_stream_update called; not applicable to legacy sound devices\n"); -} - const device_type TMS5220 = &device_creator<tms5220_device>; tms5220_device::tms5220_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : device_t(mconfig, TMS5220, "TMS5220", tag, owner, clock), - device_sound_interface(mconfig, *this) + device_sound_interface(mconfig, *this), + m_irq_handler(*this), + m_readyq_handler(*this), + m_speechrom_tag(NULL) { - m_token = global_alloc_clear(tms5220_state); } + tms5220_device::tms5220_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock) : device_t(mconfig, type, name, tag, owner, clock), - device_sound_interface(mconfig, *this) + device_sound_interface(mconfig, *this), + m_irq_handler(*this), + m_readyq_handler(*this), + m_speechrom_tag(NULL) { - m_token = global_alloc_clear(tms5220_state); } //------------------------------------------------- @@ -1986,34 +1842,6 @@ void tms5220_device::device_config_complete() { } -//------------------------------------------------- -// device_start - device-specific startup -//------------------------------------------------- - -void tms5220_device::device_start() -{ - DEVICE_START_NAME( tms5220 )(this); -} - -//------------------------------------------------- -// device_reset - device-specific reset -//------------------------------------------------- - -void tms5220_device::device_reset() -{ - DEVICE_RESET_NAME( tms5220 )(this); -} - -//------------------------------------------------- -// sound_stream_update - handle a stream update -//------------------------------------------------- - -void tms5220_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) -{ - // should never get here - fatalerror("sound_stream_update called; not applicable to legacy sound devices\n"); -} - const device_type TMC0285 = &device_creator<tmc0285_device>; @@ -2022,25 +1850,6 @@ tmc0285_device::tmc0285_device(const machine_config &mconfig, const char *tag, d { } -//------------------------------------------------- -// device_start - device-specific startup -//------------------------------------------------- - -void tmc0285_device::device_start() -{ - DEVICE_START_NAME( tmc0285 )(this); -} - -//------------------------------------------------- -// sound_stream_update - handle a stream update -//------------------------------------------------- - -void tmc0285_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) -{ - // should never get here - fatalerror("sound_stream_update called; not applicable to legacy sound devices\n"); -} - const device_type TMS5200 = &device_creator<tms5200_device>; @@ -2048,1367 +1857,3 @@ tms5200_device::tms5200_device(const machine_config &mconfig, const char *tag, d : tms5220_device(mconfig, TMS5200, "TMS5200", tag, owner, clock) { } - -//------------------------------------------------- -// device_start - device-specific startup -//------------------------------------------------- - -void tms5200_device::device_start() -{ - DEVICE_START_NAME( tms5200 )(this); -} - -//------------------------------------------------- -// sound_stream_update - handle a stream update -//------------------------------------------------- - -void tms5200_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) -{ - // should never get here - fatalerror("sound_stream_update called; not applicable to legacy sound devices\n"); -} - - - - -/****************************************************************************** - New class implementation -******************************************************************************/ - -#define M_INTERP_SHIFT >> m_coeff->interp_coeff[m_IP] - -tms52xx_device::tms52xx_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, const struct tms5100_coeffs* coeffs, const int var, device_t *owner, UINT32 clock) - : device_t(mconfig, type, name, tag, owner, clock), - device_sound_interface(mconfig, *this), - m_variant(var), - m_coeff(coeffs) -{ -} - - -tms5220n_device::tms5220n_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) - : tms52xx_device(mconfig, TMS5220N, "TMS5220N", tag, &tms5220_coeff, TMS5220_IS_5220, owner, clock) -{ -} - -tms5220cn_device::tms5220cn_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) - : tms52xx_device(mconfig, TMS5220CN, "TMS5220CN", tag, &tms5220_coeff, TMS5220_IS_5220C, owner, clock) -{ -} - -tmc0285n_device::tmc0285n_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) - : tms52xx_device(mconfig, TMC0285N, "TMC0285N", tag, &tms5200_coeff, TMS5220_IS_TMC0285, owner, clock) -{ -} - -tms5200n_device::tms5200n_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) - : tms52xx_device(mconfig, TMS5200N, "TMS5200N", tag, &tms5200_coeff, TMS5220_IS_5200, owner, clock) -{ -} - -void tms52xx_device::device_start() -{ - const tms52xx_config *conf = reinterpret_cast<const tms52xx_config *>(static_config()); - //m_table = region(); - - /* resolve irq and readyq line */ - m_irq_func.resolve(conf->irq_func, *this); - m_readyq_func.resolve(conf->readyq_func, *this); - m_read_mem.resolve(conf->read_mem, *this); - m_load_address.resolve(conf->load_address, *this); - m_read_and_branch.resolve(conf->read_and_branch, *this); - - /* initialize a stream */ - m_stream = machine().sound().stream_alloc(*this, 0, 1, clock() / 80, this); - - /*if (m_table == NULL) - { - assert_always(m_conf->M0_callback != NULL, "Missing _mandatory_ 'M0_callback' function pointer in the TMS5110 interface\n This function is used by TMS5220 to call for a new single bit\n needed to generate the speech when in VSM mode\n Aborting startup...\n"); - m_M0_callback = conf->M0_callback; - m_set_load_address = conf->load_address; - } - else - { - m_M0_callback = speech_rom_read_bit; - m_set_load_address = speech_rom_set_addr; - }*/ - - /* not during reset which is called frm within a write! */ - m_io_ready = true; - m_true_timing = false; - m_rs_ws = 0x03; // rs and ws are assumed to be inactive on device startup - - m_ready_timer = timer_alloc(0); - - register_for_save_states(); -} - -void tms52xx_device::device_reset() -{ - m_digital_select = FORCE_DIGITAL; // assume analog output - // initialize the FIFO - // should we do a memset here to clear the fifo contents? - m_fifo_head = 0; - m_fifo_tail = 0; - m_fifo_count = 0; - m_fifo_bits_taken = 0; - - // 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 = false; - m_speak_external = false; - m_talk_status = false; - m_irq_pin = 0; // CLEAR_LINE - m_ready_pin = 0; // CLEAR_LINE - - set_interrupt_state(0); // CLEAR_LINE - update_ready_state(); - m_buffer_empty = true; - m_buffer_low = true; - - m_RDB_flag = false; - - /* initialize the energy/pitch/k states */ -#ifdef PERFECT_INTERPOLATION_HACK - m_old_frame_energy_idx = 0; - m_old_frame_pitch_idx = 0; - memset(m_old_frame_k_idx, 0, sizeof(m_old_frame_k_idx)); -#endif - - m_new_frame_energy_idx = 0; - m_current_energy = 0; - m_target_energy = 0; - m_new_frame_pitch_idx = 0; - m_current_pitch = 0; - m_target_pitch = 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 = true; - m_subcycle = 0; - m_tms5220c_rate = 0; - m_pitch_count = 0; - m_PC = 0; - - m_subc_reload = FORCE_SUBC_RELOAD; - m_OLDE = 1; - m_OLDP = 1; - - m_IP = reload_table[m_tms5220c_rate&0x3]; - m_RNG = 0x1FFF; - memset(m_u, 0, sizeof(m_u)); - memset(m_x, 0, sizeof(m_x)); - - m_load_address(0, 0); - m_schedule_dummy_read = true; -} - -/****************************************************************************** - set_interrupt_state -- generate an interrupt -*******************************************************************************/ - -void tms52xx_device::set_interrupt_state(int state) -{ -#ifdef DEBUG_PIN_READS - logerror("tms52xx: irq pin set to state %d\n", state); -#endif - if (state != m_irq_pin) m_irq_func(!state); - m_irq_pin = state; -} - -/****************************************************************************** - update_ready_state -- update the ready line -*******************************************************************************/ - -void tms52xx_device::update_ready_state() -{ - int state = ready_read(); -#ifdef DEBUG_PIN_READS - logerror("tms52xx: ready pin set to state %d\n", state); -#endif - if (state != m_ready_pin) m_readyq_func(!state); - m_ready_pin = state; -} - -/****************************************************************************** - tms5220_set_frequency -- adjusts the playback frequency -*******************************************************************************/ - -void tms52xx_device::set_frequency(int frequency) -{ - m_stream->set_sample_rate(frequency / 80); - m_clock = frequency; -} - -/****************************************************************************** - tms5220_update -- update the sound chip so that it is in sync with CPU execution -*******************************************************************************/ - -void tms52xx_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) -{ - INT16 sample_data[MAX_SAMPLE_CHUNK]; - stream_sample_t *buffer = outputs[0]; - - /* loop while we still have samples to generate */ - while (samples>0) - { - int length = (samples > MAX_SAMPLE_CHUNK) ? MAX_SAMPLE_CHUNK : samples; - int index; - - /* generate the samples and copy to the target buffer */ - process(sample_data, length); - for (index = 0; index < length; index++) - *buffer++ = sample_data[index]; - - /* account for the samples */ - samples -= length; - } -} - -/****************************************************************************** - process -- fill the buffer with a specific number of samples -*******************************************************************************/ - -void tms52xx_device::process(INT16 *buffer, unsigned int size) -{ - int buf_count=0; - int i, bitout; - bool zpar; - 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) 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; - - // loop until the buffer is full or we've stopped speaking - while ((size > 0) && m_speaking_now) - { - /* if it is the appropriate time to update the old energy/pitch idxes, - * 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. - */ - 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 PC=x time period, but it doesn't affect anything until IP=0 PC=12 happens) - */ - if ((m_IP == 0) && (m_PC == 12) && (m_subcycle == 1)) - { - // HACK for regression testing, be sure to comment out before release! - // m_RNG = 0x1234; - // end HACK - - // appropriately override the interp count if needed; this will be incremented after the frame parse! - m_IP = reload_table[m_tms5220c_rate & 0x3]; - -#ifdef PERFECT_INTERPOLATION_HACK - // remember previous frame energy, pitch, and coefficients - m_old_frame_energy_idx = m_new_frame_energy_idx; - m_old_frame_pitch_idx = m_new_frame_pitch_idx; - for (i = 0; i < m_coeff->num_k; i++) - 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 == false) - { -#ifdef DEBUG_GENERATION - fprintf(stderr,"tms5220_process: processing frame: talk status = 0 caused by stop frame or buffer empty, halting speech.\n"); -#endif - m_speaking_now = false; // finally halt speech - goto empty; - } - - // Parse a new frame into the new_target_energy, new_target_pitch and new_target_k[] - parse_frame(); -#ifdef DEBUG_PARSE_FRAME_DUMP - fprintf(stderr,"\n"); -#endif - // if the new frame is a stop frame, set an interrupt and set talk status to 0 - if (M_NEW_FRAME_STOP_FLAG == 1) - { - m_talk_status = false; - m_speak_external = false; - set_interrupt_state(1); - update_status_and_ints(); - } - - // in all cases where interpolation would be inhibited, set the inhibit flag; otherwise clear it. - // Interpolation inhibit cases: - // * Old frame was voiced, new is unvoiced - // * Old frame was silence/zero energy, new has nonzero energy - // * Old frame was unvoiced, new is voiced - - if ( ((M_OLD_FRAME_UNVOICED_FLAG == false) && (M_NEW_FRAME_UNVOICED_FLAG == true)) - || ((M_OLD_FRAME_UNVOICED_FLAG == true) && (M_NEW_FRAME_UNVOICED_FLAG == false)) /* this line needs further investigation, starwars tie fighters may sound better without it */ - || ((M_OLD_FRAME_SILENCE_FLAG == true) && (M_NEW_FRAME_SILENCE_FLAG == false)) ) - m_inhibit = true; - else // normal frame, normal interpolation - m_inhibit = false; - - // 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 = M_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: "); - 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]); -#endif - - /* if TS is now 0, ramp the energy down to 0. Is this really correct to hardware? */ - if (!m_talk_status) - { -#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. - { - bool inhibit_state = (m_inhibit && (m_IP != 0)); // disable inhibit when reaching the last interp period, but don't overwrite the tms->inhibit value -#ifdef PERFECT_INTERPOLATION_HACK - int samples_per_frame = (m_subc_reload!=0)? 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!=0)?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 = M_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)); - // now adjust each value to be exactly correct for each of the samples per frame - if (m_IP != 0) // if we're still interpolating... - { - if (!inhibit_state) - { - m_current_energy += ((m_target_energy - m_current_energy)*current_sample)/samples_per_frame; - m_current_pitch += ((m_target_pitch - m_current_pitch)*current_sample)/samples_per_frame; - for (i = 0; i < m_coeff->num_k; i++) - { - m_current_k[i] += ((m_target_k[i] - m_current_k[i])*current_sample)/samples_per_frame; - } - } - } - else // we're done, play this frame for 1/8 frame. - { - m_current_energy = m_target_energy; - m_current_pitch = m_target_pitch; - for (i = 0; i < m_coeff->num_k; i++) - m_current_k[i] = m_target_k[i]; - } -#else - // Updates to parameters only happen on subcycle '2' (B cycle) of PCs. - if (m_subcycle == 2) - { - if (!inhibit_state) - { - switch(m_PC) - { - case 0: // PC = 0, B cycle, write updated energy - m_current_energy += ((m_target_energy - m_current_energy) M_INTERP_SHIFT); - break; - case 1: /* PC = 1, B cycle, write updated pitch */ - m_current_pitch += ((m_target_pitch - m_current_pitch) M_INTERP_SHIFT); - 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]) M_INTERP_SHIFT); - break; - case 12: /* PC = 12, do nothing */ - break; - } - } - } -#endif - } - - // calculate the output - if (M_OLD_FRAME_UNVOICED_FLAG == true) - { - // generate unvoiced samples here - if (m_RNG & 1) - m_excitation_data = ~0x3F; // according to the patent it is (either + or -) half of the maximum value in the chirp table, so either 01000000(0x40) or 11000000(0xC0) - else - m_excitation_data = 0x40; - } - else // (M_OLD_FRAME_UNVOICED_FLAG == false) - { - // generate voiced samples here - // US patent 4331836 Figure 14B shows, and logic would hold, that a pitch based chirp - // function has a chirp/peak and then a long chain of zeroes. - // The last entry of the chirp rom is at address 0b110011 (51d), the 52nd sample, - // and if the address reaches that point the ADDRESS incrementer is - // disabled, forcing all samples beyond 51d to be == 51d - - if (m_pitch_count >= 51) - m_excitation_data = m_coeff->chirptable[51]; - else // tms->pitch_count < 51 - m_excitation_data = m_coeff->chirptable[m_pitch_count]; - } - - // Update LFSR *20* times every sample (once per T cycle), like patent shows - for (i=0; i<20; i++) - { - bitout = ((m_RNG >> 12) & 1) ^ - ((m_RNG >> 3) & 1) ^ - ((m_RNG >> 2) & 1) ^ - ((m_RNG >> 0) & 1); - m_RNG <<= 1; - m_RNG |= bitout; - } - - this_sample = lattice_filter(); // execute lattice filter -#ifdef DEBUG_GENERATION_VERBOSE - //fprintf(stderr,"C:%01d; ",tms->subcycle); - fprintf(stderr,"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); - //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,"\n"); -#endif - // 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; - if (!m_digital_select) // analog SPK pin output is only 8 bits, with clipping - buffer[buf_count] = clip_analog(this_sample); - else // digital I/O pin output is 12 bits - { -#ifdef ALLOW_4_LSB - // input: ssss ssss ssss ssss ssnn nnnn nnnn nnnn - // N taps: ^ = 0x2000; - // output: ssss ssss ssss ssss snnn nnnn nnnn nnnN - buffer[buf_count] = (this_sample<<1)|((this_sample&0x2000)>>13); -#else - this_sample &= ~0xF; - // input: ssss ssss ssss ssss ssnn nnnn nnnn 0000 - // N taps: ^^ ^^^ = 0x3E00; - // output: ssss ssss ssss ssss snnn nnnn nnnN NNNN - buffer[buf_count] = (this_sample<<1)|((this_sample&0x3E00)>>9); -#endif - } - - // Update all counts - m_subcycle++; - if ((m_subcycle == 2) && (m_PC == 12)) - { - /* Circuit 412 in the patent acts a reset, resetting the pitch counter to 0 - * if INHIBIT was true during the most recent frame transition. - * The exact time this occurs is betwen IP=7, PC=12 sub=0, T=t12 - * and tms->IP = 0, PC=0 sub=0, T=t12, a period of exactly 20 cycles, - * which overlaps the time OLDE and OLDP are updated at IP=7 PC=12 T17 - * (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) m_pitch_count = 0; - m_subcycle = m_subc_reload; - m_PC = 0; - m_IP++; - m_IP &= 0x7; - } - else if (m_subcycle == 3) - { - m_subcycle = m_subc_reload; - m_PC++; - } - m_pitch_count++; - if (m_pitch_count >= m_current_pitch) m_pitch_count = 0; - m_pitch_count &= 0x1FF; - buf_count++; - size--; - } - -empty: - - while (size > 0) - { - m_subcycle++; - if ((m_subcycle == 2) && (m_PC == 12)) - { - m_subcycle = m_subc_reload; - m_PC = 0; - m_IP++; - m_IP &= 0x7; - } - else if (m_subcycle == 3) - { - m_subcycle = m_subc_reload; - m_PC++; - } - buffer[buf_count] = -1; // should be just -1; actual chip outputs -1 every idle sample; (cf note in data sheet, p 10, table 4) - buf_count++; - size--; - } -} - -/****************************************************************************** - 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. -******************************************************************************/ - -INT32 tms52xx_device::lattice_filter() -{ - /* Lattice filter here */ - /* Aug/05/07: redone as unrolled loop, for clarity - LN - * Originally Copied verbatim from table I in US patent 4,209,804, now updated - * to be in same order as the actual chip does it, not that it matters. - * notation equivalencies from table: - * Yn(i) == m_u[n-1] - * Kn = m_current_k[n-1] - * bn = m_x[n-1] - */ - - m_u[10] = matrix_multiply(m_previous_energy, (m_excitation_data<<6)); //Y(11) - m_u[9] = m_u[10] - matrix_multiply(m_current_k[9], m_x[9]); - m_u[8] = m_u[9] - matrix_multiply(m_current_k[8], m_x[8]); - m_u[7] = m_u[8] - matrix_multiply(m_current_k[7], m_x[7]); - m_u[6] = m_u[7] - matrix_multiply(m_current_k[6], m_x[6]); - m_u[5] = m_u[6] - matrix_multiply(m_current_k[5], m_x[5]); - m_u[4] = m_u[5] - matrix_multiply(m_current_k[4], m_x[4]); - m_u[3] = m_u[4] - matrix_multiply(m_current_k[3], m_x[3]); - 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]); - 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]); - m_x[6] = m_x[5] + matrix_multiply(m_current_k[5], m_u[5]); - m_x[5] = m_x[4] + matrix_multiply(m_current_k[4], m_u[4]); - m_x[4] = m_x[3] + matrix_multiply(m_current_k[3], m_u[3]); - m_x[3] = m_x[2] + matrix_multiply(m_current_k[2], m_u[2]); - m_x[2] = m_x[1] + matrix_multiply(m_current_k[1], m_u[1]); - 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; - fprintf(stderr,"V:%04d ", m_u[10]); - for (i = 9; i >= 0; i--) - { - fprintf(stderr,"Y%d:%04d ", i+1, m_u[i]); - fprintf(stderr,"b%d:%04d ", i+1, m_x[i]); - if ((i % 5) == 0) fprintf(stderr,"\n"); - } -#endif - return m_u[0]; -} - -/********************************************************************************************** - data_write -- handle a write to the TMS5220 -***********************************************************************************************/ - -void tms52xx_device::data_write(int data) -{ -#ifdef DEBUG_DUMP_INPUT_DATA - fprintf(stdout, "%c",data); -#endif - if (m_speak_external) // If we're in speak external mode - { - /* add this byte to the FIFO */ - if (m_fifo_count < FIFO_SIZE) - { - m_fifo[m_fifo_tail] = data; - m_fifo_tail = (m_fifo_tail + 1) % FIFO_SIZE; - m_fifo_count++; -#ifdef DEBUG_FIFO - logerror("tms52xx: data_write: Added byte to FIFO (current count=%2d)\n", m_fifo_count); -#endif - update_status_and_ints(); - if ((!m_talk_status) && (!m_buffer_low)) // we just unset buffer low with that last write, and talk status *was* zero... - { - int i; -#ifdef DEBUG_FIFO - logerror("tms52xx: data_write triggered talk status to go active!\n"); -#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_tms5220c_rate & 0x3]; // is this correct? should this be always 7 instead, so that the new frame is loaded quickly? - 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_talk_status = m_speaking_now = true; - } - } - else - { -#ifdef DEBUG_FIFO - logerror("tms52xx: 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) - /* R Nabet : we parse commands at once. It is necessary for such commands as read. */ - process_command(data); -} - -/****************************************************************************** - process_command -- extract a byte from the FIFO and interpret it as a command -*******************************************************************************/ - -void tms52xx_device::process_command(unsigned char cmd) -{ -#ifdef DEBUG_COMMAND_DUMP - fprintf(stderr,"process_command called with parameter %02X\n",cmd); -#endif - // parse the command - switch (cmd & 0x70) - { - case 0x10 : // read byte - if (!m_talk_status) // TALKST must be clear for RDBY - { - if (m_schedule_dummy_read) - { - m_schedule_dummy_read = false; - (void)m_read_mem(1); - } - m_data_register = m_read_mem(8); // read one byte from speech ROM... - m_RDB_flag = true; - } - break; - - case 0x00: - case 0x20: // set rate (tms5220c only), otherwise NOP - if (m_variant == TMS5220_IS_5220C) - { - m_tms5220c_rate = cmd & 0x0F; - } - break; - - case 0x30: // read and branch - if (!m_talk_status) // TALKST must be clear for RB - { -#ifdef VERBOSE - logerror("tms5520: read and branch command received\n"); -#endif - m_RDB_flag = false; - m_read_and_branch(0, 0); - } - break; - - case 0x40 : // load address - if (!m_talk_status) // TALKST must be clear for LA - { - // tms5220 data sheet says that if we load only one 4-bit nibble, it won't work. - // This code does not care about this. - m_load_address(0, cmd & 0x0f); - m_schedule_dummy_read = true; - } - break; - - case 0x50: // speak - if (m_schedule_dummy_read) - { - m_schedule_dummy_read = false; - (void)m_read_mem(1); - } - m_speaking_now = true; - m_speak_external = false; - m_talk_status = true; // 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_tms5220c_rate & 0x3]; - 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++) - 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 = 0; - m_fifo_count = m_fifo_bits_taken = 0; - m_speak_external = true; - m_RDB_flag = false; - break; - - case 0x70: // reset - if (m_schedule_dummy_read) - { - m_schedule_dummy_read = false; - (void)m_read_mem(1); - } - device_reset(); - break; - } - - // update the buffer low state - update_status_and_ints(); -} - -/****************************************************************************** - parse_frame -- parse a new frame's worth of data; returns 0 if not - enough bits in buffer -*******************************************************************************/ - -void tms52xx_device::parse_frame() -{ - int indx, i, rep_flag; - - /* We actually don't care how many bits are left in the fifo here; the - * frame subpart will be processed normally, and any bits extracted 'past - * the end' of the fifo will be read as zeroes; the fifo being emptied will - * set the /BE latch which will halt speech exactly as if a stop frame had - * been encountered (instead of whatever partial frame was read); the same - * exact circuitry is used for both on the real chip, see us patent 4335277 - * sheet 16, gates 232a (decode stop frame) and 232b (decode /BE plus DDIS - * (decode disable) which is active during speak external). */ - - /* if the chip is a tms5220C, and the rate mode is set to that each frame (0x04 bit set) - * has a 2 bit rate preceding it, grab two bits here and store them as the rate; */ - if ((m_variant == TMS5220_IS_5220C) && (m_tms5220c_rate & 0x04)) - { - indx = extract_bits(2); -#ifdef DEBUG_PARSE_FRAME_DUMP - printbits(indx,2); - fprintf(stderr," "); -#endif - m_IP = reload_table[indx]; - } - else // non-5220C and 5220C in fixed rate mode - m_IP = reload_table[m_tms5220c_rate & 0x3]; - - update_status_and_ints(); - if (!m_talk_status) goto ranout; - - // 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); - fprintf(stderr," "); -#endif - update_status_and_ints(); - if (!m_talk_status) 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; - - // attempt to extract the repeat flag - rep_flag = extract_bits(1); -#ifdef DEBUG_PARSE_FRAME_DUMP - printbits(rep_flag, 1); - fprintf(stderr," "); -#endif - - // 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); - fprintf(stderr," "); -#endif - update_status_and_ints(); - if (!m_talk_status) goto ranout; - /* if this is a repeat frame, just do nothing, it will reuse the - * old coefficients */ - if (rep_flag) return; - - // extract first 4 K coefficients - 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]); - fprintf(stderr," "); -#endif - update_status_and_ints(); - if (!m_talk_status) goto ranout; - } - - // if the pitch index was zero, we only need 4 K's... - if (m_new_frame_pitch_idx == 0) - { - // and the rest of the coefficients are zeroed, but that's done in the generator code - return; - } - - // If we got here, we need the remaining 6 K's - for (i = 4; i < m_coeff->num_k; i++) - { - m_new_frame_k_idx[i] = extract_bits(m_coeff->kbits[i]); -#ifdef DEBUG_PARSE_FRAME_DUMP - printbits(m_new_frame_k_idx[i], m_coeff->kbits[i]); - fprintf(stderr," "); -#endif - update_status_and_ints(); - if (!m_talk_status) goto ranout; - } -#ifdef VERBOSE - if (m_speak_external) - logerror("tms52xx: Parsed a frame successfully in FIFO - %d bits remaining\n", (m_fifo_count*8)-(m_fifo_bits_taken)); - else - logerror("tms52xx: Parsed a frame successfully in ROM\n"); -#endif - return; - - ranout: -#ifdef DEBUG_FRAME_ERRORS - logerror("tms52xx: Ran out of bits on a parse!\n"); -#endif - return; -} - -/********************************************************************************************** - - update_status_and_ints -- check to see if the various flags should be on or off - Description of flags, and their position in the status register: - From the data sheet: - bit D0(bit 7) = 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 D1(bit 6) = 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 - eighth data byte of the stack. - bit D2(bit 5) = 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. Speech is terminated at some abnormal point and the - Speak External command execution is terminated. - -***********************************************************************************************/ - -void tms52xx_device::update_status_and_ints() -{ - // update flags and set ints if needed - update_ready_state(); - - /* BL is set if neither byte 9 nor 8 of the fifo are in use; this - * translates to having fifo_count (which ranges from 0 bytes in use to 16 - * bytes used) being less than or equal to 8. Victory/Victorba depends on this. */ - if (m_fifo_count <= 8) - { - // generate an interrupt if necessary; if /BL was inactive and is now active, set int. - if (!m_buffer_low) set_interrupt_state(1); - m_buffer_low = true; - } - else - m_buffer_low = false; - - /* 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 */ - 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 = true; - } - else - m_buffer_empty = false; - - /* 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 && m_buffer_empty) - { - // generate an interrupt: /TS was active, and is now inactive. - if (m_talk_status) - { - m_talk_status = m_speak_external = false; - set_interrupt_state(1); - } - } - /* 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 */ -} - -/****************************************************************************** - extract_bits -- extract a specific number of bits from the current input stream (FIFO or VSM) -*******************************************************************************/ - -int tms52xx_device::extract_bits(int count) -{ - int val = 0; - - if (m_speak_external) - { - // extract from FIFO - while (count--) - { - val = (val << 1) | ((m_fifo[m_fifo_head] >> m_fifo_bits_taken) & 1); - m_fifo_bits_taken++; - if (m_fifo_bits_taken >= 8) - { - m_fifo_count--; - m_fifo[m_fifo_head] = 0; // zero the newly depleted fifo head byte - m_fifo_head = (m_fifo_head + 1) % FIFO_SIZE; - m_fifo_bits_taken = 0; - update_status_and_ints(); - } - } - } - else - { - // extract from VSM (speech ROM) - val = m_read_mem(count); - } - return val; -} - -/****************************************************************************** - status_read -- read status or data from the TMS5220 -*******************************************************************************/ - -int tms52xx_device::status_read() -{ - if (m_RDB_flag) - { // if last command was read, return data register - m_RDB_flag = false; - return m_data_register; - } - else - { // read status - // clear the interrupt pin on status read - set_interrupt_state(0); -#ifdef DEBUG_PIN_READS - logerror("tms52xx: Status read: TS=%d BL=%d BE=%d\n", m_talk_status, m_buffer_low, m_buffer_empty); -#endif - - int retvalue = 0; - if (m_talk_status) retvalue |= 0x80; - if (m_buffer_low) retvalue |= 0x40; - if (m_buffer_empty) retvalue |= 0x20; - return retvalue; - } -} - -/****************************************************************************** - ready_read -- returns the ready state of the TMS5220 -*******************************************************************************/ - -inline bool tms52xx_device::ready_read() -{ -#ifdef DEBUG_PIN_READS - logerror("tms52xx: ready_read: ready pin read, io_ready is %d, fifo count is %d\n", m_io_ready, m_fifo_count); -#endif - return ((m_fifo_count < FIFO_SIZE)||(!m_speak_external)) && m_io_ready; -} - -/****************************************************************************** - int_read -- returns the interrupt state of the TMS5220 -*******************************************************************************/ - -inline int tms52xx_device::int_read() -{ -#ifdef DEBUG_PIN_READS - logerror("tms52xx: int_read: irq pin read, state is %d\n", m_irq_pin); -#endif - return m_irq_pin; -} - -/****************************************************************************** - cycles_to_ready -- returns the number of cycles until ready is asserted - NOTE: this function is deprecated and is known to be VERY inaccurate. - Use at your own peril! -*******************************************************************************/ - -int tms52xx_device::cycles_to_ready() -{ - int answer; - - if (ready_read()) answer = 0; - else - { - int val; - int samples_per_frame = (m_subc_reload!=0)?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_PC * (3-m_subc_reload)) + (((m_subc_reload!=0)? 38:25) * m_IP)); - answer = samples_per_frame - current_sample + 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 (m_fifo_bits_taken < 4) - { - // read energy - val = (m_fifo[m_fifo_head] >> m_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 += (m_subc_reload!=0)?200:304; - // 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; -} - -/****************************************************************************** - True timing -*******************************************************************************/ - -void tms52xx_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) -{ - if (param) - { - switch (m_rs_ws) - { - case 0x02: - // Write - // bring up to date first -#ifdef DEBUG_IO_READY - logerror("tms52xx: Serviced write: %02x\n", tms->write_latch); - //fprintf(stderr, "Processed write data: %02X\n", tms->write_latch); -#endif - m_stream->update(); - data_write(m_write_latch); - break; - case 0x01: - // Read - // bring up to date first - m_stream->update(); - m_read_latch = status_read(); - break; - case 0x03: - // High Impedance - case 0x00: - // illegal - break; - } - } - m_io_ready = param; - update_ready_state(); -} - -/***************************************************** - /RS line write handler -*****************************************************/ - -WRITE_LINE_MEMBER( tms52xx_device::rsq_w ) -{ - UINT8 new_val; - - m_true_timing = true; - state &= 0x01; -#ifdef DEBUG_RS_WS - logerror("tms52xx: /RS written with data: %d\n", state); -#endif - new_val = (m_rs_ws & 0x01) | (state<<1); - if (new_val != m_rs_ws) - { - m_rs_ws = new_val; - if (new_val == 0) - { - if (m_variant == TMS5220_IS_5220C) - reset(); -#ifdef DEBUG_RS_WS - else - // illegal - logerror("tms52xx: illegal line setting /RS=0 and /WS=0\n"); -#endif - return; - } - else if (new_val == 3) - { - // high impedance - m_read_latch = 0xff; - return; - } - if (state) - { - // low to high - } - else - { - // high to low - schedule ready cycle -#ifdef DEBUG_RS_WS - logerror("tms52xx: 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; - 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_ready_timer->adjust(attotime::from_hz(clock()/16)); - } - } -} - -/***************************************************** - /WS line write handler -*****************************************************/ - -WRITE_LINE_MEMBER( tms52xx_device::wsq_w ) -{ - UINT8 new_val; - - m_true_timing = true; - state &= 0x01; -#ifdef DEBUG_RS_WS - logerror("tms52xx: /WS written with data: %d\n", state); -#endif - new_val = (m_rs_ws & 0x02) | (state<<0); - if (new_val != m_rs_ws) - { - m_rs_ws = new_val; - if (new_val == 0) - { - if (m_variant == TMS5220_IS_5220C) - reset(); -#ifdef DEBUG_RS_WS - else - // illegal - logerror("tms52xx: illegal line setting /RS=0 and /WS=0\n"); -#endif - return; - } - else if ( new_val == 3) - { - // high impedance - m_read_latch = 0xff; - return; - } - if (state) - { - // low to high - } - else - { - // high to low - schedule ready cycle -#ifdef DEBUG_RS_WS - logerror("tms52xx: 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; - update_ready_state(); - /* Now comes the complicated part: 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: - * SPK: ? cycles - * SPKEXT: ? cycles - * RDBY: between 60 and 140 cycles - * RB: ? cycles (80?) - * RST: between 60 and 140 cycles - * SET RATE (5220C only): ? cycles (probably ~16) */ - - // TODO: actually HANDLE the timing differences! currently just assuming always 16 cycles - m_ready_timer->adjust(attotime::from_hz(clock()/16)); - } - } -} - -/***************************************************************************** - write -- write data to the sound chip -*******************************************************************************/ - -WRITE8_MEMBER( tms52xx_device::write ) -{ -#ifdef DEBUG_RS_WS - logerror("tms52xx: write data %02x\n", data); -#endif - if (!m_true_timing) - { - // bring up to date first - m_stream->update(); - data_write(data); - } - else - { - // actually in a write? -#ifdef DEBUG_RS_WS - if (!(m_rs_ws == 0x02)) - logerror("tms52xx: write data written outside ws, status: %02x!\n", m_rs_ws); -#endif - m_write_latch = data; - } -} - -/****************************************************************************** - read -- read status or data from the sound chip -*******************************************************************************/ - -READ8_MEMBER( tms52xx_device::read ) -{ - if (!m_true_timing) - { - // bring up-to-date first - m_stream->update(); - return status_read(); - } - else - { - // actually in a read? - if (m_rs_ws == 0x01) - return m_read_latch; -#ifdef DEBUG_RS_WS - else - logerror("tms52xx: data read outside rs!\n"); -#endif - return 0xff; - } -} - -/******************************************************************************* - ready -- return the not ready status from the sound chip -*******************************************************************************/ - -READ_LINE_MEMBER( tms52xx_device::readyq ) -{ - // bring up-to-date first - m_stream->update(); - return !ready_read(); -} - - -/****************************************************************************** - time_to_ready -- return the time in seconds until the - ready line is asserted -*******************************************************************************/ - -double tms52xx_device::time_to_ready() -{ - double cycles; - - // bring up-to-date - m_stream->update(); - cycles = cycles_to_ready(); - return cycles * 80.0 / m_clock; -} - -/****************************************************************************** - intq -- return the interrupt status from the sound chip -******************************************************************************/ - -READ_LINE_MEMBER( tms52xx_device::intq ) -{ - // bring up-to-date first - m_stream->update(); - return !int_read(); -} - -void tms52xx_device::register_for_save_states() -{ - save_item(NAME(m_fifo)); - save_item(NAME(m_fifo_head)); - save_item(NAME(m_fifo_tail)); - 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_buffer_low)); - save_item(NAME(m_buffer_empty)); - save_item(NAME(m_irq_pin)); - save_item(NAME(m_ready_pin)); - - save_item(NAME(m_OLDE)); - save_item(NAME(m_OLDP)); - - save_item(NAME(m_new_frame_energy_idx)); - save_item(NAME(m_new_frame_pitch_idx)); - save_item(NAME(m_new_frame_k_idx)); -#ifdef PERFECT_INTERPOLATION_HACK - save_item(NAME(m_old_frame_energy_idx)); - save_item(NAME(m_old_frame_pitch_idx)); - save_item(NAME(m_old_frame_k_idx)); -#endif - save_item(NAME(m_current_energy)); - 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)); - save_item(NAME(m_subc_reload)); - save_item(NAME(m_PC)); - save_item(NAME(m_IP)); - save_item(NAME(m_inhibit)); - save_item(NAME(m_tms5220c_rate)); - save_item(NAME(m_pitch_count)); - - save_item(NAME(m_u)); - save_item(NAME(m_x)); - - save_item(NAME(m_RNG)); - save_item(NAME(m_excitation_data)); - - save_item(NAME(m_schedule_dummy_read)); - save_item(NAME(m_data_register)); - save_item(NAME(m_RDB_flag)); - save_item(NAME(m_digital_select)); - - save_item(NAME(m_io_ready)); -} - - -const device_type TMS5220N = &device_creator<tms5220n_device>; -const device_type TMS5220CN = &device_creator<tms5220cn_device>; -const device_type TMC0285N = &device_creator<tmc0285n_device>; -const device_type TMS5200N = &device_creator<tms5200n_device>; diff --git a/src/emu/sound/tms5220.h b/src/emu/sound/tms5220.h index 59859153776..4979d284d08 100644 --- a/src/emu/sound/tms5220.h +++ b/src/emu/sound/tms5220.h @@ -3,7 +3,11 @@ #ifndef __TMS5220_H__ #define __TMS5220_H__ -#include "devlegcy.h" +#include "emu.h" +#include "machine/spchrom.h" + +/* HACK: if defined, uses impossibly perfect 'straight line' interpolation */ +#undef PERFECT_INTERPOLATION_HACK #define FIFO_SIZE 16 @@ -11,35 +15,16 @@ /* usually 640000 for 8000 Hz sample rate or */ /* usually 800000 for 10000 Hz sample rate. */ -struct tms5220_interface -{ - devcb_write_line irq_func; /* IRQ callback function, active low, i.e. state=0 */ - devcb_write_line readyq_func; /* Ready callback function, active low, i.e. state=0 */ - - int (*read)(device_t *device, int count); /* speech ROM read callback */ - void (*load_address)(device_t *device, int data); /* speech ROM load address callback */ - void (*read_and_branch)(device_t *device); /* speech ROM read and branch callback */ -}; - -/* Control lines - once written to will switch interface into - * "true" timing behaviour. - */ - -/* all lines with suffix q are active low! */ - -WRITE_LINE_DEVICE_HANDLER( tms5220_rsq_w ); -WRITE_LINE_DEVICE_HANDLER( tms5220_wsq_w ); - -DECLARE_WRITE8_DEVICE_HANDLER( tms5220_data_w ); -DECLARE_READ8_DEVICE_HANDLER( tms5220_status_r ); +/* IRQ callback function, active low, i.e. state=0 */ +#define MCFG_TMS52XX_IRQ_HANDLER(_devcb) \ + devcb = &tms5220_device::set_irq_handler(*device, DEVCB2_##_devcb); -READ_LINE_DEVICE_HANDLER( tms5220_readyq_r ); -READ_LINE_DEVICE_HANDLER( tms5220_intq_r ); +/* Ready callback function, active low, i.e. state=0 */ +#define MCFG_TMS52XX_READYQ_HANDLER(_devcb) \ + devcb = &tms5220_device::set_readyq_handler(*device, DEVCB2_##_devcb); - -double tms5220_time_to_ready(device_t *device); - -void tms5220_set_frequency(device_t *device, int frequency); +#define MCFG_TMS52XX_SPEECHROM(_tag) \ + tms5220_device::set_speechrom_tag(*device, _tag); class tms5220_device : public device_t, public device_sound_interface @@ -47,267 +32,210 @@ class tms5220_device : public device_t, public: tms5220_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); tms5220_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock); - ~tms5220_device() { global_free(m_token); } - // access to legacy token - void *token() const { assert(m_token != NULL); return m_token; } -protected: - // device-level overrides - virtual void device_config_complete(); - virtual void device_start(); - virtual void device_reset(); + // static configuration helpers + template<class _Object> static devcb2_base &set_irq_handler(device_t &device, _Object object) { return downcast<tms5220_device &>(device).m_irq_handler.set_callback(object); } + template<class _Object> static devcb2_base &set_readyq_handler(device_t &device, _Object object) { return downcast<tms5220_device &>(device).m_readyq_handler.set_callback(object); } + static void set_speechrom_tag(device_t &device, const char *_tag) { downcast<tms5220_device &>(device).m_speechrom_tag = _tag; } - // sound stream update overrides - virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples); -private: - // internal state - void *m_token; -}; + /* Control lines - once written to will switch interface into + * "true" timing behaviour. + */ -extern const device_type TMS5220; + /* all lines with suffix q are active low! */ -class tms5220c_device : public tms5220_device -{ -public: - tms5220c_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); -protected: - // device-level overrides - virtual void device_start(); - - // sound stream update overrides - virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples); -}; + WRITE_LINE_MEMBER( rsq_w ); + WRITE_LINE_MEMBER( wsq_w ); -extern const device_type TMS5220C; + DECLARE_WRITE8_MEMBER( data_w ); + DECLARE_READ8_MEMBER( status_r ); -class tmc0285_device : public tms5220_device -{ -public: - tmc0285_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); -protected: - // device-level overrides - virtual void device_start(); + READ_LINE_MEMBER( readyq_r ); + READ_LINE_MEMBER( intq_r ); - // sound stream update overrides - virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples); -}; + double time_to_ready(); -extern const device_type TMC0285; + void set_frequency(int frequency); -class tms5200_device : public tms5220_device -{ -public: - tms5200_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); protected: // device-level overrides + virtual void device_config_complete(); virtual void device_start(); + virtual void device_reset(); - // sound stream update overrides - virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples); -}; - -extern const device_type TMS5200; - - - -/*************************************************************************** - New class implementation - Michael Zapf, June 2012 -***************************************************************************/ - -extern const device_type TMS5220N; -extern const device_type TMS5220CN; -extern const device_type TMC0285N; -extern const device_type TMS5200N; - - -struct tms52xx_config -{ - devcb_write_line irq_func; // IRQ callback function, active low, i.e. state=0 (TODO: change to ASSERT/CLEAR) - devcb_write_line readyq_func; // Ready callback function, active low, i.e. state=0 - - devcb_read8 read_mem; // speech ROM read callback - devcb_write8 load_address; // speech ROM load address callback - devcb_write8 read_and_branch; // speech ROM read and branch callback - -}; - -// Change back from 5220n to 5220 when all client drivers will be converted -class tms52xx_device : public device_t, public device_sound_interface -{ -public: - tms52xx_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, const struct tms5100_coeffs* coeffs, const int var, device_t *owner, UINT32 clock); - virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples); virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr); - DECLARE_WRITE_LINE_MEMBER( rsq_w ); - DECLARE_WRITE_LINE_MEMBER( wsq_w ); - DECLARE_WRITE8_MEMBER( write ); - DECLARE_READ8_MEMBER( read ); - DECLARE_READ_LINE_MEMBER( readyq ); - DECLARE_READ_LINE_MEMBER( intq ); - - double time_to_ready(); + // sound stream update overrides + virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples); -protected: - virtual void device_start(); - virtual void device_reset(); + void set_variant(int variant); private: - // Methods - void set_interrupt_state(int state); void register_for_save_states(); - void update_ready_state(); - void set_frequency(int frequency); - void process(INT16 *buffer, unsigned int size); void data_write(int data); void update_status_and_ints(); - void parse_frame(); - int extract_bits(int count); int status_read(); + int ready_read(); int cycles_to_ready(); + int int_read(); + void process(INT16 *buffer, unsigned int size); INT32 lattice_filter(); void process_command(unsigned char cmd); + void parse_frame(); + void set_interrupt_state(int state); + void update_ready_state(); - inline bool ready_read(); - inline int int_read(); + // internal state - // coefficient tables - const int m_variant; // Variant of the 5xxx - see tms5110r.h + /* coefficient tables */ + int m_variant; /* Variant of the 5xxx - see tms5110r.h */ - // coefficient tables + /* coefficient tables */ const struct tms5100_coeffs *m_coeff; - // callbacks - devcb_resolved_write_line m_irq_func; - devcb_resolved_write_line m_readyq_func; - devcb_resolved_read8 m_read_mem; - devcb_resolved_write8 m_load_address; - devcb_resolved_write8 m_read_and_branch; - - // these contain data that describes the 128-bit data FIFO - UINT8 m_fifo[FIFO_SIZE]; - UINT8 m_fifo_head; - UINT8 m_fifo_tail; - int m_fifo_count; - int m_fifo_bits_taken; - - // these contain global status bits - bool 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. - bool m_speak_external; // If 1, DDIS is 1, i.e. Speak External command in progress, writes go to FIFO. - bool 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. - bool m_buffer_low; // If 1, FIFO has less than 8 bytes in it - bool m_buffer_empty; // If 1, FIFO is empty - int m_irq_pin; // state of the IRQ pin (output) - int m_ready_pin; // state of the READY pin (output) - - // these contain data describing the current and previous voice frames -#define M_OLD_FRAME_SILENCE_FLAG m_OLDE // 1 if E=0, 0 otherwise. -#define M_OLD_FRAME_UNVOICED_FLAG m_OLDP // 1 if P=0 (unvoiced), 0 if voiced - bool m_OLDE; - bool m_OLDP; - -#define M_NEW_FRAME_STOP_FLAG (m_new_frame_energy_idx == 0xF) // 1 if this is a stop (Energy = 0xF) frame -#define M_NEW_FRAME_SILENCE_FLAG (m_new_frame_energy_idx == 0) // ditto as above -#define M_NEW_FRAME_UNVOICED_FLAG (m_new_frame_pitch_idx == 0) // ditto as above - int m_new_frame_energy_idx; - int m_new_frame_pitch_idx; - int m_new_frame_k_idx[10]; - - // these are all used to contain the current state of the sound generation + /* these contain data that describes the 128-bit data FIFO */ + UINT8 m_fifo[FIFO_SIZE]; + UINT8 m_fifo_head; + UINT8 m_fifo_tail; + UINT8 m_fifo_count; + UINT8 m_fifo_bits_taken; + + + /* 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_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) */ + UINT8 m_ready_pin; /* state of the READY pin (output) */ + + /* these contain data describing the current and previous voice frames */ +#define OLD_FRAME_SILENCE_FLAG m_OLDE // 1 if E=0, 0 otherwise. +#define OLD_FRAME_UNVOICED_FLAG m_OLDP // 1 if P=0 (unvoiced), 0 if voiced + UINT8 m_OLDE; + UINT8 m_OLDP; + +#define NEW_FRAME_STOP_FLAG (m_new_frame_energy_idx == 0xF) // 1 if this is a stop (Energy = 0xF) frame +#define NEW_FRAME_SILENCE_FLAG (m_new_frame_energy_idx == 0) // ditto as above +#define NEW_FRAME_UNVOICED_FLAG (m_new_frame_pitch_idx == 0) // ditto as above + UINT8 m_new_frame_energy_idx; + UINT8 m_new_frame_pitch_idx; + UINT8 m_new_frame_k_idx[10]; + + + /* these are all used to contain the current state of the sound generation */ #ifndef PERFECT_INTERPOLATION_HACK - INT16 m_current_energy; - INT16 m_current_pitch; - INT16 m_current_k[10]; + 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]; + INT16 m_target_energy; + INT16 m_target_pitch; + INT16 m_target_k[10]; #else - int m_old_frame_energy_idx; - int m_old_frame_pitch_idx; - int m_old_frame_k_idx[10]; + UINT8 m_old_frame_energy_idx; + UINT8 m_old_frame_pitch_idx; + UINT8 m_old_frame_k_idx[10]; - INT32 m_current_energy; - INT32 m_current_pitch; - INT32 m_current_k[10]; + 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]; + 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 - int 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 - int m_subc_reload; // contains 1 for normal speech, 0 when SPKSLOW is active - int 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 - int m_IP; // the current interpolation period - bool m_inhibit; // If 1, interpolation is inhibited until the DIV1 period - UINT8 m_tms5220c_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 - INT16 m_excitation_data; - - // R Nabet : These have been added to emulate speech Roms - bool m_schedule_dummy_read; // set after each load address, so that next read operation is preceded by a dummy read - UINT8 m_data_register; // data register, used by read command - bool m_RDB_flag; // whether we should read data register or status register - - // io_ready: page 3 of the datasheet specifies that READY will be asserted until - // data is available or processed by the system. - int m_io_ready; - - // flag for "true" timing involving rs/ws - bool m_true_timing; - - // rsws - state, rs bit 1, ws bit 0 - int m_rs_ws; - UINT8 m_read_latch; - UINT8 m_write_latch; - - // 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, and has the - // funny clipping/clamping logic, while the digital pin gives full 12? bit - // resolution of the output data. - // TODO: add a way to set/reset this other than the FORCE_DIGITAL define - bool m_digital_select; - - sound_stream *m_stream; - - emu_timer *m_ready_timer; + UINT16 m_previous_energy; /* needed for lattice filter to match patent */ + + 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 */ + UINT8 m_IP; /* the current interpolation period */ + UINT8 m_inhibit; /* If 1, interpolation is inhibited until the DIV1 period */ + UINT8 m_tms5220c_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 */ + INT16 m_excitation_data; + + /* R Nabet : These have been added to emulate speech Roms */ + UINT8 m_schedule_dummy_read; /* set after each load address, so that next read operation is preceded by a dummy read */ + UINT8 m_data_register; /* data register, used by read command */ + UINT8 m_RDB_flag; /* whether we should read data register or status register */ + + /* io_ready: page 3 of the datasheet specifies that READY will be asserted until + * data is available or processed by the system. + */ + UINT8 m_io_ready; + + /* flag for "true" timing involving rs/ws */ + UINT8 m_true_timing; + + /* rsws - state, rs bit 1, ws bit 0 */ + UINT8 m_rs_ws; + UINT8 m_read_latch; + UINT8 m_write_latch; + + /* 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, and has the + funny clipping/clamping logic, while the digital pin gives full 10 bit + resolution of the output data. + TODO: add a way to set/reset this other than the FORCE_DIGITAL define + */ + UINT8 m_digital_select; + + sound_stream *m_stream; + int m_clock; + emu_timer *m_timer_io_ready; + + /* callbacks */ + devcb2_write_line m_irq_handler; + devcb2_write_line m_readyq_handler; + const char *m_speechrom_tag; + speechrom_device *m_speechrom; }; -class tms5220n_device : public tms52xx_device -{ -public: - tms5220n_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); -}; +extern const device_type TMS5220; -class tms5220cn_device : public tms52xx_device +class tms5220c_device : public tms5220_device { public: - tms5220cn_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + tms5220c_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); +protected: + // device-level overrides + virtual void device_start(); }; -class tmc0285n_device : public tms52xx_device +extern const device_type TMS5220C; + +class tmc0285_device : public tms5220_device { public: - tmc0285n_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + tmc0285_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); +protected: + // device-level overrides + virtual void device_start(); }; -class tms5200n_device : public tms52xx_device +extern const device_type TMC0285; + +class tms5200_device : public tms5220_device { public: - tms5200n_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + tms5200_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); +protected: + // device-level overrides + virtual void device_start(); }; +extern const device_type TMS5200; -#endif /* __TMS5220_H__ */ +#endif |