s14001a: shorthand variable types, small cleanup

This commit is contained in:
hap 2024-08-11 15:05:03 +02:00
parent a8d909a02a
commit d352702d22
2 changed files with 134 additions and 127 deletions

View File

@ -2,22 +2,20 @@
// copyright-holders:Ed Bernard, Jonathan Gevaryahu, hap // copyright-holders:Ed Bernard, Jonathan Gevaryahu, hap
// thanks-to:Kevin Horton // thanks-to:Kevin Horton
/* /*
SSi TSI S14001A speech IC emulator
aka CRC: Custom ROM Controller, designed in 1975, first usage in 1976 on TSI Speech+ calculator
Originally written for MAME by Jonathan Gevaryahu(Lord Nightmare) 2006-2013,
replaced with near-complete rewrite by Ed Bernard in 2016
TODO: SSi TSI S14001A speech IC emulator
- nothing at the moment? aka CRC: Custom ROM Controller, designed in 1975, first usage in 1976 on TSI Speech+ calculator
Originally written for MAME by Jonathan Gevaryahu(Lord Nightmare) 2006-2013,
replaced with near-complete rewrite by Ed Bernard in 2016
Further reading: Further reading:
- http://www.vintagecalculators.com/html/speech-.html - http://www.vintagecalculators.com/html/speech-.html
- http://www.vintagecalculators.com/html/development_of_the_tsi_speech-.html - http://www.vintagecalculators.com/html/development_of_the_tsi_speech-.html
- http://www.vintagecalculators.com/html/speech-_state_machine.html - http://www.vintagecalculators.com/html/speech-_state_machine.html
- https://archive.org/stream/pdfy-QPCSwTWiFz1u9WU_/david_djvu.txt - https://archive.org/stream/pdfy-QPCSwTWiFz1u9WU_/david_djvu.txt
*/
Chip Pinout:
/* Chip Pinout:
The original datasheet (which is lost as far as I know) clearly called the The original datasheet (which is lost as far as I know) clearly called the
s14001a chip the 'CRC chip', or 'Custom Rom Controller', as it appears with s14001a chip the 'CRC chip', or 'Custom Rom Controller', as it appears with
this name on the Stern and Canon schematics, as well as on some TSI speech this name on the Stern and Canon schematics, as well as on some TSI speech
@ -101,6 +99,7 @@ line is held high, the first address byte of the first word will be read repeate
every clock, with the rom enable line enabled constantly (i.e. it doesn't toggle on every clock, with the rom enable line enabled constantly (i.e. it doesn't toggle on
and off as it normally does during speech). Once START has gone low-high-low, the and off as it normally does during speech). Once START has gone low-high-low, the
/BUSY line will go low until 3 clocks after the chip is done speaking. /BUSY line will go low until 3 clocks after the chip is done speaking.
*/ */
#include "emu.h" #include "emu.h"
@ -108,7 +107,7 @@ and off as it normally does during speech). Once START has gone low-high-low, th
namespace { namespace {
uint8_t Mux8To2(bool bVoicedP2, uint8_t uPPQtrP2, uint8_t uDeltaAdrP2, uint8_t uRomDataP2) u8 Mux8To2(bool bVoicedP2, u8 uPPQtrP2, u8 uDeltaAdrP2, u8 uRomDataP2)
{ {
// pick two bits of rom data as delta // pick two bits of rom data as delta
@ -120,7 +119,7 @@ uint8_t Mux8To2(bool bVoicedP2, uint8_t uPPQtrP2, uint8_t uDeltaAdrP2, uint8_t u
} }
void CalculateIncrement(bool bVoicedP2, uint8_t uPPQtrP2, bool bPPQStartP2, uint8_t uDelta, uint8_t uDeltaOldP2, uint8_t &uDeltaOldP1, uint8_t &uIncrementP2, bool &bAddP2) void CalculateIncrement(bool bVoicedP2, u8 uPPQtrP2, bool bPPQStartP2, u8 uDelta, u8 uDeltaOldP2, u8 &uDeltaOldP1, u8 &uIncrementP2, bool &bAddP2)
{ {
// uPPQtr, pitch period quarter counter; 2 lsb of uLength // uPPQtr, pitch period quarter counter; 2 lsb of uLength
// bPPStart, start of a pitch period // bPPStart, start of a pitch period
@ -130,7 +129,7 @@ void CalculateIncrement(bool bVoicedP2, uint8_t uPPQtrP2, bool bPPQStartP2, uint
if ((uPPQtrP2 == 0x00) && bPPQStartP2) // note this is done for voiced and unvoiced if ((uPPQtrP2 == 0x00) && bPPQStartP2) // note this is done for voiced and unvoiced
uDeltaOldP2 = 0x02; uDeltaOldP2 = 0x02;
static constexpr uint8_t uIncrements[4][4] = static constexpr u8 uIncrements[4][4] =
{ {
// 00 01 10 11 // 00 01 10 11
{ 3, 3, 1, 1,}, // 00 { 3, 3, 1, 1,}, // 00
@ -159,7 +158,7 @@ void CalculateIncrement(bool bVoicedP2, uint8_t uPPQtrP2, bool bPPQStartP2, uint
} }
uint8_t CalculateOutput(bool bVoiced, bool bXSilence, uint8_t uPPQtr, bool bPPQStart, uint8_t uLOutput, uint8_t uIncrementP2, bool bAddP2) u8 CalculateOutput(bool bVoiced, bool bXSilence, u8 uPPQtr, bool bPPQStart, u8 uLOutput, u8 uIncrementP2, bool bAddP2)
{ {
// implemented to mimic silicon (a bit) // implemented to mimic silicon (a bit)
// limits output to 0x00 and 0x0f // limits output to 0x00 and 0x0f
@ -175,9 +174,9 @@ uint8_t CalculateOutput(bool bVoiced, bool bXSilence, uint8_t uPPQtr, bool bPPQS
uLOutput = 7; uLOutput = 7;
// adder // adder
uint8_t uTmp = uLOutput; u8 uTmp = uLOutput;
if (!bAddP2) if (!bAddP2)
uTmp ^= 0x0F; // turns subtraction into addition uTmp ^= 0x0f; // turns subtraction into addition
// add 0, 1, 3; limit at 15 // add 0, 1, 3; limit at 15
uTmp += uIncrementP2; uTmp += uIncrementP2;
@ -185,7 +184,7 @@ uint8_t CalculateOutput(bool bVoiced, bool bXSilence, uint8_t uPPQtr, bool bPPQS
uTmp = 15; uTmp = 15;
if (!bAddP2) if (!bAddP2)
uTmp ^= 0x0F; // turns addition back to subtraction uTmp ^= 0x0f; // turns addition back to subtraction
return uTmp; return uTmp;
} }
@ -196,7 +195,7 @@ uint8_t CalculateOutput(bool bVoiced, bool bXSilence, uint8_t uPPQtr, bool bPPQS
// device definition // device definition
DEFINE_DEVICE_TYPE(S14001A, s14001a_device, "s14001a", "SSi TSI S14001A") DEFINE_DEVICE_TYPE(S14001A, s14001a_device, "s14001a", "SSi TSI S14001A")
s14001a_device::s14001a_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : s14001a_device::s14001a_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) :
device_t(mconfig, S14001A, tag, owner, clock), device_t(mconfig, S14001A, tag, owner, clock),
device_sound_interface(mconfig, *this), device_sound_interface(mconfig, *this),
m_SpeechRom(*this, DEVICE_SELF), m_SpeechRom(*this, DEVICE_SELF),
@ -303,7 +302,7 @@ void s14001a_device::sound_stream_update(sound_stream &stream, std::vector<read_
for (int i = 0; i < outputs[0].samples(); i++) for (int i = 0; i < outputs[0].samples(); i++)
{ {
Clock(); Clock();
int16_t sample = m_uOutputP2 - 7; // range -7..8 s16 sample = m_uOutputP2 - 7; // range -7..8
outputs[0].put_int(i, sample, 8); outputs[0].put_int(i, sample, 8);
} }
} }
@ -330,7 +329,7 @@ int s14001a_device::busy_r()
return (m_bBusyP1) ? 1 : 0; return (m_bBusyP1) ? 1 : 0;
} }
void s14001a_device::data_w(uint8_t data) void s14001a_device::data_w(u8 data)
{ {
m_stream->update(); m_stream->update();
m_uWord = data & 0x3f; // C0-C5 m_uWord = data & 0x3f; // C0-C5
@ -340,10 +339,11 @@ void s14001a_device::start_w(int state)
{ {
m_stream->update(); m_stream->update();
m_bStart = (state != 0); m_bStart = (state != 0);
if (m_bStart) m_uStateP1 = states::WORDWAIT; if (m_bStart)
m_uStateP1 = states::WORDWAIT;
} }
void s14001a_device::set_clock(uint32_t clock) void s14001a_device::set_clock(u32 clock)
{ {
m_stream->update(); m_stream->update();
m_stream->set_sample_rate(clock); m_stream->set_sample_rate(clock);
@ -354,7 +354,7 @@ void s14001a_device::set_clock(uint32_t clock)
Device emulation Device emulation
**************************************************************************/ **************************************************************************/
uint8_t s14001a_device::readmem(uint16_t offset, bool phase) u8 s14001a_device::readmem(u16 offset, bool phase)
{ {
offset &= 0xfff; // 11-bit internal offset &= 0xfff; // 11-bit internal
return (m_ext_read_handler.isunset()) ? m_SpeechRom[offset & (m_SpeechRom.bytes() - 1)] : m_ext_read_handler(offset); return (m_ext_read_handler.isunset()) ? m_SpeechRom[offset & (m_SpeechRom.bytes() - 1)] : m_ext_read_handler(offset);
@ -393,10 +393,10 @@ bool s14001a_device::Clock()
m_uRomAddrP2 = m_RomAddrP1; m_uRomAddrP2 = m_RomAddrP1;
// setup carries from phase 2 values // setup carries from phase 2 values
m_bDAR04To00CarryP2 = m_uDAR04To00P2 == 0x1F; m_bDAR04To00CarryP2 = m_uDAR04To00P2 == 0x1f;
m_bPPQCarryP2 = m_bDAR04To00CarryP2 && ((m_uLengthP2&0x03) == 0x03); // pitch period quarter m_bPPQCarryP2 = m_bDAR04To00CarryP2 && ((m_uLengthP2 & 0x03) == 0x03); // pitch period quarter
m_bRepeatCarryP2 = m_bPPQCarryP2 && ((m_uLengthP2&0x0C) == 0x0C); m_bRepeatCarryP2 = m_bPPQCarryP2 && ((m_uLengthP2 & 0x0c) == 0x0c);
m_bLengthCarryP2 = m_bRepeatCarryP2 && ( m_uLengthP2 == 0x7F); m_bLengthCarryP2 = m_bRepeatCarryP2 && ( m_uLengthP2 == 0x7f);
return true; return true;
} }
@ -407,7 +407,8 @@ bool s14001a_device::Clock()
{ {
case states::IDLE: case states::IDLE:
m_uOutputP1 = 7; m_uOutputP1 = 7;
if (m_bStart) m_uStateP1 = states::WORDWAIT; if (m_bStart)
m_uStateP1 = states::WORDWAIT;
if (m_bBusyP1) if (m_bBusyP1)
m_bsy_handler(0); m_bsy_handler(0);
@ -417,12 +418,12 @@ bool s14001a_device::Clock()
case states::WORDWAIT: case states::WORDWAIT:
// the delta address register latches the word number into bits 03 to 08 // the delta address register latches the word number into bits 03 to 08
// all other bits forced to 0. 04 to 08 makes a multiply by two. // all other bits forced to 0. 04 to 08 makes a multiply by two.
m_uDAR13To05P1 = (m_uWord&0x3C)>>2; m_uDAR13To05P1 = (m_uWord & 0x3c) >> 2;
m_uDAR04To00P1 = (m_uWord&0x03)<<3; m_uDAR04To00P1 = (m_uWord & 0x03) << 3;
m_RomAddrP1 = (m_uDAR13To05P1<<3)|(m_uDAR04To00P1>>2); // remove lower two bits m_RomAddrP1 = (m_uDAR13To05P1 << 3) | (m_uDAR04To00P1 >> 2); // remove lower two bits
m_uOutputP1 = 7; m_uOutputP1 = 7;
if (m_bStart) m_uStateP1 = states::WORDWAIT; m_uStateP1 = m_bStart ? states::WORDWAIT : states::CWARMSB;
else m_uStateP1 = states::CWARMSB;
if (!m_bBusyP1) if (!m_bBusyP1)
m_bsy_handler(1); m_bsy_handler(1);
@ -431,59 +432,60 @@ bool s14001a_device::Clock()
case states::CWARMSB: case states::CWARMSB:
if (m_uPrintLevel >= 1) if (m_uPrintLevel >= 1)
printf("\n speaking word %02x",m_uWord); logerror("\n speaking word %02x",m_uWord);
// use uDAR to load uCWAR 8 msb // use uDAR to load uCWAR 8 msb
m_uCWARP1 = readmem(m_uRomAddrP2,m_bPhase1)<<4; // note use of rom address setup in previous state m_uCWARP1 = readmem(m_uRomAddrP2, m_bPhase1) << 4; // note use of rom address setup in previous state
// increment DAR by 4, 2 lsb's count deltas within a byte // increment DAR by 4, 2 lsb's count deltas within a byte
m_uDAR04To00P1 += 4; m_uDAR04To00P1 += 4;
if (m_uDAR04To00P1 >= 32) m_uDAR04To00P1 = 0; // emulate 5 bit counter if (m_uDAR04To00P1 >= 32)
m_RomAddrP1 = (m_uDAR13To05P1<<3)|(m_uDAR04To00P1>>2); // remove lower two bits m_uDAR04To00P1 = 0; // emulate 5 bit counter
m_RomAddrP1 = (m_uDAR13To05P1 << 3) | (m_uDAR04To00P1 >> 2); // remove lower two bits
m_uOutputP1 = 7; m_uOutputP1 = 7;
if (m_bStart) m_uStateP1 = states::WORDWAIT; m_uStateP1 = m_bStart ? states::WORDWAIT : states::CWARLSB;
else m_uStateP1 = states::CWARLSB;
break; break;
case states::CWARLSB: case states::CWARLSB:
m_uCWARP1 = m_uCWARP2|(readmem(m_uRomAddrP2,m_bPhase1)>>4); // setup in previous state m_uCWARP1 = m_uCWARP2 | (readmem(m_uRomAddrP2, m_bPhase1) >> 4); // setup in previous state
m_RomAddrP1 = m_uCWARP1; m_RomAddrP1 = m_uCWARP1;
m_uOutputP1 = 7; m_uOutputP1 = 7;
if (m_bStart) m_uStateP1 = states::WORDWAIT; m_uStateP1 = m_bStart ? states::WORDWAIT : states::DARMSB;
else m_uStateP1 = states::DARMSB;
break; break;
case states::DARMSB: case states::DARMSB:
m_uDAR13To05P1 = readmem(m_uRomAddrP2,m_bPhase1)<<1; // 9 bit counter, 8 MSBs from ROM, lsb zeroed m_uDAR13To05P1 = readmem(m_uRomAddrP2, m_bPhase1) << 1; // 9 bit counter, 8 MSBs from ROM, lsb zeroed
m_uDAR04To00P1 = 0; m_uDAR04To00P1 = 0;
m_uCWARP1++; m_uCWARP1++;
m_RomAddrP1 = m_uCWARP1; m_RomAddrP1 = m_uCWARP1;
m_uNControlWords++; // statistics m_uNControlWords++; // statistics
m_uOutputP1 = 7; m_uOutputP1 = 7;
if (m_bStart) m_uStateP1 = states::WORDWAIT; m_uStateP1 = m_bStart ? states::WORDWAIT : states::CTRLBITS;
else m_uStateP1 = states::CTRLBITS;
break; break;
case states::CTRLBITS: case states::CTRLBITS:
m_bStopP1 = readmem(m_uRomAddrP2,m_bPhase1)&0x80? true: false; {
m_bVoicedP1 = readmem(m_uRomAddrP2,m_bPhase1)&0x40? true: false; u8 data = readmem(m_uRomAddrP2, m_bPhase1);
m_bSilenceP1 = readmem(m_uRomAddrP2,m_bPhase1)&0x20? true: false;
m_uXRepeatP1 = readmem(m_uRomAddrP2,m_bPhase1)&0x03; m_bStopP1 = bool(data & 0x80);
m_uLengthP1 =(readmem(m_uRomAddrP2,m_bPhase1)&0x1F)<<2; // includes external length and repeat m_bVoicedP1 = bool(data & 0x40);
m_bSilenceP1 = bool(data & 0x20);
m_uXRepeatP1 = data & 0x03;
m_uLengthP1 = (data & 0x1f) << 2; // includes external length and repeat
m_uDAR04To00P1 = 0; m_uDAR04To00P1 = 0;
m_uCWARP1++; // gets ready for next DARMSB m_uCWARP1++; // gets ready for next DARMSB
m_RomAddrP1 = (m_uDAR13To05P1<<3)|(m_uDAR04To00P1>>2); // remove lower two bits m_RomAddrP1 = (m_uDAR13To05P1 << 3) | (m_uDAR04To00P1 >> 2); // remove lower two bits
m_uOutputP1 = 7; m_uOutputP1 = 7;
if (m_bStart) m_uStateP1 = states::WORDWAIT; m_uStateP1 = m_bStart ? states::WORDWAIT : states::PLAY;
else m_uStateP1 = states::PLAY;
if (m_uPrintLevel >= 2) if (m_uPrintLevel >= 2)
printf("\n cw %d %d %d %d %d",m_bStopP1,m_bVoicedP1,m_bSilenceP1,m_uLengthP1>>4,m_uXRepeatP1); logerror("\n cw %d %d %d %d %d", m_bStopP1, m_bVoicedP1, m_bSilenceP1, m_uLengthP1 >> 4, m_uXRepeatP1);
break; break;
}
case states::PLAY: case states::PLAY:
{ {
@ -492,38 +494,39 @@ bool s14001a_device::Clock()
{ {
// pitch period end // pitch period end
if (m_uPrintLevel >= 3) if (m_uPrintLevel >= 3)
printf("\n ppe: RomAddr %03x",m_uRomAddrP2); logerror("\n ppe: RomAddr %03x", m_uRomAddrP2);
m_uNPitchPeriods++; m_uNPitchPeriods++;
if (m_bVoicedP2) m_uNVoiced++; if (m_bVoicedP2)
m_uNVoiced++;
} }
// end statistics // end statistics
// modify output // modify output
uint8_t uDeltaP2; // signal line u8 uDeltaP2; // signal line
uint8_t uIncrementP2; // signal lines u8 uIncrementP2; // signal lines
bool bAddP2; // signal line bool bAddP2; // signal line
uDeltaP2 = Mux8To2(m_bVoicedP2, uDeltaP2 = Mux8To2(m_bVoicedP2,
m_uLengthP2 & 0x03, // pitch period quater counter m_uLengthP2 & 0x03, // pitch period quater counter
m_uDAR04To00P2 & 0x03, // two bit delta address within byte m_uDAR04To00P2 & 0x03, // two bit delta address within byte
readmem(m_uRomAddrP2,m_bPhase1) readmem(m_uRomAddrP2, m_bPhase1)
); );
CalculateIncrement(m_bVoicedP2, CalculateIncrement(m_bVoicedP2,
m_uLengthP2 & 0x03, // pitch period quater counter m_uLengthP2 & 0x03, // pitch period quater counter
m_uDAR04To00P2 == 0, // pitch period quarter start m_uDAR04To00P2 == 0, // pitch period quarter start
uDeltaP2, uDeltaP2,
m_uDeltaOldP2, // input m_uDeltaOldP2, // input
m_uDeltaOldP1, // output m_uDeltaOldP1, // output
uIncrementP2, // output 0, 1, or 3 uIncrementP2, // output 0, 1, or 3
bAddP2 // output bAddP2 // output
); );
m_uOutputP1 = CalculateOutput(m_bVoicedP2, m_uOutputP1 = CalculateOutput(m_bVoicedP2,
m_bSilenceP2, m_bSilenceP2,
m_uLengthP2 & 0x03, // pitch period quater counter m_uLengthP2 & 0x03, // pitch period quater counter
m_uDAR04To00P2 == 0, // pitch period quarter start m_uDAR04To00P2 == 0, // pitch period quarter start
m_uOutputP2, // last output m_uOutputP2, // last output
uIncrementP2, uIncrementP2,
bAddP2 bAddP2
); );
// advance counters // advance counters
@ -542,42 +545,44 @@ bool s14001a_device::Clock()
if (m_bVoicedP2 && m_bRepeatCarryP2) // repeat complete if (m_bVoicedP2 && m_bRepeatCarryP2) // repeat complete
{ {
m_uLengthP1 &= 0x70; // keep current "length" m_uLengthP1 &= 0x70; // keep current "length"
m_uLengthP1 |= (m_uXRepeatP1<<2); // load repeat from external repeat m_uLengthP1 |= (m_uXRepeatP1 << 2); // load repeat from external repeat
m_uDAR13To05P1++; // advances ROM address 8 bytes m_uDAR13To05P1++; // advances ROM address 8 bytes
if (m_uDAR13To05P1 >= 0x200) m_uDAR13To05P1 = 0; // emulate 9 bit counter if (m_uDAR13To05P1 >= 0x200)
m_uDAR13To05P1 = 0; // emulate 9 bit counter
} }
if (!m_bVoicedP2 && m_bDAR04To00CarryP2) if (!m_bVoicedP2 && m_bDAR04To00CarryP2)
{ {
// unvoiced advances each quarter pitch period // unvoiced advances each quarter pitch period
// note repeat counter not reloaded for non voiced speech // note repeat counter not reloaded for non voiced speech
m_uDAR13To05P1++; // advances ROM address 8 bytes m_uDAR13To05P1++; // advances ROM address 8 bytes
if (m_uDAR13To05P1 >= 0x200) m_uDAR13To05P1 = 0; // emulate 9 bit counter if (m_uDAR13To05P1 >= 0x200)
m_uDAR13To05P1 = 0; // emulate 9 bit counter
} }
// construct m_RomAddrP1 // construct m_RomAddrP1
m_RomAddrP1 = m_uDAR04To00P1; m_RomAddrP1 = m_uDAR04To00P1;
if (m_bVoicedP2 && m_uLengthP1&0x1) // mirroring if (m_bVoicedP2 && m_uLengthP1 & 0x1) // mirroring
{
m_RomAddrP1 ^= 0x1f; // count backwards m_RomAddrP1 ^= 0x1f; // count backwards
} m_RomAddrP1 = (m_uDAR13To05P1 << 3) | m_RomAddrP1 >> 2;
m_RomAddrP1 = (m_uDAR13To05P1<<3) | m_RomAddrP1>>2;
// next state // next state
if (m_bStart) m_uStateP1 = states::WORDWAIT; if (m_bStart)
else if (m_bStopP2 && m_bLengthCarryP2) m_uStateP1 = states::DELAY; m_uStateP1 = states::WORDWAIT;
else if (m_bStopP2 && m_bLengthCarryP2)
m_uStateP1 = states::DELAY;
else if (m_bLengthCarryP2) else if (m_bLengthCarryP2)
{ {
m_uStateP1 = states::DARMSB; m_uStateP1 = states::DARMSB;
m_RomAddrP1 = m_uCWARP1; // output correct address m_RomAddrP1 = m_uCWARP1; // output correct address
} }
else m_uStateP1 = states::PLAY; else
m_uStateP1 = states::PLAY;
break; break;
} }
case states::DELAY: case states::DELAY:
m_uOutputP1 = 7; m_uOutputP1 = 7;
if (m_bStart) m_uStateP1 = states::WORDWAIT; m_uStateP1 = m_bStart ? states::WORDWAIT : states::IDLE;
else m_uStateP1 = states::IDLE;
break; break;
} }
@ -592,7 +597,7 @@ void s14001a_device::ClearStatistics()
m_uNControlWords = 0; m_uNControlWords = 0;
} }
void s14001a_device::GetStatistics(uint32_t &uNPitchPeriods, uint32_t &uNVoiced, uint32_t &uNControlWords) void s14001a_device::GetStatistics(u32 &uNPitchPeriods, u32 &uNVoiced, u32 &uNControlWords)
{ {
uNPitchPeriods = m_uNPitchPeriods; uNPitchPeriods = m_uNPitchPeriods;
uNVoiced = m_uNVoiced; uNVoiced = m_uNVoiced;

View File

@ -8,10 +8,12 @@
#ifndef MAME_SOUND_S14001A_H #ifndef MAME_SOUND_S14001A_H
#define MAME_SOUND_S14001A_H #define MAME_SOUND_S14001A_H
#pragma once
class s14001a_device : public device_t, public device_sound_interface class s14001a_device : public device_t, public device_sound_interface
{ {
public: public:
s14001a_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); s14001a_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);
// configuration helpers // configuration helpers
auto bsy() { return m_bsy_handler.bind(); } auto bsy() { return m_bsy_handler.bind(); }
@ -20,11 +22,11 @@ public:
int busy_r(); // /BUSY (pin 40) int busy_r(); // /BUSY (pin 40)
int romen_r(); // ROM /EN (pin 9) int romen_r(); // ROM /EN (pin 9)
void start_w(int state); // START (pin 10) void start_w(int state); // START (pin 10)
void data_w(uint8_t data); // 6-bit word void data_w(u8 data); // 6-bit word
void set_clock(uint32_t clock); // set new CLK frequency void set_clock(u32 clock); // set new CLK frequency
void set_clock(const XTAL &xtal) { set_clock(xtal.value()); } void set_clock(const XTAL &xtal) { set_clock(xtal.value()); }
void force_update(); // update stream, eg. before external ROM bankswitch void force_update(); // update stream, eg. before external ROM bankswitch
protected: protected:
// device-level overrides // device-level overrides
@ -34,13 +36,13 @@ protected:
virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override; virtual void sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs) override;
private: private:
uint8_t readmem(uint16_t offset, bool phase); u8 readmem(u16 offset, bool phase);
bool Clock(); // called once to toggle external clock twice bool Clock(); // called once to toggle external clock twice
// emulator helper functions // emulator helper functions
void ClearStatistics(); void ClearStatistics();
void GetStatistics(uint32_t &uNPitchPeriods, uint32_t &uNVoiced, uint32_t &uNControlWords); void GetStatistics(u32 &uNPitchPeriods, u32 &uNVoiced, u32 &uNControlWords);
void SetPrintLevel(uint32_t uPrintLevel) { m_uPrintLevel = uPrintLevel; } void SetPrintLevel(u32 uPrintLevel) { m_uPrintLevel = uPrintLevel; }
enum class states : u8 enum class states : u8
{ {
@ -54,28 +56,28 @@ private:
DELAY DELAY
}; };
required_region_ptr<uint8_t> m_SpeechRom; required_region_ptr<u8> m_SpeechRom;
sound_stream * m_stream; sound_stream * m_stream;
devcb_write_line m_bsy_handler; devcb_write_line m_bsy_handler;
devcb_read8 m_ext_read_handler; devcb_read8 m_ext_read_handler;
// internal state // internal state
bool m_bPhase1; // 1 bit internal clock bool m_bPhase1; // 1 bit internal clock
// registers // registers
states m_uStateP1; // 3 bits states m_uStateP1; // 3 bits
states m_uStateP2; states m_uStateP2;
uint16_t m_uDAR13To05P1; // 9 MSBs of delta address register u16 m_uDAR13To05P1; // 9 MSBs of delta address register
uint16_t m_uDAR13To05P2; // incrementing uDAR05To13 advances ROM address by 8 bytes u16 m_uDAR13To05P2; // incrementing uDAR05To13 advances ROM address by 8 bytes
uint16_t m_uDAR04To00P1; // 5 LSBs of delta address register u16 m_uDAR04To00P1; // 5 LSBs of delta address register
uint16_t m_uDAR04To00P2; // 3 address ROM, 2 mux 8 bits of data into 2 bit delta u16 m_uDAR04To00P2; // 3 address ROM, 2 mux 8 bits of data into 2 bit delta
// carry indicates end of quarter pitch period (32 cycles) // carry indicates end of quarter pitch period (32 cycles)
uint16_t m_uCWARP1; // 12 bits Control Word Address Register (syllable) u16 m_uCWARP1; // 12 bits Control Word Address Register (syllable)
uint16_t m_uCWARP2; u16 m_uCWARP2;
bool m_bStopP1; bool m_bStopP1;
bool m_bStopP2; bool m_bStopP2;
@ -83,42 +85,42 @@ private:
bool m_bVoicedP2; bool m_bVoicedP2;
bool m_bSilenceP1; bool m_bSilenceP1;
bool m_bSilenceP2; bool m_bSilenceP2;
uint8_t m_uLengthP1; // 7 bits, upper three loaded from ROM length u8 m_uLengthP1; // 7 bits, upper three loaded from ROM length
uint8_t m_uLengthP2; // middle two loaded from ROM repeat and/or uXRepeat u8 m_uLengthP2; // middle two loaded from ROM repeat and/or uXRepeat
// bit 0 indicates mirror in voiced mode // bit 0 indicates mirror in voiced mode
// bit 1 indicates internal silence in voiced mode // bit 1 indicates internal silence in voiced mode
// incremented each pitch period quarter // incremented each pitch period quarter
uint8_t m_uXRepeatP1; // 2 bits, loaded from ROM repeat u8 m_uXRepeatP1; // 2 bits, loaded from ROM repeat
uint8_t m_uXRepeatP2; u8 m_uXRepeatP2;
uint8_t m_uDeltaOldP1; // 2 bit old delta u8 m_uDeltaOldP1; // 2 bit old delta
uint8_t m_uDeltaOldP2; u8 m_uDeltaOldP2;
uint8_t m_uOutputP1; // 4 bits audio output, calculated during phase 1 u8 m_uOutputP1; // 4 bits audio output, calculated during phase 1
// derived signals // derived signals
bool m_bDAR04To00CarryP2; bool m_bDAR04To00CarryP2;
bool m_bPPQCarryP2; bool m_bPPQCarryP2;
bool m_bRepeatCarryP2; bool m_bRepeatCarryP2;
bool m_bLengthCarryP2; bool m_bLengthCarryP2;
uint16_t m_RomAddrP1; // rom address u16 m_RomAddrP1; // rom address
// output pins // output pins
uint8_t m_uOutputP2; // output changes on phase2 u8 m_uOutputP2; // output changes on phase2
uint16_t m_uRomAddrP2; // address pins change on phase 2 u16 m_uRomAddrP2; // address pins change on phase 2
bool m_bBusyP1; // busy changes on phase 1 bool m_bBusyP1; // busy changes on phase 1
// input pins // input pins
bool m_bStart; bool m_bStart;
uint8_t m_uWord; // 6 bit word number to be spoken u8 m_uWord; // 6 bit word number to be spoken
// emulator variables // emulator variables
// statistics // statistics
uint32_t m_uNPitchPeriods; u32 m_uNPitchPeriods;
uint32_t m_uNVoiced; u32 m_uNVoiced;
uint32_t m_uNControlWords; u32 m_uNControlWords;
// diagnostic output // diagnostic output
uint32_t m_uPrintLevel; u32 m_uPrintLevel;
}; };
DECLARE_DEVICE_TYPE(S14001A, s14001a_device) DECLARE_DEVICE_TYPE(S14001A, s14001a_device)