mirror of
https://github.com/AppleWin/AppleWin.git
synced 2024-12-26 20:30:16 +00:00
. A phoneme will continue playing back infinitely; unless the phoneme is changed or CTL=1. . Reset doesn't affect SSI263/SC01 (so phonemes continue to play). . CTL=1 sets "PD" (Power Down / "standby") mode, also set at power-on. . CTL=0 brings device out of "PD" mode, the mode will be set to DR1,DR0 and the phoneme P5-P0 will play. . Setting mode to DR1:0 = %00 just disables A/!R (ie. disables interrupts), but otherwise retains the previous DR1:0 mode. . RESET is not connected to !PD/!RST pin 18. . Support edge-case where RESET can enable ints & assert IRQ. . Power-on: PD=1 (so D7=0), reg4 (Filter Freq)=0xFF. . Support SSI263 IRQ and D7 on a Phasor mode change (including Echo+). . $Csxx I/O mapping (same for Mockingboard and Phasor mode). Other: . SSI263 save-state: support SC01 as a sub-unit of the card. . 6522: Fix reg $F (ORA w/HS) to be identical to reg $1 (ORA).
This commit is contained in:
parent
ae7e5a63a9
commit
a88040c3ef
@ -153,6 +153,7 @@ void SY6522::Write(BYTE nReg, BYTE nValue)
|
||||
m_regs.ORB = nValue & m_regs.DDRB;
|
||||
break;
|
||||
case 0x01: // ORA
|
||||
case 0x0f: // ORA_NO_HS
|
||||
m_regs.ORA = nValue & m_regs.DDRA;
|
||||
break;
|
||||
case 0x02: // DDRB
|
||||
@ -220,8 +221,6 @@ void SY6522::Write(BYTE nReg, BYTE nValue)
|
||||
m_syncEvent[1]->m_canAssertIRQ = (m_regs.IER & IxR_TIMER2) ? true : false;
|
||||
UpdateIFR(0);
|
||||
break;
|
||||
case 0x0f: // ORA_NO_HS
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -348,6 +347,7 @@ BYTE SY6522::Read(BYTE nReg)
|
||||
if (m_isMegaAudio) nValue = 0x00; // MegaAudio: IRB just reads as $00
|
||||
break;
|
||||
case 0x01: // IRA
|
||||
case 0x0f: // IRA_NO_HS
|
||||
nValue = m_regs.ORA | (m_isBusDriven ? 0x00 : (m_regs.DDRA ^ 0xff)); // NB. Inputs bits driven by AY8913 if in PSG READ mode
|
||||
if (m_isMegaAudio) nValue = 0x00; // MegaAudio: IRA just reads as $00
|
||||
break;
|
||||
@ -400,9 +400,6 @@ BYTE SY6522::Read(BYTE nReg)
|
||||
if (m_isMegaAudio)
|
||||
nValue &= 0x7F;
|
||||
break;
|
||||
case 0x0f: // ORA_NO_HS
|
||||
nValue = m_regs.ORA;
|
||||
break;
|
||||
}
|
||||
|
||||
return nValue;
|
||||
|
@ -405,9 +405,9 @@ bool MockingboardCard::Is6522IRQ(void)
|
||||
irq |= m_MBSubUnit[i].sy6522.GetReg(SY6522::rIFR) & 0x80 ? true : false;
|
||||
|
||||
// NB. Mockingboard generates IRQ on both 6522s:
|
||||
// . SSI263's IRQ (A/!R) is routed via the 2nd 6522 (at $Cn80) and must generate a 6502 IRQ (not NMI)
|
||||
// - NB. 2nd SSI263's IRQ is routed via the 1st 6522 (at $Cn00) and again generates a 6502 IRQ
|
||||
// . SC-01's IRQ (A/!R) is routed via the 6522 at $Cn00 (NB. Only the Mockingboard "Sound/Speech I" card supports the SC-01)
|
||||
// . SSI263's IRQ (A/!R) is routed via the 2nd 6522's CA1 input (at $Cn80) and must generate a 6502 IRQ (not NMI)
|
||||
// - NB. 2nd SSI263's IRQ is routed via the 1st 6522's CA1 input (at $Cn00) and again generates a 6502 IRQ
|
||||
// . SC-01's IRQ (!A/R) is routed via the 6522 at $Cn00 (NB. Only the Mockingboard "Sound/Speech I" card supports the SC-01)
|
||||
// Phasor's SSI263 IRQ (A/!R) line is *also* wired directly to the 6502's IRQ (as well as the 6522's CA1)
|
||||
|
||||
return irq;
|
||||
@ -576,7 +576,7 @@ void MockingboardCard::Reset(const bool powerCycle) // CTRL+RESET or power-cycle
|
||||
|
||||
m_MBSubUnit[subunit].Reset(QueryType());
|
||||
m_MBSubUnit[subunit].ssi263.SetCardMode(PH_Mockingboard); // Revert to PH_Mockingboard mode
|
||||
m_MBSubUnit[subunit].ssi263.Reset();
|
||||
m_MBSubUnit[subunit].ssi263.Reset(powerCycle, m_phasorEnable);
|
||||
}
|
||||
|
||||
// Reset state
|
||||
@ -668,7 +668,7 @@ BYTE MockingboardCard::IOReadInternal(WORD PC, WORD nAddr, BYTE bWrite, BYTE nVa
|
||||
return MemReadFloatingBus(nExecutedCycles);
|
||||
#endif
|
||||
|
||||
// NB. Mockingboard: SSI263.bit7 not readable (TODO: check this with real h/w)
|
||||
// NB. Mockingboard: SSI263.bit7 not readable
|
||||
const BYTE subunit = QueryType() == CT_SDMusic ? SY6522_DEVICE_A : !(nAddr & 0x80) ? SY6522_DEVICE_A : SY6522_DEVICE_B;
|
||||
const BYTE reg = nAddr & 0xf;
|
||||
return m_MBSubUnit[subunit].sy6522.Read(reg);
|
||||
@ -751,14 +751,13 @@ BYTE MockingboardCard::IOWriteInternal(WORD PC, WORD nAddr, BYTE bWrite, BYTE nV
|
||||
WriteToORB(SY6522_DEVICE_B);
|
||||
}
|
||||
|
||||
bool CS_SSI263_A = (m_phasorMode == PH_Phasor) ? !(nAddr & 0x80) && (nAddr & 0x40) // SSI263 at $Cn4x, $Cn6x
|
||||
: nAddr & 0x40; // SSI263 at $Cn4x-Cn7x, $CnCx-CnFx
|
||||
|
||||
bool CS_SSI263_B = (m_phasorMode == PH_Phasor) ? !(nAddr & 0x80) && (nAddr & 0x20) // SSI263 at $Cn2x, $Cn6x
|
||||
: nAddr & 0x20; // SSI263 at $Cn2x-Cn3x, $Cn6x-Cn7x, $CnAx-CnBx, $CnEx-CnFx
|
||||
|
||||
if (m_phasorMode == PH_Mockingboard || m_phasorMode == PH_Phasor) // No SSI263 for Echo+
|
||||
{
|
||||
// Confirmed that Phasor has no extra logic to map SSI263 (it's the same as Mockingboard's)
|
||||
bool CS_SSI263_A = nAddr & 0x40; // SSI263 at $Cn4x-Cn7x, $CnCx-CnFx
|
||||
|
||||
bool CS_SSI263_B = nAddr & 0x20; // SSI263 at $Cn2x-Cn3x, $Cn6x-Cn7x, $CnAx-CnBx, $CnEx-CnFx
|
||||
|
||||
// NB. Mockingboard mode: writes to $Cn4x/SSI263 also get written to 1st 6522 (have confirmed on real Phasor h/w)
|
||||
if (CS_SSI263_A) // Primary SSI263
|
||||
m_MBSubUnit[1].ssi263.Write(nAddr&0x7, nValue); // 2nd 6522 is used for 1st speech chip
|
||||
@ -839,6 +838,8 @@ BYTE MockingboardCard::PhasorIOInternal(WORD PC, WORD nAddr, BYTE bWrite, BYTE n
|
||||
m_phasorClockScaleFactor = 1;
|
||||
else if (m_phasorMode == PH_Phasor)
|
||||
m_phasorClockScaleFactor = 2;
|
||||
else // undefined mode
|
||||
m_phasorClockScaleFactor = 1; // TODO: Check for undefined Phasor mode
|
||||
|
||||
if (m_phasorMode == PH_Mockingboard)
|
||||
{
|
||||
@ -849,7 +850,7 @@ BYTE MockingboardCard::PhasorIOInternal(WORD PC, WORD nAddr, BYTE bWrite, BYTE n
|
||||
AY8910_InitClock((int)(Get6502BaseClock() * m_phasorClockScaleFactor));
|
||||
|
||||
for (UINT i = 0; i < NUM_SSI263; i++)
|
||||
m_MBSubUnit[i].ssi263.SetCardMode(m_phasorMode);
|
||||
m_MBSubUnit[i].ssi263.SetCardMode(m_phasorMode); // TODO: Check for undefined Phasor mode
|
||||
|
||||
#if DBG_SUPPORT_ECHOPLUS
|
||||
if (m_phasorMode == PH_EchoPlus && (nAddr & 0xf) == 0)
|
||||
@ -1155,7 +1156,9 @@ UINT MockingboardCard::AY8910_LoadSnapshot(YamlLoadHelper& yamlLoadHelper, BYTE
|
||||
// "Reg Address Latch Valid A" + "Reg Address Latch Valid B"
|
||||
// Changed at AppleWin 1.30.14
|
||||
//11: Added: "Bus Driven by AY"
|
||||
const UINT kUNIT_VERSION = 11;
|
||||
//12: Added: SSI263: SC01 phoneme & active
|
||||
// Current Mode changed (added bit5 = enableInts)
|
||||
const UINT kUNIT_VERSION = 12;
|
||||
|
||||
#define SS_YAML_KEY_MB_UNIT "Unit"
|
||||
#define SS_YAML_KEY_AY_CURR_REG "AY Current Register"
|
||||
@ -1231,6 +1234,8 @@ void MockingboardCard::SaveSnapshot(YamlSaveHelper& yamlSaveHelper)
|
||||
pMB->sy6522.SaveSnapshot(yamlSaveHelper);
|
||||
AY8910_SaveSnapshot(yamlSaveHelper, subunit, AY8913_DEVICE_A, std::string(""));
|
||||
pMB->ssi263.SaveSnapshot(yamlSaveHelper);
|
||||
if (subunit == 0) // has SC01
|
||||
pMB->ssi263.SC01_SaveSnapshot(yamlSaveHelper);
|
||||
|
||||
yamlSaveHelper.SaveHexUint4(SS_YAML_KEY_MB_UNIT_STATE, pMB->state[0]);
|
||||
yamlSaveHelper.SaveHexUint8(SS_YAML_KEY_AY_CURR_REG, pMB->nAYCurrentRegister[0]); // save all 8 bits (even though top 4 bits should be 0)
|
||||
@ -1271,6 +1276,8 @@ bool MockingboardCard::LoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT version
|
||||
UpdateIFRandIRQ(pMB, 0, pMB->sy6522.GetReg(SY6522::rIFR)); // Assert any pending IRQs (GH#677)
|
||||
AY8910_LoadSnapshot(yamlLoadHelper, subunit, AY8913_DEVICE_A, std::string(""));
|
||||
pMB->ssi263.LoadSnapshot(yamlLoadHelper, PH_Mockingboard, version); // Pre: SetVotraxPhoneme()
|
||||
if (subunit == 0) // has SC01
|
||||
pMB->ssi263.SC01_LoadSnapshot(yamlLoadHelper, version);
|
||||
|
||||
pMB->nAYCurrentRegister[0] = yamlLoadHelper.LoadUint(SS_YAML_KEY_AY_CURR_REG);
|
||||
|
||||
@ -1337,6 +1344,8 @@ void MockingboardCard::Phasor_SaveSnapshot(YamlSaveHelper& yamlSaveHelper)
|
||||
AY8910_SaveSnapshot(yamlSaveHelper, subunit, AY8913_DEVICE_A, std::string("-A"));
|
||||
AY8910_SaveSnapshot(yamlSaveHelper, subunit, AY8913_DEVICE_B, std::string("-B"));
|
||||
pMB->ssi263.SaveSnapshot(yamlSaveHelper);
|
||||
if (subunit == 0) // has SC01
|
||||
pMB->ssi263.SC01_SaveSnapshot(yamlSaveHelper);
|
||||
|
||||
yamlSaveHelper.SaveHexUint4(SS_YAML_KEY_MB_UNIT_STATE, pMB->state[0]);
|
||||
yamlSaveHelper.SaveHexUint4(SS_YAML_KEY_MB_UNIT_STATE_B, pMB->state[1]);
|
||||
@ -1405,6 +1414,8 @@ bool MockingboardCard::Phasor_LoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT
|
||||
AY8910_LoadSnapshot(yamlLoadHelper, subunit, AY8913_DEVICE_B, std::string("-B"));
|
||||
}
|
||||
pMB->ssi263.LoadSnapshot(yamlLoadHelper, m_phasorMode, version); // Pre: SetVotraxPhoneme()
|
||||
if (subunit == 0) // has SC01
|
||||
pMB->ssi263.SC01_LoadSnapshot(yamlLoadHelper, version);
|
||||
|
||||
pMB->nAYCurrentRegister[0] = yamlLoadHelper.LoadUint(SS_YAML_KEY_AY_CURR_REG);
|
||||
if (version >= 10)
|
||||
|
@ -4,7 +4,7 @@ AppleWin : An Apple //e emulator for Windows
|
||||
Copyright (C) 1994-1996, Michael O'Brien
|
||||
Copyright (C) 1999-2001, Oliver Schmidt
|
||||
Copyright (C) 2002-2005, Tom Charlesworth
|
||||
Copyright (C) 2006-2021, Tom Charlesworth, Michael Pohoreski, Nick Westgate
|
||||
Copyright (C) 2006-2024, Tom Charlesworth, Michael Pohoreski, Nick Westgate
|
||||
|
||||
AppleWin is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@ -23,7 +23,24 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
/* Description: SSI263 emulation
|
||||
*
|
||||
* Author: Various
|
||||
* Extra "spec" that's not obvious from the datasheet: (GH#175)
|
||||
* . Writes to regs 0,1,2 (and reg3.CTL=1) all de-assert the IRQ (and writes to reg3.CTL=0 and regs 4..7 don't) (GH#1197)
|
||||
* . A phoneme will continue playing back infinitely; unless the phoneme is changed or CTL=1.
|
||||
* . NB. if silenced (Amplitude=0) it's still playing.
|
||||
* . The IRQ is set at the end of the phoneme.
|
||||
* . If IRQ is then cleared, a new IRQ will occur when the phoneme completes again (but need to clear IRQ with a write to reg0, 1 or 2, even for Mockingboard-C).
|
||||
* . CTL=1 sets "PD" (Power Down / "standby") mode, also set at power-on.
|
||||
* . Registers can still be changed in this mode.
|
||||
* . IRQ de-asserted & D7=0.
|
||||
* . CTL=0 brings device out of "PD" mode, the mode will be set to DR1,DR0 and the phoneme P5-P0 will play.
|
||||
* . Setting mode to DR1:0 = %00 just disables A/!R (ie. disables interrupts), but otherwise retains the previous DR1:0 mode.
|
||||
* . If an IRQ was previously asserted then to set DR1:0=%00, you must go via CTL=1, which de-asserts the IRQ.
|
||||
* . Mockingboard-C: CTRL+RESET is not connected to !PD/!RST pin 18.
|
||||
* . Phasor: TODO: check with a 'scope.
|
||||
* . Phasor: with SSI263 ints disabled & reg0's DR1:0 != %00, then CTRL+RESET will cause SSI263 to enable ints & assert IRQ.
|
||||
* . it's as if the SSI263 does a CTL H->L to pick-up the new DR1:0. (Bug in SSI263? Assume it should remain in PD mode.)
|
||||
* . but if CTL=1, then CTRL+RESET has no effect.
|
||||
* . Power-on: PD=1 (so D7=0), reg4 (Filter Freq)=0xFF (other regs are seemingly random?).
|
||||
*/
|
||||
|
||||
#include "StdAfx.h"
|
||||
@ -54,26 +71,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
const DWORD SAMPLE_RATE_SSI263 = 22050;
|
||||
|
||||
// Duration/Phonome
|
||||
const BYTE DURATION_MODE_MASK = 0xC0;
|
||||
const BYTE DURATION_SHIFT = 6;
|
||||
const BYTE PHONEME_MASK = 0x3F;
|
||||
|
||||
const BYTE MODE_PHONEME_TRANSITIONED_INFLECTION = 0xC0; // IRQ active
|
||||
const BYTE MODE_PHONEME_IMMEDIATE_INFLECTION = 0x80; // IRQ active
|
||||
const BYTE MODE_FRAME_IMMEDIATE_INFLECTION = 0x40; // IRQ active
|
||||
const BYTE MODE_IRQ_DISABLED = 0x00;
|
||||
|
||||
// Rate/Inflection
|
||||
const BYTE RATE_MASK = 0xF0;
|
||||
const BYTE INFLECTION_MASK_H = 0x08; // I11
|
||||
const BYTE INFLECTION_MASK_L = 0x07; // I2..I0
|
||||
|
||||
// Ctrl/Art/Amp
|
||||
const BYTE CONTROL_MASK = 0x80;
|
||||
const BYTE ARTICULATION_MASK = 0x70;
|
||||
const BYTE AMPLITUDE_MASK = 0x0F;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#if LOG_SSI263B
|
||||
@ -129,7 +126,7 @@ BYTE SSI263::Read(ULONG nExecutedCycles)
|
||||
// . inverted "A/!R" is high for REQ (ie. Request, as phoneme nearly complete)
|
||||
// NB. this doesn't clear the IRQ
|
||||
|
||||
return MemReadFloatingBus(m_currentMode & 1, nExecutedCycles);
|
||||
return MemReadFloatingBus(m_currentMode.D7, nExecutedCycles);
|
||||
}
|
||||
|
||||
void SSI263::Write(BYTE nReg, BYTE nValue)
|
||||
@ -141,30 +138,20 @@ void SSI263::Write(BYTE nReg, BYTE nValue)
|
||||
ssiRegs[nReg] = nValue;
|
||||
#endif
|
||||
|
||||
// Notes:
|
||||
// . Phasor's text-to-speech playback has no CTL H->L
|
||||
// - ISR just writes CTL=0 (and new ART+AMP values), and writes DUR=x (and new PHON)
|
||||
// - since no CTL H->L, then DUR value doesn't take affect (so continue using previous)
|
||||
// - so the write to DURPHON must clear the IRQ
|
||||
// . Does a write of CTL=0 clear IRQ? (ie. CTL 0->0)
|
||||
// . Does a write of CTL=1 clear IRQ? (ie. CTL 0->1)
|
||||
// - SSI263 datasheet says: "Setting the Control bit (CTL) to a logic one puts the device into Power Down mode..."
|
||||
// . Does phoneme output only happen when CTL=0? (Otherwise device is in PD mode)
|
||||
|
||||
// SSI263 datasheet is not clear, but a write to DURPHON must clear the IRQ.
|
||||
// . Empirically writes to regs 0,1 & 2 all clear the IRQ (and writes to 3,4..7 don't) (GH#1197)
|
||||
// NB. For Mockingboard, A/!R is ack'ed by 6522's PCR handshake and D7 is cleared.
|
||||
if (m_cardMode == PH_Phasor && nReg <= SSI_RATEINF)
|
||||
// SSI263 datasheet is not clear, but a write to DURPHON de-asserts the IRQ and clears D7.
|
||||
// . Empirically writes to regs 0,1,2 (and reg3.CTL=1) all de-assert the IRQ (and writes to reg3.CTL=0 and regs 4..7 don't) (GH#1197)
|
||||
// NB. The same for Mockingboard as there's no automatic handshake from the 6522 (CA2 isn't connected to the SSI263). So writes to regs 0, 1 or 2 complete the "handshake".
|
||||
if (nReg <= SSI_RATEINF)
|
||||
{
|
||||
CpuIrqDeassert(IS_SPEECH);
|
||||
m_currentMode &= ~1; // Clear SSI263's D7 pin
|
||||
m_currentMode.D7 = 0;
|
||||
}
|
||||
|
||||
switch(nReg)
|
||||
{
|
||||
case SSI_DURPHON:
|
||||
#if LOG_SSI263
|
||||
if(g_fh) fprintf(g_fh, "DUR = 0x%02X, PHON = 0x%02X\n\n", nValue>>6, nValue&PHONEME_MASK);
|
||||
if (g_fh) fprintf(g_fh, "DUR = 0x%02X, PHON = 0x%02X\n\n", nValue>>6, nValue&PHONEME_MASK);
|
||||
LogOutput("DUR = %d, PHON = 0x%02X\n", nValue>>6, nValue&PHONEME_MASK);
|
||||
#endif
|
||||
#if LOG_SSI263B
|
||||
@ -172,25 +159,27 @@ void SSI263::Write(BYTE nReg, BYTE nValue)
|
||||
#endif
|
||||
|
||||
m_durationPhoneme = nValue;
|
||||
m_isVotraxPhoneme = false;
|
||||
|
||||
Play(nValue & PHONEME_MASK);
|
||||
if ((m_ctrlArtAmp & CONTROL_MASK) == 0)
|
||||
Play(m_durationPhoneme & PHONEME_MASK); // Play phoneme when *not* in power-down / standby mode
|
||||
break;
|
||||
case SSI_INFLECT:
|
||||
#if LOG_SSI263
|
||||
if(g_fh) fprintf(g_fh, "INF = 0x%02X\n", nValue);
|
||||
if (g_fh) fprintf(g_fh, "INF = 0x%02X\n", nValue);
|
||||
#endif
|
||||
m_inflection = nValue;
|
||||
break;
|
||||
|
||||
case SSI_RATEINF:
|
||||
#if LOG_SSI263
|
||||
if(g_fh) fprintf(g_fh, "RATE = 0x%02X, INF = 0x%02X\n", nValue>>4, nValue&0x0F);
|
||||
if (g_fh) fprintf(g_fh, "RATE = 0x%02X, INF = 0x%02X\n", nValue>>4, nValue&0x0F);
|
||||
#endif
|
||||
m_rateInflection = nValue;
|
||||
break;
|
||||
case SSI_CTTRAMP:
|
||||
#if LOG_SSI263
|
||||
if(g_fh) fprintf(g_fh, "CTRL = %d, ART = 0x%02X, AMP=0x%02X\n", nValue>>7, (nValue&ARTICULATION_MASK)>>4, nValue&LITUDE_MASK);
|
||||
if (g_fh) fprintf(g_fh, "CTRL = %d, ART = 0x%02X, AMP=0x%02X\n", nValue>>7, (nValue&ARTICULATION_MASK)>>4, nValue&LITUDE_MASK);
|
||||
//
|
||||
{
|
||||
bool H2L = (m_ctrlArtAmp & CONTROL_MASK) && !(nValue & CONTROL_MASK);
|
||||
@ -202,23 +191,25 @@ void SSI263::Write(BYTE nReg, BYTE nValue)
|
||||
if ( ((m_ctrlArtAmp & CONTROL_MASK) && !(nValue & CONTROL_MASK)) || ((nValue&0xF) == 0x0) ) // H->L or amp=0
|
||||
SSI_Output();
|
||||
#endif
|
||||
if((m_ctrlArtAmp & CONTROL_MASK) && !(nValue & CONTROL_MASK)) // H->L
|
||||
if ((m_ctrlArtAmp & CONTROL_MASK) && !(nValue & CONTROL_MASK)) // H->L
|
||||
{
|
||||
m_currentMode = m_durationPhoneme & DURATION_MODE_MASK;
|
||||
if (m_currentMode == MODE_IRQ_DISABLED)
|
||||
{
|
||||
// "Disables A/!R output only; does not change previous A/!R response" (SSI263 datasheet)
|
||||
// CpuIrqDeassert(IS_SPEECH);
|
||||
}
|
||||
// NB. Just changed from CTL=1 (power-down) - where IRQ was already de-asserted & D7=0
|
||||
// . So CTL H->L never affects IRQ or D7
|
||||
SetDeviceModeAndInts();
|
||||
|
||||
// Device out of power down / "standby" mode, so play phoneme
|
||||
m_isVotraxPhoneme = false;
|
||||
Play(m_durationPhoneme & PHONEME_MASK);
|
||||
}
|
||||
|
||||
m_ctrlArtAmp = nValue;
|
||||
|
||||
// "Setting the Control bit (CTL) to a logic one puts the device into Power Down mode..." (SSI263 datasheet)
|
||||
// . this silences the phoneme - actually "turns off the excitation sources and analog circuits"
|
||||
if (m_ctrlArtAmp & CONTROL_MASK)
|
||||
{
|
||||
// CpuIrqDeassert(IS_SPEECH);
|
||||
// m_currentMode &= ~1; // Clear SSI263's D7 pin
|
||||
CpuIrqDeassert(IS_SPEECH);
|
||||
m_currentMode.D7 = 0;
|
||||
}
|
||||
break;
|
||||
case SSI_FILFREQ: // RegAddr.b2=1 (b1 & b0 are: don't care)
|
||||
@ -231,6 +222,20 @@ void SSI263::Write(BYTE nReg, BYTE nValue)
|
||||
}
|
||||
}
|
||||
|
||||
void SSI263::SetDeviceModeAndInts(void)
|
||||
{
|
||||
if ((m_durationPhoneme & DURATION_MODE_MASK) != MODE_IRQ_DISABLED)
|
||||
{
|
||||
m_currentMode.function = (m_durationPhoneme & DURATION_MODE_MASK) >> DURATION_MODE_SHIFT;
|
||||
m_currentMode.enableInts = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// "Disables A/!R output only; does not change previous A/!R response" (SSI263 datasheet)
|
||||
m_currentMode.enableInts = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
const BYTE SSI263::m_Votrax2SSI263[/*64*/] =
|
||||
@ -310,6 +315,7 @@ void SSI263::Votrax_Write(BYTE value)
|
||||
LogOutput("SC01: %02X (= SSI263: %02X)\n", value, m_Votrax2SSI263[value & PHONEME_MASK]);
|
||||
#endif
|
||||
m_isVotraxPhoneme = true;
|
||||
m_votraxPhoneme = value & PHONEME_MASK;
|
||||
|
||||
// !A/R: Acknowledge receipt of phoneme data (signal goes from high to low)
|
||||
UpdateIFR(m_device, SY6522::IxR_VOTRAX, 0);
|
||||
@ -317,7 +323,7 @@ void SSI263::Votrax_Write(BYTE value)
|
||||
// NB. Don't set reg0.DUR, as SC01's phoneme duration doesn't change with pitch (empirically determined from MAME's SC01 emulation)
|
||||
//m_durationPhoneme = value; // Set reg0.DUR = I1:0 (inflection or pitch)
|
||||
m_durationPhoneme = 0;
|
||||
Play(m_Votrax2SSI263[value & PHONEME_MASK]);
|
||||
Play(m_Votrax2SSI263[m_votraxPhoneme]);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -366,6 +372,7 @@ void SSI263::Play(unsigned int nPhoneme)
|
||||
m_phonemeAccurateLengthRemaining = m_phonemeLengthRemaining;
|
||||
m_phonemePlaybackAndDebugger = (g_nAppMode == MODE_STEPPING || g_nAppMode == MODE_DEBUG);
|
||||
m_phonemeCompleteByFullSpeed = false;
|
||||
m_phonemeLeadoutLength = m_phonemeLengthRemaining / 10; // Arbitrary! (TODO: determine a more accurate factor)
|
||||
|
||||
if (bPause)
|
||||
{
|
||||
@ -581,18 +588,22 @@ void SSI263::Update(void)
|
||||
|
||||
//-------------
|
||||
|
||||
const double amplitude = !m_isVotraxPhoneme ? (double)(m_ctrlArtAmp & AMPLITUDE_MASK) / (double)AMPLITUDE_MASK : 1.0;
|
||||
const double amplitude = m_isVotraxPhoneme ? 1.0
|
||||
: m_ctrlArtAmp & CONTROL_MASK ? 0.0 // Power-down / standby
|
||||
: m_filterFreq == FILTER_FREQ_SILENCE ? 0.0
|
||||
: (double)(m_ctrlArtAmp & AMPLITUDE_MASK) / (double)AMPLITUDE_MASK;
|
||||
|
||||
bool bSpeechIRQ = false;
|
||||
|
||||
{
|
||||
const BYTE DUR = m_durationPhoneme >> DURATION_SHIFT;
|
||||
const BYTE DUR = (m_currentMode.function == (MODE_FRAME_IMMEDIATE_INFLECTION >> DURATION_MODE_SHIFT)) ? 3 // Frame timing mode
|
||||
: m_durationPhoneme >> DURATION_MODE_SHIFT; // Phoneme timing mode
|
||||
const BYTE numSamplesToAvg = (DUR <= 1) ? 1 :
|
||||
(DUR == 2) ? 2 :
|
||||
4;
|
||||
|
||||
short* pMixBuffer = &m_mixBufferSSI263[0];
|
||||
int zeroSize = nNumSamples;
|
||||
UINT zeroSize = nNumSamples;
|
||||
|
||||
if (m_phonemeLengthRemaining && !prefillBufferOnInit)
|
||||
{
|
||||
@ -633,7 +644,12 @@ void SSI263::Update(void)
|
||||
}
|
||||
|
||||
if (zeroSize)
|
||||
{
|
||||
memset(pMixBuffer, 0, zeroSize * sizeof(short));
|
||||
|
||||
if (!prefillBufferOnInit)
|
||||
m_phonemeLeadoutLength -= (m_phonemeLeadoutLength > zeroSize) ? zeroSize : m_phonemeLeadoutLength;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
@ -666,8 +682,24 @@ void SSI263::Update(void)
|
||||
{
|
||||
// NB. if m_phonemePlaybackAndDebugger==true, then "m_phonemeAccurateLengthRemaining!=0" must be true.
|
||||
// Since in UpdateAccurateLength(), (when m_phonemePlaybackAndDebugger==true) then m_phonemeAccurateLengthRemaining decs to zero.
|
||||
#if _DEBUG
|
||||
if (m_phonemePlaybackAndDebugger)
|
||||
{
|
||||
_ASSERT(m_phonemeAccurateLengthRemaining); // Check this!
|
||||
}
|
||||
#endif
|
||||
if (!m_phonemePlaybackAndDebugger /*|| m_phonemeAccurateLengthRemaining*/) // superfluous, so commented out (see above)
|
||||
{
|
||||
UpdateIRQ();
|
||||
}
|
||||
}
|
||||
|
||||
if (m_phonemeLeadoutLength == 0)
|
||||
{
|
||||
if (!m_isVotraxPhoneme)
|
||||
Play(m_durationPhoneme & PHONEME_MASK); // Repeat this phoneme again
|
||||
else
|
||||
Play(m_Votrax2SSI263[m_votraxPhoneme]); // Votrax phoneme repeats too (tested in MAME 0.262)
|
||||
}
|
||||
}
|
||||
|
||||
@ -689,7 +721,7 @@ void SSI263::UpdateAccurateLength(void)
|
||||
const double nIrqFreq = g_fCurrentCLK6502 / updateInterval + 0.5; // Round-up
|
||||
const int nNumSamplesPerPeriod = (int)((double)(SAMPLE_RATE_SSI263) / nIrqFreq); // Eg. For 60Hz this is 367
|
||||
|
||||
const BYTE DUR = m_durationPhoneme >> DURATION_SHIFT;
|
||||
const BYTE DUR = m_durationPhoneme >> DURATION_MODE_SHIFT;
|
||||
|
||||
const UINT numSamples = nNumSamplesPerPeriod * (DUR+1);
|
||||
if (m_phonemeAccurateLengthRemaining > numSamples)
|
||||
@ -733,32 +765,37 @@ void SSI263::UpdateIRQ(void)
|
||||
// Pre: m_isVotraxPhoneme, m_cardMode, m_device
|
||||
void SSI263::SetSpeechIRQ(void)
|
||||
{
|
||||
if (!m_isVotraxPhoneme)
|
||||
if (!m_isVotraxPhoneme && (m_ctrlArtAmp & CONTROL_MASK) == 0)
|
||||
{
|
||||
// Always set SSI263's D7 pin regardless of SSI263 mode (DR1:0), including MODE_IRQ_DISABLED
|
||||
m_currentMode |= 1; // Set SSI263's D7 pin
|
||||
|
||||
if ((m_currentMode & DURATION_MODE_MASK) != MODE_IRQ_DISABLED)
|
||||
if (m_currentMode.enableInts)
|
||||
{
|
||||
if (m_cardMode == PH_Mockingboard)
|
||||
{
|
||||
if ((GetPCR(m_device) & 1) == 0) // CA1 Latch/Input = 0 (Negative active edge)
|
||||
UpdateIFR(m_device, 0, SY6522::IxR_SSI263);
|
||||
if (GetPCR(m_device) == 0x0C) // CA2 Control = b#110 (Low output)
|
||||
m_currentMode &= ~1; // Clear SSI263's D7 pin (cleared by 6522's PCR CA1/CA2 handshake)
|
||||
|
||||
// NB. Don't set CTL=1, as Mockingboard(SMS) speech doesn't work (it sets MODE_IRQ_DISABLED mode during ISR)
|
||||
//pMB->SpeechChip.CtrlArtAmp |= CONTROL_MASK; // 6522's CA2 sets Power Down mode (pin 18), which sets Control bit
|
||||
if (m_currentMode.D7 == 0)
|
||||
{
|
||||
// 6522's PCR = 0x0C (all SSI263 speech routine use this value, but 0x00 will do equally as well!)
|
||||
// . b3:1 CA2 Control = b#110 (Low output) - not connected
|
||||
// . b0 CA1 Latch/Input = 0 (Negative active edge) - input from SSI263's A/!R
|
||||
if ((GetPCR(m_device) & 1) == 0) // Level change from SSI263's A/!R, latch this as an interrupt
|
||||
UpdateIFR(m_device, 0, SY6522::IxR_SSI263);
|
||||
}
|
||||
}
|
||||
else if (m_cardMode == PH_Phasor) // Phasor's SSI263 IRQ (A/!R) line is *also* wired directly to the 6502's IRQ (as well as the 6522's CA1)
|
||||
else if (m_cardMode == PH_Phasor)
|
||||
{
|
||||
// Phasor (in native mode): SSI263 IRQ (A/!R) pin is connected directly to the 6502's IRQ
|
||||
// . And Mockingboard mode: A/!R is connected to the 6522's CA1
|
||||
CpuIrqAssert(IS_SPEECH);
|
||||
}
|
||||
else
|
||||
{
|
||||
_ASSERT(0);
|
||||
_ASSERT(m_cardMode == PH_EchoPlus);
|
||||
// SSI263 not visible from Echo+ mode, but still continues to operate
|
||||
}
|
||||
}
|
||||
|
||||
// Always set SSI263's D7 pin regardless of SSI263 mode (DR1:0), including when SSI263 ints are disabled (via MODE_IRQ_DISABLED)
|
||||
// NB. Don't set D7 when in power-down / standby mode.
|
||||
m_currentMode.D7 = 1;
|
||||
}
|
||||
|
||||
//
|
||||
@ -766,15 +803,34 @@ void SSI263::SetSpeechIRQ(void)
|
||||
if (m_isVotraxPhoneme && GetPCR(m_device) == 0xB0)
|
||||
{
|
||||
// !A/R: Time-out of old phoneme (signal goes from low to high)
|
||||
|
||||
UpdateIFR(m_device, 0, SY6522::IxR_VOTRAX);
|
||||
|
||||
m_isVotraxPhoneme = false;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SSI263::SetCardMode(PHASOR_MODE mode)
|
||||
{
|
||||
const PHASOR_MODE oldCardMode = m_cardMode;
|
||||
m_cardMode = mode;
|
||||
|
||||
if (oldCardMode == m_cardMode)
|
||||
return;
|
||||
|
||||
// mode change
|
||||
|
||||
if (m_currentMode.D7 == 1)
|
||||
{
|
||||
m_currentMode.D7 = 0; // So that \PH_Mockingboard\ path sets IFR. Post: D7=1
|
||||
SetSpeechIRQ();
|
||||
}
|
||||
|
||||
if (m_cardMode != PH_Phasor)
|
||||
CpuIrqDeassert(IS_SPEECH);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool SSI263::DSInit(void)
|
||||
{
|
||||
//
|
||||
@ -811,10 +867,27 @@ void SSI263::DSUninit(void)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void SSI263::Reset(void)
|
||||
// SSI263 phoneme continues to play after CTRL+RESET (tested on real h/w)
|
||||
// Votrax phoneme continues to play after CTRL+RESET (tested on MAME 0.262)
|
||||
void SSI263::Reset(const bool powerCycle, const bool isPhasorCard)
|
||||
{
|
||||
if (!powerCycle)
|
||||
{
|
||||
if (isPhasorCard)
|
||||
{
|
||||
// Empirically observed it does CTL H->L to enable ints (and set the device mode?) (GH#175)
|
||||
// NB. CTRL+RESET doesn't clear m_ctrlArtAmp.CTL (ie. if the device is in power-down/standby mode then ignore RST)
|
||||
// Speculate that there's a bug in the SSI263 and that RST should put the device into power-down/standby mode (ie. silence the device)
|
||||
// TODO: Stick a 'scope on !PD/!RST pin 18 to see what the Phasor h/w does.
|
||||
if ((m_ctrlArtAmp & CONTROL_MASK) == 0)
|
||||
SetDeviceModeAndInts();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Stop();
|
||||
ResetState();
|
||||
ResetState(powerCycle);
|
||||
CpuIrqDeassert(IS_SPEECH);
|
||||
}
|
||||
|
||||
@ -884,7 +957,7 @@ void SSI263::SaveSnapshot(YamlSaveHelper& yamlSaveHelper)
|
||||
yamlSaveHelper.SaveHexUint8(SS_YAML_KEY_SSI263_REG_RATE_INF, m_rateInflection);
|
||||
yamlSaveHelper.SaveHexUint8(SS_YAML_KEY_SSI263_REG_CTRL_ART_AMP, m_ctrlArtAmp);
|
||||
yamlSaveHelper.SaveHexUint8(SS_YAML_KEY_SSI263_REG_FILTER_FREQ, m_filterFreq);
|
||||
yamlSaveHelper.SaveHexUint8(SS_YAML_KEY_SSI263_CURRENT_MODE, m_currentMode);
|
||||
yamlSaveHelper.SaveHexUint8(SS_YAML_KEY_SSI263_CURRENT_MODE, m_currentMode.mode);
|
||||
yamlSaveHelper.SaveBool(SS_YAML_KEY_SSI263_ACTIVE_PHONEME, IsPhonemeActive());
|
||||
}
|
||||
|
||||
@ -898,10 +971,23 @@ void SSI263::LoadSnapshot(YamlLoadHelper& yamlLoadHelper, PHASOR_MODE mode, UINT
|
||||
m_rateInflection = yamlLoadHelper.LoadUint(SS_YAML_KEY_SSI263_REG_RATE_INF);
|
||||
m_ctrlArtAmp = yamlLoadHelper.LoadUint(SS_YAML_KEY_SSI263_REG_CTRL_ART_AMP);
|
||||
m_filterFreq = yamlLoadHelper.LoadUint(SS_YAML_KEY_SSI263_REG_FILTER_FREQ);
|
||||
m_currentMode = yamlLoadHelper.LoadUint(SS_YAML_KEY_SSI263_CURRENT_MODE);
|
||||
m_currentMode.mode = yamlLoadHelper.LoadUint(SS_YAML_KEY_SSI263_CURRENT_MODE);
|
||||
bool activePhoneme = (version >= 7) ? yamlLoadHelper.LoadBool(SS_YAML_KEY_SSI263_ACTIVE_PHONEME) : false;
|
||||
m_currentActivePhoneme = !activePhoneme ? -1 : 0x00; // Not important which phoneme, since UpdateIRQ() resets this
|
||||
|
||||
if (version < 12)
|
||||
{
|
||||
if (m_currentMode.function == 0) // invalid function (but in older versions this was accepted)
|
||||
{
|
||||
m_currentMode.function = MODE_PHONEME_TRANSITIONED_INFLECTION >> DURATION_MODE_SHIFT; // Typically this is used
|
||||
m_currentMode.enableInts = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_currentMode.enableInts = 1;
|
||||
}
|
||||
}
|
||||
|
||||
yamlLoadHelper.PopMap();
|
||||
|
||||
//
|
||||
@ -910,7 +996,7 @@ void SSI263::LoadSnapshot(YamlLoadHelper& yamlLoadHelper, PHASOR_MODE mode, UINT
|
||||
SetCardMode(mode);
|
||||
|
||||
// Only need to directly assert IRQ for Phasor mode (for Mockingboard mode it's done via UpdateIFR() in parent)
|
||||
if (m_cardMode == PH_Phasor && (m_currentMode & DURATION_MODE_MASK) != MODE_IRQ_DISABLED && (m_currentMode & 1))
|
||||
if (m_cardMode == PH_Phasor && (m_ctrlArtAmp & CONTROL_MASK) == 0 && m_currentMode.enableInts && m_currentMode.D7 == 1)
|
||||
CpuIrqAssert(IS_SPEECH);
|
||||
|
||||
if (IsPhonemeActive())
|
||||
@ -918,3 +1004,30 @@ void SSI263::LoadSnapshot(YamlLoadHelper& yamlLoadHelper, PHASOR_MODE mode, UINT
|
||||
|
||||
m_lastUpdateCycle = GetLastCumulativeCycles();
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
|
||||
#define SS_YAML_KEY_SC01 "SC01"
|
||||
// NB. No version - this is determined by the parent "Mockingboard C" or "Phasor" unit
|
||||
|
||||
#define SS_YAML_KEY_SC01_PHONEME "SC01 Phoneme"
|
||||
#define SS_YAML_KEY_SC01_ACTIVE_PHONEME "SC01 Active Phoneme"
|
||||
|
||||
void SSI263::SC01_SaveSnapshot(YamlSaveHelper& yamlSaveHelper)
|
||||
{
|
||||
YamlSaveHelper::Label label(yamlSaveHelper, "%s:\n", SS_YAML_KEY_SC01);
|
||||
|
||||
yamlSaveHelper.SaveHexUint8(SS_YAML_KEY_SC01_PHONEME, m_votraxPhoneme);
|
||||
yamlSaveHelper.SaveBool(SS_YAML_KEY_SC01_ACTIVE_PHONEME, m_isVotraxPhoneme);
|
||||
}
|
||||
|
||||
void SSI263::SC01_LoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT version)
|
||||
{
|
||||
if (!yamlLoadHelper.GetSubMap(SS_YAML_KEY_SC01))
|
||||
throw std::runtime_error("Card: Expected key: " SS_YAML_KEY_SC01);
|
||||
|
||||
m_votraxPhoneme = yamlLoadHelper.LoadUint(SS_YAML_KEY_SC01_PHONEME);
|
||||
m_isVotraxPhoneme = yamlLoadHelper.LoadBool(SS_YAML_KEY_SC01_ACTIVE_PHONEME);
|
||||
|
||||
yamlLoadHelper.PopMap();
|
||||
}
|
||||
|
@ -11,17 +11,18 @@ public:
|
||||
m_cardMode = PH_Mockingboard;
|
||||
m_pPhonemeData00 = NULL;
|
||||
|
||||
ResetState();
|
||||
ResetState(true);
|
||||
}
|
||||
~SSI263(void)
|
||||
{
|
||||
delete [] m_pPhonemeData00;
|
||||
}
|
||||
|
||||
void ResetState(void)
|
||||
void ResetState(const bool powerCycle)
|
||||
{
|
||||
m_currentActivePhoneme = -1;
|
||||
m_isVotraxPhoneme = false;
|
||||
m_votraxPhoneme = 0;
|
||||
m_cyclesThisAudioFrame = 0;
|
||||
|
||||
//
|
||||
@ -34,6 +35,7 @@ public:
|
||||
m_phonemeAccurateLengthRemaining = 0;
|
||||
m_phonemePlaybackAndDebugger = false;
|
||||
m_phonemeCompleteByFullSpeed = false;
|
||||
m_phonemeLeadoutLength = 0;
|
||||
|
||||
//
|
||||
|
||||
@ -45,13 +47,19 @@ public:
|
||||
|
||||
//
|
||||
|
||||
m_durationPhoneme = 0;
|
||||
// After a chip power-on, if the first thing done is CTL=0, then empirically it can be observed that:
|
||||
// . enableInts = 1 (mostly an SSI263 interrupt occurs, but not always)
|
||||
// . DR1:0 != b#00 (since enableInts is set to 1, except when no interrupt => DR1:0 == b#00)
|
||||
m_durationPhoneme = MODE_PHONEME_TRANSITIONED_INFLECTION; // Typical function & phoneme=$00
|
||||
m_inflection = 0;
|
||||
m_rateInflection = 0;
|
||||
m_ctrlArtAmp = 0;
|
||||
m_filterFreq = 0;
|
||||
m_ctrlArtAmp = powerCycle ? CONTROL_MASK : 0; // Chip power-on, so CTL=1 (power-down / standby)
|
||||
m_filterFreq = powerCycle ? FILTER_FREQ_SILENCE : 0; // Empirically observed at chip power-on (GH#1302)
|
||||
|
||||
m_currentMode = 0;
|
||||
m_currentMode.mode = 0;
|
||||
m_currentMode.function = 0; // Set at runtime when CTL=0
|
||||
m_currentMode.enableInts = 0; // Set at runtime when CTL=0
|
||||
m_currentMode.D7 = 0; // Since in power-down mode (as per SSI263 datasheet)
|
||||
|
||||
//
|
||||
|
||||
@ -60,12 +68,12 @@ public:
|
||||
}
|
||||
|
||||
void SetDevice(UINT device) { m_device = device; }
|
||||
void SetCardMode(PHASOR_MODE mode) { m_cardMode = mode; }
|
||||
void SetCardMode(PHASOR_MODE mode);
|
||||
|
||||
bool DSInit(void);
|
||||
void DSUninit(void);
|
||||
|
||||
void Reset(void);
|
||||
void Reset(const bool powerCycle, const bool isPhasorCard);
|
||||
bool IsPhonemeActive(void) { return m_currentActivePhoneme >= 0; }
|
||||
|
||||
BYTE Read(ULONG nExecutedCycles);
|
||||
@ -85,12 +93,15 @@ public:
|
||||
|
||||
void SaveSnapshot(class YamlSaveHelper& yamlSaveHelper);
|
||||
void LoadSnapshot(class YamlLoadHelper& yamlLoadHelper, PHASOR_MODE mode, UINT version);
|
||||
void SC01_SaveSnapshot(YamlSaveHelper& yamlSaveHelper);
|
||||
void SC01_LoadSnapshot(YamlLoadHelper& yamlLoadHelper, UINT version);
|
||||
|
||||
private:
|
||||
void Play(unsigned int nPhoneme);
|
||||
void Stop(void);
|
||||
void UpdateIRQ(void);
|
||||
void UpdateAccurateLength(void);
|
||||
void SetDeviceModeAndInts(void);
|
||||
|
||||
UINT64 GetLastCumulativeCycles(void);
|
||||
void UpdateIFR(BYTE nDevice, BYTE clr_mask, BYTE set_mask);
|
||||
@ -105,13 +116,37 @@ private:
|
||||
|
||||
//
|
||||
|
||||
// Duration/Phonome
|
||||
static const BYTE DURATION_MODE_MASK = 0xC0;
|
||||
static const BYTE DURATION_MODE_SHIFT = 6;
|
||||
static const BYTE PHONEME_MASK = 0x3F;
|
||||
|
||||
static const BYTE MODE_PHONEME_TRANSITIONED_INFLECTION = 0xC0;
|
||||
static const BYTE MODE_PHONEME_IMMEDIATE_INFLECTION = 0x80;
|
||||
static const BYTE MODE_FRAME_IMMEDIATE_INFLECTION = 0x40;
|
||||
static const BYTE MODE_IRQ_DISABLED = 0x00; // disable interrupts, but retains one of the 3 modes
|
||||
|
||||
// Rate/Inflection
|
||||
static const BYTE RATE_MASK = 0xF0;
|
||||
static const BYTE INFLECTION_MASK_H = 0x08; // I11
|
||||
static const BYTE INFLECTION_MASK_L = 0x07; // I2..I0
|
||||
|
||||
// Ctrl/Art/Amp
|
||||
static const BYTE ARTICULATION_MASK = 0x70;
|
||||
static const BYTE AMPLITUDE_MASK = 0x0F;
|
||||
static const BYTE CONTROL_MASK = 0x80;
|
||||
|
||||
// Filter frequency range
|
||||
static const BYTE FILTER_FREQ_SILENCE = 0xFF;
|
||||
|
||||
UINT m_slot;
|
||||
BYTE m_device; // SSI263 device# which is generating phoneme-complete IRQ (and only required whilst Mockingboard isn't a class)
|
||||
PHASOR_MODE m_cardMode;
|
||||
short* m_pPhonemeData00;
|
||||
|
||||
int m_currentActivePhoneme;
|
||||
int m_currentActivePhoneme; // -1 (if none) or SSI263 or SC01 phoneme
|
||||
bool m_isVotraxPhoneme;
|
||||
BYTE m_votraxPhoneme;
|
||||
UINT m_cyclesThisAudioFrame;
|
||||
|
||||
//
|
||||
@ -124,6 +159,7 @@ private:
|
||||
UINT m_phonemeAccurateLengthRemaining; // length in samples, decremented by cycles executed
|
||||
bool m_phonemePlaybackAndDebugger;
|
||||
bool m_phonemeCompleteByFullSpeed;
|
||||
UINT m_phonemeLeadoutLength; // length in samples, decremented after \m_phonemeLengthRemaining\ goes to zero. Delay until phoneme repeats
|
||||
|
||||
//
|
||||
|
||||
@ -140,7 +176,17 @@ private:
|
||||
BYTE m_ctrlArtAmp;
|
||||
BYTE m_filterFreq;
|
||||
|
||||
BYTE m_currentMode; // b7:6=Mode; b0=D7 pin (for IRQ)
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
BYTE D7 : 1; // b0=D7 pin (for IRQ)
|
||||
BYTE reserved : 4;
|
||||
BYTE enableInts : 1; // b5 = enable A/!R (ie. interrupts)
|
||||
BYTE function : 2; // b7:6 = function
|
||||
};
|
||||
BYTE mode;
|
||||
} m_currentMode;
|
||||
|
||||
// Debug
|
||||
bool m_dbgFirst;
|
||||
|
Loading…
Reference in New Issue
Block a user