mirror of
https://github.com/tildearrow/furnace.git
synced 2024-11-26 22:43:01 +00:00
Amiga: improve accuracy
don't stop DMA until sample is done playing do not update output if PM/AM is on
This commit is contained in:
parent
6df06a7f89
commit
f0e51f6e88
3 changed files with 111 additions and 79 deletions
|
@ -81,7 +81,18 @@ const char** DivPlatformAmiga::getRegisterSheet() {
|
|||
|
||||
void DivPlatformAmiga::acquire(short** buf, size_t len) {
|
||||
thread_local int outL, outR, output;
|
||||
|
||||
for (size_t h=0; h<len; h++) {
|
||||
if (--delay<0) delay=0;
|
||||
if (!writes.empty() && delay<=0) {
|
||||
QueuedWrite w=writes.front();
|
||||
|
||||
if (w.addr==0x96 && !(w.val&0x8000)) delay=4096/AMIGA_DIVIDER;
|
||||
|
||||
amiga.write(w.addr,w.val);
|
||||
writes.pop();
|
||||
}
|
||||
|
||||
bool hsync=bypassLimits;
|
||||
outL=0;
|
||||
outR=0;
|
||||
|
@ -99,7 +110,8 @@ void DivPlatformAmiga::acquire(short** buf, size_t len) {
|
|||
}
|
||||
for (int i=0; i<4; i++) {
|
||||
// run DMA
|
||||
if (amiga.dmaEn && amiga.audEn[i] && !amiga.audIr[i]) {
|
||||
if (amiga.audEn[i]) amiga.mustDMA[i]=true;
|
||||
if (amiga.dmaEn && amiga.mustDMA[i] && !amiga.audIr[i]) {
|
||||
amiga.audTick[i]-=AMIGA_DIVIDER;
|
||||
if (amiga.audTick[i]<0) {
|
||||
amiga.audTick[i]+=MAX(AMIGA_DIVIDER,amiga.audPer[i]);
|
||||
|
@ -114,6 +126,8 @@ void DivPlatformAmiga::acquire(short** buf, size_t len) {
|
|||
amiga.audWord[i]=!amiga.audWord[i];
|
||||
}
|
||||
|
||||
amiga.mustDMA[i]=amiga.audEn[i];
|
||||
|
||||
amiga.audByte[i]=!amiga.audByte[i];
|
||||
if (!amiga.audByte[i] && (amiga.useV[i] || amiga.useP[i])) {
|
||||
amiga.nextOut2[i]=((unsigned char)amiga.audDat[0][i])<<8|((unsigned char)amiga.audDat[1][i]);
|
||||
|
@ -130,7 +144,7 @@ void DivPlatformAmiga::acquire(short** buf, size_t len) {
|
|||
amiga.audPer[i+1]=amiga.nextOut2[i];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
} else if (!amiga.useV[i] && !amiga.useP[i]) {
|
||||
amiga.nextOut[i]=amiga.audDat[amiga.audByte[i]][i];
|
||||
}
|
||||
}
|
||||
|
@ -202,111 +216,96 @@ 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;
|
||||
dmaLen[x]=audLen[x]; \
|
||||
dmaLoc[x]=audLoc[x]; \
|
||||
audByte[x]=true; \
|
||||
audTick[x]=0;
|
||||
|
||||
void DivPlatformAmiga::rWrite(unsigned short addr, unsigned short val) {
|
||||
void DivPlatformAmiga::Amiga::write(unsigned short addr, unsigned short val) {
|
||||
if (addr&1) return;
|
||||
|
||||
//logV("%.3x = %.4x",addr,val);
|
||||
regPool[addr>>1]=val;
|
||||
|
||||
if (!skipRegisterWrites && dumpWrites) {
|
||||
addWrite(addr,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;
|
||||
if (val&1) audEn[0]=true;
|
||||
if (val&2) audEn[1]=true;
|
||||
if (val&4) audEn[2]=true;
|
||||
if (val&8) audEn[3]=true;
|
||||
if (val&512) dmaEn=true;
|
||||
} else {
|
||||
if (val&1) {
|
||||
amiga.audEn[0]=false;
|
||||
UPDATE_DMA(0);
|
||||
audEn[0]=false;
|
||||
}
|
||||
if (val&2) {
|
||||
amiga.audEn[1]=false;
|
||||
UPDATE_DMA(1);
|
||||
audEn[1]=false;
|
||||
}
|
||||
if (val&4) {
|
||||
amiga.audEn[2]=false;
|
||||
UPDATE_DMA(2);
|
||||
audEn[2]=false;
|
||||
}
|
||||
if (val&8) {
|
||||
amiga.audEn[3]=false;
|
||||
UPDATE_DMA(3);
|
||||
audEn[3]=false;
|
||||
}
|
||||
if (val&512) {
|
||||
amiga.dmaEn=false;
|
||||
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;
|
||||
if (val&128) audInt[0]=true;
|
||||
if (val&256) audInt[1]=true;
|
||||
if (val&512) audInt[2]=true;
|
||||
if (val&1024) 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;
|
||||
if (val&128) audInt[0]=false;
|
||||
if (val&256) audInt[1]=false;
|
||||
if (val&512) audInt[2]=false;
|
||||
if (val&1024) audInt[3]=false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0x9c: { // INTREQ
|
||||
if (val&32768) {
|
||||
if (val&128) {
|
||||
amiga.audIr[0]=true;
|
||||
irq(0);
|
||||
audIr[0]=true;
|
||||
}
|
||||
if (val&256) {
|
||||
amiga.audIr[1]=true;
|
||||
irq(1);
|
||||
audIr[1]=true;
|
||||
}
|
||||
if (val&512) {
|
||||
amiga.audIr[2]=true;
|
||||
irq(2);
|
||||
audIr[2]=true;
|
||||
}
|
||||
if (val&1024) {
|
||||
amiga.audIr[3]=true;
|
||||
irq(3);
|
||||
audIr[3]=true;
|
||||
}
|
||||
} else {
|
||||
if (val&128) amiga.audIr[0]=false;
|
||||
if (val&256) amiga.audIr[1]=false;
|
||||
if (val&512) amiga.audIr[2]=false;
|
||||
if (val&1024) amiga.audIr[3]=false;
|
||||
if (val&128) audIr[0]=false;
|
||||
if (val&256) audIr[1]=false;
|
||||
if (val&512) audIr[2]=false;
|
||||
if (val&1024) audIr[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;
|
||||
if (val&1) useV[0]=true;
|
||||
if (val&2) useV[1]=true;
|
||||
if (val&4) useV[2]=true;
|
||||
if (val&8) useV[3]=true;
|
||||
if (val&16) useP[0]=true;
|
||||
if (val&32) useP[1]=true;
|
||||
if (val&64) useP[2]=true;
|
||||
if (val&128) 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;
|
||||
if (val&1) useV[0]=false;
|
||||
if (val&2) useV[1]=false;
|
||||
if (val&4) useV[2]=false;
|
||||
if (val&8) useV[3]=false;
|
||||
if (val&16) useP[0]=false;
|
||||
if (val&32) useP[1]=false;
|
||||
if (val&64) useP[2]=false;
|
||||
if (val&128) useP[3]=false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -316,31 +315,31 @@ void DivPlatformAmiga::rWrite(unsigned short addr, unsigned short val) {
|
|||
bool updateDMA=false;
|
||||
switch (addr&15) {
|
||||
case 0: // LCH
|
||||
amiga.audLoc[ch]&=0xffff;
|
||||
amiga.audLoc[ch]|=val<<16;
|
||||
audLoc[ch]&=0xffff;
|
||||
audLoc[ch]|=val<<16;
|
||||
updateDMA=true;
|
||||
break;
|
||||
case 2: // LCL
|
||||
amiga.audLoc[ch]&=0xffff0000;
|
||||
amiga.audLoc[ch]|=val&0xfffe;
|
||||
audLoc[ch]&=0xffff0000;
|
||||
audLoc[ch]|=val&0xfffe;
|
||||
updateDMA=true;
|
||||
break;
|
||||
case 4: // LEN
|
||||
amiga.audLen[ch]=val;
|
||||
audLen[ch]=val;
|
||||
updateDMA=true;
|
||||
break;
|
||||
case 6: // PER
|
||||
amiga.audPer[ch]=val;
|
||||
audPer[ch]=val;
|
||||
break;
|
||||
case 8: // VOL
|
||||
amiga.audVol[ch]=val;
|
||||
audVol[ch]=val;
|
||||
break;
|
||||
case 10: // DAT
|
||||
amiga.audDat[0][ch]=val&0xff;
|
||||
amiga.audDat[1][ch]=val>>8;
|
||||
audDat[0][ch]=val&0xff;
|
||||
audDat[1][ch]=val>>8;
|
||||
break;
|
||||
}
|
||||
if (updateDMA && !amiga.audEn[ch]) {
|
||||
if (updateDMA && !mustDMA[ch]) {
|
||||
UPDATE_DMA(ch);
|
||||
}
|
||||
}
|
||||
|
@ -349,6 +348,20 @@ void DivPlatformAmiga::rWrite(unsigned short addr, unsigned short val) {
|
|||
}
|
||||
}
|
||||
|
||||
void DivPlatformAmiga::rWrite(unsigned short addr, unsigned short val) {
|
||||
if (addr&1) return;
|
||||
|
||||
//logV("%.3x = %.4x",addr,val);
|
||||
if (!skipRegisterWrites) {
|
||||
writes.push(QueuedWrite(addr,val));
|
||||
regPool[addr>>1]=val;
|
||||
|
||||
if (dumpWrites) {
|
||||
addWrite(addr,val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformAmiga::updateWave(int ch) {
|
||||
for (int i=0; i<MIN(256,(chan[ch].audLen<<1)); i++) {
|
||||
sampleMem[(ch<<8)|i]=chan[ch].ws.output[i]^0x80;
|
||||
|
@ -417,6 +430,13 @@ void DivPlatformAmiga::tick(bool sysTick) {
|
|||
|
||||
if (dmaOff) rWrite(0x96,dmaOff);
|
||||
|
||||
for (int i=0; i<4; i++) {
|
||||
if (chan[i].updateWave) {
|
||||
chan[i].updateWave=false;
|
||||
updateWave(i);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i=0; i<4; i++) {
|
||||
double off=1.0;
|
||||
if (!chan[i].useWave && chan[i].sample>=0 && chan[i].sample<parent->song.sampleLen) {
|
||||
|
@ -520,10 +540,6 @@ void DivPlatformAmiga::tick(bool sysTick) {
|
|||
chan[i].writeVol=false;
|
||||
chWrite(i,8,chan[i].outVol);
|
||||
}
|
||||
if (chan[i].updateWave) {
|
||||
chan[i].updateWave=false;
|
||||
updateWave(i);
|
||||
}
|
||||
}
|
||||
|
||||
if (updateADKCon) {
|
||||
|
@ -717,6 +733,7 @@ void DivPlatformAmiga::muteChannel(int ch, bool mute) {
|
|||
}
|
||||
|
||||
void DivPlatformAmiga::forceIns() {
|
||||
logV("at time of clear: %d",writes.size());
|
||||
for (int i=0; i<4; i++) {
|
||||
chan[i].insChanged=true;
|
||||
chan[i].freqChanged=true;
|
||||
|
@ -738,6 +755,7 @@ DivDispatchOscBuffer* DivPlatformAmiga::getOscBuffer(int ch) {
|
|||
}
|
||||
|
||||
void DivPlatformAmiga::reset() {
|
||||
writes.clear();
|
||||
memset(regPool,0,256*sizeof(unsigned short));
|
||||
for (int i=0; i<4; i++) {
|
||||
chan[i]=DivPlatformAmiga::Channel();
|
||||
|
@ -750,6 +768,7 @@ void DivPlatformAmiga::reset() {
|
|||
filterOn=false;
|
||||
filtConst=filterOn?filtConstOn:filtConstOff;
|
||||
updateADKCon=true;
|
||||
delay=0;
|
||||
|
||||
amiga=Amiga();
|
||||
// enable DMA
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#define _AMIGA_H
|
||||
|
||||
#include "../dispatch.h"
|
||||
#include "../../fixedQueue.h"
|
||||
#include "../waveSynth.h"
|
||||
|
||||
class DivPlatformAmiga: public DivDispatch {
|
||||
|
@ -59,12 +60,14 @@ class DivPlatformAmiga: public DivDispatch {
|
|||
bool amigaModel;
|
||||
bool filterOn;
|
||||
bool updateADKCon;
|
||||
short delay;
|
||||
|
||||
struct Amiga {
|
||||
// register state
|
||||
bool audInt[4]; // interrupt on
|
||||
bool audIr[4]; // interrupt request
|
||||
bool audEn[4]; // audio DMA on
|
||||
bool mustDMA[4]; // audio DMA must run
|
||||
bool useP[4]; // period modulation
|
||||
bool useV[4]; // volume modulation
|
||||
|
||||
|
@ -91,6 +94,8 @@ class DivPlatformAmiga: public DivDispatch {
|
|||
unsigned short hPos; // horizontal position of beam
|
||||
unsigned char state[4]; // current channel state
|
||||
|
||||
void write(unsigned short addr, unsigned short val);
|
||||
|
||||
Amiga() {
|
||||
memset(this,0,sizeof(*this));
|
||||
}
|
||||
|
@ -113,6 +118,14 @@ class DivPlatformAmiga: public DivDispatch {
|
|||
|
||||
int sep1, sep2;
|
||||
|
||||
struct QueuedWrite {
|
||||
unsigned short addr;
|
||||
unsigned short val;
|
||||
QueuedWrite(): addr(0), val(9) {}
|
||||
QueuedWrite(unsigned short a, unsigned short v): addr(a), val(v) {}
|
||||
};
|
||||
FixedQueue<QueuedWrite,512> writes;
|
||||
|
||||
friend void putDispatchChip(void*,int);
|
||||
friend void putDispatchChan(void*,int,int);
|
||||
friend class DivExportAmigaValidation;
|
||||
|
|
Loading…
Reference in a new issue