mirror of
https://github.com/tildearrow/furnace.git
synced 2024-11-15 17:25:06 +00:00
Add AY-3-8914 support as configurable in AY-3-8910
Previous PR (https://github.com/tildearrow/furnace/pull/278) is closed due this, but archived for info. It's AY with 4 level envelope volume per channel and different register format.
This commit is contained in:
parent
74a23b3ec5
commit
3ac1dce3fe
5 changed files with 79 additions and 13 deletions
|
@ -4,6 +4,8 @@ this chip was used in several home computers (ZX Spectrum, MSX, Amstrad CPC, Ata
|
||||||
|
|
||||||
the chip's powerful sound comes from the envelope...
|
the chip's powerful sound comes from the envelope...
|
||||||
|
|
||||||
|
AY-3-8914 variant was used in Intellivision, it's basically original AY with 4 level envelope volume per channel and different register format.
|
||||||
|
|
||||||
# effects
|
# effects
|
||||||
|
|
||||||
- `20xx`: set channel mode. `xx` may be one of the following:
|
- `20xx`: set channel mode. `xx` may be one of the following:
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
#define rWrite(a,v) if (!skipRegisterWrites) {pendingWrites[a]=v;}
|
#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
|
#define CHIP_DIVIDER 8
|
||||||
|
|
||||||
|
@ -48,8 +48,28 @@ const char* regCheatSheetAY[]={
|
||||||
NULL
|
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() {
|
const char** DivPlatformAY8910::getRegisterSheet() {
|
||||||
return regCheatSheetAY;
|
return intellivision?regCheatSheetAY8914:regCheatSheetAY;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* DivPlatformAY8910::getEffectName(unsigned char effect) {
|
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()) {
|
while (!writes.empty()) {
|
||||||
QueuedWrite w=writes.front();
|
QueuedWrite w=writes.front();
|
||||||
ay->address_w(w.addr);
|
if (intellivision) {
|
||||||
ay->data_w(w.val);
|
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;
|
regPool[w.addr&0x0f]=w.val;
|
||||||
writes.pop();
|
writes.pop();
|
||||||
}
|
}
|
||||||
|
@ -125,6 +150,8 @@ void DivPlatformAY8910::tick() {
|
||||||
if (chan[i].outVol<0) chan[i].outVol=0;
|
if (chan[i].outVol<0) chan[i].outVol=0;
|
||||||
if (isMuted[i]) {
|
if (isMuted[i]) {
|
||||||
rWrite(0x08+i,0);
|
rWrite(0x08+i,0);
|
||||||
|
} else if (intellivision && (chan[i].psgMode&4)) {
|
||||||
|
rWrite(0x08+i,(chan[i].outVol&0xc)<<2);
|
||||||
} else {
|
} else {
|
||||||
rWrite(0x08+i,(chan[i].outVol&15)|((chan[i].psgMode&4)<<2));
|
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;
|
chan[i].psgMode=(chan[i].std.wave+1)&7;
|
||||||
if (isMuted[i]) {
|
if (isMuted[i]) {
|
||||||
rWrite(0x08+i,0);
|
rWrite(0x08+i,0);
|
||||||
|
} else if (intellivision && (chan[i].psgMode&4)) {
|
||||||
|
rWrite(0x08+i,(chan[i].outVol&0xc)<<2);
|
||||||
} else {
|
} else {
|
||||||
rWrite(0x08+i,(chan[i].outVol&15)|((chan[i].psgMode&4)<<2));
|
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);
|
chan[c.chan].std.init(ins);
|
||||||
if (isMuted[c.chan]) {
|
if (isMuted[c.chan]) {
|
||||||
rWrite(0x08+c.chan,0);
|
rWrite(0x08+c.chan,0);
|
||||||
|
} else if (intellivision && (chan[c.chan].psgMode&4)) {
|
||||||
|
rWrite(0x08+c.chan,(chan[c.chan].vol&0xc)<<2);
|
||||||
} else {
|
} else {
|
||||||
rWrite(0x08+c.chan,(chan[c.chan].vol&15)|((chan[c.chan].psgMode&4)<<2));
|
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]) {
|
if (isMuted[c.chan]) {
|
||||||
rWrite(0x08+c.chan,0);
|
rWrite(0x08+c.chan,0);
|
||||||
} else {
|
} 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;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -317,7 +354,11 @@ int DivPlatformAY8910::dispatch(DivCommand c) {
|
||||||
if (isMuted[c.chan]) {
|
if (isMuted[c.chan]) {
|
||||||
rWrite(0x08+c.chan,0);
|
rWrite(0x08+c.chan,0);
|
||||||
} else if (chan[c.chan].active) {
|
} 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;
|
break;
|
||||||
|
@ -334,6 +375,8 @@ int DivPlatformAY8910::dispatch(DivCommand c) {
|
||||||
}
|
}
|
||||||
if (isMuted[c.chan]) {
|
if (isMuted[c.chan]) {
|
||||||
rWrite(0x08+c.chan,0);
|
rWrite(0x08+c.chan,0);
|
||||||
|
} else if (intellivision && (chan[c.chan].psgMode&4)) {
|
||||||
|
rWrite(0x08+c.chan,(chan[c.chan].vol&0xc)<<2);
|
||||||
} else {
|
} else {
|
||||||
rWrite(0x08+c.chan,(chan[c.chan].vol&15)|((chan[c.chan].psgMode&4)<<2));
|
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;
|
isMuted[ch]=mute;
|
||||||
if (isMuted[ch]) {
|
if (isMuted[ch]) {
|
||||||
rWrite(0x08+ch,0);
|
rWrite(0x08+ch,0);
|
||||||
|
} else if (intellivision && (chan[ch].psgMode&4)) {
|
||||||
|
rWrite(0x08+ch,(chan[ch].vol&0xc)<<2);
|
||||||
} else {
|
} else {
|
||||||
rWrite(0x08+ch,(chan[ch].outVol&15)|((chan[ch].psgMode&4)<<2));
|
rWrite(0x08+ch,(chan[ch].outVol&15)|((chan[ch].psgMode&4)<<2));
|
||||||
}
|
}
|
||||||
|
@ -508,14 +553,22 @@ void DivPlatformAY8910::setFlags(unsigned int flags) {
|
||||||
case 1:
|
case 1:
|
||||||
ay=new ym2149_device(rate);
|
ay=new ym2149_device(rate);
|
||||||
sunsoft=false;
|
sunsoft=false;
|
||||||
|
intellivision=false;
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
ay=new sunsoft_5b_sound_device(rate);
|
ay=new sunsoft_5b_sound_device(rate);
|
||||||
sunsoft=true;
|
sunsoft=true;
|
||||||
|
intellivision=false;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
ay=new ay8914_device(rate);
|
||||||
|
sunsoft=false;
|
||||||
|
intellivision=true;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
ay=new ay8910_device(rate);
|
ay=new ay8910_device(rate);
|
||||||
sunsoft=false;
|
sunsoft=false;
|
||||||
|
intellivision=false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
ay->device_start();
|
ay->device_start();
|
||||||
|
|
|
@ -26,6 +26,10 @@
|
||||||
|
|
||||||
class DivPlatformAY8910: public DivDispatch {
|
class DivPlatformAY8910: public DivDispatch {
|
||||||
protected:
|
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 {
|
struct Channel {
|
||||||
unsigned char freqH, freqL;
|
unsigned char freqH, freqL;
|
||||||
int freq, baseFreq, note, pitch;
|
int freq, baseFreq, note, pitch;
|
||||||
|
@ -60,7 +64,7 @@ class DivPlatformAY8910: public DivDispatch {
|
||||||
int delay;
|
int delay;
|
||||||
|
|
||||||
bool extMode;
|
bool extMode;
|
||||||
bool stereo, sunsoft;
|
bool stereo, sunsoft, intellivision;
|
||||||
|
|
||||||
short oldWrites[16];
|
short oldWrites[16];
|
||||||
short pendingWrites[16];
|
short pendingWrites[16];
|
||||||
|
|
|
@ -419,10 +419,6 @@ const char* DivEngine::getSongSystemName() {
|
||||||
return "Vectrex";
|
return "Vectrex";
|
||||||
case 5: // AY-3-8910, 1MHz
|
case 5: // AY-3-8910, 1MHz
|
||||||
return "Amstrad CPC";
|
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
|
case 0x10: // YM2149, 1.79MHz
|
||||||
return "MSX";
|
return "MSX";
|
||||||
|
@ -432,7 +428,12 @@ const char* DivEngine::getSongSystemName() {
|
||||||
return "Sunsoft 5B standalone";
|
return "Sunsoft 5B standalone";
|
||||||
case 0x28: // 5B PAL
|
case 0x28: // 5B PAL
|
||||||
return "Sunsoft 5B standalone (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:
|
default:
|
||||||
if ((song.systemFlags[0]&0x30)==0x00) {
|
if ((song.systemFlags[0]&0x30)==0x00) {
|
||||||
return "AY-3-8910";
|
return "AY-3-8910";
|
||||||
|
@ -440,6 +441,8 @@ const char* DivEngine::getSongSystemName() {
|
||||||
return "Yamaha YM2149";
|
return "Yamaha YM2149";
|
||||||
} else if ((song.systemFlags[0]&0x30)==0x20) {
|
} else if ((song.systemFlags[0]&0x30)==0x20) {
|
||||||
return "Overclocked Sunsoft 5B";
|
return "Overclocked Sunsoft 5B";
|
||||||
|
} else if ((song.systemFlags[0]&0x30)==0x30) {
|
||||||
|
return "Intellivision";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (song.system[0]==DIV_SYSTEM_SMS) {
|
} else if (song.system[0]==DIV_SYSTEM_SMS) {
|
||||||
|
|
|
@ -5116,6 +5116,10 @@ bool FurnaceGUI::loop() {
|
||||||
e->setSysFlags(i,(flags&(~0x30))|32,restart);
|
e->setSysFlags(i,(flags&(~0x30))|32,restart);
|
||||||
updateWindowTitle();
|
updateWindowTitle();
|
||||||
}
|
}
|
||||||
|
if (ImGui::RadioButton("AY-3-8914",(flags&0x30)==48)) {
|
||||||
|
e->setSysFlags(i,(flags&(~0x30))|48,restart);
|
||||||
|
updateWindowTitle();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
bool stereo=flags&0x40;
|
bool stereo=flags&0x40;
|
||||||
ImGui::BeginDisabled((flags&0x30)==32);
|
ImGui::BeginDisabled((flags&0x30)==32);
|
||||||
|
@ -6684,7 +6688,7 @@ FurnaceGUI::FurnaceGUI():
|
||||||
));
|
));
|
||||||
cat.systems.push_back(FurnaceGUISysDef(
|
cat.systems.push_back(FurnaceGUISysDef(
|
||||||
"Mattel Intellivision", {
|
"Mattel Intellivision", {
|
||||||
DIV_SYSTEM_AY8910, 64, 0, 6,
|
DIV_SYSTEM_AY8910, 64, 0, 48,
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
));
|
));
|
||||||
|
|
Loading…
Reference in a new issue