NES: DPCM work!

This commit is contained in:
tildearrow 2022-05-02 03:42:40 -05:00
parent dbe9bf25e7
commit 5a724e4949
4 changed files with 101 additions and 8 deletions

View file

@ -18,4 +18,8 @@ also known as Famicom. It is a five-channel PSG: first two channels play pulse w
- `14xy`: setup sweep down. - `14xy`: setup sweep down.
- `x` is the time. - `x` is the time.
- `y` is the shift. - `y` is the shift.
- set to 0 to disable it. - set to 0 to disable it.
- `18xx`: set PCM channel mode.
- `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.

View file

@ -206,6 +206,25 @@ static unsigned char noiseTable[253]={
15 15
}; };
unsigned char DivPlatformNES::calcDPCMRate(int inRate) {
if (inRate<4450) return 0;
if (inRate<5000) return 1;
if (inRate<5400) return 2;
if (inRate<5900) return 3;
if (inRate<6650) return 4;
if (inRate<7450) return 5;
if (inRate<8100) return 6;
if (inRate<8800) return 7;
if (inRate<10200) return 8;
if (inRate<11700) return 9;
if (inRate<13300) return 10;
if (inRate<15900) return 11;
if (inRate<18900) return 12;
if (inRate<23500) return 13;
if (inRate<29000) return 14;
return 15;
}
void DivPlatformNES::tick(bool sysTick) { void DivPlatformNES::tick(bool sysTick) {
for (int i=0; i<4; i++) { for (int i=0; i<4; i++) {
chan[i].std.next(); chan[i].std.next();
@ -333,6 +352,9 @@ void DivPlatformNES::tick(bool sysTick) {
off=(double)s->centerRate/8363.0; off=(double)s->centerRate/8363.0;
} }
dacRate=MIN(chan[4].freq*off,32000); dacRate=MIN(chan[4].freq*off,32000);
if (dpcmMode && !skipRegisterWrites) {
rWrite(0x4010,calcDPCMRate(dacRate));
}
if (dumpWrites) addWrite(0xffff0001,dacRate); if (dumpWrites) addWrite(0xffff0001,dacRate);
} }
chan[4].freqChanged=false; chan[4].freqChanged=false;
@ -363,6 +385,18 @@ int DivPlatformNES::dispatch(DivCommand c) {
chan[c.chan].active=true; chan[c.chan].active=true;
chan[c.chan].keyOn=true; chan[c.chan].keyOn=true;
chan[c.chan].furnaceDac=true; chan[c.chan].furnaceDac=true;
if (dpcmMode && !skipRegisterWrites) {
unsigned int dpcmAddr=parent->getSample(dacSample)->offDPCM;
unsigned int dpcmLen=(parent->getSample(dacSample)->lengthDPCM+15)>>4;
if (dpcmLen>255) dpcmLen=255;
// write DPCM
rWrite(0x4015,15);
rWrite(0x4010,calcDPCMRate(chan[c.chan].baseFreq));
rWrite(0x4012,(dpcmAddr>>6)&0xff);
rWrite(0x4013,dpcmLen&0xff);
rWrite(0x4015,31);
dpcmBank=dpcmAddr>>14;
}
} else { } else {
if (c.value!=DIV_NOTE_NULL) { if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].note=c.value; chan[c.chan].note=c.value;
@ -386,12 +420,11 @@ int DivPlatformNES::dispatch(DivCommand c) {
if (dpcmLen>255) dpcmLen=255; if (dpcmLen>255) dpcmLen=255;
// write DPCM // write DPCM
rWrite(0x4015,15); rWrite(0x4015,15);
rWrite(0x4010,15); rWrite(0x4010,calcDPCMRate(dacRate));
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;
logV("writing DPCM: %x %x",dpcmAddr,dpcmLen);
} }
} }
break; break;
@ -421,6 +454,7 @@ int DivPlatformNES::dispatch(DivCommand c) {
if (c.chan==4) { if (c.chan==4) {
dacSample=-1; dacSample=-1;
if (dumpWrites) addWrite(0xffff0002,0); if (dumpWrites) addWrite(0xffff0002,0);
if (dpcmMode && !skipRegisterWrites) rWrite(0x4015,15);
} }
chan[c.chan].active=false; chan[c.chan].active=false;
chan[c.chan].keyOff=true; chan[c.chan].keyOff=true;
@ -501,10 +535,16 @@ int DivPlatformNES::dispatch(DivCommand c) {
break; break;
case DIV_CMD_NES_DMC: case DIV_CMD_NES_DMC:
rWrite(0x4011,c.value&0x7f); rWrite(0x4011,c.value&0x7f);
if (dumpWrites && dpcmMode) addWrite(0xffff0002,0);
break; break;
case DIV_CMD_SAMPLE_MODE: case DIV_CMD_SAMPLE_MODE:
dpcmMode=c.value; dpcmMode=c.value;
if (dumpWrites && dpcmMode) addWrite(0xffff0002,0);
dacSample=-1;
rWrite(0x4015,15);
rWrite(0x4010,0);
rWrite(0x4012,0);
rWrite(0x4013,0);
rWrite(0x4015,31);
break; break;
case DIV_CMD_SAMPLE_BANK: case DIV_CMD_SAMPLE_BANK:
sampleBank=c.value; sampleBank=c.value;
@ -682,10 +722,13 @@ void DivPlatformNES::renderSamples() {
size_t memPos=0; size_t memPos=0;
for (int i=0; i<parent->song.sampleLen; i++) { for (int i=0; i<parent->song.sampleLen; i++) {
DivSample* s=parent->song.sample[i]; DivSample* s=parent->song.sample[i];
int paddedLen=(s->lengthDPCM+63)&(~0xff); unsigned int paddedLen=(s->lengthDPCM+63)&(~0x3f);
logV("%d padded length: %d",i,paddedLen); logV("%d padded length: %d",i,paddedLen);
if ((memPos&0x4000)!=((memPos+paddedLen)&0x4000)) { if ((memPos&(~0x3fff))!=((memPos+paddedLen)&(~0x3fff))) {
memPos=(memPos+0x3fff)&0x4000; memPos=(memPos+0x3fff)&(~0x3fff);
}
if (paddedLen>4081) {
paddedLen=4096;
} }
if (memPos>=getSampleMemCapacity(0)) { if (memPos>=getSampleMemCapacity(0)) {
logW("out of DPCM memory for sample %d!",i); logW("out of DPCM memory for sample %d!",i);
@ -695,7 +738,7 @@ void DivPlatformNES::renderSamples() {
memcpy(dpcmMem+memPos,s->dataDPCM,getSampleMemCapacity(0)-memPos); memcpy(dpcmMem+memPos,s->dataDPCM,getSampleMemCapacity(0)-memPos);
logW("out of DPCM memory for sample %d!",i); logW("out of DPCM memory for sample %d!",i);
} else { } else {
memcpy(dpcmMem+memPos,s->dataDPCM,paddedLen); memcpy(dpcmMem+memPos,s->dataDPCM,MIN(s->lengthDPCM,paddedLen));
} }
s->offDPCM=memPos; s->offDPCM=memPos;
memPos+=paddedLen; memPos+=paddedLen;

View file

@ -81,6 +81,7 @@ class DivPlatformNES: public DivDispatch {
friend void putDispatchChan(void*,int,int); friend void putDispatchChan(void*,int,int);
void doWrite(unsigned short addr, unsigned char data); void doWrite(unsigned short addr, unsigned char data);
unsigned char calcDPCMRate(int inRate);
void acquire_puNES(short* bufL, short* bufR, size_t start, size_t len); void acquire_puNES(short* bufL, short* bufR, size_t start, size_t len);
void acquire_NSFPlay(short* bufL, short* bufR, size_t start, size_t len); void acquire_NSFPlay(short* bufL, short* bufR, size_t start, size_t len);

View file

@ -21,6 +21,7 @@
#include "debug.h" #include "debug.h"
#include "IconsFontAwesome4.h" #include "IconsFontAwesome4.h"
#include <fmt/printf.h> #include <fmt/printf.h>
#include <imgui.h>
void FurnaceGUI::drawDebug() { void FurnaceGUI::drawDebug() {
static int bpOrder; static int bpOrder;
@ -141,6 +142,50 @@ void FurnaceGUI::drawDebug() {
ImGui::Columns(); ImGui::Columns();
ImGui::TreePop(); ImGui::TreePop();
} }
if (ImGui::TreeNode("Sample Debug")) {
for (int i=0; i<e->song.sampleLen; i++) {
DivSample* sample=e->getSample(i);
if (sample==NULL) {
ImGui::Text("%d: <NULL!>",i);
continue;
}
ImGui::Text("%d: %s",i,sample->name.c_str());
ImGui::Indent();
ImGui::Text("rate: %d",sample->rate);
ImGui::Text("centerRate: %d",sample->centerRate);
ImGui::Text("loopStart: %d",sample->loopStart);
ImGui::Text("loopOffP: %d",sample->loopOffP);
ImGui::Text("depth: %d",sample->depth);
ImGui::Text("length8: %d",sample->length8);
ImGui::Text("length16: %d",sample->length16);
ImGui::Text("length1: %d",sample->length1);
ImGui::Text("lengthDPCM: %d",sample->lengthDPCM);
ImGui::Text("lengthQSoundA: %d",sample->lengthQSoundA);
ImGui::Text("lengthA: %d",sample->lengthA);
ImGui::Text("lengthB: %d",sample->lengthB);
ImGui::Text("lengthX68: %d",sample->lengthX68);
ImGui::Text("lengthBRR: %d",sample->lengthBRR);
ImGui::Text("lengthVOX: %d",sample->lengthVOX);
ImGui::Text("off8: %x",sample->off8);
ImGui::Text("off16: %x",sample->off16);
ImGui::Text("off1: %x",sample->off1);
ImGui::Text("offDPCM: %x",sample->offDPCM);
ImGui::Text("offQSoundA: %x",sample->offQSoundA);
ImGui::Text("offA: %x",sample->offA);
ImGui::Text("offB: %x",sample->offB);
ImGui::Text("offX68: %x",sample->offX68);
ImGui::Text("offBRR: %x",sample->offBRR);
ImGui::Text("offVOX: %x",sample->offVOX);
ImGui::Text("offSegaPCM: %x",sample->offSegaPCM);
ImGui::Text("offQSound: %x",sample->offQSound);
ImGui::Text("offX1_010: %x",sample->offX1_010);
ImGui::Text("samples: %d",sample->samples);
ImGui::Unindent();
}
ImGui::TreePop();
}
if (ImGui::TreeNode("Playground")) { if (ImGui::TreeNode("Playground")) {
if (pgSys<0 || pgSys>=e->song.systemLen) pgSys=0; if (pgSys<0 || pgSys>=e->song.systemLen) pgSys=0;
if (ImGui::BeginCombo("System",fmt::sprintf("%d. %s",pgSys+1,e->getSystemName(e->song.system[pgSys])).c_str())) { if (ImGui::BeginCombo("System",fmt::sprintf("%d. %s",pgSys+1,e->getSystemName(e->song.system[pgSys])).c_str())) {