[WIP] K063260 skeleton

This commit is contained in:
cam900 2023-04-02 14:32:47 +09:00
parent fe7ba3c56b
commit 9c56c3d01b
20 changed files with 837 additions and 84 deletions

View File

@ -551,6 +551,7 @@ src/engine/platform/k007232.cpp
src/engine/platform/ga20.cpp
src/engine/platform/sm8521.cpp
src/engine/platform/pv1000.cpp
src/engine/platform/k053260.cpp
src/engine/platform/pcmdac.cpp
src/engine/platform/dummy.cpp

View File

@ -8,18 +8,19 @@
#include "k053260.hpp"
void k053260_core::tick()
void k053260_core::tick(u32 cycle)
{
m_out[0] = m_out[1] = 0;
if (m_ctrl.sound_en())
{
for (int i = 0; i < 4; i++)
{
m_voice[i].tick();
m_voice[i].tick(cycle);
m_out[0] += m_voice[i].out(0);
m_out[1] += m_voice[i].out(1);
}
}
/*
// dac clock (YM3012 format)
u8 dac_clock = m_dac.clock();
if (bitfield(++dac_clock, 0, 4) == 0)
@ -34,62 +35,59 @@ void k053260_core::tick()
m_dac.set_state(bitfield(dac_state, 0, 2));
}
m_dac.set_clock(bitfield(dac_clock, 0, 4));
*/
}
void k053260_core::voice_t::tick()
void k053260_core::voice_t::tick(u32 cycle)
{
if (m_enable && m_busy)
{
bool update = false;
// update counter
if (bitfield(++m_counter, 0, 12) == 0)
m_counter += cycle;
if (m_counter >= 0x1000)
{
if (m_bitpos < 8)
{
m_bitpos += 8;
m_addr = bitfield(m_addr + 1, 0, 21);
m_remain--;
if (m_remain < 0) // check end flag
{
if (m_loop)
{
m_addr = m_start;
m_remain = m_length;
m_output = 0;
}
else
{
m_busy = false;
}
}
}
m_data = m_host.m_intf.read_sample(bitfield(m_addr, 0, 21)); // fetch ROM
if (m_adpcm)
{
m_bitpos -= 4;
update = true;
m_bitpos -= 4;
const u8 nibble = bitfield(m_data, m_bitpos & 4, 4); // get nibble from ROM
if (nibble)
{
m_output += m_host.adpcm_lut(nibble);
}
}
else
{
m_bitpos -= 8;
}
m_counter = bitfield(m_pitch, 0, 12);
}
m_data = m_host.m_intf.read_sample(bitfield(m_addr, 0, 21)); // fetch ROM
if (update)
{
const u8 nibble = bitfield(m_data, m_bitpos & 4, 4); // get nibble from ROM
if (nibble)
{
m_adpcm_buf += bitfield(nibble, 3) ? s8(0x80 >> bitfield(nibble, 0, 3))
: (1 << bitfield(nibble - 1, 0, 3));
}
m_counter = 0x1000 - bitfield(m_pitch, 0, 12);
}
if (m_remain < 0) // check end flag
{
if (m_loop)
{
m_addr = m_start;
m_remain = m_length;
m_adpcm_buf = 0;
}
else
{
m_busy = false;
}
}
// calculate output
s32 output = m_adpcm ? m_adpcm_buf : sign_ext<s32>(m_data, 8) * s32(m_volume);
s32 output = (m_adpcm ? m_output : sign_ext<s32>(m_data, 8)) * s32(m_volume);
// use math for now; actually fomula unknown
m_out[0] = (m_pan >= 0) ? s32(output * cos(f64(m_pan) * PI / 180)) : 0;
m_out[1] = (m_pan >= 0) ? s32(output * sin(f64(m_pan) * PI / 180)) : 0;
m_out[0] = (output * m_host.pan_lut(m_pan, 0)) >> 7;
m_out[1] = (output * m_host.pan_lut(m_pan, 1)) >> 7;
}
else
{
@ -244,7 +242,8 @@ void k053260_core::voice_t::keyon()
m_addr = m_start;
m_remain = m_length;
m_bitpos = 4;
m_adpcm_buf = 0;
m_data = 0;
m_output = 0;
std::fill(m_out.begin(), m_out.end(), 0);
}
@ -259,12 +258,12 @@ void k053260_core::reset()
elem.reset();
}
m_intf.write_int(0);
//m_intf.write_int(0);
std::fill(m_host2snd.begin(), m_host2snd.end(), 0);
std::fill(m_snd2host.begin(), m_snd2host.end(), 0);
m_ctrl.reset();
m_dac.reset();
//m_dac.reset();
std::fill(m_reg.begin(), m_reg.end(), 0);
std::fill(m_out.begin(), m_out.end(), 0);
@ -273,20 +272,20 @@ void k053260_core::reset()
// reset voice
void k053260_core::voice_t::reset()
{
m_enable = 0;
m_busy = 0;
m_loop = 0;
m_adpcm = 0;
m_pitch = 0;
m_start = 0;
m_length = 0;
m_volume = 0;
m_pan = -1;
m_counter = 0;
m_addr = 0;
m_remain = 0;
m_bitpos = 4;
m_data = 0;
m_adpcm_buf = 0;
m_enable = 0;
m_busy = 0;
m_loop = 0;
m_adpcm = 0;
m_pitch = 0;
m_start = 0;
m_length = 0;
m_volume = 0;
m_pan = 4;
m_counter = 0;
m_addr = 0;
m_remain = 0;
m_bitpos = 4;
m_data = 0;
m_output = 0;
m_out[0] = m_out[1] = 0;
}

