add (Partial) GA20 support

This commit is contained in:
cam900 2022-12-15 20:03:54 +09:00
parent 7a91996e3a
commit d5f44557c2
12 changed files with 877 additions and 5 deletions

View File

@ -445,6 +445,8 @@ src/engine/platform/sound/oki/okim6258.cpp
src/engine/platform/sound/snes/SPC_DSP.cpp
src/engine/platform/sound/ga20/iremga20.cpp
src/engine/platform/oplAInterface.cpp
src/engine/platform/ym2608Interface.cpp
src/engine/platform/ym2610Interface.cpp
@ -526,6 +528,7 @@ src/engine/platform/namcowsg.cpp
src/engine/platform/rf5c68.cpp
src/engine/platform/snes.cpp
src/engine/platform/k007232.cpp
src/engine/platform/ga20.cpp
src/engine/platform/pcmdac.cpp
src/engine/platform/dummy.cpp
)

View File

@ -72,6 +72,7 @@
#include "platform/snes.h"
#include "platform/vb.h"
#include "platform/k007232.h"
#include "platform/ga20.h"
#include "platform/pcmdac.h"
#include "platform/dummy.h"
#include "../ta-log.h"
@ -426,6 +427,9 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do
case DIV_SYSTEM_K007232:
dispatch=new DivPlatformK007232;
break;
case DIV_SYSTEM_GA20:
dispatch=new DivPlatformGA20;
break;
case DIV_SYSTEM_PCM_DAC:
dispatch=new DivPlatformPCMDAC;
break;

View File

