static 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 * 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 { #ifdef ACCURATE_SQUEAL chip->audioout = ALTFLAG; // flag to the renderer that this output should be the average of the last 8 #endif // DIGITAL INPUT *MIGHT* occur on the test pins occurs on this cycle? } 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. // Note: this behavior is NOT accurate, and needs to be fixed. see TODO. 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. // DIGITAL OUTPUT *might* be driven onto the test pins on this cycle? 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 = SILENCE ; // set DAC output to center/silence position 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; /* the dac is 4 bits wide. if a delta step forced it outside of 4 bits, mask it back over here */ chip->DACOutput &= 0xF; } }
void s14001a_clock(void) /* 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 */ oddeven = !(oddeven); // invert the clock if (oddeven == 0) // even clock { audioout = audiofilter(); // function to handle output filtering by internal capacitance based on clock speed and such #ifdef PINMAME if (!machineState) audioout = SILENCE; #endif shiftIntoFilter(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 (GlobalSilenceState || LOCALSILENCESTATE) { DACOutput = SILENCE; OldDelta = 2; } audioout = (GlobalSilenceState || LOCALSILENCESTATE) ? SILENCE : DACOutput; // when either silence state is 1, output silence. #ifdef PINMAME if (!machineState) audioout = SILENCE; #endif shiftIntoFilter(audioout); // shift over all the filter outputs and stick in audioout switch(machineState) { case 0: // idle state nextstate = 0; break; case 1: // read starting syllable high byte from word table SyllableAddress = 0; // clear syllable address SyllableAddress |= s14001a_readmem(LatchedWord<<1)<<4; nextstate = resetState ? 1 : 2; break; case 2: // read starting syllable low byte from word table SyllableAddress |= s14001a_readmem((LatchedWord<<1)+1)>>4; nextstate = 3; break; case 3: // read starting phone address PhoneAddress = s14001a_readmem(SyllableAddress)<<4; nextstate = 4; break; case 4: // read playback parameters and prepare for play PlayParams = s14001a_readmem(SyllableAddress+1); GlobalSilenceState = SILENCEFLAG; // load phone silence flag LengthCounter = LENGTHCOUNT; // load length counter RepeatCounter = REPEATCOUNT; // load repeat counter OutputCounter = 0; // clear output counter and disable mirrored phoneme silence indirectly via LOCALSILENCESTATE PhoneOffset = 0; // set offset within phone to zero OldDelta = 0x2; // set old delta to 2 <- is this right? DACOutput = 0x88; // set DAC output to center/silence position (0x88) nextstate = 5; break; case 5: // Play phone forward, shift = 0 (also load) CurDelta = (s14001a_readmem((PhoneAddress)+PhoneOffset)&0xc0)>>6; // grab current delta from high 2 bits of high nybble DACOutput += DeltaTable[CurDelta][OldDelta]; // send data to forward delta table and add result to accumulator OldDelta = CurDelta; // Move current delta to old nextstate = 6; break; case 6: // Play phone forward, shift = 2 CurDelta = (s14001a_readmem((PhoneAddress)+PhoneOffset)&0x30)>>4; // grab current delta from low 2 bits of high nybble DACOutput += DeltaTable[CurDelta][OldDelta]; // send data to forward delta table and add result to accumulator OldDelta = CurDelta; // Move current delta to old nextstate = 7; break; case 7: // Play phone forward, shift = 4 CurDelta = (s14001a_readmem((PhoneAddress)+PhoneOffset)&0xc)>>2; // grab current delta from high 2 bits of low nybble DACOutput += DeltaTable[CurDelta][OldDelta]; // send data to forward delta table and add result to accumulator OldDelta = CurDelta; // Move current delta to old nextstate = 8; break; case 8: // Play phone forward, shift = 6 (increment address if needed) CurDelta = s14001a_readmem((PhoneAddress)+PhoneOffset)&0x3; // grab current delta from low 2 bits of low nybble DACOutput += DeltaTable[CurDelta][OldDelta]; // send data to forward delta table and add result to accumulator OldDelta = CurDelta; // Move current delta to old PhoneOffset++; // increment phone offset if (PhoneOffset == 0x8) // if we're now done this phone { /* call the PostPhoneme Function */ PostPhoneme(); } else { nextstate = 5; } break; case 9: // Play phone backward, shift = 6 (also load) CurDelta = (s14001a_readmem((PhoneAddress)+PhoneOffset)&0x3); // grab current delta from low 2 bits of low nybble if (laststate != 8) // ignore first (bogus) dac change in mirrored backwards mode. observations and the patent show this. { DACOutput -= DeltaTable[OldDelta][CurDelta]; // send data to forward delta table and subtract result from accumulator } OldDelta = CurDelta; // Move current delta to old nextstate = 10; break; case 10: // Play phone backward, shift = 4 CurDelta = (s14001a_readmem((PhoneAddress)+PhoneOffset)&0xc)>>2; // grab current delta from high 2 bits of low nybble DACOutput -= DeltaTable[OldDelta][CurDelta]; // send data to forward delta table and subtract result from accumulator OldDelta = CurDelta; // Move current delta to old nextstate = 11; break; case 11: // Play phone backward, shift = 2 CurDelta = (s14001a_readmem((PhoneAddress)+PhoneOffset)&0x30)>>4; // grab current delta from low 2 bits of high nybble DACOutput -= DeltaTable[OldDelta][CurDelta]; // send data to forward delta table and subtract result from accumulator OldDelta = CurDelta; // Move current delta to old nextstate = 12; break; case 12: // Play phone backward, shift = 0 (increment address if needed) CurDelta = (s14001a_readmem((PhoneAddress)+PhoneOffset)&0xc0)>>6; // grab current delta from high 2 bits of high nybble DACOutput -= DeltaTable[OldDelta][CurDelta]; // send data to forward delta table and subtract result from accumulator OldDelta = CurDelta; // Move current delta to old PhoneOffset--; // decrement phone offset if (PhoneOffset == 0xFF) // if we're now done this phone { /* call the PostPhoneme() function */ PostPhoneme(); } else { nextstate = 9; } break; case 13: // For those pedantic among us, consume an extra two clocks like the real chip does. nextstate = 0; break; } #ifdef DEBUGSTATE fprintf(stderr, "Machine state is now %d, was %d, PhoneOffset is %d\n", nextstate, machineState, PhoneOffset); #endif laststate = machineState; machineState = nextstate; } }