View File

@ -25,7 +25,7 @@ class k053260_intf : public vgsound_emu_core
virtual u8 read_sample(u32 address) { return 0; } // sample fetch
virtual void write_int(u8 out) {} // timer interrupt
//virtual void write_int(u8 out) {} // timer interrupt
};
class k053260_core : public vgsound_emu_core
@ -33,7 +33,19 @@ class k053260_core : public vgsound_emu_core
friend class k053260_intf; // k053260 specific interface
private:
const int pan_dir[8] = {-1, 0, 24, 35, 45, 55, 66, 90}; // pan direction
const s32 m_pan_lut[8][2] = {
{0x00, 0x00},
{0x7f, 0x00},
{0x74, 0x34},
{0x68, 0x49},
{0x5a, 0x5a},
{0x49, 0x68},
{0x34, 0x74},
{0x00, 0x7f}
}; // pan LUT
const s8 m_adpcm_lut[16] =
{0, 1, 2, 4, 8, 16, 32, 64, -128, -64, -32, -16, -8, -4, -2, -1}; // ADPCM LUT
class voice_t : public vgsound_emu_core
{
@ -50,20 +62,20 @@ class k053260_core : public vgsound_emu_core
, m_start(0)
, m_length(0)
, m_volume(0)
, m_pan(-1)
, m_pan(4)
, m_counter(0)
, m_addr(0)
, m_remain(0)
, m_bitpos(4)
, m_data(0)
, m_adpcm_buf(0)
, m_output(0)
{
m_out.fill(0);
}
// internal state
void reset();
void tick();
void tick(u32 cycle);
// accessors
void write(u8 address, u8 data);
@ -81,7 +93,7 @@ class k053260_core : public vgsound_emu_core
inline void length_inc() { m_length = (m_length + 1) & 0xffff; }
inline void set_pan(u8 pan) { m_pan = m_host.pan_dir[pan & 7]; }
inline void set_pan(u8 pan) { m_pan = pan & 7; }
// getters
inline bool enable() { return m_enable; }
@ -97,21 +109,21 @@ class k053260_core : public vgsound_emu_core
private:
// registers
k053260_core &m_host;
u16 m_enable : 1; // enable flag
u16 m_busy : 1; // busy status
u16 m_loop : 1; // loop flag
u16 m_adpcm : 1; // ADPCM flag
u16 m_pitch : 12; // pitch
u32 m_start = 0; // start position
u16 m_length = 0; // source length
u8 m_volume = 0; // master volume
int m_pan = -1; // master pan
u16 m_counter = 0; // frequency counter
u32 m_addr = 0; // current address
s32 m_remain = 0; // remain for end sample
u8 m_bitpos = 4; // bit position for ADPCM decoding
u8 m_data = 0; // current data
s8 m_adpcm_buf = 0; // ADPCM buffer
u16 m_enable : 1; // enable flag
u16 m_busy : 1; // busy status
u16 m_loop : 1; // loop flag
u16 m_adpcm : 1; // ADPCM flag
u16 m_pitch : 12; // pitch
u32 m_start = 0; // start position
u16 m_length = 0; // source length
u8 m_volume = 0; // master volume
int m_pan = -1; // master pan
u16 m_counter = 0; // frequency counter
u32 m_addr = 0; // current address
s32 m_remain = 0; // remain for end sample
u8 m_bitpos = 4; // bit position for ADPCM decoding
u8 m_data = 0; // current data
s8 m_output = 0; // ADPCM buffer
std::array<s32, 2> m_out; // current output
};
@ -152,6 +164,7 @@ class k053260_core : public vgsound_emu_core
u8 m_input_en : 2; // Input enable
};
/*
class ym3012_t
{
public:
@ -177,7 +190,9 @@ class k053260_core : public vgsound_emu_core
std::array<s32, 2> m_in;
std::array<s32, 2> m_out;
};
*/
/*
class dac_t
{
public:
@ -205,6 +220,7 @@ class k053260_core : public vgsound_emu_core
u8 m_clock : 4; // DAC clock (16 clock)
u8 m_state : 2; // DAC state (4 state - SAM1, SAM2)
};
*/
public:
// constructor
@ -213,8 +229,8 @@ class k053260_core : public vgsound_emu_core
, m_voice{*this, *this, *this, *this}
, m_intf(intf)
, m_ctrl(ctrl_t())
, m_ym3012(ym3012_t())
, m_dac(dac_t())
//, m_ym3012(ym3012_t())
//, m_dac(dac_t())
{
m_host2snd.fill(0);
m_snd2host.fill(0);
@ -233,7 +249,7 @@ class k053260_core : public vgsound_emu_core
// internal state
void reset();
void tick();
void tick(u32 cycle);
// getters for debug, trackers, etc
inline s32 output(u8 ch) { return m_out[ch & 1]; } // output for each channels
@ -245,6 +261,11 @@ class k053260_core : public vgsound_emu_core
return (voice < 4) ? m_voice[voice].out(ch & 1) : 0;
}
protected:
inline s32 pan_lut(const u8 pan, const u8 out) { return m_pan_lut[pan][out]; }
inline s32 adpcm_lut(const u8 nibble) { return m_adpcm_lut[nibble]; }
private:
std::array<voice_t, 4> m_voice;
k053260_intf &m_intf; // common memory interface
@ -254,8 +275,8 @@ class k053260_core : public vgsound_emu_core
ctrl_t m_ctrl; // chip control
ym3012_t m_ym3012; // YM3012 output
dac_t m_dac; // YM3012 interface
//ym3012_t m_ym3012; // YM3012 output
//dac_t m_dac; // YM3012 interface
std::array<u8, 64> m_reg; // register pool
std::array<s32, 2> m_out; // stereo output

