GBA: implement memory composition and status

This commit is contained in:
Natt Akuma 2024-03-16 23:16:40 +07:00
parent 67253245ab
commit 48b51f5108
6 changed files with 106 additions and 6 deletions

View file

@ -480,6 +480,7 @@ enum DivMemoryWaveView: unsigned char {
DIV_MEMORY_WAVE_NONE=0,
DIV_MEMORY_WAVE_4BIT, // Namco 163
DIV_MEMORY_WAVE_6BIT, // Virtual Boy
DIV_MEMORY_WAVE_8BIT_SIGNED, // SCC
};
struct DivMemoryComposition {

View file

@ -195,6 +195,7 @@ int DivPlatformGBADMA::dispatch(DivCommand c) {
if (ins->amiga.useWave) {
chan[c.chan].useWave=true;
chan[c.chan].audLen=ins->amiga.waveLen+1;
wtMemCompo.entries[c.chan].end=wtMemCompo.entries[c.chan].begin+chan[c.chan].audLen;
if (chan[c.chan].insChanged) {
if (chan[c.chan].wave<0) {
chan[c.chan].wave=0;
@ -392,11 +393,14 @@ unsigned short DivPlatformGBADMA::getPan(int ch) {
}
DivSamplePos DivPlatformGBADMA::getSamplePos(int ch) {
if (ch>=2) return DivSamplePos();
if (ch>=2 || !chan[ch].active ||
chan[ch].sample<0 || chan[ch].sample>=parent->song.sampleLen) {
return DivSamplePos();
}
return DivSamplePos(
chan[ch].sample,
chan[ch].audPos,
chan[ch].freq
chipClock/chan[ch].freq
);
}
@ -441,9 +445,19 @@ bool DivPlatformGBADMA::isSampleLoaded(int index, int sample) {
return sampleLoaded[sample];
}
const DivMemoryComposition* DivPlatformGBADMA::getMemCompo(int index) {
switch (index) {
case 0: return &romMemCompo;
case 1: return &wtMemCompo;
}
return NULL;
}
void DivPlatformGBADMA::renderSamples(int sysID) {
size_t maxPos=getSampleMemCapacity();
memset(sampleMem,0,maxPos);
romMemCompo.entries.clear();
romMemCompo.capacity=maxPos;
size_t memPos=0;
for (int i=0; i<parent->song.sampleLen; i++) {
@ -466,8 +480,10 @@ void DivPlatformGBADMA::renderSamples(int sysID) {
sampleLoaded[i]=true;
// pad to multiple of 16 bytes
memPos=(memPos+15)&~15;
romMemCompo.entries.push_back(DivMemoryEntry(DIV_MEMORY_SAMPLE,"PCM",i,sampleOff[i],memPos));
}
sampleMemLen=memPos;
romMemCompo.used=sampleMemLen;
}
void DivPlatformGBADMA::setFlags(const DivConfig& flags) {
@ -484,9 +500,18 @@ int DivPlatformGBADMA::init(DivEngine* p, int channels, int sugRate, const DivCo
parent=p;
dumpWrites=false;
skipRegisterWrites=false;
romMemCompo=DivMemoryComposition();
romMemCompo.name="Sample ROM";
wtMemCompo=DivMemoryComposition();
wtMemCompo.name="Wavetable RAM";
wtMemCompo.used=256*2;
wtMemCompo.capacity=256*2;
wtMemCompo.memory=(unsigned char*)wtMem;
wtMemCompo.waveformView=DIV_MEMORY_WAVE_8BIT_SIGNED;
for (int i=0; i<2; i++) {
isMuted[i]=false;
oscBuf[i]=new DivDispatchOscBuffer;
wtMemCompo.entries.push_back(DivMemoryEntry(DIV_MEMORY_WAVE_RAM, fmt::sprintf("Channel %d",i),-1,i*256,i*256));
}
sampleMem=new signed char[getSampleMemCapacity()];
sampleMemLen=0;

View file

@ -62,6 +62,9 @@ class DivPlatformGBADMA: public DivDispatch {
size_t sampleMemLen;
// maximum wavetable length is currently hardcoded to 256
signed char wtMem[256*2];
DivMemoryComposition romMemCompo;
DivMemoryComposition wtMemCompo;
friend void putDispatchChip(void*,int);
friend void putDispatchChan(void*,int,int);
@ -86,10 +89,11 @@ class DivPlatformGBADMA: public DivDispatch {
size_t getSampleMemCapacity(int index = 0);
size_t getSampleMemUsage(int index = 0);
bool isSampleLoaded(int index, int sample);
const DivMemoryComposition* getMemCompo(int index);
void renderSamples(int chipID);
int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags);
void quit();
private:
void updateWave(int ch);
};

View file

@ -142,6 +142,8 @@ void DivPlatformGBAMinMod::acquire(short** buf, size_t len) {
while (updTimer>=updCycles) {
// flip buffer
// logV("ut=%d,pg=%d,w=%d,r=%d,sc=%d,st=%d",updTimer,mixBufPage,mixBufWritePos,mixBufReadPos,sampCycles,sampTimer);
mixMemCompo.entries[mixBufPage].end=mixMemCompo.entries[mixBufPage].begin+mixBufWritePos;
mixMemCompo.entries[mixBufPage+1].end=mixMemCompo.entries[mixBufPage+1].begin+mixBufWritePos;
mixBufPage=(mixBufPage+2)%(mixBufs*2);
memset(mixBuf[mixBufPage],0,sizeof(mixBuf[mixBufPage]));
memset(mixBuf[mixBufPage+1],0,sizeof(mixBuf[mixBufPage+1]));
@ -298,9 +300,9 @@ void DivPlatformGBAMinMod::tick(bool sysTick) {
chan[i].freq=(int)(off*parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,false,2,chan[i].pitch2,chipClock,CHIP_FREQBASE));
if (chan[i].keyOn) {
unsigned int start, end, loop;
if (chan[i].echo!=0) {
if ((chan[i].echo&0xf)!=0) {
// make sure echo channels' frequency can't be faster than the sample rate
if (chan[i].echo!=0 && chan[i].freq>CHIP_FREQBASE) {
if (chan[i].freq>CHIP_FREQBASE) {
chan[i].freq=CHIP_FREQBASE;
}
// this is only to match the HLE implementation
@ -381,6 +383,9 @@ int DivPlatformGBAMinMod::dispatch(DivCommand c) {
if (ins->amiga.useWave) {
chan[c.chan].useWave=true;
chan[c.chan].wtLen=ins->amiga.waveLen+1;
if (c.chan<chanMax) {
wtMemCompo.entries[c.chan].end=wtMemCompo.entries[c.chan].begin+chan[c.chan].wtLen;
}
if (chan[c.chan].insChanged) {
if (chan[c.chan].wave<0) {
chan[c.chan].wave=0;
@ -558,6 +563,20 @@ unsigned short DivPlatformGBAMinMod::getPan(int ch) {
return (chan[ch].chPanL<<8)|(chan[ch].chPanR);
}
DivSamplePos DivPlatformGBAMinMod::getSamplePos(int ch) {
if (ch>=chanMax ||
chan[ch].sample<0 || chan[ch].sample>=parent->song.sampleLen ||
!chan[ch].active || (chan[ch].echo&0xf)!=0
) {
return DivSamplePos();
}
return DivSamplePos(
chan[ch].sample,
(((int)regPool[ch*16+2]|((int)regPool[ch*16+3]<<16))&0x01ffffff)-sampleOff[chan[ch].sample],
(long long)chan[ch].freq*chipClock/CHIP_FREQBASE
);
}
DivDispatchOscBuffer* DivPlatformGBAMinMod::getOscBuffer(int ch) {
return oscBuf[ch];
}
@ -577,6 +596,7 @@ void DivPlatformGBAMinMod::reset() {
void DivPlatformGBAMinMod::resetMixer() {
sampTimer=sampCycles;
updTimer=0;
updCycles=0;
mixBufReadPos=0;
mixBufWritePos=0;
mixBufPage=0;
@ -651,9 +671,20 @@ bool DivPlatformGBAMinMod::isSampleLoaded(int index, int sample) {
return sampleLoaded[sample];
}
const DivMemoryComposition* DivPlatformGBAMinMod::getMemCompo(int index) {
switch (index) {
case 0: return &romMemCompo;
case 1: return &wtMemCompo;
case 2: return &mixMemCompo;
}
return NULL;
}
void DivPlatformGBAMinMod::renderSamples(int sysID) {
size_t maxPos=getSampleMemCapacity();
memset(sampleMem,0,maxPos);
romMemCompo.entries.clear();
romMemCompo.capacity=maxPos;
// dummy zero-length samples are at pos 0 as the engine still outputs them
size_t memPos=1;
@ -677,6 +708,7 @@ void DivPlatformGBAMinMod::renderSamples(int sysID) {
memset(&sampleMem[memPos],0,oneShotLen);
memPos+=oneShotLen;
}
romMemCompo.entries.push_back(DivMemoryEntry(DIV_MEMORY_SAMPLE,"PCM",i,sampleOff[i],memPos));
}
if (actualLength<length) {
logW("out of GBA MinMod PCM memory for sample %d!",i);
@ -685,6 +717,7 @@ void DivPlatformGBAMinMod::renderSamples(int sysID) {
sampleLoaded[i]=true;
}
sampleMemLen=memPos;
romMemCompo.used=sampleMemLen;
}
void DivPlatformGBAMinMod::setFlags(const DivConfig& flags) {
@ -699,12 +732,35 @@ void DivPlatformGBAMinMod::setFlags(const DivConfig& flags) {
sampCycles=16777216/flags.getInt("sampRate",21845);
chipClock=16777216/sampCycles;
resetMixer();
wtMemCompo.used=256*chanMax;
mixMemCompo.used=2048*mixBufs;
wtMemCompo.entries.clear();
mixMemCompo.entries.clear();
for (int i=0; i<chanMax; i++) {
wtMemCompo.entries.push_back(DivMemoryEntry(DIV_MEMORY_WAVE_RAM, fmt::sprintf("Channel %d",i),-1,i*256,i*256));
}
for (int i=0; i<mixBufs; i++) {
mixMemCompo.entries.push_back(DivMemoryEntry(DIV_MEMORY_WAVE_RAM, fmt::sprintf("Buffer %d Left",i),-1,i*2048,i*2048));
mixMemCompo.entries.push_back(DivMemoryEntry(DIV_MEMORY_WAVE_RAM, fmt::sprintf("Buffer %d Right",i),-1,i*2048+1024,i*2048+1024));
}
}
int DivPlatformGBAMinMod::init(DivEngine* p, int channels, int sugRate, const DivConfig& flags) {
parent=p;
dumpWrites=false;
skipRegisterWrites=false;
romMemCompo=DivMemoryComposition();
romMemCompo.name="Sample ROM";
wtMemCompo=DivMemoryComposition();
wtMemCompo.name="Wavetable RAM";
wtMemCompo.capacity=256*16;
wtMemCompo.memory=(unsigned char*)wtMem;
wtMemCompo.waveformView=DIV_MEMORY_WAVE_8BIT_SIGNED;
mixMemCompo=DivMemoryComposition();
mixMemCompo.name="Mix/Echo Buffer";
mixMemCompo.capacity=2048*15;
mixMemCompo.memory=(unsigned char*)mixBuf;
mixMemCompo.waveformView=DIV_MEMORY_WAVE_8BIT_SIGNED;
for (int i=0; i<16; i++) {
isMuted[i]=false;
oscBuf[i]=new DivDispatchOscBuffer;

View file

@ -81,8 +81,12 @@ class DivPlatformGBAMinMod: public DivDispatch {
signed char* sampleMem;
size_t sampleMemLen;
// maximum wavetable length is currently hardcoded to 256
signed char wtMem[256*16];
unsigned short regPool[16*16];
signed char wtMem[256*16];
DivMemoryComposition romMemCompo;
DivMemoryComposition mixMemCompo;
DivMemoryComposition wtMemCompo;
friend void putDispatchChip(void*,int);
friend void putDispatchChan(void*,int,int);
@ -92,6 +96,7 @@ class DivPlatformGBAMinMod: public DivDispatch {
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
unsigned short getPan(int chan);
DivSamplePos getSamplePos(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();
@ -111,6 +116,7 @@ class DivPlatformGBAMinMod: public DivDispatch {
size_t getSampleMemCapacity(int index = 0);
size_t getSampleMemUsage(int index = 0);
bool isSampleLoaded(int index, int sample);
const DivMemoryComposition* getMemCompo(int index);
void renderSamples(int chipID);
void setFlags(const DivConfig& flags);
int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags);

View file

@ -109,6 +109,14 @@ void FurnaceGUI::drawMemory() {
dl->AddRectFilled(pos1,pos2,ImGui::GetColorU32(uiColors[GUI_COLOR_MEMORY_DATA]));
}
break;
case DIV_MEMORY_WAVE_8BIT_SIGNED:
for (int k=0; k<mc->capacity; k++) {
signed char val=(signed char)mc->memory[k];
ImVec2 pos1=ImLerp(dataRect.Min,dataRect.Max,ImVec2((double)k/(double)(mc->capacity),1.0f-((float)(val+129)/256.0f)));
ImVec2 pos2=ImLerp(dataRect.Min,dataRect.Max,ImVec2((double)(k+1)/(double)(mc->capacity),1.0f));
dl->AddRectFilled(pos1,pos2,ImGui::GetColorU32(uiColors[GUI_COLOR_MEMORY_DATA]));
}
break;
default:
break;
}