205
src/sound.c
205
src/sound.c
@@ -70,6 +70,7 @@ static int32 RectDutyCount[2];
|
|||||||
static uint8 sweepon[2];
|
static uint8 sweepon[2];
|
||||||
static int32 curfreq[2];
|
static int32 curfreq[2];
|
||||||
static uint8 SweepCount[2];
|
static uint8 SweepCount[2];
|
||||||
|
static uint8 sweepReload[2];
|
||||||
|
|
||||||
static uint16 nreg = 0;
|
static uint16 nreg = 0;
|
||||||
|
|
||||||
@@ -84,6 +85,9 @@ int32 nesincsize = 0;
|
|||||||
uint32 soundtsinc = 0;
|
uint32 soundtsinc = 0;
|
||||||
uint32 soundtsi = 0;
|
uint32 soundtsi = 0;
|
||||||
static int32 sqacc[2];
|
static int32 sqacc[2];
|
||||||
|
static uint32 lq_tcout;
|
||||||
|
static int32 lq_triacc;
|
||||||
|
static int32 lq_noiseacc;
|
||||||
/* LQ variables segment ends. */
|
/* LQ variables segment ends. */
|
||||||
|
|
||||||
static int32 lengthcount[4];
|
static int32 lengthcount[4];
|
||||||
@@ -125,9 +129,9 @@ static const uint32 PALDMCTable[0x10] =
|
|||||||
* $4013 - Size register: Size in bytes = (V+1)*64
|
* $4013 - Size register: Size in bytes = (V+1)*64
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*static*/ int32 DMCacc = 1;
|
static int32 DMCacc = 1;
|
||||||
static int32 DMCPeriod = 0;
|
static int32 DMCPeriod = 0;
|
||||||
/*static*/ uint8 DMCBitCount = 0;
|
static uint8 DMCBitCount = 0;
|
||||||
|
|
||||||
static uint8 DMCAddressLatch = 0, DMCSizeLatch = 0; /* writes to 4012 and 4013 */
|
static uint8 DMCAddressLatch = 0, DMCSizeLatch = 0; /* writes to 4012 and 4013 */
|
||||||
static uint8 DMCFormat = 0; /* Write to $4010 */
|
static uint8 DMCFormat = 0; /* Write to $4010 */
|
||||||
@@ -175,21 +179,12 @@ static int FASTAPASS(2) CheckFreq(uint32 cf, uint8 sr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void SQReload(int x, uint8 V) {
|
static void SQReload(int x, uint8 V) {
|
||||||
if (EnabledChannels & (1 << x)) {
|
if (EnabledChannels & (1 << x))
|
||||||
if (x)
|
|
||||||
DoSQ2();
|
|
||||||
else
|
|
||||||
DoSQ1();
|
|
||||||
lengthcount[x] = lengthtable[(V >> 3) & 0x1f];
|
lengthcount[x] = lengthtable[(V >> 3) & 0x1f];
|
||||||
}
|
|
||||||
|
|
||||||
sweepon[x] = PSG[(x << 2) | 1] & 0x80;
|
|
||||||
curfreq[x] = PSG[(x << 2) | 0x2] | ((V & 7) << 8);
|
|
||||||
SweepCount[x] = ((PSG[(x << 2) | 0x1] >> 4) & 7) + 1;
|
|
||||||
|
|
||||||
|
curfreq[x] = curfreq[x] & 0xff | ((V & 7) << 8);
|
||||||
RectDutyCount[x] = 7;
|
RectDutyCount[x] = 7;
|
||||||
EnvUnits[x].reloaddec = 1;
|
EnvUnits[x].reloaddec = 1;
|
||||||
/* reloadfreq[x]=1; */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static DECLFW(Write_PSG) {
|
static DECLFW(Write_PSG) {
|
||||||
@@ -204,7 +199,9 @@ static DECLFW(Write_PSG) {
|
|||||||
V = (V & 0x3F) | ((V & 0x80) >> 1) | ((V & 0x40) << 1);
|
V = (V & 0x3F) | ((V & 0x80) >> 1) | ((V & 0x40) << 1);
|
||||||
break;
|
break;
|
||||||
case 0x1:
|
case 0x1:
|
||||||
sweepon[0] = V & 0x80;
|
DoSQ1();
|
||||||
|
sweepReload[0] = 1;
|
||||||
|
sweepon[0] = (V & 0x80);
|
||||||
break;
|
break;
|
||||||
case 0x2:
|
case 0x2:
|
||||||
DoSQ1();
|
DoSQ1();
|
||||||
@@ -212,6 +209,7 @@ static DECLFW(Write_PSG) {
|
|||||||
curfreq[0] |= V;
|
curfreq[0] |= V;
|
||||||
break;
|
break;
|
||||||
case 0x3:
|
case 0x3:
|
||||||
|
DoSQ1();
|
||||||
SQReload(0, V);
|
SQReload(0, V);
|
||||||
break;
|
break;
|
||||||
case 0x4:
|
case 0x4:
|
||||||
@@ -222,7 +220,9 @@ static DECLFW(Write_PSG) {
|
|||||||
V = (V & 0x3F) | ((V & 0x80) >> 1) | ((V & 0x40) << 1);
|
V = (V & 0x3F) | ((V & 0x80) >> 1) | ((V & 0x40) << 1);
|
||||||
break;
|
break;
|
||||||
case 0x5:
|
case 0x5:
|
||||||
sweepon[1] = V & 0x80;
|
DoSQ2();
|
||||||
|
sweepReload[1] = 1;
|
||||||
|
sweepon[1] = (V & 0x80);
|
||||||
break;
|
break;
|
||||||
case 0x6:
|
case 0x6:
|
||||||
DoSQ2();
|
DoSQ2();
|
||||||
@@ -230,6 +230,7 @@ static DECLFW(Write_PSG) {
|
|||||||
curfreq[1] |= V;
|
curfreq[1] |= V;
|
||||||
break;
|
break;
|
||||||
case 0x7:
|
case 0x7:
|
||||||
|
DoSQ2();
|
||||||
SQReload(1, V);
|
SQReload(1, V);
|
||||||
break;
|
break;
|
||||||
case 0xa:
|
case 0xa:
|
||||||
@@ -288,17 +289,17 @@ static DECLFW(Write_DMCRegs) {
|
|||||||
break;
|
break;
|
||||||
case 0x01: DoPCM();
|
case 0x01: DoPCM();
|
||||||
RawDALatch = V & 0x7F;
|
RawDALatch = V & 0x7F;
|
||||||
if (RawDALatch)
|
if (RawDALatch)
|
||||||
DMC_7bit = 1;
|
DMC_7bit = 1;
|
||||||
break;
|
break;
|
||||||
case 0x02:
|
case 0x02:
|
||||||
DMCAddressLatch = V;
|
DMCAddressLatch = V;
|
||||||
if (V)
|
if (V)
|
||||||
DMC_7bit = 0;
|
DMC_7bit = 0;
|
||||||
break;
|
break;
|
||||||
case 0x03:
|
case 0x03:
|
||||||
DMCSizeLatch = V;
|
DMCSizeLatch = V;
|
||||||
if (V)
|
if (V)
|
||||||
DMC_7bit = 0;
|
DMC_7bit = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -313,6 +314,7 @@ static DECLFW(StatusWrite) {
|
|||||||
DoTriangle();
|
DoTriangle();
|
||||||
DoNoise();
|
DoNoise();
|
||||||
DoPCM();
|
DoPCM();
|
||||||
|
|
||||||
for (x = 0; x < 4; x++)
|
for (x = 0; x < 4; x++)
|
||||||
if (!(V & (1 << x))) lengthcount[x] = 0; /* Force length counters to 0. */
|
if (!(V & (1 << x))) lengthcount[x] = 0; /* Force length counters to 0. */
|
||||||
|
|
||||||
@@ -336,9 +338,9 @@ static DECLFR(StatusRead) {
|
|||||||
for (x = 0; x < 4; x++) ret |= lengthcount[x] ? (1 << x) : 0;
|
for (x = 0; x < 4; x++) ret |= lengthcount[x] ? (1 << x) : 0;
|
||||||
if (DMCSize) ret |= 0x10;
|
if (DMCSize) ret |= 0x10;
|
||||||
|
|
||||||
#ifdef FCEUDEF_DEBUGGER
|
#ifdef FCEUDEF_DEBUGGER
|
||||||
if (!fceuindbg)
|
if (!fceuindbg)
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
SIRQStat &= ~0x40;
|
SIRQStat &= ~0x40;
|
||||||
X6502_IRQEnd(FCEU_IQFCOUNT);
|
X6502_IRQEnd(FCEU_IQFCOUNT);
|
||||||
@@ -371,34 +373,25 @@ static void FASTAPASS(1) FrameSoundStuff(int V) {
|
|||||||
/* Frequency Sweep Code Here */
|
/* Frequency Sweep Code Here */
|
||||||
/* xxxx 0000 */
|
/* xxxx 0000 */
|
||||||
/* xxxx = hz. 120/(x+1)*/
|
/* xxxx = hz. 120/(x+1)*/
|
||||||
if (sweepon[P]) {
|
/* http://wiki.nesdev.com/w/index.php/APU_Sweep */
|
||||||
int32 mod = 0;
|
if (SweepCount[P] > 0) SweepCount[P]--;
|
||||||
|
if (SweepCount[P] <= 0) {
|
||||||
if (SweepCount[P] > 0) SweepCount[P]--;
|
uint32 sweepShift = (PSG[(P << 2) + 0x1] & 7);
|
||||||
if (SweepCount[P] <= 0) {
|
if (sweepon[P] && sweepShift && curfreq[P] >= 8) {
|
||||||
SweepCount[P] = ((PSG[(P << 2) + 0x1] >> 4) & 7) + 1; /* +1; */
|
int32 mod = (curfreq[P] >> sweepShift);
|
||||||
if (PSG[(P << 2) + 0x1] & 0x8) {
|
if (PSG[(P << 2) + 0x1] & 0x8) {
|
||||||
mod -= (P ^ 1) + ((curfreq[P]) >> (PSG[(P << 2) + 0x1] & 7));
|
curfreq[P] -= (mod + (P ^ 1));
|
||||||
if (curfreq[P] && (PSG[(P << 2) + 0x1] & 7) /* && sweepon[P]&0x80*/) {
|
} else if ((mod + curfreq[P]) < 0x800) {
|
||||||
curfreq[P] += mod;
|
curfreq[P] += mod;
|
||||||
}
|
|
||||||
} else {
|
|
||||||
mod = curfreq[P] >> (PSG[(P << 2) + 0x1] & 7);
|
|
||||||
if ((mod + curfreq[P]) & 0x800) {
|
|
||||||
sweepon[P] = 0;
|
|
||||||
curfreq[P] = 0;
|
|
||||||
} else {
|
|
||||||
if (curfreq[P] && (PSG[(P << 2) + 0x1] & 7) /* && sweepon[P]&0x80*/) {
|
|
||||||
curfreq[P] += mod;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {/* Sweeping is disabled: */
|
|
||||||
#if 0
|
SweepCount[P] = (((PSG[(P << 2) + 0x1] >> 4) & 7) + 1);
|
||||||
curfreq[P]&=0xFF00;
|
}
|
||||||
curfreq[P]|=PSG[(P<<2)|0x2]; /* |((PSG[(P<<2)|3]&7)<<8); */
|
|
||||||
#endif
|
if (sweepReload[P]) {
|
||||||
|
SweepCount[P] = (((PSG[(P << 2) + 0x1] >> 4) & 7) + 1);
|
||||||
|
sweepReload[P] = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -437,19 +430,21 @@ void FrameSoundUpdate(void) {
|
|||||||
* Length counter: Bit 4-7 of $4003, $4007, $400b, $400f
|
* Length counter: Bit 4-7 of $4003, $4007, $400b, $400f
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (!fcnt && !(IRQFrameMode & 0x3)) {
|
|
||||||
SIRQStat |= 0x40;
|
|
||||||
X6502_IRQBegin(FCEU_IQFCOUNT);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fcnt == 3) {
|
if (fcnt == 3) {
|
||||||
if (IRQFrameMode & 0x2)
|
if (IRQFrameMode & 0x2)
|
||||||
fhcnt += fhinc;
|
fhcnt += fhinc;
|
||||||
}
|
}
|
||||||
|
|
||||||
FrameSoundStuff(fcnt);
|
FrameSoundStuff(fcnt);
|
||||||
fcnt = (fcnt + 1) & 3;
|
fcnt = (fcnt + 1) & 3;
|
||||||
}
|
|
||||||
|
|
||||||
|
/* has to be moved here to fix Dragon Warrior 4
|
||||||
|
* after irq inhibit fix for $4017 */
|
||||||
|
if (!fcnt && !(IRQFrameMode & 0x3)) {
|
||||||
|
SIRQStat |= 0x40;
|
||||||
|
X6502_IRQBegin(FCEU_IQFCOUNT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static INLINE void tester(void) {
|
static INLINE void tester(void) {
|
||||||
if (DMCBitCount == 0) {
|
if (DMCBitCount == 0) {
|
||||||
@@ -476,9 +471,10 @@ static INLINE void DMCDMA(void) {
|
|||||||
if (DMCFormat & 0x40)
|
if (DMCFormat & 0x40)
|
||||||
PrepDPCM();
|
PrepDPCM();
|
||||||
else {
|
else {
|
||||||
SIRQStat |= 0x80;
|
if (DMCFormat & 0x80) {
|
||||||
if (DMCFormat & 0x80)
|
SIRQStat |= 0x80;
|
||||||
X6502_IRQBegin(FCEU_IQDPCM);
|
X6502_IRQBegin(FCEU_IQDPCM);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -536,54 +532,56 @@ static INLINE void RDoSQ(int x) {
|
|||||||
int32 cf;
|
int32 cf;
|
||||||
int32 rc;
|
int32 rc;
|
||||||
|
|
||||||
if (curfreq[x] < 8 || curfreq[x] > 0x7ff)
|
|
||||||
goto endit;
|
|
||||||
if (!CheckFreq(curfreq[x], PSG[(x << 2) | 0x1]))
|
|
||||||
goto endit;
|
|
||||||
if (!lengthcount[x])
|
|
||||||
goto endit;
|
|
||||||
|
|
||||||
if (EnvUnits[x].Mode & 0x1)
|
|
||||||
amp = EnvUnits[x].Speed;
|
|
||||||
else
|
|
||||||
amp = EnvUnits[x].decvolume;
|
|
||||||
/* printf("%d\n",amp); */
|
|
||||||
|
|
||||||
/* Modify Square wave volume based on channel volume modifiers
|
|
||||||
* Note: the formulat x = x * y /100 does not yield exact results,
|
|
||||||
* but is "close enough" and avoids the need for using double values
|
|
||||||
* or implicit cohersion which are slower (we need speed here) */
|
|
||||||
/* TODO: Optimize this. */
|
|
||||||
if (FSettings.SquareVolume[x] != 256)
|
|
||||||
amp = (amp * FSettings.SquareVolume[x]) / 256;
|
|
||||||
|
|
||||||
amp <<= 24;
|
|
||||||
|
|
||||||
rthresh = RectDuties[(PSG[(x << 2)] & 0xC0) >> 6];
|
|
||||||
|
|
||||||
D = &WaveHi[ChannelBC[x]];
|
|
||||||
V = SOUNDTS - ChannelBC[x];
|
V = SOUNDTS - ChannelBC[x];
|
||||||
|
|
||||||
currdc = RectDutyCount[x];
|
|
||||||
cf = (curfreq[x] + 1) * 2;
|
cf = (curfreq[x] + 1) * 2;
|
||||||
rc = wlcount[x];
|
rc = wlcount[x];
|
||||||
|
|
||||||
while (V > 0) {
|
/* added 2018/12/08 */
|
||||||
if (currdc < rthresh)
|
/* when pulse channel is silenced, resets length counters but not
|
||||||
*D += amp;
|
* duty cycle, instead of resetting both */
|
||||||
rc--;
|
if ((curfreq[x] < 8 || curfreq[x] > 0x7ff) ||
|
||||||
if (!rc) {
|
!CheckFreq(curfreq[x], PSG[(x << 2) | 0x1]) ||
|
||||||
rc = cf;
|
!lengthcount[x]) {
|
||||||
currdc = (currdc + 1) & 7;
|
rc -= V;
|
||||||
|
if (rc <= 0) {
|
||||||
|
rc = cf - (-rc % cf);
|
||||||
}
|
}
|
||||||
V--;
|
} else {
|
||||||
D++;
|
if (EnvUnits[x].Mode & 0x1)
|
||||||
|
amp = EnvUnits[x].Speed;
|
||||||
|
else
|
||||||
|
amp = EnvUnits[x].decvolume;
|
||||||
|
/* printf("%d\n",amp); */
|
||||||
|
|
||||||
|
/* Modify Square wave volume based on channel volume modifiers
|
||||||
|
* Note: the formulat x = x * y /100 does not yield exact results,
|
||||||
|
* but is "close enough" and avoids the need for using double values
|
||||||
|
* or implicit cohersion which are slower (we need speed here) */
|
||||||
|
/* TODO: Optimize this. */
|
||||||
|
if (FSettings.SquareVolume[x] != 256)
|
||||||
|
amp = (amp * FSettings.SquareVolume[x]) / 256;
|
||||||
|
|
||||||
|
amp <<= 24;
|
||||||
|
rthresh = RectDuties[(PSG[(x << 2)] & 0xC0) >> 6];
|
||||||
|
currdc = RectDutyCount[x];
|
||||||
|
D = &WaveHi[ChannelBC[x]];
|
||||||
|
|
||||||
|
while (V > 0) {
|
||||||
|
if (currdc < rthresh)
|
||||||
|
*D += amp;
|
||||||
|
rc--;
|
||||||
|
if (!rc) {
|
||||||
|
rc = cf;
|
||||||
|
currdc = (currdc + 1) & 7;
|
||||||
|
}
|
||||||
|
V--;
|
||||||
|
D++;
|
||||||
|
}
|
||||||
|
|
||||||
|
RectDutyCount[x] = currdc;
|
||||||
}
|
}
|
||||||
|
|
||||||
RectDutyCount[x] = currdc;
|
|
||||||
wlcount[x] = rc;
|
wlcount[x] = rc;
|
||||||
|
|
||||||
endit:
|
|
||||||
ChannelBC[x] = SOUNDTS;
|
ChannelBC[x] = SOUNDTS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -728,10 +726,6 @@ static void RDoTriangle(void) {
|
|||||||
ChannelBC[2] = SOUNDTS;
|
ChannelBC[2] = SOUNDTS;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32 lq_tcout = 0;
|
|
||||||
int32 lq_triacc = 0;
|
|
||||||
int32 lq_noiseacc = 0;
|
|
||||||
|
|
||||||
static void RDoTriangleNoisePCMLQ(void) {
|
static void RDoTriangleNoisePCMLQ(void) {
|
||||||
int32 V;
|
int32 V;
|
||||||
int32 start, end;
|
int32 start, end;
|
||||||
@@ -861,7 +855,6 @@ static void RDoTriangleNoisePCMLQ(void) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void RDoNoise(void) {
|
static void RDoNoise(void) {
|
||||||
uint32 V;
|
uint32 V;
|
||||||
int32 outo;
|
int32 outo;
|
||||||
@@ -932,10 +925,12 @@ DECLFW(Write_IRQFM) {
|
|||||||
fcnt = 0;
|
fcnt = 0;
|
||||||
if (V & 2)
|
if (V & 2)
|
||||||
FrameSoundUpdate();
|
FrameSoundUpdate();
|
||||||
fcnt = 1;
|
/* fcnt = 1; */
|
||||||
fhcnt = fhinc;
|
fhcnt = fhinc;
|
||||||
X6502_IRQEnd(FCEU_IQFCOUNT);
|
if (V & 1) {
|
||||||
SIRQStat &= ~0x40;
|
X6502_IRQEnd(FCEU_IQFCOUNT);
|
||||||
|
SIRQStat &= ~0x40;
|
||||||
|
}
|
||||||
IRQFrameMode = V;
|
IRQFrameMode = V;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user