View File

@ -78,6 +78,7 @@
#include "platform/ga20.h"
#include "platform/sm8521.h"
#include "platform/pv1000.h"
#include "platform/k053260.h"
#include "platform/pcmdac.h"
#include "platform/dummy.h"
#include "../ta-log.h"
@ -501,6 +502,9 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do
case DIV_SYSTEM_PV1000:
dispatch=new DivPlatformPV1000;
break;
case DIV_SYSTEM_K053260:
dispatch=new DivPlatformK053260;
break;
case DIV_SYSTEM_PCM_DAC:
dispatch=new DivPlatformPCMDAC;
break;

View File

@ -930,6 +930,10 @@ void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song) {
break;
case DIV_INS_PV1000:
break;
case DIV_INS_K053260:
featureSM=true;
featureSL=true;
break;
case DIV_INS_MAX:
break;

View File

@ -80,6 +80,7 @@ enum DivInstrumentType: unsigned short {
DIV_INS_POKEMINI=47,
DIV_INS_SM8521=48,
DIV_INS_PV1000=49,
DIV_INS_K053260=50,
DIV_INS_MAX,
DIV_INS_NULL
};

View File

@ -0,0 +1,475 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 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 "k053260.h"
#include "../engine.h"
#include "../../ta-log.h"
#include <math.h>
#define rWrite(a,v) {if(!skipRegisterWrites) {k053260.write(a,v); regPool[a]=v; if(dumpWrites) addWrite(a,v);}}
#define CHIP_DIVIDER 64
#define TICK_DIVIDER 4
const char* regCheatSheetK053260[]={
"FreqL", "0",
"FreqH", "1",
"LengthL", "2",
"LengthH", "3",
"StartL", "4",
"StartM", "5",
"StartH", "6",
"Volume", "7",
NULL
};
const char** DivPlatformK053260::getRegisterSheet() {
return regCheatSheetK053260;
}
inline void DivPlatformK053260::chWrite(unsigned char ch, unsigned int addr, unsigned char val) {
if (!skipRegisterWrites) {
rWrite(8+((ch<<3)|(addr&7)),val);
}
}
// TODO: this code is weird
// make sure newDispatch didn't break it up
void DivPlatformK053260::acquire(short** buf, size_t len) {
for (int i=0; i<len; i++) {
k053260.tick(TICK_DIVIDER);
int lout=(k053260.output(0)); // scale to 16 bit
int rout=(k053260.output(1)); // scale to 16 bit
if (lout>32767) lout=32767;
if (lout<-32768) lout=-32768;
if (rout>32767) rout=32767;
if (rout<-32768) rout=-32768;
buf[0][i]=lout;
buf[1][i]=rout;
for (int i=0; i<4; i++) {
oscBuf[i]->data[oscBuf[i]->needle++]=(k053260.voice_out(i,0)+k053260.voice_out(i,1))>>1;
}
}
}
void DivPlatformK053260::tick(bool sysTick) {
unsigned char panMask=0;
for (int i=0; i<4; i++) {
chan[i].std.next();
if (chan[i].std.vol.had) {
chan[i].outVol=((chan[i].vol&0x7f)*MIN(chan[i].macroVolMul,chan[i].std.vol.val))/chan[i].macroVolMul;
chWrite(i,7,chan[i].outVol);
}
if (NEW_ARP_STRAT) {
chan[i].handleArp();
} else 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.panL.had) { // panning
chan[i].panning=4+chan[i].std.panL.val;
if (!isMuted[i]) {
panMask|=1<<i;
}
}
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].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) {
unsigned char keyon=regPool[0x28]|(1<<i);
unsigned char keyoff=keyon&~(1<<i);
unsigned char loopon=regPool[0x2a]|(1<<i);
unsigned char loopoff=loopon&~(1<<i);
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=0x1000-(int)(off*parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER));
if (chan[i].freq>4095) chan[i].freq=4095;
if (chan[i].freq<0) chan[i].freq=0;
if (chan[i].keyOn) {
unsigned int start=0;
unsigned int length=0;
if (chan[i].sample>=0 && chan[i].sample<parent->song.sampleLen) {
start=sampleOffK053260[chan[i].sample];
length=start+s->length8;
}
if (chan[i].audPos>0) {
start=start+MIN(chan[i].audPos,s->length8);
}
start=MIN(start,getSampleMemCapacity()-31);
length=MIN(length,getSampleMemCapacity()-31);
rWrite(0x28,keyoff); // force keyoff first
rWrite(0x2a,loopoff);
chWrite(i,2,length&0xff);
chWrite(i,3,length>>8);
chWrite(i,4,start&0xff);
chWrite(i,5,start>>8);
chWrite(i,6,start>>16);
if (!chan[i].std.vol.had) {
chan[i].outVol=chan[i].vol;
chWrite(i,7,chan[i].outVol);
}
rWrite(0x28,keyon);
if (s->isLoopable()) {
rWrite(0x2a,loopon);
}
chan[i].keyOn=false;
}
if (chan[i].keyOff) {
rWrite(0x28,keyoff);
rWrite(0x2a,loopoff);
chan[i].keyOff=false;
}
if (chan[i].freqChanged) {
chWrite(i,0,chan[i].freq&0xff);
chWrite(i,1,chan[i].freq>>8);
chan[i].freqChanged=false;
}
}
}
if (panMask) {
updatePanning(panMask);
}
}
void DivPlatformK053260::updatePanning(unsigned char mask) {
if (mask&3) {
rWrite(0x2c,
(isMuted[0]?0:chan[0].panning)|
(isMuted[1]?0:chan[1].panning<<3));
}
if (mask&0xc) {
rWrite(0x2d,
(isMuted[2]?0:chan[2].panning)|
(isMuted[3]?0:chan[3].panning<<3));
}
}
int DivPlatformK053260::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:127;
if (c.value!=DIV_NOTE_NULL) 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;
}
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;
chWrite(c.chan,7,chan[c.chan].outVol);
}
}
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_PANNING:
chan[c.chan].panning=MIN(parent->convertPanSplitToLinearLR(c.value,c.value2,7)+1,7);
if (!isMuted[c.chan]) {
updatePanning(1<<c.chan);
}
break;
case DIV_CMD_PITCH:
chan[c.chan].pitch=c.value;
chan[c.chan].freqChanged=true;
break;
case DIV_CMD_NOTE_PORTA: {
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+((HACKY_LEGATO_MESS)?(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 && !NEW_ARP_STRAT) 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 127;
break;
case DIV_CMD_MACRO_OFF:
chan[c.chan].std.mask(c.value,true);
break;
case DIV_CMD_MACRO_ON:
chan[c.chan].std.mask(c.value,false);
break;
case DIV_ALWAYS_SET_VOLUME:
return 1;
break;
default:
break;
}
return 1;
}
void DivPlatformK053260::muteChannel(int ch, bool mute) {
isMuted[ch]=mute;
updatePanning(1<<ch);
}
void DivPlatformK053260::forceIns() {
for (int i=0; i<4; i++) {
chan[i].insChanged=true;
chan[i].freqChanged=true;
chan[i].sample=-1;
chWrite(i,1,isMuted[i]?0:chan[i].panning);
}
}
void* DivPlatformK053260::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformK053260::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformK053260::getOscBuffer(int ch) {
return oscBuf[ch];
}
void DivPlatformK053260::reset() {
memset(regPool,0,48);
k053260.reset();
rWrite(0x28,0); // keyoff all channels
for (int i=0; i<4; i++) {
chan[i]=DivPlatformK053260::Channel();
chan[i].std.setEngine(parent);
}
updatePanning(0xf);
rWrite(0x2f,2); // sound enable
}
int DivPlatformK053260::getOutputCount() {
return 2;
}
void DivPlatformK053260::notifyInsChange(int ins) {
for (int i=0; i<4; i++) {
if (chan[i].ins==ins) {
chan[i].insChanged=true;
}
}
}
void DivPlatformK053260::notifyWaveChange(int wave) {
// TODO when wavetables are added
// TODO they probably won't be added unless the samples reside in RAM
}
void DivPlatformK053260::notifyInsDeletion(void* ins) {
for (int i=0; i<4; i++) {
chan[i].std.notifyInsDeletion((DivInstrument*)ins);
}
}
void DivPlatformK053260::setFlags(const DivConfig& flags) {
switch (flags.getInt("clockSel",0)) {
case 1: chipClock=4000000; break;
default: chipClock=COLOR_NTSC; break;
}
CHECK_CUSTOM_CLOCK;
rate=chipClock/TICK_DIVIDER;
for (int i=0; i<4; i++) {
oscBuf[i]->rate=rate;
}
}
void DivPlatformK053260::poke(unsigned int addr, unsigned short val) {
rWrite(addr&0x0f,val);
}
void DivPlatformK053260::poke(std::vector<DivRegWrite>& wlist) {
for (DivRegWrite& i: wlist) rWrite(i.addr&0x0f,i.val);
}
unsigned char* DivPlatformK053260::getRegisterPool() {
return regPool;
}
int DivPlatformK053260::getRegisterPoolSize() {
return 48;
}
const void* DivPlatformK053260::getSampleMem(int index) {
return index == 0 ? sampleMem : NULL;
}
size_t DivPlatformK053260::getSampleMemCapacity(int index) {
return index == 0 ? 2097152 : 0;
}
size_t DivPlatformK053260::getSampleMemUsage(int index) {
return index == 0 ? sampleMemLen : 0;
}
bool DivPlatformK053260::isSampleLoaded(int index, int sample) {
if (index!=0) return false;
if (sample<0 || sample>255) return false;
return sampleLoaded[sample];
}
void DivPlatformK053260::renderSamples(int sysID) {
memset(sampleMem,0,getSampleMemCapacity());
memset(sampleOffK053260,0,256*sizeof(unsigned int));
memset(sampleLoaded,0,256*sizeof(bool));
size_t memPos=1; // for avoid silence
for (int i=0; i<parent->song.sampleLen; i++) {
DivSample* s=parent->song.sample[i];
if (!s->renderOn[0][sysID]) {
sampleOffK053260[i]=0;
continue;
}
int length=MIN(65535,s->getLoopEndPosition(DIV_SAMPLE_DEPTH_8BIT));
int actualLength=MIN((int)(getSampleMemCapacity()-memPos),length);
if (actualLength>0) {
sampleOffK053260[i]=memPos-1;
for (int j=0; j<actualLength; j++) {
sampleMem[memPos++]=s->data8[j];
}
}
if (actualLength<length) {
logW("out of K053260 PCM memory for sample %d!",i);
break;
}
sampleLoaded[i]=true;
}
sampleMemLen=memPos;
}
int DivPlatformK053260::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);
reset();
return 4;
}
void DivPlatformK053260::quit() {
delete[] sampleMem;
for (int i=0; i<4; i++) {
delete oscBuf[i];
}
}