@ -0,0 +1,485 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "ga20.h"
#include "../engine.h"
#include "../../ta-log.h"
#include <math.h>
#define rWrite(a,v) {if(!skipRegisterWrites) {writes.emplace(a,v); if(dumpWrites) addWrite(a,v);}}
#define CHIP_DIVIDER 64
const char* regCheatSheetGA20[]={
// on-chip
"CHX_StartL", "X*8+0",
"CHX_StartH", "X*8+1",
"CHX_EndL", "X*8+2",
"CHX_EndH", "X*8+3",
"CHX_Freq", "X*8+4",
"CHX_Volume", "X*8+5",
"CHX_Control", "X*8+6",
"CHX_Status", "X*8+7",
NULL
};
const char** DivPlatformGA20::getRegisterSheet() {
return regCheatSheetGA20;
}
inline void DivPlatformGA20::chWrite(unsigned char ch, unsigned int addr, unsigned char val) {
if (!skipRegisterWrites) {
if ((ch<4) && (addr<8)) {
rWrite((ch<<3)+(addr&7),val);
}
}
}
void DivPlatformGA20::acquire(short* bufL, short* bufR, size_t start, size_t len) {
if (ga20BufLen<len) {
ga20BufLen=len;
for (int i=0; i<4; i++) {
delete[] ga20Buf[i];
ga20Buf[i]=new short[ga20BufLen];
}
}
for (size_t h=start; h<start+len; h++) {
if ((--delay)<=0) {
delay=MAX(0,delay);
if (!writes.empty()) {
QueuedWrite& w=writes.front();
ga20.write(w.addr,w.val);
regPool[w.addr]=w.val;
writes.pop();
delay=w.delay;
}
}
ga20.sound_stream_update(ga20Buf, 1);
bufL[h]=(ga20Buf[0][h]+ga20Buf[1][h]+ga20Buf[2][h]+ga20Buf[3][h])<<4;
for (int i=0; i<4; i++) {
oscBuf[i]->data[oscBuf[i]->needle++]=ga20Buf[i][h]<<6;
}
}
}
u8 DivPlatformGA20::read_byte(u32 address) {
if ((sampleMem!=NULL) && (address<getSampleMemCapacity())) {
return sampleMem[address&0xfffff];
}
return 0;
}
void DivPlatformGA20::tick(bool sysTick) {
for (int i=0; i<4; i++) {
chan[i].std.next();
if (chan[i].std.vol.had) {
const signed char macroVol=VOL_SCALE_LOG((chan[i].vol&0xff),(0xff*MIN(chan[i].macroVolMul,chan[i].std.vol.val))/chan[i].macroVolMul,0xff);
if ((!isMuted[i]) && (macroVol!=chan[i].outVol)) {
chan[i].outVol=macroVol;
chan[i].volumeChanged=true;
}
}
if (chan[i].std.arp.had) {
if (!chan[i].inPorta) {
chan[i].baseFreq=NOTE_PERIODIC(parent->calcArp(chan[i].note,chan[i].std.arp.val));
}
chan[i].freqChanged=true;
}
if (chan[i].std.pitch.had) {
if (chan[i].std.pitch.mode) {
chan[i].pitch2+=chan[i].std.pitch.val;
CLAMP_VAR(chan[i].pitch2,-32768,32767);
} else {
chan[i].pitch2=chan[i].std.pitch.val;
}
chan[i].freqChanged=true;
}
if (chan[i].std.phaseReset.had) {
if (chan[i].std.phaseReset.val==1 && chan[i].active) {
chan[i].audPos=0;
chan[i].setPos=true;
}
}
if (chan[i].volumeChanged) {
chan[i].resVol=(chan[i].active && isMuted[i])?0:chan[i].outVol&0xff;
chWrite(i,0x5,chan[i].resVol);
chan[i].volumeChanged=false;
}
if (chan[i].setPos) {
// force keyon
chan[i].keyOn=true;
chan[i].setPos=false;
} else {
chan[i].audPos=0;
}
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
double off=1.0;
int sample=chan[i].sample;
if (sample>=0 && sample<parent->song.sampleLen) {
DivSample* s=parent->getSample(sample);
if (s->centerRate<1) {
off=1.0;
} else {
off=8363.0/s->centerRate;
}
}
DivSample* s=parent->getSample(chan[i].sample);
chan[i].freq=0x100-(int)(off*parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER));
if (chan[i].freq>255) chan[i].freq=255;
if (chan[i].freq<0) chan[i].freq=0;
if (chan[i].keyOn) {
unsigned int start=0;
unsigned int end=0;
if (chan[i].sample>=0 && chan[i].sample<parent->song.sampleLen) {
start=sampleOffGA20[chan[i].sample]&0xfffff;
end=start+s->length8;
}
if (chan[i].audPos>0) {
start=start+MIN(chan[i].audPos,MIN(getSampleMemCapacity()-1,s->length8));
}
start=MIN(start,getSampleMemCapacity()-1);
end=MIN(end,getSampleMemCapacity()-1);
// force keyoff first
chWrite(i,5,0);
chWrite(i,6,0);
// keyon
if (chan[i].prevFreq!=chan[i].freq) {
chWrite(i,4,chan[i].freq&0xff);
chan[i].prevFreq=chan[i].freq;
}
chWrite(i,0,start&0xff);
chWrite(i,1,start>>8);
chWrite(i,2,end&0xff);
chWrite(i,3,end>>8);
chWrite(i,5,chan[i].resVol);
chWrite(i,6,2);
if (!chan[i].std.vol.had) {
chan[i].outVol=chan[i].vol;
if (!isMuted[i]) {
chan[i].volumeChanged=true;
}
}
chan[i].keyOn=false;
}
if (chan[i].keyOff) {
chWrite(i,5,0);
chWrite(i,6,0);
chan[i].keyOff=false;
}
if (chan[i].freqChanged) {
if (chan[i].prevFreq!=chan[i].freq) {
chWrite(i,4,chan[i].freq&0xff);
chan[i].prevFreq=chan[i].freq;
}
chan[i].freqChanged=false;
}
}
}
}
int DivPlatformGA20::dispatch(DivCommand c) {
switch (c.cmd) {
case DIV_CMD_NOTE_ON: {
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA);
chan[c.chan].macroVolMul=ins->type==DIV_INS_AMIGA?64:255;
chan[c.chan].sample=ins->amiga.getSample(c.value);
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
}
if (chan[c.chan].sample<0 || chan[c.chan].sample>=parent->song.sampleLen) {
chan[c.chan].sample=-1;
}
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].freqChanged=true;
chan[c.chan].note=c.value;
}
chan[c.chan].active=true;
chan[c.chan].keyOn=true;
chan[c.chan].macroInit(ins);
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
chan[c.chan].outVol=chan[c.chan].vol;
if (!isMuted[c.chan]) {
chan[c.chan].volumeChanged=true;
}
}
break;
}
case DIV_CMD_NOTE_OFF:
chan[c.chan].sample=-1;
chan[c.chan].active=false;
chan[c.chan].keyOff=true;
chan[c.chan].macroInit(NULL);
break;
case DIV_CMD_NOTE_OFF_ENV:
case DIV_CMD_ENV_RELEASE:
chan[c.chan].std.release();
break;
case DIV_CMD_INSTRUMENT:
if (chan[c.chan].ins!=c.value || c.value2==1) {
chan[c.chan].ins=c.value;
}
break;
case DIV_CMD_VOLUME:
if (chan[c.chan].vol!=c.value) {
chan[c.chan].vol=c.value;
if (!chan[c.chan].std.vol.has) {
chan[c.chan].outVol=c.value;
if (!isMuted[c.chan]) {
chan[c.chan].volumeChanged=true;
}
}
}
break;
case DIV_CMD_GET_VOLUME:
if (chan[c.chan].std.vol.has) {
return chan[c.chan].vol;
}
return chan[c.chan].outVol;
break;
case DIV_CMD_PITCH:
chan[c.chan].pitch=c.value;
chan[c.chan].freqChanged=true;
break;
case DIV_CMD_NOTE_PORTA: {
const int destFreq=NOTE_PERIODIC(c.value2);
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=NOTE_PERIODIC(c.value+((chan[c.chan].std.arp.will && !chan[c.chan].std.arp.mode)?(chan[c.chan].std.arp.val-12):(0)));
chan[c.chan].freqChanged=true;
chan[c.chan].note=c.value;
break;
}
case DIV_CMD_PRE_PORTA:
if (chan[c.chan].active && c.value2) {
if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA));
}
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
chan[c.chan].inPorta=c.value;
break;
case DIV_CMD_SAMPLE_POS:
chan[c.chan].audPos=c.value;
chan[c.chan].setPos=true;
break;
case DIV_CMD_GET_VOLMAX:
return 255;
break;
case DIV_ALWAYS_SET_VOLUME:
return 1;
break;
default:
break;
}
return 1;
}
void DivPlatformGA20::muteChannel(int ch, bool mute) {
isMuted[ch]=mute;
chan[ch].volumeChanged=true;
}
void DivPlatformGA20::forceIns() {
while (!writes.empty()) writes.pop();
for (int i=0; i<4; i++) {
chan[i].insChanged=true;
chan[i].freqChanged=true;
chan[i].sample=-1;
}
}
void* DivPlatformGA20::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformGA20::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformGA20::getOscBuffer(int ch) {
return oscBuf[ch];
}
void DivPlatformGA20::reset() {
while (!writes.empty()) {
writes.pop();
}
memset(regPool,0,32);
ga20.device_reset();
for (int i=0; i<4; i++) {
chan[i]=DivPlatformGA20::Channel();
chan[i].std.setEngine(parent);
// keyoff all channels
chWrite(i,0,0);
chWrite(i,1,0);
chWrite(i,2,0xff);
chWrite(i,3,0xff);
chWrite(i,4,1);
chWrite(i,5,0);
}
}
bool DivPlatformGA20::isStereo() {
return false;
}
void DivPlatformGA20::notifyInsChange(int ins) {
for (int i=0; i<4; i++) {
if (chan[i].ins==ins) {
chan[i].insChanged=true;
}
}
}
void DivPlatformGA20::notifyWaveChange(int wave) {
// TODO when wavetables are added
// TODO they probably won't be added unless the samples reside in RAM
}
void DivPlatformGA20::notifyInsDeletion(void* ins) {
for (int i=0; i<4; i++) {
chan[i].std.notifyInsDeletion((DivInstrument*)ins);
}
}
void DivPlatformGA20::setFlags(const DivConfig& flags) {
chipClock=COLOR_NTSC;
CHECK_CUSTOM_CLOCK;
rate=chipClock/4;
for (int i=0; i<4; i++) {
oscBuf[i]->rate=rate;
}
}
void DivPlatformGA20::poke(unsigned int addr, unsigned short val) {
rWrite(addr&0x1f,val);
}
void DivPlatformGA20::poke(std::vector<DivRegWrite>& wlist) {
for (DivRegWrite& i: wlist) rWrite(i.addr&0x1f,i.val);
}
unsigned char* DivPlatformGA20::getRegisterPool() {
return regPool;
}
int DivPlatformGA20::getRegisterPoolSize() {
return 32;
}
const void* DivPlatformGA20::getSampleMem(int index) {
return index == 0 ? sampleMem : NULL;
}
size_t DivPlatformGA20::getSampleMemCapacity(int index) {
return index == 0 ? 1048576 : 0;
}
size_t DivPlatformGA20::getSampleMemUsage(int index) {
return index == 0 ? sampleMemLen : 0;
}
bool DivPlatformGA20::isSampleLoaded(int index, int sample) {
if (index!=0) return false;
if (sample<0 || sample>255) return false;
return sampleLoaded[sample];
}
void DivPlatformGA20::renderSamples(int sysID) {
memset(sampleMem,0x00,getSampleMemCapacity());
memset(sampleOffGA20,0,256*sizeof(unsigned int));
memset(sampleLoaded,0,256*sizeof(bool));
size_t memPos=0;
for (int i=0; i<parent->song.sampleLen; i++) {
DivSample* s=parent->song.sample[i];
if (!s->renderOn[0][sysID]) {
sampleOffGA20[i]=0;
continue;
}
const int length=s->getLoopEndPosition(DIV_SAMPLE_DEPTH_8BIT);
int actualLength=MIN((int)(getSampleMemCapacity()-memPos)-1,length);
if (actualLength>0) {
sampleOffGA20[i]=memPos;
for (int j=0; j<actualLength; j++) {
// convert to 8 bit unsigned
unsigned char val=((unsigned char)s->data8[j])^0x80;
sampleMem[memPos++]=CLAMP(val,1,255);
}
// write end of sample marker
memset(&sampleMem[memPos],0x00,1);
memPos+=1;
}
if ((memPos+MAX(actualLength,0))>=(getSampleMemCapacity()-1)) {
logW("out of K007232 PCM memory for sample %d!",i);
break;
} else {
sampleLoaded[i]=true;
}
memPos=(memPos+0xf)&~0xf;
}
sampleMemLen=memPos;
}
int DivPlatformGA20::init(DivEngine* p, int channels, int sugRate, const DivConfig& flags) {
parent=p;
dumpWrites=false;
skipRegisterWrites=false;
for (int i=0; i<4; i++) {
isMuted[i]=false;
oscBuf[i]=new DivDispatchOscBuffer;
}
sampleMem=new unsigned char[getSampleMemCapacity()];
sampleMemLen=0;
setFlags(flags);
ga20BufLen=65536;
for (int i=0; i<4; i++) ga20Buf[i]=new short[ga20BufLen];
reset();
return 4;
}
void DivPlatformGA20::quit() {
delete[] sampleMem;
for (int i=0; i<4; i++) {
delete[] ga20Buf[i];
delete oscBuf[i];
}
}

