diff --git a/build/Makefile b/build/Makefile
index 135f68c..7f66a00 100644
--- a/build/Makefile
+++ b/build/Makefile
@@ -150,7 +150,7 @@ $(BOX16_OBJDIR)/%.o: $(BOX16_SRCDIR)/%.cpp | $(BOX16_OBJDIRS)
$(BOX16_OBJDIRS):
mkdir -p $@
-$(OUTDIR)/box16: $(NFD_OBJS) $(LPNG_OBJS) $(RTMIDI_OBJS) $(YMFM_OBJS) $(BOX16_OBJS)
+$(OUTDIR)/box16: $(BOX16_OBJS) $(NFD_OBJS) $(LPNG_OBJS) $(RTMIDI_OBJS) $(YMFM_OBJS)
mkdir -p $(OUTDIR)
g++ $^ -o $@ $(BOX16_LDFLAGS) $(NFD_LDFLAGS)
cp $(REPODIR)/resources/*.png $(OUTDIR)/
diff --git a/build/vs2019/box16.vcxproj b/build/vs2019/box16.vcxproj
index 19ad88b..5152347 100644
--- a/build/vs2019/box16.vcxproj
+++ b/build/vs2019/box16.vcxproj
@@ -214,6 +214,7 @@ xcopy $(VendorDir)\glew-2.2.0\bin\Release\x64\*.dll $(OutDir) /E /I /F /Y
+
@@ -245,6 +246,7 @@ xcopy $(VendorDir)\glew-2.2.0\bin\Release\x64\*.dll $(OutDir) /E /I /F /Y
+
@@ -264,6 +266,7 @@ xcopy $(VendorDir)\glew-2.2.0\bin\Release\x64\*.dll $(OutDir) /E /I /F /Y
+
@@ -304,6 +307,7 @@ xcopy $(VendorDir)\glew-2.2.0\bin\Release\x64\*.dll $(OutDir) /E /I /F /Y
+
diff --git a/build/vs2019/box16.vcxproj.filters b/build/vs2019/box16.vcxproj.filters
index 40acd1f..dbe105e 100644
--- a/build/vs2019/box16.vcxproj.filters
+++ b/build/vs2019/box16.vcxproj.filters
@@ -185,6 +185,12 @@
Source Files\imgui
+
+ Source Files
+
+
+ Source Files\overlay
+
@@ -385,6 +391,12 @@
Vendor Source Files\lodepng
+
+ Source Files
+
+
+ Source Files\overlay
+
diff --git a/src/bitutils.cpp b/src/bitutils.cpp
new file mode 100644
index 0000000..32b0ad9
--- /dev/null
+++ b/src/bitutils.cpp
@@ -0,0 +1 @@
+#include "bitutils.h"
diff --git a/src/bitutils.h b/src/bitutils.h
new file mode 100644
index 0000000..c51a388
--- /dev/null
+++ b/src/bitutils.h
@@ -0,0 +1,27 @@
+#pragma once
+#if !defined(BITUTILS_H)
+# define BITUTILS_H
+
+template
+uint8_t get_bit_field(const uint8_t value)
+{
+ static_assert(msb >= lsb);
+ constexpr const uint8_t mask = (2 << (msb - lsb)) - 1;
+ return (value >> lsb) & mask;
+}
+
+template
+uint8_t set_bit_field(const uint8_t src, const uint8_t value)
+{
+ static_assert(msb >= lsb);
+ constexpr const uint8_t mask = (2 << (msb - lsb)) - 1;
+ return (src & ~(mask << lsb)) | (value << lsb);
+}
+
+template
+constexpr T bit_set_or_res(const T val, const T mask, bool cond)
+{
+ return cond ? (val | mask) : (val & ~mask);
+}
+
+#endif
\ No newline at end of file
diff --git a/src/midi.cpp b/src/midi.cpp
index ec5d9d9..b62691d 100644
--- a/src/midi.cpp
+++ b/src/midi.cpp
@@ -1,12 +1,12 @@
#include "midi.h"
-#include
-#include "math.h"
#include "RtMidi.h"
-
-#include "vera/vera_psg.h"
+#include "math.h"
+#include
#include
+#include "vera/vera_psg.h"
+#include "ym2151/ym2151.h"
#define MAX_MIDI_KEYS (128)
@@ -37,10 +37,15 @@ struct psg_midi_mapping {
midi_channel channel;
};
+struct ym_midi_mapping {
+ midi_channel channel;
+};
+
const midi_port_descriptor INVALID_MIDI_PORT{ RtMidi::Api::NUM_APIS, 0xffff };
const midi_channel INVALID_MIDI_CHANNEL{ INVALID_MIDI_PORT, 0xff };
static psg_midi_mapping Psg_midi_mappings[PSG_NUM_CHANNELS];
+static ym_midi_mapping Ym_midi_mappings[MAX_YM2151_VOICES];
static RtMidiIn Midi_in_api;
@@ -181,6 +186,18 @@ static uint16_t Psg_frequency_table[MAX_MIDI_KEYS] = {
33672,
};
+midi_ym_patch_entry Ym_default_patch[] = {
+ { 0x20, 0xc0 }, // YM_R_L_FB_CONN_OFFSET
+ { 0x58, 0x01 }, // YM_DT1_MUL_OFFSET voice 4 slot 3
+ { 0x68, 0x00 }, // YM_TL_OFFSET voice 4 slot 1
+ { 0x70, 0x00 }, // YM_TL_OFFSET voice 4 slot 2
+ { 0x78, 0x00 }, // YM_TL_OFFSET voice 4 slot 3
+ { 0x60, 0x00 }, // YM_TL_OFFSET voice 4 slot 4
+ { 0x98, 0x1f }, // YM_KS_AR_OFFSET voice 4 slot 3
+ { 0xb8, 0x0d }, // YM_A_D1R_OFFSET voice 4 slot 3
+ { 0xf8, 0xf6 } // YM_D1L_RR_OFFSET voice 4 slot 3
+};
+
// -------------------
//
// midi_port_descriptor
@@ -258,6 +275,48 @@ static uint8_t alloc_psg_voice()
return INVALID_VOICE;
}
+// -------------------
+//
+// ym helpers
+//
+// -------------------
+
+static uint8_t alloc_ym_voice()
+{
+ for (uint8_t i = 0; i < MAX_YM2151_VOICES; ++i) {
+ if (Ym_midi_mappings[i].channel == INVALID_MIDI_CHANNEL) {
+ return i;
+ }
+ }
+ return INVALID_VOICE;
+}
+
+static void apply_ym_patch(uint8_t ym_channel, midi_ym_patch_entry *patch_data, int num_entries)
+{
+ for (int i = 0; i < num_entries; ++i) {
+ YM_write(0, patch_data[i].addr + ym_channel);
+ YM_write(1, patch_data[i].value);
+ }
+}
+
+uint8_t midi_ym_note(uint8_t midikey)
+{
+ const uint8_t chroma = (midikey + 11) % 12;
+ return chroma + (chroma / 3);
+}
+
+uint8_t midi_ym_octave(uint8_t midikey)
+{
+ if (midikey < 13) {
+ return 0;
+ }
+ const uint8_t result = (midikey - 13) / 12;
+ if (result > 7) {
+ return 7;
+ }
+ return result;
+}
+
// -------------------
//
// midi message helpers
@@ -290,10 +349,16 @@ static uint16_t get_bent_frequency(int keynum, int bend)
return (uint16_t)(f0 + ((diff * bend) >> 13));
}
-static uint8_t get_velocitated_volume(int volume, int velocity)
+static uint8_t get_psg_velocitated_volume(int volume, int velocity)
+{
+ const float vv = sqrtf((float)(volume * velocity) / (127.0f * 127.0f));
+ return (uint8_t)(vv * 63.0f);
+}
+
+static uint8_t get_ym_velocitated_volume(int volume, int velocity)
{
- const int vv = (const int)sqrtf((float)(volume * velocity)); // max 7 bits
- return (uint8_t)(vv >> 1);
+ const int vv = (const int)sqrtf((float)(volume * velocity) / (127.0f * 127.0f));
+ return (uint8_t)(vv * 127.0f);
}
static void note_off(open_midi_port &port, uint8_t channel, int keynum, int velocity)
@@ -309,7 +374,8 @@ static void note_off(open_midi_port &port, uint8_t channel, int keynum, int velo
psg_set_channel_volume(key.voice, 0);
break;
case midi_playback_device::ym2151:
- // TODO: Implement me.
+ YM_key_on(key.voice, false, false, false, false);
+ Ym_midi_mappings[key.voice].channel = INVALID_MIDI_CHANNEL;
break;
default:
break;
@@ -349,10 +415,27 @@ static void note_on(open_midi_port &port, uint8_t channel, int keynum, int veloc
psg_set_channel_pulse_width(key.voice, (uint8_t)(settings.modulation >> 1));
psg_set_channel_left(key.voice, settings.pan < 96);
psg_set_channel_right(key.voice, settings.pan > 32);
- psg_set_channel_volume(key.voice, settings.use_velocity ? get_velocitated_volume(settings.volume, velocity) : (uint8_t)(settings.volume >> 1));
+ psg_set_channel_volume(key.voice, settings.use_velocity ? get_psg_velocitated_volume(settings.volume, velocity) : (uint8_t)(settings.volume >> 1));
break;
case midi_playback_device::ym2151:
- // TODO: Implement me.
+ if (key.voice == INVALID_VOICE) {
+ key.voice = alloc_ym_voice();
+ }
+ if (key.voice == INVALID_VOICE) {
+ return;
+ }
+ Ym_midi_mappings[key.voice].channel = { port.descriptor, channel };
+ apply_ym_patch(key.voice, settings.device.ym2151.patch_bytes, settings.device.ym2151.patch_size);
+ for (int i = 0; i < 4; ++i) {
+ const uint8_t tl = YM_get_operator_total_level(key.voice, i);
+ const uint8_t vv = settings.use_velocity ? (get_psg_velocitated_volume(settings.volume, velocity) << 1) : (uint8_t)(settings.volume);
+ const uint8_t fv = 127 - (uint8_t)(((uint16_t)vv * (uint16_t)(127 - tl)) >> 7);
+ YM_set_operator_total_level(key.voice, i, fv);
+ }
+ YM_set_voice_key_fraction(key.voice, settings.pitch_bend >> 8);
+ YM_set_voice_octave(key.voice, midi_ym_octave(keynum));
+ YM_set_voice_note(key.voice, midi_ym_note(keynum));
+ YM_key_on(key.voice);
break;
default:
break;
@@ -643,7 +726,12 @@ static void pitch_bend(open_midi_port &port, uint8_t channel, int bend)
}
break;
case midi_playback_device::ym2151:
- // TODO: Implement me.
+ for (int i = 0; i < MAX_MIDI_KEYS; ++i) {
+ const midi_key &key = port.channels[channel].keys_on[i];
+ if (key.voice != INVALID_VOICE) {
+ YM_set_voice_key_fraction(key.voice, bend >> 8);
+ }
+ }
break;
default:
break;
@@ -716,6 +804,9 @@ void midi_init()
for (uint8_t i = 0; i < PSG_NUM_CHANNELS; ++i) {
Psg_midi_mappings[i].channel = INVALID_MIDI_CHANNEL;
}
+ for (uint8_t i = 0; i < MAX_YM2151_VOICES; ++i) {
+ Ym_midi_mappings[i].channel = INVALID_MIDI_CHANNEL;
+ }
}
void midi_process()
@@ -826,7 +917,7 @@ void midi_port_set_channel_playback_device(midi_port_descriptor port, uint8_t ch
auto &[port_number, open_port] = *value;
midi_channel_state &state = open_port.channels[channel];
- midi_key keys_on[MAX_MIDI_KEYS];
+ midi_key keys_on[MAX_MIDI_KEYS];
memcpy(keys_on, state.keys_on, sizeof(midi_key) * MAX_MIDI_KEYS);
for (uint8_t i = 0; i < MAX_MIDI_KEYS; ++i) {
@@ -844,6 +935,11 @@ void midi_port_set_channel_playback_device(midi_port_descriptor port, uint8_t ch
}
}
}
+
+ if (d == midi_playback_device::ym2151) {
+ memcpy(state.settings.device.ym2151.patch_bytes, Ym_default_patch, sizeof(Ym_default_patch));
+ state.settings.device.ym2151.patch_size = sizeof(Ym_default_patch) / sizeof(Ym_default_patch[0]);
+ }
}
}
}
@@ -855,7 +951,7 @@ void midi_port_set_channel_use_velocity(midi_port_descriptor port, uint8_t chann
if (value != Open_midi_ports.end()) {
auto &[port_number, open_port] = *value;
- midi_channel_state &state = open_port.channels[channel];
+ midi_channel_state &state = open_port.channels[channel];
state.settings.use_velocity = use_velocity;
}
}
@@ -876,7 +972,47 @@ void midi_port_set_channel_psg_waveform(midi_port_descriptor port, uint8_t chann
}
}
- state.settings.device.psg.waveform = waveform;
+ state.settings.device.psg.waveform = waveform;
+ }
+ }
+}
+
+void midi_port_set_channel_ym2151_patch_byte(midi_port_descriptor port, uint8_t channel, uint8_t addr, uint8_t data)
+{
+ if (channel < MAX_MIDI_CHANNELS) {
+ auto value = Open_midi_ports.find(port);
+ if (value != Open_midi_ports.end()) {
+ auto &[port_number, open_port] = *value;
+
+ midi_channel_state &state = open_port.channels[channel];
+
+ midi_ym2151_settings &settings = state.settings.device.ym2151;
+ for (int i = 0; i < settings.patch_size; ++i) {
+ if (settings.patch_bytes[i].addr == addr) {
+ settings.patch_bytes[i].value = data;
+ return;
+ }
+ }
+ settings.patch_bytes[settings.patch_size].addr = addr;
+ settings.patch_bytes[settings.patch_size].value = data;
+ ++settings.patch_size;
+ }
+ }
+}
+
+void midi_port_get_channel_ym2151_patch(midi_port_descriptor port, uint8_t channel, uint8_t *bytes)
+{
+ if (channel < MAX_MIDI_CHANNELS) {
+ auto value = Open_midi_ports.find(port);
+ if (value != Open_midi_ports.end()) {
+ auto &[port_number, open_port] = *value;
+
+ midi_channel_state &state = open_port.channels[channel];
+
+ midi_ym2151_settings &settings = state.settings.device.ym2151;
+ for (int i = 0; i < settings.patch_size; ++i) {
+ bytes[settings.patch_bytes[i].addr] = settings.patch_bytes[i].value;
+ }
}
}
}
diff --git a/src/midi.h b/src/midi.h
index 7121289..5ec485a 100644
--- a/src/midi.h
+++ b/src/midi.h
@@ -33,7 +33,14 @@ struct midi_psg_settings {
uint8_t waveform = 0;
};
+struct midi_ym_patch_entry {
+ uint8_t addr;
+ uint8_t value;
+};
+
struct midi_ym2151_settings {
+ midi_ym_patch_entry patch_bytes[256];
+ int patch_size = 0;
};
struct midi_channel_settings {
@@ -73,5 +80,7 @@ void midi_port_set_channel_playback_device(midi_port_descriptor port, uint8_t ch
void midi_port_set_channel_use_velocity(midi_port_descriptor port, uint8_t channel, bool use_velocity);
void midi_port_set_channel_psg_waveform(midi_port_descriptor port, uint8_t channel, uint8_t waveform);
+void midi_port_set_channel_ym2151_patch_byte(midi_port_descriptor port, uint8_t channel, uint8_t addr, uint8_t value);
+void midi_port_get_channel_ym2151_patch(midi_port_descriptor port, uint8_t channel, uint8_t *bytes);
#endif
diff --git a/src/overlay/midi_overlay.cpp b/src/overlay/midi_overlay.cpp
index 31410ec..154b95e 100644
--- a/src/overlay/midi_overlay.cpp
+++ b/src/overlay/midi_overlay.cpp
@@ -4,6 +4,8 @@
#include "midi.h"
#include "vera/vera_psg.h"
+#include "ym2151/ym2151.h"
+#include "ym2151_overlay.h"
void draw_midi_overlay()
{
@@ -61,6 +63,22 @@ void draw_midi_overlay()
if (ImGui::Combo("Waveform", &wf, waveforms, IM_ARRAYSIZE(waveforms))) {
midi_port_set_channel_psg_waveform(port, i, (uint8_t)wf);
}
+ } else if (settings->playback_device == midi_playback_device::ym2151) {
+ uint8_t ym_regs[256];
+ memset(ym_regs, 0, sizeof(ym_regs));
+
+ YM_get_modulation_regs(ym_regs);
+ midi_port_get_channel_ym2151_patch(port, i, ym_regs);
+ if (ImGui::TreeNodeEx("LFO & Noise", ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_DefaultOpen)) {
+ debugger_draw_ym_lfo_and_noise(ym_regs);
+ ImGui::TreePop();
+ }
+ if (ImGui::TreeNodeEx("Channel Data", ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_DefaultOpen)) {
+ static ym_channel_data channel;
+ static ym_keyon_state keyon;
+ debugger_draw_ym_voices(ym_regs, &channel, &keyon, 1, [port, i](uint8_t addr, uint8_t value) { midi_port_set_channel_ym2151_patch_byte(port, i, addr, value); });
+ ImGui::TreePop();
+ }
}
bool velocity = settings->use_velocity;
diff --git a/src/overlay/overlay.cpp b/src/overlay/overlay.cpp
index 4babd26..0d47757 100644
--- a/src/overlay/overlay.cpp
+++ b/src/overlay/overlay.cpp
@@ -3,8 +3,8 @@
#include
#include
-#include
#include
+#include
#include "imgui/imgui.h"
#include "imgui/imgui_impl_sdl.h"
@@ -16,21 +16,22 @@
#include "vram_dump.h"
#include "audio.h"
+#include "bitutils.h"
#include "cpu/fake6502.h"
#include "debugger.h"
#include "display.h"
#include "glue.h"
#include "joystick.h"
#include "keyboard.h"
-#include "options_menu.h"
#include "midi_overlay.h"
+#include "options_menu.h"
#include "smc.h"
#include "symbols.h"
#include "timing.h"
#include "vera/sdcard.h"
#include "vera/vera_psg.h"
#include "vera/vera_video.h"
-#include "ym2151/ym2151.h"
+#include "ym2151_overlay.h"
bool Show_options = false;
bool Show_imgui_demo = false;
@@ -1164,9 +1165,9 @@ static void draw_debugger_vera_psg()
for (unsigned int i = 0; i < 16; ++i) {
ImGui::TableNextRow();
if (i == 0) {
- ImGui::TableSetColumnIndex(2); // freq
+ ImGui::TableSetColumnIndex(2); // freq
ImGui::PushItemWidth(-FLT_MIN); // Right-aligned
- ImGui::TableSetColumnIndex(3); // wave
+ ImGui::TableSetColumnIndex(3); // wave
ImGui::PushItemWidth(-FLT_MIN);
ImGui::TableSetColumnIndex(4); // width
ImGui::PushItemWidth(-FLT_MIN);
@@ -1284,505 +1285,6 @@ static void draw_debugger_vera_psg()
}
}
-static void ym2151_reg_input(uint8_t *regs, uint8_t idx)
-{
- ImGui::TableSetColumnIndex((idx & 0xf) + 1);
- if (ImGui::InputHex(idx, regs[idx])) {
- YM_debug_write(idx, regs[idx]);
- }
-}
-
-template
-constexpr T bit_set_or_res(T val, T mask, bool cond)
-{
- return cond ? (val | mask) : (val & ~mask);
-}
-
-static void draw_debugger_ym2151()
-{
- uint8_t regs[256];
- uint8_t status = YM_read_status();
- for (int i = 0; i < 256; i++) {
- regs[i] = YM_debug_read(i);
- }
- if (ImGui::TreeNodeEx("Interface", ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_DefaultOpen)) {
- uint8_t addr = YM_last_address();
- uint8_t data = YM_last_data();
- if (ImGui::InputHexLabel("Address", addr)) {
- YM_write(0, addr);
- }
- ImGui::SameLine();
- if (ImGui::InputHexLabel("Data", data)) {
- YM_write(1, data);
- }
- ImGui::SameLine();
- ImGui::InputHexLabel("Status", status);
-
- ImGui::TreePop();
- }
- if (ImGui::TreeNodeEx("Raw Bytes", ImGuiTreeNodeFlags_Framed)) {
- if (ImGui::BeginTable("ym raw bytes", 17, ImGuiTableFlags_SizingFixedFit)) {
- static const char *row_txts[16] = {
- "0x", "1x", "2x", "3x", "4x", "5x", "6x", "7x", "8x", "9x", "Ax", "Bx", "Cx", "Dx", "Ex", "Fx"
- };
- ImGui::TableNextRow();
- ImGui::TableSetColumnIndex(0);
- ImGui::Text(row_txts[0]);
- ym2151_reg_input(regs, 0x01); // TEST
- ym2151_reg_input(regs, 0x08); // KEYON
- ym2151_reg_input(regs, 0x0F); // NOISE
- ImGui::TableNextRow();
- ImGui::TableSetColumnIndex(0);
- ImGui::Text(row_txts[1]);
- ym2151_reg_input(regs, 0x10); // CLKA1
- ym2151_reg_input(regs, 0x11); // CLKA2
- ym2151_reg_input(regs, 0x12); // CLKB
- ym2151_reg_input(regs, 0x14); // CONTROL
- ym2151_reg_input(regs, 0x18); // LFRQ
- ym2151_reg_input(regs, 0x19); // PMD/AMD
- ym2151_reg_input(regs, 0x1B); // CT/W
- // no unused registers at this point
- for (int i = 2; i < 16; i++) {
- ImGui::TableNextRow();
- ImGui::TableSetColumnIndex(0);
- ImGui::Text(row_txts[i]);
- for (int j = 0; j < 16; j++) {
- ym2151_reg_input(regs, i * 16 + j);
- }
- }
- ImGui::EndTable();
- }
- ImGui::TreePop();
- }
- if (ImGui::TreeNodeEx("Timer & Control", ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_DefaultOpen)) {
- if (ImGui::BeginTable("ym timer & control", 7)) {
- struct {
- bool en, irq, ovf;
- int reload, cur;
- } timer[2];
- bool csm = regs[0x14] & (1 << 7);
- bool ct1 = regs[0x1B] & (1 << 6);
- bool ct2 = regs[0x1B] & (1 << 7);
-
- ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed);
- ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed);
- ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed);
- ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed);
- ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed);
-
- for (int i = 0; i < 2; i++) {
- const uint8_t EN_MASK = 1 << (i + 0);
- const uint8_t IRQ_MASK = 1 << (i + 2);
- const uint8_t RES_MASK = 1 << (i + 4);
- const uint8_t OVF_MASK = 1 << (i + 0);
- const int TIM_MAX = i ? 255 : 1023;
- auto tim = &timer[i];
- tim->en = regs[0x14] & EN_MASK;
- tim->irq = regs[0x14] & IRQ_MASK;
- tim->ovf = status & OVF_MASK;
- tim->reload = i ? regs[0x12] : regs[0x10] * 4 + (regs[0x11] & 0x03);
- tim->cur = YM_get_timer_counter(i);
-
- ImGui::PushID(i);
- ImGui::TableNextRow();
- ImGui::TableNextColumn();
- ImGui::Text(i ? "Timer B" : "Timer A");
- ImGui::TableNextColumn();
- if (ImGui::Checkbox("Enable", &tim->en)) {
- YM_debug_write(0x14, bit_set_or_res(regs[0x14], EN_MASK, tim->en));
- }
- ImGui::TableNextColumn();
- if (ImGui::Checkbox("IRQ Enable", &tim->irq)) {
- YM_debug_write(0x14, bit_set_or_res(regs[0x14], IRQ_MASK, tim->irq));
- }
- ImGui::TableNextColumn();
- ImGui::Checkbox("Overflow", &tim->ovf);
- ImGui::TableNextColumn();
- if (ImGui::Button("Reset")) {
- YM_debug_write(0x14, regs[0x14] | RES_MASK);
- }
- ImGui::TableNextColumn();
- if (ImGui::SliderInt("Reload", &tim->reload, 0, TIM_MAX)) {
- if (i) {
- // timer b
- YM_debug_write(0x12, tim->reload);
- } else {
- // timer a
- YM_debug_write(0x10, tim->reload >> 2);
- YM_debug_write(0x11, regs[0x11] & ~0x03 | (tim->reload & 0x03));
- }
- }
- ImGui::TableNextColumn();
- char buf[5];
- std::sprintf(buf, "%d", tim->cur);
- ImGui::ProgressBar(tim->cur / (float)TIM_MAX, ImVec2(0, 0), buf);
- ImGui::SameLine();
- ImGui::Text("Counter");
-
- ImGui::PopID();
- }
-
- ImGui::TableNextRow();
- ImGui::TableSetColumnIndex(1);
- if (ImGui::Checkbox("CSM", &csm)) {
- YM_debug_write(0x14, bit_set_or_res(regs[0x14], (uint8_t)(1 << 7), csm));
- }
- ImGui::TableNextColumn();
- if (ImGui::Checkbox("CT1", &ct1)) {
- YM_debug_write(0x1B, bit_set_or_res(regs[0x1B], (uint8_t)(1 << 6), ct1));
- }
- ImGui::TableNextColumn();
- if (ImGui::Checkbox("CT2", &ct2)) {
- YM_debug_write(0x1B, bit_set_or_res(regs[0x1B], (uint8_t)(1 << 7), ct2));
- }
- ImGui::EndTable();
- }
- ImGui::TreePop();
- }
- if (ImGui::TreeNodeEx("LFO & Noise", ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_DefaultOpen)) {
- if (ImGui::BeginTable("ym lfo & noise", 2, ImGuiTableFlags_SizingStretchSame)) {
- static const char *waveforms[] = {
- "Sawtooth",
- "Square",
- "Triangle",
- "Noise"
- };
- const uint8_t LRES_MASK = (1 << 1);
- const uint8_t LW_MASK = 0x03;
- bool lres = regs[0x01] & LRES_MASK;
- int lw = regs[0x1B] & LW_MASK;
- int lfrq = regs[0x18];
- float lcnt = YM_get_LFO_phase();
- int amd = YM_get_AMD();
- int pmd = YM_get_PMD();
-
- ImGui::TableNextRow();
- ImGui::TableNextColumn();
- ImGui::Text("LFO");
- ImGui::SameLine(72);
- if (ImGui::Checkbox("Reset", &lres)) {
- YM_debug_write(0x01, bit_set_or_res(regs[0x01], LRES_MASK, lres));
- }
- ImGui::TableNextColumn();
- if (ImGui::Combo("Waveform", &lw, waveforms, 4)) {
- YM_debug_write(0x1B, regs[0x1B] & ~LW_MASK | lw);
- }
-
- ImGui::TableNextRow();
- ImGui::TableNextColumn();
- if (ImGui::SliderInt("LFO Freq", &lfrq, 0, 255)) {
- YM_debug_write(0x18, lfrq);
- }
- ImGui::TableNextColumn();
- char buf[4];
- std::sprintf(buf, "%d", (int)(lcnt * 256));
- ImGui::ProgressBar(lcnt, ImVec2(0, 0), buf);
- ImGui::SameLine(0, ImGui::GetStyle().ItemInnerSpacing.x);
- ImGui::Text("Cur. Phase");
-
- ImGui::TableNextRow();
- ImGui::TableNextColumn();
- if (ImGui::SliderInt("AMD", &amd, 0, 127)) {
- YM_debug_write(0x19, amd);
- }
- ImGui::TableNextColumn();
- if (ImGui::SliderInt("PMD", &pmd, 0, 127)) {
- YM_debug_write(0x19, pmd | 0x80);
- }
-
- const uint8_t NEN_MASK = (1 << 7);
- const uint8_t NFRQ_MASK = 0x1F;
- bool nen = regs[0x0F] & NEN_MASK;
- int nfrq = regs[0x0F] & NFRQ_MASK;
- ImGui::TableNextRow();
- ImGui::TableNextColumn();
- ImGui::Text("Noise");
- ImGui::SameLine(72);
- if (ImGui::Checkbox("Enable", &nen)) {
- YM_debug_write(0x0F, bit_set_or_res(regs[0x0F], NEN_MASK, nen));
- }
- ImGui::TableNextColumn();
- if (ImGui::SliderInt("Frequency", &nfrq, 31, 0)) {
- YM_debug_write(0x0F, regs[0x0F] & ~NFRQ_MASK | nfrq);
- }
-
- ImGui::EndTable();
- }
- ImGui::TreePop();
- }
- if (ImGui::TreeNodeEx("Channels", ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_DefaultOpen)) {
- if (ImGui::BeginTable("ym channels", 4, ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_BordersInnerH | ImGuiTableFlags_BordersInnerV)) {
- static const uint8_t slot_map[4] = { 0, 16, 8, 24 };
- static struct {
- int con, fb;
- bool l, r;
- float kc;
- int ams, pms;
- bool debug_kon[4] = { 1, 1, 1, 1};
- int dkob_state = 0;
- struct {
- int dt1, dt2, mul;
- int ar, d1r, d1l, d2r, rr, ks;
- int tl;
- bool ame;
- } slot[4];
- } channel[8];
-
- ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed); // chan num
- ImGui::TableSetupColumn("", 0, 0.4f); // chan regs
- ImGui::TableSetupColumn(""); // slot regs
- ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed); // slot con
-
- for (int i = 0; i < 8; i++) {
- const uint8_t confb = 0x20 + i;
- const uint8_t kc = 0x28 + i;
- const uint8_t kf = 0x30 + i;
- const uint8_t amspms = 0x38 + i;
- const uint8_t tmp_kc = regs[kc] & 0x7f;
- auto ch = &channel[i];
-
- ch->l = regs[confb] & (1 << 6);
- ch->r = regs[confb] & (1 << 7);
- ch->con = regs[confb] & 0x07;
- ch->fb = (regs[confb] >> 3) & 0x07;
- ch->kc = (tmp_kc - ((tmp_kc + 1) >> 2)) + (regs[kf] / 256.f);
- ch->ams = regs[amspms] & 0x03;
- ch->pms = (regs[amspms] >> 4) & 0x07;
- int fpkc = (int)(ch->kc * 256);
-
- ImGui::PushID(i);
- ImGui::TableNextRow();
- ImGui::TableNextColumn();
- ImGui::Text("%d", i);
-
- // Channel
- ImGui::TableNextColumn();
- ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2(2, 0));
- ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2, 2));
- ImGui::PushStyleVar(ImGuiStyleVar_GrabMinSize, 8);
- if (ImGui::BeginTable("confb", 4)) {
- ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed);
- ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed);
- ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed);
-
- ImGui::TableNextRow();
- ImGui::TableNextColumn();
- ImGui::SetNextItemWidth(12);
- if (ImGui::Checkbox("L", &ch->l)) {
- YM_debug_write(confb, bit_set_or_res(regs[confb], (uint8_t)(1 << 6), ch->l));
- }
- ImGui::TableNextColumn();
- ImGui::SetNextItemWidth(12);
- if (ImGui::Checkbox("R", &ch->r)) {
- YM_debug_write(confb, bit_set_or_res(regs[confb], (uint8_t)(1 << 7), ch->r));
- }
- ImGui::TableNextColumn();
- ImGui::SetNextItemWidth(12);
- if (ImGui::DragInt("CON", &ch->con, 1, 0, 7)) {
- YM_debug_write(confb, regs[confb] & ~0x07 | ch->con);
- }
- ImGui::TableNextColumn();
- ImGui::SetNextItemWidth(-28);
- if (ImGui::SliderInt("FB", &ch->fb, 0, 7)) {
- YM_debug_write(confb, regs[confb] & ~0x38 | (ch->fb << 3));
- }
- ImGui::EndTable();
- }
-
- if (ImGui::BeginTable("amspms", 2)) {
- ImGui::TableNextRow();
- ImGui::TableNextColumn();
- ImGui::SetNextItemWidth(-28);
- if (ImGui::SliderInt("AMS", &ch->ams, 0, 3)) {
- YM_debug_write(amspms, regs[amspms] & ~0x03 | ch->ams);
- }
- ImGui::TableNextColumn();
- ImGui::SetNextItemWidth(-28);
- if (ImGui::SliderInt("PMS", &ch->pms, 0, 7)) {
- YM_debug_write(amspms, regs[amspms] & ~0x70 | (ch->pms << 4));
- }
- ImGui::EndTable();
- }
-
- const char notes[] = "C-C#D-D#E-F-F#G-G#A-A#B-";
- float cents = (fpkc & 0xFF) * 100.f / 256.f;
- if (cents > 50) {
- cents = cents - 100;
- }
- const uint8_t note = (fpkc >> 8) + (cents < 0) + 1;
- const uint8_t ni = (note % 12) * 2;
- const uint8_t oct = note / 12;
- // C#8 +00.0
- char kcinfo[12];
- std::sprintf(kcinfo, "%c%c%d %+05.1f", notes[ni], notes[ni + 1], oct, cents);
- ImGui::SetNextItemWidth(-28);
- if (ImGui::SliderFloat("KC", &ch->kc, 0, 96, kcinfo, ImGuiSliderFlags_NoRoundToFormat)) {
- fpkc = std::min((int)(ch->kc * 256), (96 * 256) - 1);
- YM_debug_write(kc, (fpkc >> 8) * 4 / 3);
- YM_debug_write(kf, fpkc & 0xFF);
- }
-
- ImGui::Button("KeyOn");
- ch->dkob_state = (ch->dkob_state << 1) | (int)ImGui::IsItemActive();
- switch (ch->dkob_state & 0b11) {
- case 0b01: // keyon checked slots
- YM_debug_write(0x08, i | (ch->debug_kon[0] << 3) | (ch->debug_kon[1] << 4) | (ch->debug_kon[2] << 5) | (ch->debug_kon[3] << 6));
- break;
- case 0b10: // keyoff all slots
- YM_debug_write(0x08, i);
- break;
- }
- ImGui::PushID("konslots");
- for (int j = 0; j < 4; j++) {
- ImGui::PushID(j);
- ImGui::SameLine();
- ImGui::Checkbox("", &ch->debug_kon[j]);
- ImGui::PopID();
- }
- ImGui::PopID();
- ImGui::PopStyleVar(3);
-
- // Slot
- ImGui::TableNextColumn();
- ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2(2, 2));
- ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(4, 0));
- ImGui::PushStyleVar(ImGuiStyleVar_GrabMinSize, 6);
- if (ImGui::BeginTable("slot", 15)) {
- ImGui::TableSetupColumn("Sl", ImGuiTableColumnFlags_WidthFixed);
- ImGui::TableSetupColumn("DT1");
- ImGui::TableSetupColumn("DT2");
- ImGui::TableSetupColumn("MUL");
- ImGui::TableSetupColumn("=Freq", ImGuiTableColumnFlags_WidthFixed, 44);
- ImGui::TableSetupColumn("AR");
- ImGui::TableSetupColumn("D1R");
- ImGui::TableSetupColumn("D1L");
- ImGui::TableSetupColumn("D2R");
- ImGui::TableSetupColumn("RR");
- ImGui::TableSetupColumn("KS");
- ImGui::TableSetupColumn("Env");
- ImGui::TableSetupColumn("TL");
- ImGui::TableSetupColumn("AM", ImGuiTableColumnFlags_WidthFixed);
- ImGui::TableSetupColumn("Out");
- ImGui::TableHeadersRow();
-
- for (int j = 0; j < 4; j++) {
- const int slnum = slot_map[j] + i;
- const uint8_t muldt1 = 0x40 + slnum;
- const uint8_t tl = 0x60 + slnum;
- const uint8_t arks = 0x80 + slnum;
- const uint8_t d1rame = 0xA0 + slnum;
- const uint8_t d2rdt2 = 0xC0 + slnum;
- const uint8_t rrd1l = 0xE0 + slnum;
- auto slot = &(channel[i].slot[j]);
-
- slot->mul = regs[muldt1] & 0x0F;
- slot->dt1 = (regs[muldt1] >> 4) & 0x07;
- slot->tl = regs[tl] & 0x7F;
- slot->ar = regs[arks] & 0x1F;
- slot->ks = regs[arks] >> 6;
- slot->d1r = regs[d1rame] & 0x1F;
- slot->ame = regs[d1rame] & 0x80;
- slot->d2r = regs[d2rdt2] & 0x1F;
- slot->dt2 = regs[d2rdt2] >> 6;
- slot->rr = regs[rrd1l] & 0x0F;
- slot->d1l = regs[rrd1l] >> 4;
-
- ImGui::PushID(j);
- ImGui::TableNextRow();
- ImGui::TableNextColumn();
- ImGui::Text("%d", slot_map[j] + i);
- ImGui::TableNextColumn();
- ImGui::SetNextItemWidth(-FLT_MIN);
- if (ImGui::SliderInt("dt1", &slot->dt1, 0, 7)) {
- YM_debug_write(muldt1, regs[muldt1] & ~0x70 | (slot->dt1 << 4));
- }
- ImGui::TableNextColumn();
- ImGui::SetNextItemWidth(-FLT_MIN);
- if (ImGui::SliderInt("dt2", &slot->dt2, 0, 3)) {
- YM_debug_write(d2rdt2, regs[d2rdt2] & ~0xC0 | (slot->dt2 << 6));
- }
- ImGui::TableNextColumn();
- char buf[11] = ".5";
- if (slot->mul > 0) {
- std::sprintf(buf, "%d", slot->mul);
- }
- ImGui::SetNextItemWidth(-FLT_MIN);
- if (ImGui::SliderInt("mul", &slot->mul, 0, 15, buf)) {
- YM_debug_write(muldt1, regs[muldt1] & ~0x0F | slot->mul);
- }
- ImGui::TableNextColumn();
- ImGui::Text("%d", YM_get_freq(slnum));
- ImGui::TableNextColumn();
- ImGui::SetNextItemWidth(-FLT_MIN);
- if (ImGui::SliderInt("ar", &slot->ar, 31, 0)) {
- YM_debug_write(arks, regs[arks] & ~0x1F | slot->ar);
- }
- ImGui::TableNextColumn();
- ImGui::SetNextItemWidth(-FLT_MIN);
- if (ImGui::SliderInt("d1r", &slot->d1r, 31, 0)) {
- YM_debug_write(d1rame, regs[d1rame] & ~0x1F | slot->d1r);
- }
- ImGui::TableNextColumn();
- ImGui::SetNextItemWidth(-FLT_MIN);
- if (ImGui::SliderInt("d1l", &slot->d1l, 15, 0)) {
- YM_debug_write(rrd1l, regs[rrd1l] & ~0xF0 | (slot->d1l << 4));
- }
- ImGui::TableNextColumn();
- ImGui::SetNextItemWidth(-FLT_MIN);
- if (ImGui::SliderInt("d2r", &slot->d2r, 31, 0)) {
- YM_debug_write(d2rdt2, regs[d2rdt2] & ~0x1F | slot->d2r);
- }
- ImGui::TableNextColumn();
- ImGui::SetNextItemWidth(-FLT_MIN);
- if (ImGui::SliderInt("rr", &slot->rr, 15, 0)) {
- YM_debug_write(rrd1l, regs[rrd1l] & ~0x0F | slot->rr);
- }
- ImGui::TableNextColumn();
- ImGui::SetNextItemWidth(-FLT_MIN);
- if (ImGui::SliderInt("ks", &slot->ks, 0, 3)) {
- YM_debug_write(arks, regs[arks] & ~0xC0 | (slot->ks << 6));
- }
- ImGui::TableNextColumn();
- char envstate_txt[] = " ";
- envstate_txt[0] = " ADSR"[YM_get_env_state(slnum)];
- ImGui::ProgressBar(YM_get_EG_output(slnum), ImVec2(-FLT_MIN, 0), envstate_txt);
- ImGui::TableNextColumn();
- ImGui::SetNextItemWidth(-FLT_MIN);
- if (ImGui::SliderInt("tl", &slot->tl, 127, 0)) {
- YM_debug_write(tl, regs[tl] & ~0x7F | slot->tl);
- }
- ImGui::TableNextColumn();
- ImGui::PushID("amelia");
- if (ImGui::Checkbox("", &slot->ame)) {
- YM_debug_write(d1rame, bit_set_or_res(regs[d1rame], (uint8_t) 0x80, slot->ame));
- }
- ImGui::PopID();
- float out = YM_get_final_env(slnum);
- char buf2[5];
- std::sprintf(buf2, "%d", (int)((1 - out) * 1024));
- ImGui::TableNextColumn();
- ImGui::ProgressBar(out, ImVec2(-FLT_MIN, 0), buf2);
-
- ImGui::PopID();
- }
- ImGui::EndTable();
- }
- ImGui::PopStyleVar(3);
-
- // CON gfx
- ImGui::TableNextColumn();
- ImGui::Dummy(ImVec2(16, 15));
- // this is so ugly
- ImGui::Tile((display_icons)((int)ICON_FM_ALG + ch->con), ImVec2(16, 64));
-
- ImGui::PopID();
- }
- ImGui::EndTable();
- }
- ImGui::TreePop();
- }
-}
-
static void draw_menu_bar()
{
if (ImGui::BeginMainMenuBar()) {
@@ -2066,7 +1568,7 @@ void overlay_draw()
if (ImGui::Begin("MIDI Control", &Show_midi_overlay)) {
draw_midi_overlay();
}
- ImGui::End();
+ ImGui::End();
}
}
diff --git a/src/overlay/ym2151_overlay.cpp b/src/overlay/ym2151_overlay.cpp
new file mode 100644
index 0000000..1991248
--- /dev/null
+++ b/src/overlay/ym2151_overlay.cpp
@@ -0,0 +1,517 @@
+#include "ym2151_overlay.h"
+
+#include "bitutils.h"
+#include "display.h"
+#include "imgui/imgui.h"
+#include "util.h"
+#include "ym2151/ym2151.h"
+
+static void ym2151_reg_input(uint8_t *regs, uint8_t idx)
+{
+ ImGui::TableSetColumnIndex((idx & 0xf) + 1);
+ if (ImGui::InputHex(idx, regs[idx])) {
+ YM_debug_write(idx, regs[idx]);
+ }
+}
+
+void draw_debugger_ym2151()
+{
+ uint8_t regs[256];
+ uint8_t status = YM_read_status();
+ for (int i = 0; i < 256; i++) {
+ regs[i] = YM_debug_read(i);
+ }
+ if (ImGui::TreeNodeEx("Interface", ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_DefaultOpen)) {
+ uint8_t addr = YM_last_address();
+ uint8_t data = YM_last_data();
+ if (ImGui::InputHexLabel("Address", addr)) {
+ YM_write(0, addr);
+ }
+ ImGui::SameLine();
+ if (ImGui::InputHexLabel("Data", data)) {
+ YM_write(1, data);
+ }
+ ImGui::SameLine();
+ ImGui::InputHexLabel("Status", status);
+
+ ImGui::TreePop();
+ }
+ if (ImGui::TreeNodeEx("Raw Bytes", ImGuiTreeNodeFlags_Framed)) {
+ if (ImGui::BeginTable("ym raw bytes", 17, ImGuiTableFlags_SizingFixedFit)) {
+ static const char *row_txts[16] = {
+ "0x", "1x", "2x", "3x", "4x", "5x", "6x", "7x", "8x", "9x", "Ax", "Bx", "Cx", "Dx", "Ex", "Fx"
+ };
+ ImGui::TableNextRow();
+ ImGui::TableSetColumnIndex(0);
+ ImGui::Text(row_txts[0]);
+ ym2151_reg_input(regs, 0x01); // TEST
+ ym2151_reg_input(regs, 0x08); // KEYON
+ ym2151_reg_input(regs, 0x0F); // NOISE
+ ImGui::TableNextRow();
+ ImGui::TableSetColumnIndex(0);
+ ImGui::Text(row_txts[1]);
+ ym2151_reg_input(regs, 0x10); // CLKA1
+ ym2151_reg_input(regs, 0x11); // CLKA2
+ ym2151_reg_input(regs, 0x12); // CLKB
+ ym2151_reg_input(regs, 0x14); // CONTROL
+ ym2151_reg_input(regs, 0x18); // LFRQ
+ ym2151_reg_input(regs, 0x19); // PMD/AMD
+ ym2151_reg_input(regs, 0x1B); // CT/W
+ // no unused registers at this point
+ for (int i = 2; i < 16; i++) {
+ ImGui::TableNextRow();
+ ImGui::TableSetColumnIndex(0);
+ ImGui::Text(row_txts[i]);
+ for (int j = 0; j < 16; j++) {
+ ym2151_reg_input(regs, i * 16 + j);
+ }
+ }
+ ImGui::EndTable();
+ }
+ ImGui::TreePop();
+ }
+ if (ImGui::TreeNodeEx("Timer & Control", ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_DefaultOpen)) {
+ if (ImGui::BeginTable("ym timer & control", 7)) {
+ struct {
+ bool en, irq, ovf;
+ int reload, cur;
+ } timer[2];
+ bool csm = regs[0x14] & (1 << 7);
+ bool ct1 = regs[0x1B] & (1 << 6);
+ bool ct2 = regs[0x1B] & (1 << 7);
+
+ ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed);
+ ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed);
+ ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed);
+ ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed);
+ ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed);
+
+ for (int i = 0; i < 2; i++) {
+ const uint8_t EN_MASK = 1 << (i + 0);
+ const uint8_t IRQ_MASK = 1 << (i + 2);
+ const uint8_t RES_MASK = 1 << (i + 4);
+ const uint8_t OVF_MASK = 1 << (i + 0);
+ const int TIM_MAX = i ? 255 : 1023;
+ auto tim = &timer[i];
+ tim->en = regs[0x14] & EN_MASK;
+ tim->irq = regs[0x14] & IRQ_MASK;
+ tim->ovf = status & OVF_MASK;
+ tim->reload = i ? regs[0x12] : regs[0x10] * 4 + (regs[0x11] & 0x03);
+ tim->cur = YM_get_timer_counter(i);
+
+ ImGui::PushID(i);
+ ImGui::TableNextRow();
+ ImGui::TableNextColumn();
+ ImGui::Text(i ? "Timer B" : "Timer A");
+ ImGui::TableNextColumn();
+ if (ImGui::Checkbox("Enable", &tim->en)) {
+ YM_debug_write(0x14, bit_set_or_res(regs[0x14], EN_MASK, tim->en));
+ }
+ ImGui::TableNextColumn();
+ if (ImGui::Checkbox("IRQ Enable", &tim->irq)) {
+ YM_debug_write(0x14, bit_set_or_res(regs[0x14], IRQ_MASK, tim->irq));
+ }
+ ImGui::TableNextColumn();
+ ImGui::Checkbox("Overflow", &tim->ovf);
+ ImGui::TableNextColumn();
+ if (ImGui::Button("Reset")) {
+ YM_debug_write(0x14, regs[0x14] | RES_MASK);
+ }
+ ImGui::TableNextColumn();
+ if (ImGui::SliderInt("Reload", &tim->reload, 0, TIM_MAX)) {
+ if (i) {
+ // timer b
+ YM_debug_write(0x12, tim->reload);
+ } else {
+ // timer a
+ YM_debug_write(0x10, tim->reload >> 2);
+ YM_debug_write(0x11, regs[0x11] & ~0x03 | (tim->reload & 0x03));
+ }
+ }
+ ImGui::TableNextColumn();
+ char buf[5];
+ std::sprintf(buf, "%d", tim->cur);
+ ImGui::ProgressBar(tim->cur / (float)TIM_MAX, ImVec2(0, 0), buf);
+ ImGui::SameLine();
+ ImGui::Text("Counter");
+
+ ImGui::PopID();
+ }
+
+ ImGui::TableNextRow();
+ ImGui::TableSetColumnIndex(1);
+ if (ImGui::Checkbox("CSM", &csm)) {
+ YM_debug_write(0x14, bit_set_or_res(regs[0x14], (uint8_t)(1 << 7), csm));
+ }
+ ImGui::TableNextColumn();
+ if (ImGui::Checkbox("CT1", &ct1)) {
+ YM_debug_write(0x1B, bit_set_or_res(regs[0x1B], (uint8_t)(1 << 6), ct1));
+ }
+ ImGui::TableNextColumn();
+ if (ImGui::Checkbox("CT2", &ct2)) {
+ YM_debug_write(0x1B, bit_set_or_res(regs[0x1B], (uint8_t)(1 << 7), ct2));
+ }
+ ImGui::EndTable();
+ }
+ ImGui::TreePop();
+ }
+ if (ImGui::TreeNodeEx("LFO & Noise", ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_DefaultOpen)) {
+ debugger_draw_ym_lfo_and_noise(regs);
+ ImGui::TreePop();
+ }
+
+ static ym_channel_data channel[8];
+ static ym_keyon_state keyon[8];
+
+ if (ImGui::TreeNodeEx("Channels", ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_DefaultOpen)) {
+ debugger_draw_ym_voices(regs, channel, keyon, IM_ARRAYSIZE(channel), [](uint8_t addr, uint8_t value) { YM_debug_write(addr, value); });
+ ImGui::TreePop();
+ }
+}
+
+void debugger_draw_ym_lfo_and_noise(uint8_t *regs)
+{
+ if (ImGui::BeginTable("ym lfo & noise", 2, ImGuiTableFlags_SizingStretchSame)) {
+ static const char *waveforms[] = {
+ "Sawtooth",
+ "Square",
+ "Triangle",
+ "Noise"
+ };
+ const uint8_t LRES_MASK = (1 << 1);
+ const uint8_t LW_MASK = 0x03;
+ bool lres = regs[0x01] & LRES_MASK;
+ int lw = regs[0x1B] & LW_MASK;
+ int lfrq = regs[0x18];
+
+ ym_modulation_state mod_data;
+ YM_get_modulation_state(mod_data);
+
+ float lcnt = mod_data.LFO_phase;
+ int amd = mod_data.amplitude_modulation;
+ int pmd = mod_data.phase_modulation;
+
+ ImGui::TableNextRow();
+ ImGui::TableNextColumn();
+ ImGui::BeginGroup();
+ ImGui::Text("LFO");
+ ImGui::SameLine(72);
+ if (ImGui::Checkbox("Reset", &lres)) {
+ YM_debug_write(0x01, bit_set_or_res(regs[0x01], LRES_MASK, lres));
+ }
+ ImGui::EndGroup();
+ ImGui::TableNextColumn();
+ if (ImGui::Combo("Waveform", &lw, waveforms, 4)) {
+ YM_debug_write(0x1B, regs[0x1B] & ~LW_MASK | lw);
+ }
+
+ ImGui::TableNextRow();
+ ImGui::TableNextColumn();
+ if (ImGui::SliderInt("LFO Freq", &lfrq, 0, 255)) {
+ YM_debug_write(0x18, lfrq);
+ }
+ ImGui::TableNextColumn();
+ char buf[4];
+ std::sprintf(buf, "%d", (int)(lcnt * 256));
+ ImGui::ProgressBar(lcnt, ImVec2(0, 0), buf);
+ ImGui::SameLine(0, ImGui::GetStyle().ItemInnerSpacing.x);
+ ImGui::Text("Cur. Phase");
+
+ ImGui::TableNextRow();
+ ImGui::TableNextColumn();
+ if (ImGui::SliderInt("AMD", &amd, 0, 127)) {
+ YM_debug_write(0x19, amd);
+ }
+ ImGui::TableNextColumn();
+ if (ImGui::SliderInt("PMD", &pmd, 0, 127)) {
+ YM_debug_write(0x19, pmd | 0x80);
+ }
+
+ const uint8_t NEN_MASK = (1 << 7);
+ const uint8_t NFRQ_MASK = 0x1F;
+ bool nen = regs[0x0F] & NEN_MASK;
+ int nfrq = regs[0x0F] & NFRQ_MASK;
+ ImGui::TableNextRow();
+ ImGui::TableNextColumn();
+ ImGui::BeginGroup();
+ ImGui::Text("Noise");
+ ImGui::SameLine(72);
+ if (ImGui::Checkbox("Enable", &nen)) {
+ YM_debug_write(0x0F, bit_set_or_res(regs[0x0F], NEN_MASK, nen));
+ }
+ ImGui::EndGroup();
+ ImGui::TableNextColumn();
+ if (ImGui::SliderInt("Frequency", &nfrq, 31, 0)) {
+ YM_debug_write(0x0F, regs[0x0F] & ~NFRQ_MASK | nfrq);
+ }
+
+ ImGui::EndTable();
+ }
+}
+
+void debugger_draw_ym_voices(uint8_t *regs, ym_channel_data *channel, ym_keyon_state *keyons, int num_channels, std::function apply_byte)
+{
+ if (ImGui::BeginTable("ym channels", 4, ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_BordersInnerH | ImGuiTableFlags_BordersInnerV)) {
+ ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed); // chan num
+ ImGui::TableSetupColumn("", 0, 0.4f); // chan regs
+ ImGui::TableSetupColumn(""); // slot regs
+ ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed); // slot con
+
+ for (int i = 0; i < num_channels; i++) {
+ ImGui::PushID(i);
+ ImGui::TableNextRow();
+ ImGui::TableNextColumn();
+ ImGui::Text("%d", i);
+
+ debugger_draw_ym_voice(i, regs, channel[i], keyons ? &keyons[i] : nullptr, apply_byte);
+
+ ImGui::PopID();
+ }
+ ImGui::EndTable();
+ }
+}
+
+void debugger_draw_ym_voice(int i, uint8_t *regs, ym_channel_data &ch, ym_keyon_state *keyon, std::function apply_byte)
+{
+ static const uint8_t slot_map[4] = { 0, 16, 8, 24 };
+
+ const uint8_t confb = 0x20 + i;
+ const uint8_t kc = 0x28 + i;
+ const uint8_t kf = 0x30 + i;
+ const uint8_t amspms = 0x38 + i;
+ const uint8_t tmp_kc = regs[kc] & 0x7f;
+
+ ch.l = regs[confb] & (1 << 6);
+ ch.r = regs[confb] & (1 << 7);
+ ch.con = regs[confb] & 0x07;
+ ch.fb = (regs[confb] >> 3) & 0x07;
+ ch.kc = (tmp_kc - ((tmp_kc + 1) >> 2)) + (regs[kf] / 256.f);
+ ch.ams = regs[amspms] & 0x03;
+ ch.pms = (regs[amspms] >> 4) & 0x07;
+ int fpkc = (int)(ch.kc * 256);
+
+ // Channel
+ ImGui::TableNextColumn();
+ ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2(2, 0));
+ ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2, 2));
+ ImGui::PushStyleVar(ImGuiStyleVar_GrabMinSize, 8);
+ if (ImGui::BeginTable("confb", 4)) {
+ ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed);
+ ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed);
+ ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed);
+
+ ImGui::TableNextRow();
+ ImGui::TableNextColumn();
+ ImGui::SetNextItemWidth(12);
+ if (ImGui::Checkbox("L", &ch.l)) {
+ apply_byte(confb, bit_set_or_res(regs[confb], (uint8_t)(1 << 6), ch.l));
+ }
+ ImGui::TableNextColumn();
+ ImGui::SetNextItemWidth(12);
+ if (ImGui::Checkbox("R", &ch.r)) {
+ apply_byte(confb, bit_set_or_res(regs[confb], (uint8_t)(1 << 7), ch.r));
+ }
+ ImGui::TableNextColumn();
+ ImGui::SetNextItemWidth(12);
+ if (ImGui::DragInt("CON", &ch.con, 1, 0, 7)) {
+ apply_byte(confb, regs[confb] & ~0x07 | ch.con);
+ }
+ ImGui::TableNextColumn();
+ ImGui::SetNextItemWidth(-28);
+ if (ImGui::SliderInt("FB", &ch.fb, 0, 7)) {
+ apply_byte(confb, regs[confb] & ~0x38 | (ch.fb << 3));
+ }
+ ImGui::EndTable();
+ }
+
+ if (ImGui::BeginTable("amspms", 2)) {
+ ImGui::TableNextRow();
+ ImGui::TableNextColumn();
+ ImGui::SetNextItemWidth(-28);
+ if (ImGui::SliderInt("AMS", &ch.ams, 0, 3)) {
+ apply_byte(amspms, regs[amspms] & ~0x03 | ch.ams);
+ }
+ ImGui::TableNextColumn();
+ ImGui::SetNextItemWidth(-28);
+ if (ImGui::SliderInt("PMS", &ch.pms, 0, 7)) {
+ apply_byte(amspms, regs[amspms] & ~0x70 | (ch.pms << 4));
+ }
+ ImGui::EndTable();
+ }
+
+ if (keyon) {
+ const char notes[] = "C-C#D-D#E-F-F#G-G#A-A#B-";
+ float cents = (fpkc & 0xFF) * 100.f / 256.f;
+ if (cents > 50) {
+ cents = cents - 100;
+ }
+ const uint8_t note = (fpkc >> 8) + (cents < 0) + 1;
+ const uint8_t ni = (note % 12) * 2;
+ const uint8_t oct = note / 12;
+ // C#8 +00.0
+ char kcinfo[12];
+ std::sprintf(kcinfo, "%c%c%d %+05.1f", notes[ni], notes[ni + 1], oct, cents);
+ ImGui::SetNextItemWidth(-28);
+ if (ImGui::SliderFloat("KC", &ch.kc, 0, 96, kcinfo, ImGuiSliderFlags_NoRoundToFormat)) {
+ fpkc = std::min((int)(ch.kc * 256), (96 * 256) - 1);
+ apply_byte(kc, (fpkc >> 8) * 4 / 3);
+ apply_byte(kf, fpkc & 0xFF);
+ }
+
+ ImGui::Button("KeyOn");
+ keyon->dkob_state = (keyon->dkob_state << 1) | (int)ImGui::IsItemActive();
+ switch (keyon->dkob_state & 0b11) {
+ case 0b01: // keyon checked slots
+ apply_byte(0x08, i | (keyon->debug_kon[0] << 3) | (keyon->debug_kon[1] << 4) | (keyon->debug_kon[2] << 5) | (keyon->debug_kon[3] << 6));
+ break;
+ case 0b10: // keyoff all slots
+ apply_byte(0x08, i);
+ break;
+ }
+ ImGui::PushID("konslots");
+ for (int j = 0; j < 4; j++) {
+ ImGui::PushID(j);
+ ImGui::SameLine();
+ ImGui::Checkbox("", &keyon->debug_kon[j]);
+ ImGui::PopID();
+ }
+ ImGui::PopID();
+ }
+ ImGui::PopStyleVar(3);
+
+ // Slot
+ ImGui::TableNextColumn();
+ ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2(2, 2));
+ ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(4, 0));
+ ImGui::PushStyleVar(ImGuiStyleVar_GrabMinSize, 6);
+ if (ImGui::BeginTable("slot", 15)) {
+ ImGui::TableSetupColumn("Sl", ImGuiTableColumnFlags_WidthFixed);
+ ImGui::TableSetupColumn("DT1");
+ ImGui::TableSetupColumn("DT2");
+ ImGui::TableSetupColumn("MUL");
+ ImGui::TableSetupColumn("=Freq", ImGuiTableColumnFlags_WidthFixed, 44);
+ ImGui::TableSetupColumn("AR");
+ ImGui::TableSetupColumn("D1R");
+ ImGui::TableSetupColumn("D1L");
+ ImGui::TableSetupColumn("D2R");
+ ImGui::TableSetupColumn("RR");
+ ImGui::TableSetupColumn("KS");
+ ImGui::TableSetupColumn("Env");
+ ImGui::TableSetupColumn("TL");
+ ImGui::TableSetupColumn("AM", ImGuiTableColumnFlags_WidthFixed);
+ ImGui::TableSetupColumn("Out");
+ ImGui::TableHeadersRow();
+
+ for (int j = 0; j < 4; j++) {
+ const int slnum = slot_map[j] + i;
+ const uint8_t muldt1 = 0x40 + slnum;
+ const uint8_t tl = 0x60 + slnum;
+ const uint8_t arks = 0x80 + slnum;
+ const uint8_t d1rame = 0xA0 + slnum;
+ const uint8_t d2rdt2 = 0xC0 + slnum;
+ const uint8_t rrd1l = 0xE0 + slnum;
+ auto & slot = ch.slot[j];
+
+ slot.mul = regs[muldt1] & 0x0F;
+ slot.dt1 = (regs[muldt1] >> 4) & 0x07;
+ slot.tl = regs[tl] & 0x7F;
+ slot.ar = regs[arks] & 0x1F;
+ slot.ks = regs[arks] >> 6;
+ slot.d1r = regs[d1rame] & 0x1F;
+ slot.ame = regs[d1rame] & 0x80;
+ slot.d2r = regs[d2rdt2] & 0x1F;
+ slot.dt2 = regs[d2rdt2] >> 6;
+ slot.rr = regs[rrd1l] & 0x0F;
+ slot.d1l = regs[rrd1l] >> 4;
+
+ ym_slot_state slot_state;
+ YM_get_slot_state(slnum, slot_state);
+
+ ImGui::PushID(j);
+ ImGui::TableNextRow();
+ ImGui::TableNextColumn();
+ ImGui::Text("%d", slot_map[j] + i);
+ ImGui::TableNextColumn();
+ ImGui::SetNextItemWidth(-FLT_MIN);
+ if (ImGui::SliderInt("dt1", &slot.dt1, 0, 7)) {
+ apply_byte(muldt1, regs[muldt1] & ~0x70 | (slot.dt1 << 4));
+ }
+ ImGui::TableNextColumn();
+ ImGui::SetNextItemWidth(-FLT_MIN);
+ if (ImGui::SliderInt("dt2", &slot.dt2, 0, 3)) {
+ apply_byte(d2rdt2, regs[d2rdt2] & ~0xC0 | (slot.dt2 << 6));
+ }
+ ImGui::TableNextColumn();
+ char buf[11] = ".5";
+ if (slot.mul > 0) {
+ std::sprintf(buf, "%d", slot.mul);
+ }
+ ImGui::SetNextItemWidth(-FLT_MIN);
+ if (ImGui::SliderInt("mul", &slot.mul, 0, 15, buf)) {
+ apply_byte(muldt1, regs[muldt1] & ~0x0F | slot.mul);
+ }
+ ImGui::TableNextColumn();
+ ImGui::Text("%d", slot_state.frequency);
+ ImGui::TableNextColumn();
+ ImGui::SetNextItemWidth(-FLT_MIN);
+ if (ImGui::SliderInt("ar", &slot.ar, 31, 0)) {
+ apply_byte(arks, regs[arks] & ~0x1F | slot.ar);
+ }
+ ImGui::TableNextColumn();
+ ImGui::SetNextItemWidth(-FLT_MIN);
+ if (ImGui::SliderInt("d1r", &slot.d1r, 31, 0)) {
+ apply_byte(d1rame, regs[d1rame] & ~0x1F | slot.d1r);
+ }
+ ImGui::TableNextColumn();
+ ImGui::SetNextItemWidth(-FLT_MIN);
+ if (ImGui::SliderInt("d1l", &slot.d1l, 15, 0)) {
+ apply_byte(rrd1l, regs[rrd1l] & ~0xF0 | (slot.d1l << 4));
+ }
+ ImGui::TableNextColumn();
+ ImGui::SetNextItemWidth(-FLT_MIN);
+ if (ImGui::SliderInt("d2r", &slot.d2r, 31, 0)) {
+ apply_byte(d2rdt2, regs[d2rdt2] & ~0x1F | slot.d2r);
+ }
+ ImGui::TableNextColumn();
+ ImGui::SetNextItemWidth(-FLT_MIN);
+ if (ImGui::SliderInt("rr", &slot.rr, 15, 0)) {
+ apply_byte(rrd1l, regs[rrd1l] & ~0x0F | slot.rr);
+ }
+ ImGui::TableNextColumn();
+ ImGui::SetNextItemWidth(-FLT_MIN);
+ if (ImGui::SliderInt("ks", &slot.ks, 0, 3)) {
+ apply_byte(arks, regs[arks] & ~0xC0 | (slot.ks << 6));
+ }
+ ImGui::TableNextColumn();
+ char envstate_txt[] = " ";
+ envstate_txt[0] = " ADSR"[slot_state.env_state];
+ ImGui::ProgressBar(slot_state.eg_output, ImVec2(-FLT_MIN, 0), envstate_txt);
+ ImGui::TableNextColumn();
+ ImGui::SetNextItemWidth(-FLT_MIN);
+ if (ImGui::SliderInt("tl", &slot.tl, 127, 0)) {
+ apply_byte(tl, regs[tl] & ~0x7F | slot.tl);
+ }
+ ImGui::TableNextColumn();
+ ImGui::PushID("amelia");
+ if (ImGui::Checkbox("", &slot.ame)) {
+ apply_byte(d1rame, bit_set_or_res(regs[d1rame], (uint8_t)0x80, slot.ame));
+ }
+ ImGui::PopID();
+ float out = slot_state.final_env;
+ char buf2[5];
+ std::sprintf(buf2, "%d", (int)((1 - out) * 1024));
+ ImGui::TableNextColumn();
+ ImGui::ProgressBar(out, ImVec2(-FLT_MIN, 0), buf2);
+
+ ImGui::PopID();
+ }
+ ImGui::EndTable();
+ }
+ ImGui::PopStyleVar(3);
+
+ // CON gfx
+ ImGui::TableNextColumn();
+ ImGui::Dummy(ImVec2(16, 15));
+ // this is so ugly
+ ImGui::Tile((display_icons)((int)ICON_FM_ALG + ch.con), ImVec2(16, 64));
+}
diff --git a/src/overlay/ym2151_overlay.h b/src/overlay/ym2151_overlay.h
new file mode 100644
index 0000000..da8660c
--- /dev/null
+++ b/src/overlay/ym2151_overlay.h
@@ -0,0 +1,36 @@
+#pragma once
+#if !defined(YM2151_OVERLAY_H)
+#define YM2151_OVERLAY_H
+
+#include
+
+struct ym_slot_data {
+ int dt1, dt2, mul;
+ int ar, d1r, d1l, d2r, rr, ks;
+ int tl;
+ bool ame;
+};
+
+struct ym_keyon_state {
+ bool debug_kon[4] = { 1, 1, 1, 1 };
+ int dkob_state = 0;
+};
+
+struct ym_channel_data {
+ int con, fb;
+ bool l, r;
+ float kc;
+ int ams, pms;
+
+ ym_slot_data slot[4];
+};
+
+void draw_debugger_ym2151();
+
+void debugger_draw_ym_voice(int i, uint8_t *regs, ym_channel_data &channel, ym_keyon_state *keyon, std::function apply_byte);
+void debugger_draw_ym_voices(uint8_t *regs, ym_channel_data *channels, ym_keyon_state *keyons, int num_channels, std::function apply_byte);
+void debugger_draw_ym_lfo_and_noise(uint8_t *regs);
+
+
+
+#endif
diff --git a/src/ym2151/ym2151.cpp b/src/ym2151/ym2151.cpp
index 425be97..adc8b1b 100644
--- a/src/ym2151/ym2151.cpp
+++ b/src/ym2151/ym2151.cpp
@@ -1,7 +1,11 @@
#include "ym2151.h"
#include "ymfm_opm.h"
+
+#include "ymfm_fm.ipp"
+
#include "audio.h"
+#include "bitutils.h"
class ym2151_interface : public ymfm::ymfm_interface
{
@@ -70,9 +74,10 @@ class ym2151_interface : public ymfm::ymfm_interface
}
}
- void write(uint8_t offset, uint8_t value)
+ void write(uint8_t addr, uint8_t value)
{
- m_chip.write(offset, value);
+ m_chip.write_address(addr);
+ m_chip.write_data(value, false);
}
void debug_write(uint8_t addr, uint8_t value)
@@ -126,11 +131,11 @@ class ym2151_interface : public ymfm::ymfm_interface
uint8_t get_env_state(uint8_t slnum)
{
switch (m_chip.get_debug_op(slnum)->debug_eg_state()) {
- case ymfm::EG_ATTACK: return 1;
- case ymfm::EG_DECAY: return 2;
+ case ymfm::EG_ATTACK: return 1;
+ case ymfm::EG_DECAY: return 2;
case ymfm::EG_SUSTAIN: return 3;
case ymfm::EG_RELEASE: return 4;
- default: return 0;
+ default: return 0;
}
}
@@ -149,9 +154,10 @@ class ym2151_interface : public ymfm::ymfm_interface
uint32_t m_chip_sample_rate;
};
-ym2151_interface Ym_interface;
-uint8_t last_address;
-uint8_t last_data;
+static ym2151_interface Ym_interface;
+static uint8_t Last_address = 0;
+static uint8_t Last_data = 0;
+static uint8_t Ym_registers[256];
void YM_render(int16_t *stream, uint32_t samples, uint32_t buffer_sample_rate)
{
@@ -160,79 +166,500 @@ void YM_render(int16_t *stream, uint32_t samples, uint32_t buffer_sample_rate)
void YM_write(uint8_t offset, uint8_t value)
{
- audio_lock_scope lock;
// save the hassle to add interface to dig into opm_registers by caching the writes here
if (offset & 1) { // data port
- last_data = value;
+ Last_data = value;
+ Ym_registers[Last_address] = Last_data;
+
+ audio_lock_scope lock;
+ Ym_interface.write(Last_address, Last_data);
} else { // address port
- last_address = value;
+ Last_address = value;
}
- Ym_interface.write(offset, value);
+}
+
+uint8_t YM_read_status()
+{
+ audio_lock_scope lock;
+ return Ym_interface.read_status();
}
void YM_debug_write(uint8_t addr, uint8_t value)
{
+ Ym_registers[addr] = value;
+
audio_lock_scope lock;
Ym_interface.debug_write(addr, value);
}
uint8_t YM_debug_read(uint8_t addr)
{
- return Ym_interface.debug_read(addr);
+ return Ym_registers[addr];
}
-uint8_t YM_read_status()
+uint8_t YM_last_address()
+{
+ return Last_address;
+}
+
+uint8_t YM_last_data()
+{
+ return Last_data;
+}
+
+void YM_get_modulation_regs(uint8_t *regs)
+{
+ regs[0x01] = Ym_registers[0x01];
+ regs[0x0F] = Ym_registers[0x0F];
+ regs[0x18] = Ym_registers[0x18];
+ regs[0x19] = Ym_registers[0x19];
+ regs[0x1B] = Ym_registers[0x1B];
+}
+
+void YM_get_voice_regs(uint8_t voice, uint8_t *regs)
+{
+ regs[YM_R_L_FB_CONN_OFFSET + voice] = Ym_registers[YM_R_L_FB_CONN_OFFSET + voice];
+ regs[YM_KC_OFFSET + voice] = Ym_registers[YM_KC_OFFSET + voice];
+ regs[YM_KF_OFFSET + voice] = Ym_registers[YM_KF_OFFSET + voice];
+ regs[YM_PMS_AMS_OFFSET + voice] = Ym_registers[YM_PMS_AMS_OFFSET + voice];
+}
+
+void YM_get_slot_regs(uint8_t voice, uint8_t slot, uint8_t *regs)
+{
+ regs[YM_DT1_MUL_OFFSET + (slot * 8) + voice] = Ym_registers[YM_DT1_MUL_OFFSET + (slot * 8) + voice];
+ regs[YM_TL_OFFSET + (slot * 8) + voice] = Ym_registers[YM_TL_OFFSET + (slot * 8) + voice];
+ regs[YM_KS_AR_OFFSET + (slot * 8) + voice] = Ym_registers[YM_KS_AR_OFFSET + (slot * 8) + voice];
+ regs[YM_A_D1R_OFFSET + (slot * 8) + voice] = Ym_registers[YM_A_D1R_OFFSET + (slot * 8) + voice];
+ regs[YM_DT2_D2R_OFFSET + (slot * 8) + voice] = Ym_registers[YM_DT2_D2R_OFFSET + (slot * 8) + voice];
+ regs[YM_D1L_RR_OFFSET + (slot * 8) + voice] = Ym_registers[YM_D1L_RR_OFFSET + (slot * 8) + voice];
+}
+
+void YM_get_modulation_state(ym_modulation_state &data)
{
audio_lock_scope lock;
- return Ym_interface.read_status();
+ data.amplitude_modulation = Ym_interface.get_AMD();
+ data.phase_modulation = Ym_interface.get_PMD();
+ data.LFO_phase = (Ym_interface.get_LFO_phase() & ((1 << 30) - 1)) / (float)(1 << 30);
}
-uint8_t YM_last_address()
+void YM_get_slot_state(uint8_t slnum, ym_slot_state &data)
{
- return last_address;
+ audio_lock_scope lock;
+ data.frequency = Ym_interface.get_freq(slnum);
+ data.eg_output = (1024 - Ym_interface.get_EG_output(slnum)) / 1024.f;
+ data.final_env = (1024 - Ym_interface.get_final_env(slnum)) / 1024.f;
+ data.env_state = Ym_interface.get_env_state(slnum);
}
-uint8_t YM_last_data()
+uint16_t YM_get_timer_counter(uint8_t tnum)
{
- return last_data;
+ audio_lock_scope lock;
+ return Ym_interface.get_timer_counter(tnum);
}
-uint8_t YM_get_AMD()
+//
+// Field Accessors
+//
+
+uint8_t YM_get_last_key_on()
{
- return Ym_interface.get_AMD();
+ return Ym_registers[0x08];
}
-uint8_t YM_get_PMD()
+uint8_t YM_get_lfo_frequency()
{
- return Ym_interface.get_PMD();
+ return Ym_registers[0x18];
}
-float YM_get_LFO_phase()
+uint8_t YM_get_modulation_depth()
{
- return (Ym_interface.get_LFO_phase() & ((1 << 30) - 1)) / (float) (1 << 30);
+ return get_bit_field<6, 0>(Ym_registers[0x19]);
}
-uint32_t YM_get_freq(uint8_t slnum)
+uint8_t YM_get_modulation_type()
{
- return Ym_interface.get_freq(slnum);
+ return get_bit_field<7>(Ym_registers[0x19]);
}
-float YM_get_EG_output(uint8_t slnum)
+uint8_t YM_get_waveform()
{
- return (1024 - Ym_interface.get_EG_output(slnum)) / 1024.f;
+ return get_bit_field<1, 0>(Ym_registers[0x1b]);
}
-float YM_get_final_env(uint8_t slnum)
+uint8_t YM_get_control_output_1()
{
- return (1024 - Ym_interface.get_final_env(slnum)) / 1024.f;
+ return get_bit_field<6>(Ym_registers[0x1b]);
}
-uint8_t YM_get_env_state(uint8_t slnum)
+uint8_t YM_get_control_output_2()
{
- return Ym_interface.get_env_state(slnum);
+ return get_bit_field<7>(Ym_registers[0x1b]);
}
-uint16_t YM_get_timer_counter(uint8_t tnum)
+uint8_t YM_get_voice_connection_type(uint8_t voice)
{
- return Ym_interface.get_timer_counter(tnum);
+ if (voice < 8) {
+ return get_bit_field<2, 0>(Ym_registers[YM_R_L_FB_CONN_OFFSET + voice]);
+ }
+
+ return 0;
+}
+
+uint8_t YM_get_voice_self_feedback_level(uint8_t voice)
+{
+ if (voice < 8) {
+ return get_bit_field<5, 3>(Ym_registers[YM_R_L_FB_CONN_OFFSET + voice]);
+ }
+
+ return 0;
+}
+
+uint8_t YM_get_voice_left_enable(uint8_t voice)
+{
+ if (voice < 8) {
+ return get_bit_field<6>(Ym_registers[YM_R_L_FB_CONN_OFFSET + voice]);
+ }
+
+ return 0;
+}
+
+uint8_t YM_get_voice_right_enable(uint8_t voice)
+{
+ if (voice < 8) {
+ return get_bit_field<7>(Ym_registers[YM_R_L_FB_CONN_OFFSET + voice]);
+ }
+
+ return 0;
+}
+
+uint8_t YM_get_voice_note(uint8_t voice)
+{
+ if (voice < 8) {
+ return get_bit_field<3, 0>(Ym_registers[YM_KC_OFFSET + voice]);
+ }
+
+ return 0;
+}
+
+uint8_t YM_get_voice_octave(uint8_t voice)
+{
+ if (voice < 8) {
+ return get_bit_field<6, 4>(Ym_registers[YM_KC_OFFSET + voice]);
+ }
+
+ return 0;
+}
+
+uint8_t YM_get_voice_key_fraction(uint8_t voice)
+{
+ if (voice < 8) {
+ return get_bit_field<7, 2>(Ym_registers[YM_KF_OFFSET + voice]);
+ }
+
+ return 0;
+}
+
+uint8_t YM_get_voice_amplitude_modulation_sensitivity(uint8_t voice)
+{
+ if (voice < 8) {
+ return get_bit_field<2, 1>(Ym_registers[YM_PMS_AMS_OFFSET + voice]);
+ }
+
+ return 0;
+}
+
+uint8_t YM_get_voice_phase_modulation_sensitivity(uint8_t voice)
+{
+ if (voice < 8) {
+ return get_bit_field<6, 4>(Ym_registers[YM_PMS_AMS_OFFSET + voice]);
+ }
+
+ return 0;
+}
+
+uint8_t YM_get_operator_phase_multiply(uint8_t voice, uint8_t op)
+{
+ if (voice < 8 && op < 4) {
+ return get_bit_field<3, 0>(Ym_registers[YM_DT1_MUL_OFFSET + (op * 8) + voice]);
+ }
+ return 0;
+}
+
+uint8_t YM_get_operator_detune_1(uint8_t voice, uint8_t op)
+{
+ if (voice < 8 && op < 4) {
+ return get_bit_field<6, 4>(Ym_registers[YM_DT1_MUL_OFFSET + (op * 8) + voice]);
+ }
+ return 0;
+}
+
+uint8_t YM_get_operator_total_level(uint8_t voice, uint8_t op)
+{
+ if (voice < 8 && op < 4) {
+ return get_bit_field<6, 0>(Ym_registers[YM_TL_OFFSET + (op * 8) + voice]);
+ }
+ return 0;
+}
+
+uint8_t YM_get_operator_attack_rate(uint8_t voice, uint8_t op)
+{
+ if (voice < 8 && op < 4) {
+ return get_bit_field<4, 0>(Ym_registers[YM_KS_AR_OFFSET + (op * 8) + voice]);
+ }
+ return 0;
+}
+
+uint8_t YM_get_operator_key_scaling(uint8_t voice, uint8_t op)
+{
+ if (voice < 8 && op < 4) {
+ return get_bit_field<7, 6>(Ym_registers[YM_KS_AR_OFFSET + (op * 8) + voice]);
+ }
+ return 0;
+}
+
+uint8_t YM_get_operator_decay_rate_1(uint8_t voice, uint8_t op)
+{
+ if (voice < 8 && op < 4) {
+ return get_bit_field<7, 4>(Ym_registers[YM_A_D1R_OFFSET + (op * 8) + voice]);
+ }
+ return 0;
+}
+
+uint8_t YM_get_operator_ams_enabled(uint8_t voice, uint8_t op)
+{
+ if (voice < 8 && op < 4) {
+ return get_bit_field<7>(Ym_registers[YM_A_D1R_OFFSET + (op * 8) + voice]);
+ }
+ return 0;
+}
+
+uint8_t YM_get_operator_decay_rate_2(uint8_t voice, uint8_t op)
+{
+ if (voice < 8 && op < 4) {
+ return get_bit_field<4, 0>(Ym_registers[YM_DT2_D2R_OFFSET + (op * 8) + voice]);
+ }
+ return 0;
+}
+
+uint8_t YM_get_operator_detune_2(uint8_t voice, uint8_t op)
+{
+ if (voice < 8 && op < 4) {
+ return get_bit_field<7, 6>(Ym_registers[YM_DT2_D2R_OFFSET + (op * 8) + voice]);
+ }
+ return 0;
+}
+
+uint8_t YM_get_operator_release_rate(uint8_t voice, uint8_t op)
+{
+ if (voice < 8 && op < 4) {
+ return get_bit_field<3, 0>(Ym_registers[YM_D1L_RR_OFFSET + (op * 8) + voice]);
+ }
+ return 0;
+}
+
+uint8_t YM_get_operator_decay_1_level(uint8_t voice, uint8_t op)
+{
+ if (voice < 8 && op < 4) {
+ return get_bit_field<7, 4>(Ym_registers[YM_D1L_RR_OFFSET + (op * 8) + voice]);
+ }
+ return 0;
+}
+
+//
+// Field Mutators
+//
+
+void YM_key_on(uint8_t channel, bool m1, bool c1, bool m2, bool c2)
+{
+ YM_debug_write(0x08, channel | (m1 ? 0x8 : 0) | (c1 ? 0x10 : 0) | (m2 ? 0x20 : 0) | (c2 ? 0x40 : 0));
+}
+
+void YM_set_lfo_frequency(uint8_t freq)
+{
+ YM_debug_write(0x18, freq);
+}
+
+void YM_set_modulation_depth(uint8_t depth)
+{
+ YM_debug_write(0x19, set_bit_field<6, 0>(Ym_registers[0x19], depth));
+}
+
+void YM_set_modulation_type(uint8_t mtype)
+{
+ YM_debug_write(0x19, set_bit_field<7>(Ym_registers[0x19], mtype));
+}
+
+void YM_set_waveform(uint8_t wf)
+{
+ YM_debug_write(0x1b, set_bit_field<1, 0>(Ym_registers[0x1b], wf));
+}
+
+void YM_set_control_output_1(bool enabled)
+{
+ YM_debug_write(0x1b, set_bit_field<6>(Ym_registers[0x1b], enabled));
+}
+
+void YM_set_control_output_2(bool enabled)
+{
+ YM_debug_write(0x1b, set_bit_field<7>(Ym_registers[0x1b], enabled));
+}
+
+void YM_set_voice_connection_type(uint8_t voice, uint8_t ctype)
+{
+ if (voice < 8) {
+ const uint8_t addr = YM_R_L_FB_CONN_OFFSET + voice;
+ YM_debug_write(addr, set_bit_field<2, 0>(Ym_registers[addr], ctype & 0x7));
+ }
+}
+
+void YM_set_voice_self_feedback_level(uint8_t voice, uint8_t fl)
+{
+ if (voice < 8) {
+ const uint8_t addr = YM_R_L_FB_CONN_OFFSET + voice;
+ YM_debug_write(addr, set_bit_field<5, 3>(Ym_registers[addr], fl));
+ }
+}
+
+void YM_set_voice_left_enable(uint8_t voice, bool enable)
+{
+ if (voice < 8) {
+ const uint8_t addr = YM_R_L_FB_CONN_OFFSET + voice;
+ YM_debug_write(addr, set_bit_field<6>(Ym_registers[addr], enable));
+ }
+}
+
+void YM_set_voice_right_enable(uint8_t voice, bool enable)
+{
+ if (voice < 8) {
+ const uint8_t addr = YM_R_L_FB_CONN_OFFSET + voice;
+ YM_debug_write(addr, set_bit_field<7>(Ym_registers[addr], enable));
+ }
+}
+
+void YM_set_voice_note(uint8_t voice, uint8_t note)
+{
+ if (voice < 8) {
+ const uint8_t addr = YM_KC_OFFSET + voice;
+ YM_debug_write(addr, set_bit_field<3, 0>(Ym_registers[addr], note));
+ }
+}
+
+void YM_set_voice_octave(uint8_t voice, uint8_t octave)
+{
+ if (voice < 8) {
+ const uint8_t addr = YM_KC_OFFSET + voice;
+ YM_debug_write(addr, set_bit_field<6, 4>(Ym_registers[addr], octave));
+ }
+}
+
+void YM_set_voice_key_fraction(uint8_t voice, uint8_t fraction)
+{
+ if (voice < 8) {
+ const uint8_t addr = YM_KF_OFFSET + voice;
+ YM_debug_write(addr, set_bit_field<7, 2>(Ym_registers[addr], fraction));
+ }
+}
+
+void YM_set_voice_amplitude_modulation_sensitivity(uint8_t voice, uint8_t ams)
+{
+ if (voice < 8) {
+ const uint8_t addr = YM_PMS_AMS_OFFSET + voice;
+ YM_debug_write(addr, set_bit_field<1, 0>(Ym_registers[addr], ams));
+ }
+}
+
+void YM_set_voice_phase_modulation_sensitivity(uint8_t voice, uint8_t pms)
+{
+ if (voice < 8) {
+ const uint8_t addr = YM_PMS_AMS_OFFSET + voice;
+ YM_debug_write(addr, set_bit_field<6, 4>(Ym_registers[addr], pms));
+ }
+}
+
+void YM_set_operator_phase_multiply(uint8_t voice, uint8_t op, uint8_t mul)
+{
+ if (voice < 8 && op < 4) {
+ const uint8_t addr = YM_DT1_MUL_OFFSET + (op * 8) + voice;
+ YM_debug_write(addr, set_bit_field<3, 0>(Ym_registers[addr], mul));
+ }
+}
+
+void YM_set_operator_detune_1(uint8_t voice, uint8_t op, uint8_t dt1)
+{
+ if (voice < 8 && op < 4) {
+ const uint8_t addr = YM_DT1_MUL_OFFSET + (op * 8) + voice;
+ YM_debug_write(addr, set_bit_field<6, 4>(Ym_registers[addr], dt1));
+ }
+}
+
+void YM_set_operator_total_level(uint8_t voice, uint8_t op, uint8_t tl)
+{
+ if (voice < 8 && op < 4) {
+ const uint8_t addr = YM_TL_OFFSET + (op * 8) + voice;
+ YM_debug_write(addr, set_bit_field<6, 0>(Ym_registers[addr], tl));
+ }
+}
+
+void YM_set_operator_attack_rate(uint8_t voice, uint8_t op, uint8_t ar)
+{
+ if (voice < 8 && op < 4) {
+ const uint8_t addr = YM_KS_AR_OFFSET + (op * 8) + voice;
+ YM_debug_write(addr, set_bit_field<4, 0>(Ym_registers[addr], ar));
+ }
+}
+
+void YM_set_operator_key_scaling(uint8_t voice, uint8_t op, uint8_t ks)
+{
+ if (voice < 8 && op < 4) {
+ const uint8_t addr = YM_KS_AR_OFFSET + (op * 8) + voice;
+ YM_debug_write(addr, set_bit_field<7, 6>(Ym_registers[addr], ks));
+ }
+}
+
+void YM_set_operator_decay_rate_1(uint8_t voice, uint8_t op, uint8_t dr1)
+{
+ if (voice < 8 && op < 4) {
+ const uint8_t addr = YM_A_D1R_OFFSET + (op * 8) + voice;
+ YM_debug_write(addr, set_bit_field<4, 0>(Ym_registers[addr], dr1));
+ }
+}
+
+void YM_set_operator_ams_enabled(uint8_t voice, uint8_t op, bool enable)
+{
+ if (voice < 8 && op < 4) {
+ const uint8_t addr = YM_A_D1R_OFFSET + (op * 8) + voice;
+ YM_debug_write(addr, set_bit_field<7>(Ym_registers[addr], enable));
+ }
+}
+
+void YM_set_operator_decay_rate_2(uint8_t voice, uint8_t op, uint8_t dr2)
+{
+ if (voice < 8 && op < 4) {
+ const uint8_t addr = YM_DT2_D2R_OFFSET + (op * 8) + voice;
+ YM_debug_write(addr, set_bit_field<4, 0>(Ym_registers[addr], dr2));
+ }
+}
+
+void YM_set_operator_detune_2(uint8_t voice, uint8_t op, uint8_t dt2)
+{
+ if (voice < 8 && op < 4) {
+ const uint8_t addr = YM_DT2_D2R_OFFSET + (op * 8) + voice;
+ YM_debug_write(addr, set_bit_field<7, 6>(Ym_registers[addr], dt2));
+ }
+}
+
+void YM_set_operator_release_rate(uint8_t voice, uint8_t op, uint8_t rr)
+{
+ if (voice < 8 && op < 4) {
+ const uint8_t addr = YM_D1L_RR_OFFSET + (op * 8) + voice;
+ YM_debug_write(addr, set_bit_field<3, 0>(Ym_registers[addr], rr));
+ }
+}
+
+void YM_set_operator_decay_1_level(uint8_t voice, uint8_t op, uint8_t d1l)
+{
+ if (voice < 8 && op < 4) {
+ const uint8_t addr = YM_D1L_RR_OFFSET + (op * 8) + voice;
+ YM_debug_write(addr, set_bit_field<7, 4>(Ym_registers[addr], d1l));
+ }
}
diff --git a/src/ym2151/ym2151.h b/src/ym2151/ym2151.h
index c53054f..61de511 100644
--- a/src/ym2151/ym2151.h
+++ b/src/ym2151/ym2151.h
@@ -11,6 +11,21 @@
//
//---------------------------------------------
+# define MAX_YM2151_VOICES (8)
+# define MAX_YM2151_SLOTS (MAX_YM2151_VOICES * 4)
+
+# define YM_R_L_FB_CONN_OFFSET 0x20
+# define YM_KC_OFFSET 0x28
+# define YM_KF_OFFSET 0x30
+# define YM_PMS_AMS_OFFSET 0x38
+
+# define YM_DT1_MUL_OFFSET 0x40
+# define YM_TL_OFFSET 0x60
+# define YM_KS_AR_OFFSET 0x80
+# define YM_A_D1R_OFFSET 0xA0
+# define YM_DT2_D2R_OFFSET 0xC0
+# define YM_D1L_RR_OFFSET 0xE0
+
void YM_render(int16_t *stream, uint32_t samples, uint32_t buffer_sample_rate);
void YM_write(uint8_t offset, uint8_t value);
@@ -19,16 +34,83 @@ uint8_t YM_read_status();
// debug stuff
void YM_debug_write(uint8_t addr, uint8_t value);
uint8_t YM_debug_read(uint8_t addr);
+
uint8_t YM_last_address();
uint8_t YM_last_data();
-uint8_t YM_get_AMD();
-uint8_t YM_get_PMD();
-float YM_get_LFO_phase();
-uint32_t YM_get_freq(uint8_t slnum);
-float YM_get_EG_output(uint8_t slnum);
-float YM_get_final_env(uint8_t slnum);
-uint8_t YM_get_env_state(uint8_t slnum);
+struct ym_modulation_state {
+ uint8_t amplitude_modulation;
+ uint8_t phase_modulation;
+ float LFO_phase;
+};
+
+struct ym_slot_state {
+ uint32_t frequency;
+ float eg_output;
+ float final_env;
+ uint8_t env_state;
+};
+
+void YM_get_modulation_regs(uint8_t *regs);
+void YM_get_slot_regs(uint8_t slnum, uint8_t *regs);
+void YM_get_modulation_state(ym_modulation_state &data);
+void YM_get_slot_state(uint8_t slnum, ym_slot_state &data);
uint16_t YM_get_timer_counter(uint8_t tnum);
+uint8_t YM_get_last_key_on();
+uint8_t YM_get_lfo_frequency();
+uint8_t YM_get_modulation_depth();
+uint8_t YM_get_modulation_type();
+uint8_t YM_get_waveform();
+uint8_t YM_get_control_output_1();
+uint8_t YM_get_control_output_2();
+uint8_t YM_get_voice_connection_type(uint8_t voice);
+uint8_t YM_get_voice_self_feedback_level(uint8_t voice);
+uint8_t YM_get_voice_left_enable(uint8_t voice);
+uint8_t YM_get_voice_right_enable(uint8_t voice);
+uint8_t YM_get_voice_note(uint8_t voice);
+uint8_t YM_get_voice_octave(uint8_t voice);
+uint8_t YM_get_voice_key_fraction(uint8_t voice);
+uint8_t YM_get_voice_amplitude_modulation_sensitivity(uint8_t voice);
+uint8_t YM_get_voice_phase_modulation_sensitivity(uint8_t voice);
+uint8_t YM_get_operator_phase_multiply(uint8_t voice, uint8_t op);
+uint8_t YM_get_operator_detune_1(uint8_t voice, uint8_t op);
+uint8_t YM_get_operator_total_level(uint8_t voice, uint8_t op);
+uint8_t YM_get_operator_attack_rate(uint8_t voice, uint8_t op);
+uint8_t YM_get_operator_key_scaling(uint8_t voice, uint8_t op);
+uint8_t YM_get_operator_decay_rate_1(uint8_t voice, uint8_t op);
+uint8_t YM_get_operator_ams_enabled(uint8_t voice, uint8_t op);
+uint8_t YM_get_operator_decay_rate_2(uint8_t voice, uint8_t op);
+uint8_t YM_get_operator_detune_2(uint8_t voice, uint8_t op);
+uint8_t YM_get_operator_release_rate(uint8_t voice, uint8_t op);
+uint8_t YM_get_operator_decay_1_level(uint8_t voice, uint8_t op);
+
+void YM_key_on(uint8_t channel, bool m1 = true, bool c1 = true, bool m2 = true, bool c2 = true);
+void YM_set_lfo_frequency(uint8_t freq);
+void YM_set_modulation_depth(uint8_t depth);
+void YM_set_modulation_type(uint8_t mtype);
+void YM_set_waveform(uint8_t wf);
+void YM_set_control_output_1(bool enabled);
+void YM_set_control_output_2(bool enabled);
+void YM_set_voice_connection_type(uint8_t voice, uint8_t ctype);
+void YM_set_voice_self_feedback_level(uint8_t voice, uint8_t fl);
+void YM_set_voice_left_enable(uint8_t voice, bool enable);
+void YM_set_voice_right_enable(uint8_t voice, bool enable);
+void YM_set_voice_note(uint8_t voice, uint8_t note);
+void YM_set_voice_octave(uint8_t voice, uint8_t octave);
+void YM_set_voice_key_fraction(uint8_t voice, uint8_t fraction);
+void YM_set_voice_amplitude_modulation_sensitivity(uint8_t voice, uint8_t ams);
+void YM_set_voice_phase_modulation_sensitivity(uint8_t voice, uint8_t pms);
+void YM_set_operator_phase_multiply(uint8_t voice, uint8_t op, uint8_t mul);
+void YM_set_operator_detune_1(uint8_t voice, uint8_t op, uint8_t dt1);
+void YM_set_operator_total_level(uint8_t voice, uint8_t op, uint8_t tl);
+void YM_set_operator_attack_rate(uint8_t voice, uint8_t op, uint8_t ar);
+void YM_set_operator_key_scaling(uint8_t voice, uint8_t op, uint8_t ks);
+void YM_set_operator_decay_rate_1(uint8_t voice, uint8_t op, uint8_t dr1);
+void YM_set_operator_ams_enabled(uint8_t voice, uint8_t op, bool enable);
+void YM_set_operator_decay_rate_2(uint8_t voice, uint8_t op, uint8_t dr2);
+void YM_set_operator_detune_2(uint8_t voice, uint8_t op, uint8_t dt2);
+void YM_set_operator_release_rate(uint8_t voice, uint8_t op, uint8_t rr);
+void YM_set_operator_decay_1_level(uint8_t voice, uint8_t op, uint8_t d1l);
+
#endif