View File

@ -0,0 +1,96 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 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 _K053260_H
#define _K053260_H
#include "../dispatch.h"
#include <queue>
#include "vgsound_emu/src/k053260/k053260.hpp"
class DivPlatformK053260: public DivDispatch, public k053260_intf {
struct Channel: public SharedChannel<int> {
unsigned int audPos;
int sample, wave;
int panning;
bool setPos;
int macroVolMul;
Channel():
SharedChannel<int>(127),
audPos(0),
sample(-1),
wave(-1),
panning(4),
setPos(false),
macroVolMul(64) {}
};
Channel chan[4];
DivDispatchOscBuffer* oscBuf[4];
bool isMuted[4];
int chipType;
unsigned char curChan;
unsigned int sampleOffK053260[256];
bool sampleLoaded[256];
unsigned char* sampleMem;
size_t sampleMemLen;
k053260_core k053260;
unsigned char regPool[48];
void updatePanning(unsigned char mask);
friend void putDispatchChip(void*,int);
friend void putDispatchChan(void*,int,int);
public:
void acquire(short** buf, 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);
int getOutputCount();
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();
DivPlatformK053260():
DivDispatch(),
k053260_intf(),
k053260(*this) {}
private:
void chWrite(unsigned char ch, unsigned int addr, unsigned char val);
};
#endif

View File

@ -127,7 +127,8 @@ enum DivSystem {
DIV_SYSTEM_YM2203_CSM,
DIV_SYSTEM_YM2608_CSM,
DIV_SYSTEM_SM8521,
DIV_SYSTEM_PV1000
DIV_SYSTEM_PV1000,
DIV_SYSTEM_K053260
};
struct DivGroovePattern {

View File

@ -1855,6 +1855,16 @@ void DivEngine::registerSystems() {
}
);
sysDefs[DIV_SYSTEM_K053260]=new DivSysDef(
"Konami K053260", NULL, 0xfe/*placeholder*/, 0, 4, false, true, 0x161, false, 1U<<DIV_SAMPLE_DEPTH_8BIT,
"this PCM chip was widely used at Konami arcade boards in 1990-1992.",
{"Channel 1", "Channel 2", "Channel 3", "Channel 4"},
{"CH1", "CH2", "CH3", "CH4"},
{DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM},
{DIV_INS_K053260, DIV_INS_K053260, DIV_INS_K053260, DIV_INS_K053260},
{DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA}
);
sysDefs[DIV_SYSTEM_DUMMY]=new DivSysDef(
"Dummy System", NULL, 0xfd, 0, 8, false, true, 0, false, 0,
"this is a system designed for testing purposes.",

View File

@ -564,7 +564,7 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write
w->writeC(0xff);
break;
case DIV_SYSTEM_GA20:
for (int i=0; i<3; i++) {
for (int i=0; i<4; i++) {
w->writeC(0xbf); // mute
w->writeC((baseAddr2|5)+(i*8));
w->writeC(0);
@ -573,6 +573,16 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write
w->writeC(0);
}
break;
case DIV_SYSTEM_K053260:
for (int i=0; i<4; i++) {
w->writeC(0xba); // mute
w->writeC(baseAddr2|0x2f);
w->writeC(0);
w->writeC(0xba); // keyoff
w->writeC(baseAddr2|0x28);
w->writeC(0);
}
break;
default:
break;
}
@ -928,6 +938,11 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write
w->writeC(baseAddr2|(write.addr&0x7f));
w->writeC(write.val);
break;
case DIV_SYSTEM_K053260:
w->writeC(0xba);
w->writeC(baseAddr2|(write.addr&0x3f));
w->writeC(write.val&0xff);
break;
default:
logW("write not handled!");
break;
@ -1093,6 +1108,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
DivDispatch* writeRF5C68[2]={NULL,NULL};
DivDispatch* writeMSM6295[2]={NULL,NULL};
DivDispatch* writeGA20[2]={NULL,NULL};
DivDispatch* writeK053260[2]={NULL,NULL};
for (int i=0; i<song.systemLen; i++) {
willExport[i]=false;
@ -1608,6 +1624,21 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
howManyChips++;
}
break;
case DIV_SYSTEM_K053260:
if (!hasK053260) {
hasK053260=disCont[i].dispatch->chipClock;
CHIP_VOL(40,0.4);
willExport[i]=true;
writeK053260[0]=disCont[i].dispatch;
} else if (!(hasK053260&0x40000000)) {
isSecond[i]=true;
CHIP_VOL_SECOND(40,0.4);
willExport[i]=true;
writeK053260[1]=disCont[i].dispatch;
hasK053260|=0x40000000;
howManyChips++;
}
break;
case DIV_SYSTEM_T6W28:
if (!hasSN) {
hasSN=0xc0000000|disCont[i].dispatch->chipClock;
@ -1964,6 +1995,15 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
w->writeI(0);
w->write(writeGA20[i]->getSampleMem(),writeGA20[i]->getSampleMemUsage());
}
if (writeK053260[i]!=NULL && writeK053260[i]->getSampleMemUsage()>0) {
w->writeC(0x67);
w->writeC(0x66);
w->writeC(0x8e);
w->writeI((writeK053260[i]->getSampleMemUsage()+8)|(i*0x80000000));
w->writeI(writeK053260[i]->getSampleMemCapacity());
w->writeI(0);
w->write(writeK053260[i]->getSampleMem(),writeK053260[i]->getSampleMemUsage());
}
}
// TODO