117
src/engine/platform/ga20.h Normal file
View File

@ -0,0 +1,117 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef _GA20_H
#define _GA20_H
#include "../dispatch.h"
#include <queue>
#include "../macroInt.h"
#include "sound/ga20/iremga20.h"
class DivPlatformGA20: public DivDispatch, public iremga20_intf {
struct Channel: public SharedChannel<int> {
int prevFreq;
unsigned int audPos;
int prevBank;
int sample;
int panning, prevPan;
bool volumeChanged, setPos;
int resVol, lvol, rvol;
int macroVolMul;
Channel():
SharedChannel<int>(15),
prevFreq(-1),
audPos(0),
prevBank(-1),
sample(-1),
panning(255),
prevPan(-1),
volumeChanged(false),
setPos(false),
resVol(15),
lvol(15),
rvol(15),
macroVolMul(64) {}
};
Channel chan[4];
DivDispatchOscBuffer* oscBuf[4];
bool isMuted[4];
struct QueuedWrite {
unsigned short addr;
unsigned char val;
unsigned short delay;
QueuedWrite(unsigned short a, unsigned char v, unsigned short d=1):
addr(a),
val(v),
delay(d) {}
};
std::queue<QueuedWrite> writes;
unsigned int sampleOffGA20[256];
bool sampleLoaded[256];
int delay;
short* ga20Buf[4];
size_t ga20BufLen;
unsigned char* sampleMem;
size_t sampleMemLen;
iremga20_device ga20;
unsigned char regPool[32];
friend void putDispatchChip(void*,int);
friend void putDispatchChan(void*,int,int);
void chWrite(unsigned char ch, unsigned int addr, unsigned char val);
public:
virtual u8 read_byte(u32 address) override;
void acquire(short* bufL, short* bufR, size_t start, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();
void reset();
void forceIns();
void tick(bool sysTick=true);
void muteChannel(int ch, bool mute);
bool isStereo();
void setChipModel(int type);
void notifyInsChange(int ins);
void notifyWaveChange(int wave);
void notifyInsDeletion(void* ins);
void setFlags(const DivConfig& flags);
void poke(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist);
const char** getRegisterSheet();
const void* getSampleMem(int index = 0);
size_t getSampleMemCapacity(int index = 0);
size_t getSampleMemUsage(int index = 0);
bool isSampleLoaded(int index, int sample);
void renderSamples(int chipID);
int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags);
void quit();
DivPlatformGA20():
DivDispatch(),
iremga20_intf(),
ga20(*this) {}
};
#endif

