arcade: finish it up

0.12 feature parityyyyyyyy!
This commit is contained in:
tildearrow 2021-12-09 00:46:48 -05:00
parent 7ac225e839
commit defaf7397a
5 changed files with 131 additions and 34 deletions

View File

@ -23,6 +23,7 @@ enum DivDispatchCmds {
DIV_CMD_SAMPLE_FREQ,
DIV_CMD_FM_LFO,
DIV_CMD_FM_LFO_WAVE,
DIV_CMD_FM_TL,
DIV_CMD_FM_AR,
DIV_CMD_FM_FB,

View File

@ -824,19 +824,13 @@ bool DivEngine::init(String outName) {
if (dispatch->isStereo()) {
for (int i=0; i<got.bufsize; i++) {
if (bbOut[0][i]<-16384) bbOut[0][i]=-16384;
if (bbOut[0][i]>16383) bbOut[0][i]=16383;
ilBuffer[i<<1]=bbOut[0][i]*2;
if (bbOut[1][i]<-16384) bbOut[1][i]=-16384;
if (bbOut[1][i]>16383) bbOut[1][i]=16383;
ilBuffer[1+(i<<1)]=bbOut[1][i]*2;
ilBuffer[i<<1]=bbOut[0][i];
ilBuffer[1+(i<<1)]=bbOut[1][i];
}
} else {
for (int i=0; i<got.bufsize; i++) {
if (bbOut[0][i]<-16384) bbOut[0][i]=-16384;
if (bbOut[0][i]>16383) bbOut[0][i]=16383;
ilBuffer[i<<1]=bbOut[0][i]*2;
ilBuffer[1+(i<<1)]=bbOut[0][i]*2;
ilBuffer[i<<1]=bbOut[0][i];
ilBuffer[1+(i<<1)]=bbOut[0][i];
}
}

View File

@ -9,6 +9,9 @@ static unsigned short chanOffs[8]={
static unsigned short opOffs[4]={
0x00, 0x08, 0x10, 0x18
};
static int pcmRates[6]={
65,65,90,131,180,255
};
static bool isOutput[8][4]={
// 1 3 2 4
{false,false,false,true},
@ -50,12 +53,39 @@ void DivPlatformArcade::acquire(short* bufL, short* bufR, size_t start, size_t l
OPM_Clock(&fm,o,NULL,NULL,NULL);
OPM_Clock(&fm,o,NULL,NULL,NULL);
OPM_Clock(&fm,o,NULL,NULL,NULL);
//if (o[0]<-32768) o[0]=-32768;
//if (o[0]>32767) o[0]=32767;
//if (o[1]<-32768) o[1]=-32768;
//if (o[1]>32767) o[1]=32767;
pcmCycles+=31250;
if (pcmCycles>=rate) {
pcmCycles-=rate;
// do a PCM cycle
pcmL=0; pcmR=0;
for (int i=8; i<13; i++) {
if (chan[i].pcm.sample>=0) {
DivSample* s=parent->song.sample[chan[i].pcm.sample];
if (s->depth==8) {
pcmL+=(s->rendData[chan[i].pcm.pos>>8]*chan[i].chVolL);
pcmR+=(s->rendData[chan[i].pcm.pos>>8]*chan[i].chVolR);
} else {
pcmL+=(s->rendData[chan[i].pcm.pos>>8]*chan[i].chVolL)>>8;
pcmR+=(s->rendData[chan[i].pcm.pos>>8]*chan[i].chVolR)>>8;
}
chan[i].pcm.pos+=chan[i].pcm.freq;
if (chan[i].pcm.pos>=(s->rendLength<<8)) {
chan[i].pcm.sample=-1;
}
}
}
}
o[0]+=pcmL;
o[1]+=pcmR;
if (o[0]<-32768) o[0]=-32768;
if (o[0]>32767) o[0]=32767;
if (o[1]<-32768) o[1]=-32768;
if (o[1]>32767) o[1]=32767;
bufL[h]=o[0];
bufR[h]=o[1];
@ -87,9 +117,7 @@ void DivPlatformArcade::tick() {
for (int i=0; i<8; i++) {
if (chan[i].freqChanged) {
chan[i].freq=chan[i].baseFreq+(chan[i].pitch>>2)-64;
//writes.emplace(chanOffs[i]+0xa4,freqt>>8);
//writes.emplace(chanOffs[i]+0xa0,freqt&0xff);
chan[i].freq=chan[i].baseFreq+(chan[i].pitch>>1)-64;
writes.emplace(i+0x28,hScale(chan[i].freq>>6));
writes.emplace(i+0x30,chan[i].freq<<2);
chan[i].freqChanged=false;
@ -104,6 +132,16 @@ void DivPlatformArcade::tick() {
int DivPlatformArcade::dispatch(DivCommand c) {
switch (c.cmd) {
case DIV_CMD_NOTE_ON: {
if (c.chan>7) {
chan[c.chan].pcm.sample=c.value%12;
if (chan[c.chan].pcm.sample>=parent->song.sampleLen) {
chan[c.chan].pcm.sample=-1;
break;
}
chan[c.chan].pcm.pos=0;
chan[c.chan].pcm.freq=pcmRates[parent->song.sample[chan[c.chan].pcm.sample]->rate];
break;
}
DivInstrument* ins=parent->getIns(chan[c.chan].ins);
for (int i=0; i<4; i++) {
@ -127,8 +165,8 @@ int DivPlatformArcade::dispatch(DivCommand c) {
}
}
if (chan[c.chan].insChanged) {
rWrite(chanOffs[c.chan]+0x20,(ins->fm.alg&7)|(ins->fm.fb<<3)|0xc0);
//rWrite(chanOffs[c.chan]+0xb4,(chan[c.chan].pan<<6)|(ins->fm.fms&7)|((ins->fm.ams&3)<<4));
rWrite(chanOffs[c.chan]+0x20,(ins->fm.alg&7)|(ins->fm.fb<<3)|((chan[c.chan].chVolL&1)<<6)|((chan[c.chan].chVolR&1)<<7));
rWrite(chanOffs[c.chan]+0x38,((ins->fm.fms&7)<<4)|(ins->fm.ams&3));
}
chan[c.chan].insChanged=false;
@ -139,11 +177,19 @@ int DivPlatformArcade::dispatch(DivCommand c) {
break;
}
case DIV_CMD_NOTE_OFF:
if (c.chan>7) {
chan[c.chan].pcm.sample=-1;
}
chan[c.chan].keyOff=true;
chan[c.chan].active=false;
break;
case DIV_CMD_VOLUME: {
chan[c.chan].vol=c.value;
if (c.chan>7) {
chan[c.chan].chVolL=c.value;
chan[c.chan].chVolR=c.value;
break;
}
DivInstrument* ins=parent->getIns(chan[c.chan].ins);
for (int i=0; i<4; i++) {
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
@ -168,6 +214,15 @@ int DivPlatformArcade::dispatch(DivCommand c) {
break;
case DIV_CMD_PANNING: {
// TODO
if (c.chan>7) {
chan[c.chan].chVolL=(c.value>>4)|(((c.value>>4)>>1)<<4);
chan[c.chan].chVolR=(c.value&15)|(((c.value&15)>>1)<<4);
} else {
DivInstrument* ins=parent->getIns(chan[c.chan].ins);
chan[c.chan].chVolL=((c.value>>4)==1);
chan[c.chan].chVolR=((c.value&15)==1);
rWrite(chanOffs[c.chan]+0x20,(ins->fm.alg&7)|(ins->fm.fb<<3)|((chan[c.chan].chVolL&1)<<6)|((chan[c.chan].chVolR&1)<<7));
}
break;
}
case DIV_CMD_PITCH: {
@ -203,42 +258,62 @@ int DivPlatformArcade::dispatch(DivCommand c) {
break;
}
case DIV_CMD_FM_LFO: {
rWrite(0x22,(c.value&7)|((c.value>>4)<<3));
if (c.chan>7) break;
rWrite(0x18,c.value);
break;
}
case DIV_CMD_FM_LFO_WAVE: {
if (c.chan>7) break;
rWrite(0x1b,c.value&3);
break;
}
case DIV_CMD_FM_MULT: {
if (c.chan>7) break;
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
DivInstrument* ins=parent->getIns(chan[c.chan].ins);
DivInstrumentFM::Operator op=ins->fm.op[orderedOps[c.value]];
rWrite(baseAddr+0x30,(c.value2&15)|(dtTable[op.dt&7]<<4));
rWrite(baseAddr+0x40,(c.value2&15)|(dtTable[op.dt&7]<<4));
break;
}
case DIV_CMD_FM_TL: {
if (c.chan>7) break;
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
DivInstrument* ins=parent->getIns(chan[c.chan].ins);
if (isOutput[ins->fm.alg][c.value]) {
rWrite(baseAddr+0x40,127-(((127-c.value2)*(chan[c.chan].vol&0x7f))/127));
rWrite(baseAddr+0x60,127-(((127-c.value2)*(chan[c.chan].vol&0x7f))/127));
} else {
rWrite(baseAddr+0x40,c.value2);
rWrite(baseAddr+0x60,c.value2);
}
break;
}
case DIV_CMD_FM_AR: {
if (c.chan>7) break;
DivInstrument* ins=parent->getIns(chan[c.chan].ins);
if (c.value<0) {
for (int i=0; i<4; i++) {
DivInstrumentFM::Operator op=ins->fm.op[i];
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
rWrite(baseAddr+0x50,(c.value2&31)|(op.rs<<6));
rWrite(baseAddr+0x80,(c.value2&31)|(op.rs<<6));
}
} else {
DivInstrumentFM::Operator op=ins->fm.op[orderedOps[c.value]];
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
rWrite(baseAddr+0x50,(c.value2&31)|(op.rs<<6));
rWrite(baseAddr+0x80,(c.value2&31)|(op.rs<<6));
}
break;
}
case DIV_CMD_STD_NOISE_FREQ: {
if (c.chan!=7) break;
if (c.value) {
if (c.value>0x1f) {
rWrite(0x0f,0x80);
} else {
rWrite(0x0f,0x80|(0x1f-c.value));
}
} else {
rWrite(0x0f,0);
}
}
case DIV_ALWAYS_SET_VOLUME:
return 0;
break;
@ -249,6 +324,9 @@ int DivPlatformArcade::dispatch(DivCommand c) {
break;
case DIV_CMD_PRE_NOTE:
break;
case DIV_CMD_SAMPLE_FREQ:
chan[c.chan].pcm.freq=c.value;
break;
default:
//printf("WARNING: unimplemented command %d\n",c.cmd);
break;
@ -275,6 +353,11 @@ int DivPlatformArcade::init(DivEngine* p, int channels, int sugRate, bool pal) {
}
lastBusy=60;
pcmCycles=0;
pcmL=0;
pcmR=0;
rWrite(0x19,0xff);
extMode=false;

View File

@ -22,7 +22,7 @@ class DivPlatformArcade: public DivDispatch {
unsigned char freq;
PCMChannel(): sample(-1), pos(0), len(0), freq(0) {}
} pcm;
Channel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), ins(-1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), portaPause(false), vol(0), chVolL(8), chVolR(8) {}
Channel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), ins(-1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), portaPause(false), vol(0), chVolL(127), chVolR(127) {}
};
Channel chan[13];
struct QueuedWrite {
@ -33,9 +33,8 @@ class DivPlatformArcade: public DivDispatch {
};
std::queue<QueuedWrite> writes;
opm_t fm;
int psgClocks;
int psgOut;
int delay;
int pcmL, pcmR, pcmCycles;
unsigned char lastBusy;
bool extMode;

View File

@ -33,6 +33,7 @@ const char* cmdName[DIV_CMD_MAX]={
"SAMPLE_FREQ",
"FM_LFO",
"FM_LFO_WAVE",
"FM_TL",
"FM_AR",
"FM_FB",
@ -167,9 +168,14 @@ bool DivEngine::perSystemPostEffect(int ch, unsigned char effect, unsigned char
switch (song.system) {
case DIV_SYSTEM_GENESIS:
case DIV_SYSTEM_GENESIS_EXT:
case DIV_SYSTEM_ARCADE:
switch (effect) {
case 0x10: // LFO
dispatchCmd(DivCommand(DIV_CMD_FM_LFO,ch,effectVal));
case 0x10: // LFO or noise mode
if (song.system==DIV_SYSTEM_ARCADE) {
dispatchCmd(DivCommand(DIV_CMD_STD_NOISE_FREQ,ch,effectVal));
} else {
dispatchCmd(DivCommand(DIV_CMD_FM_LFO,ch,effectVal));
}
break;
case 0x11: // FB
dispatchCmd(DivCommand(DIV_CMD_FM_FB,ch,effectVal&7));
@ -191,8 +197,17 @@ bool DivEngine::perSystemPostEffect(int ch, unsigned char effect, unsigned char
dispatchCmd(DivCommand(DIV_CMD_FM_MULT,ch,(effectVal>>4)-1,effectVal&15));
}
break;
case 0x18: // EXT
dispatchCmd(DivCommand(DIV_CMD_FM_EXTCH,ch,effectVal));
case 0x17: // arcade LFO
if (song.system==DIV_SYSTEM_ARCADE) {
dispatchCmd(DivCommand(DIV_CMD_FM_LFO,ch,effectVal));
}
break;
case 0x18: // EXT or LFO waveform
if (song.system==DIV_SYSTEM_ARCADE) {
dispatchCmd(DivCommand(DIV_CMD_FM_LFO_WAVE,ch,effectVal));
} else {
dispatchCmd(DivCommand(DIV_CMD_FM_EXTCH,ch,effectVal));
}
break;
case 0x19: // AR global
dispatchCmd(DivCommand(DIV_CMD_FM_AR,ch,-1,effectVal&31));
@ -209,6 +224,11 @@ bool DivEngine::perSystemPostEffect(int ch, unsigned char effect, unsigned char
case 0x1d: // AR op4
dispatchCmd(DivCommand(DIV_CMD_FM_AR,ch,3,effectVal&31));
break;
case 0x20: // PCM frequency
if (song.system==DIV_SYSTEM_ARCADE) {
dispatchCmd(DivCommand(DIV_CMD_SAMPLE_FREQ,ch,effectVal));
}
break;
default:
return false;
}