From 0c0472ce7613bc394acefd79209f021b656efade Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 5 May 2023 00:59:55 -0500 Subject: [PATCH] NES: add 20xx effect for changing DPCM freq --- papers/doc/7-systems/nes.md | 24 ++++++++++++++++++++++++ src/engine/platform/nes.cpp | 30 +++++++++++++++++++++++++++--- src/engine/platform/nes.h | 1 + src/engine/sysDef.cpp | 3 ++- 4 files changed, 54 insertions(+), 4 deletions(-) diff --git a/papers/doc/7-systems/nes.md b/papers/doc/7-systems/nes.md index 37d4a7037..dc8b635be 100644 --- a/papers/doc/7-systems/nes.md +++ b/papers/doc/7-systems/nes.md @@ -42,6 +42,30 @@ also known as Famicom. it is a five-channel sound generator: first two channels - `00`: PCM (software). - `01`: DPCM (hardware). - 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 diff --git a/src/engine/platform/nes.cpp b/src/engine/platform/nes.cpp index d6b64ecff..974e71d51 100644 --- a/src/engine/platform/nes.cpp +++ b/src/engine/platform/nes.cpp @@ -337,14 +337,22 @@ void DivPlatformNES::tick(bool sysTick) { goingToLoop=parent->getSample(dacSample)->isLoopable(); // write DPCM rWrite(0x4015,15); - rWrite(0x4010,calcDPCMRate(dacRate)|(goingToLoop?0x40:0)); + if (nextDPCMFreq>=0) { + rWrite(0x4010,nextDPCMFreq|(goingToLoop?0x40:0)); + nextDPCMFreq=-1; + } else { + rWrite(0x4010,calcDPCMRate(dacRate)|(goingToLoop?0x40:0)); + } rWrite(0x4012,(dpcmAddr>>6)&0xff); rWrite(0x4013,dpcmLen&0xff); rWrite(0x4015,31); dpcmBank=dpcmAddr>>14; } } else { - if (dpcmMode) { + if (nextDPCMFreq>=0) { + rWrite(0x4010,nextDPCMFreq|(goingToLoop?0x40:0)); + nextDPCMFreq=-1; + } else { rWrite(0x4010,calcDPCMRate(dacRate)|(goingToLoop?0x40:0)); } } @@ -353,6 +361,8 @@ void DivPlatformNES::tick(bool sysTick) { if (chan[4].keyOn) chan[4].keyOn=false; chan[4].freqChanged=false; } + + nextDPCMFreq=-1; } int DivPlatformNES::dispatch(DivCommand c) { @@ -406,7 +416,12 @@ int DivPlatformNES::dispatch(DivCommand c) { goingToLoop=parent->getSample(dacSample)->isLoopable(); // write DPCM rWrite(0x4015,15); - rWrite(0x4010,calcDPCMRate(dacRate)|(goingToLoop?0x40:0)); + if (nextDPCMFreq>=0) { + rWrite(0x4010,nextDPCMFreq|(goingToLoop?0x40:0)); + nextDPCMFreq=-1; + } else { + rWrite(0x4010,calcDPCMRate(dacRate)|(goingToLoop?0x40:0)); + } rWrite(0x4012,(dpcmAddr>>6)&0xff); rWrite(0x4013,dpcmLen&0xff); rWrite(0x4015,31); @@ -555,6 +570,14 @@ int DivPlatformNES::dispatch(DivCommand c) { rWrite(0x4013,0); rWrite(0x4015,31); 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: sampleBank=c.value; if (sampleBank>(parent->song.sample.size()/12)) { @@ -658,6 +681,7 @@ void DivPlatformNES::reset() { dpcmMode=dpcmModeDefault; goingToLoop=false; countMode=false; + nextDPCMFreq=-1; if (useNP) { nes1_NP->Reset(); diff --git a/src/engine/platform/nes.h b/src/engine/platform/nes.h index cbc08e2a1..2ab7ede20 100644 --- a/src/engine/platform/nes.h +++ b/src/engine/platform/nes.h @@ -52,6 +52,7 @@ class DivPlatformNES: public DivDispatch { unsigned char sampleBank; unsigned char writeOscBuf; unsigned char apuType; + signed char nextDPCMFreq; bool dpcmMode; bool dpcmModeDefault; bool dacAntiClickOn; diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index 6afbf4188..4e45df018 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -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)"}}, {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)"}}, - {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)"}} } );