Merge branch 'master' into ay_divider
This commit is contained in:
commit
8e50ad8c90
87 changed files with 8037 additions and 649 deletions
|
|
@ -220,7 +220,7 @@ void DivPlatformAmiga::tick(bool sysTick) {
|
|||
}
|
||||
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
|
||||
//DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_AMIGA);
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2);
|
||||
chan[i].freq=off*parent->calcFreq(chan[i].baseFreq,chan[i].pitch,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;
|
||||
if (chan[i].keyOn) {
|
||||
|
|
@ -238,7 +238,6 @@ int DivPlatformAmiga::dispatch(DivCommand c) {
|
|||
switch (c.cmd) {
|
||||
case DIV_CMD_NOTE_ON: {
|
||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA);
|
||||
double off=1.0;
|
||||
if (ins->amiga.useWave) {
|
||||
chan[c.chan].useWave=true;
|
||||
chan[c.chan].audLen=(ins->amiga.waveLen+1)>>1;
|
||||
|
|
@ -250,19 +249,11 @@ int DivPlatformAmiga::dispatch(DivCommand c) {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
chan[c.chan].sample=ins->amiga.initSample;
|
||||
chan[c.chan].sample=ins->amiga.getSample(c.value);
|
||||
chan[c.chan].useWave=false;
|
||||
if (chan[c.chan].sample>=0 && chan[c.chan].sample<parent->song.sampleLen) {
|
||||
DivSample* s=parent->getSample(chan[c.chan].sample);
|
||||
if (s->centerRate<1) {
|
||||
off=1.0;
|
||||
} else {
|
||||
off=8363.0/(double)s->centerRate;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].baseFreq=round(off*NOTE_PERIODIC_NOROUND(c.value));
|
||||
chan[c.chan].baseFreq=round(NOTE_PERIODIC_NOROUND(c.value));
|
||||
}
|
||||
if (chan[c.chan].useWave || chan[c.chan].sample<0 || chan[c.chan].sample>=parent->song.sampleLen) {
|
||||
chan[c.chan].sample=-1;
|
||||
|
|
@ -328,17 +319,8 @@ int DivPlatformAmiga::dispatch(DivCommand c) {
|
|||
break;
|
||||
case DIV_CMD_NOTE_PORTA: {
|
||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA);
|
||||
chan[c.chan].sample=ins->amiga.initSample;
|
||||
double off=1.0;
|
||||
if (!chan[c.chan].useWave && chan[c.chan].sample>=0 && chan[c.chan].sample<parent->song.sampleLen) {
|
||||
DivSample* s=parent->getSample(chan[c.chan].sample);
|
||||
if (s->centerRate<1) {
|
||||
off=1.0;
|
||||
} else {
|
||||
off=8363.0/(double)s->centerRate;
|
||||
}
|
||||
}
|
||||
int destFreq=round(off*NOTE_PERIODIC_NOROUND(c.value2));
|
||||
chan[c.chan].sample=ins->amiga.getSample(c.value2);
|
||||
int destFreq=round(NOTE_PERIODIC_NOROUND(c.value2));
|
||||
bool return2=false;
|
||||
if (destFreq>chan[c.chan].baseFreq) {
|
||||
chan[c.chan].baseFreq+=c.value;
|
||||
|
|
@ -361,16 +343,7 @@ int DivPlatformAmiga::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_LEGATO: {
|
||||
double off=1.0;
|
||||
if (!chan[c.chan].useWave && chan[c.chan].sample>=0 && chan[c.chan].sample<parent->song.sampleLen) {
|
||||
DivSample* s=parent->getSample(chan[c.chan].sample);
|
||||
if (s->centerRate<1) {
|
||||
off=1.0;
|
||||
} else {
|
||||
off=8363.0/(double)s->centerRate;
|
||||
}
|
||||
}
|
||||
chan[c.chan].baseFreq=round(off*NOTE_PERIODIC_NOROUND(c.value+((chan[c.chan].std.arp.will && !chan[c.chan].std.arp.mode)?(chan[c.chan].std.arp.val):(0))));
|
||||
chan[c.chan].baseFreq=round(NOTE_PERIODIC_NOROUND(c.value+((chan[c.chan].std.arp.will && !chan[c.chan].std.arp.mode)?(chan[c.chan].std.arp.val):(0))));
|
||||
chan[c.chan].freqChanged=true;
|
||||
chan[c.chan].note=c.value;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -251,7 +251,7 @@ void DivPlatformAY8910::tick(bool sysTick) {
|
|||
if (!chan[i].std.ex3.will) chan[i].autoEnvNum=1;
|
||||
}
|
||||
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2);
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER);
|
||||
if (chan[i].freq>4095) chan[i].freq=4095;
|
||||
if (chan[i].keyOn) {
|
||||
//rWrite(16+i*5+1,((chan[i].duty&3)<<6)|(63-(ins->gb.soundLen&63)));
|
||||
|
|
|
|||
|
|
@ -280,7 +280,7 @@ void DivPlatformAY8930::tick(bool sysTick) {
|
|||
immWrite(0x1a,ayNoiseOr);
|
||||
}
|
||||
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2);
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER);
|
||||
if (chan[i].freq>65535) chan[i].freq=65535;
|
||||
if (chan[i].keyOn) {
|
||||
if (chan[i].insChanged) {
|
||||
|
|
|
|||
|
|
@ -137,7 +137,7 @@ void DivPlatformBubSysWSG::tick(bool sysTick) {
|
|||
}
|
||||
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
|
||||
//DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_SCC);
|
||||
chan[i].freq=0x1000-parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)+chan[i].pitch2;
|
||||
chan[i].freq=0x1000-parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER);
|
||||
if (chan[i].freq<0) chan[i].freq=0;
|
||||
if (chan[i].freq>4095) chan[i].freq=4095;
|
||||
k005289->load(i,chan[i].freq);
|
||||
|
|
|
|||
|
|
@ -217,7 +217,7 @@ void DivPlatformC64::tick(bool sysTick) {
|
|||
}
|
||||
|
||||
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,8,chan[i].pitch2);
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,8,chan[i].pitch2,chipClock,CHIP_FREQBASE);
|
||||
if (chan[i].freq>0xffff) chan[i].freq=0xffff;
|
||||
if (chan[i].keyOn) {
|
||||
rWrite(i*7+5,(chan[i].attack<<4)|(chan[i].decay));
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ void DivPlatformDummy::tick(bool sysTick) {
|
|||
|
||||
if (chan[i].freqChanged) {
|
||||
chan[i].freqChanged=false;
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch);
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,0,0,chipClock,CHIP_FREQBASE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -220,7 +220,7 @@ void DivPlatformFDS::tick(bool sysTick) {
|
|||
}
|
||||
}
|
||||
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,2,chan[i].pitch2);
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,2,chan[i].pitch2,chipClock,CHIP_FREQBASE);
|
||||
if (chan[i].freq>4095) chan[i].freq=4095;
|
||||
if (chan[i].freq<0) chan[i].freq=0;
|
||||
if (chan[i].keyOn) {
|
||||
|
|
|
|||
|
|
@ -237,7 +237,7 @@ void DivPlatformGB::tick(bool sysTick) {
|
|||
if (ntPos>255) ntPos=255;
|
||||
chan[i].freq=noiseTable[ntPos];
|
||||
} else {
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2);
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER);
|
||||
if (chan[i].freq>2047) chan[i].freq=2047;
|
||||
if (chan[i].freq<0) chan[i].freq=0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -471,17 +471,21 @@ void DivPlatformGenesis::tick(bool sysTick) {
|
|||
for (int i=0; i<6; i++) {
|
||||
if (i==2 && extMode) continue;
|
||||
if (chan[i].freqChanged) {
|
||||
int fNum=parent->calcFreq(chan[i].baseFreq&0x7ff,chan[i].pitch,false,4,chan[i].pitch2);
|
||||
int block=(chan[i].baseFreq&0xf800)>>11;
|
||||
if (fNum<0) fNum=0;
|
||||
if (fNum>2047) {
|
||||
while (block<7) {
|
||||
fNum>>=1;
|
||||
block++;
|
||||
if (parent->song.linearPitch==2) {
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,4,chan[i].pitch2,chipClock,CHIP_FREQBASE,11);
|
||||
} else {
|
||||
int fNum=parent->calcFreq(chan[i].baseFreq&0x7ff,chan[i].pitch,false,4,chan[i].pitch2,chipClock,CHIP_FREQBASE,11);
|
||||
int block=(chan[i].baseFreq&0xf800)>>11;
|
||||
if (fNum<0) fNum=0;
|
||||
if (fNum>2047) {
|
||||
while (block<7) {
|
||||
fNum>>=1;
|
||||
block++;
|
||||
}
|
||||
if (fNum>2047) fNum=2047;
|
||||
}
|
||||
if (fNum>2047) fNum=2047;
|
||||
chan[i].freq=(block<<11)|fNum;
|
||||
}
|
||||
chan[i].freq=(block<<11)|fNum;
|
||||
if (chan[i].freq>0x3fff) chan[i].freq=0x3fff;
|
||||
immWrite(chanOffs[i]+ADDR_FREQH,chan[i].freq>>8);
|
||||
immWrite(chanOffs[i]+ADDR_FREQ,chan[i].freq&0xff);
|
||||
|
|
@ -495,7 +499,7 @@ void DivPlatformGenesis::tick(bool sysTick) {
|
|||
off=(double)s->centerRate/8363.0;
|
||||
}
|
||||
}
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,4)+chan[i].pitch2;
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,4,chan[i].pitch2,1,1);
|
||||
dacRate=chan[i].freq*off;
|
||||
if (dacRate<1) dacRate=1;
|
||||
if (dumpWrites) addWrite(0xffff0001,dacRate);
|
||||
|
|
@ -543,7 +547,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
|
|||
if (c.chan==5 && dacMode) {
|
||||
if (skipRegisterWrites) break;
|
||||
if (ins->type==DIV_INS_AMIGA) { // Furnace mode
|
||||
dacSample=ins->amiga.initSample;
|
||||
dacSample=ins->amiga.getSample(c.value);
|
||||
if (dacSample<0 || dacSample>=parent->song.sampleLen) {
|
||||
dacSample=-1;
|
||||
if (dumpWrites) addWrite(0xffff0002,0);
|
||||
|
|
@ -702,6 +706,29 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_NOTE_PORTA: {
|
||||
if (parent->song.linearPitch==2) {
|
||||
int destFreq=NOTE_FREQUENCY(c.value2);
|
||||
bool return2=false;
|
||||
if (destFreq>chan[c.chan].baseFreq) {
|
||||
chan[c.chan].baseFreq+=c.value;
|
||||
if (chan[c.chan].baseFreq>=destFreq) {
|
||||
chan[c.chan].baseFreq=destFreq;
|
||||
return2=true;
|
||||
}
|
||||
} else {
|
||||
chan[c.chan].baseFreq-=c.value;
|
||||
if (chan[c.chan].baseFreq<=destFreq) {
|
||||
chan[c.chan].baseFreq=destFreq;
|
||||
return2=true;
|
||||
}
|
||||
}
|
||||
chan[c.chan].freqChanged=true;
|
||||
if (return2) {
|
||||
chan[c.chan].inPorta=false;
|
||||
return 2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (c.chan==5 && chan[c.chan].furnaceDac && dacMode) {
|
||||
int destFreq=parent->calcBaseFreq(1,1,c.value2,false);
|
||||
bool return2=false;
|
||||
|
|
@ -725,6 +752,8 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
|
|||
}
|
||||
break;
|
||||
}
|
||||
int boundaryBottom=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,0,false);
|
||||
int boundaryTop=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,12,false);
|
||||
int destFreq=NOTE_FNUM_BLOCK(c.value2,11);
|
||||
int newFreq;
|
||||
bool return2=false;
|
||||
|
|
@ -747,13 +776,13 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
|
|||
// check for octave boundary
|
||||
// what the heck!
|
||||
if (!chan[c.chan].portaPause) {
|
||||
if ((newFreq&0x7ff)>1288 && (newFreq&0xf800)<0x3800) {
|
||||
chan[c.chan].portaPauseFreq=(644)|((newFreq+0x800)&0xf800);
|
||||
if ((newFreq&0x7ff)>boundaryTop && (newFreq&0xf800)<0x3800) {
|
||||
chan[c.chan].portaPauseFreq=(boundaryBottom)|((newFreq+0x800)&0xf800);
|
||||
chan[c.chan].portaPause=true;
|
||||
break;
|
||||
}
|
||||
if ((newFreq&0x7ff)<644 && (newFreq&0xf800)>0) {
|
||||
chan[c.chan].portaPauseFreq=newFreq=(1287)|((newFreq-0x800)&0xf800);
|
||||
if ((newFreq&0x7ff)<boundaryBottom && (newFreq&0xf800)>0) {
|
||||
chan[c.chan].portaPauseFreq=newFreq=(boundaryTop-1)|((newFreq-0x800)&0xf800);
|
||||
chan[c.chan].portaPause=true;
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -127,6 +127,31 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_NOTE_PORTA: {
|
||||
if (parent->song.linearPitch==2) {
|
||||
int destFreq=NOTE_FREQUENCY(c.value2);
|
||||
bool return2=false;
|
||||
if (destFreq>opChan[ch].baseFreq) {
|
||||
opChan[ch].baseFreq+=c.value;
|
||||
if (opChan[ch].baseFreq>=destFreq) {
|
||||
opChan[ch].baseFreq=destFreq;
|
||||
return2=true;
|
||||
}
|
||||
} else {
|
||||
opChan[ch].baseFreq-=c.value;
|
||||
if (opChan[ch].baseFreq<=destFreq) {
|
||||
opChan[ch].baseFreq=destFreq;
|
||||
return2=true;
|
||||
}
|
||||
}
|
||||
opChan[ch].freqChanged=true;
|
||||
if (return2) {
|
||||
//opChan[ch].inPorta=false;
|
||||
return 2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
int boundaryBottom=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,0,false);
|
||||
int boundaryTop=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,12,false);
|
||||
int destFreq=NOTE_FNUM_BLOCK(c.value2,11);
|
||||
int newFreq;
|
||||
bool return2=false;
|
||||
|
|
@ -148,18 +173,18 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) {
|
|||
}
|
||||
// what the heck!
|
||||
if (!opChan[ch].portaPause) {
|
||||
if ((newFreq&0x7ff)>1288 && (newFreq&0xf800)<0x3800) {
|
||||
if ((newFreq&0x7ff)>boundaryTop && (newFreq&0xf800)<0x3800) {
|
||||
if (parent->song.fbPortaPause) {
|
||||
opChan[ch].portaPauseFreq=(644)|((newFreq+0x800)&0xf800);
|
||||
opChan[ch].portaPauseFreq=(boundaryBottom)|((newFreq+0x800)&0xf800);
|
||||
opChan[ch].portaPause=true;
|
||||
break;
|
||||
} else {
|
||||
newFreq=(newFreq>>1)|((newFreq+0x800)&0xf800);
|
||||
}
|
||||
}
|
||||
if ((newFreq&0x7ff)<644 && (newFreq&0xf800)>0) {
|
||||
if ((newFreq&0x7ff)<boundaryBottom && (newFreq&0xf800)>0) {
|
||||
if (parent->song.fbPortaPause) {
|
||||
opChan[ch].portaPauseFreq=newFreq=(1287)|((newFreq-0x800)&0xf800);
|
||||
opChan[ch].portaPauseFreq=newFreq=(boundaryTop-1)|((newFreq-0x800)&0xf800);
|
||||
opChan[ch].portaPause=true;
|
||||
break;
|
||||
} else {
|
||||
|
|
@ -435,17 +460,21 @@ void DivPlatformGenesisExt::tick(bool sysTick) {
|
|||
unsigned char writeMask=2;
|
||||
if (extMode) for (int i=0; i<4; i++) {
|
||||
if (opChan[i].freqChanged) {
|
||||
int fNum=parent->calcFreq(opChan[i].baseFreq&0x7ff,opChan[i].pitch,false,4,opChan[i].pitch2);
|
||||
int block=(opChan[i].baseFreq&0xf800)>>11;
|
||||
if (fNum<0) fNum=0;
|
||||
if (fNum>2047) {
|
||||
while (block<7) {
|
||||
fNum>>=1;
|
||||
block++;
|
||||
if (parent->song.linearPitch==2) {
|
||||
opChan[i].freq=parent->calcFreq(opChan[i].baseFreq,opChan[i].pitch,false,4,opChan[i].pitch2,chipClock,CHIP_FREQBASE,11);
|
||||
} else {
|
||||
int fNum=parent->calcFreq(opChan[i].baseFreq&0x7ff,opChan[i].pitch,false,4,opChan[i].pitch2);
|
||||
int block=(opChan[i].baseFreq&0xf800)>>11;
|
||||
if (fNum<0) fNum=0;
|
||||
if (fNum>2047) {
|
||||
while (block<7) {
|
||||
fNum>>=1;
|
||||
block++;
|
||||
}
|
||||
if (fNum>2047) fNum=2047;
|
||||
}
|
||||
if (fNum>2047) fNum=2047;
|
||||
opChan[i].freq=(block<<11)|fNum;
|
||||
}
|
||||
opChan[i].freq=(block<<11)|fNum;
|
||||
if (opChan[i].freq>0x3fff) opChan[i].freq=0x3fff;
|
||||
immWrite(opChanOffsH[i],opChan[i].freq>>8);
|
||||
immWrite(opChanOffsL[i],opChan[i].freq&0xff);
|
||||
|
|
|
|||
|
|
@ -201,7 +201,7 @@ void DivPlatformLynx::tick(bool sysTick) {
|
|||
WRITE_OTHER(i, ((chan[i].lfsr&0xf00)>>4));
|
||||
chan[i].lfsr=-1;
|
||||
}
|
||||
chan[i].fd=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2);
|
||||
chan[i].fd=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER);
|
||||
if (chan[i].std.duty.had) {
|
||||
chan[i].duty=chan[i].std.duty.val;
|
||||
WRITE_FEEDBACK(i, chan[i].duty.feedback);
|
||||
|
|
|
|||
|
|
@ -146,7 +146,7 @@ void DivPlatformMMC5::tick(bool sysTick) {
|
|||
}
|
||||
}
|
||||
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2)-1;
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER)-1;
|
||||
if (chan[i].freq>2047) chan[i].freq=2047;
|
||||
if (chan[i].freq<0) chan[i].freq=0;
|
||||
if (chan[i].keyOn) {
|
||||
|
|
@ -192,7 +192,7 @@ int DivPlatformMMC5::dispatch(DivCommand c) {
|
|||
if (c.chan==2) { // PCM
|
||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_STD);
|
||||
if (ins->type==DIV_INS_AMIGA) {
|
||||
dacSample=ins->amiga.initSample;
|
||||
dacSample=ins->amiga.getSample(c.value);
|
||||
if (dacSample<0 || dacSample>=parent->song.sampleLen) {
|
||||
dacSample=-1;
|
||||
if (dumpWrites) addWrite(0xffff0002,0);
|
||||
|
|
|
|||
|
|
@ -357,7 +357,7 @@ void DivPlatformN163::tick(bool sysTick) {
|
|||
chan[i].waveUpdated=false;
|
||||
}
|
||||
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
|
||||
chan[i].freq=parent->calcFreq((((chan[i].baseFreq*chan[i].waveLen)*(chanMax+1))/16),chan[i].pitch,false,0,chan[i].pitch2);
|
||||
chan[i].freq=parent->calcFreq((((chan[i].baseFreq*chan[i].waveLen)*(chanMax+1))/16),chan[i].pitch,false,0,chan[i].pitch2,chipClock,CHIP_FREQBASE);
|
||||
if (chan[i].freq<0) chan[i].freq=0;
|
||||
if (chan[i].freq>0x3ffff) chan[i].freq=0x3ffff;
|
||||
if (chan[i].keyOn) {
|
||||
|
|
|
|||
|
|
@ -308,7 +308,7 @@ void DivPlatformNES::tick(bool sysTick) {
|
|||
if (ntPos>252) ntPos=252;
|
||||
chan[i].freq=(parent->song.properNoiseLayout)?(15-(chan[i].baseFreq&15)):(noiseTable[ntPos]);
|
||||
} else {
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2)-1;
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER)-1;
|
||||
if (chan[i].freq>2047) chan[i].freq=2047;
|
||||
if (chan[i].freq<0) chan[i].freq=0;
|
||||
}
|
||||
|
|
@ -381,7 +381,7 @@ int DivPlatformNES::dispatch(DivCommand c) {
|
|||
if (c.chan==4) { // PCM
|
||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_STD);
|
||||
if (ins->type==DIV_INS_AMIGA) {
|
||||
dacSample=ins->amiga.initSample;
|
||||
dacSample=ins->amiga.getSample(c.value);
|
||||
if (dacSample<0 || dacSample>=parent->song.sampleLen) {
|
||||
dacSample=-1;
|
||||
if (dumpWrites && !dpcmMode) addWrite(0xffff0002,0);
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
#include "opl.h"
|
||||
#include "../engine.h"
|
||||
#include "../../ta-log.h"
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
|
|
@ -246,19 +247,53 @@ const char* DivPlatformOPL::getEffectName(unsigned char effect) {
|
|||
void DivPlatformOPL::acquire_nuked(short* bufL, short* bufR, size_t start, size_t len) {
|
||||
static short o[2];
|
||||
static int os[2];
|
||||
static ymfm::ymfm_output<2> aOut;
|
||||
|
||||
for (size_t h=start; h<start+len; h++) {
|
||||
os[0]=0; os[1]=0;
|
||||
if (!writes.empty() && --delay<0) {
|
||||
delay=1;
|
||||
QueuedWrite& w=writes.front();
|
||||
OPL3_WriteReg(&fm,w.addr,w.val);
|
||||
switch (w.addr) {
|
||||
case 8:
|
||||
if (adpcmChan>=0) {
|
||||
adpcmB->write(w.addr-7,(w.val&15)|0x80);
|
||||
OPL3_WriteReg(&fm,w.addr,w.val&0xc0);
|
||||
} else {
|
||||
OPL3_WriteReg(&fm,w.addr,w.val);
|
||||
}
|
||||
break;
|
||||
case 7: case 9: case 10: case 11: case 12: case 13: case 14: case 15: case 16: case 17: case 18: case 21: case 22: case 23:
|
||||
if (adpcmChan>=0) {
|
||||
adpcmB->write(w.addr-7,w.val);
|
||||
} else {
|
||||
OPL3_WriteReg(&fm,w.addr,w.val);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
OPL3_WriteReg(&fm,w.addr,w.val);
|
||||
break;
|
||||
}
|
||||
regPool[w.addr&511]=w.val;
|
||||
writes.pop();
|
||||
}
|
||||
|
||||
OPL3_Generate(&fm,o); os[0]+=o[0]; os[1]+=o[1];
|
||||
|
||||
if (adpcmChan>=0) {
|
||||
adpcmB->clock();
|
||||
aOut.clear();
|
||||
adpcmB->output<2>(aOut,0);
|
||||
|
||||
if (!isMuted[adpcmChan]) {
|
||||
os[0]+=aOut.data[0];
|
||||
os[1]+=aOut.data[0];
|
||||
oscBuf[adpcmChan]->data[oscBuf[adpcmChan]->needle++]+=aOut.data[0];
|
||||
} else {
|
||||
oscBuf[adpcmChan]->data[oscBuf[adpcmChan]->needle++]=0;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i=0; i<chans; i++) {
|
||||
unsigned char ch=outChanMap[i];
|
||||
if (ch==255) continue;
|
||||
|
|
@ -292,6 +327,15 @@ void DivPlatformOPL::acquire(short* bufL, short* bufR, size_t start, size_t len)
|
|||
//}
|
||||
}
|
||||
|
||||
double DivPlatformOPL::NOTE_ADPCMB(int note) {
|
||||
if (adpcmChan<0) return 0;
|
||||
if (chan[adpcmChan].sample>=0 && chan[adpcmChan].sample<parent->song.sampleLen) {
|
||||
double off=65535.0*(double)(parent->getSample(chan[adpcmChan].sample)->centerRate)/8363.0;
|
||||
return parent->calcBaseFreq((double)chipClock/144,off,note,false);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void DivPlatformOPL::tick(bool sysTick) {
|
||||
for (int i=0; i<totalChans; i++) {
|
||||
int ops=(slots[3][i]!=255 && chan[i].state.ops==4 && oplType==3)?4:2;
|
||||
|
|
@ -501,6 +545,45 @@ void DivPlatformOPL::tick(bool sysTick) {
|
|||
}
|
||||
}
|
||||
|
||||
// ADPCM
|
||||
if (adpcmChan>=0) {
|
||||
if (chan[adpcmChan].furnacePCM) {
|
||||
chan[adpcmChan].std.next();
|
||||
|
||||
if (chan[adpcmChan].std.vol.had) {
|
||||
chan[adpcmChan].outVol=(chan[adpcmChan].vol*MIN(64,chan[adpcmChan].std.vol.val))/64;
|
||||
immWrite(18,chan[adpcmChan].outVol);
|
||||
}
|
||||
|
||||
if (chan[adpcmChan].std.arp.had) {
|
||||
if (!chan[adpcmChan].inPorta) {
|
||||
if (chan[adpcmChan].std.arp.mode) {
|
||||
chan[adpcmChan].baseFreq=NOTE_ADPCMB(chan[adpcmChan].std.arp.val);
|
||||
} else {
|
||||
chan[adpcmChan].baseFreq=NOTE_ADPCMB(chan[adpcmChan].note+(signed char)chan[adpcmChan].std.arp.val);
|
||||
}
|
||||
}
|
||||
chan[adpcmChan].freqChanged=true;
|
||||
} else {
|
||||
if (chan[adpcmChan].std.arp.mode && chan[adpcmChan].std.arp.finished) {
|
||||
chan[adpcmChan].baseFreq=NOTE_ADPCMB(chan[adpcmChan].note);
|
||||
chan[adpcmChan].freqChanged=true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (chan[adpcmChan].freqChanged) {
|
||||
if (chan[adpcmChan].sample>=0 && chan[adpcmChan].sample<parent->song.sampleLen) {
|
||||
double off=65535.0*(double)(parent->getSample(chan[adpcmChan].sample)->centerRate)/8363.0;
|
||||
chan[adpcmChan].freq=parent->calcFreq(chan[adpcmChan].baseFreq,chan[adpcmChan].pitch,false,4,chan[adpcmChan].pitch2,(double)chipClock/144,off);
|
||||
} else {
|
||||
chan[adpcmChan].freq=0;
|
||||
}
|
||||
immWrite(16,chan[adpcmChan].freq&0xff);
|
||||
immWrite(17,(chan[adpcmChan].freq>>8)&0xff);
|
||||
chan[adpcmChan].freqChanged=false;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i=0; i<512; i++) {
|
||||
if (pendingWrites[i]!=oldWrites[i]) {
|
||||
immWrite(i,pendingWrites[i]&0xff);
|
||||
|
|
@ -511,7 +594,7 @@ void DivPlatformOPL::tick(bool sysTick) {
|
|||
bool updateDrums=false;
|
||||
for (int i=0; i<totalChans; i++) {
|
||||
if (chan[i].freqChanged) {
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,octave(chan[i].baseFreq),chan[i].pitch2);
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,octave(chan[i].baseFreq),chan[i].pitch2,chipClock,CHIP_FREQBASE);
|
||||
if (chan[i].freq>131071) chan[i].freq=131071;
|
||||
int freqt=toFreq(chan[i].freq)+chan[i].pitch2;
|
||||
chan[i].freqH=freqt>>8;
|
||||
|
|
@ -588,6 +671,7 @@ int DivPlatformOPL::toFreq(int freq) {
|
|||
|
||||
void DivPlatformOPL::muteChannel(int ch, bool mute) {
|
||||
isMuted[ch]=mute;
|
||||
if (ch==adpcmChan) return;
|
||||
if (oplType<3 && ch<melodicChans) {
|
||||
fm.channel[outChanMap[ch]].muted=mute;
|
||||
}
|
||||
|
|
@ -625,13 +709,78 @@ void DivPlatformOPL::muteChannel(int ch, bool mute) {
|
|||
}
|
||||
|
||||
int DivPlatformOPL::dispatch(DivCommand c) {
|
||||
if (c.chan>=totalChans) return 0;
|
||||
if (c.chan>=totalChans && c.chan!=adpcmChan) return 0;
|
||||
// ineffective in 4-op mode
|
||||
if (oplType==3 && c.chan<14 && (c.chan&1) && c.cmd!=DIV_CMD_GET_VOLMAX && c.cmd!=DIV_ALWAYS_SET_VOLUME) {
|
||||
if (oplType==3 && c.chan!=adpcmChan && c.chan<14 && (c.chan&1) && c.cmd!=DIV_CMD_GET_VOLMAX && c.cmd!=DIV_ALWAYS_SET_VOLUME) {
|
||||
if (chan[c.chan-1].fourOp) return 0;
|
||||
}
|
||||
switch (c.cmd) {
|
||||
case DIV_CMD_NOTE_ON: {
|
||||
if (c.chan==adpcmChan) { // ADPCM
|
||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM);
|
||||
if (ins->type==DIV_INS_AMIGA) {
|
||||
chan[c.chan].furnacePCM=true;
|
||||
} else {
|
||||
chan[c.chan].furnacePCM=false;
|
||||
}
|
||||
if (skipRegisterWrites) break;
|
||||
if (chan[c.chan].furnacePCM) {
|
||||
chan[c.chan].macroInit(ins);
|
||||
if (!chan[c.chan].std.vol.will) {
|
||||
chan[c.chan].outVol=chan[c.chan].vol;
|
||||
immWrite(18,chan[c.chan].outVol);
|
||||
}
|
||||
chan[c.chan].sample=ins->amiga.getSample(c.value);
|
||||
if (chan[c.chan].sample>=0 && chan[c.chan].sample<parent->song.sampleLen) {
|
||||
DivSample* s=parent->getSample(chan[c.chan].sample);
|
||||
immWrite(9,(s->offB>>2)&0xff);
|
||||
immWrite(10,(s->offB>>10)&0xff);
|
||||
int end=s->offB+s->lengthB-1;
|
||||
immWrite(11,(end>>2)&0xff);
|
||||
immWrite(12,(end>>10)&0xff);
|
||||
immWrite(8,2);
|
||||
immWrite(7,(s->loopStart>=0)?0xb0:0xa0); // start/repeat
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].note=c.value;
|
||||
chan[c.chan].baseFreq=NOTE_ADPCMB(chan[c.chan].note);
|
||||
chan[c.chan].freqChanged=true;
|
||||
}
|
||||
chan[c.chan].active=true;
|
||||
chan[c.chan].keyOn=true;
|
||||
} else {
|
||||
immWrite(7,0x01); // reset
|
||||
immWrite(9,0);
|
||||
immWrite(10,0);
|
||||
immWrite(11,0);
|
||||
immWrite(12,0);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
chan[c.chan].sample=-1;
|
||||
chan[c.chan].macroInit(NULL);
|
||||
chan[c.chan].outVol=chan[c.chan].vol;
|
||||
if ((12*sampleBank+c.value%12)>=parent->song.sampleLen) {
|
||||
immWrite(7,0x01); // reset
|
||||
immWrite(9,0);
|
||||
immWrite(10,0);
|
||||
immWrite(11,0);
|
||||
immWrite(12,0);
|
||||
break;
|
||||
}
|
||||
DivSample* s=parent->getSample(12*sampleBank+c.value%12);
|
||||
immWrite(9,(s->offB>>2)&0xff);
|
||||
immWrite(10,(s->offB>>10)&0xff);
|
||||
int end=s->offB+s->lengthB-1;
|
||||
immWrite(11,(end>>2)&0xff);
|
||||
immWrite(12,(end>>10)&0xff);
|
||||
immWrite(8,2);
|
||||
immWrite(7,(s->loopStart>=0)?0xb0:0xa0); // start/repeat
|
||||
int freq=(65536.0*(double)s->rate)/(double)rate;
|
||||
immWrite(16,freq&0xff);
|
||||
immWrite(17,(freq>>8)&0xff);
|
||||
}
|
||||
break;
|
||||
}
|
||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_OPL);
|
||||
|
||||
if (chan[c.chan].insChanged) {
|
||||
|
|
@ -714,11 +863,19 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_NOTE_OFF:
|
||||
if (c.chan==adpcmChan) {
|
||||
immWrite(7,0x01); // reset
|
||||
break;
|
||||
}
|
||||
chan[c.chan].keyOff=true;
|
||||
chan[c.chan].keyOn=false;
|
||||
chan[c.chan].active=false;
|
||||
break;
|
||||
case DIV_CMD_NOTE_OFF_ENV:
|
||||
if (c.chan==adpcmChan) {
|
||||
immWrite(7,0x01); // reset
|
||||
break;
|
||||
}
|
||||
chan[c.chan].keyOff=true;
|
||||
chan[c.chan].keyOn=false;
|
||||
chan[c.chan].active=false;
|
||||
|
|
@ -728,7 +885,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
|||
chan[c.chan].std.release();
|
||||
break;
|
||||
case DIV_CMD_VOLUME: {
|
||||
if (pretendYMU) {
|
||||
if (pretendYMU && c.chan!=adpcmChan) {
|
||||
c.value=pow(((double)c.value/127.0),0.5)*63.0;
|
||||
if (c.value<0) c.value=0;
|
||||
if (c.value>63) c.value=63;
|
||||
|
|
@ -737,6 +894,10 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
|||
if (!chan[c.chan].std.vol.has) {
|
||||
chan[c.chan].outVol=c.value;
|
||||
}
|
||||
if (c.chan==adpcmChan) { // ADPCM-B
|
||||
immWrite(18,chan[c.chan].outVol);
|
||||
break;
|
||||
}
|
||||
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
|
||||
for (int i=0; i<ops; i++) {
|
||||
unsigned char slot=slots[i][c.chan];
|
||||
|
|
@ -757,7 +918,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_GET_VOLUME: {
|
||||
if (pretendYMU) {
|
||||
if (pretendYMU && c.chan!=adpcmChan) {
|
||||
return pow(((double)chan[c.chan].vol/63.0),2.0)*127.0;
|
||||
}
|
||||
return chan[c.chan].vol;
|
||||
|
|
@ -771,6 +932,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
|||
break;
|
||||
case DIV_CMD_PANNING: {
|
||||
if (oplType!=3) break;
|
||||
if (c.chan==adpcmChan) break;
|
||||
if (c.value==0 && c.value2==0) {
|
||||
chan[c.chan].pan=3;
|
||||
} else {
|
||||
|
|
@ -791,28 +953,31 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_PITCH: {
|
||||
if (c.chan==adpcmChan) {
|
||||
if (!chan[c.chan].furnacePCM) break;
|
||||
}
|
||||
chan[c.chan].pitch=c.value;
|
||||
chan[c.chan].freqChanged=true;
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_NOTE_PORTA: {
|
||||
int destFreq=NOTE_FREQUENCY(c.value2);
|
||||
int destFreq=(c.chan==adpcmChan)?(NOTE_ADPCMB(c.value2)):(NOTE_FREQUENCY(c.value2));
|
||||
int newFreq;
|
||||
bool return2=false;
|
||||
if (destFreq>chan[c.chan].baseFreq) {
|
||||
newFreq=chan[c.chan].baseFreq+c.value*octave(chan[c.chan].baseFreq);
|
||||
newFreq=chan[c.chan].baseFreq+c.value*((parent->song.linearPitch==2)?1:octave(chan[c.chan].baseFreq));
|
||||
if (newFreq>=destFreq) {
|
||||
newFreq=destFreq;
|
||||
return2=true;
|
||||
}
|
||||
} else {
|
||||
newFreq=chan[c.chan].baseFreq-c.value*octave(chan[c.chan].baseFreq);
|
||||
newFreq=chan[c.chan].baseFreq-c.value*((parent->song.linearPitch==2)?1:octave(chan[c.chan].baseFreq));
|
||||
if (newFreq<=destFreq) {
|
||||
newFreq=destFreq;
|
||||
return2=true;
|
||||
}
|
||||
}
|
||||
if (!chan[c.chan].portaPause) {
|
||||
if (!chan[c.chan].portaPause && parent->song.linearPitch!=2) {
|
||||
if (octave(chan[c.chan].baseFreq)!=octave(newFreq)) {
|
||||
chan[c.chan].portaPause=true;
|
||||
break;
|
||||
|
|
@ -827,13 +992,22 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
|||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_SAMPLE_BANK:
|
||||
if (adpcmChan<0) break;
|
||||
sampleBank=c.value;
|
||||
if (sampleBank>(int)(parent->song.sample.size()/12)) {
|
||||
sampleBank=parent->song.sample.size()/12;
|
||||
}
|
||||
iface.sampleBank=sampleBank;
|
||||
break;
|
||||
case DIV_CMD_LEGATO: {
|
||||
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value);
|
||||
chan[c.chan].baseFreq=(c.chan==adpcmChan)?(NOTE_ADPCMB(c.value)):(NOTE_FREQUENCY(c.value));
|
||||
chan[c.chan].note=c.value;
|
||||
chan[c.chan].freqChanged=true;
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_LFO: {
|
||||
if (c.chan==adpcmChan) break;
|
||||
if (c.value&2) {
|
||||
dvb=c.value&1;
|
||||
} else {
|
||||
|
|
@ -843,6 +1017,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_FM_FB: {
|
||||
if (c.chan==adpcmChan) break;
|
||||
chan[c.chan].state.fb=c.value&7;
|
||||
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
|
||||
if (isMuted[c.chan]) {
|
||||
|
|
@ -859,6 +1034,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_FM_MULT: {
|
||||
if (c.chan==adpcmChan) break;
|
||||
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
|
||||
if (c.value>=ops) break;
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[(ops==4)?orderedOpsL[c.value]:c.value];
|
||||
|
|
@ -870,6 +1046,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_FM_TL: {
|
||||
if (c.chan==adpcmChan) break;
|
||||
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
|
||||
if (c.value>=ops) break;
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[(ops==4)?orderedOpsL[c.value]:c.value];
|
||||
|
|
@ -889,6 +1066,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_FM_AR: {
|
||||
if (c.chan==adpcmChan) break;
|
||||
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<ops; i++) {
|
||||
|
|
@ -911,6 +1089,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_FM_DR: {
|
||||
if (c.chan==adpcmChan) break;
|
||||
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<ops; i++) {
|
||||
|
|
@ -933,6 +1112,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_FM_SL: {
|
||||
if (c.chan==adpcmChan) break;
|
||||
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<ops; i++) {
|
||||
|
|
@ -955,6 +1135,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_FM_RR: {
|
||||
if (c.chan==adpcmChan) break;
|
||||
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<ops; i++) {
|
||||
|
|
@ -977,6 +1158,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_FM_AM: {
|
||||
if (c.chan==adpcmChan) break;
|
||||
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<ops; i++) {
|
||||
|
|
@ -999,6 +1181,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_FM_VIB: {
|
||||
if (c.chan==adpcmChan) break;
|
||||
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<ops; i++) {
|
||||
|
|
@ -1021,6 +1204,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_FM_SUS: {
|
||||
if (c.chan==adpcmChan) break;
|
||||
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<ops; i++) {
|
||||
|
|
@ -1043,6 +1227,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_FM_KSR: {
|
||||
if (c.chan==adpcmChan) break;
|
||||
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<ops; i++) {
|
||||
|
|
@ -1065,6 +1250,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_FM_WS: {
|
||||
if (c.chan==adpcmChan) break;
|
||||
if (oplType<2) break;
|
||||
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
|
||||
if (c.value<0) {
|
||||
|
|
@ -1088,6 +1274,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_FM_RS: {
|
||||
if (c.chan==adpcmChan) break;
|
||||
if (oplType<2) break;
|
||||
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
|
||||
if (c.value<0) {
|
||||
|
|
@ -1143,12 +1330,14 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_FM_HARD_RESET:
|
||||
if (c.chan==adpcmChan) break;
|
||||
chan[c.chan].hardReset=c.value;
|
||||
break;
|
||||
case DIV_ALWAYS_SET_VOLUME:
|
||||
return 0;
|
||||
break;
|
||||
case DIV_CMD_GET_VOLMAX:
|
||||
if (c.chan==adpcmChan) return 255;
|
||||
if (pretendYMU) return 127;
|
||||
return 63;
|
||||
break;
|
||||
|
|
@ -1237,7 +1426,7 @@ unsigned char* DivPlatformOPL::getRegisterPool() {
|
|||
}
|
||||
|
||||
int DivPlatformOPL::getRegisterPoolSize() {
|
||||
return 512;
|
||||
return (oplType<3)?256:512;
|
||||
}
|
||||
|
||||
void DivPlatformOPL::reset() {
|
||||
|
|
@ -1272,6 +1461,21 @@ void DivPlatformOPL::reset() {
|
|||
chan[i].vol=0x3f;
|
||||
chan[i].outVol=0x3f;
|
||||
}
|
||||
|
||||
if (adpcmChan>=0) {
|
||||
chan[adpcmChan]=DivPlatformOPL::Channel();
|
||||
chan[adpcmChan].std.setEngine(parent);
|
||||
chan[adpcmChan].vol=0xff;
|
||||
chan[adpcmChan].outVol=0xff;
|
||||
|
||||
adpcmB->reset();
|
||||
|
||||
// volume
|
||||
immWrite(18,0xff);
|
||||
// ADPCM limit
|
||||
immWrite(20,0xff);
|
||||
immWrite(19,0xff);
|
||||
}
|
||||
|
||||
if (oplType<3) for (int i=0; i<melodicChans; i++) {
|
||||
fm.channel[outChanMap[i]].muted=isMuted[i];
|
||||
|
|
@ -1285,6 +1489,7 @@ void DivPlatformOPL::reset() {
|
|||
lastBusy=60;
|
||||
lfoValue=8;
|
||||
drumState=0;
|
||||
sampleBank=0;
|
||||
|
||||
drumVol[0]=0;
|
||||
drumVol[1]=0;
|
||||
|
|
@ -1349,8 +1554,9 @@ void DivPlatformOPL::setYMFM(bool use) {
|
|||
|
||||
void DivPlatformOPL::setOPLType(int type, bool drums) {
|
||||
pretendYMU=false;
|
||||
adpcmChan=-1;
|
||||
switch (type) {
|
||||
case 1: case 2:
|
||||
case 1: case 2: case 8950:
|
||||
slotsNonDrums=slotsOPL2;
|
||||
slotsDrums=slotsOPL2Drums;
|
||||
slots=drums?slotsDrums:slotsNonDrums;
|
||||
|
|
@ -1360,6 +1566,9 @@ void DivPlatformOPL::setOPLType(int type, bool drums) {
|
|||
chans=9;
|
||||
melodicChans=drums?6:9;
|
||||
totalChans=drums?11:9;
|
||||
if (type==8950) {
|
||||
adpcmChan=drums?11:9;
|
||||
}
|
||||
break;
|
||||
case 3: case 759:
|
||||
slotsNonDrums=slotsOPL3;
|
||||
|
|
@ -1371,11 +1580,16 @@ void DivPlatformOPL::setOPLType(int type, bool drums) {
|
|||
chans=18;
|
||||
melodicChans=drums?15:18;
|
||||
totalChans=drums?20:18;
|
||||
if (type==759) pretendYMU=true;
|
||||
if (type==759) {
|
||||
pretendYMU=true;
|
||||
adpcmChan=16;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (type==759) {
|
||||
oplType=3;
|
||||
} else if (type==8950) {
|
||||
oplType=1;
|
||||
} else {
|
||||
oplType=type;
|
||||
}
|
||||
|
|
@ -1425,6 +1639,45 @@ void DivPlatformOPL::setFlags(unsigned int flags) {
|
|||
}
|
||||
}
|
||||
|
||||
const void* DivPlatformOPL::getSampleMem(int index) {
|
||||
return (index==0 && adpcmChan>=0) ? adpcmBMem : NULL;
|
||||
}
|
||||
|
||||
size_t DivPlatformOPL::getSampleMemCapacity(int index) {
|
||||
return (index==0 && adpcmChan>=0) ? 262144 : 0;
|
||||
}
|
||||
|
||||
size_t DivPlatformOPL::getSampleMemUsage(int index) {
|
||||
return (index==0 && adpcmChan>=0) ? adpcmBMemLen : 0;
|
||||
}
|
||||
|
||||
void DivPlatformOPL::renderSamples() {
|
||||
if (adpcmChan<0) return;
|
||||
memset(adpcmBMem,0,getSampleMemCapacity(0));
|
||||
|
||||
size_t memPos=0;
|
||||
for (int i=0; i<parent->song.sampleLen; i++) {
|
||||
DivSample* s=parent->song.sample[i];
|
||||
int paddedLen=(s->lengthB+255)&(~0xff);
|
||||
if ((memPos&0xf00000)!=((memPos+paddedLen)&0xf00000)) {
|
||||
memPos=(memPos+0xfffff)&0xf00000;
|
||||
}
|
||||
if (memPos>=getSampleMemCapacity(0)) {
|
||||
logW("out of ADPCM memory for sample %d!",i);
|
||||
break;
|
||||
}
|
||||
if (memPos+paddedLen>=getSampleMemCapacity(0)) {
|
||||
memcpy(adpcmBMem+memPos,s->dataB,getSampleMemCapacity(0)-memPos);
|
||||
logW("out of ADPCM memory for sample %d!",i);
|
||||
} else {
|
||||
memcpy(adpcmBMem+memPos,s->dataB,paddedLen);
|
||||
}
|
||||
s->offB=memPos;
|
||||
memPos+=paddedLen;
|
||||
}
|
||||
adpcmBMemLen=memPos+256;
|
||||
}
|
||||
|
||||
int DivPlatformOPL::init(DivEngine* p, int channels, int sugRate, unsigned int flags) {
|
||||
parent=p;
|
||||
dumpWrites=false;
|
||||
|
|
@ -1437,6 +1690,14 @@ int DivPlatformOPL::init(DivEngine* p, int channels, int sugRate, unsigned int f
|
|||
}
|
||||
setFlags(flags);
|
||||
|
||||
if (adpcmChan>=0) {
|
||||
adpcmBMem=new unsigned char[getSampleMemCapacity(0)];
|
||||
adpcmBMemLen=0;
|
||||
iface.adpcmBMem=adpcmBMem;
|
||||
iface.sampleBank=0;
|
||||
adpcmB=new ymfm::adpcm_b_engine(iface,2);
|
||||
}
|
||||
|
||||
reset();
|
||||
return totalChans;
|
||||
}
|
||||
|
|
@ -1445,6 +1706,10 @@ void DivPlatformOPL::quit() {
|
|||
for (int i=0; i<18; i++) {
|
||||
delete oscBuf[i];
|
||||
}
|
||||
if (adpcmChan>=0) {
|
||||
delete adpcmB;
|
||||
delete[] adpcmBMem;
|
||||
}
|
||||
}
|
||||
|
||||
DivPlatformOPL::~DivPlatformOPL() {
|
||||
|
|
|
|||
|
|
@ -23,6 +23,16 @@
|
|||
#include "../macroInt.h"
|
||||
#include <queue>
|
||||
#include "../../../extern/opl/opl3.h"
|
||||
#include "sound/ymfm/ymfm_adpcm.h"
|
||||
|
||||
class DivOPLAInterface: public ymfm::ymfm_interface {
|
||||
public:
|
||||
unsigned char* adpcmBMem;
|
||||
int sampleBank;
|
||||
uint8_t ymfm_external_read(ymfm::access_class type, uint32_t address);
|
||||
void ymfm_external_write(ymfm::access_class type, uint32_t address, uint8_t data);
|
||||
DivOPLAInterface(): adpcmBMem(NULL), sampleBank(0) {}
|
||||
};
|
||||
|
||||
class DivPlatformOPL: public DivDispatch {
|
||||
protected:
|
||||
|
|
@ -30,8 +40,8 @@ class DivPlatformOPL: public DivDispatch {
|
|||
DivInstrumentFM state;
|
||||
DivMacroInt std;
|
||||
unsigned char freqH, freqL;
|
||||
int freq, baseFreq, pitch, pitch2, note, ins;
|
||||
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, furnaceDac, inPorta, fourOp, hardReset;
|
||||
int freq, baseFreq, pitch, pitch2, note, ins, sample;
|
||||
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, furnacePCM, inPorta, fourOp, hardReset;
|
||||
int vol, outVol;
|
||||
unsigned char pan;
|
||||
void macroInit(DivInstrument* which) {
|
||||
|
|
@ -47,13 +57,14 @@ class DivPlatformOPL: public DivDispatch {
|
|||
pitch2(0),
|
||||
note(0),
|
||||
ins(-1),
|
||||
sample(-1),
|
||||
active(false),
|
||||
insChanged(true),
|
||||
freqChanged(false),
|
||||
keyOn(false),
|
||||
keyOff(false),
|
||||
portaPause(false),
|
||||
furnaceDac(false),
|
||||
furnacePCM(false),
|
||||
inPorta(false),
|
||||
fourOp(false),
|
||||
hardReset(false),
|
||||
|
|
@ -73,13 +84,18 @@ class DivPlatformOPL: public DivDispatch {
|
|||
};
|
||||
std::queue<QueuedWrite> writes;
|
||||
opl3_chip fm;
|
||||
unsigned char* adpcmBMem;
|
||||
size_t adpcmBMemLen;
|
||||
DivOPLAInterface iface;
|
||||
|
||||
ymfm::adpcm_b_engine* adpcmB;
|
||||
const unsigned char** slotsNonDrums;
|
||||
const unsigned char** slotsDrums;
|
||||
const unsigned char** slots;
|
||||
const unsigned short* chanMap;
|
||||
const unsigned char* outChanMap;
|
||||
double chipFreqBase;
|
||||
int delay, oplType, chans, melodicChans, totalChans;
|
||||
int delay, oplType, chans, melodicChans, totalChans, adpcmChan, sampleBank;
|
||||
unsigned char lastBusy;
|
||||
unsigned char drumState;
|
||||
unsigned char drumVol[5];
|
||||
|
|
@ -97,6 +113,7 @@ class DivPlatformOPL: public DivDispatch {
|
|||
|
||||
int octave(int freq);
|
||||
int toFreq(int freq);
|
||||
double NOTE_ADPCMB(int note);
|
||||
|
||||
friend void putDispatchChan(void*,int,int);
|
||||
|
||||
|
|
@ -127,6 +144,10 @@ class DivPlatformOPL: public DivDispatch {
|
|||
void poke(unsigned int addr, unsigned short val);
|
||||
void poke(std::vector<DivRegWrite>& wlist);
|
||||
const char* getEffectName(unsigned char effect);
|
||||
const void* getSampleMem(int index);
|
||||
size_t getSampleMemCapacity(int index);
|
||||
size_t getSampleMemUsage(int index);
|
||||
void renderSamples();
|
||||
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
|
||||
void quit();
|
||||
~DivPlatformOPL();
|
||||
|
|
|
|||
38
src/engine/platform/oplAInterface.cpp
Normal file
38
src/engine/platform/oplAInterface.cpp
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "sound/ymfm/ymfm.h"
|
||||
#include "opl.h"
|
||||
#include "../engine.h"
|
||||
|
||||
uint8_t DivOPLAInterface::ymfm_external_read(ymfm::access_class type, uint32_t address) {
|
||||
switch (type) {
|
||||
case ymfm::ACCESS_ADPCM_B:
|
||||
if (adpcmBMem==NULL) {
|
||||
return 0;
|
||||
}
|
||||
return adpcmBMem[address&0xffffff];
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void DivOPLAInterface::ymfm_external_write(ymfm::access_class type, uint32_t address, uint8_t data) {
|
||||
}
|
||||
|
|
@ -300,7 +300,7 @@ void DivPlatformOPLL::tick(bool sysTick) {
|
|||
|
||||
for (int i=0; i<11; i++) {
|
||||
if (chan[i].freqChanged) {
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,octave(chan[i].baseFreq),chan[i].pitch2);
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,octave(chan[i].baseFreq),chan[i].pitch2,chipClock,CHIP_FREQBASE);
|
||||
if (chan[i].freq>262143) chan[i].freq=262143;
|
||||
int freqt=toFreq(chan[i].freq)+chan[i].pitch2;
|
||||
chan[i].freqL=freqt&0xff;
|
||||
|
|
@ -578,13 +578,13 @@ int DivPlatformOPLL::dispatch(DivCommand c) {
|
|||
int newFreq;
|
||||
bool return2=false;
|
||||
if (destFreq>chan[c.chan].baseFreq) {
|
||||
newFreq=chan[c.chan].baseFreq+c.value*octave(chan[c.chan].baseFreq);
|
||||
newFreq=chan[c.chan].baseFreq+c.value*((parent->song.linearPitch==2)?1:octave(chan[c.chan].baseFreq));
|
||||
if (newFreq>=destFreq) {
|
||||
newFreq=destFreq;
|
||||
return2=true;
|
||||
}
|
||||
} else {
|
||||
newFreq=chan[c.chan].baseFreq-c.value*octave(chan[c.chan].baseFreq);
|
||||
newFreq=chan[c.chan].baseFreq-c.value*((parent->song.linearPitch==2)?1:octave(chan[c.chan].baseFreq));
|
||||
if (newFreq<=destFreq) {
|
||||
newFreq=destFreq;
|
||||
return2=true;
|
||||
|
|
|
|||
|
|
@ -227,7 +227,7 @@ void DivPlatformPCE::tick(bool sysTick) {
|
|||
}
|
||||
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
|
||||
//DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_PCE);
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2);
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER);
|
||||
if (chan[i].furnaceDac) {
|
||||
double off=1.0;
|
||||
if (chan[i].dacSample>=0 && chan[i].dacSample<parent->song.sampleLen) {
|
||||
|
|
@ -270,7 +270,7 @@ int DivPlatformPCE::dispatch(DivCommand c) {
|
|||
if (chan[c.chan].pcm) {
|
||||
if (skipRegisterWrites) break;
|
||||
if (ins->type==DIV_INS_AMIGA) {
|
||||
chan[c.chan].dacSample=ins->amiga.initSample;
|
||||
chan[c.chan].dacSample=ins->amiga.getSample(c.value);
|
||||
if (chan[c.chan].dacSample<0 || chan[c.chan].dacSample>=parent->song.sampleLen) {
|
||||
chan[c.chan].dacSample=-1;
|
||||
if (dumpWrites) addWrite(0xffff0002+(c.chan<<8),0);
|
||||
|
|
|
|||
|
|
@ -223,7 +223,7 @@ void DivPlatformPCSpeaker::tick(bool sysTick) {
|
|||
chan[i].freqChanged=true;
|
||||
}
|
||||
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2)-1;
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER)-1;
|
||||
if (chan[i].freq<0) chan[i].freq=0;
|
||||
if (chan[i].freq>65535) chan[i].freq=65535;
|
||||
if (chan[i].keyOn) {
|
||||
|
|
|
|||
|
|
@ -118,7 +118,7 @@ void DivPlatformPET::tick(bool sysTick) {
|
|||
chan.freqChanged=true;
|
||||
}
|
||||
if (chan.freqChanged || chan.keyOn || chan.keyOff) {
|
||||
chan.freq=parent->calcFreq(chan.baseFreq,chan.pitch,true,0,chan.pitch2);
|
||||
chan.freq=parent->calcFreq(chan.baseFreq,chan.pitch,true,0,chan.pitch2,chipClock,CHIP_DIVIDER);
|
||||
if (chan.freq>257) chan.freq=257;
|
||||
if (chan.freq<2) chan.freq=2;
|
||||
rWrite(8,chan.freq-2);
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@
|
|||
#include <map>
|
||||
|
||||
#define CHIP_DIVIDER (1248*2)
|
||||
#define QS_NOTE_FREQUENCY(x) parent->calcBaseFreq(440,0x1000,(x)-3,false)
|
||||
#define QS_NOTE_FREQUENCY(x) parent->calcBaseFreq(440,4096,(x)-3,false)
|
||||
|
||||
#define rWrite(a,v) {if(!skipRegisterWrites) {qsound_write_data(&chip,a,v); if(dumpWrites) addWrite(a,v); }}
|
||||
#define immWrite(a,v) {qsound_write_data(&chip,a,v); if(dumpWrites) addWrite(a,v);}
|
||||
|
|
@ -296,14 +296,8 @@ void DivPlatformQSound::tick(bool sysTick) {
|
|||
uint16_t qsound_addr = 0;
|
||||
uint16_t qsound_loop = 0;
|
||||
uint16_t qsound_end = 0;
|
||||
double off=1.0;
|
||||
if (chan[i].sample>=0 && chan[i].sample<parent->song.sampleLen) {
|
||||
DivSample* s=parent->getSample(chan[i].sample);
|
||||
if (s->centerRate<1) {
|
||||
off=1.0;
|
||||
} else {
|
||||
off=(double)s->centerRate/24038.0/16.0;
|
||||
}
|
||||
qsound_bank = 0x8000 | (s->offQSound >> 16);
|
||||
qsound_addr = s->offQSound & 0xffff;
|
||||
|
||||
|
|
@ -322,15 +316,15 @@ void DivPlatformQSound::tick(bool sysTick) {
|
|||
if (chan[i].std.arp.had) {
|
||||
if (!chan[i].inPorta) {
|
||||
if (chan[i].std.arp.mode) {
|
||||
chan[i].baseFreq=off*QS_NOTE_FREQUENCY(chan[i].std.arp.val);
|
||||
chan[i].baseFreq=QS_NOTE_FREQUENCY(chan[i].std.arp.val);
|
||||
} else {
|
||||
chan[i].baseFreq=off*QS_NOTE_FREQUENCY(chan[i].note+chan[i].std.arp.val);
|
||||
chan[i].baseFreq=QS_NOTE_FREQUENCY(chan[i].note+chan[i].std.arp.val);
|
||||
}
|
||||
}
|
||||
chan[i].freqChanged=true;
|
||||
} else {
|
||||
if (chan[i].std.arp.mode && chan[i].std.arp.finished) {
|
||||
chan[i].baseFreq=off*QS_NOTE_FREQUENCY(chan[i].note);
|
||||
chan[i].baseFreq=QS_NOTE_FREQUENCY(chan[i].note);
|
||||
chan[i].freqChanged=true;
|
||||
}
|
||||
}
|
||||
|
|
@ -354,7 +348,16 @@ void DivPlatformQSound::tick(bool sysTick) {
|
|||
}
|
||||
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
|
||||
//DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_AMIGA);
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,2,chan[i].pitch2);
|
||||
double off=1.0;
|
||||
if (chan[i].sample>=0 && chan[i].sample<parent->song.sampleLen) {
|
||||
DivSample* s=parent->getSample(chan[i].sample);
|
||||
if (s->centerRate<1) {
|
||||
off=1.0;
|
||||
} else {
|
||||
off=(double)s->centerRate/24038.0/16.0;
|
||||
}
|
||||
}
|
||||
chan[i].freq=off*parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,2,chan[i].pitch2,440.0,4096.0);
|
||||
if (chan[i].freq>0xffff) chan[i].freq=0xffff;
|
||||
if (chan[i].keyOn) {
|
||||
rWrite(q1_reg_map[Q1V_BANK][i], qsound_bank);
|
||||
|
|
@ -387,18 +390,9 @@ int DivPlatformQSound::dispatch(DivCommand c) {
|
|||
switch (c.cmd) {
|
||||
case DIV_CMD_NOTE_ON: {
|
||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA);
|
||||
chan[c.chan].sample=ins->amiga.initSample;
|
||||
double off=1.0;
|
||||
if (chan[c.chan].sample>=0 && chan[c.chan].sample<parent->song.sampleLen) {
|
||||
DivSample* s=parent->getSample(chan[c.chan].sample);
|
||||
if (s->centerRate<1) {
|
||||
off=1.0;
|
||||
} else {
|
||||
off=(double)s->centerRate/24038.0/16.0;
|
||||
}
|
||||
}
|
||||
chan[c.chan].sample=ins->amiga.getSample(c.value);
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].baseFreq=off*QS_NOTE_FREQUENCY(c.value);
|
||||
chan[c.chan].baseFreq=QS_NOTE_FREQUENCY(c.value);
|
||||
}
|
||||
if (chan[c.chan].sample<0 || chan[c.chan].sample>=parent->song.sampleLen) {
|
||||
chan[c.chan].sample=-1;
|
||||
|
|
@ -467,16 +461,7 @@ int DivPlatformQSound::dispatch(DivCommand c) {
|
|||
chan[c.chan].freqChanged=true;
|
||||
break;
|
||||
case DIV_CMD_NOTE_PORTA: {
|
||||
double off=1.0;
|
||||
if (chan[c.chan].sample>=0 && chan[c.chan].sample<parent->song.sampleLen) {
|
||||
DivSample* s=parent->getSample(chan[c.chan].sample);
|
||||
if (s->centerRate<1) {
|
||||
off=1.0;
|
||||
} else {
|
||||
off=(double)s->centerRate/24038.0/16.0;
|
||||
}
|
||||
}
|
||||
int destFreq=off*QS_NOTE_FREQUENCY(c.value2);
|
||||
int destFreq=QS_NOTE_FREQUENCY(c.value2);
|
||||
bool return2=false;
|
||||
if (destFreq>chan[c.chan].baseFreq) {
|
||||
chan[c.chan].baseFreq+=c.value;
|
||||
|
|
@ -499,16 +484,7 @@ int DivPlatformQSound::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_LEGATO: {
|
||||
double off=1.0;
|
||||
if (chan[c.chan].sample>=0 && chan[c.chan].sample<parent->song.sampleLen) {
|
||||
DivSample* s=parent->getSample(chan[c.chan].sample);
|
||||
if (s->centerRate<1) {
|
||||
off=1.0;
|
||||
} else {
|
||||
off=(double)s->centerRate/24038.0/16.0;
|
||||
}
|
||||
}
|
||||
chan[c.chan].baseFreq=off*QS_NOTE_FREQUENCY(c.value+((chan[c.chan].std.arp.will && !chan[c.chan].std.arp.mode)?(chan[c.chan].std.arp.val-12):(0)));
|
||||
chan[c.chan].baseFreq=QS_NOTE_FREQUENCY(c.value+((chan[c.chan].std.arp.will && !chan[c.chan].std.arp.mode)?(chan[c.chan].std.arp.val-12):(0)));
|
||||
chan[c.chan].freqChanged=true;
|
||||
chan[c.chan].note=c.value;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -200,7 +200,7 @@ void DivPlatformSAA1099::tick(bool sysTick) {
|
|||
rWrite(0x18+(i/3),saaEnv[i/3]);
|
||||
}
|
||||
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2);
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER);
|
||||
if (chan[i].freq>65535) chan[i].freq=65535;
|
||||
if (chan[i].freq>=32768) {
|
||||
chan[i].freqH=7;
|
||||
|
|
@ -311,13 +311,13 @@ int DivPlatformSAA1099::dispatch(DivCommand c) {
|
|||
int destFreq=NOTE_PERIODIC(c.value2);
|
||||
bool return2=false;
|
||||
if (destFreq>chan[c.chan].baseFreq) {
|
||||
chan[c.chan].baseFreq+=c.value*(8-chan[c.chan].freqH);
|
||||
chan[c.chan].baseFreq+=c.value*((parent->song.linearPitch==2)?1:(8-chan[c.chan].freqH));
|
||||
if (chan[c.chan].baseFreq>=destFreq) {
|
||||
chan[c.chan].baseFreq=destFreq;
|
||||
return2=true;
|
||||
}
|
||||
} else {
|
||||
chan[c.chan].baseFreq-=c.value*(8-chan[c.chan].freqH);
|
||||
chan[c.chan].baseFreq-=c.value*((parent->song.linearPitch==2)?1:(8-chan[c.chan].freqH));
|
||||
if (chan[c.chan].baseFreq<=destFreq) {
|
||||
chan[c.chan].baseFreq=destFreq;
|
||||
return2=true;
|
||||
|
|
|
|||
402
src/engine/platform/scc.cpp
Normal file
402
src/engine/platform/scc.cpp
Normal file
|
|
@ -0,0 +1,402 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "scc.h"
|
||||
#include "../engine.h"
|
||||
#include <math.h>
|
||||
|
||||
#define CHIP_DIVIDER 16
|
||||
|
||||
#define rWrite(a,v) {if (!skipRegisterWrites) {scc->scc_w(true,a,v); regPool[a]=v; if (dumpWrites) addWrite(a,v); }}
|
||||
|
||||
const char* regCheatSheetSCC[]={
|
||||
"Ch1_Wave", "00",
|
||||
"Ch2_Wave", "20",
|
||||
"Ch3_Wave", "40",
|
||||
"Ch4_5_Wave", "60",
|
||||
"Ch1_FreqL", "80",
|
||||
"Ch1_FreqH", "81",
|
||||
"Ch2_FreqL", "82",
|
||||
"Ch2_FreqH", "83",
|
||||
"Ch3_FreqL", "84",
|
||||
"Ch3_FreqH", "85",
|
||||
"Ch4_FreqL", "86",
|
||||
"Ch4_FreqH", "87",
|
||||
"Ch5_FreqL", "88",
|
||||
"Ch5_FreqH", "89",
|
||||
"Ch1_Vol", "8a",
|
||||
"Ch2_Vol", "8b",
|
||||
"Ch3_Vol", "8c",
|
||||
"Ch4_Vol", "8d",
|
||||
"Ch5_Vol", "8e",
|
||||
"Output", "8f",
|
||||
"Test", "e0",
|
||||
NULL
|
||||
};
|
||||
|
||||
const char* regCheatSheetSCCPlus[]={
|
||||
"Ch1_Wave", "00",
|
||||
"Ch2_Wave", "20",
|
||||
"Ch3_Wave", "40",
|
||||
"Ch4_Wave", "60",
|
||||
"Ch5_Wave", "80",
|
||||
"Ch1_FreqL", "a0",
|
||||
"Ch1_FreqH", "a1",
|
||||
"Ch2_FreqL", "a2",
|
||||
"Ch2_FreqH", "a3",
|
||||
"Ch3_FreqL", "a4",
|
||||
"Ch3_FreqH", "a5",
|
||||
"Ch4_FreqL", "a6",
|
||||
"Ch4_FreqH", "a7",
|
||||
"Ch5_FreqL", "a8",
|
||||
"Ch5_FreqH", "a9",
|
||||
"Ch1_Vol", "aa",
|
||||
"Ch2_Vol", "ab",
|
||||
"Ch3_Vol", "ac",
|
||||
"Ch4_Vol", "ad",
|
||||
"Ch5_Vol", "ae",
|
||||
"Output", "af",
|
||||
"Test", "c0",
|
||||
NULL
|
||||
};
|
||||
|
||||
const char** DivPlatformSCC::getRegisterSheet() {
|
||||
return isPlus ? regCheatSheetSCCPlus : regCheatSheetSCC;
|
||||
}
|
||||
|
||||
const char* DivPlatformSCC::getEffectName(unsigned char effect) {
|
||||
switch (effect) {
|
||||
case 0x10:
|
||||
return "10xx: Change waveform";
|
||||
break;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void DivPlatformSCC::acquire(short* bufL, short* bufR, size_t start, size_t len) {
|
||||
for (size_t h=start; h<start+len; h++) {
|
||||
for (int i=0; i<16; i++) {
|
||||
scc->tick();
|
||||
}
|
||||
short out=(short)scc->out()<<5;
|
||||
bufL[h]=bufR[h]=out;
|
||||
|
||||
for (int i=0; i<5; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=scc->chan_out(i)<<7;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformSCC::updateWave(int ch) {
|
||||
int dstCh=(!isPlus && ch>=4)?3:ch;
|
||||
if (ch==3) {
|
||||
lastUpdated34=3;
|
||||
} else if (ch==4) {
|
||||
lastUpdated34=4;
|
||||
}
|
||||
for (int i=0; i<32; i++) {
|
||||
rWrite(dstCh*32+i,(unsigned char)chan[ch].ws.output[i]-128);
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformSCC::tick(bool sysTick) {
|
||||
for (int i=0; i<5; i++) {
|
||||
chan[i].std.next();
|
||||
if (chan[i].std.vol.had) {
|
||||
chan[i].outVol=((chan[i].vol&15)*MIN(15,chan[i].std.vol.val))/15;
|
||||
rWrite(regBase+10+i,chan[i].outVol);
|
||||
}
|
||||
if (chan[i].std.arp.had) {
|
||||
if (!chan[i].inPorta) {
|
||||
if (chan[i].std.arp.mode) {
|
||||
chan[i].baseFreq=NOTE_PERIODIC(chan[i].std.arp.val);
|
||||
} else {
|
||||
chan[i].baseFreq=NOTE_PERIODIC(chan[i].note+chan[i].std.arp.val);
|
||||
}
|
||||
}
|
||||
chan[i].freqChanged=true;
|
||||
} else {
|
||||
if (chan[i].std.arp.mode && chan[i].std.arp.finished) {
|
||||
chan[i].baseFreq=NOTE_PERIODIC(chan[i].note);
|
||||
chan[i].freqChanged=true;
|
||||
}
|
||||
}
|
||||
if (chan[i].std.wave.had) {
|
||||
if (chan[i].wave!=chan[i].std.wave.val || chan[i].ws.activeChanged()) {
|
||||
chan[i].wave=chan[i].std.wave.val;
|
||||
chan[i].ws.changeWave1(chan[i].wave);
|
||||
}
|
||||
}
|
||||
if (chan[i].std.pitch.had) {
|
||||
if (chan[i].std.pitch.mode) {
|
||||
chan[i].pitch2+=chan[i].std.pitch.val;
|
||||
CLAMP_VAR(chan[i].pitch2,-2048,2048);
|
||||
} else {
|
||||
chan[i].pitch2=chan[i].std.pitch.val;
|
||||
}
|
||||
chan[i].freqChanged=true;
|
||||
}
|
||||
if (chan[i].active) {
|
||||
if (chan[i].ws.tick()) {
|
||||
updateWave(i);
|
||||
}
|
||||
}
|
||||
if (chan[i].freqChanged) {
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER)-1;
|
||||
if (chan[i].freq<0) chan[i].freq=0;
|
||||
if (chan[i].freq>4095) chan[i].freq=4095;
|
||||
rWrite(regBase+0+i*2,chan[i].freq&0xff);
|
||||
rWrite(regBase+1+i*2,chan[i].freq>>8);
|
||||
chan[i].freqChanged=false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int DivPlatformSCC::dispatch(DivCommand c) {
|
||||
switch (c.cmd) {
|
||||
case DIV_CMD_NOTE_ON: {
|
||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_SCC);
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
|
||||
chan[c.chan].freqChanged=true;
|
||||
chan[c.chan].note=c.value;
|
||||
}
|
||||
chan[c.chan].active=true;
|
||||
chan[c.chan].macroInit(ins);
|
||||
if (!isMuted[c.chan]) {
|
||||
rWrite(regBase+15,regPool[regBase+15]|(1<<c.chan));
|
||||
}
|
||||
if (chan[c.chan].wave<0) {
|
||||
chan[c.chan].wave=0;
|
||||
chan[c.chan].ws.changeWave1(chan[c.chan].wave);
|
||||
}
|
||||
chan[c.chan].ws.init(ins,32,255,chan[c.chan].insChanged);
|
||||
chan[c.chan].insChanged=false;
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_NOTE_OFF:
|
||||
chan[c.chan].active=false;
|
||||
chan[c.chan].macroInit(NULL);
|
||||
rWrite(regBase+15,regPool[regBase+15]&~(1<<c.chan));
|
||||
break;
|
||||
case DIV_CMD_NOTE_OFF_ENV:
|
||||
case DIV_CMD_ENV_RELEASE:
|
||||
chan[c.chan].std.release();
|
||||
break;
|
||||
case DIV_CMD_INSTRUMENT:
|
||||
if (chan[c.chan].ins!=c.value || c.value2==1) {
|
||||
chan[c.chan].ins=c.value;
|
||||
}
|
||||
break;
|
||||
case DIV_CMD_VOLUME:
|
||||
if (chan[c.chan].vol!=c.value) {
|
||||
chan[c.chan].vol=c.value;
|
||||
if (!chan[c.chan].std.vol.has) {
|
||||
chan[c.chan].outVol=c.value;
|
||||
rWrite(regBase+10+c.chan,c.value);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DIV_CMD_GET_VOLUME:
|
||||
if (chan[c.chan].std.vol.has) {
|
||||
return chan[c.chan].vol;
|
||||
}
|
||||
return chan[c.chan].outVol;
|
||||
break;
|
||||
case DIV_CMD_PITCH:
|
||||
chan[c.chan].pitch=c.value;
|
||||
chan[c.chan].freqChanged=true;
|
||||
break;
|
||||
case DIV_CMD_WAVE:
|
||||
chan[c.chan].wave=c.value;
|
||||
chan[c.chan].ws.changeWave1(chan[c.chan].wave);
|
||||
break;
|
||||
case DIV_CMD_NOTE_PORTA: {
|
||||
int destFreq=NOTE_PERIODIC(c.value2);
|
||||
bool return2=false;
|
||||
if (destFreq>chan[c.chan].baseFreq) {
|
||||
chan[c.chan].baseFreq+=c.value;
|
||||
if (chan[c.chan].baseFreq>=destFreq) {
|
||||
chan[c.chan].baseFreq=destFreq;
|
||||
return2=true;
|
||||
}
|
||||
} else {
|
||||
chan[c.chan].baseFreq-=c.value;
|
||||
if (chan[c.chan].baseFreq<=destFreq) {
|
||||
chan[c.chan].baseFreq=destFreq;
|
||||
return2=true;
|
||||
}
|
||||
}
|
||||
chan[c.chan].freqChanged=true;
|
||||
if (return2) {
|
||||
chan[c.chan].inPorta=false;
|
||||
return 2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_LEGATO:
|
||||
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value+((chan[c.chan].std.arp.will && !chan[c.chan].std.arp.mode)?(chan[c.chan].std.arp.val):(0)));
|
||||
chan[c.chan].freqChanged=true;
|
||||
chan[c.chan].note=c.value;
|
||||
break;
|
||||
case DIV_CMD_PRE_PORTA:
|
||||
if (chan[c.chan].active && c.value2) {
|
||||
if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_SCC));
|
||||
}
|
||||
chan[c.chan].inPorta=c.value;
|
||||
break;
|
||||
case DIV_CMD_GET_VOLMAX:
|
||||
return 15;
|
||||
break;
|
||||
case DIV_ALWAYS_SET_VOLUME:
|
||||
return 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void DivPlatformSCC::muteChannel(int ch, bool mute) {
|
||||
isMuted[ch]=mute;
|
||||
if (mute) {
|
||||
rWrite(regBase+15,regPool[regBase+15]&~(1<<ch));
|
||||
} else if (chan[ch].active) {
|
||||
rWrite(regBase+15,regPool[regBase+15]|(1<<ch));
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformSCC::forceIns() {
|
||||
for (int i=0; i<5; i++) {
|
||||
chan[i].insChanged=true;
|
||||
chan[i].freqChanged=true;
|
||||
if (isPlus || i<3) {
|
||||
updateWave(i);
|
||||
}
|
||||
}
|
||||
if (!isPlus) {
|
||||
if (lastUpdated34>=3) {
|
||||
updateWave(lastUpdated34);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void* DivPlatformSCC::getChanState(int ch) {
|
||||
return &chan[ch];
|
||||
}
|
||||
|
||||
DivDispatchOscBuffer* DivPlatformSCC::getOscBuffer(int ch) {
|
||||
return oscBuf[ch];
|
||||
}
|
||||
|
||||
unsigned char* DivPlatformSCC::getRegisterPool() {
|
||||
return (unsigned char*)regPool;
|
||||
}
|
||||
|
||||
int DivPlatformSCC::getRegisterPoolSize() {
|
||||
return 225;
|
||||
}
|
||||
|
||||
void DivPlatformSCC::reset() {
|
||||
memset(regPool,0,225);
|
||||
scc->reset();
|
||||
for (int i=0; i<5; i++) {
|
||||
chan[i]=DivPlatformSCC::Channel();
|
||||
chan[i].std.setEngine(parent);
|
||||
chan[i].ws.setEngine(parent);
|
||||
chan[i].ws.init(NULL,32,255,false);
|
||||
chan[i].vol=15;
|
||||
chan[i].outVol=15;
|
||||
rWrite(regBase+10+i,15);
|
||||
}
|
||||
if (dumpWrites) {
|
||||
addWrite(0xffffffff,0);
|
||||
}
|
||||
lastUpdated34=0;
|
||||
}
|
||||
|
||||
bool DivPlatformSCC::isStereo() {
|
||||
return false;
|
||||
}
|
||||
|
||||
void DivPlatformSCC::notifyWaveChange(int wave) {
|
||||
for (int i=0; i<5; i++) {
|
||||
if (chan[i].wave==wave) {
|
||||
chan[i].ws.changeWave1(chan[i].wave);
|
||||
if (chan[i].active) {
|
||||
updateWave(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformSCC::notifyInsDeletion(void* ins) {
|
||||
for (int i=0; i<5; i++) {
|
||||
chan[i].std.notifyInsDeletion((DivInstrument*)ins);
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformSCC::poke(unsigned int addr, unsigned short val) {
|
||||
rWrite(addr,val);
|
||||
}
|
||||
|
||||
void DivPlatformSCC::poke(std::vector<DivRegWrite>& wlist) {
|
||||
for (DivRegWrite& i: wlist) rWrite(i.addr,i.val);
|
||||
}
|
||||
|
||||
void DivPlatformSCC::setChipModel(bool isplus) {
|
||||
isPlus=isplus;
|
||||
}
|
||||
|
||||
int DivPlatformSCC::init(DivEngine* p, int channels, int sugRate, unsigned int flags) {
|
||||
parent=p;
|
||||
dumpWrites=false;
|
||||
skipRegisterWrites=false;
|
||||
writeOscBuf=0;
|
||||
for (int i=0; i<5; i++) {
|
||||
isMuted[i]=false;
|
||||
oscBuf[i]=new DivDispatchOscBuffer;
|
||||
}
|
||||
chipClock=COLOR_NTSC/2.0;
|
||||
rate=chipClock/8;
|
||||
for (int i=0; i<5; i++) {
|
||||
oscBuf[i]->rate=rate;
|
||||
}
|
||||
if (isPlus) {
|
||||
scc=new k052539_scc_core;
|
||||
regBase=0xa0;
|
||||
} else {
|
||||
scc=new k051649_scc_core;
|
||||
regBase=0x80;
|
||||
}
|
||||
reset();
|
||||
return 5;
|
||||
}
|
||||
|
||||
void DivPlatformSCC::quit() {
|
||||
for (int i=0; i<5; i++) {
|
||||
delete oscBuf[i];
|
||||
}
|
||||
if (scc!=NULL) {
|
||||
delete scc;
|
||||
}
|
||||
}
|
||||
|
||||
DivPlatformSCC::~DivPlatformSCC() {
|
||||
}
|
||||
92
src/engine/platform/scc.h
Normal file
92
src/engine/platform/scc.h
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef _SCC_H
|
||||
#define _SCC_H
|
||||
|
||||
#include "../dispatch.h"
|
||||
#include <queue>
|
||||
#include "../macroInt.h"
|
||||
#include "../waveSynth.h"
|
||||
#include "sound/scc/scc.hpp"
|
||||
|
||||
class DivPlatformSCC: public DivDispatch {
|
||||
struct Channel {
|
||||
int freq, baseFreq, pitch, pitch2, note, ins;
|
||||
bool active, insChanged, freqChanged, inPorta;
|
||||
signed char vol, outVol, wave;
|
||||
signed char waveROM[32] = {0}; // 4 bit PROM per channel on bubble system
|
||||
DivMacroInt std;
|
||||
DivWaveSynth ws;
|
||||
void macroInit(DivInstrument* which) {
|
||||
std.init(which);
|
||||
pitch2=0;
|
||||
}
|
||||
Channel():
|
||||
freq(0),
|
||||
baseFreq(0),
|
||||
pitch(0),
|
||||
pitch2(0),
|
||||
note(0),
|
||||
ins(-1),
|
||||
active(false),
|
||||
insChanged(true),
|
||||
freqChanged(false),
|
||||
inPorta(false),
|
||||
vol(15),
|
||||
outVol(15),
|
||||
wave(-1) {}
|
||||
};
|
||||
Channel chan[5];
|
||||
DivDispatchOscBuffer* oscBuf[5];
|
||||
bool isMuted[5];
|
||||
unsigned char writeOscBuf;
|
||||
int lastUpdated34;
|
||||
|
||||
scc_core* scc;
|
||||
bool isPlus;
|
||||
unsigned char regBase;
|
||||
unsigned char regPool[225];
|
||||
void updateWave(int ch);
|
||||
friend void putDispatchChan(void*,int,int);
|
||||
public:
|
||||
void acquire(short* bufL, short* bufR, size_t start, size_t len);
|
||||
int dispatch(DivCommand c);
|
||||
void* getChanState(int chan);
|
||||
DivDispatchOscBuffer* getOscBuffer(int chan);
|
||||
unsigned char* getRegisterPool();
|
||||
int getRegisterPoolSize();
|
||||
void reset();
|
||||
void forceIns();
|
||||
void tick(bool sysTick=true);
|
||||
void muteChannel(int ch, bool mute);
|
||||
bool isStereo();
|
||||
void notifyWaveChange(int wave);
|
||||
void notifyInsDeletion(void* ins);
|
||||
void poke(unsigned int addr, unsigned short val);
|
||||
void poke(std::vector<DivRegWrite>& wlist);
|
||||
const char** getRegisterSheet();
|
||||
const char* getEffectName(unsigned char effect);
|
||||
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
|
||||
void setChipModel(bool isPlus);
|
||||
void quit();
|
||||
~DivPlatformSCC();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -162,7 +162,7 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) {
|
|||
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA);
|
||||
if (skipRegisterWrites) break;
|
||||
if (ins->type==DIV_INS_AMIGA) {
|
||||
chan[c.chan].pcm.sample=ins->amiga.initSample;
|
||||
chan[c.chan].pcm.sample=ins->amiga.getSample(c.value);
|
||||
if (chan[c.chan].pcm.sample<0 || chan[c.chan].pcm.sample>=parent->song.sampleLen) {
|
||||
chan[c.chan].pcm.sample=-1;
|
||||
if (dumpWrites) {
|
||||
|
|
|
|||
|
|
@ -121,7 +121,7 @@ void DivPlatformSMS::tick(bool sysTick) {
|
|||
}
|
||||
for (int i=0; i<3; i++) {
|
||||
if (chan[i].freqChanged) {
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2);
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2,chipClock,64);
|
||||
if (chan[i].freq>1023) chan[i].freq=1023;
|
||||
if (chan[i].freq<8) chan[i].freq=1;
|
||||
//if (chan[i].actualNote>0x5d) chan[i].freq=0x01;
|
||||
|
|
@ -136,7 +136,7 @@ void DivPlatformSMS::tick(bool sysTick) {
|
|||
}
|
||||
}
|
||||
if (chan[3].freqChanged || updateSNMode) {
|
||||
chan[3].freq=parent->calcFreq(chan[3].baseFreq,chan[3].pitch,true,0,chan[3].pitch2);
|
||||
chan[3].freq=parent->calcFreq(chan[3].baseFreq,chan[3].pitch,true,0,chan[3].pitch2,chipClock,isRealSN?60:64);
|
||||
if (chan[3].freq>1023) chan[3].freq=1023;
|
||||
if (chan[3].actualNote>0x5d) chan[3].freq=0x01;
|
||||
if (snNoiseMode&2) { // take period from channel 3
|
||||
|
|
|
|||
543
src/engine/platform/sound/rss.h
Normal file
543
src/engine/platform/sound/rss.h
Normal file
|
|
@ -0,0 +1,543 @@
|
|||
/*
|
||||
This data is derived from the chip's output - internal ROM can't be read.
|
||||
It was verified, using real YM2608, that this ADPCM stream produces 100% correct output signal.
|
||||
*/
|
||||
|
||||
const unsigned char YM2608_ADPCM_ROM[0x2000] = {
|
||||
|
||||
/* Source: 01BD.ROM */
|
||||
/* Length: 448 / 0x000001C0 */
|
||||
|
||||
0x88,0x08,0x08,0x08,0x00,0x88,0x16,0x76,0x99,0xB8,0x22,0x3A,0x84,0x3C,0xB1,0x54,
|
||||
0x10,0xA9,0x98,0x32,0x80,0x33,0x9A,0xA7,0x4A,0xB4,0x58,0xBC,0x15,0x29,0x8A,0x97,
|
||||
0x9B,0x44,0xAC,0x80,0x12,0xDE,0x13,0x1B,0xC0,0x58,0xC8,0x11,0x0A,0xA2,0x1A,0xA0,
|
||||
0x00,0x98,0x0B,0x93,0x9E,0x92,0x0A,0x88,0xBE,0x14,0x1B,0x98,0x08,0xA1,0x4A,0xC1,
|
||||
0x30,0xD9,0x33,0x98,0x10,0x89,0x17,0x1A,0x82,0x29,0x37,0x0C,0x83,0x50,0x9A,0x24,
|
||||
0x1A,0x83,0x10,0x23,0x19,0xB3,0x72,0x8A,0x16,0x10,0x0A,0x93,0x70,0x99,0x23,0x99,
|
||||
0x02,0x20,0x91,0x18,0x02,0x41,0xAB,0x24,0x18,0x81,0x99,0x4A,0xE8,0x28,0x9A,0x99,
|
||||
0xA1,0x2F,0xA8,0x9D,0x90,0x08,0xCC,0xA3,0x1D,0xCA,0x82,0x0B,0xD8,0x08,0xB9,0x09,
|
||||
0xBC,0xB8,0x00,0xBE,0x90,0x1B,0xCA,0x00,0x9B,0x8A,0xA8,0x91,0x0F,0xB3,0x3D,0xB8,
|
||||
0x31,0x0B,0xA5,0x0A,0x11,0xA1,0x48,0x92,0x10,0x50,0x91,0x30,0x23,0x09,0x37,0x39,
|
||||
0xA2,0x72,0x89,0x92,0x30,0x83,0x1C,0x96,0x28,0xB9,0x24,0x8C,0xA1,0x31,0xAD,0xA9,
|
||||
0x13,0x9C,0xBA,0xA8,0x0B,0xBF,0xB8,0x9B,0xCA,0x88,0xDB,0xB8,0x19,0xFC,0x92,0x0A,
|
||||
0xBA,0x89,0xAB,0xB8,0xAB,0xD8,0x08,0xAD,0xBA,0x33,0x9D,0xAA,0x83,0x3A,0xC0,0x40,
|
||||
0xB9,0x15,0x39,0xA2,0x52,0x89,0x02,0x63,0x88,0x13,0x23,0x03,0x52,0x02,0x54,0x00,
|
||||
0x11,0x23,0x23,0x35,0x20,0x01,0x44,0x41,0x80,0x24,0x40,0xA9,0x45,0x19,0x81,0x12,
|
||||
0x81,0x02,0x11,0x21,0x19,0x02,0x61,0x8A,0x13,0x3A,0x10,0x12,0x23,0x8B,0x37,0x18,
|
||||
0x91,0x24,0x10,0x81,0x34,0x20,0x05,0x32,0x82,0x53,0x20,0x14,0x33,0x31,0x34,0x52,
|
||||
0x00,0x43,0x32,0x13,0x52,0x22,0x13,0x52,0x11,0x43,0x11,0x32,0x32,0x32,0x22,0x02,
|
||||
0x13,0x12,0x89,0x22,0x19,0x81,0x81,0x08,0xA8,0x08,0x8B,0x90,0x1B,0xBA,0x8A,0x9B,
|
||||
0xB9,0x89,0xCA,0xB9,0xAB,0xCA,0x9B,0xCA,0xB9,0xAB,0xDA,0x99,0xAC,0xBB,0x9B,0xAC,
|
||||
0xAA,0xBA,0xAC,0xAB,0x9A,0xAA,0xAA,0xBA,0xB8,0xA9,0xBA,0x99,0xA9,0x9A,0xA0,0x8A,
|
||||
0xA9,0x08,0x8A,0xA9,0x00,0x99,0x89,0x88,0x98,0x08,0x99,0x00,0x89,0x80,0x08,0x98,
|
||||
0x00,0x88,0x88,0x80,0x90,0x80,0x90,0x80,0x81,0x99,0x08,0x88,0x99,0x09,0x00,0x1A,
|
||||
0xA8,0x10,0x9A,0x88,0x08,0x0A,0x8A,0x89,0x99,0xA8,0x98,0xA9,0x99,0x99,0xA9,0x99,
|
||||
0xAA,0x8A,0xAA,0x9B,0x8A,0x9A,0xA9,0x9A,0xBA,0x99,0x9A,0xAA,0x99,0x89,0xA9,0x99,
|
||||
0x98,0x9A,0x98,0x88,0x09,0x89,0x09,0x08,0x08,0x09,0x18,0x18,0x00,0x12,0x00,0x11,
|
||||
0x11,0x11,0x12,0x12,0x21,0x21,0x22,0x22,0x22,0x22,0x22,0x22,0x32,0x31,0x32,0x31,
|
||||
0x32,0x32,0x21,0x31,0x21,0x32,0x21,0x12,0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
|
||||
|
||||
/* Source: 02SD.ROM */
|
||||
/* Length: 640 / 0x00000280 */
|
||||
|
||||
0x0A,0xDC,0x14,0x0B,0xBA,0xBC,0x01,0x0F,0xF5,0x2F,0x87,0x19,0xC9,0x24,0x1B,0xA1,
|
||||
0x31,0x99,0x90,0x32,0x32,0xFE,0x83,0x48,0xA8,0xA9,0x23,0x19,0xBC,0x91,0x02,0x41,
|
||||
0xDE,0x81,0x28,0xA8,0x0A,0xB1,0x72,0xDA,0x23,0xBC,0x04,0x19,0xB8,0x21,0x8A,0x03,
|
||||
0x29,0xBA,0x14,0x21,0x0B,0xC0,0x43,0x08,0x91,0x50,0x93,0x0F,0x86,0x1A,0x91,0x18,
|
||||
0x21,0xCB,0x27,0x0A,0xA1,0x42,0x8C,0xA9,0x21,0x10,0x08,0xAB,0x94,0x2A,0xDA,0x02,
|
||||
0x8B,0x91,0x09,0x98,0xAE,0x80,0xA9,0x02,0x0A,0xE9,0x21,0xBB,0x15,0x20,0xBE,0x92,
|
||||
0x42,0x09,0xA9,0x11,0x34,0x08,0x12,0x0A,0x27,0x29,0xA1,0x52,0x12,0x8E,0x92,0x28,
|
||||
0x92,0x2B,0xD1,0x23,0xBF,0x81,0x10,0x99,0xA8,0x0A,0xC4,0x3B,0xB9,0xB0,0x00,0x62,
|
||||
0xCF,0x92,0x29,0x92,0x2B,0xB1,0x1C,0xB2,0x72,0xAA,0x88,0x11,0x18,0x80,0x13,0x9E,
|
||||
0x03,0x18,0xB0,0x60,0xA1,0x28,0x88,0x08,0x04,0x10,0x8F,0x96,0x19,0x90,0x01,0x09,
|
||||
0xC8,0x50,0x91,0x8A,0x01,0xAB,0x03,0x50,0xBA,0x9D,0x93,0x68,0xBA,0x80,0x22,0xCB,
|
||||
0x41,0xBC,0x92,0x60,0xB9,0x1A,0x95,0x4A,0xC8,0x20,0x88,0x33,0xAC,0x92,0x38,0x83,
|
||||
0x09,0x80,0x16,0x09,0x29,0xD0,0x54,0x8C,0xA2,0x28,0x91,0x89,0x93,0x60,0xCD,0x85,
|
||||
0x1B,0xA1,0x49,0x90,0x8A,0x80,0x34,0x0C,0xC9,0x14,0x19,0x98,0xA0,0x40,0xA9,0x21,
|
||||
0xD9,0x34,0x0A,0xA9,0x10,0x23,0xCB,0x25,0xAA,0x25,0x9B,0x13,0xCD,0x16,0x09,0xA0,
|
||||
0x80,0x01,0x19,0x90,0x88,0x21,0xAC,0x33,0x8B,0xD8,0x27,0x3B,0xB8,0x81,0x31,0x80,
|
||||
0xAF,0x97,0x0A,0x82,0x0A,0xA0,0x21,0x89,0x8A,0xA2,0x32,0x8D,0xBB,0x87,0x19,0x21,
|
||||
0xC9,0xBC,0x45,0x09,0x90,0x09,0xA1,0x24,0x1A,0xD0,0x10,0x08,0x11,0xA9,0x21,0xE8,
|
||||
0x60,0xA9,0x14,0x0C,0xD1,0x32,0xAB,0x04,0x0C,0x81,0x90,0x29,0x83,0x9B,0x01,0x8F,
|
||||
0x97,0x0B,0x82,0x18,0x88,0xBA,0x06,0x39,0xC8,0x23,0xBC,0x04,0x09,0x92,0x08,0x1A,
|
||||
0xBB,0x74,0x8C,0x81,0x18,0x81,0x9D,0x83,0x41,0xCD,0x81,0x40,0x9A,0x90,0x10,0x12,
|
||||
0x9C,0xA1,0x68,0xD8,0x33,0x9C,0x91,0x01,0x12,0xBE,0x02,0x09,0x12,0x99,0x9A,0x36,
|
||||
0x0A,0xB0,0x30,0x88,0xA3,0x2D,0x12,0xBC,0x03,0x3A,0x11,0xBD,0x08,0xC8,0x62,0x80,
|
||||
0x8B,0xD8,0x23,0x38,0xF9,0x12,0x08,0x99,0x91,0x21,0x99,0x85,0x2F,0xB2,0x30,0x90,
|
||||
0x88,0xD9,0x53,0xAC,0x82,0x19,0x91,0x20,0xCC,0x96,0x29,0xC9,0x24,0x89,0x80,0x99,
|
||||
0x12,0x08,0x18,0x88,0x99,0x23,0xAB,0x73,0xCB,0x33,0x9F,0x04,0x2B,0xB1,0x08,0x03,
|
||||
0x1B,0xC9,0x21,0x32,0xFA,0x33,0xDB,0x02,0x33,0xAE,0xB9,0x54,0x8B,0xA1,0x20,0x89,
|
||||
0x90,0x11,0x88,0x09,0x98,0x23,0xBE,0x37,0x8D,0x81,0x20,0xAA,0x34,0xBB,0x13,0x18,
|
||||
0xB9,0x40,0xB1,0x18,0x83,0x8E,0xB2,0x72,0xBC,0x82,0x30,0xA9,0x9A,0x24,0x8B,0x27,
|
||||
0x0E,0x91,0x20,0x90,0x08,0xB0,0x32,0xB9,0x21,0xB0,0xAC,0x45,0x9A,0xA1,0x50,0xA9,
|
||||
0x80,0x0A,0x26,0x9B,0x11,0xBB,0x23,0x71,0xCB,0x12,0x10,0xB8,0x40,0xA9,0xA5,0x39,
|
||||
0xC0,0x30,0xB2,0x20,0xAA,0xBA,0x76,0x1C,0xC1,0x48,0x98,0x80,0x18,0x81,0xAA,0x23,
|
||||
0x9C,0xA2,0x32,0xAC,0x9A,0x43,0x9C,0x12,0xAD,0x82,0x72,0xBC,0x00,0x82,0x39,0xD1,
|
||||
0x3A,0xB8,0x35,0x9B,0x10,0x40,0xF9,0x22,0x0A,0xC0,0x51,0xB9,0x82,0x18,0x98,0xA3,
|
||||
0x79,0xD0,0x20,0x88,0x09,0x01,0x99,0x82,0x11,0x38,0xFC,0x33,0x09,0xC8,0x40,0xA9,
|
||||
0x11,0x29,0xAA,0x94,0x3A,0xC2,0x4A,0xC0,0x89,0x52,0xBC,0x11,0x08,0x09,0xB8,0x71,
|
||||
0xA9,0x08,0xA8,0x62,0x8D,0x92,0x10,0x00,0x9E,0x94,0x38,0xBA,0x13,0x88,0x90,0x4A,
|
||||
0xE2,0x30,0xBA,0x02,0x00,0x19,0xD9,0x62,0xBB,0x04,0x0B,0xA3,0x68,0xB9,0x21,0x88,
|
||||
0x9D,0x04,0x10,0x8C,0xC8,0x62,0x99,0xAA,0x24,0x1A,0x80,0x9A,0x14,0x9B,0x26,0x8C,
|
||||
0x92,0x30,0xB9,0x09,0xA3,0x71,0xBB,0x10,0x19,0x82,0x39,0xDB,0x02,0x44,0x9F,0x10,
|
||||
|
||||
/* Source: 04TOP.ROM */
|
||||
/* Length: 5952 / 0x00001740 */
|
||||
|
||||
0x07,0xFF,0x7C,0x3C,0x31,0xC6,0xC4,0xBB,0x7F,0x7F,0x7B,0x82,0x8A,0x4D,0x5F,0x7C,
|
||||
0x3E,0x44,0xD2,0xB3,0xA0,0x19,0x1B,0x6C,0x81,0x28,0xC4,0xA1,0x1C,0x4B,0x18,0x00,
|
||||
0x2A,0xA2,0x0A,0x7C,0x2A,0x00,0x01,0x89,0x98,0x48,0x8A,0x3C,0x28,0x2A,0x5B,0x3E,
|
||||
0x3A,0x1A,0x3B,0x3D,0x4B,0x3B,0x4A,0x08,0x2A,0x1A,0x2C,0x4A,0x3B,0x82,0x99,0x3C,
|
||||
0x5D,0x29,0x2B,0x39,0x0B,0x23,0xAB,0x1A,0x4C,0x79,0xA3,0x01,0xC1,0x2A,0x0A,0x38,
|
||||
0xA7,0xB9,0x12,0x1F,0x29,0x08,0x82,0xA1,0x08,0xA9,0x42,0xAA,0x95,0xB3,0x90,0x81,
|
||||
0x09,0xD4,0x1A,0x80,0x1B,0x07,0xB8,0x12,0x8E,0x49,0x81,0x92,0xD3,0x90,0xA1,0x2A,
|
||||
0x02,0xE1,0xA3,0x99,0x02,0xB3,0x94,0xB3,0xB0,0xF4,0x98,0x93,0x90,0x13,0xE1,0x81,
|
||||
0x99,0x38,0x91,0xA6,0xD3,0x99,0x94,0xC1,0x83,0xB1,0x92,0x98,0x49,0xC4,0xB2,0xA4,
|
||||
0xA3,0xD0,0x1A,0x30,0xBA,0x59,0x02,0xD4,0xA0,0xA4,0xA2,0x8A,0x01,0x00,0xB7,0xA8,
|
||||
0x18,0x2A,0x2B,0x1E,0x23,0xC8,0x1A,0x00,0x39,0xA0,0x18,0x92,0x4F,0x2D,0x5A,0x10,
|
||||
0x89,0x81,0x2A,0x8B,0x6A,0x02,0x09,0xB3,0x8D,0x48,0x1B,0x80,0x19,0x34,0xF8,0x29,
|
||||
0x0A,0x7B,0x2A,0x28,0x81,0x0C,0x02,0x1E,0x29,0x09,0x12,0xC2,0x94,0xE1,0x18,0x98,
|
||||
0x02,0xC4,0x89,0x91,0x1A,0x20,0xA9,0x02,0x1B,0x48,0x8E,0x20,0x88,0x2D,0x08,0x59,
|
||||
0x1B,0x02,0xA3,0xB1,0x8A,0x1E,0x58,0x80,0xC2,0xB6,0x88,0x91,0x88,0x11,0xA1,0xA3,
|
||||
0xE2,0x01,0xB0,0x19,0x11,0x09,0xF4,0x88,0x09,0x88,0x19,0x89,0x12,0xF1,0x2A,0x28,
|
||||
0x8C,0x25,0x99,0xA4,0x98,0x39,0xA1,0x00,0xD0,0x58,0xAA,0x59,0x01,0x0C,0x00,0x2B,
|
||||
0x00,0x08,0x89,0x6B,0x69,0x90,0x01,0x90,0x98,0x12,0xB3,0xF3,0xA0,0x89,0x02,0x3B,
|
||||
0x0C,0x50,0xA9,0x4E,0x6B,0x19,0x28,0x09,0xA2,0x08,0x2F,0x20,0x88,0x92,0x8A,0x11,
|
||||
0xC4,0x93,0xF1,0x18,0x88,0x11,0xF2,0x80,0x92,0xA8,0x02,0xA8,0xB7,0xB3,0xA3,0xA0,
|
||||
0x88,0x1A,0x40,0xE2,0x91,0x19,0x88,0x18,0x91,0x83,0xC1,0xB5,0x92,0xA9,0xC6,0x90,
|
||||
0x01,0xC2,0x81,0x98,0x03,0xF0,0x00,0x2C,0x2A,0x92,0x2C,0x83,0x1F,0x3A,0x29,0x00,
|
||||
0xB8,0x70,0xAB,0x69,0x18,0x89,0x10,0x0D,0x12,0x0B,0x88,0x4A,0x3A,0x9B,0x70,0xA8,
|
||||
0x28,0x2F,0x2A,0x3A,0x1B,0x85,0x88,0x8B,0x6A,0x29,0x00,0x91,0x91,0x1B,0x7C,0x29,
|
||||
0x01,0x88,0x90,0x19,0x2B,0x2B,0x00,0x39,0xA8,0x5E,0x21,0x89,0x91,0x09,0x3A,0x6F,
|
||||
0x2A,0x18,0x18,0x8B,0x50,0x89,0x2B,0x19,0x49,0x88,0x29,0xF5,0x89,0x08,0x09,0x12,
|
||||
0xAA,0x15,0xB0,0x82,0xAC,0x38,0x00,0x3F,0x81,0x10,0xB0,0x49,0xA2,0x81,0x3A,0xC8,
|
||||
0x87,0x90,0xC4,0xA3,0x99,0x19,0x83,0xE1,0x84,0xE2,0xA2,0x90,0x80,0x93,0xB5,0xC4,
|
||||
0xB3,0xA1,0x0A,0x18,0x92,0xC4,0xA0,0x93,0x0C,0x3A,0x18,0x01,0x1E,0x20,0xB1,0x82,
|
||||
0x8C,0x03,0xB5,0x2E,0x82,0x19,0xB2,0x1B,0x1B,0x6B,0x4C,0x19,0x12,0x8B,0x5A,0x11,
|
||||
0x0C,0x3A,0x2C,0x18,0x3D,0x08,0x2A,0x5C,0x18,0x00,0x88,0x3D,0x29,0x80,0x2A,0x09,
|
||||
0x00,0x7A,0x0A,0x10,0x0B,0x69,0x98,0x10,0x81,0x3F,0x00,0x18,0x19,0x91,0xB7,0x9A,
|
||||
0x28,0x8A,0x48,0x92,0xF3,0xA2,0x88,0x98,0x87,0xA1,0x88,0x80,0x81,0x95,0xD1,0xA3,
|
||||
0x1B,0x1C,0x39,0x10,0xA1,0x2A,0x0B,0x7A,0x4B,0x80,0x13,0xC1,0xD1,0x2B,0x2A,0x85,
|
||||
0xB2,0xA2,0x93,0xB2,0xD3,0x80,0xD1,0x18,0x08,0x08,0xB7,0x98,0x81,0x3F,0x01,0x88,
|
||||
0x01,0xE2,0x00,0x9A,0x59,0x08,0x10,0xC3,0x99,0x84,0xA9,0xA5,0x91,0x91,0x91,0x80,
|
||||
0xB5,0x94,0xC0,0x01,0x98,0x09,0x84,0xB0,0x80,0x7A,0x08,0x18,0x90,0xA8,0x6A,0x1C,
|
||||
0x39,0x2A,0xB7,0x98,0x19,0x10,0x2A,0xA1,0x10,0xBD,0x39,0x18,0x2D,0x39,0x3F,0x10,
|
||||
0x3F,0x01,0x09,0x19,0x0A,0x38,0x8C,0x40,0xB3,0xB4,0x93,0xAD,0x20,0x2B,0xD4,0x81,
|
||||
0xC3,0xB0,0x39,0xA0,0x23,0xD8,0x04,0xB1,0x9B,0xA7,0x1A,0x92,0x08,0xA5,0x88,0x81,
|
||||
0xE2,0x01,0xB8,0x01,0x81,0xC1,0xC7,0x90,0x92,0x80,0xA1,0x97,0xA0,0xA2,0x82,0xB8,
|
||||
0x18,0x00,0x9C,0x78,0x98,0x83,0x0B,0x0B,0x32,0x7D,0x19,0x10,0xA1,0x19,0x09,0x0A,
|
||||
0x78,0xA8,0x10,0x1B,0x29,0x29,0x1A,0x14,0x2F,0x88,0x4A,0x1B,0x10,0x10,0xAB,0x79,
|
||||
0x0D,0x49,0x18,0xA0,0x02,0x1F,0x19,0x3A,0x2B,0x11,0x8A,0x88,0x79,0x8A,0x20,0x49,
|
||||
0x9B,0x58,0x0B,0x28,0x18,0xA9,0x3A,0x7D,0x00,0x29,0x88,0x82,0x3D,0x1A,0x38,0xBA,
|
||||
0x15,0x09,0xAA,0x51,0x8B,0x83,0x3C,0x8A,0x58,0x1B,0xB5,0x01,0xBB,0x50,0x19,0x99,
|
||||
0x24,0xCA,0x21,0x1B,0xA2,0x87,0xA8,0xB1,0x68,0xA1,0xA6,0xA2,0xA8,0x29,0x8B,0x24,
|
||||
0xB4,0xE2,0x92,0x8A,0x00,0x19,0x93,0xB5,0xB4,0xB1,0x81,0xB1,0x03,0x9A,0x82,0xA7,
|
||||
0x90,0xD6,0xA0,0x80,0x1B,0x29,0x01,0xA4,0xE1,0x18,0x0A,0x2A,0x29,0x92,0xC7,0xA8,
|
||||
0x81,0x19,0x89,0x30,0x10,0xE0,0x30,0xB8,0x10,0x0C,0x1A,0x79,0x1B,0xA7,0x80,0xA0,
|
||||
0x00,0x0B,0x28,0x18,0xB1,0x85,0x1E,0x00,0x20,0xA9,0x18,0x18,0x1C,0x13,0xBC,0x15,
|
||||
0x99,0x2E,0x12,0x00,0xE1,0x00,0x0B,0x3B,0x21,0x90,0x06,0xC9,0x2A,0x49,0x0A,0x18,
|
||||
0x20,0xD1,0x3C,0x08,0x00,0x83,0xC9,0x41,0x8E,0x18,0x08,0x02,0xA0,0x09,0xA4,0x7B,
|
||||
0x90,0x19,0x2A,0x10,0x2A,0xA8,0x71,0xBA,0x10,0x4A,0x0E,0x22,0xB2,0xB2,0x1B,0x8C,
|
||||
0x78,0x1A,0xB5,0x93,0xA9,0x1B,0x49,0x19,0x29,0xA3,0xC6,0x88,0xAA,0x32,0x0D,0x1B,
|
||||
0x22,0x08,0xC2,0x18,0xB9,0x79,0x3F,0x01,0x10,0xA9,0x84,0x1C,0x09,0x21,0xB0,0xA7,
|
||||
0x0A,0x99,0x50,0x0C,0x81,0x28,0x8B,0x48,0x2E,0x00,0x08,0x99,0x38,0x5B,0x88,0x14,
|
||||
0xA9,0x08,0x11,0xAA,0x72,0xC1,0xB3,0x09,0x8A,0x05,0x91,0xF2,0x81,0xA1,0x09,0x02,
|
||||
0xF2,0x92,0x99,0x1A,0x49,0x80,0xC5,0x90,0x90,0x18,0x09,0x12,0xA1,0xF2,0x81,0x98,
|
||||
0xC6,0x91,0xA0,0x11,0xA0,0x94,0xB4,0xF2,0x81,0x8B,0x03,0x80,0xD2,0x93,0xA8,0x88,
|
||||
0x69,0xA0,0x03,0xB8,0x88,0x32,0xBC,0x97,0x80,0xB1,0x3B,0x1A,0xA6,0x00,0xD1,0x01,
|
||||
0x0B,0x3B,0x30,0x9B,0x31,0x3E,0x92,0x19,0x8A,0xD3,0x5C,0x1B,0x41,0xA0,0x93,0xA2,
|
||||
0xAF,0x39,0x4C,0x01,0x92,0xA8,0x81,0x3C,0x0D,0x78,0x98,0x00,0x19,0x0A,0x20,0x2D,
|
||||
0x29,0x3C,0x1B,0x48,0x88,0x99,0x7A,0x2D,0x29,0x2A,0x82,0x80,0xA8,0x49,0x3E,0x19,
|
||||
0x11,0x98,0x82,0x9A,0x3B,0x28,0x2F,0x20,0x4C,0x90,0x29,0x19,0x9A,0x7A,0x29,0x28,
|
||||
0x98,0x88,0x33,0xCD,0x11,0x3A,0xC1,0xA4,0xA0,0xC4,0x82,0xC8,0x50,0x98,0xB2,0x21,
|
||||
0xC0,0xB6,0x98,0x82,0x80,0x9C,0x23,0x00,0xF8,0x30,0xA8,0x1A,0x68,0xA8,0x86,0x9A,
|
||||
0x01,0x2A,0x0A,0x97,0x91,0xC1,0x18,0x89,0x02,0x83,0xE0,0x01,0x8B,0x29,0x30,0xE2,
|
||||
0x91,0x0B,0x18,0x3B,0x1C,0x11,0x28,0xAC,0x78,0x80,0x93,0x91,0xA9,0x49,0x8B,0x87,
|
||||
0x90,0x99,0x3D,0x5A,0x81,0x08,0xA1,0x11,0x2F,0x1A,0x21,0x9B,0x15,0xA2,0xB0,0x11,
|
||||
0xC0,0x91,0x5B,0x98,0x24,0xA2,0xF2,0x92,0x8B,0x6A,0x18,0x81,0xB5,0xB1,0x88,0x4C,
|
||||
0x00,0x00,0xA4,0xC1,0x2B,0x1A,0x59,0x0A,0x02,0x80,0x1E,0x02,0x08,0xB3,0x80,0x9A,
|
||||
0x23,0xB8,0xF2,0x84,0xAB,0x01,0x48,0x90,0xA7,0x90,0x0A,0x29,0x09,0x95,0x99,0xA0,
|
||||
0x59,0x2B,0x00,0x97,0xB0,0x29,0x89,0x2A,0x03,0xD0,0xB7,0x1B,0x81,0x00,0xA6,0xB1,
|
||||
0x90,0x09,0x48,0xC0,0x11,0x00,0x8A,0x00,0x5B,0x83,0x9A,0x18,0x2F,0x3C,0x18,0x11,
|
||||
0xA9,0x04,0x1A,0x4F,0x01,0x98,0x81,0x09,0x09,0x4A,0x18,0xB4,0xA2,0x0B,0x59,0x90,
|
||||
0x3B,0x49,0xBC,0x40,0x6A,0x88,0x3A,0x08,0x3E,0x3A,0x80,0x93,0xB0,0xE1,0x5A,0x00,
|
||||
0xA4,0xB3,0xE3,0x90,0x0D,0x38,0x09,0x82,0xC4,0xA1,0xB1,0x4C,0x18,0x10,0x91,0xB2,
|
||||
0x13,0xEA,0x34,0x99,0x88,0xA6,0x89,0x92,0x91,0xC1,0x20,0xB2,0xC2,0x86,0xD2,0xB3,
|
||||
0x80,0xB2,0x08,0x09,0x87,0x91,0xC0,0x11,0x89,0x90,0x28,0xB9,0x79,0x19,0xA4,0x82,
|
||||
0xD0,0x03,0x0C,0xA3,0xA5,0xB2,0xB2,0x1B,0x29,0x13,0xF1,0xB4,0x81,0x9D,0x38,0x00,
|
||||
0xC4,0xA1,0x89,0x59,0x1A,0x81,0xA4,0xA9,0x1C,0x6A,0x19,0x02,0xB1,0x1A,0x4A,0x0B,
|
||||
0x78,0x89,0x81,0x1C,0x2A,0x29,0x4A,0xA3,0x3E,0x1C,0x49,0x1A,0x08,0x21,0xAE,0x28,
|
||||
0x4B,0x19,0x20,0x8C,0x10,0x3A,0xAB,0x26,0x8B,0x18,0x59,0x99,0x13,0xA2,0xAB,0x79,
|
||||
0x2F,0x18,0x10,0xB2,0x80,0x1B,0x4D,0x5A,0x80,0x82,0x98,0x81,0x80,0x09,0xA5,0x90,
|
||||
0x91,0x03,0xC2,0xE2,0x81,0xA8,0x82,0x09,0xC6,0xA3,0xB1,0x08,0x5B,0x08,0x05,0xD1,
|
||||
0xA2,0x89,0x2A,0x28,0x91,0xA6,0x88,0xB0,0x49,0x80,0x09,0x08,0x88,0x07,0xB8,0x05,
|
||||
0x99,0x81,0x88,0x18,0xE2,0x00,0xC3,0x18,0x0D,0x10,0x30,0xD0,0x93,0x8A,0x09,0x10,
|
||||
0x2F,0x11,0x90,0xA1,0x20,0x9B,0xB1,0x73,0xC8,0x94,0x98,0x3B,0x01,0x0C,0x30,0x19,
|
||||
0xF8,0x12,0x90,0xBA,0x78,0x0A,0x11,0x98,0xA0,0x79,0x8A,0x30,0x2B,0xC2,0x11,0x0D,
|
||||
0x09,0x7A,0x00,0x82,0xB9,0x01,0x7A,0x89,0x21,0x09,0xA1,0x0A,0x7C,0x10,0x88,0xB5,
|
||||
0x88,0x0A,0x2B,0x69,0x1A,0x10,0xA0,0x5B,0x19,0x1A,0x10,0x19,0x1A,0x6C,0x20,0x90,
|
||||
0xA5,0x98,0x1B,0x0A,0x69,0x82,0xD1,0x18,0x09,0x19,0x2A,0x93,0xD4,0x9A,0x01,0x49,
|
||||
0xA2,0xA2,0x82,0xD8,0x22,0xAA,0x97,0xA9,0x2D,0x38,0x2A,0xB6,0x80,0x90,0x0A,0x3C,
|
||||
0x82,0x94,0xB8,0x21,0x0E,0x2A,0x22,0xB8,0x00,0x4F,0x2B,0x3A,0x81,0xA1,0x29,0x2C,
|
||||
0x6A,0x13,0xD1,0xA2,0x98,0x28,0x0C,0x01,0xD5,0x08,0xA9,0x31,0xB3,0xB0,0xA7,0xB0,
|
||||
0x29,0x1B,0x87,0xA2,0xA1,0xB2,0x4A,0x89,0x11,0xC3,0xF3,0x98,0x08,0x03,0xA0,0xA3,
|
||||
0xC5,0x90,0xB3,0xB5,0xB4,0xB8,0x02,0x91,0x91,0xD3,0xA4,0xC1,0x1B,0x82,0x28,0xA4,
|
||||
0xD1,0x94,0x8A,0x28,0x08,0x03,0xE0,0x80,0xD4,0x90,0x91,0xA1,0x3B,0x3D,0x02,0xE4,
|
||||
0xA1,0x92,0x89,0x1A,0x4B,0x95,0xB3,0x90,0x99,0x6A,0x0A,0x30,0xA1,0x93,0xA6,0xA9,
|
||||
0x85,0x8B,0x82,0x10,0xB1,0xA3,0x94,0xF8,0x38,0x9A,0x30,0x1A,0x8B,0xA7,0x89,0x01,
|
||||
0x5B,0x19,0x18,0x11,0xF0,0x18,0x1C,0x39,0x19,0x0C,0x12,0x1C,0x2A,0x7B,0x3A,0x88,
|
||||
0x2B,0x18,0x2B,0x5C,0x20,0x92,0x8D,0x38,0x8A,0x3A,0x5B,0x2E,0x3A,0x2B,0x10,0x12,
|
||||
0xBB,0x6A,0x4D,0x18,0x10,0xB1,0x81,0x2A,0x8B,0x79,0x80,0x01,0x0A,0x09,0x5B,0x2D,
|
||||
0x84,0x8A,0x08,0x02,0xA2,0x91,0x82,0xE8,0x50,0x9B,0x85,0xA3,0xB0,0xA3,0x1B,0x02,
|
||||
0x18,0xF3,0xA2,0x88,0xAB,0x53,0xD1,0xB4,0xA3,0x09,0x09,0x18,0xD4,0x08,0xB0,0x09,
|
||||
0x58,0xD1,0x82,0x89,0x81,0x1A,0x18,0x05,0xB9,0xC3,0x30,0xC0,0x95,0x80,0xC3,0x89,
|
||||
0x89,0x13,0x88,0xF2,0x93,0x0E,0x18,0x01,0x92,0xA5,0xB8,0x2A,0x39,0xAA,0x33,0x9A,
|
||||
0xB1,0x11,0xF5,0xA1,0xA1,0x0A,0x50,0xB8,0x03,0xC4,0xA0,0x4E,0x29,0x10,0x88,0xC2,
|
||||
0x1A,0x39,0x1D,0x28,0x98,0x94,0x0E,0x10,0x2A,0x3C,0x02,0x2D,0x1B,0x4B,0x3B,0x49,
|
||||
0x19,0xA9,0x48,0x2F,0x29,0x10,0x89,0x02,0x0C,0x10,0x09,0xB9,0x70,0x1B,0x8A,0x50,
|
||||
0xA8,0x2B,0x49,0x89,0x69,0x88,0x95,0x89,0x90,0x92,0x4C,0x19,0x82,0xC1,0x01,0x80,
|
||||
0xA0,0x2B,0x7A,0x81,0x10,0xC2,0xB7,0x98,0x88,0x19,0x2C,0x03,0xB1,0xA4,0xA1,0x0C,
|
||||
0x3B,0x78,0x88,0x85,0xB1,0xA0,0x1B,0x3A,0x4A,0x08,0x94,0x81,0xF1,0x80,0x00,0x0C,
|
||||
0x59,0x09,0x18,0x90,0xA6,0x92,0x8C,0x1A,0x79,0x92,0xA8,0x00,0x81,0x2E,0x2A,0x13,
|
||||
0xA2,0xB0,0xA5,0x88,0x88,0x89,0x11,0x19,0xA0,0xF3,0x82,0xB0,0x83,0x5F,0x2A,0x01,
|
||||
0xA1,0x94,0xB0,0x09,0x78,0x98,0xA3,0xA6,0xA0,0x91,0x80,0x93,0x98,0xC1,0x12,0x18,
|
||||
0xC9,0x17,0xA0,0xA0,0x1A,0x21,0x80,0x99,0xD4,0x30,0x9D,0x00,0x10,0x2F,0x08,0x1C,
|
||||
0x21,0x08,0xB4,0xC3,0x2B,0xA9,0x52,0xD2,0xA3,0xD1,0x09,0x10,0x8B,0x24,0x92,0xD1,
|
||||
0x80,0x19,0xA0,0x2C,0x12,0x49,0xAA,0xB6,0x95,0xB8,0x08,0x3A,0x2B,0x01,0xF3,0xB3,
|
||||
0x0B,0x09,0x79,0x18,0xA2,0xA4,0xA0,0x18,0x0C,0x20,0x08,0xA9,0x16,0x0C,0x00,0x1B,
|
||||
0x08,0x2B,0x7B,0x01,0x01,0xB9,0x59,0x19,0x8B,0x45,0xA8,0x80,0x0C,0x1A,0x41,0x1E,
|
||||
0x00,0x28,0xA8,0x5A,0x00,0xC1,0x49,0x99,0x21,0x1D,0x08,0x85,0x99,0x95,0x89,0x90,
|
||||
0x11,0x90,0xD1,0x28,0xB2,0xA7,0x99,0x81,0x02,0xAC,0x13,0x81,0xB2,0xA6,0xA9,0x28,
|
||||
0x1C,0xB1,0x33,0xD1,0xC1,0x58,0xA8,0x14,0xB0,0xB7,0x91,0xA0,0x82,0x89,0xC2,0x28,
|
||||
0xA1,0xB2,0x49,0xD2,0x94,0xC8,0x12,0x80,0x99,0x85,0x08,0xD3,0x09,0xA2,0xB3,0x1E,
|
||||
0x08,0x21,0xB9,0x23,0xB4,0xAB,0x41,0xAC,0x87,0x09,0xA2,0xC5,0x0B,0x2A,0x5A,0x91,
|
||||
0x20,0x9A,0x89,0x78,0x9B,0x31,0x89,0x80,0x29,0x0A,0xB7,0x3C,0x98,0x48,0x1D,0x00,
|
||||
0x01,0xB0,0x20,0x2F,0x29,0x4A,0x89,0x94,0x1C,0x88,0x28,0x2B,0x10,0x88,0x9A,0x71,
|
||||
0x9A,0x08,0x4A,0x2F,0x18,0x2B,0x18,0x02,0xA8,0x4B,0x7A,0x99,0x48,0x80,0xA8,0x20,
|
||||
0x1D,0x40,0xA8,0x10,0x08,0xA8,0xC5,0x88,0xC2,0x18,0x88,0x2A,0x12,0xF3,0x82,0xD8,
|
||||
0x20,0x0A,0x09,0xA6,0x98,0x04,0xB9,0x11,0x18,0xC3,0xE1,0x29,0xA1,0x11,0xC1,0x03,
|
||||
0xE2,0x9A,0x33,0xA9,0xB5,0x98,0x92,0xA1,0x02,0xF8,0x21,0xA8,0x10,0x02,0xC1,0xB7,
|
||||
0x1B,0x90,0x5B,0x3C,0x83,0x93,0xE0,0x19,0x1A,0x11,0x11,0xF1,0x92,0x89,0x19,0x2C,
|
||||
0x2C,0x41,0x99,0x92,0x90,0x3F,0x18,0x4B,0x00,0x08,0xD2,0x01,0xB2,0xAA,0x78,0x09,
|
||||
0x01,0x91,0xA2,0x98,0x2F,0x3A,0x2C,0x01,0x00,0x93,0xE0,0x28,0x2C,0x2B,0x01,0x12,
|
||||
0xE1,0x80,0xB3,0x3D,0x3A,0x0A,0x50,0x98,0xC2,0xA0,0x11,0xAA,0x30,0x87,0x90,0xC2,
|
||||
0x29,0x88,0x38,0xC8,0xB5,0x90,0xBA,0x70,0x1A,0x02,0x94,0xD0,0x80,0x1A,0x82,0xA6,
|
||||
0xB0,0x91,0x18,0xB3,0x00,0x13,0xF1,0xA2,0xC1,0x82,0xB0,0x00,0x15,0x0B,0xD3,0x02,
|
||||
0xA8,0x91,0x2B,0x1F,0x49,0x88,0xA6,0x80,0x88,0x08,0x1B,0xA5,0x80,0xB9,0x06,0x0B,
|
||||
0x90,0x21,0x9D,0x48,0x18,0xA0,0x15,0xC9,0x82,0x2B,0x1A,0x42,0x9A,0xC4,0x39,0xBC,
|
||||
0x69,0x00,0xA0,0x29,0x8C,0x39,0x59,0x08,0x09,0x49,0xA9,0x6B,0x81,0x00,0x98,0xB0,
|
||||
0x68,0x3D,0x81,0x88,0x18,0x19,0x1D,0x12,0x80,0xB2,0x3A,0x3F,0x85,0x92,0xD0,0x00,
|
||||
0x0A,0x19,0x12,0xF1,0x02,0x9B,0x19,0x40,0xB9,0x11,0x02,0xF2,0x1A,0x08,0x94,0x0A,
|
||||
0xC2,0x83,0x0B,0xB4,0xA4,0xC0,0x32,0xD8,0x86,0x98,0x90,0x95,0x89,0xA3,0x83,0xC2,
|
||||
0x92,0xE1,0x92,0x82,0xD9,0x03,0x08,0xA9,0x85,0x92,0xA2,0x80,0xE0,0x30,0x8B,0xB3,
|
||||
0x87,0x89,0x90,0x83,0xA0,0x08,0x92,0x93,0x3E,0xAB,0x43,0x89,0xE3,0x80,0x83,0x2F,
|
||||
0x00,0xA3,0x80,0xC9,0x22,0x3F,0x08,0x81,0x0B,0x33,0x9A,0xA3,0x7B,0x0C,0x29,0x4A,
|
||||
0x1B,0x21,0xAA,0x70,0x1B,0x0D,0x48,0x1A,0x81,0x88,0xB1,0x39,0x3F,0x08,0x58,0xA0,
|
||||
0x81,0x1A,0x1A,0x2B,0x6D,0x11,0x0A,0x91,0x01,0x1A,0x98,0x5A,0x0C,0x03,0xB1,0x84,
|
||||
0xA3,0xAD,0x58,0x2A,0xA1,0x84,0xB1,0xA0,0x5C,0x2B,0x13,0xA8,0x95,0x83,0xE8,0x10,
|
||||
0x81,0xB0,0x00,0xC2,0x96,0xA0,0x91,0x00,0x2C,0x90,0x30,0xF2,0x80,0xA8,0x39,0x21,
|
||||
0xC1,0x03,0xAC,0x39,0x7C,0x29,0x91,0x1A,0x00,0x19,0x2C,0x3A,0x93,0xB0,0x29,0x8F,
|
||||
0x28,0x02,0x93,0xF3,0xA9,0x01,0x03,0xE0,0x08,0x09,0x1D,0x58,0xA1,0x83,0xA9,0x6B,
|
||||
0x2A,0x3C,0x21,0x89,0xC2,0x2C,0x4B,0x8A,0x50,0x81,0x98,0xA8,0x32,0x0C,0x8E,0x24,
|
||||
0x0B,0x1A,0x81,0x92,0xA1,0x4F,0x18,0x3A,0x0A,0xB4,0x18,0x2E,0x39,0x82,0x19,0xD3,
|
||||
0xD0,0x28,0x1B,0x11,0x98,0x07,0xAA,0x28,0x00,0x88,0xB4,0x89,0x1B,0x1F,0x22,0x00,
|
||||
0xB3,0xC9,0x33,0xAB,0x2B,0xB5,0x48,0x98,0x98,0xA7,0x10,0xD2,0xC1,0x23,0xCA,0x93,
|
||||
0xC6,0x80,0xA1,0x88,0x02,0x89,0xE2,0x09,0x38,0xBA,0x40,0x89,0x21,0xD8,0x49,0x10,
|
||||
0x8D,0x02,0x90,0xC3,0x9A,0x24,0x89,0x08,0x84,0xA5,0x9C,0x10,0x11,0x9C,0x88,0x30,
|
||||
0x3C,0xA1,0x94,0x58,0x8C,0x0B,0x69,0x29,0x9A,0x81,0x12,0x2B,0x8B,0x79,0x94,0xB0,
|
||||
0xC1,0x84,0xC2,0x99,0x25,0x99,0x11,0xA2,0x93,0xE4,0x99,0x80,0x0A,0x00,0x10,0xB7,
|
||||
0xB0,0x31,0xBA,0x3C,0x21,0xB3,0xF1,0x18,0xA0,0x2A,0x20,0xA3,0x06,0xE8,0x28,0xA1,
|
||||
0xB4,0x08,0x0B,0x11,0x4B,0xB7,0x90,0xA5,0x98,0x3D,0x19,0x02,0xA1,0xC4,0xB2,0x19,
|
||||
0x28,0xC0,0xA5,0x92,0xB1,0xA3,0x0A,0x0A,0x08,0x2B,0x70,0xC4,0xB3,0x00,0xBC,0x4B,
|
||||
0x39,0x12,0xE3,0xA0,0x00,0x3F,0x18,0x29,0x94,0xD1,0x19,0x09,0x00,0xA1,0x83,0x99,
|
||||
0x9B,0x35,0x80,0xC4,0xB1,0x6A,0x1A,0x1C,0x29,0x38,0x0E,0x19,0x5A,0x1A,0x82,0x8A,
|
||||
0x59,0x2A,0x2E,0x20,0x88,0xA8,0x3A,0x38,0x3D,0x00,0xB3,0x29,0xAD,0x49,0x10,0x0C,
|
||||
0x01,0x01,0xA3,0x8F,0x85,0x09,0x1B,0x88,0x10,0xA3,0xD2,0x90,0x3C,0x5C,0x39,0x03,
|
||||
0xD1,0xA0,0x00,0x2A,0x0B,0x04,0xA7,0x90,0xA0,0x11,0x90,0x99,0x83,0xB4,0xB1,0xF1,
|
||||
0x84,0x88,0x90,0x18,0x18,0xD3,0xD2,0xB3,0xA0,0x1A,0x21,0xA7,0xB2,0xB3,0x92,0x9A,
|
||||
0x22,0xB9,0x28,0x38,0xBD,0x87,0x2A,0xB1,0x13,0x0D,0x0A,0x38,0xC9,0x24,0xC0,0x19,
|
||||
0x23,0x0F,0x01,0x88,0xC0,0x2A,0x82,0x18,0x28,0xF0,0x18,0x2A,0x29,0x4B,0x35,0xB8,
|
||||
0xA3,0x9D,0x18,0x1B,0x40,0x00,0x9A,0x5C,0x3A,0x09,0x2F,0x38,0x8A,0x3B,0x3B,0x11,
|
||||
0x5C,0x19,0x2B,0x4A,0x08,0x0A,0x3D,0x20,0x4F,0x3A,0x19,0x2A,0x18,0x4D,0x1B,0x3A,
|
||||
0x11,0x0D,0x3A,0x3C,0x4B,0x93,0x81,0xAA,0x6B,0x4A,0x18,0x00,0xC3,0xC3,0x9A,0x59,
|
||||
0x2A,0x1B,0xA7,0xA1,0x81,0x88,0x88,0x58,0xB2,0xB1,0x2B,0x83,0xD4,0x81,0x08,0x0F,
|
||||
0x00,0x20,0xC2,0xE2,0x80,0x08,0x1C,0x29,0x04,0xB1,0xA2,0x01,0x1C,0x91,0x00,0x0C,
|
||||
0x49,0xB0,0x43,0xF2,0x99,0x39,0x3F,0x00,0x81,0x94,0xC1,0x09,0x1A,0x69,0x90,0x80,
|
||||
0x94,0xAA,0x20,0x2A,0x91,0xB1,0x39,0x7A,0x38,0xD1,0x10,0x8A,0x8C,0x5A,0x01,0xB5,
|
||||
0x98,0x80,0x2A,0x0B,0x32,0x92,0xF1,0x81,0x9A,0x23,0x8A,0xA3,0xB7,0x09,0x03,0x08,
|
||||
0xD0,0x94,0x9A,0x09,0x01,0x93,0xB7,0xC2,0x8C,0x3A,0x83,0x99,0x05,0xA0,0x0B,0x29,
|
||||
0x93,0xE5,0x80,0x89,0x38,0x90,0x8A,0xD7,0xA1,0x19,0x1B,0x48,0x98,0x92,0xC3,0xA1,
|
||||
0x09,0x3F,0x02,0x0C,0x22,0xC3,0xB2,0xA1,0x01,0x9F,0x4A,0x01,0xA3,0xD3,0xB0,0x28,
|
||||
0x3F,0x29,0x20,0xA2,0xC2,0xB1,0x08,0x5A,0x98,0x13,0xD2,0xC1,0x01,0xB2,0x80,0x3D,
|
||||
0x03,0xC1,0x89,0x96,0x90,0x90,0x3A,0x1A,0x9A,0x32,0xB6,0xA2,0x8E,0x4A,0x28,0x8A,
|
||||
0x84,0xA2,0x8A,0x2D,0x49,0x09,0x88,0x18,0x30,0x9D,0x2C,0x23,0xB1,0x0C,0x92,0x2D,
|
||||
0x39,0x82,0xC4,0x2E,0x10,0x1A,0x10,0xB9,0x48,0x19,0x39,0xBA,0x34,0xDA,0x2D,0x48,
|
||||
0x1A,0xA6,0x98,0x83,0x9A,0x1D,0x38,0x04,0xD0,0x18,0x90,0x2C,0x11,0x93,0xD3,0x9A,
|
||||
0x11,0x08,0x82,0xF1,0x01,0xA0,0x2A,0x93,0xD3,0xB4,0xB8,0x82,0x2F,0x11,0xA3,0xB3,
|
||||
0xA8,0x3B,0x09,0x23,0x96,0xC8,0x3B,0x3F,0x93,0x82,0xA1,0x90,0x3F,0x28,0x81,0xD1,
|
||||
0x93,0x08,0x2D,0x18,0x91,0xB3,0xB5,0x98,0x2A,0x2B,0x84,0xB1,0x5B,0x8A,0x31,0x18,
|
||||
0x80,0x8B,0x7E,0x39,0x2B,0x02,0xC1,0x8B,0x6C,0x49,0x09,0x10,0xA1,0x08,0x01,0x0C,
|
||||
0x20,0xA1,0x09,0x4F,0x18,0x00,0x01,0xA0,0x5C,0x1B,0x5B,0x10,0x92,0x90,0x2B,0x5A,
|
||||
0x3D,0x18,0x91,0x19,0x98,0x2D,0x39,0x89,0x2D,0x3A,0x48,0x2C,0x11,0xB5,0x9A,0x19,
|
||||
0x5B,0x28,0x90,0x95,0x98,0x89,0x2B,0x40,0x08,0x90,0xF3,0x0A,0x08,0xA6,0x80,0x91,
|
||||
0xB2,0xA0,0x02,0xF2,0xA1,0xB7,0x89,0x81,0x82,0x91,0xB1,0x21,0xAB,0x32,0xE9,0x04,
|
||||
0xA2,0x8D,0x12,0x91,0xA3,0xA3,0xD2,0x8B,0x39,0xD1,0x84,0xE2,0x90,0x00,0x2B,0x29,
|
||||
0xA3,0xD4,0xA1,0x91,0x1D,0x5A,0x08,0x19,0x11,0x99,0x08,0x18,0x49,0x0F,0x18,0x10,
|
||||
0x82,0xF1,0x00,0x89,0x2F,0x3A,0x01,0xB3,0xC2,0x81,0x3F,0x29,0x08,0x10,0xA1,0xA1,
|
||||
0x3B,0x5D,0x19,0x28,0x0B,0x38,0x82,0x91,0x19,0xBD,0x3B,0x7A,0x80,0x12,0xB3,0xE0,
|
||||
0x0B,0x6A,0x01,0x88,0xA4,0x08,0x0B,0x08,0x59,0x80,0x80,0x1D,0x49,0x89,0x00,0x84,
|
||||
0x99,0x1A,0x2B,0x32,0xE3,0xB4,0xA9,0x3A,0x99,0x31,0xE3,0xAA,0x58,0x3B,0x88,0x95,
|
||||
0xC0,0x18,0x4A,0x09,0x30,0xF2,0xA3,0x1C,0x1B,0x49,0x00,0xD3,0xB2,0xA0,0x18,0x11,
|
||||
0x92,0xD3,0xB2,0x91,0x80,0xE7,0xA1,0x91,0x98,0x19,0x22,0xC2,0xD2,0x18,0x8D,0x3B,
|
||||
0x10,0xA5,0x91,0x98,0x02,0x3E,0x80,0x01,0x90,0xAA,0x13,0xF1,0x02,0xD1,0x08,0x19,
|
||||
0x49,0xB4,0x91,0xB4,0x99,0x2A,0x0C,0x32,0xC0,0x05,0x88,0x0B,0x80,0x2C,0x81,0x10,
|
||||
0x0B,0x51,0xA9,0x19,0x05,0xBF,0x28,0x20,0xE1,0x90,0x80,0x28,0x19,0x08,0x26,0xB1,
|
||||
0xA1,0x18,0x88,0x2A,0xF0,0x12,0x8A,0xB3,0x14,0x1B,0xD4,0xD8,0x10,0x08,0x8A,0x17,
|
||||
0xA0,0x98,0x2B,0x3A,0x29,0x48,0xA4,0x99,0x0E,0x4A,0x12,0x8B,0x31,0x8B,0x4E,0x1A,
|
||||
0x11,0xB5,0x89,0x91,0x29,0x89,0xC2,0x97,0x90,0x0A,0x19,0x11,0x91,0xC1,0xD5,0x08,
|
||||
0x89,0x20,0x91,0xB1,0x1A,0x2D,0x18,0x29,0xD2,0x3B,0x3E,0x3A,0x2A,0x90,0x82,0x1C,
|
||||
0x49,0x3B,0x93,0xB6,0xC8,0x4C,0x02,0x91,0x93,0xF2,0x88,0x2D,0x28,0x81,0x82,0xC1,
|
||||
0x89,0x2D,0x6B,0x19,0x82,0x80,0x18,0x8B,0x39,0x39,0xC8,0x3A,0x6A,0x0A,0x22,0xD2,
|
||||
0x09,0x2C,0x1A,0x68,0x92,0xE2,0x89,0x2A,0x2A,0x30,0xC2,0xA3,0xB4,0x1D,0x2A,0x09,
|
||||
0x93,0x18,0xF2,0x89,0x28,0xB3,0x01,0x8F,0x18,0x11,0xA1,0x93,0x90,0xD1,0x7A,0x20,
|
||||
0xC3,0xA2,0xA8,0x88,0x1D,0x28,0xA5,0xA2,0xA2,0x0B,0x29,0x2B,0x87,0xC1,0x80,0x0A,
|
||||
0x19,0x01,0x12,0xF1,0x10,0x80,0x0A,0x18,0x08,0x2F,0x4A,0x02,0x89,0x1B,0x29,0x5D,
|
||||
0x4C,0x08,0x82,0xA1,0x0A,0x3A,0x4B,0x29,0xC6,0xC3,0x09,0x09,0x88,0x39,0x98,0x82,
|
||||
0xA5,0x1A,0x30,0x11,0xBD,0x3F,0x12,0x8B,0x28,0xC3,0x88,0x3F,0x2B,0x3B,0x48,0xA1,
|
||||
0x80,0x8A,0x4D,0x39,0x01,0x93,0xA2,0xF1,0x19,0x19,0x0A,0x02,0xB2,0x8B,0x24,0xD2,
|
||||
0x4B,0x12,0xC8,0x2E,0x10,0xB5,0x89,0x01,0x09,0x1C,0x2A,0x03,0xD4,0x91,0x98,0x99,
|
||||
0x11,0x2B,0xE4,0x00,0x00,0x01,0xE0,0xA5,0x89,0x99,0x31,0x18,0xD0,0xB7,0x98,0x18,
|
||||
0x0A,0x10,0x94,0xC2,0x90,0x18,0x00,0x99,0x87,0xA0,0x90,0x2A,0x3C,0x02,0xB8,0xC1,
|
||||
0x79,0x1A,0x20,0x08,0xA1,0xD2,0x1C,0x29,0x03,0xD1,0x29,0x99,0x2C,0x50,0xB3,0xD1,
|
||||
0x08,0x09,0x3C,0x10,0x04,0xB2,0x0D,0x2B,0x59,0x80,0x90,0x01,0x0F,0x3A,0x18,0x01,
|
||||
0xA2,0x9B,0x5B,0x3D,0x81,0x03,0xD2,0x98,0x59,0x90,0x81,0x92,0xB4,0x8B,0x1B,0x40,
|
||||
0xB2,0xB5,0x08,0x4B,0x01,0x09,0xD1,0x91,0x8B,0x7A,0x10,0xB3,0xC3,0x99,0x49,0x1A,
|
||||
0x29,0xB5,0xA2,0xAB,0x40,0x81,0x19,0xB7,0xB0,0x20,0x2B,0xD4,0x88,0xA1,0x91,0x3C,
|
||||
0x82,0x37,0xD3,0xB1,0x8A,0x1B,0x30,0xB3,0xF4,0xA1,0x91,0x09,0x10,0x03,0xD0,0x83,
|
||||
0xA9,0x8F,0x10,0x01,0x90,0x18,0x80,0x20,0x2B,0xF1,0x28,0x99,0x2A,0x41,0xF0,0x12,
|
||||
0xAA,0x83,0x82,0xD1,0xC1,0x08,0x89,0x59,0x09,0x83,0x87,0xB0,0x2A,0x4D,0x18,0x09,
|
||||
0x19,0xB3,0x4B,0x3F,0x39,0x19,0x09,0x01,0x89,0x03,0x1F,0x00,0x1A,0x0B,0x10,0x68,
|
||||
0xA0,0x18,0x8C,0x6A,0x09,0x08,0x97,0xA1,0x81,0x1B,0x2B,0x4C,0x03,0xB4,0xA8,0x92,
|
||||
0x4B,0x3C,0xA1,0x81,0x95,0xA8,0x81,0x12,0xBB,0x92,0x45,0xB9,0x93,0xF4,0x88,0x0A,
|
||||
0x2D,0x28,0x00,0xA3,0xA3,0x8A,0x3F,0x48,0xB1,0x92,0xB4,0xA8,0x30,0x80,0xD3,0x80,
|
||||
0xD1,0x19,0x3B,0xC4,0x81,0xC1,0x29,0x0D,0x20,0x13,0xC8,0xB4,0x4C,0x09,0x00,0x82,
|
||||
0xC2,0x3B,0x0D,0x30,0x0B,0x12,0xF0,0x1B,0x20,0x0A,0xA6,0x80,0x0A,0x4A,0x4A,0x80,
|
||||
0x94,0xB1,0x2E,0x3B,0x1A,0x10,0x93,0x10,0x4C,0x3D,0x08,0x82,0xC9,0x19,0x6A,0x2B,
|
||||
0x38,0xD1,0x08,0x19,0x2A,0x5A,0x82,0xB1,0x8D,0x29,0x78,0x09,0x82,0x0A,0x2C,0x1B,
|
||||
0x19,0x41,0xB8,0x8C,0x79,0x2B,0x11,0x88,0x82,0x91,0xDC,0x28,0x11,0xB0,0x11,0x18,
|
||||
0xC9,0x62,0xA1,0x91,0x98,0x3B,0x3A,0xB0,0xF4,0x01,0xC0,0x29,0x39,0xF8,0x95,0x91,
|
||||
0x88,0x88,0x91,0x03,0xA1,0xE2,0x18,0x82,0xD1,0xA2,0xD1,0x80,0x19,0x20,0x83,0xB1,
|
||||
0xE3,0x80,0x91,0x4D,0x1A,0x03,0xB2,0x09,0x18,0xD1,0x19,0x09,0x92,0xA6,0xA0,0xB6,
|
||||
0xB2,0x8B,0x38,0x10,0x42,0xD3,0xD0,0xA8,0x20,0x2C,0x10,0x01,0xB1,0xB4,0xAB,0x5B,
|
||||
0x79,0x80,0x10,0x1A,0xA8,0x3D,0x18,0x20,0xB3,0x8F,0x18,0x01,0x00,0x09,0xF3,0x89,
|
||||
0x69,0x88,0x81,0x91,0x08,0xE1,0x1A,0x08,0x11,0x81,0x1E,0x29,0xA0,0x01,0x00,0x90,
|
||||
0x3E,0x7B,0x18,0x82,0xC3,0xA1,0x2A,0x2C,0x5B,0x81,0xA5,0x90,0x81,0x00,0x0B,0x1A,
|
||||
0x1C,0x2C,0x32,0xC0,0xF3,0x80,0x2D,0x2A,0x10,0x02,0xE4,0xC1,0x89,0x4A,0x09,0x01,
|
||||
0x03,0xD2,0x98,0x2A,0x39,0x8A,0x89,0x26,0xB1,0xB2,0x12,0xC0,0x0A,0x5A,0x18,0x98,
|
||||
0xF3,0x92,0x99,0x99,0x79,0x01,0xB5,0xA1,0x80,0x80,0x90,0x83,0xA0,0xE2,0x81,0x29,
|
||||
0x93,0x8A,0x0A,0x6A,0x1F,0x18,0x02,0xC8,0x01,0x19,0x3B,0x4A,0x98,0x17,0xA8,0x0D,
|
||||
0x38,0xA1,0x91,0x10,0xA2,0x2B,0x4C,0xA6,0x81,0xBA,0x21,0x4C,0x80,0x21,0xD1,0x92,
|
||||
0x2C,0x08,0x30,0x9F,0x93,0x2A,0x89,0x03,0x8B,0x87,0x0A,0x0D,0x12,0x98,0xA4,0x93,
|
||||
0xBB,0x59,0x18,0xA1,0x32,0xE9,0x84,0x08,0x8A,0x02,0xA1,0x91,0x4B,0xB4,0x20,0x88,
|
||||
0xF0,0x3A,0x1A,0x88,0x87,0xB1,0x92,0x0A,0x08,0x6B,0x83,0xC3,0x91,0xC0,0x2B,0x79,
|
||||
0x08,0x8A,0x84,0xA0,0x89,0x40,0x1B,0xA1,0x39,0x98,0x17,0xC2,0xA2,0x12,0xCD,0x20,
|
||||
0x89,0x92,0x25,0xB0,0x2D,0x3A,0x8B,0x58,0x2A,0xA0,0x4C,0x08,0x30,0xAE,0x82,0x59,
|
||||
0x89,0x1A,0x10,0xC2,0x18,0x2C,0x40,0x1E,0x01,0xA3,0x8A,0x81,0x2C,0x29,0x29,0xA9,
|
||||
0x13,0x51,0xAD,0x12,0x89,0x8F,0x18,0x2C,0x39,0x00,0xC1,0x10,0x3C,0x2A,0x41,0xC8,
|
||||
0xA2,0x91,0x0A,0x6C,0x10,0x12,0x88,0xE8,0x30,0x91,0x81,0xD8,0x01,0x1B,0x0D,0x07,
|
||||
0x00,0xA8,0x92,0x0A,0x28,0xD2,0xC3,0x02,0xAA,0x94,0x81,0xB4,0xB3,0x1A,0x0B,0x13,
|
||||
0xF9,0x16,0xA1,0x8A,0x59,0x19,0x02,0xC1,0x91,0x8B,0x3D,0x18,0x3B,0xA4,0x94,0x80,
|
||||
0x99,0x88,0x1C,0x79,0x0A,0x02,0x03,0xF8,0x90,0x39,0x5B,0x19,0x02,0xC3,0x90,0xBB,
|
||||
0x58,0x6A,0x09,0x02,0x89,0x91,0x88,0x1A,0x69,0x8A,0x19,0x15,0xA0,0xA2,0x00,0x9A,
|
||||
0x6B,0x49,0x88,0xA3,0x92,0xBB,0x6B,0x3D,0x38,0x01,0x98,0x91,0x3F,0x09,0x18,0x20,
|
||||
0x90,0x80,0xAC,0x70,0x91,0x9B,0x51,0x09,0x88,0x99,0x14,0x8B,0x98,0x83,0x79,0xA0,
|
||||
0x99,0x13,0x01,0x19,0xE0,0x83,0x0B,0xB0,0x0C,0x31,0x95,0xB5,0xC2,0x8A,0x39,0x20,
|
||||
0x80,0x39,0xF3,0xB1,0x10,0x88,0x5E,0x18,0x94,0xA1,0x88,0xA1,0x98,0x15,0xAA,0x39,
|
||||
0xD4,0x84,0xC0,0xA2,0xA2,0x0C,0x81,0x86,0xB5,0xA1,0xB1,0x14,0x1B,0xB1,0x02,0x92,
|
||||
0xC3,0xE0,0x88,0x11,0xAA,0x69,0x18,0x81,0xA3,0xB0,0x01,0xBF,0x2A,0x31,0x93,0xF1,
|
||||
0x00,0x89,0x18,0x19,0x11,0xD3,0xE0,0x10,0x18,0xB1,0x18,0x24,0x9A,0x2B,0xA4,0xC0,
|
||||
0xB0,0x31,0x6C,0x19,0xB4,0x12,0xA8,0xEA,0x58,0x10,0x8B,0x93,0x82,0x88,0x9A,0x41,
|
||||
0x10,0xC3,0xEA,0x41,0xA9,0x9C,0x34,0xA1,0x2A,0x79,0xA2,0x01,0xA8,0xB3,0x28,0xCC,
|
||||
0x41,0x9A,0xB3,0x4B,0xB3,0x27,0x8B,0x83,0x2B,0x2F,0x08,0x28,0xB2,0x80,0x2C,0x30,
|
||||
0x5E,0x09,0x12,0x9B,0x09,0x22,0x5B,0x19,0x8A,0x11,0x59,0x99,0xA4,0x32,0xCD,0x18,
|
||||
0x08,0x10,0x85,0xB3,0xB4,0x1E,0x88,0x28,0x8A,0x11,0x09,0xC0,0x79,0x80,0x91,0x3B,
|
||||
0x80,0x10,0x0F,0x01,0x80,0x91,0x19,0x3D,0x92,0x28,0xA8,0x37,0x9A,0x0A,0x3A,0x8A,
|
||||
0x45,0xA9,0xA4,0x00,0xAA,0x09,0x3D,0x59,0x20,0xE1,0x08,0x98,0x90,0x59,0x10,0x09,
|
||||
0xA3,0xC3,0x93,0x99,0x2B,0x69,0x11,0xD1,0xB1,0xA4,0x91,0x3C,0x89,0x83,0xF0,0x10,
|
||||
0x91,0xA1,0x89,0x59,0x05,0x99,0x93,0x94,0xC8,0x08,0x0A,0x09,0x17,0xB1,0x83,0xC1,
|
||||
0x91,0x40,0xA2,0xC2,0x98,0xC3,0xBA,0x28,0x23,0x0F,0x80,0x50,0xB8,0x19,0x10,0x96,
|
||||
0x98,0x8C,0x05,0x98,0x19,0x29,0x2B,0x3B,0x0A,0xE2,0x01,0x0F,0x3C,0x38,0x08,0x09,
|
||||
0x81,0x4A,0x6C,0x08,0x00,0x88,0x98,0x38,0x2C,0x5A,0x1B,0x20,0x1A,0x39,0xB0,0x09,
|
||||
0xCB,0x5B,0x49,0x09,0x71,0x00,0xC1,0x0E,0x08,0x38,0x0C,0x02,0x10,0x0E,0x10,0x8A,
|
||||
0x48,0x19,0x90,0x92,0x0D,0xA3,0x98,0x3B,0x79,0x19,0x01,0x10,0xE1,0x80,0x19,0x2B,
|
||||
0x10,0xF2,0x02,0xAB,0x84,0x9A,0x29,0xB4,0x80,0x92,0x03,0x88,0x95,0xD0,0x03,0x90,
|
||||
0xA0,0xC7,0xA1,0xB0,0xA2,0x02,0x18,0xB5,0xD4,0x01,0xC0,0x08,0xA2,0x93,0xA8,0xA0,
|
||||
0xC3,0x20,0xF3,0x90,0x00,0xD5,0x08,0x89,0xA5,0x80,0xA0,0x81,0x82,0xC2,0x09,0xD1,
|
||||
0x13,0xCB,0x03,0x84,0x91,0xE1,0x1B,0x12,0x08,0xAB,0x87,0x18,0xAB,0x58,0x89,0x28,
|
||||
0x81,0xC9,0x33,0xA9,0x80,0x2E,0x20,0x83,0xB9,0x20,0x3B,0x9E,0x7A,0x08,0x81,0x18,
|
||||
0x0B,0x88,0x79,0x80,0x8B,0x00,0x12,0x0E,0x89,0x51,0x1B,0x81,0xA0,0x3A,0x01,0xAF,
|
||||
0x11,0x28,0xBA,0x35,0x98,0x88,0x52,0xC0,0x83,0x2F,0xA9,0x11,0x0A,0x19,0x25,0xD0,
|
||||
0x30,0x9C,0x08,0x21,0x98,0x81,0x2A,0xF3,0x2A,0x80,0xB6,0x2B,0x08,0x93,0xE9,0x02,
|
||||
0x81,0x8C,0x21,0x00,0xA6,0xA9,0x94,0x01,0x8F,0x80,0x94,0x98,0x93,0xB4,0x00,0x08,
|
||||
0xC0,0x14,0x98,0xB3,0xB4,0xC1,0x09,0x18,0xA7,0x00,0xA3,0xC8,0x0A,0x3C,0x19,0x96,
|
||||
0x83,0xC1,0x99,0x19,0x4A,0x85,0x80,0xC1,0x91,0x99,0x90,0x2A,0x17,0x95,0x99,0x88,
|
||||
0x12,0xAE,0x39,0x08,0x92,0x84,0xB0,0xA8,0x79,0x09,0x19,0x01,0xB2,0xA3,0x8F,0x28,
|
||||
0x2B,0xA2,0x40,0x82,0xA0,0x4C,0xA9,0x39,0x8D,0x81,0x70,0x88,0xA0,0x1A,0x49,0x2D,
|
||||
0x1A,0x26,0xA8,0x98,0x08,0x29,0x0B,0x12,0x96,0xB1,0xB2,0x3A,0x13,0x9B,0x60,0xA0,
|
||||
0x88,0xB2,0x34,0xEA,0x1A,0x2A,0x79,0x98,0x10,0x04,0x8C,0x1C,0x81,0x04,0x8C,0x83,
|
||||
0x19,0x2F,0x81,0x93,0x98,0x10,0x08,0x30,0x2A,0xFA,0x05,0x08,0x2A,0x89,0x91,0xA3,
|
||||
0xFA,0x11,0x11,0x00,0x8C,0x04,0x8A,0x2A,0xB5,0x10,0xA9,0xC2,0x3D,0x1B,0x32,0x04,
|
||||
0x0A,0x1A,0x09,0x40,0x1F,0x92,0x1D,0x2A,0x91,0x10,0x30,0x2F,0x0B,0x68,0x99,0xA2,
|
||||
0x92,0x88,0x78,0xA9,0x20,0x28,0xE2,0x92,0x1A,0x99,0x4B,0x19,0x22,0xA1,0xE2,0x21,
|
||||
0x2F,0x98,0x29,0x18,0x91,0x08,0xB0,0x79,0x1A,0x82,0x3B,0xB1,0xA7,0x8A,0xB3,0x98,
|
||||
0x5B,0x23,0xCA,0x42,0x83,0xF0,0x90,0x18,0x98,0x08,0xB4,0x20,0xA3,0xC0,0x43,0xD8,
|
||||
0x80,0x81,0xA3,0x99,0xD9,0xA7,0x19,0x90,0x10,0x05,0xB1,0x8B,0x02,0xA4,0xBD,0x23,
|
||||
0x93,0x8A,0x99,0x4B,0x03,0xC1,0xF8,0x38,0x09,0x2B,0x14,0xD0,0x03,0x8A,0x2A,0x39,
|
||||
0xB9,0x97,0x90,0xAA,0x50,0x01,0x99,0x51,0xD1,0x09,0x1A,0xB5,0x00,0x8B,0x93,0x08,
|
||||
0x98,0x11,0xF9,0x85,0x2B,0x08,0x96,0x89,0x90,0x2A,0x12,0x4A,0xD8,0x85,0x2B,0x0E,
|
||||
0x10,0x00,0x01,0xB1,0x9B,0x69,0x1A,0x90,0x40,0xB8,0x01,0x08,0x0A,0x2C,0x09,0x14,
|
||||
0x4B,0xE2,0x82,0x88,0xB1,0x78,0x0A,0x01,0xC2,0x93,0x19,0xCE,0x20,0x3C,0x82,0xB4,
|
||||
0x1B,0x20,0x8C,0x3B,0x29,0xAB,0x86,0x23,0xD8,0x81,0x9A,0x5A,0x49,0xB0,0x16,0xA0,
|
||||
0xB0,0x28,0x1B,0x13,0x93,0xE4,0xA2,0xA9,0x08,0x5A,0xB3,0x12,0xC1,0xE1,0x10,0x88,
|
||||
0x01,0x0C,0x92,0x08,0x89,0xB7,0x88,0x81,0x10,0x9A,0x17,0xA0,0xB0,0x13,0x99,0xE0,
|
||||
0x39,0x31,0xD2,0xB2,0x80,0x0B,0x2D,0x49,0x80,0x01,0xB0,0x06,0x09,0x0C,0x3A,0x69,
|
||||
0xA0,0x08,0xB2,0xA1,0x69,0x2B,0x5A,0x81,0x92,0xBA,0x21,0xB1,0x7D,0x10,0x80,0x08,
|
||||
0x88,0x82,0x32,0x0D,0xB0,0x1A,0x1C,0x21,0x94,0xA9,0x58,0xB9,0x5A,0x4A,0xA0,0x13,
|
||||
0xA9,0x80,0x7C,0x00,0x20,0x8A,0x04,0x0C,0x00,0x82,0x2A,0xB2,0xAC,0x4B,0x69,0xA0,
|
||||
0xA6,0x81,0x9B,0x19,0x38,0x8B,0x17,0xB2,0x81,0x2A,0xBB,0x94,0x29,0xA2,0x15,0xBA,
|
||||
0x97,0xA3,0xB9,0x79,0x01,0xB2,0x02,0xF1,0x90,0x0A,0x29,0x11,0x88,0xE5,0xA0,0x81,
|
||||
0x19,0x91,0x90,0x28,0xB3,0x14,0xD0,0xB5,0x91,0x9A,0x29,0x0B,0x07,0xA2,0xB3,0x01,
|
||||
0x9D,0x28,0x41,0xD0,0x91,0x90,0x82,0x1A,0xA8,0x44,0x9A,0xA9,0x21,0xE3,0xA9,0x4B,
|
||||
0x19,0x78,0x89,0x83,0xA3,0xB9,0x5A,0x3D,0x80,0x82,0xA2,0xA0,0x6C,0x10,0x20,0x8B,
|
||||
0x93,0x8B,0x0E,0x33,0xA9,0xB1,0x68,0x8A,0x31,0xAC,0x94,0xB4,0x8B,0x32,0x0B,0xB4,
|
||||
0x81,0x91,0x1D,0x33,0xD9,0x31,0xE1,0x8B,0x3B,0x30,0x12,0x49,0xD2,0x8E,0x29,0x18,
|
||||
0x8A,0x92,0x02,0xAA,0x59,0x1C,0x32,0x88,0x01,0x23,0xFB,0x83,0x29,0xDA,0x59,0x01,
|
||||
0x81,0x92,0xE1,0x18,0x8A,0x1D,0x30,0x93,0xF1,0x00,0x01,0x0B,0x39,0x92,0x89,0xA0,
|
||||
0x11,0x5B,0xE0,0x82,0x09,0x13,0xAA,0xB4,0x16,0xD8,0x91,0x2A,0x29,0x84,0x1B,0xC5,
|
||||
0x98,0x98,0x31,0x98,0x99,0x17,0xA9,0x20,0x92,0xC3,0x18,0x9D,0x20,0x3D,0x89,0x94,
|
||||
0xA2,0x1C,0x5C,0x29,0x39,0xA0,0xB3,0x00,0x0C,0x4C,0x48,0x92,0x0A,0x91,0x85,0x9A,
|
||||
0x01,0x82,0x1F,0x10,0x99,0x15,0xC1,0xA0,0x39,0x1A,0x1D,0x85,0xB4,0x90,0x1A,0x2A,
|
||||
0x4B,0x01,0xB2,0x93,0xBE,0x12,0x83,0xC9,0x18,0x09,0x20,0x78,0xF1,0x08,0x19,0x88,
|
||||
0x3A,0x83,0xB3,0xA9,0x93,0x7A,0x0A,0x96,0x98,0x00,0xA8,0x3A,0x30,0x92,0xF2,0x9B,
|
||||
0x3D,0x38,0x92,0x92,0xC3,0xB8,0x6B,0x29,0x01,0x01,0xB2,0x2F,0x09,0x19,0x18,0x01,
|
||||
0x3B,0x7B,0x10,0xA1,0x90,0x39,0x0F,0x38,0x0A,0xB5,0xA4,0x89,0x8B,0x6A,0x2B,0x12,
|
||||
0xC8,0x90,0x40,0x2A,0x9E,0x22,0x88,0x18,0x09,0x3A,0xC3,0xE8,0x09,0x59,0x08,0x12,
|
||||
0x94,0xD0,0x1A,0x2C,0x38,0x00,0xA1,0x83,0xE8,0x08,0x3A,0x08,0x10,0x9E,0x83,0x1D,
|
||||
0x92,0x19,0x2C,0x39,0x3B,0x59,0x04,0xE1,0x80,0x08,0x8D,0x21,0x81,0xB2,0xB2,0x02,
|
||||
0x99,0x91,0xA4,0xD6,0x98,0x99,0x03,0x80,0x98,0xA7,0x91,0x09,0xA1,0xB2,0xB3,0xE1,
|
||||
0x12,0x92,0xB1,0x81,0x06,0x99,0x0A,0x23,0xC4,0xB1,0xF2,0x89,0x19,0x3A,0x94,0x82,
|
||||
0xE0,0x89,0x38,0x0B,0xA4,0xA5,0x80,0x80,0x8C,0x34,0xB9,0xA9,0x23,0x13,0xB9,0xC1,
|
||||
0xC7,0x1B,0x89,0x10,0x20,0x11,0xE3,0xA8,0x4B,0x0B,0x40,0x91,0x90,0x1B,0x5F,0x2A,
|
||||
0x18,0x82,0x91,0x0B,0x4A,0x28,0xCA,0x40,0x80,0x5B,0x2C,0x13,0xB0,0x8A,0xA9,0x5A,
|
||||
0x58,0x89,0x82,0x88,0x2E,0x3B,0x31,0xA1,0x9B,0x01,0x7A,0x2C,0x01,0x91,0x93,0x3F,
|
||||
0x88,0x39,0x10,0xF1,0x91,0x8B,0x48,0x0A,0x12,0xE3,0xA8,0x18,0x28,0x92,0x97,0x98,
|
||||
0x99,0x19,0xA1,0x11,0xB6,0x88,0x3B,0x10,0xD3,0xC3,0xA1,0x2A,0x8A,0x49,0x04,0xF1,
|
||||
0x91,0x02,0x8A,0x89,0x04,0xF1,0x98,0x80,0x18,0x12,0xE3,0x81,0x98,0x80,0x01,0xB3,
|
||||
0xF2,0x99,0x12,0x2A,0xB5,0xB3,0x92,0xAA,0x19,0x50,0xB2,0xC3,0x92,0xD0,0x2B,0x68,
|
||||
0x93,0x99,0xC0,0x2C,0x3E,0x80,0x20,0x08,0x93,0x0D,0x2A,0x31,0x8D,0x02,0x2B,0x91,
|
||||
0x08,0x0A,0x03,0x2C,0x3C,0x52,0xB9,0xA0,0x12,0xBF,0x3A,0x29,0x01,0x88,0xC0,0x6A,
|
||||
0x3C,0x0A,0x49,0x18,0x0B,0x39,0x2B,0x69,0x0A,0x84,0x2A,0x2A,0x1C,0x2A,0xC3,0x8C,
|
||||
0x19,0x50,0x09,0x91,0xA7,0x8D,0x18,0x1A,0x28,0x00,0xA0,0x94,0x10,0x1F,0x20,0x90,
|
||||
0x8A,0x12,0xD0,0x1A,0x5A,0x81,0x04,0xBC,0x23,0x10,0xE0,0x90,0x90,0x18,0x1A,0xA6,
|
||||
0x12,0xB1,0xD0,0x4A,0x08,0x82,0x92,0xB6,0x9A,0x0A,0x12,0x88,0xC3,0xC5,0x8A,0x89,
|
||||
0x20,0xB5,0x93,0x0B,0x18,0x00,0x09,0xF2,0x88,0x2A,0x4A,0x08,0x05,0xB2,0xA9,0x3B,
|
||||
0x5D,0x28,0xA4,0xB1,0x00,0x19,0x19,0x7A,0xA3,0xB3,0x0A,0x90,0xA1,0xC4,0x80,0xBA,
|
||||
0x50,0x13,0xC1,0xC2,0x9A,0x2A,0x7B,0x28,0x84,0xC1,0x09,0x3B,0x4E,0x20,0x91,0xA1,
|
||||
0x18,0xAB,0x79,0x10,0xB4,0x08,0x9A,0x11,0x2B,0xF0,0x93,0xAA,0x01,0x6A,0x01,0x93,
|
||||
0x80,0xB8,0x2A,0x5B,0x10,0x80,0x89,0x4A,0x5B,0x92,0x15,0xB2,0xA0,0x2F,0x19,0x93,
|
||||
0xB8,0x95,0x80,0x1C,0x21,0xA9,0x02,0x0B,0xA0,0x5A,0x18,0x98,0x39,0x1B,0x68,0x00,
|
||||
0x91,0x91,0x9C,0x39,0x3E,0x18,0x84,0xB3,0x9B,0x7A,0x08,0x18,0x0A,0xB5,0x91,0x0B,
|
||||
0x28,0x39,0x19,0x90,0x0A,0x50,0xAC,0x11,0x01,0xAB,0x88,0x52,0x1B,0x83,0xC4,0xA2,
|
||||
0x9A,0xAB,0x03,0x90,0x19,0x93,0x81,0x08,0x92,0x9A,0x68,0x98,0x19,0x39,0xC1,0x92,
|
||||
0x8A,0x38,0x4E,0x02,0xB1,0x90,0xC3,0x18,0x2B,0x04,0xC3,0xD2,0x91,0x90,0x81,0x89,
|
||||
0x13,0xF1,0x88,0x93,0xA2,0x00,0x91,0xC0,0x5B,0x21,0x99,0x93,0x06,0x9A,0x1B,0x48,
|
||||
0x99,0xB7,0x90,0x89,0x18,0x1B,0x11,0xA4,0xB2,0x81,0x9A,0x08,0x97,0x98,0x91,0x10,
|
||||
0xB8,0x06,0xA2,0xA0,0x29,0x2B,0x21,0xC2,0xD1,0x10,0x1A,0x4A,0x29,0xF1,0x98,0x29,
|
||||
0x1B,0x31,0x10,0xA0,0xA1,0x1D,0x5A,0x29,0xB2,0x82,0xA8,0x0F,0x28,0x21,0x09,0x91,
|
||||
0x82,0x4D,0x10,0xA3,0xB0,0x89,0x4C,0x39,0xA0,0xA4,0xA1,0x89,0x1E,0x28,0x29,0xA3,
|
||||
0xC3,0x2D,0x19,0x01,0x49,0x01,0x9B,0x0C,0x21,0xC2,0xA2,0x93,0x7C,0x2A,0x10,0x90,
|
||||
|
||||
/* Source: 08HH.ROM */
|
||||
/* Length: 384 / 0x00000180 */
|
||||
|
||||
0x75,0xF2,0xAB,0x7D,0x7E,0x5C,0x3B,0x4B,0x3C,0x4D,0x4A,0x02,0xB3,0xC5,0xE7,0xE3,
|
||||
0x92,0xB3,0xC4,0xB3,0xC3,0x8A,0x3B,0x5D,0x5C,0x3A,0x84,0xC2,0x91,0xA4,0xE7,0xF7,
|
||||
0xF7,0xF4,0xA1,0x1B,0x49,0xA5,0xB1,0x1E,0x7F,0x5A,0x00,0x89,0x39,0xB7,0xA8,0x3D,
|
||||
0x4A,0x84,0xE7,0xF7,0xE2,0x2D,0x4C,0x3A,0x4E,0x7D,0x04,0xB0,0x2D,0x4B,0x10,0x80,
|
||||
0xA3,0x99,0x10,0x0E,0x59,0x93,0xC4,0xB1,0x81,0xC4,0xA2,0xB2,0x88,0x08,0x3F,0x3B,
|
||||
0x28,0xA6,0xC3,0xA2,0xA2,0xC5,0xC1,0x3F,0x7E,0x39,0x81,0x93,0xC2,0xA3,0xE5,0xD2,
|
||||
0x80,0x93,0xB8,0x6D,0x49,0x82,0xD4,0xA1,0x90,0x01,0xA0,0x09,0x04,0xE3,0xB2,0x91,
|
||||
0xB7,0xB3,0xA8,0x2A,0x03,0xF3,0xA1,0x92,0xC5,0xC3,0xB2,0x0B,0x30,0xB3,0x8E,0x6D,
|
||||
0x4A,0x01,0xB4,0xB4,0xC4,0xC3,0x99,0x3B,0x12,0xE3,0xA1,0x88,0x82,0xB4,0x9A,0x5C,
|
||||
0x3A,0x18,0x93,0xC3,0xB3,0xB4,0xA8,0x19,0x04,0xF3,0xA8,0x3B,0x10,0xA2,0x88,0xA5,
|
||||
0xB2,0x0B,0x6D,0x4B,0x10,0x91,0x89,0x3C,0x18,0x18,0xA6,0xC4,0xC3,0x98,0x19,0x2B,
|
||||
0x20,0x91,0xA0,0x4E,0x28,0x93,0xB3,0xC2,0x92,0xA9,0x5A,0x96,0xC4,0xC2,0x09,0x01,
|
||||
0xC4,0xA1,0x92,0xC4,0xA1,0x89,0x10,0xA3,0xA1,0x90,0x1C,0x5A,0x01,0xC5,0xA1,0x92,
|
||||
0xD4,0xB3,0xC4,0xC4,0xC3,0xA1,0x88,0x1A,0x28,0x89,0x3C,0x3A,0x3D,0x29,0x00,0x93,
|
||||
0xB0,0x3D,0x28,0x80,0x91,0x82,0xE3,0x99,0x2A,0x11,0xD6,0xC3,0x99,0x29,0x82,0xC4,
|
||||
0xC3,0xA1,0x0A,0x3B,0x3D,0x3A,0x02,0xC3,0xA2,0x99,0x3B,0x2C,0x7C,0x28,0x81,0xA3,
|
||||
0xB2,0xA3,0xB1,0x08,0x1A,0x3C,0x18,0x2E,0x4C,0x39,0xA5,0xB3,0xB4,0xC2,0x88,0x08,
|
||||
0x19,0x0A,0x49,0xB7,0xB3,0xA2,0xA1,0x92,0xA1,0x93,0xB1,0x0C,0x7D,0x39,0x93,0xB3,
|
||||
0xB1,0x1A,0x19,0x5D,0x28,0xA6,0xC4,0xB2,0x90,0x09,0x2A,0x18,0x1B,0x5B,0x28,0x88,
|
||||
0x2C,0x29,0x82,0xA0,0x18,0x91,0x2D,0x29,0x2B,0x5C,0x4C,0x3B,0x4C,0x28,0x80,0x92,
|
||||
0x90,0x09,0x2B,0x28,0x1D,0x6B,0x11,0xC5,0xB2,0x0B,0x39,0x09,0x4D,0x28,0x88,0x00,
|
||||
0x1B,0x28,0x94,0xE3,0xA0,0x1A,0x28,0xB5,0xB4,0xB3,0xB2,0x93,0xE2,0x91,0x92,0xD4,
|
||||
0xA0,0x1B,0x4A,0x01,0xA1,0x88,0x2D,0x5C,0x3B,0x28,0x08,0x93,0xD4,0xB2,0x91,0xB4,
|
||||
0xA0,0x3E,0x3B,0x4B,0x3B,0x29,0x08,0x93,0x9B,0x7B,0x3A,0x19,0x00,0x80,0x80,0xA0,
|
||||
|
||||
/* Source: 10TOM.ROM */
|
||||
/* Length: 640 / 0x00000280 */
|
||||
|
||||
0x77,0x27,0x87,0x01,0x2D,0x4F,0xC3,0xC1,0x92,0x91,0x89,0x59,0x83,0x1A,0x32,0xC2,
|
||||
0x95,0xB1,0x81,0x88,0x81,0x4A,0x3D,0x11,0x9E,0x0B,0x88,0x0C,0x18,0x3B,0x11,0x11,
|
||||
0x91,0x00,0xA0,0xE2,0x0A,0x48,0x13,0x24,0x81,0x48,0x1B,0x39,0x1C,0x83,0x84,0xA1,
|
||||
0xD1,0x8E,0x8A,0x0B,0xC0,0x98,0x92,0xB8,0x39,0x90,0x10,0x92,0xF0,0xB5,0x88,0x32,
|
||||
0x49,0x51,0x21,0x03,0x82,0x10,0x8A,0x7A,0x09,0x00,0xA2,0xCA,0x1B,0xCC,0x1C,0xB9,
|
||||
0x8E,0x89,0x89,0xA1,0x89,0x92,0x29,0x11,0x60,0x40,0x14,0x22,0x32,0x78,0x40,0x01,
|
||||
0x02,0x90,0x81,0xAB,0x0B,0x00,0xAF,0x99,0xCC,0xAB,0xDA,0xA9,0x99,0x1B,0x30,0x14,
|
||||
0x92,0x22,0x19,0x68,0x32,0x14,0x26,0x13,0x23,0x23,0x20,0x12,0x9A,0xA8,0xB9,0xFA,
|
||||
0xAA,0xCA,0xCC,0x0C,0xA8,0xAE,0x88,0xB9,0x88,0xA0,0x02,0x21,0x50,0x43,0x03,0x81,
|
||||
0x2A,0x11,0x34,0x63,0x24,0x33,0x22,0x38,0x8B,0xEA,0xAE,0x99,0xA0,0x90,0x82,0x00,
|
||||
0x89,0xBF,0x8A,0xE8,0xA9,0x90,0x01,0x12,0x13,0x12,0x08,0xA9,0xAA,0xC9,0x22,0x63,
|
||||
0x63,0x12,0x44,0x00,0x10,0x88,0x9C,0x98,0xA1,0x85,0x03,0x32,0x36,0x80,0x89,0xDB,
|
||||
0xDB,0xBB,0xB9,0xBA,0x01,0x81,0x28,0x19,0xCB,0xFA,0xBC,0x09,0x13,0x37,0x34,0x34,
|
||||
0x23,0x31,0x20,0x10,0x00,0x00,0x28,0x38,0x10,0x88,0xEC,0x8D,0xCB,0xBC,0xCC,0xBB,
|
||||
0xBB,0xC9,0x99,0x00,0x00,0x33,0x11,0x22,0x81,0x07,0x41,0x54,0x34,0x34,0x22,0x31,
|
||||
0x00,0x88,0x9A,0x9B,0x98,0xAB,0x8E,0x9B,0xBD,0x9C,0xBC,0xBB,0xDA,0xAA,0xA9,0x99,
|
||||
0x18,0x38,0x60,0x20,0x31,0x13,0x13,0x51,0x14,0x31,0x53,0x33,0x35,0x22,0x01,0x8A,
|
||||
0x9C,0xA9,0xCA,0xC9,0xA8,0x00,0x10,0x81,0x9C,0x9E,0xAB,0xCC,0xAB,0xBA,0x98,0x30,
|
||||
0x52,0x03,0x81,0x08,0x9C,0xAC,0xAC,0x18,0x11,0x03,0x51,0x61,0x41,0x31,0x31,0x02,
|
||||
0x01,0x20,0x24,0x43,0x44,0x40,0x30,0x10,0xBC,0xBE,0xCB,0xDB,0xAB,0xBA,0x99,0x98,
|
||||
0x99,0xAA,0xBD,0xAA,0xC8,0x90,0x11,0x53,0x37,0x23,0x43,0x34,0x33,0x33,0x33,0x11,
|
||||
0x28,0x00,0x19,0xA9,0x9A,0xCB,0xCE,0xBB,0xEB,0xBC,0xBB,0xCA,0xBA,0xA8,0x88,0x11,
|
||||
0x12,0x21,0x20,0x22,0x26,0x26,0x23,0x23,0x43,0x24,0x22,0x32,0x20,0x31,0x81,0x9A,
|
||||
0xBC,0xBC,0xCB,0xBD,0x9A,0xA9,0x90,0x98,0xBA,0xCC,0xCB,0xBC,0x8B,0x88,0x22,0x35,
|
||||
0x23,0x12,0x99,0x8B,0xAA,0xAA,0x89,0x82,0x93,0x31,0x42,0x23,0x23,0x21,0x32,0x11,
|
||||
0x20,0x13,0x13,0x24,0x24,0x24,0x22,0x11,0x8A,0x9E,0xAC,0xAC,0xAA,0xBA,0xAA,0xAB,
|
||||
0xBD,0xBC,0xCB,0xCB,0xA9,0xA8,0x91,0x12,0x44,0x43,0x44,0x34,0x34,0x42,0x33,0x42,
|
||||
0x21,0x11,0x11,0x88,0x80,0xAA,0x0B,0xAC,0xCB,0xEC,0xAC,0xBA,0xCA,0xAB,0x9A,0x99,
|
||||
0x80,0x91,0x09,0x08,0x10,0x22,0x44,0x43,0x44,0x33,0x43,0x22,0x13,0x21,0x22,0x20,
|
||||
0x09,0x88,0xB9,0xC8,0xBB,0xAB,0xAB,0xA9,0xA9,0x9B,0x9B,0x99,0x90,0x90,0x00,0x81,
|
||||
0x00,0x08,0x09,0x8A,0x9A,0xAA,0xA9,0xA9,0x99,0x90,0x80,0x01,0x80,0x00,0x09,0x31,
|
||||
0x32,0x44,0x33,0x43,0x34,0x33,0x24,0x22,0x23,0x12,0x10,0x09,0x9B,0xAB,0xCA,0xCC,
|
||||
0xBB,0xCB,0xDA,0xCA,0xAB,0xCA,0xAB,0xA9,0xA8,0x92,0x12,0x43,0x53,0x35,0x23,0x33,
|
||||
0x43,0x43,0x52,0x22,0x22,0x21,0x01,0x09,0x89,0xA9,0xBB,0xBD,0xBC,0xCB,0xDA,0xAB,
|
||||
0xAB,0xAB,0xAA,0xA9,0x99,0xA8,0x09,0x01,0x11,0x34,0x25,0x23,0x33,0x51,0x22,0x31,
|
||||
0x12,0x20,0x21,0x12,0x10,0x80,0x99,0x9A,0x99,0x99,0x88,0x08,0x00,0x88,0xA9,0x99,
|
||||
0x99,0x80,0x80,0x10,0x01,0x00,0x9A,0xAA,0xBB,0xBA,0xBA,0xA9,0x99,0x99,0x89,0x99,
|
||||
0x99,0x00,0x01,0x33,0x35,0x24,0x23,0x34,0x23,0x33,0x34,0x33,0x43,0x32,0x21,0x88,
|
||||
0xAB,0xBD,0xBB,0xDB,0xAB,0xBA,0xBB,0xDA,0xBB,0xCB,0xBB,0xBC,0xA8,0x90,0x01,0x12,
|
||||
0x23,0x43,0x53,0x34,0x34,0x39,0x80,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x00,
|
||||
|
||||
/* Source: 20RIM.ROM */
|
||||
/* Length: 128 / 0x00000080 */
|
||||
|
||||
0x0F,0xFF,0x73,0x8E,0x71,0xCD,0x00,0x49,0x10,0x90,0x21,0x49,0xA0,0xDB,0x02,0x3A,
|
||||
0xE3,0x0A,0x50,0x98,0xC0,0x59,0xA2,0x99,0x09,0x22,0xA2,0x80,0x10,0xA8,0x5B,0xD2,
|
||||
0x88,0x21,0x09,0x96,0xA8,0x10,0x0A,0xE0,0x08,0x48,0x19,0xAB,0x52,0xA8,0x92,0x0C,
|
||||
0x03,0x19,0xE2,0x0A,0x12,0xC2,0x81,0x1E,0x01,0xD0,0x48,0x88,0x98,0x01,0x49,0x91,
|
||||
0xAA,0x2C,0x25,0x89,0x88,0xB5,0x81,0xA2,0x9A,0x12,0x9E,0x38,0x3B,0x81,0x9B,0x59,
|
||||
0x01,0x93,0xCA,0x4A,0x21,0xA0,0x3D,0x0A,0x39,0x3D,0x12,0xA8,0x3F,0x18,0x01,0x92,
|
||||
0x1C,0x00,0xB2,0x48,0xB9,0x94,0xA3,0x19,0x4F,0x19,0xB2,0x32,0x90,0xBA,0x01,0xE6,
|
||||
0x91,0x80,0xC1,0xA4,0x2A,0x08,0xA1,0xB1,0x25,0xD2,0x88,0x99,0x21,0x80,0x88,0x80,
|
||||
};
|
||||
612
src/engine/platform/sound/scc/scc.cpp
Normal file
612
src/engine/platform/sound/scc/scc.cpp
Normal file
|
|
@ -0,0 +1,612 @@
|
|||
/*
|
||||
License: BSD-3-Clause
|
||||
see https://github.com/cam900/vgsound_emu/LICENSE for more details
|
||||
|
||||
Copyright holder(s): cam900
|
||||
Konami SCC emulation core
|
||||
|
||||
Konami SCC means "Sound Creative Chip", it's actually MSX MegaROM/RAM Mapper with 5 channel Wavetable sound generator.
|
||||
|
||||
It was first appeared at 1987, F-1 Spirit and Nemesis 2/Gradius 2 for MSX. then several MSX cartridges used that until 1990, Metal Gear 2: Solid Snake.
|
||||
Even after MSX is discontinued, it was still used at some low-end arcade and amusement hardwares.
|
||||
and some Third-party MSX utilities still support this due to its market shares.
|
||||
|
||||
There's 2 SCC types:
|
||||
|
||||
K051649 (or simply known as SCC)
|
||||
This chip is used for MSX MegaROM Mapper, some arcade machines.
|
||||
Channel 4 and 5 must be share waveform, other channels has its own waveforms.
|
||||
|
||||
K052539 (also known as SCC+)
|
||||
This chip is used for MSX MegaRAM Mapper (Konami Sound Cartridges for Snatcher/SD Snatcher).
|
||||
All channels can be has its own waveforms, and also has backward compatibility mode with K051649.
|
||||
|
||||
Based on:
|
||||
https://www.msx.org/wiki/MegaROM_Mappers
|
||||
https://www.msx.org/wiki/Konami_051649
|
||||
https://www.msx.org/wiki/Konami_052539
|
||||
http://bifi.msxnet.org/msxnet/tech/scc
|
||||
http://bifi.msxnet.org/msxnet/tech/soundcartridge
|
||||
|
||||
K051649 Register Layout
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
4000-bfff MegaROM Mapper
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
Address Bit R/W Description
|
||||
7654 3210
|
||||
|
||||
4000-5fff xxxx xxxx R Bank page 0
|
||||
c000-dfff mirror of 4000-5fff
|
||||
|
||||
6000-7fff xxxx xxxx R Bank page 1
|
||||
e000-ffff mirror of 6000-7fff
|
||||
|
||||
8000-9fff xxxx xxxx R Bank page 2
|
||||
0000-1fff mirror of 8000-9fff
|
||||
|
||||
a000-bfff xxxx xxxx R Bank page 3
|
||||
2000-3fff mirror of a000-bfff
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
5000-57ff, 7000-77ff, 9000-97ff, b000-b7ff Bank select
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
Address Bit R/W Description
|
||||
7654 3210
|
||||
|
||||
5000 --xx xxxx W Bank select, Page 0
|
||||
5001-57ff Mirror of 5000
|
||||
|
||||
7000 --xx xxxx W Bank select, Page 1
|
||||
7001-77ff Mirror of 7000
|
||||
|
||||
9000 --xx xxxx W Bank select, Page 2
|
||||
--11 1111 W SCC Enable
|
||||
9001-97ff Mirror of 9000
|
||||
|
||||
b000 --xx xxxx W Bank select, Page 3
|
||||
b001-b7ff Mirror of b000
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
9800-9fff SCC register
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
9800-987f Waveform
|
||||
|
||||
Address Bit R/W Description
|
||||
7654 3210
|
||||
|
||||
9800-981f xxxx xxxx R/W Channel 0 Waveform (32 byte, 8 bit signed)
|
||||
9820-983f xxxx xxxx R/W Channel 1 ""
|
||||
9840-985f xxxx xxxx R/W Channel 2 ""
|
||||
9860-987f xxxx xxxx R/W Channel 3/4 ""
|
||||
|
||||
9880-9889 Pitch
|
||||
|
||||
9880 xxxx xxxx W Channel 0 Pitch LSB
|
||||
9881 ---- xxxx W Channel 0 Pitch MSB
|
||||
9882 xxxx xxxx W Channel 1 Pitch LSB
|
||||
9883 ---- xxxx W Channel 1 Pitch MSB
|
||||
9884 xxxx xxxx W Channel 2 Pitch LSB
|
||||
9885 ---- xxxx W Channel 2 Pitch MSB
|
||||
9886 xxxx xxxx W Channel 3 Pitch LSB
|
||||
9887 ---- xxxx W Channel 3 Pitch MSB
|
||||
9888 xxxx xxxx W Channel 4 Pitch LSB
|
||||
9889 ---- xxxx W Channel 4 Pitch MSB
|
||||
|
||||
9888-988e Volume
|
||||
|
||||
988a ---- xxxx W Channel 0 Volume
|
||||
988b ---- xxxx W Channel 1 Volume
|
||||
988c ---- xxxx W Channel 2 Volume
|
||||
988d ---- xxxx W Channel 3 Volume
|
||||
988e ---- xxxx W Channel 4 Volume
|
||||
|
||||
988f ---x ---- W Channel 4 Output enable/disable flag
|
||||
---- x--- W Channel 3 Output enable/disable flag
|
||||
---- -x-- W Channel 2 Output enable/disable flag
|
||||
---- --x- W Channel 1 Output enable/disable flag
|
||||
---- ---x W Channel 0 Output enable/disable flag
|
||||
|
||||
9890-989f Mirror of 9880-988f
|
||||
|
||||
98a0-98bf xxxx xxxx R Channel 4 Waveform
|
||||
|
||||
98e0 x--- ---- W Waveform rotate flag for channel 4
|
||||
-x-- ---- W Waveform rotate flag for all channels
|
||||
--x- ---- W Reset waveform position after pitch writes
|
||||
---- --x- W 8 bit frequency
|
||||
---- --0x W 4 bit frequency
|
||||
|
||||
98e1-98ff Mirror of 98e0
|
||||
|
||||
9900-9fff Mirror of 9800-98ff
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
K052539 Register Layout
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
4000-bfff MegaRAM Mapper
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
Address Bit R/W Description
|
||||
7654 3210
|
||||
|
||||
4000-5fff xxxx xxxx R/W Bank page 0
|
||||
c000-dfff xxxx xxxx R/W ""
|
||||
|
||||
6000-7fff xxxx xxxx R/W Bank page 1
|
||||
e000-ffff xxxx xxxx R/W ""
|
||||
|
||||
8000-9fff xxxx xxxx R/W Bank page 2
|
||||
0000-1fff xxxx xxxx R/W ""
|
||||
|
||||
a000-bfff xxxx xxxx R/W Bank page 3
|
||||
2000-3fff xxxx xxxx R/W ""
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
5000-57ff, 7000-77ff, 9000-97ff, b000-b7ff Bank select
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
Address Bit R/W Description
|
||||
7654 3210
|
||||
|
||||
5000 xxxx xxxx W Bank select, Page 0
|
||||
5001-57ff Mirror of 5000
|
||||
|
||||
7000 xxxx xxxx W Bank select, Page 1
|
||||
7001-77ff Mirror of 7000
|
||||
|
||||
9000 xxxx xxxx W Bank select, Page 2
|
||||
--11 1111 W SCC Enable (SCC Compatible mode)
|
||||
9001-97ff Mirror of 9000
|
||||
|
||||
b000 xxxx xxxx W Bank select, Page 3
|
||||
1--- ---- W SCC+ Enable (SCC+ mode)
|
||||
b001-b7ff Mirror of b000
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
bffe-bfff Mapper configuration
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
Address Bit R/W Description
|
||||
7654 3210
|
||||
|
||||
bffe --x- ---- W SCC operation mode
|
||||
--0- ---- W SCC Compatible mode
|
||||
--1- ---- W SCC+ mode
|
||||
---x ---- W RAM write/Bank select toggle for all Bank pages
|
||||
---0 ---- W Bank select enable
|
||||
---1 ---- W RAM write enable
|
||||
---0 -x-- W RAM write/Bank select toggle for Bank page 2
|
||||
---0 --x- W RAM write/Bank select toggle for Bank page 1
|
||||
---0 ---x W RAM write/Bank select toggle for Bank page 0
|
||||
bfff Mirror of bffe
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
9800-9fff SCC Compatible mode register
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
9800-987f Waveform
|
||||
|
||||
Address Bit R/W Description
|
||||
7654 3210
|
||||
|
||||
9800-981f xxxx xxxx R/W Channel 0 Waveform (32 byte, 8 bit signed)
|
||||
9820-983f xxxx xxxx R/W Channel 1 ""
|
||||
9840-985f xxxx xxxx R/W Channel 2 ""
|
||||
9860-987f xxxx xxxx R/W Channel 3/4 ""
|
||||
|
||||
9880-9889 Pitch
|
||||
|
||||
9880 xxxx xxxx W Channel 0 Pitch LSB
|
||||
9881 ---- xxxx W Channel 0 Pitch MSB
|
||||
9882 xxxx xxxx W Channel 1 Pitch LSB
|
||||
9883 ---- xxxx W Channel 1 Pitch MSB
|
||||
9884 xxxx xxxx W Channel 2 Pitch LSB
|
||||
9885 ---- xxxx W Channel 2 Pitch MSB
|
||||
9886 xxxx xxxx W Channel 3 Pitch LSB
|
||||
9887 ---- xxxx W Channel 3 Pitch MSB
|
||||
9888 xxxx xxxx W Channel 4 Pitch LSB
|
||||
9889 ---- xxxx W Channel 4 Pitch MSB
|
||||
|
||||
9888-988e Volume
|
||||
|
||||
988a ---- xxxx W Channel 0 Volume
|
||||
988b ---- xxxx W Channel 1 Volume
|
||||
988c ---- xxxx W Channel 2 Volume
|
||||
988d ---- xxxx W Channel 3 Volume
|
||||
988e ---- xxxx W Channel 4 Volume
|
||||
|
||||
988f ---x ---- W Channel 4 Output enable/disable flag
|
||||
---- x--- W Channel 3 Output enable/disable flag
|
||||
---- -x-- W Channel 2 Output enable/disable flag
|
||||
---- --x- W Channel 1 Output enable/disable flag
|
||||
---- ---x W Channel 0 Output enable/disable flag
|
||||
|
||||
9890-989f Mirror of 9880-988f
|
||||
|
||||
98a0-98bf xxxx xxxx R Channel 4 Waveform
|
||||
|
||||
98c0 -x-- ---- W Waveform rotate flag for all channels
|
||||
--x- ---- W Reset waveform position after pitch writes
|
||||
---- --x- W 8 bit frequency
|
||||
---- --0x W 4 bit frequency
|
||||
|
||||
98c1-98df Mirror of 98c0
|
||||
|
||||
9900-9fff Mirror of 9800-98ff
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
b800-bfff SCC+ mode register
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
b800-b89f Waveform
|
||||
|
||||
Address Bit R/W Description
|
||||
7654 3210
|
||||
|
||||
b800-b81f xxxx xxxx R/W Channel 0 Waveform (32 byte, 8 bit signed)
|
||||
b820-b83f xxxx xxxx R/W Channel 1 ""
|
||||
b840-b85f xxxx xxxx R/W Channel 2 ""
|
||||
b860-b87f xxxx xxxx R/W Channel 3 ""
|
||||
b880-b89f xxxx xxxx R/W Channel 3 ""
|
||||
|
||||
b8a0-b8a9 Pitch
|
||||
|
||||
b8a0 xxxx xxxx W Channel 0 Pitch LSB
|
||||
b8a1 ---- xxxx W Channel 0 Pitch MSB
|
||||
b8a2 xxxx xxxx W Channel 1 Pitch LSB
|
||||
b8a3 ---- xxxx W Channel 1 Pitch MSB
|
||||
b8a4 xxxx xxxx W Channel 2 Pitch LSB
|
||||
b8a5 ---- xxxx W Channel 2 Pitch MSB
|
||||
b8a6 xxxx xxxx W Channel 3 Pitch LSB
|
||||
b8a7 ---- xxxx W Channel 3 Pitch MSB
|
||||
b8a8 xxxx xxxx W Channel 4 Pitch LSB
|
||||
b8a9 ---- xxxx W Channel 4 Pitch MSB
|
||||
|
||||
b8a8-b8ae Volume
|
||||
|
||||
b8aa ---- xxxx W Channel 0 Volume
|
||||
b8ab ---- xxxx W Channel 1 Volume
|
||||
b8ac ---- xxxx W Channel 2 Volume
|
||||
b8ad ---- xxxx W Channel 3 Volume
|
||||
b8ae ---- xxxx W Channel 4 Volume
|
||||
|
||||
b8af ---x ---- W Channel 4 Output enable/disable flag
|
||||
---- x--- W Channel 3 Output enable/disable flag
|
||||
---- -x-- W Channel 2 Output enable/disable flag
|
||||
---- --x- W Channel 1 Output enable/disable flag
|
||||
---- ---x W Channel 0 Output enable/disable flag
|
||||
|
||||
b8b0-b8bf Mirror of b8a0-b8af
|
||||
|
||||
b8c0 -x-- ---- W Waveform rotate flag for all channels
|
||||
--x- ---- W Reset waveform position after pitch writes
|
||||
---- --x- W 8 bit frequency
|
||||
---- --0x W 4 bit frequency
|
||||
|
||||
b8c1-b8df Mirror of b8c0
|
||||
|
||||
b900-bfff Mirror of b800-b8ff
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
SCC Frequency calculation:
|
||||
if 8 bit frequency then
|
||||
Frequency = Input clock / ((bit 0 to 7 of Pitch input) + 1)
|
||||
else if 4 bit frequency then
|
||||
Frequency = Input clock / ((bit 8 to 11 of Pitch input) + 1)
|
||||
else
|
||||
Frequency = Input clock / (Pitch input + 1)
|
||||
|
||||
*/
|
||||
|
||||
#include "scc.hpp"
|
||||
|
||||
// shared SCC features
|
||||
void scc_core::tick()
|
||||
{
|
||||
m_out = 0;
|
||||
for (auto & elem : m_voice)
|
||||
{
|
||||
elem.tick();
|
||||
m_out += elem.out;
|
||||
}
|
||||
}
|
||||
|
||||
void scc_core::voice_t::tick()
|
||||
{
|
||||
if (pitch >= 9) // or voice is halted
|
||||
{
|
||||
// update counter - Post decrement
|
||||
u16 temp = counter;
|
||||
if (m_host.m_test.freq_4bit) // 4 bit frequency mode
|
||||
{
|
||||
counter = (counter & ~0x0ff) | (bitfield(bitfield(counter, 0, 8) - 1, 0, 8) << 0);
|
||||
counter = (counter & ~0xf00) | (bitfield(bitfield(counter, 8, 4) - 1, 0, 4) << 8);
|
||||
}
|
||||
else
|
||||
counter = bitfield(counter - 1, 0, 12);
|
||||
|
||||
// handle counter carry
|
||||
bool carry = m_host.m_test.freq_8bit ? (bitfield(temp, 0, 8) == 0) :
|
||||
(m_host.m_test.freq_4bit ? (bitfield(temp, 8, 4) == 0) :
|
||||
(bitfield(temp, 0, 12) == 0));
|
||||
if (carry)
|
||||
{
|
||||
addr = bitfield(addr + 1, 0, 5);
|
||||
counter = pitch;
|
||||
}
|
||||
}
|
||||
// get output
|
||||
if (enable)
|
||||
out = (wave[addr] * volume) >> 4; // scale to 11 bit digital output
|
||||
else
|
||||
out = 0;
|
||||
}
|
||||
|
||||
void scc_core::reset()
|
||||
{
|
||||
for (auto & elem : m_voice)
|
||||
elem.reset();
|
||||
|
||||
m_test.reset();
|
||||
m_out = 0;
|
||||
std::fill(std::begin(m_reg), std::end(m_reg), 0);
|
||||
}
|
||||
|
||||
void scc_core::voice_t::reset()
|
||||
{
|
||||
std::fill(std::begin(wave), std::end(wave), 0);
|
||||
enable = false;
|
||||
pitch = 0;
|
||||
volume = 0;
|
||||
addr = 0;
|
||||
counter = 0;
|
||||
out = 0;
|
||||
}
|
||||
|
||||
// SCC accessors
|
||||
u8 scc_core::wave_r(bool is_sccplus, u8 address)
|
||||
{
|
||||
u8 ret = 0xff;
|
||||
const u8 voice = bitfield(address, 5, 3);
|
||||
if (voice > 4)
|
||||
return ret;
|
||||
|
||||
u8 wave_addr = bitfield(address, 0, 5);
|
||||
|
||||
if (m_test.rotate) // rotate flag
|
||||
wave_addr = bitfield(wave_addr + m_voice[voice].addr, 0, 5);
|
||||
|
||||
if (!is_sccplus)
|
||||
{
|
||||
if (voice == 3) // rotate voice 3~4 flag
|
||||
{
|
||||
if (m_test.rotate4 || m_test.rotate) // rotate flag
|
||||
wave_addr = bitfield(bitfield(address, 0, 5) + m_voice[3 + m_test.rotate].addr, 0, 5);
|
||||
}
|
||||
}
|
||||
ret = m_voice[voice].wave[wave_addr];
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void scc_core::wave_w(bool is_sccplus, u8 address, u8 data)
|
||||
{
|
||||
if (m_test.rotate) // write protected
|
||||
return;
|
||||
|
||||
const u8 voice = bitfield(address, 5, 3);
|
||||
if (voice > 4)
|
||||
return;
|
||||
|
||||
const u8 wave_addr = bitfield(address, 0, 5);
|
||||
|
||||
if (!is_sccplus)
|
||||
{
|
||||
if (((voice >= 3) && m_test.rotate4) || (voice >= 4)) // Ignore if write protected, or voice 4
|
||||
return;
|
||||
if (voice >= 3) // voice 3, 4 shares waveform
|
||||
{
|
||||
m_voice[3].wave[wave_addr] = data;
|
||||
m_voice[4].wave[wave_addr] = data;
|
||||
}
|
||||
else
|
||||
m_voice[voice].wave[wave_addr] = data;
|
||||
}
|
||||
else
|
||||
m_voice[voice].wave[wave_addr] = data;
|
||||
}
|
||||
|
||||
void scc_core::freq_vol_enable_w(u8 address, u8 data)
|
||||
{
|
||||
const u8 voice_freq = bitfield(address, 1, 3);
|
||||
const u8 voice_reg = bitfield(address, 0, 4);
|
||||
// *0-*f Pitch, Volume, Enable
|
||||
switch (voice_reg)
|
||||
{
|
||||
case 0x0: // 0x*0 Voice 0 Pitch LSB
|
||||
case 0x2: // 0x*2 Voice 1 Pitch LSB
|
||||
case 0x4: // 0x*4 Voice 2 Pitch LSB
|
||||
case 0x6: // 0x*6 Voice 3 Pitch LSB
|
||||
case 0x8: // 0x*8 Voice 4 Pitch LSB
|
||||
if (m_test.resetpos) // Reset address
|
||||
m_voice[voice_freq].addr = 0;
|
||||
m_voice[voice_freq].pitch = (m_voice[voice_freq].pitch & ~0x0ff) | data;
|
||||
break;
|
||||
case 0x1: // 0x*1 Voice 0 Pitch MSB
|
||||
case 0x3: // 0x*3 Voice 1 Pitch MSB
|
||||
case 0x5: // 0x*5 Voice 2 Pitch MSB
|
||||
case 0x7: // 0x*7 Voice 3 Pitch MSB
|
||||
case 0x9: // 0x*9 Voice 4 Pitch MSB
|
||||
if (m_test.resetpos) // Reset address
|
||||
m_voice[voice_freq].addr = 0;
|
||||
m_voice[voice_freq].pitch = (m_voice[voice_freq].pitch & ~0xf00) | (u16(bitfield(data, 0, 4)) << 8);
|
||||
break;
|
||||
case 0xa: // 0x*a Voice 0 Volume
|
||||
case 0xb: // 0x*b Voice 1 Volume
|
||||
case 0xc: // 0x*c Voice 2 Volume
|
||||
case 0xd: // 0x*d Voice 3 Volume
|
||||
case 0xe: // 0x*e Voice 4 Volume
|
||||
m_voice[voice_reg - 0xa].volume = bitfield(data, 0, 4);
|
||||
break;
|
||||
case 0xf: // 0x*f Enable/Disable flag
|
||||
m_voice[0].enable = bitfield(data, 0);
|
||||
m_voice[1].enable = bitfield(data, 1);
|
||||
m_voice[2].enable = bitfield(data, 2);
|
||||
m_voice[3].enable = bitfield(data, 3);
|
||||
m_voice[4].enable = bitfield(data, 4);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void k051649_scc_core::scc_w(bool is_sccplus, u8 address, u8 data)
|
||||
{
|
||||
const u8 voice = bitfield(address, 5, 3);
|
||||
switch (voice)
|
||||
{
|
||||
case 0b000: // 0x00-0x1f Voice 0 Waveform
|
||||
case 0b001: // 0x20-0x3f Voice 1 Waveform
|
||||
case 0b010: // 0x40-0x5f Voice 2 Waveform
|
||||
case 0b011: // 0x60-0x7f Voice 3/4 Waveform
|
||||
wave_w(false, address, data);
|
||||
break;
|
||||
case 0b100: // 0x80-0x9f Pitch, Volume, Enable
|
||||
freq_vol_enable_w(address, data);
|
||||
break;
|
||||
case 0b111: // 0xe0-0xff Test register
|
||||
m_test.freq_4bit = bitfield(data, 0);
|
||||
m_test.freq_8bit = bitfield(data, 1);
|
||||
m_test.resetpos = bitfield(data, 5);
|
||||
m_test.rotate = bitfield(data, 6);
|
||||
m_test.rotate4 = bitfield(data, 7);
|
||||
break;
|
||||
}
|
||||
m_reg[address] = data;
|
||||
}
|
||||
|
||||
void k052539_scc_core::scc_w(bool is_sccplus, u8 address, u8 data)
|
||||
{
|
||||
const u8 voice = bitfield(address, 5, 3);
|
||||
if (is_sccplus)
|
||||
{
|
||||
switch (voice)
|
||||
{
|
||||
case 0b000: // 0x00-0x1f Voice 0 Waveform
|
||||
case 0b001: // 0x20-0x3f Voice 1 Waveform
|
||||
case 0b010: // 0x40-0x5f Voice 2 Waveform
|
||||
case 0b011: // 0x60-0x7f Voice 3 Waveform
|
||||
case 0b100: // 0x80-0x9f Voice 4 Waveform
|
||||
wave_w(true, address, data);
|
||||
break;
|
||||
case 0b101: // 0xa0-0xbf Pitch, Volume, Enable
|
||||
freq_vol_enable_w(address, data);
|
||||
break;
|
||||
case 0b110: // 0xc0-0xdf Test register
|
||||
m_test.freq_4bit = bitfield(data, 0);
|
||||
m_test.freq_8bit = bitfield(data, 1);
|
||||
m_test.resetpos = bitfield(data, 5);
|
||||
m_test.rotate = bitfield(data, 6);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (voice)
|
||||
{
|
||||
case 0b000: // 0x00-0x1f Voice 0 Waveform
|
||||
case 0b001: // 0x20-0x3f Voice 1 Waveform
|
||||
case 0b010: // 0x40-0x5f Voice 2 Waveform
|
||||
case 0b011: // 0x60-0x7f Voice 3/4 Waveform
|
||||
wave_w(false, address, data);
|
||||
break;
|
||||
case 0b100: // 0x80-0x9f Pitch, Volume, Enable
|
||||
freq_vol_enable_w(address, data);
|
||||
break;
|
||||
case 0b110: // 0xc0-0xdf Test register
|
||||
m_test.freq_4bit = bitfield(data, 0);
|
||||
m_test.freq_8bit = bitfield(data, 1);
|
||||
m_test.resetpos = bitfield(data, 5);
|
||||
m_test.rotate = bitfield(data, 6);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
m_reg[address] = data;
|
||||
}
|
||||
|
||||
u8 k051649_scc_core::scc_r(bool is_sccplus, u8 address)
|
||||
{
|
||||
const u8 voice = bitfield(address, 5, 3);
|
||||
const u8 wave = bitfield(address, 0, 5);
|
||||
u8 ret = 0xff;
|
||||
switch (voice)
|
||||
{
|
||||
case 0b000: // 0x00-0x1f Voice 0 Waveform
|
||||
case 0b001: // 0x20-0x3f Voice 1 Waveform
|
||||
case 0b010: // 0x40-0x5f Voice 2 Waveform
|
||||
case 0b011: // 0x60-0x7f Voice 3 Waveform
|
||||
case 0b101: // 0xa0-0xbf Voice 4 Waveform
|
||||
ret = wave_r(false, (std::min<u8>(4, voice) << 5) | wave);
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
u8 k052539_scc_core::scc_r(bool is_sccplus, u8 address)
|
||||
{
|
||||
const u8 voice = bitfield(address, 5, 3);
|
||||
const u8 wave = bitfield(address, 0, 5);
|
||||
u8 ret = 0xff;
|
||||
if (is_sccplus)
|
||||
{
|
||||
switch (voice)
|
||||
{
|
||||
case 0b000: // 0x00-0x1f Voice 0 Waveform
|
||||
case 0b001: // 0x20-0x3f Voice 1 Waveform
|
||||
case 0b010: // 0x40-0x5f Voice 2 Waveform
|
||||
case 0b011: // 0x60-0x7f Voice 3 Waveform
|
||||
case 0b100: // 0x80-0x9f Voice 4 Waveform
|
||||
ret = wave_r(true, address);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (voice)
|
||||
{
|
||||
case 0b000: // 0x00-0x1f Voice 0 Waveform
|
||||
case 0b001: // 0x20-0x3f Voice 1 Waveform
|
||||
case 0b010: // 0x40-0x5f Voice 2 Waveform
|
||||
case 0b011: // 0x60-0x7f Voice 3 Waveform
|
||||
case 0b101: // 0xa0-0xbf Voice 4 Waveform
|
||||
ret = wave_r(false, (std::min<u8>(4, voice) << 5) | wave);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
137
src/engine/platform/sound/scc/scc.hpp
Normal file
137
src/engine/platform/sound/scc/scc.hpp
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
/*
|
||||
License: BSD-3-Clause
|
||||
see https://github.com/cam900/vgsound_emu/LICENSE for more details
|
||||
|
||||
Copyright holder(s): cam900
|
||||
Konami SCC emulation core
|
||||
|
||||
See scc.cpp for more info.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
#ifndef _VGSOUND_EMU_SCC_HPP
|
||||
#define _VGSOUND_EMU_SCC_HPP
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace scc
|
||||
{
|
||||
typedef unsigned char u8;
|
||||
typedef signed char s8;
|
||||
typedef unsigned short u16;
|
||||
typedef signed short s16;
|
||||
typedef unsigned int u32;
|
||||
typedef signed int s32;
|
||||
|
||||
// get bitfield, bitfield(input, position, len)
|
||||
template<typename T> T bitfield(T in, u8 pos, u8 len = 1)
|
||||
{
|
||||
return (in >> pos) & (len ? (T(1 << len) - 1) : 1);
|
||||
}
|
||||
}
|
||||
|
||||
using namespace scc;
|
||||
// shared for SCCs
|
||||
class scc_core
|
||||
{
|
||||
public:
|
||||
// constructor
|
||||
scc_core()
|
||||
: m_voice{*this,*this,*this,*this,*this}
|
||||
{};
|
||||
virtual ~scc_core(){};
|
||||
|
||||
// accessors
|
||||
virtual u8 scc_r(bool is_sccplus, u8 address) = 0;
|
||||
virtual void scc_w(bool is_sccplus, u8 address, u8 data) = 0;
|
||||
|
||||
// internal state
|
||||
virtual void reset();
|
||||
void tick();
|
||||
|
||||
// getters
|
||||
s32 out() { return m_out; } // output to DA0...DA10 pin
|
||||
s32 chan_out(u8 ch) { return m_voice[ch].out; }
|
||||
u8 reg(u8 address) { return m_reg[address]; }
|
||||
|
||||
protected:
|
||||
// voice structs
|
||||
struct voice_t
|
||||
{
|
||||
// constructor
|
||||
voice_t(scc_core &host) : m_host(host) {};
|
||||
|
||||
// internal state
|
||||
void reset();
|
||||
void tick();
|
||||
|
||||
// registers
|
||||
scc_core &m_host;
|
||||
s8 wave[32] = {0}; // internal waveform
|
||||
bool enable = false; // output enable flag
|
||||
u16 pitch = 0; // pitch
|
||||
u8 volume = 0; // volume
|
||||
u8 addr = 0; // waveform pointer
|
||||
u16 counter = 0; // frequency counter
|
||||
s32 out = 0; // current output
|
||||
};
|
||||
voice_t m_voice[5]; // 5 voices
|
||||
|
||||
// accessor
|
||||
u8 wave_r(bool is_sccplus, u8 address);
|
||||
void wave_w(bool is_sccplus, u8 address, u8 data);
|
||||
void freq_vol_enable_w(u8 address, u8 data);
|
||||
|
||||
struct test_t
|
||||
{
|
||||
// constructor
|
||||
test_t()
|
||||
: freq_4bit(0)
|
||||
, freq_8bit(0)
|
||||
, resetpos(0)
|
||||
, rotate(0)
|
||||
, rotate4(0)
|
||||
{ };
|
||||
|
||||
void reset()
|
||||
{
|
||||
freq_4bit = 0;
|
||||
freq_8bit = 0;
|
||||
resetpos = 0;
|
||||
rotate = 0;
|
||||
rotate4 = 0;
|
||||
}
|
||||
|
||||
u8 freq_4bit : 1; // 4 bit frequency
|
||||
u8 freq_8bit : 1; // 8 bit frequency
|
||||
u8 resetpos : 1; // reset counter after pitch writes
|
||||
u8 rotate : 1; // rotate and write protect waveform for all channels
|
||||
u8 rotate4 : 1; // same as above but for channel 4 only
|
||||
};
|
||||
|
||||
test_t m_test; // test register
|
||||
s32 m_out = 0; // output to DA0...10
|
||||
|
||||
u8 m_reg[256] = {0}; // register pool
|
||||
};
|
||||
|
||||
// SCC core
|
||||
class k051649_scc_core : public scc_core
|
||||
{
|
||||
public:
|
||||
// accessors
|
||||
virtual u8 scc_r(bool is_sccplus, u8 address) override;
|
||||
virtual void scc_w(bool is_sccplus, u8 address, u8 data) override;
|
||||
};
|
||||
|
||||
class k052539_scc_core : public k051649_scc_core
|
||||
{
|
||||
public:
|
||||
// accessors
|
||||
virtual u8 scc_r(bool is_sccplus, u8 address) override;
|
||||
virtual void scc_w(bool is_sccplus, u8 address, u8 data) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -465,6 +465,8 @@ public:
|
|||
// generate one sample of sound
|
||||
void generate(output_data *output, uint32_t numsamples = 1);
|
||||
|
||||
fm_engine* debug_fm_engine() { return &m_fm; }
|
||||
|
||||
protected:
|
||||
// internal helpers
|
||||
void update_prescale(uint8_t prescale);
|
||||
|
|
@ -546,6 +548,12 @@ public:
|
|||
// generate one sample of sound
|
||||
void generate(output_data *output, uint32_t numsamples = 1);
|
||||
|
||||
// get the engine
|
||||
fm_engine* debug_fm_engine() { return &m_fm; }
|
||||
ssg_engine* debug_ssg_engine() { return &m_ssg; }
|
||||
adpcm_a_engine* debug_adpcm_a_engine() { return &m_adpcm_a; }
|
||||
adpcm_b_engine* debug_adpcm_b_engine() { return &m_adpcm_b; }
|
||||
|
||||
protected:
|
||||
// internal helpers
|
||||
void update_prescale(uint8_t prescale);
|
||||
|
|
|
|||
|
|
@ -183,10 +183,11 @@ void DivPlatformSoundUnit::tick(bool sysTick) {
|
|||
}
|
||||
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
|
||||
//DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_SU);
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,2,chan[i].pitch2);
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,2,chan[i].pitch2,chipClock,CHIP_FREQBASE);
|
||||
if (chan[i].pcm) {
|
||||
DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_SU);
|
||||
DivSample* sample=parent->getSample(ins->amiga.initSample);
|
||||
// TODO: sample map?
|
||||
DivSample* sample=parent->getSample(ins->amiga.getSample(chan[i].note));
|
||||
if (sample!=NULL) {
|
||||
double off=0.25;
|
||||
if (sample->centerRate<1) {
|
||||
|
|
@ -204,7 +205,7 @@ void DivPlatformSoundUnit::tick(bool sysTick) {
|
|||
if (chan[i].keyOn) {
|
||||
if (chan[i].pcm) {
|
||||
DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_SU);
|
||||
DivSample* sample=parent->getSample(ins->amiga.initSample);
|
||||
DivSample* sample=parent->getSample(ins->amiga.getSample(chan[i].note));
|
||||
if (sample!=NULL) {
|
||||
unsigned int sampleEnd=sample->offSU+sample->samples;
|
||||
if (sampleEnd>=getSampleMemCapacity(0)) sampleEnd=getSampleMemCapacity(0)-1;
|
||||
|
|
@ -298,13 +299,13 @@ int DivPlatformSoundUnit::dispatch(DivCommand c) {
|
|||
int destFreq=NOTE_FREQUENCY(c.value2);
|
||||
bool return2=false;
|
||||
if (destFreq>chan[c.chan].baseFreq) {
|
||||
chan[c.chan].baseFreq+=c.value*(1+(chan[c.chan].baseFreq>>9));
|
||||
chan[c.chan].baseFreq+=c.value*((parent->song.linearPitch==2)?1:(1+(chan[c.chan].baseFreq>>9)));
|
||||
if (chan[c.chan].baseFreq>=destFreq) {
|
||||
chan[c.chan].baseFreq=destFreq;
|
||||
return2=true;
|
||||
}
|
||||
} else {
|
||||
chan[c.chan].baseFreq-=c.value*(1+(chan[c.chan].baseFreq>>9));
|
||||
chan[c.chan].baseFreq-=c.value*((parent->song.linearPitch==2)?1:(1+(chan[c.chan].baseFreq>>9)));
|
||||
if (chan[c.chan].baseFreq<=destFreq) {
|
||||
chan[c.chan].baseFreq=destFreq;
|
||||
return2=true;
|
||||
|
|
|
|||
|
|
@ -203,7 +203,7 @@ void DivPlatformSwan::tick(bool sysTick) {
|
|||
}
|
||||
}
|
||||
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2);
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER);
|
||||
if (i==1 && pcm && furnaceDac) {
|
||||
double off=1.0;
|
||||
if (dacSample>=0 && dacSample<parent->song.sampleLen) {
|
||||
|
|
@ -263,7 +263,7 @@ int DivPlatformSwan::dispatch(DivCommand c) {
|
|||
dacPos=0;
|
||||
dacPeriod=0;
|
||||
if (ins->type==DIV_INS_AMIGA) {
|
||||
dacSample=ins->amiga.initSample;
|
||||
dacSample=ins->amiga.getSample(c.value);
|
||||
if (dacSample<0 || dacSample>=parent->song.sampleLen) {
|
||||
dacSample=-1;
|
||||
if (dumpWrites) addWrite(0xffff0002,0);
|
||||
|
|
@ -448,6 +448,7 @@ void DivPlatformSwan::muteChannel(int ch, bool mute) {
|
|||
}
|
||||
|
||||
void DivPlatformSwan::forceIns() {
|
||||
noise=0;
|
||||
for (int i=0; i<4; i++) {
|
||||
chan[i].insChanged=true;
|
||||
chan[i].freqChanged=true;
|
||||
|
|
|
|||
|
|
@ -151,16 +151,16 @@ int DivPlatformVERA::calcNoteFreq(int ch, int note) {
|
|||
if (ch<16) {
|
||||
return parent->calcBaseFreq(chipClock,2097152,note,false);
|
||||
} else {
|
||||
double off=1.0;
|
||||
double off=65536.0;
|
||||
if (chan[ch].pcm.sample>=0 && chan[ch].pcm.sample<parent->song.sampleLen) {
|
||||
DivSample* s=parent->getSample(chan[ch].pcm.sample);
|
||||
if (s->centerRate<1) {
|
||||
off=1.0;
|
||||
off=65536.0;
|
||||
} else {
|
||||
off=s->centerRate/8363.0;
|
||||
off=65536.0*(s->centerRate/8363.0);
|
||||
}
|
||||
}
|
||||
return (int)(off*parent->calcBaseFreq(chipClock,65536,note,false));
|
||||
return (int)(parent->calcBaseFreq(chipClock,off,note,false));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -208,7 +208,7 @@ void DivPlatformVERA::tick(bool sysTick) {
|
|||
chan[i].freqChanged=true;
|
||||
}
|
||||
if (chan[i].freqChanged) {
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,8,chan[i].pitch2);
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,8,chan[i].pitch2,chipClock,2097152);
|
||||
if (chan[i].freq>65535) chan[i].freq=65535;
|
||||
rWrite(i,0,chan[i].freq&0xff);
|
||||
rWrite(i,1,(chan[i].freq>>8)&0xff);
|
||||
|
|
@ -237,7 +237,16 @@ void DivPlatformVERA::tick(bool sysTick) {
|
|||
}
|
||||
}
|
||||
if (chan[16].freqChanged) {
|
||||
chan[16].freq=parent->calcFreq(chan[16].baseFreq,chan[16].pitch,false,8,chan[16].pitch2);
|
||||
double off=65536.0;
|
||||
if (chan[16].pcm.sample>=0 && chan[16].pcm.sample<parent->song.sampleLen) {
|
||||
DivSample* s=parent->getSample(chan[16].pcm.sample);
|
||||
if (s->centerRate<1) {
|
||||
off=65536.0;
|
||||
} else {
|
||||
off=65536.0*(s->centerRate/8363.0);
|
||||
}
|
||||
}
|
||||
chan[16].freq=parent->calcFreq(chan[16].baseFreq,chan[16].pitch,false,8,chan[16].pitch2,chipClock,off);
|
||||
if (chan[16].freq>128) chan[16].freq=128;
|
||||
rWritePCMRate(chan[16].freq&0xff);
|
||||
chan[16].freqChanged=false;
|
||||
|
|
@ -248,10 +257,10 @@ int DivPlatformVERA::dispatch(DivCommand c) {
|
|||
int tmp;
|
||||
switch (c.cmd) {
|
||||
case DIV_CMD_NOTE_ON:
|
||||
if(c.chan<16) {
|
||||
rWriteLo(c.chan,2,chan[c.chan].vol)
|
||||
if (c.chan<16) {
|
||||
rWriteLo(c.chan,2,chan[c.chan].vol);
|
||||
} else {
|
||||
chan[16].pcm.sample=parent->getIns(chan[16].ins,DIV_INS_VERA)->amiga.initSample;
|
||||
chan[16].pcm.sample=parent->getIns(chan[16].ins,DIV_INS_VERA)->amiga.getSample(c.value);
|
||||
if (chan[16].pcm.sample<0 || chan[16].pcm.sample>=parent->song.sampleLen) {
|
||||
chan[16].pcm.sample=-1;
|
||||
}
|
||||
|
|
@ -297,11 +306,15 @@ int DivPlatformVERA::dispatch(DivCommand c) {
|
|||
if (c.chan<16) {
|
||||
tmp=c.value&0x3f;
|
||||
chan[c.chan].vol=tmp;
|
||||
rWriteLo(c.chan,2,tmp);
|
||||
if (chan[c.chan].active) {
|
||||
rWriteLo(c.chan,2,tmp);
|
||||
}
|
||||
} else {
|
||||
tmp=c.value&0x0f;
|
||||
chan[c.chan].vol=tmp;
|
||||
rWritePCMVol(tmp);
|
||||
if (chan[c.chan].active) {
|
||||
rWritePCMVol(tmp);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DIV_CMD_GET_VOLUME:
|
||||
|
|
@ -401,7 +414,7 @@ void DivPlatformVERA::muteChannel(int ch, bool mute) {
|
|||
}
|
||||
|
||||
float DivPlatformVERA::getPostAmp() {
|
||||
return 8.0f;
|
||||
return 4.0f;
|
||||
}
|
||||
|
||||
bool DivPlatformVERA::isStereo() {
|
||||
|
|
|
|||
|
|
@ -132,7 +132,7 @@ void DivPlatformVIC20::tick(bool sysTick) {
|
|||
chan[i].freqChanged=true;
|
||||
}
|
||||
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2);
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER);
|
||||
if (i<3) {
|
||||
chan[i].freq>>=(2-i);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -197,9 +197,9 @@ void DivPlatformVRC6::tick(bool sysTick) {
|
|||
}
|
||||
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
|
||||
if (i==2) { // sawtooth
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2)-1;
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2,chipClock,14)-1;
|
||||
} else { // pulse
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2)-1;
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2,chipClock,16)-1;
|
||||
if (chan[i].furnaceDac) {
|
||||
double off=1.0;
|
||||
if (chan[i].dacSample>=0 && chan[i].dacSample<parent->song.sampleLen) {
|
||||
|
|
@ -243,7 +243,7 @@ int DivPlatformVRC6::dispatch(DivCommand c) {
|
|||
if (chan[c.chan].pcm) {
|
||||
if (skipRegisterWrites) break;
|
||||
if (ins->type==DIV_INS_AMIGA) {
|
||||
chan[c.chan].dacSample=ins->amiga.initSample;
|
||||
chan[c.chan].dacSample=ins->amiga.getSample(c.value);
|
||||
if (chan[c.chan].dacSample<0 || chan[c.chan].dacSample>=parent->song.sampleLen) {
|
||||
chan[c.chan].dacSample=-1;
|
||||
if (dumpWrites) addWrite(0xffff0002+(c.chan<<8),0);
|
||||
|
|
|
|||
|
|
@ -262,17 +262,17 @@ void DivPlatformX1_010::acquire(short* bufL, short* bufR, size_t start, size_t l
|
|||
|
||||
double DivPlatformX1_010::NoteX1_010(int ch, int note) {
|
||||
if (chan[ch].pcm) { // PCM note
|
||||
double off=1.0;
|
||||
double off=8192.0;
|
||||
int sample=chan[ch].sample;
|
||||
if (sample>=0 && sample<parent->song.sampleLen) {
|
||||
DivSample* s=parent->getSample(sample);
|
||||
if (s->centerRate<1) {
|
||||
off=1.0;
|
||||
off=8192.0;
|
||||
} else {
|
||||
off=s->centerRate/8363.0;
|
||||
off=8192.0*(s->centerRate/8363.0);
|
||||
}
|
||||
}
|
||||
return off*parent->calcBaseFreq(chipClock,8192,note,false);
|
||||
return parent->calcBaseFreq(chipClock,off,note,false);
|
||||
}
|
||||
// Wavetable note
|
||||
return NOTE_FREQUENCY(note);
|
||||
|
|
@ -487,7 +487,19 @@ void DivPlatformX1_010::tick(bool sysTick) {
|
|||
chan[i].envChanged=false;
|
||||
}
|
||||
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,2,chan[i].pitch2);
|
||||
double off=8192.0;
|
||||
if (chan[i].pcm) {
|
||||
int sample=chan[i].sample;
|
||||
if (sample>=0 && sample<parent->song.sampleLen) {
|
||||
DivSample* s=parent->getSample(sample);
|
||||
if (s->centerRate<1) {
|
||||
off=8192.0;
|
||||
} else {
|
||||
off=8192.0*(s->centerRate/8363.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,2,chan[i].pitch2,chipClock,chan[i].pcm?off:CHIP_FREQBASE);
|
||||
if (chan[i].pcm) {
|
||||
if (chan[i].freq<1) chan[i].freq=1;
|
||||
if (chan[i].freq>255) chan[i].freq=255;
|
||||
|
|
@ -547,7 +559,7 @@ int DivPlatformX1_010::dispatch(DivCommand c) {
|
|||
if (chan[c.chan].furnacePCM) {
|
||||
chan[c.chan].pcm=true;
|
||||
chan[c.chan].macroInit(ins);
|
||||
chan[c.chan].sample=ins->amiga.initSample;
|
||||
chan[c.chan].sample=ins->amiga.getSample(c.value);
|
||||
if (chan[c.chan].sample>=0 && chan[c.chan].sample<parent->song.sampleLen) {
|
||||
DivSample* s=parent->getSample(chan[c.chan].sample);
|
||||
chWrite(c.chan,4,(s->offX1_010>>12)&0xff);
|
||||
|
|
|
|||
1111
src/engine/platform/ym2203.cpp
Normal file
1111
src/engine/platform/ym2203.cpp
Normal file
File diff suppressed because it is too large
Load diff
132
src/engine/platform/ym2203.h
Normal file
132
src/engine/platform/ym2203.h
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef _YM2203_H
|
||||
#define _YM2203_H
|
||||
#include "../dispatch.h"
|
||||
#include "../macroInt.h"
|
||||
#include <queue>
|
||||
#include "sound/ymfm/ymfm_opn.h"
|
||||
|
||||
#include "ay.h"
|
||||
|
||||
class DivYM2203Interface: public ymfm::ymfm_interface {
|
||||
|
||||
};
|
||||
|
||||
class DivPlatformYM2203: public DivDispatch {
|
||||
protected:
|
||||
const unsigned short chanOffs[3]={
|
||||
0x00, 0x01, 0x02
|
||||
};
|
||||
|
||||
struct Channel {
|
||||
DivInstrumentFM state;
|
||||
unsigned char freqH, freqL;
|
||||
int freq, baseFreq, pitch, pitch2, portaPauseFreq, note, ins;
|
||||
unsigned char psgMode, autoEnvNum, autoEnvDen;
|
||||
signed char konCycles;
|
||||
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, inPorta, furnacePCM, hardReset;
|
||||
int vol, outVol;
|
||||
int sample;
|
||||
DivMacroInt std;
|
||||
void macroInit(DivInstrument* which) {
|
||||
std.init(which);
|
||||
pitch2=0;
|
||||
}
|
||||
Channel():
|
||||
freqH(0),
|
||||
freqL(0),
|
||||
freq(0),
|
||||
baseFreq(0),
|
||||
pitch(0),
|
||||
pitch2(0),
|
||||
portaPauseFreq(0),
|
||||
note(0),
|
||||
ins(-1),
|
||||
psgMode(1),
|
||||
autoEnvNum(0),
|
||||
autoEnvDen(0),
|
||||
active(false),
|
||||
insChanged(true),
|
||||
freqChanged(false),
|
||||
keyOn(false),
|
||||
keyOff(false),
|
||||
portaPause(false),
|
||||
inPorta(false),
|
||||
furnacePCM(false),
|
||||
hardReset(false),
|
||||
vol(0),
|
||||
outVol(15),
|
||||
sample(-1) {}
|
||||
};
|
||||
Channel chan[6];
|
||||
DivDispatchOscBuffer* oscBuf[6];
|
||||
bool isMuted[6];
|
||||
struct QueuedWrite {
|
||||
unsigned short addr;
|
||||
unsigned char val;
|
||||
bool addrOrVal;
|
||||
QueuedWrite(unsigned short a, unsigned char v): addr(a), val(v), addrOrVal(false) {}
|
||||
};
|
||||
std::queue<QueuedWrite> writes;
|
||||
ymfm::ym2203* fm;
|
||||
ymfm::ym2203::output_data fmout;
|
||||
DivYM2203Interface iface;
|
||||
unsigned char regPool[512];
|
||||
unsigned char lastBusy;
|
||||
|
||||
DivPlatformAY8910* ay;
|
||||
unsigned char sampleBank;
|
||||
|
||||
int delay;
|
||||
|
||||
bool extMode;
|
||||
|
||||
short oldWrites[256];
|
||||
short pendingWrites[256];
|
||||
|
||||
friend void putDispatchChan(void*,int,int);
|
||||
|
||||
public:
|
||||
void acquire(short* bufL, short* bufR, size_t start, size_t len);
|
||||
int dispatch(DivCommand c);
|
||||
void* getChanState(int chan);
|
||||
DivDispatchOscBuffer* getOscBuffer(int chan);
|
||||
unsigned char* getRegisterPool();
|
||||
int getRegisterPoolSize();
|
||||
void reset();
|
||||
void forceIns();
|
||||
void tick(bool sysTick=true);
|
||||
void muteChannel(int ch, bool mute);
|
||||
bool isStereo();
|
||||
bool keyOffAffectsArp(int ch);
|
||||
void notifyInsChange(int ins);
|
||||
void notifyInsDeletion(void* ins);
|
||||
void setSkipRegisterWrites(bool val);
|
||||
void poke(unsigned int addr, unsigned short val);
|
||||
void poke(std::vector<DivRegWrite>& wlist);
|
||||
const char** getRegisterSheet();
|
||||
const char* getEffectName(unsigned char effect);
|
||||
void setFlags(unsigned int flags);
|
||||
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
|
||||
void quit();
|
||||
~DivPlatformYM2203();
|
||||
};
|
||||
#endif
|
||||
574
src/engine/platform/ym2203ext.cpp
Normal file
574
src/engine/platform/ym2203ext.cpp
Normal file
|
|
@ -0,0 +1,574 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "ym2610bext.h"
|
||||
#include "../engine.h"
|
||||
#include <math.h>
|
||||
|
||||
#include "ym2610shared.h"
|
||||
#include "fmshared_OPN.h"
|
||||
|
||||
int DivPlatformYM2610BExt::dispatch(DivCommand c) {
|
||||
if (c.chan<2) {
|
||||
return DivPlatformYM2610B::dispatch(c);
|
||||
}
|
||||
if (c.chan>5) {
|
||||
c.chan-=3;
|
||||
return DivPlatformYM2610B::dispatch(c);
|
||||
}
|
||||
int ch=c.chan-2;
|
||||
int ordch=orderedOps[ch];
|
||||
switch (c.cmd) {
|
||||
case DIV_CMD_NOTE_ON: {
|
||||
DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM);
|
||||
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[ordch];
|
||||
DivInstrumentFM::Operator op=ins->fm.op[ordch];
|
||||
// TODO: how does this work?!
|
||||
if (isOpMuted[ch]) {
|
||||
rWrite(baseAddr+0x40,127);
|
||||
} else {
|
||||
if (opChan[ch].insChanged) {
|
||||
rWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[ch].vol&0x7f))/127));
|
||||
}
|
||||
}
|
||||
if (opChan[ch].insChanged) {
|
||||
rWrite(baseAddr+0x30,(op.mult&15)|(dtTable[op.dt&7]<<4));
|
||||
rWrite(baseAddr+0x50,(op.ar&31)|(op.rs<<6));
|
||||
rWrite(baseAddr+0x60,(op.dr&31)|(op.am<<7));
|
||||
rWrite(baseAddr+0x70,op.d2r&31);
|
||||
rWrite(baseAddr+0x80,(op.rr&15)|(op.sl<<4));
|
||||
rWrite(baseAddr+0x90,op.ssgEnv&15);
|
||||
}
|
||||
if (opChan[ch].insChanged) { // TODO how does this work?
|
||||
rWrite(chanOffs[2]+0xb0,(ins->fm.alg&7)|(ins->fm.fb<<3));
|
||||
rWrite(chanOffs[2]+0xb4,(opChan[ch].pan<<6)|(ins->fm.fms&7)|((ins->fm.ams&3)<<4));
|
||||
}
|
||||
opChan[ch].insChanged=false;
|
||||
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
opChan[ch].baseFreq=NOTE_FREQUENCY(c.value);
|
||||
opChan[ch].portaPause=false;
|
||||
opChan[ch].freqChanged=true;
|
||||
}
|
||||
opChan[ch].keyOn=true;
|
||||
opChan[ch].active=true;
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_NOTE_OFF:
|
||||
opChan[ch].keyOff=true;
|
||||
opChan[ch].keyOn=false;
|
||||
opChan[ch].active=false;
|
||||
break;
|
||||
case DIV_CMD_VOLUME: {
|
||||
opChan[ch].vol=c.value;
|
||||
DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM);
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[ordch];
|
||||
DivInstrumentFM::Operator op=ins->fm.op[ordch];
|
||||
if (isOpMuted[ch]) {
|
||||
rWrite(baseAddr+0x40,127);
|
||||
} else {
|
||||
rWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[ch].vol&0x7f))/127));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_GET_VOLUME: {
|
||||
return opChan[ch].vol;
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_INSTRUMENT:
|
||||
if (opChan[ch].ins!=c.value || c.value2==1) {
|
||||
opChan[ch].insChanged=true;
|
||||
}
|
||||
opChan[ch].ins=c.value;
|
||||
break;
|
||||
case DIV_CMD_PANNING: {
|
||||
if (c.value==0 && c.value2==0) {
|
||||
opChan[ch].pan=3;
|
||||
} else {
|
||||
opChan[ch].pan=(c.value2>0)|((c.value>0)<<1);
|
||||
}
|
||||
DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM);
|
||||
if (parent->song.sharedExtStat) {
|
||||
for (int i=0; i<4; i++) {
|
||||
if (ch==i) continue;
|
||||
opChan[i].pan=opChan[ch].pan;
|
||||
}
|
||||
}
|
||||
rWrite(chanOffs[2]+0xb4,(opChan[ch].pan<<6)|(ins->fm.fms&7)|((ins->fm.ams&3)<<4));
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_PITCH: {
|
||||
opChan[ch].pitch=c.value;
|
||||
opChan[ch].freqChanged=true;
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_NOTE_PORTA: {
|
||||
if (parent->song.linearPitch==2) {
|
||||
int destFreq=NOTE_FREQUENCY(c.value2);
|
||||
bool return2=false;
|
||||
if (destFreq>opChan[ch].baseFreq) {
|
||||
opChan[ch].baseFreq+=c.value;
|
||||
if (opChan[ch].baseFreq>=destFreq) {
|
||||
opChan[ch].baseFreq=destFreq;
|
||||
return2=true;
|
||||
}
|
||||
} else {
|
||||
opChan[ch].baseFreq-=c.value;
|
||||
if (opChan[ch].baseFreq<=destFreq) {
|
||||
opChan[ch].baseFreq=destFreq;
|
||||
return2=true;
|
||||
}
|
||||
}
|
||||
opChan[ch].freqChanged=true;
|
||||
if (return2) {
|
||||
//opChan[ch].inPorta=false;
|
||||
return 2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
int boundaryBottom=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,0,false);
|
||||
int boundaryTop=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,12,false);
|
||||
int destFreq=NOTE_FNUM_BLOCK(c.value2,11);
|
||||
int newFreq;
|
||||
bool return2=false;
|
||||
if (opChan[ch].portaPause) {
|
||||
opChan[ch].baseFreq=opChan[ch].portaPauseFreq;
|
||||
}
|
||||
if (destFreq>opChan[ch].baseFreq) {
|
||||
newFreq=opChan[ch].baseFreq+c.value;
|
||||
if (newFreq>=destFreq) {
|
||||
newFreq=destFreq;
|
||||
return2=true;
|
||||
}
|
||||
} else {
|
||||
newFreq=opChan[ch].baseFreq-c.value;
|
||||
if (newFreq<=destFreq) {
|
||||
newFreq=destFreq;
|
||||
return2=true;
|
||||
}
|
||||
}
|
||||
// what the heck!
|
||||
if (!opChan[ch].portaPause) {
|
||||
if ((newFreq&0x7ff)>boundaryTop && (newFreq&0xf800)<0x3800) {
|
||||
if (parent->song.fbPortaPause) {
|
||||
opChan[ch].portaPauseFreq=(boundaryBottom)|((newFreq+0x800)&0xf800);
|
||||
opChan[ch].portaPause=true;
|
||||
break;
|
||||
} else {
|
||||
newFreq=(newFreq>>1)|((newFreq+0x800)&0xf800);
|
||||
}
|
||||
}
|
||||
if ((newFreq&0x7ff)<boundaryBottom && (newFreq&0xf800)>0) {
|
||||
if (parent->song.fbPortaPause) {
|
||||
opChan[ch].portaPauseFreq=newFreq=(boundaryTop-1)|((newFreq-0x800)&0xf800);
|
||||
opChan[ch].portaPause=true;
|
||||
break;
|
||||
} else {
|
||||
newFreq=(newFreq<<1)|((newFreq-0x800)&0xf800);
|
||||
}
|
||||
}
|
||||
}
|
||||
opChan[ch].portaPause=false;
|
||||
opChan[ch].freqChanged=true;
|
||||
opChan[ch].baseFreq=newFreq;
|
||||
if (return2) return 2;
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_LEGATO: {
|
||||
opChan[ch].baseFreq=NOTE_FREQUENCY(c.value);
|
||||
opChan[ch].freqChanged=true;
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_LFO: {
|
||||
rWrite(0x22,(c.value&7)|((c.value>>4)<<3));
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_MULT: { // TODO
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]];
|
||||
DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM);
|
||||
DivInstrumentFM::Operator op=ins->fm.op[orderedOps[c.value]];
|
||||
rWrite(baseAddr+0x30,(c.value2&15)|(dtTable[op.dt&7]<<4));
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_TL: { // TODO
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]];
|
||||
DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM);
|
||||
if (isOutput[ins->fm.alg][c.value]) {
|
||||
rWrite(baseAddr+0x40,127-(((127-c.value2)*(opChan[ch].vol&0x7f))/127));
|
||||
} else {
|
||||
rWrite(baseAddr+0x40,c.value2);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_AR: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[2].state.op[i];
|
||||
op.ar=c.value2&31;
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[i];
|
||||
rWrite(baseAddr+0x50,(op.ar&31)|(op.rs<<6));
|
||||
}
|
||||
} else {
|
||||
DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]];
|
||||
op.ar=c.value2&31;
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+0x50,(op.ar&31)|(op.rs<<6));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_RS: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[2].state.op[i];
|
||||
op.rs=c.value2&3;
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6));
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]];
|
||||
op.rs=c.value2&3;
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_AM: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[2].state.op[i];
|
||||
op.am=c.value2&1;
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]];
|
||||
op.am=c.value2&1;
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_DR: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[2].state.op[i];
|
||||
op.dr=c.value2&31;
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]];
|
||||
op.dr=c.value2&31;
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_SL: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[2].state.op[i];
|
||||
op.sl=c.value2&15;
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]];
|
||||
op.sl=c.value2&15;
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_RR: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[2].state.op[i];
|
||||
op.rr=c.value2&15;
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]];
|
||||
op.rr=c.value2&15;
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_D2R: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[2].state.op[i];
|
||||
op.d2r=c.value2&31;
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31);
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]];
|
||||
op.d2r=c.value2&31;
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_DT: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[2].state.op[i];
|
||||
op.dt=c.value&7;
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4));
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]];
|
||||
op.dt=c.value2&7;
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_SSG: {
|
||||
if (c.value<0) {
|
||||
for (int i=0; i<4; i++) {
|
||||
DivInstrumentFM::Operator& op=chan[2].state.op[i];
|
||||
op.ssgEnv=8^(c.value2&15);
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[i];
|
||||
rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15);
|
||||
}
|
||||
} else if (c.value<4) {
|
||||
DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]];
|
||||
op.ssgEnv=8^(c.value2&15);
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]];
|
||||
rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_GET_VOLMAX:
|
||||
return 127;
|
||||
break;
|
||||
case DIV_ALWAYS_SET_VOLUME:
|
||||
return 0;
|
||||
break;
|
||||
case DIV_CMD_PRE_PORTA:
|
||||
break;
|
||||
default:
|
||||
//printf("WARNING: unimplemented command %d\n",c.cmd);
|
||||
break;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int opChanOffsL[4]={
|
||||
0xa9, 0xaa, 0xa8, 0xa2
|
||||
};
|
||||
|
||||
static int opChanOffsH[4]={
|
||||
0xad, 0xae, 0xac, 0xa6
|
||||
};
|
||||
|
||||
void DivPlatformYM2610BExt::tick(bool sysTick) {
|
||||
if (extMode) {
|
||||
bool writeSomething=false;
|
||||
unsigned char writeMask=2;
|
||||
for (int i=0; i<4; i++) {
|
||||
writeMask|=opChan[i].active<<(4+i);
|
||||
if (opChan[i].keyOn || opChan[i].keyOff) {
|
||||
writeSomething=true;
|
||||
writeMask&=~(1<<(4+i));
|
||||
opChan[i].keyOff=false;
|
||||
}
|
||||
}
|
||||
if (writeSomething) {
|
||||
immWrite(0x28,writeMask);
|
||||
}
|
||||
}
|
||||
|
||||
DivPlatformYM2610B::tick(sysTick);
|
||||
|
||||
bool writeNoteOn=false;
|
||||
unsigned char writeMask=2;
|
||||
if (extMode) for (int i=0; i<4; i++) {
|
||||
if (opChan[i].freqChanged) {
|
||||
if (parent->song.linearPitch==2) {
|
||||
opChan[i].freq=parent->calcFreq(opChan[i].baseFreq,opChan[i].pitch,false,4,opChan[i].pitch2,chipClock,CHIP_FREQBASE,11);
|
||||
} else {
|
||||
int fNum=parent->calcFreq(opChan[i].baseFreq&0x7ff,opChan[i].pitch,false,4,opChan[i].pitch2);
|
||||
int block=(opChan[i].baseFreq&0xf800)>>11;
|
||||
if (fNum<0) fNum=0;
|
||||
if (fNum>2047) {
|
||||
while (block<7) {
|
||||
fNum>>=1;
|
||||
block++;
|
||||
}
|
||||
if (fNum>2047) fNum=2047;
|
||||
}
|
||||
opChan[i].freq=(block<<11)|fNum;
|
||||
}
|
||||
if (opChan[i].freq>0x3fff) opChan[i].freq=0x3fff;
|
||||
immWrite(opChanOffsH[i],opChan[i].freq>>8);
|
||||
immWrite(opChanOffsL[i],opChan[i].freq&0xff);
|
||||
}
|
||||
writeMask|=opChan[i].active<<(4+i);
|
||||
if (opChan[i].keyOn) {
|
||||
writeNoteOn=true;
|
||||
writeMask|=1<<(4+i);
|
||||
opChan[i].keyOn=false;
|
||||
}
|
||||
}
|
||||
if (writeNoteOn) {
|
||||
immWrite(0x28,writeMask);
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformYM2610BExt::muteChannel(int ch, bool mute) {
|
||||
if (ch<2) {
|
||||
DivPlatformYM2610B::muteChannel(ch,mute);
|
||||
return;
|
||||
}
|
||||
if (ch>5) {
|
||||
DivPlatformYM2610B::muteChannel(ch-3,mute);
|
||||
return;
|
||||
}
|
||||
isOpMuted[ch-2]=mute;
|
||||
|
||||
int ordch=orderedOps[ch-2];
|
||||
DivInstrument* ins=parent->getIns(opChan[ch-2].ins,DIV_INS_FM);
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[ordch];
|
||||
DivInstrumentFM::Operator op=ins->fm.op[ordch];
|
||||
if (isOpMuted[ch-2]) {
|
||||
rWrite(baseAddr+0x40,127);
|
||||
} else if (isOutput[ins->fm.alg][ordch]) {
|
||||
rWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[ch-2].vol&0x7f))/127));
|
||||
} else {
|
||||
rWrite(baseAddr+0x40,op.tl);
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformYM2610BExt::forceIns() {
|
||||
for (int i=0; i<6; i++) {
|
||||
for (int j=0; j<4; j++) {
|
||||
unsigned short baseAddr=chanOffs[i]|opOffs[j];
|
||||
DivInstrumentFM::Operator& op=chan[i].state.op[j];
|
||||
if (i==2) { // extended channel
|
||||
if (isOpMuted[j]) {
|
||||
rWrite(baseAddr+0x40,127);
|
||||
} else if (isOutput[chan[i].state.alg][j]) {
|
||||
rWrite(baseAddr+0x40,127-(((127-op.tl)*(opChan[j].vol&0x7f))/127));
|
||||
} else {
|
||||
rWrite(baseAddr+0x40,op.tl);
|
||||
}
|
||||
} else {
|
||||
if (isMuted[i]) {
|
||||
rWrite(baseAddr+ADDR_TL,127);
|
||||
} else {
|
||||
if (isOutput[chan[i].state.alg][j]) {
|
||||
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_TL,op.tl);
|
||||
}
|
||||
}
|
||||
}
|
||||
rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4));
|
||||
rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6));
|
||||
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
|
||||
rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31);
|
||||
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
|
||||
rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15);
|
||||
}
|
||||
rWrite(chanOffs[i]+ADDR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3));
|
||||
rWrite(chanOffs[i]+ADDR_LRAF,(isMuted[i]?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4));
|
||||
if (chan[i].active) {
|
||||
chan[i].keyOn=true;
|
||||
chan[i].freqChanged=true;
|
||||
}
|
||||
}
|
||||
for (int i=6; i<16; i++) {
|
||||
chan[i].insChanged=true;
|
||||
}
|
||||
ay->forceIns();
|
||||
ay->flushWrites();
|
||||
for (DivRegWrite& i: ay->getRegisterWrites()) {
|
||||
immWrite(i.addr&15,i.val);
|
||||
}
|
||||
ay->getRegisterWrites().clear();
|
||||
for (int i=0; i<4; i++) {
|
||||
opChan[i].insChanged=true;
|
||||
if (opChan[i].active) {
|
||||
opChan[i].keyOn=true;
|
||||
opChan[i].freqChanged=true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void* DivPlatformYM2610BExt::getChanState(int ch) {
|
||||
if (ch>=6) return &chan[ch-3];
|
||||
if (ch>=2) return &opChan[ch-2];
|
||||
return &chan[ch];
|
||||
}
|
||||
|
||||
DivDispatchOscBuffer* DivPlatformYM2610BExt::getOscBuffer(int ch) {
|
||||
if (ch>=6) return oscBuf[ch-3];
|
||||
if (ch<3) return oscBuf[ch];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void DivPlatformYM2610BExt::reset() {
|
||||
DivPlatformYM2610B::reset();
|
||||
|
||||
for (int i=0; i<4; i++) {
|
||||
opChan[i]=DivPlatformYM2610BExt::OpChannel();
|
||||
opChan[i].vol=127;
|
||||
}
|
||||
|
||||
// channel 2 mode
|
||||
immWrite(0x27,0x40);
|
||||
extMode=true;
|
||||
}
|
||||
|
||||
bool DivPlatformYM2610BExt::keyOffAffectsArp(int ch) {
|
||||
return (ch>8);
|
||||
}
|
||||
|
||||
void DivPlatformYM2610BExt::notifyInsChange(int ins) {
|
||||
DivPlatformYM2610B::notifyInsChange(ins);
|
||||
for (int i=0; i<4; i++) {
|
||||
if (opChan[i].ins==ins) {
|
||||
opChan[i].insChanged=true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int DivPlatformYM2610BExt::init(DivEngine* parent, int channels, int sugRate, unsigned int flags) {
|
||||
DivPlatformYM2610B::init(parent,channels,sugRate,flags);
|
||||
for (int i=0; i<4; i++) {
|
||||
isOpMuted[i]=false;
|
||||
}
|
||||
|
||||
reset();
|
||||
return 19;
|
||||
}
|
||||
|
||||
void DivPlatformYM2610BExt::quit() {
|
||||
DivPlatformYM2610B::quit();
|
||||
}
|
||||
|
||||
DivPlatformYM2610BExt::~DivPlatformYM2610BExt() {
|
||||
}
|
||||
51
src/engine/platform/ym2203ext.h
Normal file
51
src/engine/platform/ym2203ext.h
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "../dispatch.h"
|
||||
|
||||
#include "ym2610b.h"
|
||||
|
||||
class DivPlatformYM2610BExt: public DivPlatformYM2610B {
|
||||
struct OpChannel {
|
||||
DivMacroInt std;
|
||||
unsigned char freqH, freqL;
|
||||
int freq, baseFreq, pitch, pitch2, portaPauseFreq, ins;
|
||||
signed char konCycles;
|
||||
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause;
|
||||
int vol;
|
||||
unsigned char pan;
|
||||
OpChannel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), pitch2(0), portaPauseFreq(0), ins(-1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), portaPause(false), vol(0), pan(3) {}
|
||||
};
|
||||
OpChannel opChan[4];
|
||||
bool isOpMuted[4];
|
||||
friend void putDispatchChan(void*,int,int);
|
||||
public:
|
||||
int dispatch(DivCommand c);
|
||||
void* getChanState(int chan);
|
||||
DivDispatchOscBuffer* getOscBuffer(int chan);
|
||||
void reset();
|
||||
void forceIns();
|
||||
void tick(bool sysTick=true);
|
||||
void muteChannel(int ch, bool mute);
|
||||
bool keyOffAffectsArp(int ch);
|
||||
void notifyInsChange(int ins);
|
||||
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
|
||||
void quit();
|
||||
~DivPlatformYM2610BExt();
|
||||
};
|
||||
45
src/engine/platform/ym2203shared.h
Normal file
45
src/engine/platform/ym2203shared.h
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
static unsigned short opOffs[4]={
|
||||
0x00, 0x04, 0x08, 0x0c
|
||||
};
|
||||
static bool isOutput[8][4]={
|
||||
// 1 3 2 4
|
||||
{false,false,false,true},
|
||||
{false,false,false,true},
|
||||
{false,false,false,true},
|
||||
{false,false,false,true},
|
||||
{false,false,true ,true},
|
||||
{false,true ,true ,true},
|
||||
{false,true ,true ,true},
|
||||
{true ,true ,true ,true},
|
||||
};
|
||||
static unsigned char dtTable[8]={
|
||||
7,6,5,0,1,2,3,4
|
||||
};
|
||||
|
||||
static int orderedOps[4]={
|
||||
0,2,1,3
|
||||
};
|
||||
|
||||
#define rWrite(a,v) if (!skipRegisterWrites) {pendingWrites[a]=v;}
|
||||
#define immWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} }
|
||||
|
||||
#define CHIP_FREQBASE 4720270
|
||||
1479
src/engine/platform/ym2608.cpp
Normal file
1479
src/engine/platform/ym2608.cpp
Normal file
File diff suppressed because it is too large
Load diff
148
src/engine/platform/ym2608.h
Normal file
148
src/engine/platform/ym2608.h
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef _YM2608_H
|
||||
#define _YM2608_H
|
||||
#include "../dispatch.h"
|
||||
#include "../macroInt.h"
|
||||
#include <queue>
|
||||
#include "sound/ymfm/ymfm_opn.h"
|
||||
|
||||
#include "ay.h"
|
||||
|
||||
class DivYM2608Interface: public ymfm::ymfm_interface {
|
||||
public:
|
||||
unsigned char* adpcmBMem;
|
||||
int sampleBank;
|
||||
uint8_t ymfm_external_read(ymfm::access_class type, uint32_t address);
|
||||
void ymfm_external_write(ymfm::access_class type, uint32_t address, uint8_t data);
|
||||
DivYM2608Interface(): adpcmBMem(NULL), sampleBank(0) {}
|
||||
};
|
||||
|
||||
class DivPlatformYM2608: public DivDispatch {
|
||||
protected:
|
||||
const unsigned short chanOffs[6]={
|
||||
0x00, 0x01, 0x02, 0x100, 0x101, 0x102
|
||||
};
|
||||
|
||||
struct Channel {
|
||||
DivInstrumentFM state;
|
||||
unsigned char freqH, freqL;
|
||||
int freq, baseFreq, pitch, pitch2, portaPauseFreq, note, ins;
|
||||
unsigned char psgMode, autoEnvNum, autoEnvDen;
|
||||
signed char konCycles;
|
||||
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, inPorta, furnacePCM, hardReset;
|
||||
int vol, outVol;
|
||||
int sample;
|
||||
unsigned char pan;
|
||||
DivMacroInt std;
|
||||
void macroInit(DivInstrument* which) {
|
||||
std.init(which);
|
||||
pitch2=0;
|
||||
}
|
||||
Channel():
|
||||
freqH(0),
|
||||
freqL(0),
|
||||
freq(0),
|
||||
baseFreq(0),
|
||||
pitch(0),
|
||||
pitch2(0),
|
||||
portaPauseFreq(0),
|
||||
note(0),
|
||||
ins(-1),
|
||||
psgMode(1),
|
||||
autoEnvNum(0),
|
||||
autoEnvDen(0),
|
||||
active(false),
|
||||
insChanged(true),
|
||||
freqChanged(false),
|
||||
keyOn(false),
|
||||
keyOff(false),
|
||||
portaPause(false),
|
||||
inPorta(false),
|
||||
furnacePCM(false),
|
||||
hardReset(false),
|
||||
vol(0),
|
||||
outVol(15),
|
||||
sample(-1),
|
||||
pan(3) {}
|
||||
};
|
||||
Channel chan[16];
|
||||
DivDispatchOscBuffer* oscBuf[16];
|
||||
bool isMuted[16];
|
||||
struct QueuedWrite {
|
||||
unsigned short addr;
|
||||
unsigned char val;
|
||||
bool addrOrVal;
|
||||
QueuedWrite(unsigned short a, unsigned char v): addr(a), val(v), addrOrVal(false) {}
|
||||
};
|
||||
std::queue<QueuedWrite> writes;
|
||||
ymfm::ym2608* fm;
|
||||
ymfm::ym2608::output_data fmout;
|
||||
unsigned char regPool[512];
|
||||
unsigned char lastBusy;
|
||||
|
||||
unsigned char* adpcmBMem;
|
||||
size_t adpcmBMemLen;
|
||||
DivYM2608Interface iface;
|
||||
|
||||
DivPlatformAY8910* ay;
|
||||
unsigned char sampleBank;
|
||||
unsigned char writeRSSOff, writeRSSOn;
|
||||
|
||||
int delay;
|
||||
|
||||
bool extMode;
|
||||
|
||||
short oldWrites[512];
|
||||
short pendingWrites[512];
|
||||
|
||||
double NOTE_OPNB(int ch, int note);
|
||||
double NOTE_ADPCMB(int note);
|
||||
friend void putDispatchChan(void*,int,int);
|
||||
|
||||
public:
|
||||
void acquire(short* bufL, short* bufR, size_t start, size_t len);
|
||||
int dispatch(DivCommand c);
|
||||
void* getChanState(int chan);
|
||||
DivDispatchOscBuffer* getOscBuffer(int chan);
|
||||
unsigned char* getRegisterPool();
|
||||
int getRegisterPoolSize();
|
||||
void reset();
|
||||
void forceIns();
|
||||
void tick(bool sysTick=true);
|
||||
void muteChannel(int ch, bool mute);
|
||||
bool isStereo();
|
||||
bool keyOffAffectsArp(int ch);
|
||||
void notifyInsChange(int ins);
|
||||
void notifyInsDeletion(void* ins);
|
||||
void setSkipRegisterWrites(bool val);
|
||||
void poke(unsigned int addr, unsigned short val);
|
||||
void poke(std::vector<DivRegWrite>& wlist);
|
||||
const char** getRegisterSheet();
|
||||
const char* getEffectName(unsigned char effect);
|
||||
const void* getSampleMem(int index);
|
||||
size_t getSampleMemCapacity(int index);
|
||||
size_t getSampleMemUsage(int index);
|
||||
void renderSamples();
|
||||
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
|
||||
void quit();
|
||||
~DivPlatformYM2608();
|
||||
};
|
||||
#endif
|
||||
42
src/engine/platform/ym2608Interface.cpp
Normal file
42
src/engine/platform/ym2608Interface.cpp
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "sound/ymfm/ymfm.h"
|
||||
#include "ym2608.h"
|
||||
#include "../engine.h"
|
||||
|
||||
#include "sound/rss.h"
|
||||
|
||||
uint8_t DivYM2608Interface::ymfm_external_read(ymfm::access_class type, uint32_t address) {
|
||||
switch (type) {
|
||||
case ymfm::ACCESS_ADPCM_A:
|
||||
return YM2608_ADPCM_ROM[address&0x1fff];
|
||||
case ymfm::ACCESS_ADPCM_B:
|
||||
if (adpcmBMem==NULL) {
|
||||
return 0;
|
||||
}
|
||||
return adpcmBMem[address&0xffffff];
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void DivYM2608Interface::ymfm_external_write(ymfm::access_class type, uint32_t address, uint8_t data) {
|
||||
}
|
||||
|
|
@ -456,13 +456,13 @@ double DivPlatformYM2610::NOTE_OPNB(int ch, int note) {
|
|||
return NOTE_PERIODIC(note);
|
||||
}
|
||||
// FM
|
||||
return NOTE_FREQUENCY(note);
|
||||
return NOTE_FNUM_BLOCK(note,11);
|
||||
}
|
||||
|
||||
double DivPlatformYM2610::NOTE_ADPCMB(int note) {
|
||||
if (chan[13].sample>=0 && chan[13].sample<parent->song.sampleLen) {
|
||||
double off=(double)(parent->getSample(chan[13].sample)->centerRate)/8363.0;
|
||||
return off*parent->calcBaseFreq((double)chipClock/144,65535,note,false);
|
||||
double off=65535.0*(double)(parent->getSample(chan[13].sample)->centerRate)/8363.0;
|
||||
return parent->calcBaseFreq((double)chipClock/144,off,note,false);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -470,7 +470,7 @@ double DivPlatformYM2610::NOTE_ADPCMB(int note) {
|
|||
void DivPlatformYM2610::acquire(short* bufL, short* bufR, size_t start, size_t len) {
|
||||
static int os[2];
|
||||
|
||||
ymfm::ym2612::fm_engine* fme=fm->debug_fm_engine();
|
||||
ymfm::ym2610::fm_engine* fme=fm->debug_fm_engine();
|
||||
ymfm::ssg_engine* ssge=fm->debug_ssg_engine();
|
||||
ymfm::adpcm_a_engine* aae=fm->debug_adpcm_a_engine();
|
||||
ymfm::adpcm_b_engine* abe=fm->debug_adpcm_b_engine();
|
||||
|
|
@ -559,15 +559,15 @@ void DivPlatformYM2610::tick(bool sysTick) {
|
|||
if (chan[i].std.arp.had) {
|
||||
if (!chan[i].inPorta) {
|
||||
if (chan[i].std.arp.mode) {
|
||||
chan[i].baseFreq=NOTE_FREQUENCY(chan[i].std.arp.val);
|
||||
chan[i].baseFreq=NOTE_FNUM_BLOCK(chan[i].std.arp.val,11);
|
||||
} else {
|
||||
chan[i].baseFreq=NOTE_FREQUENCY(chan[i].note+(signed char)chan[i].std.arp.val);
|
||||
chan[i].baseFreq=NOTE_FNUM_BLOCK(chan[i].note+(signed char)chan[i].std.arp.val,11);
|
||||
}
|
||||
}
|
||||
chan[i].freqChanged=true;
|
||||
} else {
|
||||
if (chan[i].std.arp.mode && chan[i].std.arp.finished) {
|
||||
chan[i].baseFreq=NOTE_FREQUENCY(chan[i].note);
|
||||
chan[i].baseFreq=NOTE_FNUM_BLOCK(chan[i].note,11);
|
||||
chan[i].freqChanged=true;
|
||||
}
|
||||
}
|
||||
|
|
@ -726,7 +726,12 @@ void DivPlatformYM2610::tick(bool sysTick) {
|
|||
}
|
||||
}
|
||||
if (chan[13].freqChanged) {
|
||||
chan[13].freq=parent->calcFreq(chan[13].baseFreq,chan[13].pitch,false,4);
|
||||
if (chan[13].sample>=0 && chan[13].sample<parent->song.sampleLen) {
|
||||
double off=65535.0*(double)(parent->getSample(chan[13].sample)->centerRate)/8363.0;
|
||||
chan[13].freq=parent->calcFreq(chan[13].baseFreq,chan[13].pitch,false,4,chan[13].pitch2,(double)chipClock/144,off);
|
||||
} else {
|
||||
chan[13].freq=0;
|
||||
}
|
||||
immWrite(0x19,chan[13].freq&0xff);
|
||||
immWrite(0x1a,(chan[13].freq>>8)&0xff);
|
||||
chan[13].freqChanged=false;
|
||||
|
|
@ -742,11 +747,24 @@ void DivPlatformYM2610::tick(bool sysTick) {
|
|||
for (int i=0; i<4; i++) {
|
||||
if (i==1 && extMode) continue;
|
||||
if (chan[i].freqChanged) {
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,octave(chan[i].baseFreq),chan[i].pitch2);
|
||||
if (chan[i].freq>262143) chan[i].freq=262143;
|
||||
int freqt=toFreq(chan[i].freq)+chan[i].pitch2;
|
||||
immWrite(chanOffs[i]+ADDR_FREQH,freqt>>8);
|
||||
immWrite(chanOffs[i]+ADDR_FREQ,freqt&0xff);
|
||||
if (parent->song.linearPitch==2) {
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,4,chan[i].pitch2,chipClock,CHIP_FREQBASE,11);
|
||||
} else {
|
||||
int fNum=parent->calcFreq(chan[i].baseFreq&0x7ff,chan[i].pitch,false,4,chan[i].pitch2,chipClock,CHIP_FREQBASE,11);
|
||||
int block=(chan[i].baseFreq&0xf800)>>11;
|
||||
if (fNum<0) fNum=0;
|
||||
if (fNum>2047) {
|
||||
while (block<7) {
|
||||
fNum>>=1;
|
||||
block++;
|
||||
}
|
||||
if (fNum>2047) fNum=2047;
|
||||
}
|
||||
chan[i].freq=(block<<11)|fNum;
|
||||
}
|
||||
if (chan[i].freq>0x3fff) chan[i].freq=0x3fff;
|
||||
immWrite(chanOffs[i]+ADDR_FREQH,chan[i].freq>>8);
|
||||
immWrite(chanOffs[i]+ADDR_FREQ,chan[i].freq&0xff);
|
||||
chan[i].freqChanged=false;
|
||||
}
|
||||
if (chan[i].keyOn) {
|
||||
|
|
@ -756,47 +774,6 @@ void DivPlatformYM2610::tick(bool sysTick) {
|
|||
}
|
||||
}
|
||||
|
||||
int DivPlatformYM2610::octave(int freq) {
|
||||
if (freq>=622.0f*128) {
|
||||
return 128;
|
||||
} else if (freq>=622.0f*64) {
|
||||
return 64;
|
||||
} else if (freq>=622.0f*32) {
|
||||
return 32;
|
||||
} else if (freq>=622.0f*16) {
|
||||
return 16;
|
||||
} else if (freq>=622.0f*8) {
|
||||
return 8;
|
||||
} else if (freq>=622.0f*4) {
|
||||
return 4;
|
||||
} else if (freq>=622.0f*2) {
|
||||
return 2;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int DivPlatformYM2610::toFreq(int freq) {
|
||||
if (freq>=622.0f*128) {
|
||||
return 0x3800|((freq>>7)&0x7ff);
|
||||
} else if (freq>=622.0f*64) {
|
||||
return 0x3000|((freq>>6)&0x7ff);
|
||||
} else if (freq>=622.0f*32) {
|
||||
return 0x2800|((freq>>5)&0x7ff);
|
||||
} else if (freq>=622.0f*16) {
|
||||
return 0x2000|((freq>>4)&0x7ff);
|
||||
} else if (freq>=622.0f*8) {
|
||||
return 0x1800|((freq>>3)&0x7ff);
|
||||
} else if (freq>=622.0f*4) {
|
||||
return 0x1000|((freq>>2)&0x7ff);
|
||||
} else if (freq>=622.0f*2) {
|
||||
return 0x800|((freq>>1)&0x7ff);
|
||||
} else {
|
||||
return freq&0x7ff;
|
||||
}
|
||||
}
|
||||
|
||||
int DivPlatformYM2610::dispatch(DivCommand c) {
|
||||
if (c.chan>3 && c.chan<7) {
|
||||
c.chan-=4;
|
||||
|
|
@ -818,7 +795,7 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
|
|||
chan[c.chan].outVol=chan[c.chan].vol;
|
||||
immWrite(0x1b,chan[c.chan].outVol);
|
||||
}
|
||||
chan[c.chan].sample=ins->amiga.initSample;
|
||||
chan[c.chan].sample=ins->amiga.getSample(c.value);
|
||||
if (chan[c.chan].sample>=0 && chan[c.chan].sample<parent->song.sampleLen) {
|
||||
DivSample* s=parent->getSample(chan[c.chan].sample);
|
||||
immWrite(0x12,(s->offB>>8)&0xff);
|
||||
|
|
@ -863,8 +840,9 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
|
|||
immWrite(0x15,end>>16);
|
||||
immWrite(0x11,isMuted[c.chan]?0:(chan[c.chan].pan<<6));
|
||||
immWrite(0x10,(s->loopStart>=0)?0x90:0x80); // start/repeat
|
||||
chan[c.chan].baseFreq=(((unsigned int)s->rate)<<16)/(chipClock/144);
|
||||
chan[c.chan].freqChanged=true;
|
||||
int freq=(65536.0*(double)s->rate)/((double)chipClock/144.0);
|
||||
immWrite(0x19,freq&0xff);
|
||||
immWrite(0x1a,(freq>>8)&0xff);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
@ -928,7 +906,7 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
|
|||
chan[c.chan].insChanged=false;
|
||||
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value);
|
||||
chan[c.chan].baseFreq=NOTE_FNUM_BLOCK(c.value,11);
|
||||
chan[c.chan].portaPause=false;
|
||||
chan[c.chan].freqChanged=true;
|
||||
chan[c.chan].note=c.value;
|
||||
|
|
@ -1020,12 +998,13 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_PITCH: {
|
||||
if (c.chan==13 && !chan[c.chan].furnacePCM) break;
|
||||
chan[c.chan].pitch=c.value;
|
||||
chan[c.chan].freqChanged=true;
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_NOTE_PORTA: {
|
||||
if (c.chan>3) { // PSG, ADPCM-B
|
||||
if (c.chan>3 || parent->song.linearPitch==2) { // PSG, ADPCM-B
|
||||
int destFreq=NOTE_OPNB(c.chan,c.value2);
|
||||
bool return2=false;
|
||||
if (destFreq>chan[c.chan].baseFreq) {
|
||||
|
|
@ -1048,33 +1027,48 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
|
|||
}
|
||||
break;
|
||||
}
|
||||
|
||||
int destFreq=NOTE_FREQUENCY(c.value2);
|
||||
int boundaryBottom=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,0,false);
|
||||
int boundaryTop=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,12,false);
|
||||
int destFreq=NOTE_FNUM_BLOCK(c.value2,11);
|
||||
int newFreq;
|
||||
bool return2=false;
|
||||
if (chan[c.chan].portaPause) {
|
||||
chan[c.chan].baseFreq=chan[c.chan].portaPauseFreq;
|
||||
}
|
||||
if (destFreq>chan[c.chan].baseFreq) {
|
||||
newFreq=chan[c.chan].baseFreq+c.value*octave(chan[c.chan].baseFreq);
|
||||
newFreq=chan[c.chan].baseFreq+c.value;
|
||||
if (newFreq>=destFreq) {
|
||||
newFreq=destFreq;
|
||||
return2=true;
|
||||
}
|
||||
} else {
|
||||
newFreq=chan[c.chan].baseFreq-c.value*octave(chan[c.chan].baseFreq);
|
||||
newFreq=chan[c.chan].baseFreq-c.value;
|
||||
if (newFreq<=destFreq) {
|
||||
newFreq=destFreq;
|
||||
return2=true;
|
||||
}
|
||||
}
|
||||
// check for octave boundary
|
||||
// what the heck!
|
||||
if (!chan[c.chan].portaPause) {
|
||||
if (octave(chan[c.chan].baseFreq)!=octave(newFreq)) {
|
||||
if ((newFreq&0x7ff)>boundaryTop && (newFreq&0xf800)<0x3800) {
|
||||
chan[c.chan].portaPauseFreq=(boundaryBottom)|((newFreq+0x800)&0xf800);
|
||||
chan[c.chan].portaPause=true;
|
||||
break;
|
||||
}
|
||||
if ((newFreq&0x7ff)<boundaryBottom && (newFreq&0xf800)>0) {
|
||||
chan[c.chan].portaPauseFreq=newFreq=(boundaryTop-1)|((newFreq-0x800)&0xf800);
|
||||
chan[c.chan].portaPause=true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
chan[c.chan].baseFreq=newFreq;
|
||||
chan[c.chan].portaPause=false;
|
||||
chan[c.chan].freqChanged=true;
|
||||
if (return2) return 2;
|
||||
chan[c.chan].baseFreq=newFreq;
|
||||
if (return2) {
|
||||
chan[c.chan].inPorta=false;
|
||||
return 2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_SAMPLE_BANK:
|
||||
|
|
@ -1085,6 +1079,7 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
|
|||
iface.sampleBank=sampleBank;
|
||||
break;
|
||||
case DIV_CMD_LEGATO: {
|
||||
if (c.chan==13 && !chan[c.chan].furnacePCM) break;
|
||||
chan[c.chan].baseFreq=NOTE_OPNB(c.chan,c.value);
|
||||
chan[c.chan].freqChanged=true;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ class DivPlatformYM2610: public DivPlatformYM2610Base {
|
|||
struct Channel {
|
||||
DivInstrumentFM state;
|
||||
unsigned char freqH, freqL;
|
||||
int freq, baseFreq, pitch, pitch2, note, ins;
|
||||
int freq, baseFreq, pitch, pitch2, portaPauseFreq, note, ins;
|
||||
unsigned char psgMode, autoEnvNum, autoEnvDen;
|
||||
signed char konCycles;
|
||||
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, inPorta, furnacePCM, hardReset;
|
||||
|
|
@ -80,6 +80,7 @@ class DivPlatformYM2610: public DivPlatformYM2610Base {
|
|||
baseFreq(0),
|
||||
pitch(0),
|
||||
pitch2(0),
|
||||
portaPauseFreq(0),
|
||||
note(0),
|
||||
ins(-1),
|
||||
psgMode(1),
|
||||
|
|
@ -125,8 +126,6 @@ class DivPlatformYM2610: public DivPlatformYM2610Base {
|
|||
short oldWrites[512];
|
||||
short pendingWrites[512];
|
||||
|
||||
int octave(int freq);
|
||||
int toFreq(int freq);
|
||||
double NOTE_OPNB(int ch, int note);
|
||||
double NOTE_ADPCMB(int note);
|
||||
friend void putDispatchChan(void*,int,int);
|
||||
|
|
|
|||
|
|
@ -436,13 +436,13 @@ double DivPlatformYM2610B::NOTE_OPNB(int ch, int note) {
|
|||
return NOTE_PERIODIC(note);
|
||||
}
|
||||
// FM
|
||||
return NOTE_FREQUENCY(note);
|
||||
return NOTE_FNUM_BLOCK(note,11);
|
||||
}
|
||||
|
||||
double DivPlatformYM2610B::NOTE_ADPCMB(int note) {
|
||||
if (chan[15].sample>=0 && chan[15].sample<parent->song.sampleLen) {
|
||||
double off=(double)(parent->getSample(chan[15].sample)->centerRate)/8363.0;
|
||||
return off*parent->calcBaseFreq((double)chipClock/144,65535,note,false);
|
||||
double off=65535.0*(double)(parent->getSample(chan[15].sample)->centerRate)/8363.0;
|
||||
return parent->calcBaseFreq((double)chipClock/144,off,note,false);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -450,7 +450,7 @@ double DivPlatformYM2610B::NOTE_ADPCMB(int note) {
|
|||
void DivPlatformYM2610B::acquire(short* bufL, short* bufR, size_t start, size_t len) {
|
||||
static int os[2];
|
||||
|
||||
ymfm::ym2612::fm_engine* fme=fm->debug_fm_engine();
|
||||
ymfm::ym2610b::fm_engine* fme=fm->debug_fm_engine();
|
||||
ymfm::ssg_engine* ssge=fm->debug_ssg_engine();
|
||||
ymfm::adpcm_a_engine* aae=fm->debug_adpcm_a_engine();
|
||||
ymfm::adpcm_b_engine* abe=fm->debug_adpcm_b_engine();
|
||||
|
|
@ -538,15 +538,15 @@ void DivPlatformYM2610B::tick(bool sysTick) {
|
|||
if (chan[i].std.arp.had) {
|
||||
if (!chan[i].inPorta) {
|
||||
if (chan[i].std.arp.mode) {
|
||||
chan[i].baseFreq=NOTE_FREQUENCY(chan[i].std.arp.val);
|
||||
chan[i].baseFreq=NOTE_FNUM_BLOCK(chan[i].std.arp.val,11);
|
||||
} else {
|
||||
chan[i].baseFreq=NOTE_FREQUENCY(chan[i].note+(signed char)chan[i].std.arp.val);
|
||||
chan[i].baseFreq=NOTE_FNUM_BLOCK(chan[i].note+(signed char)chan[i].std.arp.val,11);
|
||||
}
|
||||
}
|
||||
chan[i].freqChanged=true;
|
||||
} else {
|
||||
if (chan[i].std.arp.mode && chan[i].std.arp.finished) {
|
||||
chan[i].baseFreq=NOTE_FREQUENCY(chan[i].note);
|
||||
chan[i].baseFreq=NOTE_FNUM_BLOCK(chan[i].note,11);
|
||||
chan[i].freqChanged=true;
|
||||
}
|
||||
}
|
||||
|
|
@ -704,7 +704,12 @@ void DivPlatformYM2610B::tick(bool sysTick) {
|
|||
}
|
||||
}
|
||||
if (chan[15].freqChanged) {
|
||||
chan[15].freq=parent->calcFreq(chan[15].baseFreq,chan[15].pitch,false,4);
|
||||
if (chan[15].sample>=0 && chan[15].sample<parent->song.sampleLen) {
|
||||
double off=65535.0*(double)(parent->getSample(chan[15].sample)->centerRate)/8363.0;
|
||||
chan[15].freq=parent->calcFreq(chan[15].baseFreq,chan[15].pitch,false,4,chan[15].pitch2,(double)chipClock/144,off);
|
||||
} else {
|
||||
chan[15].freq=0;
|
||||
}
|
||||
immWrite(0x19,chan[15].freq&0xff);
|
||||
immWrite(0x1a,(chan[15].freq>>8)&0xff);
|
||||
chan[15].freqChanged=false;
|
||||
|
|
@ -720,11 +725,24 @@ void DivPlatformYM2610B::tick(bool sysTick) {
|
|||
for (int i=0; i<6; i++) {
|
||||
if (i==2 && extMode) continue;
|
||||
if (chan[i].freqChanged) {
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,octave(chan[i].baseFreq),chan[i].pitch2);
|
||||
if (chan[i].freq>262143) chan[i].freq=262143;
|
||||
int freqt=toFreq(chan[i].freq)+chan[i].pitch2;
|
||||
immWrite(chanOffs[i]+ADDR_FREQH,freqt>>8);
|
||||
immWrite(chanOffs[i]+ADDR_FREQ,freqt&0xff);
|
||||
if (parent->song.linearPitch==2) {
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,4,chan[i].pitch2,chipClock,CHIP_FREQBASE,11);
|
||||
} else {
|
||||
int fNum=parent->calcFreq(chan[i].baseFreq&0x7ff,chan[i].pitch,false,4,chan[i].pitch2);
|
||||
int block=(chan[i].baseFreq&0xf800)>>11;
|
||||
if (fNum<0) fNum=0;
|
||||
if (fNum>2047) {
|
||||
while (block<7) {
|
||||
fNum>>=1;
|
||||
block++;
|
||||
}
|
||||
if (fNum>2047) fNum=2047;
|
||||
}
|
||||
chan[i].freq=(block<<11)|fNum;
|
||||
}
|
||||
if (chan[i].freq>0x3fff) chan[i].freq=0x3fff;
|
||||
immWrite(chanOffs[i]+ADDR_FREQH,chan[i].freq>>8);
|
||||
immWrite(chanOffs[i]+ADDR_FREQ,chan[i].freq&0xff);
|
||||
chan[i].freqChanged=false;
|
||||
}
|
||||
if (chan[i].keyOn) {
|
||||
|
|
@ -734,47 +752,6 @@ void DivPlatformYM2610B::tick(bool sysTick) {
|
|||
}
|
||||
}
|
||||
|
||||
int DivPlatformYM2610B::octave(int freq) {
|
||||
if (freq>=622.0f*128) {
|
||||
return 128;
|
||||
} else if (freq>=622.0f*64) {
|
||||
return 64;
|
||||
} else if (freq>=622.0f*32) {
|
||||
return 32;
|
||||
} else if (freq>=622.0f*16) {
|
||||
return 16;
|
||||
} else if (freq>=622.0f*8) {
|
||||
return 8;
|
||||
} else if (freq>=622.0f*4) {
|
||||
return 4;
|
||||
} else if (freq>=622.0f*2) {
|
||||
return 2;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int DivPlatformYM2610B::toFreq(int freq) {
|
||||
if (freq>=622.0f*128) {
|
||||
return 0x3800|((freq>>7)&0x7ff);
|
||||
} else if (freq>=622.0f*64) {
|
||||
return 0x3000|((freq>>6)&0x7ff);
|
||||
} else if (freq>=622.0f*32) {
|
||||
return 0x2800|((freq>>5)&0x7ff);
|
||||
} else if (freq>=622.0f*16) {
|
||||
return 0x2000|((freq>>4)&0x7ff);
|
||||
} else if (freq>=622.0f*8) {
|
||||
return 0x1800|((freq>>3)&0x7ff);
|
||||
} else if (freq>=622.0f*4) {
|
||||
return 0x1000|((freq>>2)&0x7ff);
|
||||
} else if (freq>=622.0f*2) {
|
||||
return 0x800|((freq>>1)&0x7ff);
|
||||
} else {
|
||||
return freq&0x7ff;
|
||||
}
|
||||
}
|
||||
|
||||
int DivPlatformYM2610B::dispatch(DivCommand c) {
|
||||
if (c.chan>5 && c.chan<9) {
|
||||
c.chan-=6;
|
||||
|
|
@ -796,7 +773,7 @@ int DivPlatformYM2610B::dispatch(DivCommand c) {
|
|||
chan[c.chan].outVol=chan[c.chan].vol;
|
||||
immWrite(0x1b,chan[c.chan].outVol);
|
||||
}
|
||||
chan[c.chan].sample=ins->amiga.initSample;
|
||||
chan[c.chan].sample=ins->amiga.getSample(c.value);
|
||||
if (chan[c.chan].sample>=0 && chan[c.chan].sample<parent->song.sampleLen) {
|
||||
DivSample* s=parent->getSample(chan[c.chan].sample);
|
||||
immWrite(0x12,(s->offB>>8)&0xff);
|
||||
|
|
@ -841,8 +818,9 @@ int DivPlatformYM2610B::dispatch(DivCommand c) {
|
|||
immWrite(0x15,end>>16);
|
||||
immWrite(0x11,isMuted[c.chan]?0:(chan[c.chan].pan<<6));
|
||||
immWrite(0x10,(s->loopStart>=0)?0x90:0x80); // start/repeat
|
||||
chan[c.chan].baseFreq=(((unsigned int)s->rate)<<16)/(chipClock/144);
|
||||
chan[c.chan].freqChanged=true;
|
||||
int freq=(65536.0*(double)s->rate)/((double)chipClock/144.0);
|
||||
immWrite(0x19,freq&0xff);
|
||||
immWrite(0x1a,(freq>>8)&0xff);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
@ -906,7 +884,7 @@ int DivPlatformYM2610B::dispatch(DivCommand c) {
|
|||
chan[c.chan].insChanged=false;
|
||||
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value);
|
||||
chan[c.chan].baseFreq=NOTE_FNUM_BLOCK(c.value,11);
|
||||
chan[c.chan].portaPause=false;
|
||||
chan[c.chan].freqChanged=true;
|
||||
chan[c.chan].note=c.value;
|
||||
|
|
@ -998,12 +976,13 @@ int DivPlatformYM2610B::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_PITCH: {
|
||||
if (c.chan==15 && !chan[c.chan].furnacePCM) break;
|
||||
chan[c.chan].pitch=c.value;
|
||||
chan[c.chan].freqChanged=true;
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_NOTE_PORTA: {
|
||||
if (c.chan>5) { // PSG, ADPCM-B
|
||||
if (c.chan>5 || parent->song.linearPitch==2) { // PSG, ADPCM-B
|
||||
int destFreq=NOTE_OPNB(c.chan,c.value2);
|
||||
bool return2=false;
|
||||
if (destFreq>chan[c.chan].baseFreq) {
|
||||
|
|
@ -1026,33 +1005,48 @@ int DivPlatformYM2610B::dispatch(DivCommand c) {
|
|||
}
|
||||
break;
|
||||
}
|
||||
|
||||
int destFreq=NOTE_FREQUENCY(c.value2);
|
||||
int boundaryBottom=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,0,false);
|
||||
int boundaryTop=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,12,false);
|
||||
int destFreq=NOTE_FNUM_BLOCK(c.value2,11);
|
||||
int newFreq;
|
||||
bool return2=false;
|
||||
if (chan[c.chan].portaPause) {
|
||||
chan[c.chan].baseFreq=chan[c.chan].portaPauseFreq;
|
||||
}
|
||||
if (destFreq>chan[c.chan].baseFreq) {
|
||||
newFreq=chan[c.chan].baseFreq+c.value*octave(chan[c.chan].baseFreq);
|
||||
newFreq=chan[c.chan].baseFreq+c.value;
|
||||
if (newFreq>=destFreq) {
|
||||
newFreq=destFreq;
|
||||
return2=true;
|
||||
}
|
||||
} else {
|
||||
newFreq=chan[c.chan].baseFreq-c.value*octave(chan[c.chan].baseFreq);
|
||||
newFreq=chan[c.chan].baseFreq-c.value;
|
||||
if (newFreq<=destFreq) {
|
||||
newFreq=destFreq;
|
||||
return2=true;
|
||||
}
|
||||
}
|
||||
// check for octave boundary
|
||||
// what the heck!
|
||||
if (!chan[c.chan].portaPause) {
|
||||
if (octave(chan[c.chan].baseFreq)!=octave(newFreq)) {
|
||||
if ((newFreq&0x7ff)>boundaryTop && (newFreq&0xf800)<0x3800) {
|
||||
chan[c.chan].portaPauseFreq=(boundaryBottom)|((newFreq+0x800)&0xf800);
|
||||
chan[c.chan].portaPause=true;
|
||||
break;
|
||||
}
|
||||
if ((newFreq&0x7ff)<boundaryBottom && (newFreq&0xf800)>0) {
|
||||
chan[c.chan].portaPauseFreq=newFreq=(boundaryTop-1)|((newFreq-0x800)&0xf800);
|
||||
chan[c.chan].portaPause=true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
chan[c.chan].baseFreq=newFreq;
|
||||
chan[c.chan].portaPause=false;
|
||||
chan[c.chan].freqChanged=true;
|
||||
if (return2) return 2;
|
||||
chan[c.chan].baseFreq=newFreq;
|
||||
if (return2) {
|
||||
chan[c.chan].inPorta=false;
|
||||
return 2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_SAMPLE_BANK:
|
||||
|
|
@ -1063,6 +1057,7 @@ int DivPlatformYM2610B::dispatch(DivCommand c) {
|
|||
iface.sampleBank=sampleBank;
|
||||
break;
|
||||
case DIV_CMD_LEGATO: {
|
||||
if (c.chan==15 && !chan[c.chan].furnacePCM) break;
|
||||
chan[c.chan].baseFreq=NOTE_OPNB(c.chan,c.value);
|
||||
chan[c.chan].freqChanged=true;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ class DivPlatformYM2610B: public DivPlatformYM2610Base {
|
|||
struct Channel {
|
||||
DivInstrumentFM state;
|
||||
unsigned char freqH, freqL;
|
||||
int freq, baseFreq, pitch, pitch2, note, ins;
|
||||
int freq, baseFreq, pitch, pitch2, portaPauseFreq, note, ins;
|
||||
unsigned char psgMode, autoEnvNum, autoEnvDen;
|
||||
signed char konCycles;
|
||||
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, inPorta, furnacePCM, hardReset;
|
||||
|
|
@ -54,6 +54,7 @@ class DivPlatformYM2610B: public DivPlatformYM2610Base {
|
|||
baseFreq(0),
|
||||
pitch(0),
|
||||
pitch2(0),
|
||||
portaPauseFreq(0),
|
||||
note(0),
|
||||
ins(-1),
|
||||
psgMode(1),
|
||||
|
|
@ -98,8 +99,6 @@ class DivPlatformYM2610B: public DivPlatformYM2610Base {
|
|||
short oldWrites[512];
|
||||
short pendingWrites[512];
|
||||
|
||||
int octave(int freq);
|
||||
int toFreq(int freq);
|
||||
double NOTE_OPNB(int ch, int note);
|
||||
double NOTE_ADPCMB(int note);
|
||||
friend void putDispatchChan(void*,int,int);
|
||||
|
|
|
|||
|
|
@ -120,31 +120,74 @@ int DivPlatformYM2610BExt::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_NOTE_PORTA: {
|
||||
int destFreq=NOTE_FREQUENCY(c.value2);
|
||||
if (parent->song.linearPitch==2) {
|
||||
int destFreq=NOTE_FREQUENCY(c.value2);
|
||||
bool return2=false;
|
||||
if (destFreq>opChan[ch].baseFreq) {
|
||||
opChan[ch].baseFreq+=c.value;
|
||||
if (opChan[ch].baseFreq>=destFreq) {
|
||||
opChan[ch].baseFreq=destFreq;
|
||||
return2=true;
|
||||
}
|
||||
} else {
|
||||
opChan[ch].baseFreq-=c.value;
|
||||
if (opChan[ch].baseFreq<=destFreq) {
|
||||
opChan[ch].baseFreq=destFreq;
|
||||
return2=true;
|
||||
}
|
||||
}
|
||||
opChan[ch].freqChanged=true;
|
||||
if (return2) {
|
||||
//opChan[ch].inPorta=false;
|
||||
return 2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
int boundaryBottom=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,0,false);
|
||||
int boundaryTop=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,12,false);
|
||||
int destFreq=NOTE_FNUM_BLOCK(c.value2,11);
|
||||
int newFreq;
|
||||
bool return2=false;
|
||||
if (opChan[ch].portaPause) {
|
||||
opChan[ch].baseFreq=opChan[ch].portaPauseFreq;
|
||||
}
|
||||
if (destFreq>opChan[ch].baseFreq) {
|
||||
newFreq=opChan[ch].baseFreq+c.value*octave(opChan[ch].baseFreq);
|
||||
newFreq=opChan[ch].baseFreq+c.value;
|
||||
if (newFreq>=destFreq) {
|
||||
newFreq=destFreq;
|
||||
return2=true;
|
||||
}
|
||||
} else {
|
||||
newFreq=opChan[ch].baseFreq-c.value*octave(opChan[ch].baseFreq);
|
||||
newFreq=opChan[ch].baseFreq-c.value;
|
||||
if (newFreq<=destFreq) {
|
||||
newFreq=destFreq;
|
||||
return2=true;
|
||||
}
|
||||
}
|
||||
// what the heck!
|
||||
if (!opChan[ch].portaPause) {
|
||||
if (octave(opChan[ch].baseFreq)!=octave(newFreq)) {
|
||||
opChan[ch].portaPause=true;
|
||||
break;
|
||||
if ((newFreq&0x7ff)>boundaryTop && (newFreq&0xf800)<0x3800) {
|
||||
if (parent->song.fbPortaPause) {
|
||||
opChan[ch].portaPauseFreq=(boundaryBottom)|((newFreq+0x800)&0xf800);
|
||||
opChan[ch].portaPause=true;
|
||||
break;
|
||||
} else {
|
||||
newFreq=(newFreq>>1)|((newFreq+0x800)&0xf800);
|
||||
}
|
||||
}
|
||||
if ((newFreq&0x7ff)<boundaryBottom && (newFreq&0xf800)>0) {
|
||||
if (parent->song.fbPortaPause) {
|
||||
opChan[ch].portaPauseFreq=newFreq=(boundaryTop-1)|((newFreq-0x800)&0xf800);
|
||||
opChan[ch].portaPause=true;
|
||||
break;
|
||||
} else {
|
||||
newFreq=(newFreq<<1)|((newFreq-0x800)&0xf800);
|
||||
}
|
||||
}
|
||||
}
|
||||
opChan[ch].baseFreq=newFreq;
|
||||
opChan[ch].portaPause=false;
|
||||
opChan[ch].freqChanged=true;
|
||||
opChan[ch].baseFreq=newFreq;
|
||||
if (return2) return 2;
|
||||
break;
|
||||
}
|
||||
|
|
@ -364,14 +407,24 @@ void DivPlatformYM2610BExt::tick(bool sysTick) {
|
|||
unsigned char writeMask=2;
|
||||
if (extMode) for (int i=0; i<4; i++) {
|
||||
if (opChan[i].freqChanged) {
|
||||
opChan[i].freq=parent->calcFreq(opChan[i].baseFreq,opChan[i].pitch,false,octave(opChan[i].baseFreq),opChan[i].pitch2);
|
||||
if (opChan[i].freq>262143) opChan[i].freq=262143;
|
||||
int freqt=toFreq(opChan[i].freq);
|
||||
opChan[i].freqH=freqt>>8;
|
||||
opChan[i].freqL=freqt&0xff;
|
||||
immWrite(opChanOffsH[i],opChan[i].freqH);
|
||||
immWrite(opChanOffsL[i],opChan[i].freqL);
|
||||
opChan[i].freqChanged=false;
|
||||
if (parent->song.linearPitch==2) {
|
||||
opChan[i].freq=parent->calcFreq(opChan[i].baseFreq,opChan[i].pitch,false,4,opChan[i].pitch2,chipClock,CHIP_FREQBASE,11);
|
||||
} else {
|
||||
int fNum=parent->calcFreq(opChan[i].baseFreq&0x7ff,opChan[i].pitch,false,4,opChan[i].pitch2);
|
||||
int block=(opChan[i].baseFreq&0xf800)>>11;
|
||||
if (fNum<0) fNum=0;
|
||||
if (fNum>2047) {
|
||||
while (block<7) {
|
||||
fNum>>=1;
|
||||
block++;
|
||||
}
|
||||
if (fNum>2047) fNum=2047;
|
||||
}
|
||||
opChan[i].freq=(block<<11)|fNum;
|
||||
}
|
||||
if (opChan[i].freq>0x3fff) opChan[i].freq=0x3fff;
|
||||
immWrite(opChanOffsH[i],opChan[i].freq>>8);
|
||||
immWrite(opChanOffsL[i],opChan[i].freq&0xff);
|
||||
}
|
||||
writeMask|=opChan[i].active<<(4+i);
|
||||
if (opChan[i].keyOn) {
|
||||
|
|
|
|||
|
|
@ -25,12 +25,12 @@ class DivPlatformYM2610BExt: public DivPlatformYM2610B {
|
|||
struct OpChannel {
|
||||
DivMacroInt std;
|
||||
unsigned char freqH, freqL;
|
||||
int freq, baseFreq, pitch, pitch2, ins;
|
||||
int freq, baseFreq, pitch, pitch2, portaPauseFreq, ins;
|
||||
signed char konCycles;
|
||||
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause;
|
||||
int vol;
|
||||
unsigned char pan;
|
||||
OpChannel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), pitch2(0), ins(-1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), portaPause(false), vol(0), pan(3) {}
|
||||
OpChannel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), pitch2(0), portaPauseFreq(0), ins(-1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), portaPause(false), vol(0), pan(3) {}
|
||||
};
|
||||
OpChannel opChan[4];
|
||||
bool isOpMuted[4];
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) {
|
|||
opChan[ch].insChanged=false;
|
||||
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
opChan[ch].baseFreq=NOTE_FREQUENCY(c.value);
|
||||
opChan[ch].baseFreq=NOTE_FNUM_BLOCK(c.value,11);
|
||||
opChan[ch].portaPause=false;
|
||||
opChan[ch].freqChanged=true;
|
||||
}
|
||||
|
|
@ -120,36 +120,79 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_NOTE_PORTA: {
|
||||
int destFreq=NOTE_FREQUENCY(c.value2);
|
||||
if (parent->song.linearPitch==2) {
|
||||
int destFreq=NOTE_FREQUENCY(c.value2);
|
||||
bool return2=false;
|
||||
if (destFreq>opChan[ch].baseFreq) {
|
||||
opChan[ch].baseFreq+=c.value;
|
||||
if (opChan[ch].baseFreq>=destFreq) {
|
||||
opChan[ch].baseFreq=destFreq;
|
||||
return2=true;
|
||||
}
|
||||
} else {
|
||||
opChan[ch].baseFreq-=c.value;
|
||||
if (opChan[ch].baseFreq<=destFreq) {
|
||||
opChan[ch].baseFreq=destFreq;
|
||||
return2=true;
|
||||
}
|
||||
}
|
||||
opChan[ch].freqChanged=true;
|
||||
if (return2) {
|
||||
//opChan[ch].inPorta=false;
|
||||
return 2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
int boundaryBottom=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,0,false);
|
||||
int boundaryTop=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,12,false);
|
||||
int destFreq=NOTE_FNUM_BLOCK(c.value2,11);
|
||||
int newFreq;
|
||||
bool return2=false;
|
||||
if (opChan[ch].portaPause) {
|
||||
opChan[ch].baseFreq=opChan[ch].portaPauseFreq;
|
||||
}
|
||||
if (destFreq>opChan[ch].baseFreq) {
|
||||
newFreq=opChan[ch].baseFreq+c.value*octave(opChan[ch].baseFreq);
|
||||
newFreq=opChan[ch].baseFreq+c.value;
|
||||
if (newFreq>=destFreq) {
|
||||
newFreq=destFreq;
|
||||
return2=true;
|
||||
}
|
||||
} else {
|
||||
newFreq=opChan[ch].baseFreq-c.value*octave(opChan[ch].baseFreq);
|
||||
newFreq=opChan[ch].baseFreq-c.value;
|
||||
if (newFreq<=destFreq) {
|
||||
newFreq=destFreq;
|
||||
return2=true;
|
||||
}
|
||||
}
|
||||
// what the heck!
|
||||
if (!opChan[ch].portaPause) {
|
||||
if (octave(opChan[ch].baseFreq)!=octave(newFreq)) {
|
||||
opChan[ch].portaPause=true;
|
||||
break;
|
||||
if ((newFreq&0x7ff)>boundaryTop && (newFreq&0xf800)<0x3800) {
|
||||
if (parent->song.fbPortaPause) {
|
||||
opChan[ch].portaPauseFreq=(boundaryBottom)|((newFreq+0x800)&0xf800);
|
||||
opChan[ch].portaPause=true;
|
||||
break;
|
||||
} else {
|
||||
newFreq=(newFreq>>1)|((newFreq+0x800)&0xf800);
|
||||
}
|
||||
}
|
||||
if ((newFreq&0x7ff)<boundaryBottom && (newFreq&0xf800)>0) {
|
||||
if (parent->song.fbPortaPause) {
|
||||
opChan[ch].portaPauseFreq=newFreq=(boundaryTop-1)|((newFreq-0x800)&0xf800);
|
||||
opChan[ch].portaPause=true;
|
||||
break;
|
||||
} else {
|
||||
newFreq=(newFreq<<1)|((newFreq-0x800)&0xf800);
|
||||
}
|
||||
}
|
||||
}
|
||||
opChan[ch].baseFreq=newFreq;
|
||||
opChan[ch].portaPause=false;
|
||||
opChan[ch].freqChanged=true;
|
||||
opChan[ch].baseFreq=newFreq;
|
||||
if (return2) return 2;
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_LEGATO: {
|
||||
opChan[ch].baseFreq=NOTE_FREQUENCY(c.value);
|
||||
opChan[ch].baseFreq=NOTE_FNUM_BLOCK(c.value,11);
|
||||
opChan[ch].freqChanged=true;
|
||||
break;
|
||||
}
|
||||
|
|
@ -364,14 +407,24 @@ void DivPlatformYM2610Ext::tick(bool sysTick) {
|
|||
unsigned char writeMask=2;
|
||||
if (extMode) for (int i=0; i<4; i++) {
|
||||
if (opChan[i].freqChanged) {
|
||||
opChan[i].freq=parent->calcFreq(opChan[i].baseFreq,opChan[i].pitch,false,octave(opChan[i].baseFreq),opChan[i].pitch2);
|
||||
if (opChan[i].freq>262143) opChan[i].freq=262143;
|
||||
int freqt=toFreq(opChan[i].freq);
|
||||
opChan[i].freqH=freqt>>8;
|
||||
opChan[i].freqL=freqt&0xff;
|
||||
immWrite(opChanOffsH[i],opChan[i].freqH);
|
||||
immWrite(opChanOffsL[i],opChan[i].freqL);
|
||||
opChan[i].freqChanged=false;
|
||||
if (parent->song.linearPitch==2) {
|
||||
opChan[i].freq=parent->calcFreq(opChan[i].baseFreq,opChan[i].pitch,false,4,opChan[i].pitch2,chipClock,CHIP_FREQBASE,11);
|
||||
} else {
|
||||
int fNum=parent->calcFreq(opChan[i].baseFreq&0x7ff,opChan[i].pitch,false,4,opChan[i].pitch2);
|
||||
int block=(opChan[i].baseFreq&0xf800)>>11;
|
||||
if (fNum<0) fNum=0;
|
||||
if (fNum>2047) {
|
||||
while (block<7) {
|
||||
fNum>>=1;
|
||||
block++;
|
||||
}
|
||||
if (fNum>2047) fNum=2047;
|
||||
}
|
||||
opChan[i].freq=(block<<11)|fNum;
|
||||
}
|
||||
if (opChan[i].freq>0x3fff) opChan[i].freq=0x3fff;
|
||||
immWrite(opChanOffsH[i],opChan[i].freq>>8);
|
||||
immWrite(opChanOffsL[i],opChan[i].freq&0xff);
|
||||
}
|
||||
writeMask|=opChan[i].active<<(4+i);
|
||||
if (opChan[i].keyOn) {
|
||||
|
|
|
|||
|
|
@ -25,12 +25,12 @@ class DivPlatformYM2610Ext: public DivPlatformYM2610 {
|
|||
struct OpChannel {
|
||||
DivMacroInt std;
|
||||
unsigned char freqH, freqL;
|
||||
int freq, baseFreq, pitch, pitch2, ins;
|
||||
int freq, baseFreq, pitch, pitch2, portaPauseFreq, ins;
|
||||
signed char konCycles;
|
||||
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause;
|
||||
int vol;
|
||||
unsigned char pan;
|
||||
OpChannel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), pitch2(0), ins(-1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), portaPause(false), vol(0), pan(3) {}
|
||||
OpChannel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), pitch2(0), portaPauseFreq(0), ins(-1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), portaPause(false), vol(0), pan(3) {}
|
||||
};
|
||||
OpChannel opChan[4];
|
||||
bool isOpMuted[4];
|
||||
|
|
|
|||
334
src/engine/platform/zxbeeper.cpp
Normal file
334
src/engine/platform/zxbeeper.cpp
Normal file
|
|
@ -0,0 +1,334 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "zxbeeper.h"
|
||||
#include "../engine.h"
|
||||
#include <math.h>
|
||||
|
||||
#define CHIP_FREQBASE 8192*6
|
||||
|
||||
const char** DivPlatformZXBeeper::getRegisterSheet() {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char* DivPlatformZXBeeper::getEffectName(unsigned char effect) {
|
||||
switch (effect) {
|
||||
case 0x10:
|
||||
return "10xx: Change waveform";
|
||||
break;
|
||||
case 0x11:
|
||||
return "11xx: Toggle noise mode";
|
||||
break;
|
||||
case 0x12:
|
||||
return "12xx: Setup LFO (0: disabled; 1: 1x depth; 2: 16x depth; 3: 256x depth)";
|
||||
break;
|
||||
case 0x13:
|
||||
return "13xx: Set LFO speed";
|
||||
break;
|
||||
case 0x17:
|
||||
return "17xx: Toggle PCM mode";
|
||||
break;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void DivPlatformZXBeeper::acquire(short* bufL, short* bufR, size_t start, size_t len) {
|
||||
for (size_t h=start; h<start+len; h++) {
|
||||
// clock here
|
||||
bool o=false;
|
||||
unsigned short oldPos=chan[curChan].sPosition;
|
||||
|
||||
if (sOffTimer) {
|
||||
sOffTimer--;
|
||||
o=true;
|
||||
}
|
||||
chan[curChan].sPosition+=chan[curChan].freq;
|
||||
if (oldPos>chan[curChan].sPosition) {
|
||||
if (!isMuted[curChan] && chan[curChan].outVol) sOffTimer+=chan[curChan].duty;
|
||||
}
|
||||
if (++curChan>=6) curChan=0;
|
||||
|
||||
bufL[h]=o?16384:0;
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformZXBeeper::tick(bool sysTick) {
|
||||
for (int i=0; i<6; i++) {
|
||||
chan[i].std.next();
|
||||
if (chan[i].std.vol.had) {
|
||||
chan[i].outVol=((chan[i].vol&1)*MIN(1,chan[i].std.vol.val));
|
||||
}
|
||||
if (chan[i].std.duty.had) {
|
||||
chan[i].duty=chan[i].std.duty.val;
|
||||
chan[i].freqChanged=true;
|
||||
}
|
||||
if (chan[i].std.arp.had) {
|
||||
if (!chan[i].inPorta) {
|
||||
if (chan[i].std.arp.mode) {
|
||||
chan[i].baseFreq=NOTE_FREQUENCY(chan[i].std.arp.val);
|
||||
} else {
|
||||
chan[i].baseFreq=NOTE_FREQUENCY(chan[i].note+chan[i].std.arp.val);
|
||||
}
|
||||
}
|
||||
chan[i].freqChanged=true;
|
||||
} else {
|
||||
if (chan[i].std.arp.mode && chan[i].std.arp.finished) {
|
||||
chan[i].baseFreq=NOTE_FREQUENCY(chan[i].note);
|
||||
chan[i].freqChanged=true;
|
||||
}
|
||||
}
|
||||
if (chan[i].std.pitch.had) {
|
||||
if (chan[i].std.pitch.mode) {
|
||||
chan[i].pitch2+=chan[i].std.pitch.val;
|
||||
CLAMP_VAR(chan[i].pitch2,-2048,2048);
|
||||
} else {
|
||||
chan[i].pitch2=chan[i].std.pitch.val;
|
||||
}
|
||||
chan[i].freqChanged=true;
|
||||
}
|
||||
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
|
||||
if (chan[i].active) {
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,0,chan[i].pitch2,chipClock,CHIP_FREQBASE);
|
||||
if (chan[i].freq>65535) chan[i].freq=65535;
|
||||
}
|
||||
if (chan[i].keyOn) {
|
||||
//rWrite(16+i*5,0x80);
|
||||
//chWrite(i,0x04,0x80|chan[i].vol);
|
||||
}
|
||||
if (chan[i].keyOff) {
|
||||
chan[i].freq=0;
|
||||
}
|
||||
if (chan[i].keyOn) chan[i].keyOn=false;
|
||||
if (chan[i].keyOff) chan[i].keyOff=false;
|
||||
chan[i].freqChanged=false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int DivPlatformZXBeeper::dispatch(DivCommand c) {
|
||||
switch (c.cmd) {
|
||||
case DIV_CMD_NOTE_ON: {
|
||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_BEEPER);
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value);
|
||||
chan[c.chan].freqChanged=true;
|
||||
chan[c.chan].note=c.value;
|
||||
}
|
||||
chan[c.chan].active=true;
|
||||
chan[c.chan].keyOn=true;
|
||||
chan[c.chan].macroInit(ins);
|
||||
chan[c.chan].insChanged=false;
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_NOTE_OFF:
|
||||
chan[c.chan].dacSample=-1;
|
||||
if (dumpWrites) addWrite(0xffff0002+(c.chan<<8),0);
|
||||
chan[c.chan].pcm=false;
|
||||
chan[c.chan].active=false;
|
||||
chan[c.chan].keyOff=true;
|
||||
chan[c.chan].macroInit(NULL);
|
||||
break;
|
||||
case DIV_CMD_NOTE_OFF_ENV:
|
||||
case DIV_CMD_ENV_RELEASE:
|
||||
chan[c.chan].std.release();
|
||||
break;
|
||||
case DIV_CMD_INSTRUMENT:
|
||||
if (chan[c.chan].ins!=c.value || c.value2==1) {
|
||||
chan[c.chan].ins=c.value;
|
||||
chan[c.chan].insChanged=true;
|
||||
}
|
||||
break;
|
||||
case DIV_CMD_VOLUME:
|
||||
if (chan[c.chan].vol!=c.value) {
|
||||
chan[c.chan].vol=c.value;
|
||||
if (!chan[c.chan].std.vol.has) {
|
||||
chan[c.chan].outVol=c.value;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DIV_CMD_GET_VOLUME:
|
||||
if (chan[c.chan].std.vol.has) {
|
||||
return chan[c.chan].vol;
|
||||
}
|
||||
return chan[c.chan].outVol;
|
||||
break;
|
||||
case DIV_CMD_PITCH:
|
||||
chan[c.chan].pitch=c.value;
|
||||
chan[c.chan].freqChanged=true;
|
||||
break;
|
||||
case DIV_CMD_NOTE_PORTA: {
|
||||
int destFreq=NOTE_FREQUENCY(c.value2);
|
||||
bool return2=false;
|
||||
if (destFreq>chan[c.chan].baseFreq) {
|
||||
chan[c.chan].baseFreq+=c.value;
|
||||
if (chan[c.chan].baseFreq>=destFreq) {
|
||||
chan[c.chan].baseFreq=destFreq;
|
||||
return2=true;
|
||||
}
|
||||
} else {
|
||||
chan[c.chan].baseFreq-=c.value;
|
||||
if (chan[c.chan].baseFreq<=destFreq) {
|
||||
chan[c.chan].baseFreq=destFreq;
|
||||
return2=true;
|
||||
}
|
||||
}
|
||||
chan[c.chan].freqChanged=true;
|
||||
if (return2) {
|
||||
chan[c.chan].inPorta=false;
|
||||
return 2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_STD_NOISE_MODE:
|
||||
chan[c.chan].duty=c.value;
|
||||
break;
|
||||
case DIV_CMD_SAMPLE_MODE:
|
||||
chan[c.chan].pcm=c.value;
|
||||
break;
|
||||
case DIV_CMD_SAMPLE_BANK:
|
||||
sampleBank=c.value;
|
||||
if (sampleBank>(parent->song.sample.size()/12)) {
|
||||
sampleBank=parent->song.sample.size()/12;
|
||||
}
|
||||
break;
|
||||
case DIV_CMD_LEGATO:
|
||||
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value+((chan[c.chan].std.arp.will && !chan[c.chan].std.arp.mode)?(chan[c.chan].std.arp.val):(0)));
|
||||
chan[c.chan].freqChanged=true;
|
||||
chan[c.chan].note=c.value;
|
||||
break;
|
||||
case DIV_CMD_PRE_PORTA:
|
||||
if (chan[c.chan].active && c.value2) {
|
||||
if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_BEEPER));
|
||||
}
|
||||
chan[c.chan].inPorta=c.value;
|
||||
break;
|
||||
case DIV_CMD_GET_VOLMAX:
|
||||
return 1;
|
||||
break;
|
||||
case DIV_ALWAYS_SET_VOLUME:
|
||||
return 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void DivPlatformZXBeeper::muteChannel(int ch, bool mute) {
|
||||
isMuted[ch]=mute;
|
||||
}
|
||||
|
||||
void DivPlatformZXBeeper::forceIns() {
|
||||
for (int i=0; i<6; i++) {
|
||||
chan[i].insChanged=true;
|
||||
chan[i].freqChanged=true;
|
||||
}
|
||||
}
|
||||
|
||||
void* DivPlatformZXBeeper::getChanState(int ch) {
|
||||
return &chan[ch];
|
||||
}
|
||||
|
||||
DivDispatchOscBuffer* DivPlatformZXBeeper::getOscBuffer(int ch) {
|
||||
return oscBuf[ch];
|
||||
}
|
||||
|
||||
unsigned char* DivPlatformZXBeeper::getRegisterPool() {
|
||||
ulaOut=sOffTimer?0x10:0x08;
|
||||
return &ulaOut;
|
||||
}
|
||||
|
||||
int DivPlatformZXBeeper::getRegisterPoolSize() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
void DivPlatformZXBeeper::reset() {
|
||||
while (!writes.empty()) writes.pop();
|
||||
memset(regPool,0,128);
|
||||
for (int i=0; i<6; i++) {
|
||||
chan[i]=DivPlatformZXBeeper::Channel();
|
||||
chan[i].std.setEngine(parent);
|
||||
}
|
||||
if (dumpWrites) {
|
||||
addWrite(0xffffffff,0);
|
||||
}
|
||||
lastPan=0xff;
|
||||
memset(tempL,0,32*sizeof(int));
|
||||
memset(tempR,0,32*sizeof(int));
|
||||
cycles=0;
|
||||
curChan=0;
|
||||
sOffTimer=0;
|
||||
sampleBank=0;
|
||||
ulaOut=0;
|
||||
}
|
||||
|
||||
bool DivPlatformZXBeeper::keyOffAffectsArp(int ch) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void DivPlatformZXBeeper::notifyWaveChange(int wave) {
|
||||
}
|
||||
|
||||
void DivPlatformZXBeeper::notifyInsDeletion(void* ins) {
|
||||
for (int i=0; i<6; i++) {
|
||||
chan[i].std.notifyInsDeletion((DivInstrument*)ins);
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformZXBeeper::setFlags(unsigned int flags) {
|
||||
if (flags&1) { // technically there is no PAL PC Engine but oh well...
|
||||
chipClock=COLOR_PAL*4.0/5.0;
|
||||
} else {
|
||||
chipClock=COLOR_NTSC;
|
||||
}
|
||||
rate=chipClock/4;
|
||||
for (int i=0; i<6; i++) {
|
||||
oscBuf[i]->rate=rate;
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformZXBeeper::poke(unsigned int addr, unsigned short val) {
|
||||
|
||||
}
|
||||
|
||||
void DivPlatformZXBeeper::poke(std::vector<DivRegWrite>& wlist) {
|
||||
|
||||
}
|
||||
|
||||
int DivPlatformZXBeeper::init(DivEngine* p, int channels, int sugRate, unsigned int flags) {
|
||||
parent=p;
|
||||
dumpWrites=false;
|
||||
skipRegisterWrites=false;
|
||||
for (int i=0; i<6; i++) {
|
||||
isMuted[i]=false;
|
||||
oscBuf[i]=new DivDispatchOscBuffer;
|
||||
}
|
||||
setFlags(flags);
|
||||
reset();
|
||||
return 6;
|
||||
}
|
||||
|
||||
void DivPlatformZXBeeper::quit() {
|
||||
for (int i=0; i<6; i++) {
|
||||
delete oscBuf[i];
|
||||
}
|
||||
}
|
||||
|
||||
DivPlatformZXBeeper::~DivPlatformZXBeeper() {
|
||||
}
|
||||
108
src/engine/platform/zxbeeper.h
Normal file
108
src/engine/platform/zxbeeper.h
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef _ZXBEEPER_H
|
||||
#define _ZXBEEPER_H
|
||||
|
||||
#include "../dispatch.h"
|
||||
#include <queue>
|
||||
#include "../macroInt.h"
|
||||
|
||||
class DivPlatformZXBeeper: public DivDispatch {
|
||||
struct Channel {
|
||||
int freq, baseFreq, pitch, pitch2, note;
|
||||
int dacPeriod, dacRate;
|
||||
unsigned int dacPos;
|
||||
int dacSample, ins;
|
||||
bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, noise, pcm, furnaceDac;
|
||||
signed char vol, outVol;
|
||||
unsigned short sPosition;
|
||||
unsigned char duty;
|
||||
DivMacroInt std;
|
||||
void macroInit(DivInstrument* which) {
|
||||
std.init(which);
|
||||
pitch2=0;
|
||||
}
|
||||
Channel():
|
||||
freq(0),
|
||||
baseFreq(0),
|
||||
pitch(0),
|
||||
pitch2(0),
|
||||
note(0),
|
||||
dacPeriod(0),
|
||||
dacRate(0),
|
||||
dacPos(0),
|
||||
dacSample(-1),
|
||||
ins(-1),
|
||||
active(false),
|
||||
insChanged(true),
|
||||
freqChanged(false),
|
||||
keyOn(false),
|
||||
keyOff(false),
|
||||
inPorta(false),
|
||||
noise(false),
|
||||
pcm(false),
|
||||
furnaceDac(false),
|
||||
vol(1),
|
||||
outVol(1),
|
||||
sPosition(0),
|
||||
duty(64) {}
|
||||
};
|
||||
Channel chan[6];
|
||||
DivDispatchOscBuffer* oscBuf[6];
|
||||
bool isMuted[6];
|
||||
struct QueuedWrite {
|
||||
unsigned char addr;
|
||||
unsigned char val;
|
||||
QueuedWrite(unsigned char a, unsigned char v): addr(a), val(v) {}
|
||||
};
|
||||
std::queue<QueuedWrite> writes;
|
||||
unsigned char lastPan, ulaOut;
|
||||
|
||||
int cycles, curChan, sOffTimer, delay;
|
||||
int tempL[32];
|
||||
int tempR[32];
|
||||
unsigned char sampleBank, lfoMode, lfoSpeed;
|
||||
unsigned char regPool[128];
|
||||
friend void putDispatchChan(void*,int,int);
|
||||
public:
|
||||
void acquire(short* bufL, short* bufR, size_t start, size_t len);
|
||||
int dispatch(DivCommand c);
|
||||
void* getChanState(int chan);
|
||||
DivDispatchOscBuffer* getOscBuffer(int chan);
|
||||
unsigned char* getRegisterPool();
|
||||
int getRegisterPoolSize();
|
||||
void reset();
|
||||
void forceIns();
|
||||
void tick(bool sysTick=true);
|
||||
void muteChannel(int ch, bool mute);
|
||||
bool keyOffAffectsArp(int ch);
|
||||
void setFlags(unsigned int flags);
|
||||
void notifyWaveChange(int wave);
|
||||
void notifyInsDeletion(void* ins);
|
||||
void poke(unsigned int addr, unsigned short val);
|
||||
void poke(std::vector<DivRegWrite>& wlist);
|
||||
const char** getRegisterSheet();
|
||||
const char* getEffectName(unsigned char effect);
|
||||
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
|
||||
void quit();
|
||||
~DivPlatformZXBeeper();
|
||||
};
|
||||
|
||||
#endif
|
||||
Loading…
Add table
Add a link
Reference in a new issue