Merge branch 'master' of https://github.com/tildearrow/furnace into scc
This commit is contained in:
commit
2c6267bd6b
47 changed files with 3473 additions and 205 deletions
|
|
@ -37,8 +37,8 @@
|
|||
warnings+=(String("\n")+x); \
|
||||
}
|
||||
|
||||
#define DIV_VERSION "dev65"
|
||||
#define DIV_ENGINE_VERSION 65
|
||||
#define DIV_VERSION "dev66"
|
||||
#define DIV_ENGINE_VERSION 66
|
||||
|
||||
enum DivStatusView {
|
||||
DIV_STATUS_NOTHING=0,
|
||||
|
|
@ -69,7 +69,7 @@ enum DivHaltPositions {
|
|||
|
||||
struct DivChannelState {
|
||||
std::vector<DivDelayedCommand> delayed;
|
||||
int note, oldNote, pitch, portaSpeed, portaNote;
|
||||
int note, oldNote, lastIns, pitch, portaSpeed, portaNote;
|
||||
int volume, volSpeed, cut, rowDelay, volMax;
|
||||
int delayOrder, delayRow, retrigSpeed, retrigTick;
|
||||
int vibratoDepth, vibratoRate, vibratoPos, vibratoDir, vibratoFine;
|
||||
|
|
@ -80,6 +80,7 @@ struct DivChannelState {
|
|||
DivChannelState():
|
||||
note(-1),
|
||||
oldNote(-1),
|
||||
lastIns(-1),
|
||||
pitch(0),
|
||||
portaSpeed(-1),
|
||||
portaNote(-1),
|
||||
|
|
|
|||
|
|
@ -142,6 +142,7 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
|
|||
ds.ignoreDuplicateSlides=true;
|
||||
ds.brokenDACMode=true;
|
||||
ds.oneTickCut=false;
|
||||
ds.newInsTriggersInPorta=true;
|
||||
|
||||
// 1.1 compat flags
|
||||
if (ds.version>24) {
|
||||
|
|
@ -807,6 +808,9 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
if (ds.version<65) {
|
||||
ds.oneTickCut=false;
|
||||
}
|
||||
if (ds.version<66) {
|
||||
ds.newInsTriggersInPorta=false;
|
||||
}
|
||||
ds.isDMF=false;
|
||||
|
||||
reader.readS(); // reserved
|
||||
|
|
@ -993,7 +997,12 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
} else {
|
||||
reader.readC();
|
||||
}
|
||||
for (int i=0; i<2; i++) reader.readC();
|
||||
if (ds.version>=66) {
|
||||
ds.newInsTriggersInPorta=reader.readC();
|
||||
} else {
|
||||
reader.readC();
|
||||
}
|
||||
for (int i=0; i<1; i++) reader.readC();
|
||||
} else {
|
||||
for (int i=0; i<20; i++) reader.readC();
|
||||
}
|
||||
|
|
@ -1437,7 +1446,8 @@ SafeWriter* DivEngine::saveFur() {
|
|||
w->writeC(song.continuousVibrato);
|
||||
w->writeC(song.brokenDACMode);
|
||||
w->writeC(song.oneTickCut);
|
||||
for (int i=0; i<2; i++) {
|
||||
w->writeC(song.newInsTriggersInPorta);
|
||||
for (int i=0; i<1; i++) {
|
||||
w->writeC(0);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@
|
|||
#include <math.h>
|
||||
|
||||
#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 immWrite(a,v) if (!skipRegisterWrites) {writes.emplace(regRemap(a),v); if (dumpWrites) {addWrite(regRemap(a),v);} }
|
||||
|
||||
#define CHIP_DIVIDER 8
|
||||
|
||||
|
|
@ -48,8 +48,28 @@ const char* regCheatSheetAY[]={
|
|||
NULL
|
||||
};
|
||||
|
||||
const char* regCheatSheetAY8914[]={
|
||||
"FreqL_A", "0",
|
||||
"FreqL_B", "1",
|
||||
"FreqL_C", "2",
|
||||
"FreqL_Env", "3",
|
||||
"FreqH_A", "4",
|
||||
"FreqH_B", "5",
|
||||
"FreqH_C", "6",
|
||||
"FreqH_Env", "7",
|
||||
"Enable", "8",
|
||||
"FreqNoise", "9",
|
||||
"Control_Env", "A",
|
||||
"Volume_A", "B",
|
||||
"Volume_B", "C",
|
||||
"Volume_C", "D",
|
||||
"PortA", "E",
|
||||
"PortB", "F",
|
||||
NULL
|
||||
};
|
||||
|
||||
const char** DivPlatformAY8910::getRegisterSheet() {
|
||||
return regCheatSheetAY;
|
||||
return intellivision?regCheatSheetAY8914:regCheatSheetAY;
|
||||
}
|
||||
|
||||
const char* DivPlatformAY8910::getEffectName(unsigned char effect) {
|
||||
|
|
@ -92,8 +112,13 @@ void DivPlatformAY8910::acquire(short* bufL, short* bufR, size_t start, size_t l
|
|||
}
|
||||
while (!writes.empty()) {
|
||||
QueuedWrite w=writes.front();
|
||||
ay->address_w(w.addr);
|
||||
ay->data_w(w.val);
|
||||
if (intellivision) {
|
||||
ay8914_device* ay8914=(ay8914_device*)ay;
|
||||
ay8914->write(w.addr,w.val);
|
||||
} else {
|
||||
ay->address_w(w.addr);
|
||||
ay->data_w(w.val);
|
||||
}
|
||||
regPool[w.addr&0x0f]=w.val;
|
||||
writes.pop();
|
||||
}
|
||||
|
|
@ -125,6 +150,8 @@ void DivPlatformAY8910::tick() {
|
|||
if (chan[i].outVol<0) chan[i].outVol=0;
|
||||
if (isMuted[i]) {
|
||||
rWrite(0x08+i,0);
|
||||
} else if (intellivision && (chan[i].psgMode&4)) {
|
||||
rWrite(0x08+i,(chan[i].outVol&0xc)<<2);
|
||||
} else {
|
||||
rWrite(0x08+i,(chan[i].outVol&15)|((chan[i].psgMode&4)<<2));
|
||||
}
|
||||
|
|
@ -151,6 +178,8 @@ void DivPlatformAY8910::tick() {
|
|||
chan[i].psgMode=(chan[i].std.wave+1)&7;
|
||||
if (isMuted[i]) {
|
||||
rWrite(0x08+i,0);
|
||||
} else if (intellivision && (chan[i].psgMode&4)) {
|
||||
rWrite(0x08+i,(chan[i].outVol&0xc)<<2);
|
||||
} else {
|
||||
rWrite(0x08+i,(chan[i].outVol&15)|((chan[i].psgMode&4)<<2));
|
||||
}
|
||||
|
|
@ -242,6 +271,8 @@ int DivPlatformAY8910::dispatch(DivCommand c) {
|
|||
chan[c.chan].std.init(ins);
|
||||
if (isMuted[c.chan]) {
|
||||
rWrite(0x08+c.chan,0);
|
||||
} else if (intellivision && (chan[c.chan].psgMode&4)) {
|
||||
rWrite(0x08+c.chan,(chan[c.chan].vol&0xc)<<2);
|
||||
} else {
|
||||
rWrite(0x08+c.chan,(chan[c.chan].vol&15)|((chan[c.chan].psgMode&4)<<2));
|
||||
}
|
||||
|
|
@ -264,7 +295,13 @@ int DivPlatformAY8910::dispatch(DivCommand c) {
|
|||
if (isMuted[c.chan]) {
|
||||
rWrite(0x08+c.chan,0);
|
||||
} else {
|
||||
if (chan[c.chan].active) rWrite(0x08+c.chan,(chan[c.chan].vol&15)|((chan[c.chan].psgMode&4)<<2));
|
||||
if (chan[c.chan].active) {
|
||||
if (intellivision && (chan[c.chan].psgMode&4)) {
|
||||
rWrite(0x08+c.chan,(chan[c.chan].vol&0xc)<<2);
|
||||
} else {
|
||||
rWrite(0x08+c.chan,(chan[c.chan].vol&15)|((chan[c.chan].psgMode&4)<<2));
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
@ -317,7 +354,11 @@ int DivPlatformAY8910::dispatch(DivCommand c) {
|
|||
if (isMuted[c.chan]) {
|
||||
rWrite(0x08+c.chan,0);
|
||||
} else if (chan[c.chan].active) {
|
||||
rWrite(0x08+c.chan,(chan[c.chan].outVol&15)|((chan[c.chan].psgMode&4)<<2));
|
||||
if (intellivision && (chan[c.chan].psgMode&4)) {
|
||||
rWrite(0x08+c.chan,(chan[c.chan].outVol&0xc)<<2);
|
||||
} else {
|
||||
rWrite(0x08+c.chan,(chan[c.chan].outVol&15)|((chan[c.chan].psgMode&4)<<2));
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
|
@ -334,6 +375,8 @@ int DivPlatformAY8910::dispatch(DivCommand c) {
|
|||
}
|
||||
if (isMuted[c.chan]) {
|
||||
rWrite(0x08+c.chan,0);
|
||||
} else if (intellivision && (chan[c.chan].psgMode&4)) {
|
||||
rWrite(0x08+c.chan,(chan[c.chan].vol&0xc)<<2);
|
||||
} else {
|
||||
rWrite(0x08+c.chan,(chan[c.chan].vol&15)|((chan[c.chan].psgMode&4)<<2));
|
||||
}
|
||||
|
|
@ -383,6 +426,8 @@ void DivPlatformAY8910::muteChannel(int ch, bool mute) {
|
|||
isMuted[ch]=mute;
|
||||
if (isMuted[ch]) {
|
||||
rWrite(0x08+ch,0);
|
||||
} else if (intellivision && (chan[ch].psgMode&4)) {
|
||||
rWrite(0x08+ch,(chan[ch].vol&0xc)<<2);
|
||||
} else {
|
||||
rWrite(0x08+ch,(chan[ch].outVol&15)|((chan[ch].psgMode&4)<<2));
|
||||
}
|
||||
|
|
@ -508,14 +553,22 @@ void DivPlatformAY8910::setFlags(unsigned int flags) {
|
|||
case 1:
|
||||
ay=new ym2149_device(rate);
|
||||
sunsoft=false;
|
||||
intellivision=false;
|
||||
break;
|
||||
case 2:
|
||||
ay=new sunsoft_5b_sound_device(rate);
|
||||
sunsoft=true;
|
||||
intellivision=false;
|
||||
break;
|
||||
case 3:
|
||||
ay=new ay8914_device(rate);
|
||||
sunsoft=false;
|
||||
intellivision=true;
|
||||
break;
|
||||
default:
|
||||
ay=new ay8910_device(rate);
|
||||
sunsoft=false;
|
||||
intellivision=false;
|
||||
break;
|
||||
}
|
||||
ay->device_start();
|
||||
|
|
|
|||
|
|
@ -26,6 +26,10 @@
|
|||
|
||||
class DivPlatformAY8910: public DivDispatch {
|
||||
protected:
|
||||
const unsigned char AY8914RegRemap[16]={
|
||||
0,4,1,5,2,6,9,8,11,12,13,3,7,10,14,15
|
||||
};
|
||||
inline unsigned char regRemap(unsigned char reg) { return intellivision?AY8914RegRemap[reg&0x0f]:reg&0x0f; }
|
||||
struct Channel {
|
||||
unsigned char freqH, freqL;
|
||||
int freq, baseFreq, note, pitch;
|
||||
|
|
@ -60,7 +64,7 @@ class DivPlatformAY8910: public DivDispatch {
|
|||
int delay;
|
||||
|
||||
bool extMode;
|
||||
bool stereo, sunsoft;
|
||||
bool stereo, sunsoft, intellivision;
|
||||
|
||||
short oldWrites[16];
|
||||
short pendingWrites[16];
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ void DivPlatformGenesis::acquire_nuked(short* bufL, short* bufR, size_t start, s
|
|||
DivSample* s=parent->getSample(dacSample);
|
||||
if (s->samples>0) {
|
||||
if (!isMuted[5]) {
|
||||
immWrite(0x2a,(unsigned char)s->data8[dacPos]+0x80);
|
||||
urgentWrite(0x2a,(unsigned char)s->data8[dacPos]+0x80);
|
||||
}
|
||||
if (++dacPos>=s->samples) {
|
||||
if (s->loopStart>=0 && s->loopStart<=(int)s->samples) {
|
||||
|
|
@ -121,7 +121,7 @@ void DivPlatformGenesis::acquire_nuked(short* bufL, short* bufR, size_t start, s
|
|||
//printf("write: %x = %.2x\n",w.addr,w.val);
|
||||
lastBusy=0;
|
||||
regPool[w.addr&0x1ff]=w.val;
|
||||
writes.pop();
|
||||
writes.pop_front();
|
||||
} else {
|
||||
lastBusy++;
|
||||
if (fm.write_busy==0) {
|
||||
|
|
@ -159,7 +159,7 @@ void DivPlatformGenesis::acquire_ymfm(short* bufL, short* bufR, size_t start, si
|
|||
DivSample* s=parent->getSample(dacSample);
|
||||
if (s->samples>0) {
|
||||
if (!isMuted[5]) {
|
||||
immWrite(0x2a,(unsigned char)s->data8[dacPos]+0x80);
|
||||
urgentWrite(0x2a,(unsigned char)s->data8[dacPos]+0x80);
|
||||
}
|
||||
if (++dacPos>=s->samples) {
|
||||
if (s->loopStart>=0 && s->loopStart<=(int)s->samples) {
|
||||
|
|
@ -184,7 +184,7 @@ void DivPlatformGenesis::acquire_ymfm(short* bufL, short* bufR, size_t start, si
|
|||
fm_ymfm->write(0x0+((w.addr>>8)<<1),w.addr);
|
||||
fm_ymfm->write(0x1+((w.addr>>8)<<1),w.val);
|
||||
regPool[w.addr&0x1ff]=w.val;
|
||||
writes.pop();
|
||||
writes.pop_front();
|
||||
lastBusy=1;
|
||||
}
|
||||
|
||||
|
|
@ -782,7 +782,7 @@ int DivPlatformGenesis::getRegisterPoolSize() {
|
|||
}
|
||||
|
||||
void DivPlatformGenesis::reset() {
|
||||
while (!writes.empty()) writes.pop();
|
||||
while (!writes.empty()) writes.pop_front();
|
||||
memset(regPool,0,512);
|
||||
if (useYMFM) {
|
||||
fm_ymfm->reset();
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
#ifndef _GENESIS_H
|
||||
#define _GENESIS_H
|
||||
#include "../dispatch.h"
|
||||
#include <queue>
|
||||
#include <deque>
|
||||
#include "../../../extern/Nuked-OPN2/ym3438.h"
|
||||
#include "sound/ymfm/ymfm_opn.h"
|
||||
|
||||
|
|
@ -68,7 +68,7 @@ class DivPlatformGenesis: public DivDispatch {
|
|||
bool addrOrVal;
|
||||
QueuedWrite(unsigned short a, unsigned char v): addr(a), val(v), addrOrVal(false) {}
|
||||
};
|
||||
std::queue<QueuedWrite> writes;
|
||||
std::deque<QueuedWrite> writes;
|
||||
ym3438_t fm;
|
||||
int delay;
|
||||
unsigned char lastBusy;
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ static int orderedOps[4]={
|
|||
};
|
||||
|
||||
#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 immWrite(a,v) if (!skipRegisterWrites) {writes.push_back(QueuedWrite(a,v)); if (dumpWrites) {addWrite(a,v);} }
|
||||
#define urgentWrite(a,v) if (!skipRegisterWrites) {if (writes.front().addrOrVal) {writes.push_back(QueuedWrite(a,v));} else {writes.push_front(QueuedWrite(a,v));}; if (dumpWrites) {addWrite(a,v);} }
|
||||
|
||||
#include "fmshared_OPN.h"
|
||||
|
|
|
|||
|
|
@ -235,7 +235,7 @@ int DivPlatformLynx::dispatch(DivCommand c) {
|
|||
}
|
||||
break;
|
||||
case DIV_CMD_PANNING:
|
||||
chan[c.chan].pan=((c.value&0x0f)<<4)|((c.value&0xf0)>>4);
|
||||
chan[c.chan].pan=c.value;
|
||||
WRITE_ATTEN(c.chan,chan[c.chan].pan);
|
||||
break;
|
||||
case DIV_CMD_GET_VOLUME:
|
||||
|
|
|
|||
|
|
@ -201,6 +201,7 @@ void DivPlatformNES::tick() {
|
|||
} else {
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)-1;
|
||||
if (chan[i].freq>2047) chan[i].freq=2047;
|
||||
if (chan[i].freq<0) chan[i].freq=0;
|
||||
}
|
||||
if (chan[i].keyOn) {
|
||||
//rWrite(16+i*5+1,((chan[i].duty&3)<<6)|(63-(ins->gb.soundLen&63)));
|
||||
|
|
|
|||
|
|
@ -222,7 +222,7 @@ void DivPlatformOPL::acquire(short* bufL, short* bufR, size_t start, size_t len)
|
|||
}
|
||||
|
||||
void DivPlatformOPL::tick() {
|
||||
for (int i=0; i<20; i++) {
|
||||
for (int i=0; i<melodicChans; i++) {
|
||||
chan[i].std.next();
|
||||
|
||||
/*
|
||||
|
|
@ -349,10 +349,22 @@ void DivPlatformOPL::tick() {
|
|||
|
||||
if (chan[i].keyOn || chan[i].keyOff) {
|
||||
immWrite(chanMap[i]+ADDR_FREQH,0x00|(chan[i].freqH&31));
|
||||
if (chan[i].state.ops==4 && i<6) {
|
||||
immWrite(chanMap[i+1]+ADDR_FREQH,0x00|(chan[i].freqH&31));
|
||||
}
|
||||
chan[i].keyOff=false;
|
||||
}
|
||||
}
|
||||
|
||||
if (update4OpMask) {
|
||||
update4OpMask=false;
|
||||
if (oplType==3) {
|
||||
unsigned char opMask=chan[0].fourOp|(chan[2].fourOp<<1)|(chan[4].fourOp<<2)|(chan[6].fourOp<<3)|(chan[8].fourOp<<4)|(chan[10].fourOp<<5);
|
||||
immWrite(0x104,opMask);
|
||||
//printf("updating opMask to %.2x\n",opMask);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i=0; i<512; i++) {
|
||||
if (pendingWrites[i]!=oldWrites[i]) {
|
||||
immWrite(i,pendingWrites[i]&0xff);
|
||||
|
|
@ -360,7 +372,7 @@ void DivPlatformOPL::tick() {
|
|||
}
|
||||
}
|
||||
|
||||
for (int i=0; i<20; i++) {
|
||||
for (int i=0; i<melodicChans; i++) {
|
||||
if (chan[i].freqChanged) {
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,octave(chan[i].baseFreq));
|
||||
if (chan[i].freq>131071) chan[i].freq=131071;
|
||||
|
|
@ -368,12 +380,21 @@ void DivPlatformOPL::tick() {
|
|||
chan[i].freqH=freqt>>8;
|
||||
chan[i].freqL=freqt&0xff;
|
||||
immWrite(chanMap[i]+ADDR_FREQ,chan[i].freqL);
|
||||
if (chan[i].state.ops==4 && i<6) {
|
||||
immWrite(chanMap[i+1]+ADDR_FREQ,chan[i].freqL);
|
||||
}
|
||||
}
|
||||
if (chan[i].keyOn) {
|
||||
immWrite(chanMap[i]+ADDR_FREQH,chan[i].freqH|(0x20));
|
||||
if (chan[i].state.ops==4 && i<6) {
|
||||
immWrite(chanMap[i+1]+ADDR_FREQH,chan[i].freqH|(0x20));
|
||||
}
|
||||
chan[i].keyOn=false;
|
||||
} else if (chan[i].freqChanged) {
|
||||
immWrite(chanMap[i]+ADDR_FREQH,chan[i].freqH|(chan[i].active<<5));
|
||||
if (chan[i].state.ops==4 && i<6) {
|
||||
immWrite(chanMap[i+1]+ADDR_FREQH,chan[i].freqH|(chan[i].active<<5));
|
||||
}
|
||||
}
|
||||
chan[i].freqChanged=false;
|
||||
}
|
||||
|
|
@ -424,25 +445,42 @@ int DivPlatformOPL::toFreq(int freq) {
|
|||
|
||||
void DivPlatformOPL::muteChannel(int ch, bool mute) {
|
||||
isMuted[ch]=mute;
|
||||
/*
|
||||
for (int j=0; j<4; j++) {
|
||||
unsigned short baseAddr=chanOffs[ch]|opOffs[j];
|
||||
DivInstrumentFM::Operator& op=chan[ch].state.op[j];
|
||||
int ops=(slots[3][ch]!=255 && chan[ch].state.ops==4 && oplType==3)?4:2;
|
||||
chan[ch].fourOp=(ops==4);
|
||||
update4OpMask=true;
|
||||
for (int i=0; i<ops; i++) {
|
||||
unsigned char slot=slots[i][ch];
|
||||
if (slot==255) continue;
|
||||
unsigned short baseAddr=slotMap[slot];
|
||||
DivInstrumentFM::Operator& op=chan[ch].state.op[(ops==4)?orderedOpsL[i]:i];
|
||||
|
||||
if (isMuted[ch]) {
|
||||
rWrite(baseAddr+ADDR_TL,127);
|
||||
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
|
||||
} else {
|
||||
if (isOutput[chan[ch].state.alg][j]) {
|
||||
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[ch].outVol&0x7f))/127));
|
||||
if (isOutputL[ops==4][chan[ch].state.alg][i]) {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,(63-(((63-op.tl)*(chan[ch].outVol&0x3f))/63))|(op.ksl<<6));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_TL,op.tl);
|
||||
rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6));
|
||||
}
|
||||
}
|
||||
}
|
||||
rWrite(chanOffs[ch]+ADDR_LRAF,(isMuted[ch]?0:(chan[ch].pan<<6))|(chan[ch].state.fms&7)|((chan[ch].state.ams&3)<<4));
|
||||
*/
|
||||
|
||||
if (isMuted[ch]) {
|
||||
rWrite(chanMap[ch]+ADDR_LR_FB_ALG,(chan[ch].state.alg&1)|(chan[ch].state.fb<<1));
|
||||
if (ops==4) {
|
||||
rWrite(chanMap[ch+1]+ADDR_LR_FB_ALG,((chan[ch].state.alg>>1)&1)|(chan[ch].state.fb<<1));
|
||||
}
|
||||
} else {
|
||||
rWrite(chanMap[ch]+ADDR_LR_FB_ALG,(chan[ch].state.alg&1)|(chan[ch].state.fb<<1)|((chan[ch].pan&3)<<4));
|
||||
if (ops==4) {
|
||||
rWrite(chanMap[ch+1]+ADDR_LR_FB_ALG,((chan[ch].state.alg>>1)&1)|(chan[ch].state.fb<<1)|((chan[ch].pan&3)<<4));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int DivPlatformOPL::dispatch(DivCommand c) {
|
||||
// TODO: drums mode!
|
||||
if (c.chan>=melodicChans) return 0;
|
||||
switch (c.cmd) {
|
||||
case DIV_CMD_NOTE_ON: {
|
||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins);
|
||||
|
|
@ -456,7 +494,9 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
|||
chan[c.chan].outVol=chan[c.chan].vol;
|
||||
}
|
||||
if (chan[c.chan].insChanged) {
|
||||
int ops=(slots[3][c.chan]!=255 && ins->fm.ops==4 && oplType==3)?4:2;
|
||||
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
|
||||
chan[c.chan].fourOp=(ops==4);
|
||||
update4OpMask=true;
|
||||
for (int i=0; i<ops; i++) {
|
||||
unsigned char slot=slots[i][c.chan];
|
||||
if (slot==255) continue;
|
||||
|
|
@ -524,21 +564,23 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
|||
if (!chan[c.chan].std.hasVol) {
|
||||
chan[c.chan].outVol=c.value;
|
||||
}
|
||||
/*
|
||||
for (int i=0; i<4; i++) {
|
||||
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
|
||||
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];
|
||||
if (slot==255) continue;
|
||||
unsigned short baseAddr=slotMap[slot];
|
||||
DivInstrumentFM::Operator& op=chan[c.chan].state.op[(ops==4)?orderedOpsL[i]:i];
|
||||
|
||||
if (isMuted[c.chan]) {
|
||||
rWrite(baseAddr+ADDR_TL,127);
|
||||
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
|
||||
} else {
|
||||
if (isOutput[chan[c.chan].state.alg][i]) {
|
||||
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[c.chan].outVol&0x7f))/127));
|
||||
if (isOutputL[ops==4][chan[c.chan].state.alg][i]) {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,(63-(((63-op.tl)*(chan[c.chan].outVol&0x3f))/63))|(op.ksl<<6));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_TL,op.tl);
|
||||
rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6));
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_GET_VOLUME: {
|
||||
|
|
@ -552,16 +594,22 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
|||
chan[c.chan].ins=c.value;
|
||||
break;
|
||||
case DIV_CMD_PANNING: {
|
||||
switch (c.value) {
|
||||
case 0x01:
|
||||
chan[c.chan].pan=1;
|
||||
break;
|
||||
case 0x10:
|
||||
chan[c.chan].pan=2;
|
||||
break;
|
||||
default:
|
||||
chan[c.chan].pan=3;
|
||||
break;
|
||||
if (c.value==0) {
|
||||
chan[c.chan].pan=3;
|
||||
} else {
|
||||
chan[c.chan].pan=(((c.value&15)>0)<<1)|((c.value>>4)>0);
|
||||
}
|
||||
int ops=(slots[3][c.chan]!=255 && chan[c.chan].state.ops==4 && oplType==3)?4:2;
|
||||
if (isMuted[c.chan]) {
|
||||
rWrite(chanMap[c.chan]+ADDR_LR_FB_ALG,(chan[c.chan].state.alg&1)|(chan[c.chan].state.fb<<1));
|
||||
if (ops==4) {
|
||||
rWrite(chanMap[c.chan+1]+ADDR_LR_FB_ALG,((chan[c.chan].state.alg>>1)&1)|(chan[c.chan].state.fb<<1));
|
||||
}
|
||||
} else {
|
||||
rWrite(chanMap[c.chan]+ADDR_LR_FB_ALG,(chan[c.chan].state.alg&1)|(chan[c.chan].state.fb<<1)|((chan[c.chan].pan&3)<<4));
|
||||
if (ops==4) {
|
||||
rWrite(chanMap[c.chan+1]+ADDR_LR_FB_ALG,((chan[c.chan].state.alg>>1)&1)|(chan[c.chan].state.fb<<1)|((chan[c.chan].pan&3)<<4));
|
||||
}
|
||||
}
|
||||
//rWrite(chanOffs[c.chan]+ADDR_LRAF,(isMuted[c.chan]?0:(chan[c.chan].pan<<6))|(chan[c.chan].state.fms&7)|((chan[c.chan].state.ams&3)<<4));
|
||||
break;
|
||||
|
|
@ -683,39 +731,46 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
|||
}
|
||||
|
||||
void DivPlatformOPL::forceIns() {
|
||||
/*
|
||||
for (int i=0; i<20; i++) {
|
||||
for (int j=0; j<4; j++) {
|
||||
unsigned short baseAddr=chanOffs[i]|opOffs[j];
|
||||
DivInstrumentFM::Operator& op=chan[i].state.op[j];
|
||||
for (int i=0; i<melodicChans; i++) {
|
||||
int ops=(slots[3][i]!=255 && chan[i].state.ops==4 && oplType==3)?4:2;
|
||||
chan[i].fourOp=(ops==4);
|
||||
for (int j=0; j<ops; j++) {
|
||||
unsigned char slot=slots[j][i];
|
||||
if (slot==255) continue;
|
||||
unsigned short baseAddr=slotMap[slot];
|
||||
DivInstrumentFM::Operator& op=chan[i].state.op[(ops==4)?orderedOpsL[j]:j];
|
||||
|
||||
if (isMuted[i]) {
|
||||
rWrite(baseAddr+ADDR_TL,127);
|
||||
rWrite(baseAddr+ADDR_KSL_TL,63|(op.ksl<<6));
|
||||
} else {
|
||||
if (isOutput[chan[i].state.alg][j]) {
|
||||
rWrite(baseAddr+ADDR_TL,127-(((127-op.tl)*(chan[i].outVol&0x7f))/127));
|
||||
if (isOutputL[ops==4][chan[i].state.alg][j]) {
|
||||
rWrite(baseAddr+ADDR_KSL_TL,(63-(((63-op.tl)*(chan[i].outVol&0x3f))/63))|(op.ksl<<6));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_TL,op.tl);
|
||||
rWrite(baseAddr+ADDR_KSL_TL,op.tl|(op.ksl<<6));
|
||||
}
|
||||
}
|
||||
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(baseAddr+ADDR_AM_VIB_SUS_KSR_MULT,(op.am<<7)|(op.vib<<6)|(op.sus<<5)|(op.ksr<<4)|op.mult);
|
||||
rWrite(baseAddr+ADDR_AR_DR,(op.ar<<4)|op.dr);
|
||||
rWrite(baseAddr+ADDR_SL_RR,(op.sl<<4)|op.rr);
|
||||
if (oplType>1) {
|
||||
rWrite(baseAddr+ADDR_WS,op.ws&((oplType==3)?7:3));
|
||||
}
|
||||
}
|
||||
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;
|
||||
|
||||
if (isMuted[i]) {
|
||||
rWrite(chanMap[i]+ADDR_LR_FB_ALG,(chan[i].state.alg&1)|(chan[i].state.fb<<1));
|
||||
if (ops==4) {
|
||||
rWrite(chanMap[i+1]+ADDR_LR_FB_ALG,((chan[i].state.alg>>1)&1)|(chan[i].state.fb<<1));
|
||||
}
|
||||
} else {
|
||||
rWrite(chanMap[i]+ADDR_LR_FB_ALG,(chan[i].state.alg&1)|(chan[i].state.fb<<1)|((chan[i].pan&3)<<4));
|
||||
if (ops==4) {
|
||||
rWrite(chanMap[i+1]+ADDR_LR_FB_ALG,((chan[i].state.alg>>1)&1)|(chan[i].state.fb<<1)|((chan[i].pan&3)<<4));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dacMode) {
|
||||
rWrite(0x2b,0x80);
|
||||
}
|
||||
immWrite(0x22,lfoValue);
|
||||
*/
|
||||
update4OpMask=true;
|
||||
}
|
||||
|
||||
void DivPlatformOPL::toggleRegisterDump(bool enable) {
|
||||
|
|
@ -746,7 +801,7 @@ void DivPlatformOPL::reset() {
|
|||
if (dumpWrites) {
|
||||
addWrite(0xffffffff,0);
|
||||
}
|
||||
for (int i=0; i<20; i++) {
|
||||
for (int i=0; i<totalChans; i++) {
|
||||
chan[i]=DivPlatformOPL::Channel();
|
||||
chan[i].vol=0x3f;
|
||||
chan[i].outVol=0x3f;
|
||||
|
|
@ -761,10 +816,15 @@ void DivPlatformOPL::reset() {
|
|||
lfoValue=8;
|
||||
properDrums=properDrumsSys;
|
||||
|
||||
if (oplType==1) { // disable waveforms
|
||||
immWrite(0x01,0x20);
|
||||
}
|
||||
|
||||
if (oplType==3) { // enable OPL3 features
|
||||
immWrite(0x105,1);
|
||||
}
|
||||
|
||||
|
||||
update4OpMask=true;
|
||||
delay=0;
|
||||
}
|
||||
|
||||
|
|
@ -781,7 +841,7 @@ bool DivPlatformOPL::keyOffAffectsPorta(int ch) {
|
|||
}
|
||||
|
||||
void DivPlatformOPL::notifyInsChange(int ins) {
|
||||
for (int i=0; i<20; i++) {
|
||||
for (int i=0; i<totalChans; i++) {
|
||||
if (chan[i].ins==ins) {
|
||||
chan[i].insChanged=true;
|
||||
}
|
||||
|
|
@ -815,6 +875,9 @@ void DivPlatformOPL::setOPLType(int type, bool drums) {
|
|||
slots=drums?slotsDrums:slotsNonDrums;
|
||||
chanMap=chanMapOPL2;
|
||||
chipFreqBase=9440540*0.25;
|
||||
chans=9;
|
||||
melodicChans=drums?6:9;
|
||||
totalChans=drums?11:9;
|
||||
break;
|
||||
case 3:
|
||||
slotsNonDrums=slotsOPL3;
|
||||
|
|
@ -822,6 +885,9 @@ void DivPlatformOPL::setOPLType(int type, bool drums) {
|
|||
slots=drums?slotsDrums:slotsNonDrums;
|
||||
chanMap=chanMapOPL3;
|
||||
chipFreqBase=9440540;
|
||||
chans=18;
|
||||
melodicChans=drums?15:18;
|
||||
totalChans=drums?20:18;
|
||||
break;
|
||||
}
|
||||
oplType=type;
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ class DivPlatformOPL: public DivDispatch {
|
|||
unsigned char freqH, freqL;
|
||||
int freq, baseFreq, pitch, note;
|
||||
unsigned char ins;
|
||||
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, furnaceDac, inPorta;
|
||||
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, furnaceDac, inPorta, fourOp;
|
||||
int vol, outVol;
|
||||
unsigned char pan;
|
||||
Channel():
|
||||
|
|
@ -51,6 +51,7 @@ class DivPlatformOPL: public DivDispatch {
|
|||
portaPause(false),
|
||||
furnaceDac(false),
|
||||
inPorta(false),
|
||||
fourOp(false),
|
||||
vol(0),
|
||||
pan(3) {}
|
||||
};
|
||||
|
|
@ -69,7 +70,7 @@ class DivPlatformOPL: public DivDispatch {
|
|||
const unsigned char** slots;
|
||||
const unsigned short* chanMap;
|
||||
double chipFreqBase;
|
||||
int delay, oplType;
|
||||
int delay, oplType, chans, melodicChans, totalChans;
|
||||
unsigned char lastBusy;
|
||||
|
||||
unsigned char regPool[512];
|
||||
|
|
@ -78,7 +79,7 @@ class DivPlatformOPL: public DivDispatch {
|
|||
|
||||
unsigned char lfoValue;
|
||||
|
||||
bool useYMFM;
|
||||
bool useYMFM, update4OpMask;
|
||||
|
||||
short oldWrites[512];
|
||||
short pendingWrites[512];
|
||||
|
|
|
|||
|
|
@ -435,8 +435,8 @@ public:
|
|||
case ATTENREG2:
|
||||
case ATTENREG3:
|
||||
mRegisterPool[8*4+idx] = value;
|
||||
mAttenuationLeft[idx] = ( value & 0x0f ) << 2;
|
||||
mAttenuationRight[idx] = ( value & 0xf0 ) >> 2;
|
||||
mAttenuationRight[idx] = ( value & 0x0f ) << 2;
|
||||
mAttenuationLeft[idx] = ( value & 0xf0 ) >> 2;
|
||||
break;
|
||||
case MPAN:
|
||||
mPan = value;
|
||||
|
|
|
|||
|
|
@ -663,6 +663,12 @@ void DivEngine::processRow(int i, bool afterDelay) {
|
|||
// instrument
|
||||
if (pat->data[whatRow][2]!=-1) {
|
||||
dispatchCmd(DivCommand(DIV_CMD_INSTRUMENT,i,pat->data[whatRow][2]));
|
||||
if (chan[i].lastIns!=pat->data[whatRow][2]) {
|
||||
chan[i].lastIns=pat->data[whatRow][2];
|
||||
if (chan[i].inPorta && song.newInsTriggersInPorta) {
|
||||
dispatchCmd(DivCommand(DIV_CMD_NOTE_ON,i,DIV_NOTE_NULL));
|
||||
}
|
||||
}
|
||||
}
|
||||
// note
|
||||
if (pat->data[whatRow][0]==100) { // note off
|
||||
|
|
|
|||
|
|
@ -298,6 +298,7 @@ struct DivSong {
|
|||
bool continuousVibrato;
|
||||
bool brokenDACMode;
|
||||
bool oneTickCut;
|
||||
bool newInsTriggersInPorta;
|
||||
|
||||
DivOrders orders;
|
||||
std::vector<DivInstrument*> ins;
|
||||
|
|
@ -364,7 +365,8 @@ struct DivSong {
|
|||
stopPortaOnNoteOff(false),
|
||||
continuousVibrato(false),
|
||||
brokenDACMode(false),
|
||||
oneTickCut(false) {
|
||||
oneTickCut(false),
|
||||
newInsTriggersInPorta(true) {
|
||||
for (int i=0; i<32; i++) {
|
||||
system[i]=DIV_SYSTEM_NULL;
|
||||
systemVol[i]=64;
|
||||
|
|
|
|||
|
|
@ -421,10 +421,6 @@ const char* DivEngine::getSongSystemName() {
|
|||
return "Vectrex";
|
||||
case 5: // AY-3-8910, 1MHz
|
||||
return "Amstrad CPC";
|
||||
case 6: // AY-3-8910, 0.somethingMhz
|
||||
return "Intellivision";
|
||||
case 8: // AY-3-8910, 0.somethingMhz
|
||||
return "Intellivision (PAL)";
|
||||
|
||||
case 0x10: // YM2149, 1.79MHz
|
||||
return "MSX";
|
||||
|
|
@ -434,7 +430,12 @@ const char* DivEngine::getSongSystemName() {
|
|||
return "Sunsoft 5B standalone";
|
||||
case 0x28: // 5B PAL
|
||||
return "Sunsoft 5B standalone (PAL)";
|
||||
|
||||
|
||||
case 0x30: // AY-3-8914, 1.79MHz
|
||||
return "Intellivision";
|
||||
case 0x33: // AY-3-8914, 2MHz
|
||||
return "Intellivision (PAL)";
|
||||
|
||||
default:
|
||||
if ((song.systemFlags[0]&0x30)==0x00) {
|
||||
return "AY-3-8910";
|
||||
|
|
@ -442,6 +443,8 @@ const char* DivEngine::getSongSystemName() {
|
|||
return "Yamaha YM2149";
|
||||
} else if ((song.systemFlags[0]&0x30)==0x20) {
|
||||
return "Overclocked Sunsoft 5B";
|
||||
} else if ((song.systemFlags[0]&0x30)==0x30) {
|
||||
return "Intellivision";
|
||||
}
|
||||
}
|
||||
} else if (song.system[0]==DIV_SYSTEM_SMS) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue