From 73536c069104461823120ce933e2780acd5bbda7 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 26 Mar 2022 20:55:43 -0500 Subject: [PATCH] AY: add effects to write to I/O ports --- papers/doc/7-systems/ay8910.md | 4 +++ src/engine/dispatch.h | 2 ++ src/engine/platform/ay.cpp | 57 +++++++++++++++++++++++++++++----- src/engine/platform/ay.h | 4 +++ src/engine/platform/ay8930.cpp | 57 +++++++++++++++++++++++++++++----- src/engine/platform/ay8930.h | 4 +++ src/engine/playback.cpp | 14 +++++++++ 7 files changed, 128 insertions(+), 14 deletions(-) diff --git a/papers/doc/7-systems/ay8910.md b/papers/doc/7-systems/ay8910.md index b01d9657..5df69eb6 100644 --- a/papers/doc/7-systems/ay8910.md +++ b/papers/doc/7-systems/ay8910.md @@ -40,3 +40,7 @@ AY-3-8914 variant was used in Intellivision, it's basically original AY with 4 l - `x` is the numerator. - `y` is the denominator. - if `x` or `y` are 0 this will disable auto-envelope mode. +- `2Exx`: write to I/O port A. + - this changes the port's mode to "write". make sure you have connected something to it. +- `2Fxx`: write to I/O port B. + - this changes the port's mode to "write". make sure you have connected something to it. \ No newline at end of file diff --git a/src/engine/dispatch.h b/src/engine/dispatch.h index c6486f26..a3cd1e81 100644 --- a/src/engine/dispatch.h +++ b/src/engine/dispatch.h @@ -101,6 +101,8 @@ enum DivDispatchCmds { DIV_CMD_AY_NOISE_MASK_AND, DIV_CMD_AY_NOISE_MASK_OR, DIV_CMD_AY_AUTO_ENVELOPE, + DIV_CMD_AY_IO_WRITE, + DIV_CMD_AY_AUTO_PWM, DIV_CMD_SAA_ENVELOPE, diff --git a/src/engine/platform/ay.cpp b/src/engine/platform/ay.cpp index 526b3fbd..3e53e8fe 100644 --- a/src/engine/platform/ay.cpp +++ b/src/engine/platform/ay.cpp @@ -19,6 +19,7 @@ #include "ay.h" #include "../engine.h" +#include "../../ta-log.h" #include "sound/ay8910.h" #include #include @@ -98,6 +99,12 @@ const char* DivPlatformAY8910::getEffectName(unsigned char effect) { case 0x29: return "29xy: Set auto-envelope (x: numerator; y: denominator)"; break; + case 0x2e: + return "2Exx: Write to I/O port A"; + break; + case 0x2f: + return "2Fxx: Write to I/O port B"; + break; } return NULL; } @@ -141,6 +148,30 @@ void DivPlatformAY8910::acquire(short* bufL, short* bufR, size_t start, size_t l } } +void DivPlatformAY8910::updateOutSel(bool immediate) { + if (immediate) { + immWrite(0x07, + ~((chan[0].psgMode&1)| + ((chan[1].psgMode&1)<<1)| + ((chan[2].psgMode&1)<<2)| + ((chan[0].psgMode&2)<<2)| + ((chan[1].psgMode&2)<<3)| + ((chan[2].psgMode&2)<<4)| + ((!ioPortA)<<6)| + ((!ioPortB)<<7))); + } else { + rWrite(0x07, + ~((chan[0].psgMode&1)| + ((chan[1].psgMode&1)<<1)| + ((chan[2].psgMode&1)<<2)| + ((chan[0].psgMode&2)<<2)| + ((chan[1].psgMode&2)<<3)| + ((chan[2].psgMode&2)<<4)| + ((!ioPortA)<<6)| + ((!ioPortB)<<7))); + } +} + void DivPlatformAY8910::tick() { // PSG for (int i=0; i<3; i++) { @@ -221,13 +252,7 @@ void DivPlatformAY8910::tick() { } } - rWrite(0x07, - ~((chan[0].psgMode&1)| - ((chan[1].psgMode&1)<<1)| - ((chan[2].psgMode&1)<<2)| - ((chan[0].psgMode&2)<<2)| - ((chan[1].psgMode&2)<<3)| - ((chan[2].psgMode&2)<<4))); + updateOutSel(); if (ayEnvSlide!=0) { ayEnvSlideLow+=ayEnvSlide; @@ -401,6 +426,19 @@ int DivPlatformAY8910::dispatch(DivCommand c) { chan[c.chan].autoEnvDen=c.value&15; chan[c.chan].freqChanged=true; break; + case DIV_CMD_AY_IO_WRITE: + if (c.value) { // port B + ioPortB=true; + portBVal=c.value2; + logI("AY I/O port B write: %x\n",portBVal); + } else { // port A + ioPortA=true; + portAVal=c.value2; + logI("AY I/O port A write: %x\n",portAVal); + } + updateOutSel(true); + immWrite(14+(c.value?1:0),(c.value?portBVal:portAVal)); + break; case DIV_ALWAYS_SET_VOLUME: return 0; break; @@ -486,6 +524,11 @@ void DivPlatformAY8910::reset() { delay=0; extMode=false; + + ioPortA=false; + ioPortB=false; + portAVal=0; + portBVal=0; } bool DivPlatformAY8910::isStereo() { diff --git a/src/engine/platform/ay.h b/src/engine/platform/ay.h index 41c3c404..498c75e6 100644 --- a/src/engine/platform/ay.h +++ b/src/engine/platform/ay.h @@ -65,6 +65,8 @@ class DivPlatformAY8910: public DivDispatch { bool extMode; bool stereo, sunsoft, intellivision; + bool ioPortA, ioPortB; + unsigned char portAVal, portBVal; short oldWrites[16]; short pendingWrites[16]; @@ -75,6 +77,8 @@ class DivPlatformAY8910: public DivDispatch { short* ayBuf[3]; size_t ayBufLen; + void updateOutSel(bool immediate=false); + friend void putDispatchChan(void*,int,int); public: diff --git a/src/engine/platform/ay8930.cpp b/src/engine/platform/ay8930.cpp index d87045a6..3e114e61 100644 --- a/src/engine/platform/ay8930.cpp +++ b/src/engine/platform/ay8930.cpp @@ -19,6 +19,7 @@ #include "ay8930.h" #include "../engine.h" +#include "../../ta-log.h" #include "sound/ay8910.h" #include #include @@ -99,6 +100,12 @@ const char* DivPlatformAY8930::getEffectName(unsigned char effect) { case 0x29: return "29xy: Set auto-envelope (x: numerator; y: denominator)"; break; + case 0x2e: + return "2Exx: Write to I/O port A"; + break; + case 0x2f: + return "2Fxx: Write to I/O port B"; + break; } return NULL; } @@ -141,6 +148,30 @@ void DivPlatformAY8930::acquire(short* bufL, short* bufR, size_t start, size_t l } } +void DivPlatformAY8930::updateOutSel(bool immediate) { + if (immediate) { + immWrite(0x07, + ~((chan[0].psgMode&1)| + ((chan[1].psgMode&1)<<1)| + ((chan[2].psgMode&1)<<2)| + ((chan[0].psgMode&2)<<2)| + ((chan[1].psgMode&2)<<3)| + ((chan[2].psgMode&2)<<4)| + ((!ioPortA)<<6)| + ((!ioPortB)<<7))); + } else { + rWrite(0x07, + ~((chan[0].psgMode&1)| + ((chan[1].psgMode&1)<<1)| + ((chan[2].psgMode&1)<<2)| + ((chan[0].psgMode&2)<<2)| + ((chan[1].psgMode&2)<<3)| + ((chan[2].psgMode&2)<<4)| + ((!ioPortA)<<6)| + ((!ioPortB)<<7))); + } +} + const unsigned char regPeriodL[3]={ 0x0b, 0x10, 0x12 }; @@ -262,13 +293,7 @@ void DivPlatformAY8930::tick() { } } - rWrite(0x07, - ~((chan[0].psgMode&1)| - ((chan[1].psgMode&1)<<1)| - ((chan[2].psgMode&1)<<2)| - ((chan[0].psgMode&2)<<2)| - ((chan[1].psgMode&2)<<3)| - ((chan[2].psgMode&2)<<4))); + updateOutSel(); for (int i=0; i<32; i++) { if (pendingWrites[i]!=oldWrites[i]) { @@ -420,6 +445,19 @@ int DivPlatformAY8930::dispatch(DivCommand c) { chan[c.chan].autoEnvDen=c.value&15; chan[c.chan].freqChanged=true; break; + case DIV_CMD_AY_IO_WRITE: + if (c.value) { // port B + ioPortB=true; + portBVal=c.value2; + logI("AY I/O port B write: %x\n",portBVal); + } else { // port A + ioPortA=true; + portAVal=c.value2; + logI("AY I/O port A write: %x\n",portAVal); + } + updateOutSel(true); + immWrite(14+(c.value?1:0),(c.value?portBVal:portAVal)); + break; case DIV_ALWAYS_SET_VOLUME: return 0; break; @@ -499,6 +537,11 @@ void DivPlatformAY8930::reset() { extMode=false; bank=false; + ioPortA=false; + ioPortB=false; + portAVal=0; + portBVal=0; + immWrite(0x0d,0xa0); immWrite(0x19,2); // and mask immWrite(0x1a,0x00); // or mask diff --git a/src/engine/platform/ay8930.h b/src/engine/platform/ay8930.h index 2568fd7a..6bd51bdc 100644 --- a/src/engine/platform/ay8930.h +++ b/src/engine/platform/ay8930.h @@ -54,6 +54,8 @@ class DivPlatformAY8930: public DivDispatch { int delay; bool extMode, stereo; + bool ioPortA, ioPortB; + unsigned char portAVal, portBVal; short oldWrites[32]; short pendingWrites[32]; @@ -64,6 +66,8 @@ class DivPlatformAY8930: public DivDispatch { short* ayBuf[3]; size_t ayBufLen; + void updateOutSel(bool immediate=false); + friend void putDispatchChan(void*,int,int); public: diff --git a/src/engine/playback.cpp b/src/engine/playback.cpp index 3a64af7a..aea9e1f8 100644 --- a/src/engine/playback.cpp +++ b/src/engine/playback.cpp @@ -109,6 +109,8 @@ const char* cmdName[DIV_CMD_MAX]={ "AY_NOISE_MASK_AND", "AY_NOISE_MASK_OR", "AY_AUTO_ENVELOPE", + "AY_IO_WRITE", + "AY_AUTO_PWM", "SAA_ENVELOPE", @@ -613,6 +615,12 @@ bool DivEngine::perSystemPostEffect(int ch, unsigned char effect, unsigned char case 0x29: // auto-envelope dispatchCmd(DivCommand(DIV_CMD_AY_AUTO_ENVELOPE,ch,effectVal)); break; + case 0x2e: // I/O port A + dispatchCmd(DivCommand(DIV_CMD_AY_IO_WRITE,ch,0,effectVal)); + break; + case 0x2f: // I/O port B + dispatchCmd(DivCommand(DIV_CMD_AY_IO_WRITE,ch,1,effectVal)); + break; default: return false; } @@ -1031,6 +1039,12 @@ void DivEngine::processRow(int i, bool afterDelay) { chan[i].inPorta=false; if (!song.arpNonPorta) dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,false,0)); break; + case 0xf3: // fine volume ramp up + chan[i].volSpeed=effectVal; + break; + case 0xf4: // fine volume ramp down + chan[i].volSpeed=-effectVal; + break; case 0xf8: // single volume ramp up chan[i].volume=MIN(chan[i].volume+effectVal*256,chan[i].volMax); dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8));