View File

@ -0,0 +1,175 @@
// license:BSD-3-Clause
// copyright-holders:Acho A. Tang,R. Belmont, Valley Bell
/*********************************************************
Irem GA20 PCM Sound Chip
80 pin QFP, label NANAO GA20 (Nanao Corporation was Irem's parent company)
TODO:
- It's not currently known whether this chip is stereo.
- Is sample position base(regs 0,1) used while sample is playing, or
latched at key on? We've always emulated it the latter way.
gunforc2 seems to be the only game updating the address regs sometimes
while a sample is playing, but it doesn't seem intentional.
- What is the 2nd sample address for? Is it end(cut-off) address, or
loop start address? Every game writes a value that's past sample end.
- All games write either 0 or 2 to reg #6, do other bits have any function?
Revisions:
04-15-2002 Acho A. Tang
- rewrote channel mixing
- added prelimenary volume and sample rate emulation
05-30-2002 Acho A. Tang
- applied hyperbolic gain control to volume and used
a musical-note style progression in sample rate
calculation(still very inaccurate)
02-18-2004 R. Belmont
- sample rate calculation reverse-engineered.
Thanks to Fujix, Yasuhiro Ogawa, the Guru, and Tormod
for real PCB samples that made this possible.
02-03-2007 R. Belmont
- Cleaned up faux x86 assembly.
09-25-2018 Valley Bell & co
- rewrote channel update to make data 0 act as sample terminator
DISCLAIMER
- This file is modified for suitable in furnace.
- modified by cam900
*********************************************************/
#include "iremga20.h"
#include <algorithm>
//**************************************************************************
// LIVE DEVICE
//**************************************************************************
//-------------------------------------------------
// iremga20_device - constructor
//-------------------------------------------------
iremga20_device::iremga20_device(iremga20_intf &intf) :
m_regs{0},
m_channel{channel_def(), channel_def(), channel_def(), channel_def()},
m_intf(intf)
{
}
//-------------------------------------------------
// device_reset - device-specific reset
//-------------------------------------------------
void iremga20_device::device_reset()
{
memset(m_regs, 0, 0x20 * sizeof(u8));
for (int i = 0; i < 4; i++)
{
m_channel[i].rate = 0;
m_channel[i].pos = 0;
m_channel[i].counter = 0;
m_channel[i].end = 0;
m_channel[i].volume = 0;
m_channel[i].play = false;
}
}
//-------------------------------------------------
// sound_stream_update - handle a stream update
//-------------------------------------------------
void iremga20_device::sound_stream_update(short** outputs, int len)
{
for (int i = 0; i < len; i++)
{
s32 sampleout = 0;
for (int j = 0; j < 4; j++)
{
channel_def &ch = m_channel[j];
if (ch.play)
{
int sample = m_intf.read_byte(ch.pos);
if (sample == 0x00) // check for sample end marker
ch.play = false;
else
{
sampleout += (sample - 0x80) * (s32)ch.volume;
ch.counter--;
if (ch.counter <= ch.rate)
{
ch.pos++;
ch.counter = 0x100;
}
}
}
outputs[j][i] = sampleout;
}
}
}
void iremga20_device::write(u32 offset, u8 data)
{
offset &= 0x1f;
m_regs[offset] = data;
int ch = offset >> 3;
// channel regs:
// 0,1: start address
// 2,3: end? address
// 4: rate
// 5: volume
// 6: control
// 7: voice status (read-only)
switch (offset & 0x7)
{
case 4:
m_channel[ch].rate = data;
break;
case 5:
m_channel[ch].volume = (data * 256) / (data + 10);
break;
case 6:
// d1: key on/off
if (data & 2)
{
m_channel[ch].play = true;
m_channel[ch].pos = (m_regs[ch << 3 | 0] | m_regs[ch << 3 | 1] << 8) << 4;
m_channel[ch].end = (m_regs[ch << 3 | 2] | m_regs[ch << 3 | 3] << 8) << 4;
m_channel[ch].counter = 0x100;
}
else
m_channel[ch].play = false;
// other: unknown/unused
// possibilities are: loop flag, left/right speaker(stereo)
break;
}
}
u8 iremga20_device::read(u32 offset)
{
offset &= 0x1f;
int ch = offset >> 3;
switch (offset & 0x7)
{
case 7: // voice status. bit 0 is 1 if active. (routine around 0xccc in rtypeleo)
return m_channel[ch].play ? 1 : 0;
}
return 0;
}

