NES: DPCM work!
This commit is contained in:
parent
dbe9bf25e7
commit
5a724e4949
|
@ -18,4 +18,8 @@ also known as Famicom. It is a five-channel PSG: first two channels play pulse w
|
|||
- `14xy`: setup sweep down.
|
||||
- `x` is the time.
|
||||
- `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.
|
|
@ -206,6 +206,25 @@ static unsigned char noiseTable[253]={
|
|||
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) {
|
||||
for (int i=0; i<4; i++) {
|
||||
chan[i].std.next();
|
||||
|
@ -333,6 +352,9 @@ void DivPlatformNES::tick(bool sysTick) {
|
|||
off=(double)s->centerRate/8363.0;
|
||||
}
|
||||
dacRate=MIN(chan[4].freq*off,32000);
|
||||
if (dpcmMode && !skipRegisterWrites) {
|
||||
rWrite(0x4010,calcDPCMRate(dacRate));
|
||||
}
|
||||
if (dumpWrites) addWrite(0xffff0001,dacRate);
|
||||
}
|
||||
chan[4].freqChanged=false;
|
||||
|
@ -363,6 +385,18 @@ int DivPlatformNES::dispatch(DivCommand c) {
|
|||
chan[c.chan].active=true;
|
||||
chan[c.chan].keyOn=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 {
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].note=c.value;
|
||||
|
@ -386,12 +420,11 @@ int DivPlatformNES::dispatch(DivCommand c) {
|
|||
if (dpcmLen>255) dpcmLen=255;
|
||||
// write DPCM
|
||||
rWrite(0x4015,15);
|
||||
rWrite(0x4010,15);
|
||||
rWrite(0x4010,calcDPCMRate(dacRate));
|
||||
rWrite(0x4012,(dpcmAddr>>6)&0xff);
|
||||
rWrite(0x4013,dpcmLen&0xff);
|
||||
rWrite(0x4015,31);
|
||||
dpcmBank=dpcmAddr>>14;
|
||||
logV("writing DPCM: %x %x",dpcmAddr,dpcmLen);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -421,6 +454,7 @@ int DivPlatformNES::dispatch(DivCommand c) {
|
|||
if (c.chan==4) {
|
||||
dacSample=-1;
|
||||
if (dumpWrites) addWrite(0xffff0002,0);
|
||||
if (dpcmMode && !skipRegisterWrites) rWrite(0x4015,15);
|
||||
}
|
||||
chan[c.chan].active=false;
|
||||
chan[c.chan].keyOff=true;
|
||||
|
@ -501,10 +535,16 @@ int DivPlatformNES::dispatch(DivCommand c) {
|
|||
break;
|
||||
case DIV_CMD_NES_DMC:
|
||||
rWrite(0x4011,c.value&0x7f);
|
||||
if (dumpWrites && dpcmMode) addWrite(0xffff0002,0);
|
||||
break;
|
||||
case DIV_CMD_SAMPLE_MODE:
|
||||
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;
|
||||
case DIV_CMD_SAMPLE_BANK:
|
||||
sampleBank=c.value;
|
||||
|
@ -682,10 +722,13 @@ void DivPlatformNES::renderSamples() {
|
|||
size_t memPos=0;
|
||||
for (int i=0; i<parent->song.sampleLen; 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);
|
||||
if ((memPos&0x4000)!=((memPos+paddedLen)&0x4000)) {
|
||||
memPos=(memPos+0x3fff)&0x4000;
|
||||
if ((memPos&(~0x3fff))!=((memPos+paddedLen)&(~0x3fff))) {
|
||||
memPos=(memPos+0x3fff)&(~0x3fff);
|
||||
}
|
||||
if (paddedLen>4081) {
|
||||
paddedLen=4096;
|
||||
}
|
||||
if (memPos>=getSampleMemCapacity(0)) {
|
||||
logW("out of DPCM memory for sample %d!",i);
|
||||
|
@ -695,7 +738,7 @@ void DivPlatformNES::renderSamples() {
|
|||
memcpy(dpcmMem+memPos,s->dataDPCM,getSampleMemCapacity(0)-memPos);
|
||||
logW("out of DPCM memory for sample %d!",i);
|
||||
} else {
|
||||
memcpy(dpcmMem+memPos,s->dataDPCM,paddedLen);
|
||||
memcpy(dpcmMem+memPos,s->dataDPCM,MIN(s->lengthDPCM,paddedLen));
|
||||
}
|
||||
s->offDPCM=memPos;
|
||||
memPos+=paddedLen;
|
||||
|
|
|
@ -81,6 +81,7 @@ class DivPlatformNES: public DivDispatch {
|
|||
friend void putDispatchChan(void*,int,int);
|
||||
|
||||
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_NSFPlay(short* bufL, short* bufR, size_t start, size_t len);
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "debug.h"
|
||||
#include "IconsFontAwesome4.h"
|
||||
#include <fmt/printf.h>
|
||||
#include <imgui.h>
|
||||
|
||||
void FurnaceGUI::drawDebug() {
|
||||
static int bpOrder;
|
||||
|
@ -141,6 +142,50 @@ void FurnaceGUI::drawDebug() {
|
|||
ImGui::Columns();
|
||||
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 (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())) {
|
||||
|
|
Loading…
Reference in New Issue