mirror of
https://github.com/tildearrow/furnace.git
synced 2024-12-03 17:57:26 +00:00
commit
ec9a3180b2
21 changed files with 916 additions and 102 deletions
|
@ -588,6 +588,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
|
||||
|
||||
|
|
|
@ -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,58 @@ 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_addr = m_reverse ? bitfield(m_addr - 1, 0, 21) : 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_reverse ? (~m_bitpos & 4) : (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 = (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
|
||||
{
|
||||
|
@ -172,6 +169,7 @@ void k053260_core::write(u8 address, u8 data)
|
|||
case 0x28: // keyon/off toggle
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
m_voice[i].set_reverse(bitfield(data, 4 + i));
|
||||
if (bitfield(data, i) && (!m_voice[i].enable()))
|
||||
{ // rising edge (keyon)
|
||||
m_voice[i].keyon();
|
||||
|
@ -244,8 +242,9 @@ void k053260_core::voice_t::keyon()
|
|||
m_addr = m_start;
|
||||
m_remain = m_length;
|
||||
m_bitpos = 4;
|
||||
m_adpcm_buf = 0;
|
||||
std::fill(m_out.begin(), m_out.end(), 0);
|
||||
m_data = 0;
|
||||
m_output = 0;
|
||||
std::fill_n(m_out, 2, 0);
|
||||
}
|
||||
|
||||
// key off trigger
|
||||
|
@ -259,34 +258,35 @@ 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);
|
||||
std::fill_n(m_host2snd, 2, 0);
|
||||
std::fill_n(m_snd2host, 2, 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);
|
||||
std::fill_n(m_reg, 64, 0);
|
||||
std::fill_n(m_out, 2, 0);
|
||||
}
|
||||
|
||||
// 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_reverse = 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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
@ -47,23 +59,24 @@ class k053260_core : public vgsound_emu_core
|
|||
, m_loop(0)
|
||||
, m_adpcm(0)
|
||||
, m_pitch(0)
|
||||
, m_reverse(0)
|
||||
, 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);
|
||||
std::fill_n(m_out, 2, 0);
|
||||
}
|
||||
|
||||
// internal state
|
||||
void reset();
|
||||
void tick();
|
||||
void tick(u32 cycle);
|
||||
|
||||
// accessors
|
||||
void write(u8 address, u8 data);
|
||||
|
@ -79,9 +92,14 @@ class k053260_core : public vgsound_emu_core
|
|||
|
||||
inline void set_adpcm(bool adpcm) { m_adpcm = adpcm ? 1 : 0; }
|
||||
|
||||
inline void set_reverse(const bool reverse)
|
||||
{
|
||||
m_reverse = reverse ? 1 : 0;
|
||||
}
|
||||
|
||||
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,22 +115,23 @@ 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
|
||||
std::array<s32, 2> m_out; // current output
|
||||
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
|
||||
u8 m_reverse : 1; // reverse playback
|
||||
u32 m_start = 0; // start position
|
||||
u16 m_length = 0; // source length
|
||||
u8 m_volume = 0; // master volume
|
||||
s32 m_pan = 4; // 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
|
||||
s32 m_out[2]; // current output
|
||||
};
|
||||
|
||||
class ctrl_t
|
||||
|
@ -152,6 +171,7 @@ class k053260_core : public vgsound_emu_core
|
|||
u8 m_input_en : 2; // Input enable
|
||||
};
|
||||
|
||||
/*
|
||||
class ym3012_t
|
||||
{
|
||||
public:
|
||||
|
@ -177,7 +197,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 +227,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,13 +236,13 @@ 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);
|
||||
m_reg.fill(0);
|
||||
m_out.fill(0);
|
||||
std::fill_n(m_host2snd, 2, 0);
|
||||
std::fill_n(m_snd2host, 2, 0);
|
||||
std::fill_n(m_reg, 64, 0);
|
||||
std::fill_n(m_out, 2, 0);
|
||||
}
|
||||
|
||||
// communications
|
||||
|
@ -233,7 +256,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,20 +268,25 @@ 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;
|
||||
voice_t m_voice[4];
|
||||
k053260_intf &m_intf; // common memory interface
|
||||
|
||||
std::array<u8, 2> m_host2snd;
|
||||
std::array<u8, 2> m_snd2host;
|
||||
u8 m_host2snd[2];
|
||||
u8 m_snd2host[2];
|
||||
|
||||
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
|
||||
u8 m_reg[64]; // register pool
|
||||
s32 m_out[2]; // stereo output
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -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"
|
||||
|
@ -503,6 +504,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;
|
||||
|
|
|
@ -929,6 +929,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;
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
513
src/engine/platform/k053260.cpp
Normal file
513
src/engine/platform/k053260.cpp
Normal file
|
@ -0,0 +1,513 @@
|
|||
/**
|
||||
* 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 16
|
||||
#define TICK_DIVIDER 64 // for match to YM3012 output rate
|
||||
|
||||
const char* regCheatSheetK053260[]={
|
||||
"MainToSub0", "00",
|
||||
"MainToSub1", "01",
|
||||
"SubToMain0", "02",
|
||||
"SubToMain1", "03",
|
||||
"CHx_FreqL", "08+x*8",
|
||||
"CHx_FreqH", "09+x*8",
|
||||
"CHx_LengthL", "0A+x*8",
|
||||
"CHx_LengthH", "0B+x*8",
|
||||
"CHx_StartL", "0C+x*8",
|
||||
"CHx_StartM", "0D+x*8",
|
||||
"CHx_StartH", "0E+x*8",
|
||||
"CHx_Volume", "0F+x*8",
|
||||
"KeyOn", "28",
|
||||
"Status", "29",
|
||||
"LoopFormat", "2A",
|
||||
"Test", "2B",
|
||||
"CH01_Pan", "2C",
|
||||
"CH23_Pan", "2D",
|
||||
"ROMReadback", "2E",
|
||||
"Control", "2F",
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
u8 DivPlatformK053260::read_sample(u32 address) {
|
||||
if ((sampleMem!=NULL) && (address<getSampleMemCapacity())) {
|
||||
return sampleMem[address&0x1fffff];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void DivPlatformK053260::acquire(short** buf, size_t len) {
|
||||
for (size_t 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))>>2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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&~(17<<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=s->length8;
|
||||
if (chan[i].reverse) {
|
||||
start+=length;
|
||||
keyon|=(16<<i);
|
||||
}
|
||||
}
|
||||
if (chan[i].audPos>0) {
|
||||
if (chan[i].reverse) {
|
||||
start=start-MIN(chan[i].audPos,s->length8);
|
||||
}
|
||||
else {
|
||||
start=start+MIN(chan[i].audPos,s->length8);
|
||||
}
|
||||
length=MAX(1,length-chan[i].audPos);
|
||||
}
|
||||
start=MIN(start,getSampleMemCapacity());
|
||||
length=MIN(65535,MIN(length,getSampleMemCapacity()));
|
||||
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);
|
||||
c.value=ins->amiga.getFreq(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_SAMPLE_DIR: {
|
||||
if (chan[c.chan].reverse!=(bool)(c.value&1)) {
|
||||
chan[c.chan].reverse=c.value&1;
|
||||
}
|
||||
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,64);
|
||||
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&0x3f,val);
|
||||
}
|
||||
|
||||
void DivPlatformK053260::poke(std::vector<DivRegWrite>& wlist) {
|
||||
for (DivRegWrite& i: wlist) rWrite(i.addr&0x3f,i.val);
|
||||
}
|
||||
|
||||
unsigned char* DivPlatformK053260::getRegisterPool() {
|
||||
regPool[0x29]=k053260.read(0x29); // dynamically updated
|
||||
return regPool;
|
||||
}
|
||||
|
||||
int DivPlatformK053260::getRegisterPoolSize() {
|
||||
return 64;
|
||||
}
|
||||
|
||||
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->getEndPosition(DIV_SAMPLE_DEPTH_8BIT));
|
||||
int actualLength=MIN((int)(getSampleMemCapacity()-memPos-1),length);
|
||||
if (actualLength>0) {
|
||||
sampleOffK053260[i]=memPos-1;
|
||||
for (int j=0; j<actualLength; j++) {
|
||||
sampleMem[memPos++]=s->data8[j];
|
||||
}
|
||||
sampleMem[memPos++]=0; // Silence for avoid popping noise
|
||||
}
|
||||
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];
|
||||
}
|
||||
}
|
97
src/engine/platform/k053260.h
Normal file
97
src/engine/platform/k053260.h
Normal file
|
@ -0,0 +1,97 @@
|
|||
/**
|
||||
* 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, reverse;
|
||||
int macroVolMul;
|
||||
Channel():
|
||||
SharedChannel<int>(127),
|
||||
audPos(0),
|
||||
sample(-1),
|
||||
wave(-1),
|
||||
panning(4),
|
||||
setPos(false),
|
||||
reverse(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[64];
|
||||
void updatePanning(unsigned char mask);
|
||||
|
||||
friend void putDispatchChip(void*,int);
|
||||
friend void putDispatchChan(void*,int,int);
|
||||
|
||||
public:
|
||||
virtual u8 read_sample(u32 address) override;
|
||||
virtual void acquire(short** buf, size_t len) override;
|
||||
virtual int dispatch(DivCommand c) override;
|
||||
virtual void* getChanState(int chan) override;
|
||||
virtual DivMacroInt* getChanMacroInt(int ch) override;
|
||||
virtual DivDispatchOscBuffer* getOscBuffer(int chan) override;
|
||||
virtual unsigned char* getRegisterPool() override;
|
||||
virtual int getRegisterPoolSize() override;
|
||||
virtual void reset() override;
|
||||
virtual void forceIns() override;
|
||||
virtual void tick(bool sysTick=true) override;
|
||||
virtual void muteChannel(int ch, bool mute) override;
|
||||
virtual int getOutputCount() override;
|
||||
virtual void notifyInsChange(int ins) override;
|
||||
virtual void notifyWaveChange(int wave) override;
|
||||
virtual void notifyInsDeletion(void* ins) override;
|
||||
virtual void setFlags(const DivConfig& flags) override;
|
||||
virtual void poke(unsigned int addr, unsigned short val) override;
|
||||
virtual void poke(std::vector<DivRegWrite>& wlist) override;
|
||||
virtual const char** getRegisterSheet() override;
|
||||
virtual const void* getSampleMem(int index = 0) override;
|
||||
virtual size_t getSampleMemCapacity(int index = 0) override;
|
||||
virtual size_t getSampleMemUsage(int index = 0) override;
|
||||
virtual bool isSampleLoaded(int index, int sample) override;
|
||||
virtual void renderSamples(int chipID) override;
|
||||
virtual int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags) override;
|
||||
virtual void quit() override;
|
||||
DivPlatformK053260():
|
||||
DivDispatch(),
|
||||
k053260_intf(),
|
||||
k053260(*this) {}
|
||||
private:
|
||||
void chWrite(unsigned char ch, unsigned int addr, unsigned char val);
|
||||
};
|
||||
|
||||
#endif
|
|
@ -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
|
||||
};
|
||||
|
||||
enum DivEffectType: unsigned short {
|
||||
|
|
|
@ -1864,6 +1864,19 @@ void DivEngine::registerSystems() {
|
|||
}
|
||||
);
|
||||
|
||||
sysDefs[DIV_SYSTEM_K053260]=new DivSysDef(
|
||||
"Konami K053260", NULL, 0xcc, 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},
|
||||
{
|
||||
{0xdf, {DIV_CMD_SAMPLE_DIR, "DFxx: Set sample playback direction (0: normal; 1: reverse)"}}
|
||||
}
|
||||
);
|
||||
|
||||
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.",
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
@ -1029,6 +1039,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;
|
||||
|
@ -1201,6 +1216,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};
|
||||
DivDispatch* writeNES[2]={NULL,NULL};
|
||||
|
||||
int writeNESIndex[2]={0,0};
|
||||
|
@ -1725,6 +1741,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(29,0.4);
|
||||
willExport[i]=true;
|
||||
writeK053260[0]=disCont[i].dispatch;
|
||||
} else if (!(hasK053260&0x40000000)) {
|
||||
isSecond[i]=true;
|
||||
CHIP_VOL_SECOND(29,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;
|
||||
|
@ -2086,6 +2117,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());
|
||||
}
|
||||
if (writeNES[i]!=NULL && writeNES[i]->getSampleMemUsage()>0) {
|
||||
size_t howMuchWillBeWritten=writeNES[i]->getSampleMemUsage();
|
||||
w->writeC(0x67);
|
||||
|
|
|
@ -279,6 +279,10 @@ void FurnaceGUI::insListItem(int i, int dir, int asset) {
|
|||
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);
|
||||
|
|
|
@ -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 \
|
||||
|
@ -544,6 +545,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;
|
||||
|
@ -1082,6 +1090,19 @@ 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");
|
||||
ImGui::TextColored(ch->reverse?colorOn:colorOff,">> Reverse");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ImGui::Text("Unimplemented chip! Help!");
|
||||
break;
|
||||
|
|
|
@ -1378,7 +1378,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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -239,6 +239,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,
|
||||
|
|
|
@ -131,6 +131,7 @@ const char* insTypes[DIV_INS_MAX+1]={
|
|||
"Pokémon Mini/QuadTone",
|
||||
"SM8521",
|
||||
"PV-1000",
|
||||
"K053260",
|
||||
NULL
|
||||
};
|
||||
|
||||
|
@ -851,6 +852,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)),
|
||||
|
@ -1034,6 +1036,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!
|
||||
|
@ -1142,6 +1145,7 @@ const int chipsSample[]={
|
|||
DIV_SYSTEM_GA20,
|
||||
DIV_SYSTEM_PCM_DAC,
|
||||
DIV_SYSTEM_ES5506,
|
||||
DIV_SYSTEM_K053260,
|
||||
0 // don't remove this last one!
|
||||
};
|
||||
|
||||
|
|
|
@ -4409,7 +4409,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;
|
||||
bool wannaOpenSMPopup=false;
|
||||
|
@ -5316,7 +5317,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) {
|
||||
|
@ -5405,7 +5407,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) {
|
||||
|
@ -5505,6 +5507,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) {
|
||||
|
@ -5623,6 +5626,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;
|
||||
|
@ -5713,7 +5721,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) {
|
||||
|
|
|
@ -1262,6 +1262,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)
|
||||
|
@ -2466,6 +2502,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.");
|
||||
|
|
|
@ -267,6 +267,16 @@ void FurnaceGUI::drawSampleEdit() {
|
|||
SAMPLE_WARN(warnLength,"SegaPCM: maximum sample length is 65280");
|
||||
}
|
||||
break;
|
||||
case DIV_SYSTEM_K053260:
|
||||
if (sample->loop) {
|
||||
if (sample->loopStart!=0 || sample->loopEnd!=(int)(sample->samples)) {
|
||||
SAMPLE_WARN(warnLoopPos,"K053260: loop point ignored (may only loop entire sample)");
|
||||
}
|
||||
}
|
||||
if (sample->samples>65535) {
|
||||
SAMPLE_WARN(warnLength,"K053260: maximum sample length is 65535");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -2102,6 +2102,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();
|
||||
}
|
||||
|
|
|
@ -1882,6 +1882,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:
|
||||
|
|
Loading…
Reference in a new issue