From 3ac1dce3fe453b02050ea6aa80f0217e0659f08c Mon Sep 17 00:00:00 2001 From: cam900 Date: Sat, 12 Mar 2022 03:30:54 +0900 Subject: [PATCH] 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. --- papers/doc/7-systems/ay8910.md | 2 ++ src/engine/platform/ay.cpp | 65 ++++++++++++++++++++++++++++++---- src/engine/platform/ay.h | 6 +++- src/engine/sysDef.cpp | 13 ++++--- src/gui/gui.cpp | 6 +++- 5 files changed, 79 insertions(+), 13 deletions(-) diff --git a/papers/doc/7-systems/ay8910.md b/papers/doc/7-systems/ay8910.md index 3dd1049f..4d13a153 100644 --- a/papers/doc/7-systems/ay8910.md +++ b/papers/doc/7-systems/ay8910.md @@ -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... +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 - `20xx`: set channel mode. `xx` may be one of the following: diff --git a/src/engine/platform/ay.cpp b/src/engine/platform/ay.cpp index 0777fca8..526b3fbd 100644 --- a/src/engine/platform/ay.cpp +++ b/src/engine/platform/ay.cpp @@ -24,7 +24,7 @@ #include #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(); diff --git a/src/engine/platform/ay.h b/src/engine/platform/ay.h index c2292990..41c3c404 100644 --- a/src/engine/platform/ay.h +++ b/src/engine/platform/ay.h @@ -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]; diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index 70e27dae..5e57f6e3 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -419,10 +419,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"; @@ -432,7 +428,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"; @@ -440,6 +441,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) { diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index e79029e3..850fbf74 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -5116,6 +5116,10 @@ bool FurnaceGUI::loop() { e->setSysFlags(i,(flags&(~0x30))|32,restart); updateWindowTitle(); } + if (ImGui::RadioButton("AY-3-8914",(flags&0x30)==48)) { + e->setSysFlags(i,(flags&(~0x30))|48,restart); + updateWindowTitle(); + } } bool stereo=flags&0x40; ImGui::BeginDisabled((flags&0x30)==32); @@ -6684,7 +6688,7 @@ FurnaceGUI::FurnaceGUI(): )); cat.systems.push_back(FurnaceGUISysDef( "Mattel Intellivision", { - DIV_SYSTEM_AY8910, 64, 0, 6, + DIV_SYSTEM_AY8910, 64, 0, 48, 0 } ));