View File

@ -0,0 +1,74 @@
// license:BSD-3-Clause
// copyright-holders:Acho A. Tang,R. Belmont
/*********************************************************
Irem GA20 PCM Sound Chip
DISCLAIMER
- This file is modified for suitable in furnace.
- modified by cam900
*********************************************************/
#ifndef MAME_SOUND_IREMGA20_H
#define MAME_SOUND_IREMGA20_H
#pragma once
//**************************************************************************
// TYPE DEFINITIONS
//**************************************************************************
using u8 = unsigned char;
using u32 = unsigned int;
using s32 = signed int;
class iremga20_intf
{
public:
virtual u8 read_byte(u32 address) { return 0; };
};
// ======================> iremga20_device
class iremga20_device
{
public:
iremga20_device(iremga20_intf &intf);
void write(u32 offset, u8 data);
u8 read(u32 offset);
// device-level overrides
void device_reset();
// sound stream update overrides
void sound_stream_update(short** outputs, int len);
private:
struct channel_def
{
channel_def() :
rate(0),
pos(0),
counter(0),
end(0),
volume(0),
play(0)
{
}
u32 rate;
u32 pos;
u32 counter;
u32 end;
u32 volume;
bool play;
};
u8 m_regs[0x20];
channel_def m_channel[4];
iremga20_intf &m_intf;
};
#endif // MAME_SOUND_IREMGA20_H

