From dca18dc726319fc1a365e4e4f85088284b2a57ba Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 10 Mar 2023 04:22:21 -0500 Subject: [PATCH] Amiga: better emulation, part 2 --- src/engine/platform/amiga.cpp | 232 ++++++++++++++++++++++++++++++++-- src/engine/platform/amiga.h | 20 ++- 2 files changed, 234 insertions(+), 18 deletions(-) diff --git a/src/engine/platform/amiga.cpp b/src/engine/platform/amiga.cpp index bb0eb1c6..298db734 100644 --- a/src/engine/platform/amiga.cpp +++ b/src/engine/platform/amiga.cpp @@ -27,6 +27,8 @@ #define AMIGA_VPMASK 7 #define CHIP_DIVIDER 16 +#define chWrite(c,a,v) rWrite(((c)<<4)+0xa0+(a),(v)); + const char* regCheatSheetAmiga[]={ "DMACON", "96", "INTENA", "9A", @@ -88,19 +90,23 @@ void DivPlatformAmiga::acquire(short** buf, size_t len) { amiga.volPos=(amiga.volPos+1)&AMIGA_VPMASK; // run DMA - if (amiga.audEn[i]) { + if (amiga.dmaEn && amiga.audEn[i]) { amiga.audTick[i]-=AMIGA_DIVIDER; if (amiga.audTick[i]<0) { + if (bypassLimits) { + amiga.audTick[i]+=MAX(AMIGA_DIVIDER,amiga.audPer[i]); + } else { + amiga.audTick[i]+=MAX(114,amiga.audPer[i]); + } if (amiga.audByte[i]) { // read next samples - amiga.audDat[0][i]=sampleMem[((amiga.dmaLoc[i]+amiga.dmaPos[i])<<1)&chipMask]; - amiga.audDat[1][i]=sampleMem[(1+((amiga.dmaLoc[i]+amiga.dmaPos[i])<<1))&chipMask]; + amiga.audDat[0][i]=sampleMem[(amiga.dmaLoc[i]++)&chipMask]; + amiga.audDat[1][i]=sampleMem[(amiga.dmaLoc[i]++)&chipMask]; // check for length - if (amiga.dmaPos[i]>amiga.dmaLen[i]) { + if ((--amiga.dmaLen[i])==0xffff) { if (amiga.audInt[i]) irq(i); amiga.dmaLoc[i]=amiga.audLoc[i]; - amiga.dmaPos[i]=0; amiga.dmaLen[i]=amiga.audLen[i]; } } @@ -132,6 +138,7 @@ void DivPlatformAmiga::acquire(short** buf, size_t len) { } // OLD CODE + /* for (int i=0; i<4; i++) { chan[i].volPos=(chan[i].volPos+1)&AMIGA_VPMASK; if (!chan[i].active) { @@ -161,15 +168,6 @@ void DivPlatformAmiga::acquire(short** buf, size_t len) { chan[i].sample=-1; } } - /*if (chan[i].freq<124) { - if (++chan[i].busClock>=512) { - unsigned int rAmount=(124-chan[i].freq)*2; - if (chan[i].audPos>=rAmount) { - chan[i].audPos-=rAmount; - } - chan[i].busClock=0; - } - }*/ if (bypassLimits) { chan[i].audSub+=MAX(AMIGA_DIVIDER,chan[i].freq); } else { @@ -197,6 +195,7 @@ void DivPlatformAmiga::acquire(short** buf, size_t len) { oscBuf[i]->data[oscBuf[i]->needle++]=0; } } + */ filter[0][0]+=(filtConst*(outL-filter[0][0]))>>12; filter[0][1]+=(filtConst*(filter[0][0]-filter[0][1]))>>12; filter[1][0]+=(filtConst*(outR-filter[1][0]))>>12; @@ -206,11 +205,145 @@ void DivPlatformAmiga::acquire(short** buf, size_t len) { } } +void DivPlatformAmiga::irq(int ch) { + +} + +#define UPDATE_DMA(x) \ + amiga.dmaLen[x]=amiga.audLen[x]; \ + amiga.dmaLoc[x]=amiga.audLoc[x]; \ + amiga.audByte[x]=true; \ + amiga.audTick[x]=0; + +void DivPlatformAmiga::rWrite(unsigned short addr, unsigned short val) { + if (addr&1) return; + + //logV("%.3x = %.4x",addr,val); + regPool[addr>>1]=val; + + switch (addr&0x1fe) { + case 0x96: { // DMACON + if (val&32768) { + if (val&1) amiga.audEn[0]=true; + if (val&2) amiga.audEn[1]=true; + if (val&4) amiga.audEn[2]=true; + if (val&8) amiga.audEn[3]=true; + if (val&512) amiga.dmaEn=true; + } else { + if (val&1) { + amiga.audEn[0]=false; + UPDATE_DMA(0); + } + if (val&2) { + amiga.audEn[1]=false; + UPDATE_DMA(1); + } + if (val&4) { + amiga.audEn[2]=false; + UPDATE_DMA(2); + } + if (val&8) { + amiga.audEn[3]=false; + UPDATE_DMA(3); + } + if (val&512) { + amiga.dmaEn=false; + } + } + break; + } + case 0x9a: { // INTENA + if (val&32768) { + if (val&128) amiga.audInt[0]=true; + if (val&256) amiga.audInt[1]=true; + if (val&512) amiga.audInt[2]=true; + if (val&1024) amiga.audInt[3]=true; + } else { + if (val&128) amiga.audInt[0]=false; + if (val&256) amiga.audInt[1]=false; + if (val&512) amiga.audInt[2]=false; + if (val&1024) amiga.audInt[3]=false; + } + break; + } + case 0x9e: { // ADKCON + if (val&32768) { + if (val&1) amiga.useV[0]=true; + if (val&2) amiga.useV[1]=true; + if (val&4) amiga.useV[2]=true; + if (val&8) amiga.useV[3]=true; + if (val&16) amiga.useP[0]=true; + if (val&32) amiga.useP[1]=true; + if (val&64) amiga.useP[2]=true; + if (val&128) amiga.useP[3]=true; + } else { + if (val&1) amiga.useV[0]=false; + if (val&2) amiga.useV[1]=false; + if (val&4) amiga.useV[2]=false; + if (val&8) amiga.useV[3]=false; + if (val&16) amiga.useP[0]=false; + if (val&32) amiga.useP[1]=false; + if (val&64) amiga.useP[2]=false; + if (val&128) amiga.useP[3]=false; + } + break; + } + default: { // AUDx + if (addr>=0xa0 && addr<0xe0) { + const unsigned char ch=((addr-0xa0)>>4)&3; + bool updateDMA=false; + switch (addr&15) { + case 0: // LCH + amiga.audLoc[ch]&=0xffff; + amiga.audLoc[ch]|=val<<16; + updateDMA=true; + break; + case 2: // LCL + amiga.audLoc[ch]&=0xffff0000; + amiga.audLoc[ch]|=val&0xfffe; + updateDMA=true; + break; + case 4: // LEN + amiga.audLen[ch]=val; + updateDMA=true; + break; + case 6: // PER + amiga.audPer[ch]=val; + break; + case 8: // VOL + amiga.audVol[ch]=val; + break; + case 10: // DAT + amiga.audDat[0][ch]=val&0xff; + amiga.audDat[1][ch]=val>>8; + break; + } + if (updateDMA && !amiga.audEn[ch]) { + UPDATE_DMA(ch); + } + } + break; + } + } +} + +void DivPlatformAmiga::updateWave(int ch) { + if (chan[ch].useWave) { + for (int i=0; i>6; + chan[i].writeVol=true; } double off=1.0; if (!chan[i].useWave && chan[i].sample>=0 && chan[i].samplesong.sampleLen) { @@ -257,15 +390,58 @@ void DivPlatformAmiga::tick(bool sysTick) { chan[i].freq=off*parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER); if (chan[i].freq>4095) chan[i].freq=4095; if (chan[i].freq<0) chan[i].freq=0; + + chWrite(i,6,chan[i].freq); + if (chan[i].keyOn) { + rWrite(0x96,1<=0 && chan[i].samplesong.sampleLen) { + DivSample* s=parent->getSample(chan[i].sample); + chWrite(i,0,sampleOff[chan[i].sample]>>16); + chWrite(i,2,sampleOff[chan[i].sample]); + chWrite(i,4,MIN(65535,s->getLoopEndPosition(DIV_SAMPLE_DEPTH_8BIT)>>1)); + rWrite(0x96,0x8000|(1<isLoopable()) { + int loopPos=(sampleOff[chan[i].sample]+s->getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT))&(~1); + int loopEnd=(s->getLoopEndPosition(DIV_SAMPLE_DEPTH_8BIT)-s->getLoopStartPosition(DIV_SAMPLE_DEPTH_8BIT))>>1; + chWrite(i,0,loopPos>>16); + chWrite(i,2,loopPos); + chWrite(i,4,MIN(65535,loopEnd)); + } else { + chWrite(i,0,0); + chWrite(i,2,i<<8); + chWrite(i,4,0); + } + } else { + chWrite(i,0,0); + chWrite(i,2,i<<8); + chWrite(i,4,0); + } + } } + if (chan[i].keyOff) { + rWrite(0x96,1<song.brokenOutVol && !chan[c.chan].std.vol.will) { chan[c.chan].outVol=chan[c.chan].vol; + chan[c.chan].writeVol=true; } if (chan[c.chan].useWave) { chan[c.chan].ws.init(ins,chan[c.chan].audLen<<1,255,chan[c.chan].insChanged); @@ -335,6 +512,7 @@ int DivPlatformAmiga::dispatch(DivCommand c) { chan[c.chan].vol=c.value; if (!chan[c.chan].std.vol.has) { chan[c.chan].outVol=c.value; + chan[c.chan].writeVol=true; } } break; @@ -448,6 +626,7 @@ DivDispatchOscBuffer* DivPlatformAmiga::getOscBuffer(int ch) { } void DivPlatformAmiga::reset() { + memset(regPool,0,256*sizeof(unsigned short)); for (int i=0; i<4; i++) { chan[i]=DivPlatformAmiga::Channel(); chan[i].std.setEngine(parent); @@ -458,6 +637,10 @@ void DivPlatformAmiga::reset() { } filterOn=false; filtConst=filterOn?filtConstOn:filtConstOff; + + amiga=Amiga(); + // enable DMA + rWrite(0x96,0x8200); } int DivPlatformAmiga::getOutputCount() { @@ -511,6 +694,7 @@ void DivPlatformAmiga::setFlags(const DivConfig& flags) { chipMem=flags.getInt("chipMem",21); if (chipMem<18) chipMem=18; if (chipMem>21) chipMem=21; + chipMask=(1<& wlist) { + for (DivRegWrite& i: wlist) rWrite(i.addr,i.val); +} + +unsigned char* DivPlatformAmiga::getRegisterPool() { + return (unsigned char*)regPool; +} + +int DivPlatformAmiga::getRegisterPoolSize() { + return 128; +} + +int DivPlatformAmiga::getRegisterPoolDepth() { + return 16; +} + const void* DivPlatformAmiga::getSampleMem(int index) { return index == 0 ? sampleMem : NULL; } diff --git a/src/engine/platform/amiga.h b/src/engine/platform/amiga.h index b2311405..80fe64ac 100644 --- a/src/engine/platform/amiga.h +++ b/src/engine/platform/amiga.h @@ -37,7 +37,7 @@ class DivPlatformAmiga: public DivDispatch { unsigned char volPos; int sample, wave; int busClock; - bool useWave, setPos, useV, useP, dmaOn, audDatClock; + bool useWave, setPos, useV, useP, dmaOn, audDatClock, writeVol; DivWaveSynth ws; Channel(): SharedChannel(64), @@ -58,7 +58,8 @@ class DivPlatformAmiga: public DivDispatch { useV(false), useP(false), dmaOn(false), - audDatClock(false) {} + audDatClock(false), + writeVol(true) {} }; Channel chan[4]; DivDispatchOscBuffer* oscBuf[4]; @@ -72,7 +73,9 @@ class DivPlatformAmiga: public DivDispatch { bool audInt[4]; // interrupt on bool audEn[4]; // audio DMA on bool useP[4]; // period modulation - bool useV[4]; // volume modulatiom + bool useV[4]; // volume modulation + + bool dmaEn; unsigned int audLoc[4]; // address unsigned short audLen[4]; // length @@ -84,7 +87,7 @@ class DivPlatformAmiga: public DivDispatch { // internal state int audTick[4]; // tick of period unsigned int dmaLoc[4]; // address - unsigned short dmaPos[4]; // position + unsigned short dmaLen[4]; // position bool audByte[4]; // which byte of audDat to output unsigned char volPos; // position of volume PWM @@ -104,6 +107,8 @@ class DivPlatformAmiga: public DivDispatch { unsigned int sampleOff[256]; bool sampleLoaded[256]; + unsigned short regPool[256]; + unsigned char* sampleMem; size_t sampleMemLen; @@ -113,12 +118,17 @@ class DivPlatformAmiga: public DivDispatch { friend void putDispatchChan(void*,int,int); void irq(int ch); + void rWrite(unsigned short addr, unsigned short val); + void updateWave(int ch); public: void acquire(short** buf, size_t len); int dispatch(DivCommand c); void* getChanState(int chan); DivDispatchOscBuffer* getOscBuffer(int chan); + unsigned char* getRegisterPool(); + int getRegisterPoolSize(); + int getRegisterPoolDepth(); void reset(); void forceIns(); void tick(bool sysTick=true); @@ -131,6 +141,8 @@ class DivPlatformAmiga: public DivDispatch { void notifyWaveChange(int wave); void notifyInsDeletion(void* ins); void renderSamples(int chipID); + void poke(unsigned int addr, unsigned short val); + void poke(std::vector& wlist); const char** getRegisterSheet(); const void* getSampleMem(int index=0); size_t getSampleMemCapacity(int index=0);