NES: add 20xx effect for changing DPCM freq

This commit is contained in:
tildearrow 2023-05-05 00:59:55 -05:00
parent bd53c57658
commit 0c0472ce76
4 changed files with 54 additions and 4 deletions

View file

@ -42,6 +42,30 @@ also known as Famicom. it is a five-channel sound generator: first two channels
- `00`: PCM (software). - `00`: PCM (software).
- `01`: DPCM (hardware). - `01`: DPCM (hardware).
- when in DPCM mode, samples will sound muffled (due to its nature), availables pitches are limited and loop point is ignored. - when in DPCM mode, samples will sound muffled (due to its nature), availables pitches are limited and loop point is ignored.
- `20xx`: set DPCM frequency.
- only works in DPCM mode.
- see table below for possible values.
# DPCM frequency table
val | NTSC | PAL
----|-----------|-----------
00 | 4181.7Hz | 4177.4Hz
01 | 4709.9Hz | 4696.6Hz
02 | 5264.0Hz | 5261.4Hz
03 | 5593.0Hz | 5579.2Hz
04 | 6257.9Hz | 6023.9Hz
05 | 7046.3Hz | 7044.9Hz
06 | 7919.3Hz | 7917.2Hz
07 | 8363.4Hz | 8397.0Hz
08 | 9419.9Hz | 9446.6Hz
09 | 11186.1Hz | 11233.8Hz
0A | 12604.0Hz | 12595.5Hz
0B | 13982.6Hz | 14089.9Hz
0C | 16884.6Hz | 16965.4Hz
0D | 21306.8Hz | 21315.5Hz
0E | 24858.0Hz | 25191.0Hz
0F | 33143.9Hz | 33252.1Hz
# length counter table # length counter table

View file