View File

@ -1325,7 +1325,8 @@ void FurnaceGUI::doAction(int what) {
i==DIV_INS_SU ||
i==DIV_INS_SNES ||
i==DIV_INS_ES5506 ||
i==DIV_INS_K007232) {
i==DIV_INS_K007232 ||
i==DIV_INS_GA20) {
makeInsTypeList.push_back(i);
}
}

View File

@ -983,6 +983,7 @@ const int availableSystems[]={
DIV_SYSTEM_SNES,
DIV_SYSTEM_MSM5232,
DIV_SYSTEM_K007232,
DIV_SYSTEM_GA20,
DIV_SYSTEM_PCM_DAC,
DIV_SYSTEM_PONG,
0 // don't remove this last one!
@ -1083,6 +1084,7 @@ const int chipsSample[]={
DIV_SYSTEM_RF5C68,
DIV_SYSTEM_SNES,
DIV_SYSTEM_K007232,
DIV_SYSTEM_GA20,
DIV_SYSTEM_PCM_DAC,
0 // don't remove this last one!
};

View File

@ -4253,7 +4253,8 @@ void FurnaceGUI::drawInsEdit() {
ins->type==DIV_INS_SU ||
ins->type==DIV_INS_SNES ||
ins->type==DIV_INS_ES5506 ||
ins->type==DIV_INS_K007232) {
ins->type==DIV_INS_K007232 ||
ins->type==DIV_INS_GA20) {
if (ImGui::BeginTabItem((ins->type==DIV_INS_SU)?"Sound Unit":"Sample")) {
String sName;
if (ins->amiga.initSample<0 || ins->amiga.initSample>=e->song.sampleLen) {
@ -4943,7 +4944,8 @@ void FurnaceGUI::drawInsEdit() {
if (ins->type==DIV_INS_ADPCMA) {
volMax=31;
}
if (ins->type==DIV_INS_ADPCMB || ins->type==DIV_INS_YMZ280B || ins->type==DIV_INS_RF5C68) {
if (ins->type==DIV_INS_ADPCMB || ins->type==DIV_INS_YMZ280B || ins->type==DIV_INS_RF5C68 ||
ins->type==DIV_INS_GA20) {
volMax=255;
}
if (ins->type==DIV_INS_QSOUND) {
@ -4998,7 +5000,7 @@ void FurnaceGUI::drawInsEdit() {
}
if (ins->type==DIV_INS_TIA || ins->type==DIV_INS_AMIGA || ins->type==DIV_INS_SCC ||
ins->type==DIV_INS_PET || ins->type==DIV_INS_VIC || ins->type==DIV_INS_SEGAPCM ||
ins->type==DIV_INS_FM || ins->type==DIV_INS_K007232) {
ins->type==DIV_INS_FM || ins->type==DIV_INS_K007232 || ins->type==DIV_INS_GA20) {
dutyMax=0;
}
if (ins->type==DIV_INS_VBOY) {
@ -5082,6 +5084,7 @@ void FurnaceGUI::drawInsEdit() {
if (ins->type==DIV_INS_MSM6295) waveMax=0;
if (ins->type==DIV_INS_SEGAPCM) waveMax=0;
if (ins->type==DIV_INS_K007232) waveMax=0;
if (ins->type==DIV_INS_GA20) waveMax=0;
if (ins->type==DIV_INS_POKEMINI) waveMax=0;
if (ins->type==DIV_INS_SU) waveMax=7;
if (ins->type==DIV_INS_PET) {
@ -5282,7 +5285,8 @@ void FurnaceGUI::drawInsEdit() {
ins->type==DIV_INS_T6W28 ||
ins->type==DIV_INS_VBOY ||
(ins->type==DIV_INS_X1_010 && ins->amiga.useSample) ||
ins->type==DIV_INS_K007232) {
ins->type==DIV_INS_K007232 ||
ins->type==DIV_INS_GA20) {
macroList.push_back(FurnaceGUIMacroDesc("Phase Reset",&ins->std.phaseResetMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true));
}
if (ex1Max>0) {

View File

@ -1158,6 +1158,11 @@ void FurnaceGUI::initSystemPresets() {
CH(DIV_SYSTEM_K007232, 64, 0, "")
}
);
ENTRY(
"Nanao GA20", {
CH(DIV_SYSTEM_GA20, 64, 0, "")
}
);
ENTRY(
"Generic PCM DAC", {
CH(DIV_SYSTEM_PCM_DAC, 64, 0, "")

View File

@ -1565,6 +1565,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo
case DIV_SYSTEM_BUBSYS_WSG:
case DIV_SYSTEM_PET:
case DIV_SYSTEM_VBOY:
case DIV_SYSTEM_GA20:
ImGui::Text("nothing to configure");
break;
case DIV_SYSTEM_VERA:

View File

@ -194,6 +194,7 @@ TAParamResult pVersion(String) {
printf("- reSIDfp by Dag Lem, Antti Lankila and Leandro Nini (GPLv2)\n");
printf("- Stella by Stella Team (GPLv2)\n");
printf("- vgsound_emu (second version, modified version) by cam900 (zlib license)\n");
printf("- MAME GA20 core by Acho A. Tang, R. Belmont, Valley Bell (BSD 3-clause)\n");
return TA_PARAM_QUIT;
}