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.
|
- `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.
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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())) {
|
||||||
|
|
Loading…
Reference in a new issue