Merge branch 'tildearrow:master' into master

This commit is contained in:
Eknous 2023-07-18 14:16:15 +04:00 committed by GitHub
commit 6daf9b3d18
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 1050 additions and 141 deletions

View File

@ -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

View File

@ -95,3 +95,24 @@ now for decimal number `69420`:
= 10F2C
```
# hex-decimal table
hex | `0` | `1` | `2` | `3` | `4` | `5` | `6` | `7` | `8` | `9` | `A` | `B` | `C` | `D` | `E` | `F`
-----:|----:|----:|----:|----:|----:|----:|----:|----:|----:|----:|----:|----:|----:|----:|----:|----:
`00` | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15
`10` | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31
`20` | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47
`30` | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63
`40` | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79
`50` | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95
`60` | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111
`70` | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127
`80` | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143
`90` | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159
`A0` | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175
`B0` | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191
`C0` | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207
`D0` | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223
`E0` | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239
`F0` | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255

View File

@ -9,6 +9,7 @@ the default layout of Furnace is depicted below.
primary topics:
- [menu bar](menu-bar.md)
- [order list](order-list.md)
- [play/edit controls](play-edit-controls.md)
- [instrument/wavetable/sample list](asset-list.md)
- [song information](song-info.md)

View File

@ -1,6 +1,6 @@
# menu bar
the menu bar allows you to select five menus: file, edit, settings, window and help.
the menu bar allows you to select from five menus: file, edit, settings, window and help.
# file
@ -154,8 +154,8 @@ it's not really useful, unless you're a developer and want to use a command stre
- if a column is already selected, it will select the entire channel.
- if a channel is already selected, it will select the entire pattern.
- **operation mask**: this is an advanced feature. see [this page](../3-pattern/opmask.md) for more information.
- **input latch**: this is an advanced feature. see [this page](../3-pattern/inputlatch.md) for more information.
- **operation mask**: toggles which columns will be affected by the listed operations. [more information here.](../8-advanced/opmask.md)
- **input latch**: determines which data are placed along with a note. [more information here.](../8-advanced/inputlatch.md)
- **note/octave up/down**: transposes notes in the current selection.
@ -176,7 +176,7 @@ it's not really useful, unless you're a developer and want to use a command stre
- **invert values**: `00` becomes `FF`, `01` becomes `FE`, `02` becomes `FD` and so on.
- **flip selection**: flips the selection so it is backwards.
- **collapse/expand amount**: allows you to specify how much to collapse/expand in the next options.
- **collapse/expand amount**: allows you to specify how much to collapse/expand in the next two menu items.
- **collapse**: shrinks the selected contents.
- **expand**: expands the selected contents.
@ -188,7 +188,7 @@ it's not really useful, unless you're a developer and want to use a command stre
- **expand song**: same as expand, but affects the entire song.
- it also changes speeds and pattern length to compensate.
- **find/replace**: opens the Find/Replace window. see [this page](../3-pattern/find-replace.md) for more information.
- **find/replace**: shows [the Find/Replace window](../8-advanced/find-replace.md).
- **clear**: allows you to mass-delete things like songs, instruments and the like.
@ -199,46 +199,52 @@ it's not really useful, unless you're a developer and want to use a command stre
- **basic mode**: toggles [Basic Mode](basic-mode.md).
- **visualizer**: toggles pattern view particle effects when the song plays.
- **reset layout**: resets the workspace to its defaults.
- **settings...**: opens the Settings window. these are detailed in [settings.md].
- **settings...**: shows the Settings window. these are detailed in [settings.md].
# window
- **song information**: shows/hides the Song Information window.
- **subsongs**: shows/hides the Subsongs window.
- **speed**: shows/hides the Speed window.
- **instruments**: shows/hides the instrument list.
- **wavetables**: shows/hides the wavetable list.
- **samples**: shows/hides the sample list.
- **orders**: shows/hides the Orders window.
- **pattern**: shows/hides the pattern view.
- **mixer**: shows/hides the Mixer window.
- **grooves**: shows/hides the Grooves window.
- **channels**: shows/hides the Channels window.
- **pattern manager**: shows/hides the Pattern Manager window.
- **chip manager**: shows/hides the Chip Manager window.
- **compatibility flags**: shows/hides the Compatibility Flags window.
- **song comments**: shows/hides the Song Comments window.
all these menu items show or hide their associated windows.
- **instrument editor**: shows/hides the Instrument Editor
- **wavetable editor**: shows/hides the Wavetable Editor.
- **sample editor**: shows/hides the Sample Editor.
- [song information](song-info.md)
- [subsongs](song-info.md)
- [speed](song-info.md)
- [instruments](../4-instrument/README.md)
- [wavetables](../5-wave/README.md)
- [samples](../6-sample/README.md)
- [orders](order-list.md)
- [pattern](../3-pattern/README.md)
- [mixer](mixer.md)
- [grooves](grooves.md)
- [channels](channels.md)
- [pattern manager](pat-manager.md)
- [chip manager](chip-manager.md)
- [compatibility flags](compat-flags.md)
- [song comments](comments.md)
- **play/edit controls**: shows/hides the Play/Edit Controls.
- **piano/input pad**: shows/hides the Piano/Input Pad window.
- **oscilloscope (master)**: shows/hides the oscilloscope.
- **oscilloscope (per-channel)**: shows/hides the per-channel oscilloscope.
- **volume meter**: shows/hides the volume meter.
- **clock**: shows/hides the clock.
- **register view**: shows/hides the Register View window.
- **log viewer**: shows/hides the log Viewer.
- **statistics**: shows/hides the Statistics window.
- [piano](piano.md)
- [oscilloscope](osc.md)
- [oscilloscopes (per-channel)](chanosc.md)
- [clock](clock.md)
- [register view](regview.md)
- [log viewer](log-viewer.md)
- [stats](stats.md)
# help
- **effect list**: displays the effect list.
- **debug menu**: this menu contains various debug utilities.
- unless you are working with the Furnace codebase, it's not useful.
- **inspector**: this options opens the Dear ImGui Metrics/Debugger window.
- **inspector**: this option shows the Dear ImGui Metrics/Debugger window.
- unless you are working with the Furnace codebase, it's not useful.
- **panic**: this resets all chips while the song is playing, effectively silencing everything.
- **about...**: displays the About screen.
at the end of the menu bar, more information may be shown:
- during editing, information about the data under the cursor will be shown here:
- note or note modifier.
- instrument number and name.
- volume in decimal, hex, and percentage.
- effect type and description.
- during playback, the current values of the following will be listed:\
speed/groove @ tick rate (BPM) | order | row | elapsed time.
- if any changes or edits have been made but not yet saved, "modified" will appear.

View File

@ -0,0 +1,29 @@
# order list
the order list is a playlist for patterns.
![order list](order-list.png)
along the top are the available channels. their abbreviations can be set in the [channels window](../8-advanced/channels.md). the highlighted channel follows the channel the pattern view cursor is in.
along the left are the order numbers. these are referenced with the `0Bxx` command. the highlighted row follows the order the pattern view cursor is in.
each entry in the table is the pattern that will play during that order. these can be changed according to the order edit mode.
hovering over a pattern number will pop up a tooltip showing the name of that pattern, if it has one.
The buttons are as follows:
- **Add new order**.
- **Remove order**.
- **Duplicate order**: adds a new order with patterns matching the selected one directly below it. right-click to "deep clone"; this copies all patterns involved to new ones.
- **Move order up**: swaps the selected order with the one above it.
- **Move order down**: swaps the selected order with the one below it.
- **Duplicate order at end of song**: same as "Duplicate order" except the new order is added at the bottom of the list.
- **Order change mode**: selects how much of the order will change with an edit. only applies if "Order edit mode" is set to "Click to change".
- **one**: only current channel's pattern will change.
- **entire row**: all patterns in the order will change.
- **Order edit mode**: selects the method of changing orders.
- **Click to change**: a click will add one to the pattern number. a right-click will subtract one.
- **Select and type (don't scroll)**: select a pattern and type.
- **Select and type (scroll horizontally)**: as above, but after entering two digits, the cursor moves to the next channel.
- **Select and type (scroll vertically)**: as above, but after entering two digits, the cursor moves to the next order.

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

View File

@ -0,0 +1,11 @@
# input latch
![input latch menu item](inputlatch.png)
input latch determines which data are placed along with a note. as in the pattern view, the columns are note (not changeable), instrument, volume, effect type, and effect value.
- `&&` fills in the currently selected instrument.
- `..` ignores the column.
- all columns (except note) can be reset with a right-click.
- **Set**: sets latch according to the data found at the cursor.
- **Reset**: resets all columns to default (selected instrument, ignore others).
- only the first effect type and effect value may be latched.

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

7
doc/8-advanced/opmask.md Normal file
View File

@ -0,0 +1,7 @@
# operation mask
![operation mask popup](opmask.png)
the operation mask toggles which columns will be affected by the listed operations. as in the pattern view, the columns are note, instrument, volume, effect types, and effect values. the effect toggles apply to all effect columns.
click any area to toggle it. a `---` or `--` means the operation will ignore any data in that column.

BIN
doc/8-advanced/opmask.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

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,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;
}

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
{
@ -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

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"
@ -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;

View File

@ -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;

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,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];
}
}

View 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

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
};
enum DivEffectType: unsigned short {

View File

@ -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.",

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;
}
@ -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);

View File

@ -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);

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 \
@ -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;

View File

@ -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);
}
}

View File

@ -39,7 +39,7 @@ const char* queryReplaceModes[GUI_QUERY_REPLACE_MAX]={
"set",
"add",
"add (overflow)",
"scale",
"scale %",
"clear"
};

View File

@ -1418,6 +1418,7 @@ void FurnaceGUI::keyDown(SDL_Event& ev) {
try {
int num=valueKeys.at(ev.key.keysym.sym);
if (orderCursor>=0 && orderCursor<e->getTotalChannelCount()) {
prepareUndo(GUI_UNDO_CHANGE_ORDER);
e->lockSave([this,num]() {
e->curOrders->ord[orderCursor][curOrder]=((e->curOrders->ord[orderCursor][curOrder]<<4)|num);
});
@ -1436,6 +1437,7 @@ void FurnaceGUI::keyDown(SDL_Event& ev) {
}
}
e->walkSong(loopOrder,loopRow,loopEnd);
makeUndo(GUI_UNDO_CHANGE_ORDER);
}
} catch (std::out_of_range& e) {
}
@ -4848,6 +4850,7 @@ bool FurnaceGUI::loop() {
case GUI_FILE_SAMPLE_OPEN_RAW:
case GUI_FILE_SAMPLE_OPEN_REPLACE_RAW:
pendingRawSample=copyOfName;
pendingRawSampleReplace=(curFileDialog==GUI_FILE_SAMPLE_OPEN_REPLACE_RAW);
displayPendingRawSample=true;
break;
case GUI_FILE_SAMPLE_SAVE:
@ -5715,10 +5718,26 @@ bool FurnaceGUI::loop() {
if (s==NULL) {
showError(e->getLastError());
} else {
if (e->addSamplePtr(s)==-1) {
showError(e->getLastError());
if (pendingRawSampleReplace) {
if (curSample>=0 && curSample<(int)e->song.sample.size()) {
e->lockEngine([this,s]() {
// if it crashes here please tell me...
DivSample* oldSample=e->song.sample[curSample];
e->song.sample[curSample]=s;
delete oldSample;
e->renderSamples();
MARK_MODIFIED;
});
} else {
showError("...but you haven't selected a sample!");
delete s;
}
} else {
MARK_MODIFIED;
if (e->addSamplePtr(s)==-1) {
showError(e->getLastError());
} else {
MARK_MODIFIED;
}
}
}
ImGui::CloseCurrentPopup();
@ -6756,6 +6775,7 @@ FurnaceGUI::FurnaceGUI():
pendingRawSampleUnsigned(false),
pendingRawSampleBigEndian(false),
pendingRawSampleSwapNibbles(false),
pendingRawSampleReplace(false),
globalWinFlags(0),
curFileDialog(GUI_FILE_OPEN),
warnAction(GUI_WARN_OPEN),

View File

@ -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,
@ -1334,7 +1335,7 @@ class FurnaceGUI {
String pendingRawSample;
int pendingRawSampleDepth, pendingRawSampleChannels;
bool pendingRawSampleUnsigned, pendingRawSampleBigEndian, pendingRawSampleSwapNibbles;
bool pendingRawSampleUnsigned, pendingRawSampleBigEndian, pendingRawSampleSwapNibbles, pendingRawSampleReplace;
ImGuiWindowFlags globalWinFlags;

View File

@ -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!
};

View File

@ -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) {

View File

@ -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.");

View File

@ -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;
}

View File

@ -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();
}

View File

@ -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: