AY: add effects to write to I/O ports
This commit is contained in:
parent
e143359b74
commit
73536c0691
|
@ -40,3 +40,7 @@ AY-3-8914 variant was used in Intellivision, it's basically original AY with 4 l
|
||||||
- `x` is the numerator.
|
- `x` is the numerator.
|
||||||
- `y` is the denominator.
|
- `y` is the denominator.
|
||||||
- if `x` or `y` are 0 this will disable auto-envelope mode.
|
- 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.
|
|
@ -101,6 +101,8 @@ enum DivDispatchCmds {
|
||||||
DIV_CMD_AY_NOISE_MASK_AND,
|
DIV_CMD_AY_NOISE_MASK_AND,
|
||||||
DIV_CMD_AY_NOISE_MASK_OR,
|
DIV_CMD_AY_NOISE_MASK_OR,
|
||||||
DIV_CMD_AY_AUTO_ENVELOPE,
|
DIV_CMD_AY_AUTO_ENVELOPE,
|
||||||
|
DIV_CMD_AY_IO_WRITE,
|
||||||
|
DIV_CMD_AY_AUTO_PWM,
|
||||||
|
|
||||||
DIV_CMD_SAA_ENVELOPE,
|
DIV_CMD_SAA_ENVELOPE,
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
|
|
||||||
#include "ay.h"
|
#include "ay.h"
|
||||||
#include "../engine.h"
|
#include "../engine.h"
|
||||||
|
#include "../../ta-log.h"
|
||||||
#include "sound/ay8910.h"
|
#include "sound/ay8910.h"
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
@ -98,6 +99,12 @@ const char* DivPlatformAY8910::getEffectName(unsigned char effect) {
|
||||||
case 0x29:
|
case 0x29:
|
||||||
return "29xy: Set auto-envelope (x: numerator; y: denominator)";
|
return "29xy: Set auto-envelope (x: numerator; y: denominator)";
|
||||||
break;
|
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;
|
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() {
|
void DivPlatformAY8910::tick() {
|
||||||
// PSG
|
// PSG
|
||||||
for (int i=0; i<3; i++) {
|
for (int i=0; i<3; i++) {
|
||||||
|
@ -221,13 +252,7 @@ void DivPlatformAY8910::tick() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rWrite(0x07,
|
updateOutSel();
|
||||||
~((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)));
|
|
||||||
|
|
||||||
if (ayEnvSlide!=0) {
|
if (ayEnvSlide!=0) {
|
||||||
ayEnvSlideLow+=ayEnvSlide;
|
ayEnvSlideLow+=ayEnvSlide;
|
||||||
|
@ -401,6 +426,19 @@ int DivPlatformAY8910::dispatch(DivCommand c) {
|
||||||
chan[c.chan].autoEnvDen=c.value&15;
|
chan[c.chan].autoEnvDen=c.value&15;
|
||||||
chan[c.chan].freqChanged=true;
|
chan[c.chan].freqChanged=true;
|
||||||
break;
|
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:
|
case DIV_ALWAYS_SET_VOLUME:
|
||||||
return 0;
|
return 0;
|
||||||
break;
|
break;
|
||||||
|
@ -486,6 +524,11 @@ void DivPlatformAY8910::reset() {
|
||||||
delay=0;
|
delay=0;
|
||||||
|
|
||||||
extMode=false;
|
extMode=false;
|
||||||
|
|
||||||
|
ioPortA=false;
|
||||||
|
ioPortB=false;
|
||||||
|
portAVal=0;
|
||||||
|
portBVal=0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DivPlatformAY8910::isStereo() {
|
bool DivPlatformAY8910::isStereo() {
|
||||||
|
|
|
@ -65,6 +65,8 @@ class DivPlatformAY8910: public DivDispatch {
|
||||||
|
|
||||||
bool extMode;
|
bool extMode;
|
||||||
bool stereo, sunsoft, intellivision;
|
bool stereo, sunsoft, intellivision;
|
||||||
|
bool ioPortA, ioPortB;
|
||||||
|
unsigned char portAVal, portBVal;
|
||||||
|
|
||||||
short oldWrites[16];
|
short oldWrites[16];
|
||||||
short pendingWrites[16];
|
short pendingWrites[16];
|
||||||
|
@ -75,6 +77,8 @@ class DivPlatformAY8910: public DivDispatch {
|
||||||
short* ayBuf[3];
|
short* ayBuf[3];
|
||||||
size_t ayBufLen;
|
size_t ayBufLen;
|
||||||
|
|
||||||
|
void updateOutSel(bool immediate=false);
|
||||||
|
|
||||||
friend void putDispatchChan(void*,int,int);
|
friend void putDispatchChan(void*,int,int);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
|
|
||||||
#include "ay8930.h"
|
#include "ay8930.h"
|
||||||
#include "../engine.h"
|
#include "../engine.h"
|
||||||
|
#include "../../ta-log.h"
|
||||||
#include "sound/ay8910.h"
|
#include "sound/ay8910.h"
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
@ -99,6 +100,12 @@ const char* DivPlatformAY8930::getEffectName(unsigned char effect) {
|
||||||
case 0x29:
|
case 0x29:
|
||||||
return "29xy: Set auto-envelope (x: numerator; y: denominator)";
|
return "29xy: Set auto-envelope (x: numerator; y: denominator)";
|
||||||
break;
|
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;
|
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]={
|
const unsigned char regPeriodL[3]={
|
||||||
0x0b, 0x10, 0x12
|
0x0b, 0x10, 0x12
|
||||||
};
|
};
|
||||||
|
@ -262,13 +293,7 @@ void DivPlatformAY8930::tick() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rWrite(0x07,
|
updateOutSel();
|
||||||
~((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)));
|
|
||||||
|
|
||||||
for (int i=0; i<32; i++) {
|
for (int i=0; i<32; i++) {
|
||||||
if (pendingWrites[i]!=oldWrites[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].autoEnvDen=c.value&15;
|
||||||
chan[c.chan].freqChanged=true;
|
chan[c.chan].freqChanged=true;
|
||||||
break;
|
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:
|
case DIV_ALWAYS_SET_VOLUME:
|
||||||
return 0;
|
return 0;
|
||||||
break;
|
break;
|
||||||
|
@ -499,6 +537,11 @@ void DivPlatformAY8930::reset() {
|
||||||
extMode=false;
|
extMode=false;
|
||||||
bank=false;
|
bank=false;
|
||||||
|
|
||||||
|
ioPortA=false;
|
||||||
|
ioPortB=false;
|
||||||
|
portAVal=0;
|
||||||
|
portBVal=0;
|
||||||
|
|
||||||
immWrite(0x0d,0xa0);
|
immWrite(0x0d,0xa0);
|
||||||
immWrite(0x19,2); // and mask
|
immWrite(0x19,2); // and mask
|
||||||
immWrite(0x1a,0x00); // or mask
|
immWrite(0x1a,0x00); // or mask
|
||||||
|
|
|
@ -54,6 +54,8 @@ class DivPlatformAY8930: public DivDispatch {
|
||||||
int delay;
|
int delay;
|
||||||
|
|
||||||
bool extMode, stereo;
|
bool extMode, stereo;
|
||||||
|
bool ioPortA, ioPortB;
|
||||||
|
unsigned char portAVal, portBVal;
|
||||||
|
|
||||||
short oldWrites[32];
|
short oldWrites[32];
|
||||||
short pendingWrites[32];
|
short pendingWrites[32];
|
||||||
|
@ -64,6 +66,8 @@ class DivPlatformAY8930: public DivDispatch {
|
||||||
short* ayBuf[3];
|
short* ayBuf[3];
|
||||||
size_t ayBufLen;
|
size_t ayBufLen;
|
||||||
|
|
||||||
|
void updateOutSel(bool immediate=false);
|
||||||
|
|
||||||
friend void putDispatchChan(void*,int,int);
|
friend void putDispatchChan(void*,int,int);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -109,6 +109,8 @@ const char* cmdName[DIV_CMD_MAX]={
|
||||||
"AY_NOISE_MASK_AND",
|
"AY_NOISE_MASK_AND",
|
||||||
"AY_NOISE_MASK_OR",
|
"AY_NOISE_MASK_OR",
|
||||||
"AY_AUTO_ENVELOPE",
|
"AY_AUTO_ENVELOPE",
|
||||||
|
"AY_IO_WRITE",
|
||||||
|
"AY_AUTO_PWM",
|
||||||
|
|
||||||
"SAA_ENVELOPE",
|
"SAA_ENVELOPE",
|
||||||
|
|
||||||
|
@ -613,6 +615,12 @@ bool DivEngine::perSystemPostEffect(int ch, unsigned char effect, unsigned char
|
||||||
case 0x29: // auto-envelope
|
case 0x29: // auto-envelope
|
||||||
dispatchCmd(DivCommand(DIV_CMD_AY_AUTO_ENVELOPE,ch,effectVal));
|
dispatchCmd(DivCommand(DIV_CMD_AY_AUTO_ENVELOPE,ch,effectVal));
|
||||||
break;
|
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:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1031,6 +1039,12 @@ void DivEngine::processRow(int i, bool afterDelay) {
|
||||||
chan[i].inPorta=false;
|
chan[i].inPorta=false;
|
||||||
if (!song.arpNonPorta) dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,false,0));
|
if (!song.arpNonPorta) dispatchCmd(DivCommand(DIV_CMD_PRE_PORTA,i,false,0));
|
||||||
break;
|
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
|
case 0xf8: // single volume ramp up
|
||||||
chan[i].volume=MIN(chan[i].volume+effectVal*256,chan[i].volMax);
|
chan[i].volume=MIN(chan[i].volume+effectVal*256,chan[i].volMax);
|
||||||
dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8));
|
dispatchCmd(DivCommand(DIV_CMD_VOLUME,i,chan[i].volume>>8));
|
||||||
|
|
Loading…
Reference in New Issue