@ -337,14 +337,22 @@ void DivPlatformNES::tick(bool sysTick) {
goingToLoop=parent->getSample(dacSample)->isLoopable(); goingToLoop=parent->getSample(dacSample)->isLoopable();
// write DPCM // write DPCM
rWrite(0x4015,15); rWrite(0x4015,15);
if (nextDPCMFreq>=0) {
rWrite(0x4010,nextDPCMFreq|(goingToLoop?0x40:0));
nextDPCMFreq=-1;
} else {
rWrite(0x4010,calcDPCMRate(dacRate)|(goingToLoop?0x40:0)); rWrite(0x4010,calcDPCMRate(dacRate)|(goingToLoop?0x40:0));
}
rWrite(0x4012,(dpcmAddr>>6)&0xff); rWrite(0x4012,(dpcmAddr>>6)&0xff);
rWrite(0x4013,dpcmLen&0xff); rWrite(0x4013,dpcmLen&0xff);
rWrite(0x4015,31); rWrite(0x4015,31);
dpcmBank=dpcmAddr>>14; dpcmBank=dpcmAddr>>14;
} }
} else { } else {
if (dpcmMode) { if (nextDPCMFreq>=0) {
rWrite(0x4010,nextDPCMFreq|(goingToLoop?0x40:0));
nextDPCMFreq=-1;
} else {
rWrite(0x4010,calcDPCMRate(dacRate)|(goingToLoop?0x40:0)); rWrite(0x4010,calcDPCMRate(dacRate)|(goingToLoop?0x40:0));
} }
} }
@ -353,6 +361,8 @@ void DivPlatformNES::tick(bool sysTick) {
if (chan[4].keyOn) chan[4].keyOn=false; if (chan[4].keyOn) chan[4].keyOn=false;
chan[4].freqChanged=false; chan[4].freqChanged=false;
} }
nextDPCMFreq=-1;
} }
int DivPlatformNES::dispatch(DivCommand c) { int DivPlatformNES::dispatch(DivCommand c) {
@ -406,7 +416,12 @@ int DivPlatformNES::dispatch(DivCommand c) {
goingToLoop=parent->getSample(dacSample)->isLoopable(); goingToLoop=parent->getSample(dacSample)->isLoopable();
// write DPCM // write DPCM
rWrite(0x4015,15); rWrite(0x4015,15);
if (nextDPCMFreq>=0) {
rWrite(0x4010,nextDPCMFreq|(goingToLoop?0x40:0));
nextDPCMFreq=-1;
} else {
rWrite(0x4010,calcDPCMRate(dacRate)|(goingToLoop?0x40:0)); rWrite(0x4010,calcDPCMRate(dacRate)|(goingToLoop?0x40:0));
}
rWrite(0x4012,(dpcmAddr>>6)&0xff); rWrite(0x4012,(dpcmAddr>>6)&0xff);
rWrite(0x4013,dpcmLen&0xff); rWrite(0x4013,dpcmLen&0xff);
rWrite(0x4015,31); rWrite(0x4015,31);
@ -555,6 +570,14 @@ int DivPlatformNES::dispatch(DivCommand c) {
rWrite(0x4013,0); rWrite(0x4013,0);
rWrite(0x4015,31); rWrite(0x4015,31);
break; break;
case DIV_CMD_SAMPLE_FREQ: {
bool goingToLoop=parent->getSample(dacSample)->isLoopable();
if (dpcmMode) {
nextDPCMFreq=c.value&15;
rWrite(0x4010,(c.value&15)|(goingToLoop?0x40:0));
}
break;
}
case DIV_CMD_SAMPLE_BANK: case DIV_CMD_SAMPLE_BANK:
sampleBank=c.value; sampleBank=c.value;
if (sampleBank>(parent->song.sample.size()/12)) { if (sampleBank>(parent->song.sample.size()/12)) {
@ -658,6 +681,7 @@ void DivPlatformNES::reset() {
dpcmMode=dpcmModeDefault; dpcmMode=dpcmModeDefault;
goingToLoop=false; goingToLoop=false;
countMode=false; countMode=false;
nextDPCMFreq=-1;
if (useNP) { if (useNP) {
nes1_NP->Reset(); nes1_NP->Reset();

View file

@ -52,6 +52,7 @@ class DivPlatformNES: public DivDispatch {
unsigned char sampleBank; unsigned char sampleBank;
unsigned char writeOscBuf; unsigned char writeOscBuf;
unsigned char apuType; unsigned char apuType;
signed char nextDPCMFreq;
bool dpcmMode; bool dpcmMode;
bool dpcmModeDefault; bool dpcmModeDefault;
bool dacAntiClickOn; bool dacAntiClickOn;

View file

@ -712,7 +712,8 @@ void DivEngine::registerSystems() {
{0x15, {DIV_CMD_NES_ENV_MODE, "15xx: Set envelope mode (0: envelope, 1: length, 2: looping, 3: constant)"}}, {0x15, {DIV_CMD_NES_ENV_MODE, "15xx: Set envelope mode (0: envelope, 1: length, 2: looping, 3: constant)"}},
{0x16, {DIV_CMD_NES_LENGTH, "16xx: Set length counter (refer to manual for a list of values)"}}, {0x16, {DIV_CMD_NES_LENGTH, "16xx: Set length counter (refer to manual for a list of values)"}},
{0x17, {DIV_CMD_NES_COUNT_MODE, "17xx: Set frame counter mode (0: 4-step, 1: 5-step)"}}, {0x17, {DIV_CMD_NES_COUNT_MODE, "17xx: Set frame counter mode (0: 4-step, 1: 5-step)"}},
{0x18, {DIV_CMD_SAMPLE_MODE, "18xx: Select PCM/DPCM mode (0: PCM; 1: DPCM)"}} {0x18, {DIV_CMD_SAMPLE_MODE, "18xx: Select PCM/DPCM mode (0: PCM; 1: DPCM)"}},
{0x20, {DIV_CMD_SAMPLE_FREQ, "20xx: Set DPCM frequency (0 to F)"}}
} }
); );