summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/sound/s14001a.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/emu/sound/s14001a.c')
-rw-r--r--src/emu/sound/s14001a.c548
1 files changed, 548 insertions, 0 deletions
diff --git a/src/emu/sound/s14001a.c b/src/emu/sound/s14001a.c
new file mode 100644
index 00000000000..051b12489aa
--- /dev/null
+++ b/src/emu/sound/s14001a.c
@@ -0,0 +1,548 @@
+/*
+
+ TSI S14001A emulator v1.11
+ By Jonathan Gevaryahu ("Lord Nightmare") with help from Kevin Horton ("kevtris")
+ MAME conversion and integration by R. Belmont
+
+ Copyright (c) 2007 Jonathan Gevaryahu.
+
+ Version history:
+ 0.8 initial version - LN
+ 0.9 MAME conversion, glue code added - R. Belmont
+ 1.0 partly fixed stream update - LN (0.111u4)
+ 1.01 fixed clipping problem - LN (0.111u5)
+ 1.1 add VSU-1000 features, fully fixed stream update by fixing word latching - LN (0.111u6)
+ 1.11 fix signedness of output, pre-multiply, fixes clicking on VSU-1000 volume change - LN (0.111u7)
+
+ TODO:
+ * increase accuracy of internal S14001A 'filter' for both driven and undriven cycles (its not terribly inaccurate for undriven cycles, but the dc sliding of driven cycles is not emulated)
+ * add option for and attach Frank P.'s emulation of the Analog external filter from the vsu-1000 using the discrete core.
+*/
+
+/* state map:
+
+ * state machine 1: odd/even clock state
+ * on even clocks, audio output is floating, /romen is low so rom data bus is driven, input is latched?
+ * on odd clocks, audio output is driven, /romen is high, state machine 2 is clocked
+ * *****
+ * state machine 2: decoder state
+ * NOTE: holding the start line high forces the state machine 2 state to go to or remain in state 1!
+ * state 0(Idle): Idle (no sample rom bus activity, output at 0), next state is 0(Idle)
+
+ * state 1(GetHiWord):
+ * grab byte at (wordinput<<1) -> register_WH
+ * reset output DAC accumulator to 0x8 <- ???
+ * reset OldValHi to 1
+ * reset OldValLo to 0
+ * next state is 2(GetLoWord) UNLESS the PLAY line is still high, in which case the state remains at 1
+
+ * state 2(GetLoWord):
+ * grab byte at (wordinput<<1)+1 -> register_WL
+ * next state is 3(GetHiPhon)
+
+ * state 3(GetHiPhon):
+ * grab byte at ((register_WH<<8) + (register_WL))>>4 -> phoneaddress
+ * next state is 4(GetLoPhon)
+
+ * state 4(GetLoPhon):
+ * grab byte at (((register_WH<<8) + (register_WL))>>4)+1 -> playparams
+ * set phonepos register to 0
+ * set oddphone register to 0
+ * next state is 5(PlayForward1)
+ * playparams:
+ * 7 6 5 4 3 2 1 0
+ * G G = LastPhone
+ * B B = PlayMode
+ * Y Y = Silenceflag
+ * S S S S = Length count load value
+ * R R R = Repeat count reload value (upon carry/overflow of 3 bits)
+ * load the repeat counter with the bits 'R R 0'
+ * load the length counter with the bits 'S S S 0'
+ * NOTE: though only three bits of the length counter load value are controllable, there is a fourth lower bit which is assumed 0 on start and controls the direction of playback, i.e. forwards or backwards within a phone.
+ * NOTE: though only two bits of the repeat counter reload value are controllable, there is a third bit which is loaded to 0 on phoneme start, and this hidden low-order bit of the counter itself is what controls whether the output is forced to silence in mirrored mode. the 'carry' from the highest bit of the 3 bit counter is what increments the address pointer for pointing to the next phoneme in mirrored mode
+
+
+ * shift register diagram:
+ * F E D C B A 9 8 7 6 5 4 3 2 1 0
+ * <new byte here>
+ * C C C = Current delta sample read point
+ * O O O = Old delta sample read point
+ * I *OPTIMIZED OUT* the shift register by making use of the fact that the device reads each rom byte 4 times
+
+ * state 5(PlayForward1):
+ * grab byte at (((phoneaddress<<8)+(oddphone*8))+(phonepos>>2)) -> PlayRegister high end, bits F to 8
+ * if Playmode is mirrored, set OldValHi and OldValLo to 1 and 0 respectively, otherwise leave them with whatever was in them before.
+ * Put OldValHi in bit 7 of PlayRegister
+ * Put OldValLo in bit 6 of PlayRegister
+ * Get new OldValHi from bit 9
+ * Get new OldValLo from bit 8
+ * feed current delta (bits 9 and 8) and olddelta (bits 7 and 6) to delta demodulator table, delta demodulator table applies a delta to the accumulator, accumulator goes to enable/disable latch which Silenceflag enables or disables (forces output to 0x8 on disable), then to DAC to output.
+ * next state: state 6(PlayForward2)
+
+ * state 6(PlayForward2):
+ * grab byte at (((phoneaddress<<8)+oddphone)+(phonepos>>2)) -> PlayRegister bits D to 6.
+ * Put OldValHi in bit 7 of PlayRegister\____already done by above operation
+ * Put OldValLo in bit 6 of PlayRegister/
+ * Get new OldValHi from bit 9
+ * Get new OldValLo from bit 8
+ * feed current delta (bits 9 and 8) and olddelta (bits 7 and 6) to delta demodulator table, delta demodulator table applies a delta to the accumulator, accumulator goes to enable/disable latch which Silenceflag enables or disables (forces output to 0x8 on disable), then to DAC to output.
+ * next state: state 7(PlayForward3)
+
+ * state 7(PlayForward3):
+ * grab byte at (((phoneaddress<<8)+oddphone)+(phonepos>>2)) -> PlayRegister bits B to 4.
+ * Put OldValHi in bit 7 of PlayRegister\____already done by above operation
+ * Put OldValLo in bit 6 of PlayRegister/
+ * Get new OldValHi from bit 9
+ * Get new OldValLo from bit 8
+ * feed current delta (bits 9 and 8) and olddelta (bits 7 and 6) to delta demodulator table, delta demodulator table applies a delta to the accumulator, accumulator goes to enable/disable latch which Silenceflag enables or disables (forces output to 0x8 on disable), then to DAC to output.
+ * next state: state 8(PlayForward4)
+
+ * state 8(PlayForward4):
+ * grab byte at (((phoneaddress<<8)+oddphone)+(phonepos>>2)) -> PlayRegister bits 9 to 2.
+ * Put OldValHi in bit 7 of PlayRegister\____already done by above operation
+ * Put OldValLo in bit 6 of PlayRegister/
+ * Get new OldValHi from bit 9
+ * Get new OldValLo from bit 8
+ * feed current delta (bits 9 and 8) and olddelta (bits 7 and 6) to delta demodulator table, delta demodulator table applies a delta to the accumulator, accumulator goes to enable/disable latch which Silenceflag enables or disables (forces output to 0x8 on disable), then to DAC to output.
+ * Call function: increment address
+
+ * next state: depends on playparams:
+ * if we're in mirrored mode, next will be LoadAndPlayBackward1
+
+ * state 9(LoadAndPlayBackward1)
+ * state 10(PlayBackward2)
+ * state 11(PlayBackward3)
+ * state 12(PlayBackward4)
+*/
+
+/* increment address function:
+ * increment repeat counter
+ if repeat counter produces a carry, do two things:
+ 1. if mirrored mode is ON, increment oddphone. if oddphone carries out (i.e. if it was 1), increment phoneaddress and zero oddphone
+ 2. increment lengthcounter. if lengthcounter carries out, we're done this phone.
+ * increment output counter
+ * if mirrored mode is on, output direction is
+ * if mirrored mode is OFF, increment oddphone. if not, don't touch it here. if oddphone was 1 before the increment, increment phoneaddress and set oddphone to 0
+ *
+ */
+
+#include <math.h>
+#include "sndintrf.h"
+#include "s14001a.h"
+#include "streams.h"
+
+typedef struct
+{
+ int index;
+ sound_stream * stream;
+
+ UINT8 WordInput; // value on word input bus
+ UINT8 LatchedWord; // value latched from input bus
+ UINT16 SyllableAddress; // address read from word table
+ UINT16 PhoneAddress; // starting/current phone address from syllable table
+ UINT8 PlayParams; // playback parameters from syllable table
+ UINT8 PhoneOffset; // offset within phone
+ UINT8 LengthCounter; // 4-bit counter which holds the inverted length of the word in phones, leftshifted by 1
+ UINT8 RepeatCounter; // 3-bit counter which holds the inverted number of repeats per phone, leftshifted by 1
+ UINT8 OutputCounter; // 2-bit counter to determine forward/backward and output/silence state.
+ UINT8 machineState; // chip state machine state
+ UINT8 nextstate; // chip state machine's new state
+ UINT8 laststate; // chip state machine's previous state, needed for mirror increment masking
+ UINT8 resetState; // reset line state
+ UINT8 oddeven; // odd versus even cycle toggle
+ UINT8 GlobalSilenceState; // same as above but for silent syllables instead of silent portions of mirrored syllables
+ UINT8 OldDelta; // 2-bit old delta value
+ UINT8 DACOutput; // 4-bit DAC Accumulator/output
+ UINT8 audioout; // filtered audio output
+ UINT8 *SpeechRom; // array to hold rom contents, mame will not need this, will use a pointer
+ UINT8 filtervals[8];
+ UINT8 VSU1000_amp; // amplitude setting on VSU-1000 board
+ UINT16 VSU1000_freq; // frequency setting on VSU-1000 board
+ UINT16 VSU1000_counter; // counter for freq divider
+} S14001AChip;
+
+//#define DEBUGSTATE
+
+#define SILENCE 0x77 // value output when silent
+
+#define LASTSYLLABLE ((chip->PlayParams & 0x80)>>7)
+#define MIRRORMODE ((chip->PlayParams & 0x40)>>6)
+#define SILENCEFLAG ((chip->PlayParams & 0x20)>>5)
+#define LENGTHCOUNT ((chip->PlayParams & 0x1C)>>1) // remember: its 4 bits and the bottom bit is always zero!
+#define REPEATCOUNT ((chip->PlayParams<<1)&0x6) // remember: its 3 bits and the bottom bit is always zero!
+#define LOCALSILENCESTATE ((chip->OutputCounter & 0x2) && (MIRRORMODE)) // 1 when silent output, 0 when DAC output.
+
+static INT8 DeltaTable[4][4] =
+{
+ { 0xCD, 0xCD, 0xEF, 0xEF, },
+ { 0xEF, 0xEF, 0x00, 0x00, },
+ { 0x00, 0x00, 0x11, 0x11, },
+ { 0x11, 0x11, 0x33, 0x33 },
+};
+
+static UINT8 audiofilter(S14001AChip *chip) /* rewrite me to better match the real filter! */
+{
+ UINT16 temp1, temp2 = 0;
+ /* crappy averaging filter! */
+ for (temp1 = 0; temp1 < 8; temp1++) { temp2 += chip->filtervals[temp1]; }
+ temp2 >>= 3;
+ return temp2;
+}
+
+static void shiftIntoFilter(S14001AChip *chip, UINT8 inputvalue)
+{
+ UINT8 temp1;
+ for (temp1 = 7; temp1 > 0; temp1--)
+ {
+ chip->filtervals[temp1] = chip->filtervals[(temp1 - 1)];
+ }
+ chip->filtervals[0] = inputvalue;
+
+}
+
+static void PostPhoneme(S14001AChip *chip) /* figure out what the heck to do after playing a phoneme */
+{
+#ifdef DEBUGSTATE
+ fprintf(stderr,"0: entered PostPhoneme\n");
+#endif
+ chip->RepeatCounter++; // increment the repeat counter
+ chip->OutputCounter++; // increment the output counter
+ if (MIRRORMODE) // if mirroring is enabled
+ {
+#ifdef DEBUGSTATE
+ fprintf(stderr,"1: MIRRORMODE was on\n");
+#endif
+ if (chip->RepeatCounter == 0x8) // exceeded 3 bits?
+ {
+#ifdef DEBUGSTATE
+ fprintf(stderr,"2: RepeatCounter was == 8\n");
+#endif
+ // reset repeat counter, increment length counter
+ // but first check if lowest bit is set
+ chip->RepeatCounter = REPEATCOUNT; // reload repeat counter with reload value
+ if (chip->LengthCounter & 0x1) // if low bit is 1 (will carry after increment)
+ {
+#ifdef DEBUGSTATE
+ fprintf(stderr,"3: LengthCounter's low bit was 1\n");
+#endif
+ chip->PhoneAddress+=8; // go to next phone in this syllable
+ }
+ chip->LengthCounter++;
+ if (chip->LengthCounter == 0x10) // if Length counter carried out of 4 bits
+ {
+#ifdef DEBUGSTATE
+ fprintf(stderr,"3: LengthCounter overflowed\n");
+#endif
+ chip->SyllableAddress += 2; // go to next syllable
+ chip->nextstate = LASTSYLLABLE ? 13 : 3; // if we're on the last syllable, go to end state, otherwise go and load the next syllable.
+ }
+ else
+ {
+#ifdef DEBUGSTATE
+ fprintf(stderr,"3: LengthCounter's low bit wasn't 1 and it didn't overflow\n");
+#endif
+ chip->PhoneOffset = (chip->OutputCounter&1) ? 7 : 0;
+ chip->nextstate = (chip->OutputCounter&1) ? 9 : 5;
+ }
+ }
+ else // repeatcounter did NOT carry out of 3 bits so leave length counter alone
+ {
+#ifdef DEBUGSTATE
+ fprintf(stderr,"2: RepeatCounter is less than 8 (its actually %d)\n", chip->RepeatCounter);
+#endif
+ chip->PhoneOffset = (chip->OutputCounter&1) ? 7 : 0;
+ chip->nextstate = (chip->OutputCounter&1) ? 9 : 5;
+ }
+ }
+ else // if mirroring is NOT enabled
+ {
+#ifdef DEBUGSTATE
+ fprintf(stderr,"1: MIRRORMODE was off\n");
+#endif
+ if (chip->RepeatCounter == 0x8) // exceeded 3 bits?
+ {
+#ifdef DEBUGSTATE
+ fprintf(stderr,"2: RepeatCounter was == 8\n");
+#endif
+ // reset repeat counter, increment length counter
+ chip->RepeatCounter = REPEATCOUNT; // reload repeat counter with reload value
+ chip->LengthCounter++;
+ if (chip->LengthCounter == 0x10) // if Length counter carried out of 4 bits
+ {
+#ifdef DEBUGSTATE
+ fprintf(stderr,"3: LengthCounter overflowed\n");
+#endif
+ chip->SyllableAddress += 2; // go to next syllable
+ chip->nextstate = LASTSYLLABLE ? 13 : 3; // if we're on the last syllable, go to end state, otherwise go and load the next syllable.
+#ifdef DEBUGSTATE
+ fprintf(stderr,"nextstate is now %d\n", chip->nextstate); // see line below, same reason.
+#endif
+ return; // need a return here so we don't hit the 'nextstate = 5' line below
+ }
+ }
+ chip->PhoneAddress += 8; // regardless of counters, the phone address always increments in non-mirrored mode
+ chip->PhoneOffset = 0;
+ chip->nextstate = 5;
+ }
+#ifdef DEBUGSTATE
+ fprintf(stderr,"nextstate is now %d\n", chip->nextstate);
+#endif
+}
+
+void s14001a_clock(S14001AChip *chip) /* called once per clock */
+{
+ UINT8 CurDelta; // Current delta
+
+ /* on even clocks, audio output is floating, /romen is low so rom data bus is driven, input is latched?
+ * on odd clocks, audio output is driven, /romen is high, state machine 2 is clocked*/
+ chip->oddeven = !(chip->oddeven); // invert the clock
+ if (chip->oddeven == 0) // even clock
+ {
+ chip->audioout = audiofilter(chip); // function to handle output filtering by internal capacitance based on clock speed and such
+ shiftIntoFilter(chip, chip->audioout); // shift over all the filter outputs and stick in audioout
+ }
+ else // odd clock
+ {
+ // fix dac output between samples. theoretically this might be unnecessary but it would require some messy logic in state 5 on the first sample load.
+ if (chip->GlobalSilenceState || LOCALSILENCESTATE)
+ {
+ chip->DACOutput = SILENCE;
+ chip->OldDelta = 2;
+ }
+ chip->audioout = (chip->GlobalSilenceState || LOCALSILENCESTATE) ? SILENCE : chip->DACOutput; // when either silence state is 1, output silence.
+ shiftIntoFilter(chip, chip->audioout); // shift over all the filter outputs and stick in audioout
+ switch(chip->machineState) // HUUUUUGE switch statement
+ {
+ case 0: // idle state
+ chip->nextstate = 0;
+ break;
+ case 1: // read starting syllable high byte from word table
+ chip->SyllableAddress = 0; // clear syllable address
+ chip->SyllableAddress |= chip->SpeechRom[(chip->LatchedWord<<1)]<<4;
+ chip->nextstate = chip->resetState ? 1 : 2;
+ break;
+ case 2: // read starting syllable low byte from word table
+ chip->SyllableAddress |= chip->SpeechRom[(chip->LatchedWord<<1)+1]>>4;
+ chip->nextstate = 3;
+ break;
+ case 3: // read starting phone address
+ chip->PhoneAddress = chip->SpeechRom[chip->SyllableAddress]<<4;
+ chip->nextstate = 4;
+ break;
+ case 4: // read playback parameters and prepare for play
+ chip->PlayParams = chip->SpeechRom[chip->SyllableAddress+1];
+ chip->GlobalSilenceState = SILENCEFLAG; // load phone silence flag
+ chip->LengthCounter = LENGTHCOUNT; // load length counter
+ chip->RepeatCounter = REPEATCOUNT; // load repeat counter
+ chip->OutputCounter = 0; // clear output counter and disable mirrored phoneme silence indirectly via LOCALSILENCESTATE
+ chip->PhoneOffset = 0; // set offset within phone to zero
+ chip->OldDelta = 0x2; // set old delta to 2 <- is this right?
+ chip->DACOutput = 0x88; // set DAC output to center/silence position (0x88)
+ chip->nextstate = 5;
+ break;
+ case 5: // Play phone forward, shift = 0 (also load)
+ CurDelta = (chip->SpeechRom[(chip->PhoneAddress)+chip->PhoneOffset]&0xc0)>>6; // grab current delta from high 2 bits of high nybble
+ chip->DACOutput += DeltaTable[CurDelta][chip->OldDelta]; // send data to forward delta table and add result to accumulator
+ chip->OldDelta = CurDelta; // Move current delta to old
+ chip->nextstate = 6;
+ break;
+ case 6: // Play phone forward, shift = 2
+ CurDelta = (chip->SpeechRom[(chip->PhoneAddress)+chip->PhoneOffset]&0x30)>>4; // grab current delta from low 2 bits of high nybble
+ chip->DACOutput += DeltaTable[CurDelta][chip->OldDelta]; // send data to forward delta table and add result to accumulator
+ chip->OldDelta = CurDelta; // Move current delta to old
+ chip->nextstate = 7;
+ break;
+ case 7: // Play phone forward, shift = 4
+ CurDelta = (chip->SpeechRom[(chip->PhoneAddress)+chip->PhoneOffset]&0xc)>>2; // grab current delta from high 2 bits of low nybble
+ chip->DACOutput += DeltaTable[CurDelta][chip->OldDelta]; // send data to forward delta table and add result to accumulator
+ chip->OldDelta = CurDelta; // Move current delta to old
+ chip->nextstate = 8;
+ break;
+ case 8: // Play phone forward, shift = 6 (increment address if needed)
+ CurDelta = chip->SpeechRom[(chip->PhoneAddress)+chip->PhoneOffset]&0x3; // grab current delta from low 2 bits of low nybble
+ chip->DACOutput += DeltaTable[CurDelta][chip->OldDelta]; // send data to forward delta table and add result to accumulator
+ chip->OldDelta = CurDelta; // Move current delta to old
+ chip->PhoneOffset++; // increment phone offset
+ if (chip->PhoneOffset == 0x8) // if we're now done this phone
+ {
+ /* call the PostPhoneme Function */
+ PostPhoneme(chip);
+ }
+ else
+ {
+ chip->nextstate = 5;
+ }
+ break;
+ case 9: // Play phone backward, shift = 6 (also load)
+ CurDelta = (chip->SpeechRom[(chip->PhoneAddress)+chip->PhoneOffset]&0x3); // grab current delta from low 2 bits of low nybble
+ if (chip->laststate != 8) // ignore first (bogus) dac change in mirrored backwards mode. observations and the patent show this.
+ {
+ chip->DACOutput -= DeltaTable[chip->OldDelta][CurDelta]; // send data to forward delta table and subtract result from accumulator
+ }
+ chip->OldDelta = CurDelta; // Move current delta to old
+ chip->nextstate = 10;
+ break;
+ case 10: // Play phone backward, shift = 4
+ CurDelta = (chip->SpeechRom[(chip->PhoneAddress)+chip->PhoneOffset]&0xc)>>2; // grab current delta from high 2 bits of low nybble
+ chip->DACOutput -= DeltaTable[chip->OldDelta][CurDelta]; // send data to forward delta table and subtract result from accumulator
+ chip->OldDelta = CurDelta; // Move current delta to old
+ chip->nextstate = 11;
+ break;
+ case 11: // Play phone backward, shift = 2
+ CurDelta = (chip->SpeechRom[(chip->PhoneAddress)+chip->PhoneOffset]&0x30)>>4; // grab current delta from low 2 bits of high nybble
+ chip->DACOutput -= DeltaTable[chip->OldDelta][CurDelta]; // send data to forward delta table and subtract result from accumulator
+ chip->OldDelta = CurDelta; // Move current delta to old
+ chip->nextstate = 12;
+ break;
+ case 12: // Play phone backward, shift = 0 (increment address if needed)
+ CurDelta = (chip->SpeechRom[(chip->PhoneAddress)+chip->PhoneOffset]&0xc0)>>6; // grab current delta from high 2 bits of high nybble
+ chip->DACOutput -= DeltaTable[chip->OldDelta][CurDelta]; // send data to forward delta table and subtract result from accumulator
+ chip->OldDelta = CurDelta; // Move current delta to old
+ chip->PhoneOffset--; // decrement phone offset
+ if (chip->PhoneOffset == 0xFF) // if we're now done this phone
+ {
+ /* call the PostPhoneme() function */
+ PostPhoneme(chip);
+ }
+ else
+ {
+ chip->nextstate = 9;
+ }
+ break;
+ case 13: // For those pedantic among us, consume an extra two clocks like the real chip does.
+ chip->nextstate = 0;
+ break;
+ }
+#ifdef DEBUGSTATE
+ fprintf(stderr, "Machine state is now %d, was %d, PhoneOffset is %d\n", chip->nextstate, chip->machineState, chip->PhoneOffset);
+#endif
+ chip->laststate = chip->machineState;
+ chip->machineState = chip->nextstate;
+ }
+}
+
+/**************************************************************************
+ MAME glue code
+ **************************************************************************/
+
+static void s14001a_pcm_update(void *param, stream_sample_t **inputs, stream_sample_t **outputs, int length)
+{
+ INT32 mix[48000];
+ INT32 *mixp;
+ S14001AChip *chip = param;
+ int i;
+
+ memset(mix, 0, sizeof(mix));
+
+ mixp = &mix[0];
+ for (i = 0; i < length; i++)
+ {
+ if (--chip->VSU1000_counter==0)
+ {
+ s14001a_clock(chip);
+ chip->VSU1000_counter = chip->VSU1000_freq;
+ }
+ outputs[0][i] = ((((INT16)chip->audioout)-128)<<6)*chip->VSU1000_amp;
+ }
+}
+
+static void *s14001a_start(int sndindex, int clock, const void *config)
+{
+ const struct S14001A_interface *intf;
+ S14001AChip *chip;
+ int i;
+
+ chip = auto_malloc(sizeof(*chip));
+ memset(chip, 0, sizeof(*chip));
+ chip->index = sndindex;
+
+ chip->GlobalSilenceState = 1;
+ chip->OldDelta = 0x02;
+ chip->DACOutput = SILENCE;
+ chip->VSU1000_amp = 0; /* reset by /reset line */
+ chip->VSU1000_freq = 1; /* base-1; reset by /reset line */
+ chip->VSU1000_counter = 1; /* base-1; not reset by /reset line but this is the best place to reset it */
+
+ for (i = 0; i < 8; i++)
+ {
+ chip->filtervals[i] = SILENCE;
+ }
+
+ intf = config;
+
+ chip->SpeechRom = memory_region(intf->region);
+
+ chip->stream = stream_create(0, 1, clock, chip, s14001a_pcm_update);
+
+ return chip;
+}
+
+static void s14001a_set_info(void *token, UINT32 state, sndinfo *info)
+{
+ switch (state)
+ {
+ /* no parameters to set */
+ }
+}
+
+int S14001A_bsy_0_r(void)
+{
+ S14001AChip *chip = sndti_token(SOUND_S14001A, 0);
+ stream_update(chip->stream);
+#ifdef DEBUGSTATE
+ fprintf(stderr,"busy state checked: %d\n",(chip->machineState != 0) );
+#endif
+ return (chip->machineState != 0);
+}
+
+void S14001A_reg_0_w(int data)
+{
+ S14001AChip *chip = sndti_token(SOUND_S14001A, 0);
+ stream_update(chip->stream);
+ chip->WordInput = data;
+}
+
+void S14001A_rst_0_w(int data)
+{
+ S14001AChip *chip = sndti_token(SOUND_S14001A, 0);
+ stream_update(chip->stream);
+ chip->LatchedWord = chip->WordInput;
+ chip->resetState = (data==1);
+ chip->machineState = chip->resetState ? 1 : chip->machineState;
+}
+
+void S14001A_set_rate(int newrate)
+{
+ S14001AChip *chip = sndti_token(SOUND_S14001A, 0);
+ stream_update(chip->stream);
+ chip->VSU1000_freq = newrate;
+}
+
+void S14001A_set_volume(int volume)
+{
+ S14001AChip *chip = sndti_token(SOUND_S14001A, 0);
+ stream_update(chip->stream);
+ chip->VSU1000_amp = volume;
+}
+
+void s14001a_get_info(void *token, UINT32 state, sndinfo *info)
+{
+ switch (state)
+ {
+ /* --- the following bits of info are returned as 64-bit signed integers --- */
+
+ /* --- the following bits of info are returned as pointers to data or functions --- */
+ case SNDINFO_PTR_SET_INFO: info->set_info = s14001a_set_info; break;
+ case SNDINFO_PTR_START: info->start = s14001a_start; break;
+ case SNDINFO_PTR_STOP: /* Nothing */ break;
+ case SNDINFO_PTR_RESET: /* Nothing */ break;
+
+ /* --- the following bits of info are returned as NULL-terminated strings --- */
+ case SNDINFO_STR_NAME: info->s = "S14001A"; break;
+ case SNDINFO_STR_CORE_FAMILY: info->s = "TSI S14001A"; break;
+ case SNDINFO_STR_CORE_VERSION: info->s = "1.11"; break;
+ case SNDINFO_STR_CORE_FILE: info->s = __FILE__; break;
+ case SNDINFO_STR_CORE_CREDITS: info->s = "Copyright (c) 2007 Jonathan Gevaryahu"; break;
+ }
+}
+