mirror of
https://github.com/tildearrow/furnace.git
synced 2024-11-22 12:35:11 +00:00
add AY-3-8910 platform!
this paves the way for eventual AY-3-8930 platform...
This commit is contained in:
parent
2fcb474544
commit
0b352ecd9a
9 changed files with 2372 additions and 3 deletions
|
@ -65,6 +65,7 @@ src/log.cpp
|
|||
extern/Nuked-OPN2/ym3438.c
|
||||
extern/opm/opm.c
|
||||
src/engine/platform/sound/sn76496.cpp
|
||||
src/engine/platform/sound/ay8910.cpp
|
||||
src/engine/platform/sound/gb/apu.c
|
||||
src/engine/platform/sound/gb/timing.c
|
||||
src/engine/platform/sound/pce_psg.cpp
|
||||
|
@ -116,6 +117,7 @@ src/engine/platform/c64.cpp
|
|||
src/engine/platform/arcade.cpp
|
||||
src/engine/platform/ym2610.cpp
|
||||
src/engine/platform/ym2610ext.cpp
|
||||
src/engine/platform/ay.cpp
|
||||
src/engine/platform/dummy.cpp)
|
||||
|
||||
set(GUI_SOURCES
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "platform/arcade.h"
|
||||
#include "platform/ym2610.h"
|
||||
#include "platform/ym2610ext.h"
|
||||
#include "platform/ay.h"
|
||||
#include "platform/dummy.h"
|
||||
#include "../ta-log.h"
|
||||
|
||||
|
@ -112,6 +113,9 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do
|
|||
case DIV_SYSTEM_YM2610_EXT:
|
||||
dispatch=new DivPlatformYM2610Ext;
|
||||
break;
|
||||
case DIV_SYSTEM_AY8910:
|
||||
dispatch=new DivPlatformAY8910;
|
||||
break;
|
||||
default:
|
||||
logW("this system is not supported yet! using dummy platform.\n");
|
||||
dispatch=new DivPlatformDummy;
|
||||
|
|
357
src/engine/platform/ay.cpp
Normal file
357
src/engine/platform/ay.cpp
Normal file
|
@ -0,0 +1,357 @@
|
|||
#include "ay.h"
|
||||
#include "../engine.h"
|
||||
#include "sound/ay8910.h"
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
#define rWrite(a,v) if (!skipRegisterWrites) {pendingWrites[a]=v;}
|
||||
#define immWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v);}
|
||||
|
||||
#define PSG_FREQ_BASE 7640.0f
|
||||
|
||||
void DivPlatformAY8910::acquire(short* bufL, short* bufR, size_t start, size_t len) {
|
||||
if (ayBufLen<len) {
|
||||
ayBufLen=len;
|
||||
for (int i=0; i<3; i++) {
|
||||
delete[] ayBuf[i];
|
||||
ayBuf[i]=new short[ayBufLen];
|
||||
}
|
||||
}
|
||||
while (!writes.empty()) {
|
||||
QueuedWrite w=writes.front();
|
||||
ay->address_w(w.addr);
|
||||
ay->data_w(w.val);
|
||||
writes.pop();
|
||||
}
|
||||
ay->sound_stream_update(ayBuf,len);
|
||||
for (size_t i=0; i<len; i++) {
|
||||
bufL[i+start]=ayBuf[0][i]+ayBuf[1][i]+ayBuf[2][i];
|
||||
bufR[i+start]=ayBuf[0][i]+ayBuf[1][i]+ayBuf[2][i];
|
||||
}
|
||||
//static int os[2];
|
||||
|
||||
/*for (size_t h=start; h<start+len; h++) {
|
||||
os[0]=0; os[1]=0;
|
||||
if (!writes.empty()) {
|
||||
if (--delay<1) {
|
||||
QueuedWrite& w=writes.front();
|
||||
fm->write(0x0+((w.addr>>8)<<1),w.addr);
|
||||
fm->write(0x1+((w.addr>>8)<<1),w.val);
|
||||
writes.pop();
|
||||
delay=4;
|
||||
}
|
||||
}
|
||||
|
||||
fm->generate(&fmout);
|
||||
|
||||
os[0]=fmout.data[0]+(fmout.data[2]>>1);
|
||||
if (os[0]<-32768) os[0]=-32768;
|
||||
if (os[0]>32767) os[0]=32767;
|
||||
|
||||
os[1]=fmout.data[1]+(fmout.data[2]>>1);
|
||||
if (os[1]<-32768) os[1]=-32768;
|
||||
if (os[1]>32767) os[1]=32767;
|
||||
|
||||
bufL[h]=os[0];
|
||||
bufR[h]=os[1];
|
||||
}*/
|
||||
}
|
||||
|
||||
void DivPlatformAY8910::tick() {
|
||||
// PSG
|
||||
for (int i=0; i<3; i++) {
|
||||
chan[i].std.next();
|
||||
if (chan[i].std.hadVol) {
|
||||
chan[i].outVol=chan[i].std.vol-(15-chan[i].vol);
|
||||
if (chan[i].outVol<0) chan[i].outVol=0;
|
||||
if (isMuted[i]) {
|
||||
rWrite(0x08+i,0);
|
||||
} else {
|
||||
rWrite(0x08+i,(chan[i].outVol&15)|((chan[i].psgMode&4)<<2));
|
||||
}
|
||||
}
|
||||
if (chan[i].std.hadArp) {
|
||||
if (!chan[i].inPorta) {
|
||||
if (chan[i].std.arpMode) {
|
||||
chan[i].baseFreq=round(PSG_FREQ_BASE/pow(2.0f,((float)(chan[i].std.arp)/12.0f)));
|
||||
} else {
|
||||
chan[i].baseFreq=round(PSG_FREQ_BASE/pow(2.0f,((float)(chan[i].note+chan[i].std.arp-12)/12.0f)));
|
||||
}
|
||||
}
|
||||
chan[i].freqChanged=true;
|
||||
} else {
|
||||
if (chan[i].std.arpMode && chan[i].std.finishedArp) {
|
||||
chan[i].baseFreq=round(PSG_FREQ_BASE/pow(2.0f,((float)(chan[i].note)/12.0f)));
|
||||
chan[i].freqChanged=true;
|
||||
}
|
||||
}
|
||||
if (chan[i].std.hadDuty) {
|
||||
rWrite(0x06,31-chan[i].std.duty);
|
||||
}
|
||||
if (chan[i].std.hadWave) {
|
||||
chan[i].psgMode&=4;
|
||||
chan[i].psgMode|=(chan[i].std.wave+1)&3;
|
||||
}
|
||||
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true);
|
||||
if (chan[i].freq>4095) chan[i].freq=4095;
|
||||
if (chan[i].keyOn) {
|
||||
//rWrite(16+i*5+1,((chan[i].duty&3)<<6)|(63-(ins->gb.soundLen&63)));
|
||||
//rWrite(16+i*5+2,((chan[i].vol<<4))|(ins->gb.envLen&7)|((ins->gb.envDir&1)<<3));
|
||||
}
|
||||
if (chan[i].keyOff) {
|
||||
rWrite(0x08+i,0);
|
||||
}
|
||||
rWrite((i)<<1,chan[i].freq&0xff);
|
||||
rWrite(1+((i)<<1),chan[i].freq>>8);
|
||||
if (chan[i].keyOn) chan[i].keyOn=false;
|
||||
if (chan[i].keyOff) chan[i].keyOff=false;
|
||||
chan[i].freqChanged=false;
|
||||
}
|
||||
}
|
||||
|
||||
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)));
|
||||
|
||||
if (ayEnvSlide!=0) {
|
||||
ayEnvSlideLow+=ayEnvSlide;
|
||||
while (ayEnvSlideLow>7) {
|
||||
ayEnvSlideLow-=8;
|
||||
if (ayEnvPeriod<0xffff) {
|
||||
ayEnvPeriod++;
|
||||
immWrite(0x0b,ayEnvPeriod);
|
||||
immWrite(0x0c,ayEnvPeriod>>8);
|
||||
}
|
||||
}
|
||||
while (ayEnvSlideLow<-7) {
|
||||
ayEnvSlideLow+=8;
|
||||
if (ayEnvPeriod>0) {
|
||||
ayEnvPeriod--;
|
||||
immWrite(0x0b,ayEnvPeriod);
|
||||
immWrite(0x0c,ayEnvPeriod>>8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i=0; i<16; i++) {
|
||||
if (pendingWrites[i]!=oldWrites[i]) {
|
||||
immWrite(i,pendingWrites[i]&0xff);
|
||||
oldWrites[i]=pendingWrites[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int DivPlatformAY8910::dispatch(DivCommand c) {
|
||||
switch (c.cmd) {
|
||||
case DIV_CMD_NOTE_ON: {
|
||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins);
|
||||
chan[c.chan].baseFreq=round(PSG_FREQ_BASE/pow(2.0f,((float)c.value/12.0f)));
|
||||
chan[c.chan].freqChanged=true;
|
||||
chan[c.chan].note=c.value;
|
||||
chan[c.chan].active=true;
|
||||
chan[c.chan].keyOn=true;
|
||||
chan[c.chan].std.init(ins);
|
||||
if (isMuted[c.chan]) {
|
||||
rWrite(0x08+c.chan,0);
|
||||
} else {
|
||||
rWrite(0x08+c.chan,(chan[c.chan].vol&15)|((chan[c.chan].psgMode&4)<<2));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_NOTE_OFF:
|
||||
chan[c.chan].keyOff=true;
|
||||
chan[c.chan].active=false;
|
||||
chan[c.chan].std.init(NULL);
|
||||
break;
|
||||
case DIV_CMD_VOLUME: {
|
||||
chan[c.chan].vol=c.value;
|
||||
if (!chan[c.chan].std.hasVol) {
|
||||
chan[c.chan].outVol=c.value;
|
||||
}
|
||||
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));
|
||||
}
|
||||
break;
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_GET_VOLUME: {
|
||||
return chan[c.chan].vol;
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_INSTRUMENT:
|
||||
if (chan[c.chan].ins!=c.value) {
|
||||
chan[c.chan].insChanged=true;
|
||||
}
|
||||
chan[c.chan].ins=c.value;
|
||||
break;
|
||||
case DIV_CMD_PITCH: {
|
||||
chan[c.chan].pitch=c.value;
|
||||
chan[c.chan].freqChanged=true;
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_NOTE_PORTA: {
|
||||
int destFreq=round(PSG_FREQ_BASE/pow(2.0f,((float)c.value2/12.0f)));
|
||||
bool return2=false;
|
||||
if (destFreq>chan[c.chan].baseFreq) {
|
||||
chan[c.chan].baseFreq+=c.value;
|
||||
if (chan[c.chan].baseFreq>=destFreq) {
|
||||
chan[c.chan].baseFreq=destFreq;
|
||||
return2=true;
|
||||
}
|
||||
} else {
|
||||
chan[c.chan].baseFreq-=c.value;
|
||||
if (chan[c.chan].baseFreq<=destFreq) {
|
||||
chan[c.chan].baseFreq=destFreq;
|
||||
return2=true;
|
||||
}
|
||||
}
|
||||
chan[c.chan].freqChanged=true;
|
||||
if (return2) {
|
||||
chan[c.chan].inPorta=false;
|
||||
return 2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_LEGATO: {
|
||||
chan[c.chan].baseFreq=round(PSG_FREQ_BASE/pow(2.0f,((float)c.value/12.0f)));
|
||||
chan[c.chan].freqChanged=true;
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_STD_NOISE_FREQ:
|
||||
rWrite(0x06,31-c.value);
|
||||
break;
|
||||
case DIV_CMD_AY_ENVELOPE_SET:
|
||||
ayEnvMode=c.value>>4;
|
||||
rWrite(0x0d,ayEnvMode);
|
||||
if (c.value&15) {
|
||||
chan[c.chan].psgMode|=4;
|
||||
} else {
|
||||
chan[c.chan].psgMode&=~4;
|
||||
}
|
||||
if (isMuted[c.chan]) {
|
||||
rWrite(0x08+c.chan,0);
|
||||
} else {
|
||||
rWrite(0x08+c.chan,(chan[c.chan].vol&15)|((chan[c.chan].psgMode&4)<<2));
|
||||
}
|
||||
break;
|
||||
case DIV_CMD_AY_ENVELOPE_LOW:
|
||||
ayEnvPeriod&=0xff00;
|
||||
ayEnvPeriod|=c.value;
|
||||
immWrite(0x0b,ayEnvPeriod);
|
||||
immWrite(0x0c,ayEnvPeriod>>8);
|
||||
break;
|
||||
case DIV_CMD_AY_ENVELOPE_HIGH:
|
||||
ayEnvPeriod&=0xff;
|
||||
ayEnvPeriod|=c.value<<8;
|
||||
immWrite(0x0b,ayEnvPeriod);
|
||||
immWrite(0x0c,ayEnvPeriod>>8);
|
||||
break;
|
||||
case DIV_CMD_AY_ENVELOPE_SLIDE:
|
||||
ayEnvSlide=c.value;
|
||||
break;
|
||||
case DIV_ALWAYS_SET_VOLUME:
|
||||
return 0;
|
||||
break;
|
||||
case DIV_CMD_GET_VOLMAX:
|
||||
return 15;
|
||||
break;
|
||||
case DIV_CMD_PRE_PORTA:
|
||||
chan[c.chan].std.init(parent->getIns(chan[c.chan].ins));
|
||||
chan[c.chan].inPorta=c.value;
|
||||
break;
|
||||
case DIV_CMD_PRE_NOTE:
|
||||
break;
|
||||
default:
|
||||
//printf("WARNING: unimplemented command %d\n",c.cmd);
|
||||
break;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void DivPlatformAY8910::muteChannel(int ch, bool mute) {
|
||||
isMuted[ch]=mute;
|
||||
if (isMuted[ch]) {
|
||||
rWrite(0x08+ch,0);
|
||||
} else {
|
||||
rWrite(0x08+ch,(chan[ch].outVol&15)|((chan[ch].psgMode&4)<<2));
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformAY8910::forceIns() {
|
||||
for (int i=0; i<3; i++) {
|
||||
chan[i].insChanged=true;
|
||||
}
|
||||
immWrite(0x0b,ayEnvPeriod);
|
||||
immWrite(0x0c,ayEnvPeriod>>8);
|
||||
immWrite(0x0d,ayEnvMode);
|
||||
}
|
||||
|
||||
void DivPlatformAY8910::reset() {
|
||||
while (!writes.empty()) writes.pop();
|
||||
ay->device_reset();
|
||||
for (int i=0; i<3; i++) {
|
||||
chan[i]=DivPlatformAY8910::Channel();
|
||||
chan[i].vol=0x0f;
|
||||
}
|
||||
|
||||
for (int i=0; i<16; i++) {
|
||||
oldWrites[i]=-1;
|
||||
pendingWrites[i]=-1;
|
||||
}
|
||||
|
||||
lastBusy=60;
|
||||
dacMode=0;
|
||||
dacPeriod=0;
|
||||
dacPos=0;
|
||||
dacRate=0;
|
||||
dacSample=-1;
|
||||
sampleBank=0;
|
||||
ayEnvPeriod=0;
|
||||
ayEnvMode=0;
|
||||
ayEnvSlide=0;
|
||||
ayEnvSlideLow=0;
|
||||
|
||||
delay=0;
|
||||
|
||||
extMode=false;
|
||||
}
|
||||
|
||||
bool DivPlatformAY8910::isStereo() {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DivPlatformAY8910::keyOffAffectsArp(int ch) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int DivPlatformAY8910::init(DivEngine* p, int channels, int sugRate, bool pal) {
|
||||
parent=p;
|
||||
skipRegisterWrites=false;
|
||||
for (int i=0; i<3; i++) {
|
||||
isMuted[i]=false;
|
||||
}
|
||||
if (pal) {
|
||||
rate=250000;
|
||||
} else {
|
||||
rate=250000;
|
||||
}
|
||||
ay=new ay8910_device(rate);
|
||||
ay->set_psg_type(ay8910_device::PSG_TYPE_AY);
|
||||
ay->device_start();
|
||||
ayBufLen=65536;
|
||||
for (int i=0; i<3; i++) ayBuf[i]=new short[ayBufLen];
|
||||
reset();
|
||||
return 3;
|
||||
}
|
||||
|
||||
void DivPlatformAY8910::quit() {
|
||||
for (int i=0; i<3; i++) delete[] ayBuf[i];
|
||||
delete ay;
|
||||
}
|
65
src/engine/platform/ay.h
Normal file
65
src/engine/platform/ay.h
Normal file
|
@ -0,0 +1,65 @@
|
|||
#ifndef _AY_H
|
||||
#define _AY_H
|
||||
#include "../dispatch.h"
|
||||
#include "../macroInt.h"
|
||||
#include <queue>
|
||||
#include "sound/ay8910.h"
|
||||
|
||||
class DivPlatformAY8910: public DivDispatch {
|
||||
protected:
|
||||
struct Channel {
|
||||
unsigned char freqH, freqL;
|
||||
int freq, baseFreq, pitch;
|
||||
unsigned char ins, note, psgMode;
|
||||
signed char konCycles;
|
||||
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, inPorta;
|
||||
int vol, outVol;
|
||||
unsigned char pan;
|
||||
DivMacroInt std;
|
||||
Channel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), ins(-1), note(0), psgMode(1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), portaPause(false), inPorta(false), vol(0), outVol(15), pan(3) {}
|
||||
};
|
||||
Channel chan[3];
|
||||
bool isMuted[3];
|
||||
struct QueuedWrite {
|
||||
unsigned short addr;
|
||||
unsigned char val;
|
||||
bool addrOrVal;
|
||||
QueuedWrite(unsigned short a, unsigned char v): addr(a), val(v), addrOrVal(false) {}
|
||||
};
|
||||
std::queue<QueuedWrite> writes;
|
||||
ay8910_device* ay;
|
||||
unsigned char lastBusy;
|
||||
|
||||
bool dacMode;
|
||||
int dacPeriod;
|
||||
int dacRate;
|
||||
int dacPos;
|
||||
int dacSample;
|
||||
unsigned char sampleBank;
|
||||
|
||||
int delay;
|
||||
|
||||
bool extMode;
|
||||
|
||||
short oldWrites[16];
|
||||
short pendingWrites[16];
|
||||
unsigned char ayEnvMode;
|
||||
unsigned short ayEnvPeriod;
|
||||
short ayEnvSlideLow;
|
||||
short ayEnvSlide;
|
||||
short* ayBuf[3];
|
||||
size_t ayBufLen;
|
||||
|
||||
public:
|
||||
void acquire(short* bufL, short* bufR, size_t start, size_t len);
|
||||
int dispatch(DivCommand c);
|
||||
void reset();
|
||||
void forceIns();
|
||||
void tick();
|
||||
void muteChannel(int ch, bool mute);
|
||||
bool isStereo();
|
||||
bool keyOffAffectsArp(int ch);
|
||||
int init(DivEngine* parent, int channels, int sugRate, bool pal);
|
||||
void quit();
|
||||
};
|
||||
#endif
|
1543
src/engine/platform/sound/ay8910.cpp
Normal file
1543
src/engine/platform/sound/ay8910.cpp
Normal file
File diff suppressed because it is too large
Load diff
371
src/engine/platform/sound/ay8910.h
Normal file
371
src/engine/platform/sound/ay8910.h
Normal file
|
@ -0,0 +1,371 @@
|
|||
// license:BSD-3-Clause
|
||||
// copyright-holders:Couriersud
|
||||
#ifndef MAME_SOUND_AY8910_H
|
||||
#define MAME_SOUND_AY8910_H
|
||||
|
||||
#define ALL_8910_CHANNELS -1
|
||||
|
||||
/* Internal resistance at Volume level 7. */
|
||||
|
||||
#define AY8910_INTERNAL_RESISTANCE (356)
|
||||
#define YM2149_INTERNAL_RESISTANCE (353)
|
||||
|
||||
/*
|
||||
* The following is used by all drivers not reviewed yet.
|
||||
* This will like the old behavior, output between
|
||||
* 0 and 7FFF
|
||||
*/
|
||||
#define AY8910_LEGACY_OUTPUT (0x01)
|
||||
|
||||
/*
|
||||
* Specifying the next define will simulate the special
|
||||
* cross channel mixing if outputs are tied together.
|
||||
* The driver will only provide one stream in this case.
|
||||
*/
|
||||
#define AY8910_SINGLE_OUTPUT (0x02)
|
||||
|
||||
/*
|
||||
* The following define is the default behavior.
|
||||
* Output level 0 is 0V and 7ffff corresponds to 5V.
|
||||
* Use this to specify that a discrete mixing stage
|
||||
* follows.
|
||||
*/
|
||||
#define AY8910_DISCRETE_OUTPUT (0x04)
|
||||
|
||||
/*
|
||||
* The following define causes the driver to output
|
||||
* resistor values. Intended to be used for
|
||||
* netlist interfacing.
|
||||
*/
|
||||
|
||||
#define AY8910_RESISTOR_OUTPUT (0x08)
|
||||
|
||||
/*
|
||||
* This define specifies the initial state of
|
||||
* YM2149, YM3439, AY8930 pin 26 (SEL pin).
|
||||
* By default it is set to high,
|
||||
* compatible with AY8910.
|
||||
*/
|
||||
/* TODO: make it controllable while it's running (used by any hw???) */
|
||||
#define YM2149_PIN26_HIGH (0x00) /* or N/C */
|
||||
#define YM2149_PIN26_LOW (0x10)
|
||||
|
||||
#define BIT(x,n) (((x)>>(n))&1)
|
||||
|
||||
enum device_type {
|
||||
AY8910,
|
||||
AY8912,
|
||||
AY8913,
|
||||
AY8914,
|
||||
AY8930,
|
||||
YM2149,
|
||||
YM3439,
|
||||
YMZ284,
|
||||
YMZ294,
|
||||
SUNSOFT_5B_SOUND
|
||||
};
|
||||
|
||||
|
||||
class ay8910_device
|
||||
{
|
||||
public:
|
||||
enum psg_type_t
|
||||
{
|
||||
PSG_TYPE_AY,
|
||||
PSG_TYPE_YM
|
||||
};
|
||||
|
||||
enum config_t
|
||||
{
|
||||
PSG_DEFAULT = 0x0,
|
||||
PSG_PIN26_IS_CLKSEL = 0x1,
|
||||
PSG_HAS_INTERNAL_DIVIDER = 0x2,
|
||||
PSG_EXTENDED_ENVELOPE = 0x4,
|
||||
PSG_HAS_EXPANDED_MODE = 0x8
|
||||
};
|
||||
|
||||
// construction/destruction
|
||||
ay8910_device(unsigned int clock);
|
||||
|
||||
// configuration helpers
|
||||
void set_flags(int flags) { m_flags = flags; }
|
||||
void set_psg_type(psg_type_t psg_type) { set_type(psg_type); }
|
||||
void set_resistors_load(int res_load0, int res_load1, int res_load2) { m_res_load[0] = res_load0; m_res_load[1] = res_load1; m_res_load[2] = res_load2; }
|
||||
|
||||
unsigned char data_r() { return ay8910_read_ym(); }
|
||||
void address_w(unsigned char data);
|
||||
void data_w(unsigned char data);
|
||||
|
||||
// /RES
|
||||
void reset_w(unsigned char data = 0) { ay8910_reset_ym(); }
|
||||
|
||||
// use this when BC1 == A0; here, BC1=0 selects 'data' and BC1=1 selects 'latch address'
|
||||
void data_address_w(int offset, unsigned char data) { ay8910_write_ym(~offset & 1, data); } // note that directly connecting BC1 to A0 puts data on 0 and address on 1
|
||||
|
||||
// use this when BC1 == !A0; here, BC1=0 selects 'latch address' and BC1=1 selects 'data'
|
||||
void address_data_w(int offset, unsigned char data) { ay8910_write_ym(offset & 1, data); }
|
||||
|
||||
// bc1=a0, bc2=a1
|
||||
void write_bc1_bc2(int offset, unsigned char data);
|
||||
|
||||
struct ay_ym_param
|
||||
{
|
||||
double r_up;
|
||||
double r_down;
|
||||
int res_count;
|
||||
double res[32];
|
||||
};
|
||||
|
||||
struct mosfet_param
|
||||
{
|
||||
double m_Vth;
|
||||
double m_Vg;
|
||||
int m_count;
|
||||
double m_Kn[32];
|
||||
};
|
||||
|
||||
// internal interface for PSG component of YM device
|
||||
// FIXME: these should be private, but vector06 accesses them directly
|
||||
|
||||
ay8910_device(device_type type, unsigned int clock, psg_type_t psg_type, int streams, int ioports, int feature = PSG_DEFAULT);
|
||||
|
||||
// device-level overrides
|
||||
void device_start();
|
||||
void device_reset();
|
||||
|
||||
// sound stream update overrides
|
||||
void sound_stream_update(short** outputs, int outLen);
|
||||
|
||||
void ay8910_write_ym(int addr, unsigned char data);
|
||||
unsigned char ay8910_read_ym();
|
||||
void ay8910_reset_ym();
|
||||
|
||||
private:
|
||||
static constexpr int NUM_CHANNELS = 3;
|
||||
device_type chip_type;
|
||||
|
||||
/* register id's */
|
||||
enum
|
||||
{
|
||||
AY_AFINE = 0x00,
|
||||
AY_ACOARSE = 0x01,
|
||||
AY_BFINE = 0x02,
|
||||
AY_BCOARSE = 0x03,
|
||||
AY_CFINE = 0x04,
|
||||
AY_CCOARSE = 0x05,
|
||||
AY_NOISEPER = 0x06,
|
||||
AY_ENABLE = 0x07,
|
||||
AY_AVOL = 0x08,
|
||||
AY_BVOL = 0x09,
|
||||
AY_CVOL = 0x0a,
|
||||
AY_EAFINE = 0x0b,
|
||||
AY_EACOARSE = 0x0c,
|
||||
AY_EASHAPE = 0x0d,
|
||||
AY_PORTA = 0x0e,
|
||||
AY_PORTB = 0x0f,
|
||||
AY_EBFINE = 0x10,
|
||||
AY_EBCOARSE = 0x11,
|
||||
AY_ECFINE = 0x12,
|
||||
AY_ECCOARSE = 0x13,
|
||||
AY_EBSHAPE = 0x14,
|
||||
AY_ECSHAPE = 0x15,
|
||||
AY_ADUTY = 0x16,
|
||||
AY_BDUTY = 0x17,
|
||||
AY_CDUTY = 0x18,
|
||||
AY_NOISEAND = 0x19,
|
||||
AY_NOISEOR = 0x1a,
|
||||
AY_TEST = 0x1f
|
||||
};
|
||||
|
||||
// structs
|
||||
struct tone_t
|
||||
{
|
||||
unsigned int period;
|
||||
unsigned char volume;
|
||||
unsigned char duty;
|
||||
int count;
|
||||
unsigned char duty_cycle;
|
||||
unsigned char output;
|
||||
|
||||
void reset()
|
||||
{
|
||||
period = 0;
|
||||
volume = 0;
|
||||
duty = 0;
|
||||
count = 0;
|
||||
duty_cycle = 0;
|
||||
output = 0;
|
||||
}
|
||||
|
||||
void set_period(unsigned char fine, unsigned char coarse)
|
||||
{
|
||||
period = fine | (coarse << 8);
|
||||
}
|
||||
|
||||
void set_volume(unsigned char val)
|
||||
{
|
||||
volume = val;
|
||||
}
|
||||
|
||||
void set_duty(unsigned char val)
|
||||
{
|
||||
duty = val;
|
||||
}
|
||||
};
|
||||
|
||||
struct envelope_t
|
||||
{
|
||||
unsigned int period;
|
||||
int count;
|
||||
signed char step;
|
||||
unsigned int volume;
|
||||
unsigned char hold, alternate, attack, holding;
|
||||
|
||||
void reset()
|
||||
{
|
||||
period = 0;
|
||||
count = 0;
|
||||
step = 0;
|
||||
volume = 0;
|
||||
hold = 0;
|
||||
alternate = 0;
|
||||
attack = 0;
|
||||
holding = 0;
|
||||
}
|
||||
|
||||
void set_period(unsigned char fine, unsigned char coarse)
|
||||
{
|
||||
period = fine | (coarse << 8);
|
||||
}
|
||||
|
||||
void set_shape(unsigned char shape, unsigned char mask)
|
||||
{
|
||||
attack = (shape & 0x04) ? mask : 0x00;
|
||||
if ((shape & 0x08) == 0)
|
||||
{
|
||||
/* if Continue = 0, map the shape to the equivalent one which has Continue = 1 */
|
||||
hold = 1;
|
||||
alternate = attack;
|
||||
}
|
||||
else
|
||||
{
|
||||
hold = shape & 0x01;
|
||||
alternate = shape & 0x02;
|
||||
}
|
||||
step = mask;
|
||||
holding = 0;
|
||||
volume = (step ^ attack);
|
||||
}
|
||||
};
|
||||
|
||||
// inlines
|
||||
inline bool tone_enable(int chan) { return BIT(m_regs[AY_ENABLE], chan); }
|
||||
inline unsigned char tone_volume(tone_t *tone) { return tone->volume & (is_expanded_mode() ? 0x1f : 0x0f); }
|
||||
inline unsigned char tone_envelope(tone_t *tone) { return (tone->volume >> (is_expanded_mode() ? 5 : 4)) & ((m_feature & PSG_EXTENDED_ENVELOPE) ? 3 : 1); }
|
||||
inline unsigned char tone_duty(tone_t *tone) { return is_expanded_mode() ? (tone->duty & 0x8 ? 0x8 : (tone->duty & 0xf)) : 0x4; }
|
||||
inline unsigned char get_envelope_chan(int chan) { return is_expanded_mode() ? chan : 0; }
|
||||
|
||||
inline bool noise_enable(int chan) { return BIT(m_regs[AY_ENABLE], 3 + chan); }
|
||||
inline unsigned char noise_period() { return is_expanded_mode() ? m_regs[AY_NOISEPER] & 0xff : m_regs[AY_NOISEPER] & 0x1f; }
|
||||
inline unsigned char noise_output() { return m_rng & 1; }
|
||||
|
||||
inline bool is_expanded_mode() { return ((m_feature & PSG_HAS_EXPANDED_MODE) && ((m_mode & 0xe) == 0xa)); }
|
||||
inline unsigned char get_register_bank() { return is_expanded_mode() ? (m_mode & 0x1) << 4 : 0; }
|
||||
|
||||
// internal helpers
|
||||
void set_type(psg_type_t psg_type);
|
||||
inline float mix_3D();
|
||||
void ay8910_write_reg(int r, int v);
|
||||
void build_mixer_table();
|
||||
|
||||
// internal state
|
||||
psg_type_t m_type;
|
||||
int m_streams;
|
||||
int m_ioports;
|
||||
int m_ready;
|
||||
//sound_stream *m_channel;
|
||||
bool m_active;
|
||||
int m_register_latch;
|
||||
unsigned char m_regs[16 * 2];
|
||||
int m_last_enable;
|
||||
tone_t m_tone[NUM_CHANNELS];
|
||||
envelope_t m_envelope[NUM_CHANNELS];
|
||||
unsigned char m_prescale_noise;
|
||||
int m_count_noise;
|
||||
int m_rng;
|
||||
unsigned char m_mode;
|
||||
unsigned char m_env_step_mask;
|
||||
/* init parameters ... */
|
||||
int m_step;
|
||||
int m_zero_is_off;
|
||||
unsigned char m_vol_enabled[NUM_CHANNELS];
|
||||
const ay_ym_param *m_par;
|
||||
const ay_ym_param *m_par_env;
|
||||
short m_vol_table[NUM_CHANNELS][16];
|
||||
short m_env_table[NUM_CHANNELS][32];
|
||||
short m_vol3d_table[32*32*32*8];
|
||||
int m_flags; /* Flags */
|
||||
int m_feature; /* Chip specific features */
|
||||
int m_res_load[3]; /* Load on channel in ohms */
|
||||
};
|
||||
|
||||
class ay8912_device : public ay8910_device
|
||||
{
|
||||
public:
|
||||
ay8912_device(unsigned int clock);
|
||||
};
|
||||
|
||||
class ay8913_device : public ay8910_device
|
||||
{
|
||||
public:
|
||||
ay8913_device(unsigned int clock);
|
||||
};
|
||||
|
||||
class ay8914_device : public ay8910_device
|
||||
{
|
||||
public:
|
||||
ay8914_device(unsigned int clock);
|
||||
|
||||
/* AY8914 handlers needed due to different register map */
|
||||
unsigned char read(int offset);
|
||||
void write(int offset, unsigned char data);
|
||||
};
|
||||
|
||||
class ay8930_device : public ay8910_device
|
||||
{
|
||||
public:
|
||||
ay8930_device(unsigned int clock);
|
||||
};
|
||||
|
||||
class ym2149_device : public ay8910_device
|
||||
{
|
||||
public:
|
||||
ym2149_device(unsigned int clock);
|
||||
};
|
||||
|
||||
class ym3439_device : public ay8910_device
|
||||
{
|
||||
public:
|
||||
ym3439_device(unsigned int clock);
|
||||
};
|
||||
|
||||
class ymz284_device : public ay8910_device
|
||||
{
|
||||
public:
|
||||
ymz284_device(unsigned int clock);
|
||||
};
|
||||
|
||||
class ymz294_device : public ay8910_device
|
||||
{
|
||||
public:
|
||||
ymz294_device(unsigned int clock);
|
||||
};
|
||||
|
||||
class sunsoft_5b_sound_device : public ay8910_device
|
||||
{
|
||||
public:
|
||||
sunsoft_5b_sound_device(unsigned int clock);
|
||||
};
|
||||
|
||||
|
||||
#endif // MAME_DEVICES_SOUND_AY8910_H
|
|
@ -327,6 +327,31 @@ bool DivEngine::perSystemPostEffect(int ch, unsigned char effect, unsigned char
|
|||
return false;
|
||||
}
|
||||
break;
|
||||
case DIV_SYSTEM_AY8910:
|
||||
switch (effect) {
|
||||
case 0x20: // mode
|
||||
dispatchCmd(DivCommand(DIV_CMD_STD_NOISE_MODE,ch,effectVal));
|
||||
break;
|
||||
case 0x21: // noise freq
|
||||
dispatchCmd(DivCommand(DIV_CMD_STD_NOISE_FREQ,ch,effectVal));
|
||||
break;
|
||||
case 0x22: // envelope enable
|
||||
dispatchCmd(DivCommand(DIV_CMD_AY_ENVELOPE_SET,ch,effectVal));
|
||||
break;
|
||||
case 0x23: // envelope period low
|
||||
dispatchCmd(DivCommand(DIV_CMD_AY_ENVELOPE_LOW,ch,effectVal));
|
||||
break;
|
||||
case 0x24: // envelope period high
|
||||
dispatchCmd(DivCommand(DIV_CMD_AY_ENVELOPE_HIGH,ch,effectVal));
|
||||
break;
|
||||
case 0x25: // envelope slide up
|
||||
dispatchCmd(DivCommand(DIV_CMD_AY_ENVELOPE_SLIDE,ch,-effectVal));
|
||||
break;
|
||||
case 0x26: // envelope slide down
|
||||
dispatchCmd(DivCommand(DIV_CMD_AY_ENVELOPE_SLIDE,ch,effectVal));
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -1532,7 +1532,7 @@ void FurnaceGUI::drawPattern() {
|
|||
ImGui::End();
|
||||
}
|
||||
|
||||
const char* aboutLine[53]={
|
||||
const char* aboutLine[54]={
|
||||
"tildearrow",
|
||||
"is proud to present",
|
||||
"",
|
||||
|
@ -1555,6 +1555,7 @@ const char* aboutLine[53]={
|
|||
"Nuked-OPM & Nuked-OPN2 by Nuke.YKT",
|
||||
"ymfm by Aaron Giles",
|
||||
"MAME SN76496 by Nicola Salmoria",
|
||||
"MAME AY-3-8910 by Couriersud",
|
||||
"SameBoy by Lior Halphon",
|
||||
"Mednafen PCE",
|
||||
"puNES by FHorse",
|
||||
|
@ -1637,7 +1638,7 @@ void FurnaceGUI::drawAbout() {
|
|||
}
|
||||
}
|
||||
|
||||
for (int i=0; i<53; i++) {
|
||||
for (int i=0; i<54; i++) {
|
||||
double posX=(scrW*dpiScale/2.0)+(sin(double(i)*0.5+double(aboutScroll)/90.0)*120*dpiScale)-(ImGui::CalcTextSize(aboutLine[i]).x*0.5);
|
||||
double posY=(scrH-aboutScroll+42*i)*dpiScale;
|
||||
if (posY<-80*dpiScale || posY>scrH*dpiScale) continue;
|
||||
|
@ -1660,7 +1661,7 @@ void FurnaceGUI::drawAbout() {
|
|||
ImGui::PopFont();
|
||||
aboutScroll+=2;
|
||||
if (++aboutSin>=2400) aboutSin=0;
|
||||
if (aboutScroll>(42*53+scrH)) aboutScroll=-20;
|
||||
if (aboutScroll>(42*54+scrH)) aboutScroll=-20;
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
|
|
@ -110,6 +110,7 @@ bool pVersion(String) {
|
|||
printf("- Nuked-OPN2 by Nuke.YKT (LGPLv2.1)\n");
|
||||
printf("- ymfm by Aaron Giles (BSD 3-clause)\n");
|
||||
printf("- MAME SN76496 emulation core by Nicola Salmoria (BSD 3-clause)\n");
|
||||
printf("- MAME AY-3-8910 emulation core by Couriersud (BSD 3-clause)\n");
|
||||
printf("- SameBoy by Lior Halphon (MIT)\n");
|
||||
printf("- Mednafen PCE by Mednafen Team (GPLv2)\n");
|
||||
printf("- puNES by FHorse (GPLv2)\n");
|
||||
|
|
Loading…
Reference in a new issue