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:
cam900 2022-03-12 03:30:54 +09:00
parent 74a23b3ec5
commit 3ac1dce3fe
5 changed files with 79 additions and 13 deletions

View file

@ -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:

View file

@ -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();

View file

@ -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];

View file

@ -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) {

View file

@ -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
} }
)); ));