View File

@ -438,6 +438,10 @@ void FurnaceGUI::drawInsList(bool asChild) {
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_PV1000]);
name=fmt::sprintf(ICON_FA_GAMEPAD "##_INS%d",i);
break;
case DIV_INS_K053260:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_K053260]);
name=fmt::sprintf(ICON_FA_BAR_CHART "##_INS%d",i);
break;
default:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_UNKNOWN]);
name=fmt::sprintf(ICON_FA_QUESTION "##_INS%d",i);

View File

@ -53,6 +53,7 @@
#include "../engine/platform/ga20.h"
#include "../engine/platform/sm8521.h"
#include "../engine/platform/pv1000.h"
#include "../engine/platform/k053260.h"
#include "../engine/platform/dummy.h"
#define COMMON_CHIP_DEBUG \
@ -545,6 +546,13 @@ void putDispatchChip(void* data, int type) {
COMMON_CHIP_DEBUG_BOOL;
break;
}
case DIV_SYSTEM_K053260: {
DivPlatformK053260* ch=(DivPlatformK053260*)data;
ImGui::Text("> K053260");
COMMON_CHIP_DEBUG;
COMMON_CHIP_DEBUG_BOOL;
break;
}
default:
ImGui::Text("Unimplemented chip! Help!");
break;
@ -1083,6 +1091,18 @@ void putDispatchChan(void* data, int chanNum, int type) {
COMMON_CHAN_DEBUG_BOOL;
break;
}
case DIV_SYSTEM_K053260: {
DivPlatformK053260::Channel* ch=(DivPlatformK053260::Channel*)data;
ImGui::Text("> K053260");
COMMON_CHAN_DEBUG;
ImGui::Text("* Sample: %d",ch->sample);
ImGui::Text(" - pos: %d",ch->audPos);
ImGui::Text("- panning: %d",ch->panning);
ImGui::Text("- macroVolMul: %.2x",ch->macroVolMul);
COMMON_CHAN_DEBUG_BOOL;
ImGui::TextColored(ch->setPos?colorOn:colorOff,">> SetPos");
break;
}
default:
ImGui::Text("Unimplemented chip! Help!");
break;

View File

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

View File

@ -193,6 +193,7 @@ enum FurnaceGUIColors {
GUI_COLOR_INSTR_POKEMINI,
GUI_COLOR_INSTR_SM8521,
GUI_COLOR_INSTR_PV1000,
GUI_COLOR_INSTR_K053260,
GUI_COLOR_INSTR_UNKNOWN,
GUI_COLOR_CHANNEL_BG,

View File

@ -131,6 +131,7 @@ const char* insTypes[DIV_INS_MAX+1]={
"Pokémon Mini/QuadTone",
"SM8521",
"PV-1000",
"K053260",
NULL
};
@ -822,6 +823,7 @@ const FurnaceGUIColorDef guiColors[GUI_COLOR_MAX]={
D(GUI_COLOR_INSTR_POKEMINI,"",ImVec4(1.0f,1.0f,0.3f,1.0f)),
D(GUI_COLOR_INSTR_SM8521,"",ImVec4(0.5f,0.55f,0.6f,1.0f)),
D(GUI_COLOR_INSTR_PV1000,"",ImVec4(0.4f,0.6f,0.7f,1.0f)),
D(GUI_COLOR_INSTR_K053260,"",ImVec4(1.0f,0.8f,0.1f,1.0f)),
D(GUI_COLOR_INSTR_UNKNOWN,"",ImVec4(0.3f,0.3f,0.3f,1.0f)),
D(GUI_COLOR_CHANNEL_BG,"",ImVec4(0.4f,0.6f,0.8f,1.0f)),
@ -1005,6 +1007,7 @@ const int availableSystems[]={
DIV_SYSTEM_GA20,
DIV_SYSTEM_SM8521,
DIV_SYSTEM_PV1000,
DIV_SYSTEM_K053260,
DIV_SYSTEM_PCM_DAC,
DIV_SYSTEM_PONG,
0 // don't remove this last one!
@ -1113,6 +1116,7 @@ const int chipsSample[]={
DIV_SYSTEM_GA20,
DIV_SYSTEM_PCM_DAC,
DIV_SYSTEM_ES5506,
DIV_SYSTEM_K053260,
0 // don't remove this last one!
};

View File

@ -4338,7 +4338,8 @@ void FurnaceGUI::drawInsEdit() {
ins->type==DIV_INS_SNES ||
ins->type==DIV_INS_ES5506 ||
ins->type==DIV_INS_K007232 ||
ins->type==DIV_INS_GA20) {
ins->type==DIV_INS_GA20 ||
ins->type==DIV_INS_K053260) {
if (ImGui::BeginTabItem((ins->type==DIV_INS_SU)?"Sound Unit":"Sample")) {
String sName;
if (ins->amiga.initSample<0 || ins->amiga.initSample>=e->song.sampleLen) {
@ -5078,7 +5079,8 @@ void FurnaceGUI::drawInsEdit() {
}
if (ins->type==DIV_INS_FM || ins->type==DIV_INS_SEGAPCM || ins->type==DIV_INS_MIKEY ||
ins->type==DIV_INS_MULTIPCM || ins->type==DIV_INS_SU || ins->type==DIV_INS_OPZ ||
ins->type==DIV_INS_OPM || ins->type==DIV_INS_SNES || ins->type==DIV_INS_MSM5232) {
ins->type==DIV_INS_OPM || ins->type==DIV_INS_SNES || ins->type==DIV_INS_MSM5232 ||
ins->type==DIV_INS_K053260) {
volMax=127;
}
if (ins->type==DIV_INS_GB) {
@ -5167,7 +5169,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_SEGAPCM ||
ins->type==DIV_INS_FM || ins->type==DIV_INS_K007232 || ins->type==DIV_INS_GA20 ||
ins->type==DIV_INS_SM8521 || ins->type==DIV_INS_PV1000) {
ins->type==DIV_INS_SM8521 || ins->type==DIV_INS_PV1000 || ins->type==DIV_INS_K053260) {
dutyMax=0;
}
if (ins->type==DIV_INS_VBOY) {
@ -5267,6 +5269,7 @@ void FurnaceGUI::drawInsEdit() {
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_K053260) waveMax=0;
if (ins->type==DIV_INS_POKEMINI) waveMax=0;
if (ins->type==DIV_INS_SU || ins->type==DIV_INS_POKEY) waveMax=7;
if (ins->type==DIV_INS_PET) {
@ -5385,6 +5388,11 @@ void FurnaceGUI::drawInsEdit() {
panMax=7;
panSingleNoBit=true;
}
if (ins->type==DIV_INS_K053260) {
panMin=-3;
panMax=3;
panSingleNoBit=true;
}
if (ins->type==DIV_INS_SU) {
panMin=-127;
panMax=127;
@ -5475,7 +5483,8 @@ void FurnaceGUI::drawInsEdit() {
ins->type==DIV_INS_VBOY ||
(ins->type==DIV_INS_X1_010 && ins->amiga.useSample) ||
ins->type==DIV_INS_K007232 ||
ins->type==DIV_INS_GA20) {
ins->type==DIV_INS_GA20 ||
ins->type==DIV_INS_K053260) {
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

@ -1245,6 +1245,42 @@ void FurnaceGUI::initSystemPresets() {
CH(DIV_SYSTEM_K007232, 1.0f, 0, "") // ""
}
);
ENTRY(
"Konami Rollergames", {
CH(DIV_SYSTEM_OPL2, 1.0f, 0, ""), // 3.58MHz
CH(DIV_SYSTEM_K053260, 1.0f, 0, ""), // ""
}
);
ENTRY(
"Konami Rollergames (drums mode)", {
CH(DIV_SYSTEM_OPL2_DRUMS, 1.0f, 0, ""), // 3.58MHz
CH(DIV_SYSTEM_K053260, 1.0f, 0, ""), // ""
}
);
ENTRY(
"Konami Golfing Greats", {
CH(DIV_SYSTEM_K053260, 1.0f, 0, ""), // 3.58MHz
}
);
ENTRY(
"Konami Lightning Fighters", {
CH(DIV_SYSTEM_YM2151, 1.0f, 0, ""), // 3.58MHz
CH(DIV_SYSTEM_K053260, 1.0f, 0, ""), // ""
}
);
ENTRY(
"Konami Over Drive", {
CH(DIV_SYSTEM_YM2151, 1.0f, 0, ""), // 3.58MHz
CH(DIV_SYSTEM_K053260, 1.0f, 0, ""), // ""
CH(DIV_SYSTEM_K053260, 1.0f, 0, ""), // ""
}
);
ENTRY(
"Konami Asterix", {
CH(DIV_SYSTEM_YM2151, 1.0f, 0, "clockSel=2"), // 4MHz
CH(DIV_SYSTEM_K053260, 1.0f, 0, "clockSel=1"), // ""
}
);
ENTRY(
"Konami Hexion", {
CH(DIV_SYSTEM_SCC, 1.0f, 0, "clockSel=2"), // 1.5MHz (3MHz input)
@ -2449,6 +2485,11 @@ void FurnaceGUI::initSystemPresets() {
CH(DIV_SYSTEM_ES5506, 1.0f, 0, "channels=31")
}
);
ENTRY(
"Konami K053260", {
CH(DIV_SYSTEM_K053260, 1.0f, 0, "")
}
);
CATEGORY_END;
CATEGORY_BEGIN("Wavetable","chips which use user-specified waveforms to generate sound.");

View File

@ -1954,6 +1954,7 @@ void FurnaceGUI::drawSettings() {
UI_COLOR_CONFIG(GUI_COLOR_INSTR_POKEMINI,"Pokémon Mini");
UI_COLOR_CONFIG(GUI_COLOR_INSTR_SM8521,"SM8521");
UI_COLOR_CONFIG(GUI_COLOR_INSTR_PV1000,"PV-1000");
UI_COLOR_CONFIG(GUI_COLOR_INSTR_K053260,"K053260");
UI_COLOR_CONFIG(GUI_COLOR_INSTR_UNKNOWN,"Other/Unknown");
ImGui::TreePop();
}

View File

@ -1784,6 +1784,26 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo
}
break;
}*/
case DIV_SYSTEM_K053260: {
int clockSel=flags.getInt("clockSel",0);
ImGui::Text("Clock rate:");
if (ImGui::RadioButton("3.58MHz (NTSC)",clockSel==0)) {
clockSel=0;
altered=true;
}
if (ImGui::RadioButton("4MHz",clockSel==1)) {
clockSel=1;
altered=true;
}
if (altered) {
e->lockSave([&]() {
flags.set("clockSel",clockSel);
});
}
break;
}
case DIV_SYSTEM_SWAN:
case DIV_SYSTEM_BUBSYS_WSG:
case DIV_SYSTEM_PET: