From 8476a12deb80a5152c09a687c296bfa6078c0ed3 Mon Sep 17 00:00:00 2001 From: Tim Allen Date: Sat, 10 Oct 2015 13:16:12 +1100 Subject: [PATCH] Update to v095r01 release (open beta). byuu says: Changelog: - added MSU1 resume support - updated sfc/dsp, sfc/controller to match my coding style - fixed hiro/Windows Button and ListView::CheckButton in Windows Classic mode --- emulator/emulator.hpp | 2 +- hiro/windows/utility.cpp | 2 +- hiro/windows/widget/button.cpp | 36 +- hiro/windows/widget/list-view.cpp | 11 +- sfc/cartridge/markup.cpp | 2 +- sfc/chip/msu1/msu1.cpp | 173 ++++---- sfc/chip/msu1/msu1.hpp | 56 +-- sfc/chip/msu1/serialization.cpp | 31 +- sfc/controller/controller.cpp | 4 +- sfc/controller/controller.hpp | 2 +- sfc/controller/gamepad/gamepad.cpp | 22 +- sfc/controller/gamepad/gamepad.hpp | 5 +- sfc/controller/justifier/justifier.cpp | 64 +-- sfc/controller/justifier/justifier.hpp | 7 +- sfc/controller/mouse/mouse.cpp | 30 +- sfc/controller/mouse/mouse.hpp | 5 +- sfc/controller/multitap/multitap.cpp | 16 +- sfc/controller/multitap/multitap.hpp | 5 +- sfc/controller/superscope/superscope.cpp | 46 +- sfc/controller/superscope/superscope.hpp | 7 +- sfc/controller/usart/usart.cpp | 66 +-- sfc/controller/usart/usart.hpp | 24 +- sfc/dsp/brr.cpp | 20 +- sfc/dsp/counter.cpp | 12 +- sfc/dsp/dsp.cpp | 542 +++++++++++------------ sfc/dsp/dsp.hpp | 254 +++++------ sfc/dsp/echo.cpp | 130 +++--- sfc/dsp/envelope.cpp | 66 +-- sfc/dsp/gaussian.cpp | 22 +- sfc/dsp/misc.cpp | 34 +- sfc/dsp/modulo-array.hpp | 31 ++ sfc/dsp/moduloarray.hpp | 30 -- sfc/dsp/serialization.cpp | 83 ++-- sfc/dsp/voice.cpp | 160 +++---- sfc/sfc.hpp | 4 +- 35 files changed, 1013 insertions(+), 991 deletions(-) create mode 100644 sfc/dsp/modulo-array.hpp delete mode 100644 sfc/dsp/moduloarray.hpp diff --git a/emulator/emulator.hpp b/emulator/emulator.hpp index 71436fbc..88801df3 100644 --- a/emulator/emulator.hpp +++ b/emulator/emulator.hpp @@ -8,7 +8,7 @@ using namespace nall; namespace Emulator { static const string Name = "higan"; - static const string Version = "095"; + static const string Version = "095.01"; static const string Author = "byuu"; static const string License = "GPLv3"; static const string Website = "http://byuu.org/"; diff --git a/hiro/windows/utility.cpp b/hiro/windows/utility.cpp index e02a9ade..41c7d477 100644 --- a/hiro/windows/utility.cpp +++ b/hiro/windows/utility.cpp @@ -5,7 +5,7 @@ static const unsigned WindowsXP = 0x0501; static const unsigned WindowsVista = 0x0600; static const unsigned Windows7 = 0x0601; -static auto Button_CustomDraw(HWND, PAINTSTRUCT&, unsigned, const Font&, const Image&, Orientation, const string&) -> void; +static auto Button_CustomDraw(HWND, PAINTSTRUCT&, bool, bool, bool, unsigned, const Font&, const Image&, Orientation, const string&) -> void; static auto OsVersion() -> unsigned { OSVERSIONINFO versionInfo{0}; diff --git a/hiro/windows/widget/button.cpp b/hiro/windows/widget/button.cpp index eabd2b77..5c2c30e8 100644 --- a/hiro/windows/widget/button.cpp +++ b/hiro/windows/widget/button.cpp @@ -9,12 +9,7 @@ static auto Button_paintProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam, PAINTSTRUCT ps; BeginPaint(hwnd, &ps); auto state = Button_GetState(hwnd); - Button_CustomDraw(hwnd, ps, - (state & BST_PUSHED || checked) ? PBS_PRESSED - : (state & BST_HOT) ? PBS_HOT - : bordered ? (enabled ? PBS_NORMAL : PBS_DISABLED) - : 0, font, image, orientation, text - ); + Button_CustomDraw(hwnd, ps, bordered, checked, enabled, state, font, image, orientation, text); EndPaint(hwnd, &ps); } return DefWindowProc(hwnd, msg, wparam, lparam); @@ -113,7 +108,7 @@ auto pButton::_setState() -> void { } //this function is designed to be used with Button, CheckButton, and RadioButton -auto Button_CustomDraw(HWND hwnd, PAINTSTRUCT& ps, unsigned state, const Font& font, const Image& image, Orientation orientation, const string& text) -> void { +auto Button_CustomDraw(HWND hwnd, PAINTSTRUCT& ps, bool bordered, bool checked, bool enabled, unsigned state, const Font& font, const Image& image, Orientation orientation, const string& text) -> void { RECT rc; GetClientRect(hwnd, &rc); Geometry geometry{rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top}, imageGeometry, textGeometry; @@ -138,23 +133,29 @@ auto Button_CustomDraw(HWND hwnd, PAINTSTRUCT& ps, unsigned state, const Font& f break; } - HDC hdcSource = CreateCompatibleDC(ps.hdc); - DrawThemeParentBackground(hwnd, ps.hdc, &rc); - - if(state) { - if(auto theme = OpenThemeData(hwnd, L"BUTTON")) { - DrawThemeBackground(theme, ps.hdc, BP_PUSHBUTTON, state, &rc, &ps.rcPaint); - CloseThemeData(theme); - } + if(auto theme = OpenThemeData(hwnd, L"BUTTON")) { + DrawThemeParentBackground(hwnd, ps.hdc, &rc); + unsigned flags = 0; + if(state & BST_PUSHED || checked) flags = PBS_PRESSED; + else if(state & BST_HOT) flags = PBS_HOT; + else if(bordered) flags = enabled ? PBS_NORMAL : PBS_DISABLED; + if(bordered || flags) DrawThemeBackground(theme, ps.hdc, BP_PUSHBUTTON, flags, &rc, &ps.rcPaint); + CloseThemeData(theme); + } else { + //Windows Classic + FillRect(ps.hdc, &rc, GetSysColorBrush(COLOR_3DFACE)); + unsigned flags = (state & BST_PUSHED || checked) ? DFCS_PUSHED : 0; + if(bordered || flags) DrawFrameControl(ps.hdc, &rc, DFC_BUTTON, DFCS_BUTTONPUSH | flags | (enabled ? 0 : DFCS_INACTIVE)); } if(GetFocus() == hwnd) { signed offset = state ? 4 : 1; RECT rcFocus{rc.left + offset, rc.top + offset, rc.right - offset, rc.bottom - offset}; - if(!state || state == PBS_NORMAL) DrawFocusRect(ps.hdc, &rcFocus); + if(!(state & BST_PUSHED) && !(state & BST_HOT)) DrawFocusRect(ps.hdc, &rcFocus); } if(image) { + HDC hdcSource = CreateCompatibleDC(ps.hdc); auto bitmap = CreateBitmap(image); SelectBitmap(hdcSource, bitmap); BLENDFUNCTION blend{AC_SRC_OVER, 0, (BYTE)(IsWindowEnabled(hwnd) ? 255 : 128), AC_SRC_ALPHA}; @@ -163,6 +164,7 @@ auto Button_CustomDraw(HWND hwnd, PAINTSTRUCT& ps, unsigned state, const Font& f hdcSource, 0, 0, image.width(), image.height(), blend ); DeleteObject(bitmap); + DeleteDC(hdcSource); } if(text) { @@ -175,8 +177,6 @@ auto Button_CustomDraw(HWND hwnd, PAINTSTRUCT& ps, unsigned state, const Font& f DrawText(ps.hdc, wText, -1, &rcText, DT_NOPREFIX | DT_END_ELLIPSIS); DeleteObject(hFont); } - - DeleteDC(hdcSource); } } diff --git a/hiro/windows/widget/list-view.cpp b/hiro/windows/widget/list-view.cpp index 4d573bed..927d6ae3 100644 --- a/hiro/windows/widget/list-view.cpp +++ b/hiro/windows/widget/list-view.cpp @@ -131,12 +131,6 @@ auto pListView::setGeometry(Geometry geometry) -> void { } } -/*auto pListView::setHeaderVisible(bool visible) -> void { - auto style = GetWindowLong(hwnd, GWL_STYLE); - !visible ? style |= LVS_NOCOLUMNHEADER : style &=~ LVS_NOCOLUMNHEADER; - SetWindowLong(hwnd, GWL_STYLE, style); -}*/ - auto pListView::onActivate(LPARAM lparam) -> void { auto nmlistview = (LPNMLISTVIEW)lparam; if(ListView_GetSelectedCount(hwnd) == 0) return; @@ -213,6 +207,11 @@ auto pListView::onCustomDraw(LPARAM lparam) -> LRESULT { RECT rd{rc.left + center, rc.top + center, rc.left + center + size.cx, rc.top + center + size.cy}; DrawThemeBackground(htheme, hdc, BP_CHECKBOX, state, &rd, nullptr); CloseThemeData(htheme); + } else { + //Windows Classic + rc.left += 2; + RECT rd{rc.left, rc.top, rc.left + iconSize, rc.top + iconSize}; + DrawFrameControl(hdc, &rd, DFC_BUTTON, DFCS_BUTTONCHECK | (cell->state.checked ? DFCS_CHECKED : 0)); } rc.left += iconSize + 2; } else { diff --git a/sfc/cartridge/markup.cpp b/sfc/cartridge/markup.cpp index 1d18d2fb..6a3c6c80 100644 --- a/sfc/cartridge/markup.cpp +++ b/sfc/cartridge/markup.cpp @@ -542,7 +542,7 @@ auto Cartridge::parseMarkupMSU1(Markup::Node root) -> void { for(auto node : root.find("map")) { if(node["id"].text() == "io") { - Mapping m({&MSU1::mmio_read, &msu1}, {&MSU1::mmio_write, &msu1}); + Mapping m({&MSU1::mmioRead, &msu1}, {&MSU1::mmioWrite, &msu1}); parseMarkupMap(m, node); mapping.append(m); } diff --git a/sfc/chip/msu1/msu1.cpp b/sfc/chip/msu1/msu1.cpp index 1a5e143f..ccb94471 100644 --- a/sfc/chip/msu1/msu1.cpp +++ b/sfc/chip/msu1/msu1.cpp @@ -7,9 +7,9 @@ MSU1 msu1; #include "serialization.cpp" -void MSU1::Enter() { msu1.enter(); } +auto MSU1::Enter() -> void { msu1.enter(); } -void MSU1::enter() { +auto MSU1::enter() -> void { while(true) { if(scheduler.sync == Scheduler::SynchronizeMode::All) { scheduler.exit(Scheduler::ExitReason::SynchronizeEvent); @@ -17,27 +17,27 @@ void MSU1::enter() { int16 left = 0, right = 0; - if(mmio.audio_play) { - if(audiofile.open()) { - if(audiofile.end()) { - if(!mmio.audio_repeat) { - mmio.audio_play = false; - audiofile.seek(mmio.audio_play_offset = 8); + if(mmio.audioPlay) { + if(audioFile.open()) { + if(audioFile.end()) { + if(!mmio.audioRepeat) { + mmio.audioPlay = false; + audioFile.seek(mmio.audioPlayOffset = 8); } else { - audiofile.seek(mmio.audio_play_offset = mmio.audio_loop_offset); + audioFile.seek(mmio.audioPlayOffset = mmio.audioLoopOffset); } } else { - mmio.audio_play_offset += 4; - left = audiofile.readl(2); - right = audiofile.readl(2); + mmio.audioPlayOffset += 4; + left = audioFile.readl(2); + right = audioFile.readl(2); } } else { - mmio.audio_play = false; + mmio.audioPlay = false; } } - signed lchannel = (double)left * (double)mmio.audio_volume / 255.0; - signed rchannel = (double)right * (double)mmio.audio_volume / 255.0; + signed lchannel = (double)left * (double)mmio.audioVolume / 255.0; + signed rchannel = (double)right * (double)mmio.audioVolume / 255.0; left = sclamp<16>(lchannel); right = sclamp<16>(rchannel); if(dsp.mute()) left = 0, right = 0; @@ -48,96 +48,99 @@ void MSU1::enter() { } } -void MSU1::init() { +auto MSU1::init() -> void { } -void MSU1::load() { +auto MSU1::load() -> void { } -void MSU1::unload() { - if(datafile.open()) datafile.close(); - if(audiofile.open()) audiofile.close(); +auto MSU1::unload() -> void { + if(dataFile.open()) dataFile.close(); + if(audioFile.open()) audioFile.close(); } -void MSU1::power() { +auto MSU1::power() -> void { audio.coprocessor_enable(true); audio.coprocessor_frequency(44100.0); } -void MSU1::reset() { +auto MSU1::reset() -> void { create(MSU1::Enter, 44100); - mmio.data_seek_offset = 0; - mmio.data_read_offset = 0; + mmio.dataSeekOffset = 0; + mmio.dataReadOffset = 0; - mmio.audio_play_offset = 0; - mmio.audio_loop_offset = 0; + mmio.audioPlayOffset = 0; + mmio.audioLoopOffset = 0; - mmio.audio_track = 0; - mmio.audio_volume = 0; + mmio.audioTrack = 0; + mmio.audioVolume = 0; - mmio.data_busy = false; - mmio.audio_busy = false; - mmio.audio_repeat = false; - mmio.audio_play = false; - mmio.audio_error = false; + mmio.audioResumeTrack = ~0; //no resume + mmio.audioResumeOffset = 0; - data_open(); - audio_open(); + mmio.dataBusy = false; + mmio.audioBusy = false; + mmio.audioRepeat = false; + mmio.audioPlay = false; + mmio.audioError = false; + + dataOpen(); + audioOpen(); } -void MSU1::data_open() { - if(datafile.open()) datafile.close(); +auto MSU1::dataOpen() -> void { + if(dataFile.open()) dataFile.close(); auto document = BML::unserialize(cartridge.information.markup.cartridge); string name = document["cartridge/msu1/rom/name"].text(); if(!name) name = "msu1.rom"; - if(datafile.open({interface->path(ID::SuperFamicom), name}, file::mode::read)) { - datafile.seek(mmio.data_read_offset); + if(dataFile.open({interface->path(ID::SuperFamicom), name}, file::mode::read)) { + dataFile.seek(mmio.dataReadOffset); } } -void MSU1::audio_open() { - if(audiofile.open()) audiofile.close(); +auto MSU1::audioOpen() -> void { + if(audioFile.open()) audioFile.close(); auto document = BML::unserialize(cartridge.information.markup.cartridge); - string name = {"track-", mmio.audio_track, ".pcm"}; + string name = {"track-", mmio.audioTrack, ".pcm"}; for(auto track : document.find("cartridge/msu1/track")) { - if(track["number"].decimal() != mmio.audio_track) continue; + if(track["number"].decimal() != mmio.audioTrack) continue; name = track["name"].text(); break; } - if(audiofile.open({interface->path(ID::SuperFamicom), name}, file::mode::read)) { - if(audiofile.size() >= 8) { - uint32 header = audiofile.readm(4); + if(audioFile.open({interface->path(ID::SuperFamicom), name}, file::mode::read)) { + if(audioFile.size() >= 8) { + uint32 header = audioFile.readm(4); if(header == 0x4d535531) { //"MSU1" - mmio.audio_loop_offset = 8 + audiofile.readl(4) * 4; - if(mmio.audio_loop_offset > audiofile.size()) mmio.audio_loop_offset = 8; - mmio.audio_error = false; - audiofile.seek(mmio.audio_play_offset); + mmio.audioLoopOffset = 8 + audioFile.readl(4) * 4; + if(mmio.audioLoopOffset > audioFile.size()) mmio.audioLoopOffset = 8; + mmio.audioError = false; + audioFile.seek(mmio.audioPlayOffset); return; } } - audiofile.close(); + audioFile.close(); } - mmio.audio_error = true; + mmio.audioError = true; } -uint8 MSU1::mmio_read(unsigned addr) { +auto MSU1::mmioRead(unsigned addr) -> uint8 { cpu.synchronize_coprocessors(); addr = 0x2000 | (addr & 7); switch(addr) { case 0x2000: - return (mmio.data_busy << 7) - | (mmio.audio_busy << 6) - | (mmio.audio_repeat << 5) - | (mmio.audio_play << 4) - | (mmio.audio_error << 3) - | (Revision << 0); + return (mmio.dataBusy << 7) + | (mmio.audioBusy << 6) + | (mmio.audioRepeat << 5) + | (mmio.audioPlay << 4) + | (mmio.audioError << 3) + | (Revision << 0); case 0x2001: - if(mmio.data_busy) return 0x00; - if(datafile.end()) return 0x00; - mmio.data_read_offset++; - return datafile.read(); + if(mmio.dataBusy) return 0x00; + if(dataFile.end()) return 0x00; + mmio.dataReadOffset++; + return dataFile.read(); case 0x2002: return 'S'; case 0x2003: return '-'; case 0x2004: return 'M'; @@ -147,29 +150,41 @@ uint8 MSU1::mmio_read(unsigned addr) { } } -void MSU1::mmio_write(unsigned addr, uint8 data) { +auto MSU1::mmioWrite(unsigned addr, uint8 data) -> void { cpu.synchronize_coprocessors(); addr = 0x2000 | (addr & 7); switch(addr) { - case 0x2000: mmio.data_seek_offset = (mmio.data_seek_offset & 0xffffff00) | (data << 0); break; - case 0x2001: mmio.data_seek_offset = (mmio.data_seek_offset & 0xffff00ff) | (data << 8); break; - case 0x2002: mmio.data_seek_offset = (mmio.data_seek_offset & 0xff00ffff) | (data << 16); break; - case 0x2003: mmio.data_seek_offset = (mmio.data_seek_offset & 0x00ffffff) | (data << 24); - mmio.data_read_offset = mmio.data_seek_offset; - data_open(); + case 0x2000: mmio.dataSeekOffset = (mmio.dataSeekOffset & 0xffffff00) | (data << 0); break; + case 0x2001: mmio.dataSeekOffset = (mmio.dataSeekOffset & 0xffff00ff) | (data << 8); break; + case 0x2002: mmio.dataSeekOffset = (mmio.dataSeekOffset & 0xff00ffff) | (data << 16); break; + case 0x2003: mmio.dataSeekOffset = (mmio.dataSeekOffset & 0x00ffffff) | (data << 24); + mmio.dataReadOffset = mmio.dataSeekOffset; + dataOpen(); break; - case 0x2004: mmio.audio_track = (mmio.audio_track & 0xff00) | (data << 0); break; - case 0x2005: mmio.audio_track = (mmio.audio_track & 0x00ff) | (data << 8); - mmio.audio_play_offset = 8; - audio_open(); + case 0x2004: mmio.audioTrack = (mmio.audioTrack & 0xff00) | (data << 0); break; + case 0x2005: mmio.audioTrack = (mmio.audioTrack & 0x00ff) | (data << 8); + mmio.audioPlayOffset = 8; + if(mmio.audioTrack == mmio.audioResumeTrack) { + mmio.audioPlayOffset = mmio.audioResumeOffset; + mmio.audioResumeTrack = ~0; //erase resume track + mmio.audioResumeOffset = 0; + } + audioOpen(); + break; + case 0x2006: + mmio.audioVolume = data; break; - case 0x2006: mmio.audio_volume = data; break; case 0x2007: - if(mmio.audio_busy) break; - if(mmio.audio_error) break; - mmio.audio_repeat = data & 2; - mmio.audio_play = data & 1; + if(mmio.audioBusy) break; + if(mmio.audioError) break; + bool audioResume = data & 4; + mmio.audioRepeat = data & 2; + mmio.audioPlay = data & 1; + if(!mmio.audioPlay && audioResume) { + mmio.audioResumeTrack = mmio.audioTrack; + mmio.audioResumeOffset = mmio.audioPlayOffset; + } break; } } diff --git a/sfc/chip/msu1/msu1.hpp b/sfc/chip/msu1/msu1.hpp index c1b35d4f..7d4c2a68 100644 --- a/sfc/chip/msu1/msu1.hpp +++ b/sfc/chip/msu1/msu1.hpp @@ -1,23 +1,24 @@ struct MSU1 : Coprocessor { - static void Enter(); - void enter(); - void init(); - void load(); - void unload(); - void power(); - void reset(); + static auto Enter() -> void; - void data_open(); - void audio_open(); + auto enter() -> void; + auto init() -> void; + auto load() -> void; + auto unload() -> void; + auto power() -> void; + auto reset() -> void; - uint8 mmio_read(unsigned addr); - void mmio_write(unsigned addr, uint8 data); + auto dataOpen() -> void; + auto audioOpen() -> void; - void serialize(serializer&); + auto mmioRead(unsigned addr) -> uint8; + auto mmioWrite(unsigned addr, uint8 data) -> void; + + auto serialize(serializer&) -> void; private: - file datafile; - file audiofile; + file dataFile; + file audioFile; enum Flag : unsigned { DataBusy = 0x80, @@ -25,24 +26,27 @@ private: AudioRepeating = 0x20, AudioPlaying = 0x10, AudioError = 0x08, - Revision = 0x01, + Revision = 0x02, }; struct MMIO { - uint32 data_seek_offset; - uint32 data_read_offset; + uint32 dataSeekOffset; + uint32 dataReadOffset; - uint32 audio_play_offset; - uint32 audio_loop_offset; + uint32 audioPlayOffset; + uint32 audioLoopOffset; - uint16 audio_track; - uint8 audio_volume; + uint16 audioTrack; + uint8 audioVolume; - bool data_busy; - bool audio_busy; - bool audio_repeat; - bool audio_play; - bool audio_error; + uint32 audioResumeTrack; + uint32 audioResumeOffset; + + bool dataBusy; + bool audioBusy; + bool audioRepeat; + bool audioPlay; + bool audioError; } mmio; }; diff --git a/sfc/chip/msu1/serialization.cpp b/sfc/chip/msu1/serialization.cpp index 3a787029..2967e999 100644 --- a/sfc/chip/msu1/serialization.cpp +++ b/sfc/chip/msu1/serialization.cpp @@ -1,25 +1,28 @@ #ifdef MSU1_CPP -void MSU1::serialize(serializer& s) { +auto MSU1::serialize(serializer& s) -> void { Thread::serialize(s); - s.integer(mmio.data_seek_offset); - s.integer(mmio.data_read_offset); + s.integer(mmio.dataSeekOffset); + s.integer(mmio.dataReadOffset); - s.integer(mmio.audio_play_offset); - s.integer(mmio.audio_loop_offset); + s.integer(mmio.audioPlayOffset); + s.integer(mmio.audioLoopOffset); - s.integer(mmio.audio_track); - s.integer(mmio.audio_volume); + s.integer(mmio.audioTrack); + s.integer(mmio.audioVolume); - s.integer(mmio.data_busy); - s.integer(mmio.audio_busy); - s.integer(mmio.audio_repeat); - s.integer(mmio.audio_play); - s.integer(mmio.audio_error); + s.integer(mmio.audioResumeTrack); + s.integer(mmio.audioResumeOffset); - data_open(); - audio_open(); + s.integer(mmio.dataBusy); + s.integer(mmio.audioBusy); + s.integer(mmio.audioRepeat); + s.integer(mmio.audioPlay); + s.integer(mmio.audioError); + + dataOpen(); + audioOpen(); } #endif diff --git a/sfc/controller/controller.cpp b/sfc/controller/controller.cpp index 69932835..7d1241f4 100644 --- a/sfc/controller/controller.cpp +++ b/sfc/controller/controller.cpp @@ -25,10 +25,10 @@ auto Controller::enter() -> void { auto Controller::step(unsigned clocks) -> void { clock += clocks * (uint64)cpu.frequency; - synchronize_cpu(); + synchronizeCPU(); } -auto Controller::synchronize_cpu() -> void { +auto Controller::synchronizeCPU() -> void { if(CPU::Threaded == true) { if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(cpu.thread); } else { diff --git a/sfc/controller/controller.hpp b/sfc/controller/controller.hpp index 2f9c4368..1cbd40bc 100644 --- a/sfc/controller/controller.hpp +++ b/sfc/controller/controller.hpp @@ -20,7 +20,7 @@ struct Controller : Thread { virtual auto enter() -> void; auto step(unsigned clocks) -> void; - auto synchronize_cpu() -> void; + auto synchronizeCPU() -> void; auto iobit() -> bool; auto iobit(bool data) -> void; diff --git a/sfc/controller/gamepad/gamepad.cpp b/sfc/controller/gamepad/gamepad.cpp index e78f93d3..e3810f38 100644 --- a/sfc/controller/gamepad/gamepad.cpp +++ b/sfc/controller/gamepad/gamepad.cpp @@ -1,6 +1,15 @@ #ifdef CONTROLLER_CPP -uint2 Gamepad::data() { +Gamepad::Gamepad(bool port) : Controller(port) { + latched = 0; + counter = 0; + + b = y = select = start = 0; + up = down = left = right = 0; + a = x = l = r = 0; +} + +auto Gamepad::data() -> uint2 { if(counter >= 16) return 1; if(latched == 1) return interface->inputPoll(port, (unsigned)Input::Device::Joypad, (unsigned)Input::JoypadID::B); @@ -23,7 +32,7 @@ uint2 Gamepad::data() { return 0; //12-15: signature } -void Gamepad::latch(bool data) { +auto Gamepad::latch(bool data) -> void { if(latched == data) return; latched = data; counter = 0; @@ -45,13 +54,4 @@ void Gamepad::latch(bool data) { } } -Gamepad::Gamepad(bool port) : Controller(port) { - latched = 0; - counter = 0; - - b = y = select = start = 0; - up = down = left = right = 0; - a = x = l = r = 0; -} - #endif diff --git a/sfc/controller/gamepad/gamepad.hpp b/sfc/controller/gamepad/gamepad.hpp index 9a12cebf..effdec3e 100644 --- a/sfc/controller/gamepad/gamepad.hpp +++ b/sfc/controller/gamepad/gamepad.hpp @@ -1,8 +1,9 @@ struct Gamepad : Controller { - uint2 data(); - void latch(bool data); Gamepad(bool port); + auto data() -> uint2; + auto latch(bool data) -> void; + private: bool latched; unsigned counter; diff --git a/sfc/controller/justifier/justifier.cpp b/sfc/controller/justifier/justifier.cpp index 9cda5432..4ff1f122 100644 --- a/sfc/controller/justifier/justifier.cpp +++ b/sfc/controller/justifier/justifier.cpp @@ -1,6 +1,35 @@ #ifdef CONTROLLER_CPP -void Justifier::enter() { +Justifier::Justifier(bool port, bool chained): +Controller(port), +chained(chained), +device(chained == false ? (unsigned)Input::Device::Justifier : (unsigned)Input::Device::Justifiers) +{ + create(Controller::Enter, 21477272); + latched = 0; + counter = 0; + active = 0; + + player1.x = 256 / 2; + player1.y = 240 / 2; + player1.trigger = false; + player2.start = false; + + player2.x = 256 / 2; + player2.y = 240 / 2; + player2.trigger = false; + player2.start = false; + + if(chained == false) { + player2.x = -1; + player2.y = -1; + } else { + player1.x -= 16; + player2.x += 16; + } +} + +auto Justifier::enter() -> void { unsigned prev = 0; while(true) { unsigned next = cpu.vcounter() * 1364 + cpu.hcounter(); @@ -40,7 +69,7 @@ void Justifier::enter() { } } -uint2 Justifier::data() { +auto Justifier::data() -> uint2 { if(counter >= 32) return 1; if(counter == 0) { @@ -93,40 +122,11 @@ uint2 Justifier::data() { } } -void Justifier::latch(bool data) { +auto Justifier::latch(bool data) -> void { if(latched == data) return; latched = data; counter = 0; if(latched == 0) active = !active; //toggle between both controllers, even when unchained } -Justifier::Justifier(bool port, bool chained): -Controller(port), -chained(chained), -device(chained == false ? (unsigned)Input::Device::Justifier : (unsigned)Input::Device::Justifiers) -{ - create(Controller::Enter, 21477272); - latched = 0; - counter = 0; - active = 0; - - player1.x = 256 / 2; - player1.y = 240 / 2; - player1.trigger = false; - player2.start = false; - - player2.x = 256 / 2; - player2.y = 240 / 2; - player2.trigger = false; - player2.start = false; - - if(chained == false) { - player2.x = -1; - player2.y = -1; - } else { - player1.x -= 16; - player2.x += 16; - } -} - #endif diff --git a/sfc/controller/justifier/justifier.hpp b/sfc/controller/justifier/justifier.hpp index da467aa1..86b0e5ea 100644 --- a/sfc/controller/justifier/justifier.hpp +++ b/sfc/controller/justifier/justifier.hpp @@ -1,9 +1,10 @@ struct Justifier : Controller { - void enter(); - uint2 data(); - void latch(bool data); Justifier(bool port, bool chained); + auto enter() -> void; + auto data() -> uint2; + auto latch(bool data) -> void; + //private: const bool chained; //true if the second justifier is attached to the first const unsigned device; diff --git a/sfc/controller/mouse/mouse.cpp b/sfc/controller/mouse/mouse.cpp index 83f647e6..cb3e375f 100644 --- a/sfc/controller/mouse/mouse.cpp +++ b/sfc/controller/mouse/mouse.cpp @@ -1,6 +1,19 @@ #ifdef CONTROLLER_CPP -uint2 Mouse::data() { +Mouse::Mouse(bool port) : Controller(port) { + latched = 0; + counter = 0; + + speed = 0; + x = 0; + y = 0; + dx = 0; + dy = 0; + l = 0; + r = 0; +} + +auto Mouse::data() -> uint2 { if(latched == 1) { speed = (speed + 1) % 3; return 0; @@ -48,7 +61,7 @@ uint2 Mouse::data() { } } -void Mouse::latch(bool data) { +auto Mouse::latch(bool data) -> void { if(latched == data) return; latched = data; counter = 0; @@ -74,17 +87,4 @@ void Mouse::latch(bool data) { y = min(127, y); } -Mouse::Mouse(bool port) : Controller(port) { - latched = 0; - counter = 0; - - speed = 0; - x = 0; - y = 0; - dx = 0; - dy = 0; - l = 0; - r = 0; -} - #endif diff --git a/sfc/controller/mouse/mouse.hpp b/sfc/controller/mouse/mouse.hpp index 2ecd9c3f..9ac44526 100644 --- a/sfc/controller/mouse/mouse.hpp +++ b/sfc/controller/mouse/mouse.hpp @@ -1,8 +1,9 @@ struct Mouse : Controller { - uint2 data(); - void latch(bool data); Mouse(bool port); + auto data() -> uint2; + auto latch(bool data) -> void; + private: bool latched; unsigned counter; diff --git a/sfc/controller/multitap/multitap.cpp b/sfc/controller/multitap/multitap.cpp index c53d9cc9..d5ae19d0 100644 --- a/sfc/controller/multitap/multitap.cpp +++ b/sfc/controller/multitap/multitap.cpp @@ -1,6 +1,12 @@ #ifdef CONTROLLER_CPP -uint2 Multitap::data() { +Multitap::Multitap(bool port) : Controller(port) { + latched = 0; + counter1 = 0; + counter2 = 0; +} + +auto Multitap::data() -> uint2 { if(latched) return 2; //multitap detection unsigned index, port1, port2; @@ -25,17 +31,11 @@ uint2 Multitap::data() { return (data2 << 1) | (data1 << 0); } -void Multitap::latch(bool data) { +auto Multitap::latch(bool data) -> void { if(latched == data) return; latched = data; counter1 = 0; counter2 = 0; } -Multitap::Multitap(bool port) : Controller(port) { - latched = 0; - counter1 = 0; - counter2 = 0; -} - #endif diff --git a/sfc/controller/multitap/multitap.hpp b/sfc/controller/multitap/multitap.hpp index 0540af71..028a47be 100644 --- a/sfc/controller/multitap/multitap.hpp +++ b/sfc/controller/multitap/multitap.hpp @@ -1,8 +1,9 @@ struct Multitap : Controller { - uint2 data(); - void latch(bool data); Multitap(bool port); + auto data() -> uint2; + auto latch(bool data) -> void; + private: bool latched; unsigned counter1; diff --git a/sfc/controller/superscope/superscope.cpp b/sfc/controller/superscope/superscope.cpp index 3aa43bdc..57a7b7b7 100644 --- a/sfc/controller/superscope/superscope.cpp +++ b/sfc/controller/superscope/superscope.cpp @@ -12,7 +12,27 @@ //require manual polling of PIO ($4201.d6) to determine when iobit was written. //Note that no commercial game ever utilizes a Super Scope in port 1. -void SuperScope::enter() { +SuperScope::SuperScope(bool port) : Controller(port) { + create(Controller::Enter, 21477272); + latched = 0; + counter = 0; + + //center cursor onscreen + x = 256 / 2; + y = 240 / 2; + + trigger = false; + cursor = false; + turbo = false; + pause = false; + offscreen = false; + + turbolock = false; + triggerlock = false; + pauselock = false; +} + +auto SuperScope::enter() -> void { unsigned prev = 0; while(true) { unsigned next = cpu.vcounter() * 1364 + cpu.hcounter(); @@ -42,7 +62,7 @@ void SuperScope::enter() { } } -uint2 SuperScope::data() { +auto SuperScope::data() -> uint2 { if(counter >= 8) return 1; if(counter == 0) { @@ -94,30 +114,10 @@ uint2 SuperScope::data() { } } -void SuperScope::latch(bool data) { +auto SuperScope::latch(bool data) -> void { if(latched == data) return; latched = data; counter = 0; } -SuperScope::SuperScope(bool port) : Controller(port) { - create(Controller::Enter, 21477272); - latched = 0; - counter = 0; - - //center cursor onscreen - x = 256 / 2; - y = 240 / 2; - - trigger = false; - cursor = false; - turbo = false; - pause = false; - offscreen = false; - - turbolock = false; - triggerlock = false; - pauselock = false; -} - #endif diff --git a/sfc/controller/superscope/superscope.hpp b/sfc/controller/superscope/superscope.hpp index a7a90b71..3e7b4e95 100644 --- a/sfc/controller/superscope/superscope.hpp +++ b/sfc/controller/superscope/superscope.hpp @@ -1,9 +1,10 @@ struct SuperScope : Controller { - void enter(); - uint2 data(); - void latch(bool data); SuperScope(bool port); + auto enter() -> void; + auto data() -> uint2; + auto latch(bool data) -> void; + //private: bool latched; unsigned counter; diff --git a/sfc/controller/usart/usart.cpp b/sfc/controller/usart/usart.cpp index 6143e8fb..9cab61ec 100644 --- a/sfc/controller/usart/usart.cpp +++ b/sfc/controller/usart/usart.cpp @@ -14,7 +14,31 @@ //SNES Clock <> 1Kohm Resistor <> Teensy D5 //Teensy D5 <> Teensy D7 -void USART::enter() { +USART::USART(bool port) : Controller(port) { + latched = 0; + data1 = 0; + data2 = 0; + counter = 0; + + rxlength = 0; + rxdata = 0; + + txlength = 0; + txdata = 0; + + string filename = {interface->path(ID::SuperFamicom), "usart.so"}; + if(openAbsolute(filename)) { + init = sym("usart_init"); + main = sym("usart_main"); + if(init && main) create(Controller::Enter, 10000000); + } +} + +USART::~USART() { + if(open()) close(); +} + +auto USART::enter() -> void { if(init && main) { init( {&USART::quit, this}, @@ -29,22 +53,22 @@ void USART::enter() { while(true) step(10000000); } -bool USART::quit() { +auto USART::quit() -> bool { step(1); return false; } -void USART::usleep(unsigned milliseconds) { +auto USART::usleep(unsigned milliseconds) -> void { step(10 * milliseconds); } -bool USART::readable() { +auto USART::readable() -> bool { step(1); return txbuffer.size(); } //SNES -> USART -uint8 USART::read() { +auto USART::read() -> uint8 { step(1); while(txbuffer.size() == 0) step(1); uint8 data = txbuffer[0]; @@ -52,19 +76,19 @@ uint8 USART::read() { return data; } -bool USART::writable() { +auto USART::writable() -> bool { step(1); return true; } //USART -> SNES -void USART::write(uint8 data) { +auto USART::write(uint8 data) -> void { step(1); rxbuffer.append(data ^ 0xff); } //clock -uint2 USART::data() { +auto USART::data() -> uint2 { //Joypad if(iobit()) { if(counter >= 16) return 1; @@ -104,34 +128,10 @@ uint2 USART::data() { } //latch -void USART::latch(bool data) { +auto USART::latch(bool data) -> void { if(latched == data) return; latched = data; counter = 0; } -USART::USART(bool port) : Controller(port) { - latched = 0; - data1 = 0; - data2 = 0; - counter = 0; - - rxlength = 0; - rxdata = 0; - - txlength = 0; - txdata = 0; - - string filename = {interface->path(ID::SuperFamicom), "usart.so"}; - if(openAbsolute(filename)) { - init = sym("usart_init"); - main = sym("usart_main"); - if(init && main) create(Controller::Enter, 10000000); - } -} - -USART::~USART() { - if(open()) close(); -} - #endif diff --git a/sfc/controller/usart/usart.hpp b/sfc/controller/usart/usart.hpp index f79b0b24..3243489e 100644 --- a/sfc/controller/usart/usart.hpp +++ b/sfc/controller/usart/usart.hpp @@ -1,19 +1,19 @@ struct USART : Controller, public library { - void enter(); - - bool quit(); - void usleep(unsigned milliseconds); - bool readable(); - uint8 read(); - bool writable(); - void write(uint8 data); - - uint2 data(); - void latch(bool data); - USART(bool port); ~USART(); + auto enter() -> void; + + auto quit() -> bool; + auto usleep(unsigned milliseconds) -> void; + auto readable() -> bool; + auto read() -> uint8; + auto writable() -> bool; + auto write(uint8 data) -> void; + + auto data() -> uint2; + auto latch(bool data) -> void; + private: bool latched; bool data1; diff --git a/sfc/dsp/brr.cpp b/sfc/dsp/brr.cpp index 3a294868..bf10549b 100644 --- a/sfc/dsp/brr.cpp +++ b/sfc/dsp/brr.cpp @@ -1,17 +1,17 @@ #ifdef DSP_CPP -void DSP::brr_decode(voice_t& v) { +auto DSP::brrDecode(Voice& v) -> void { //state.t_brr_byte = ram[v.brr_addr + v.brr_offset] cached from previous clock cycle - int nybbles = (state.t_brr_byte << 8) + smp.apuram[(uint16)(v.brr_addr + v.brr_offset + 1)]; + signed nybbles = (state._brrByte << 8) + smp.apuram[(uint16)(v.brrAddress + v.brrOffset + 1)]; - const int filter = (state.t_brr_header >> 2) & 3; - const int scale = (state.t_brr_header >> 4); + const signed filter = (state._brrHeader >> 2) & 3; + const signed scale = (state._brrHeader >> 4); //decode four samples - for(unsigned i = 0; i < 4; i++) { + for(auto n : range(4)) { //bits 12-15 = current nybble; sign extend, then shift right to 4-bit precision //result: s = 4-bit sign-extended sample value - int s = (int16)nybbles >> 12; + signed s = (int16)nybbles >> 12; nybbles <<= 4; //slide nybble so that on next loop iteration, bits 12-15 = current nybble if(scale <= 12) { @@ -22,8 +22,8 @@ void DSP::brr_decode(voice_t& v) { } //apply IIR filter (2 is the most commonly used) - const int p1 = v.buffer[v.buf_pos - 1]; - const int p2 = v.buffer[v.buf_pos - 2] >> 1; + const signed p1 = v.buffer[v.bufferOffset - 1]; + const signed p2 = v.buffer[v.bufferOffset - 2] >> 1; switch(filter) { case 0: @@ -55,8 +55,8 @@ void DSP::brr_decode(voice_t& v) { //adjust and write sample s = sclamp<16>(s); s = (int16)(s << 1); - v.buffer.write(v.buf_pos++, s); - if(v.buf_pos >= brr_buf_size) v.buf_pos = 0; + v.buffer.write(v.bufferOffset++, s); + if(v.bufferOffset >= BrrBufferSize) v.bufferOffset = 0; } } diff --git a/sfc/dsp/counter.cpp b/sfc/dsp/counter.cpp index f65fdd26..4b7c87b2 100644 --- a/sfc/dsp/counter.cpp +++ b/sfc/dsp/counter.cpp @@ -4,7 +4,7 @@ //all rates are evenly divisible by counter_range (0x7800, 30720, or 2048 * 5 * 3) //note that rate[0] is a special case, which never triggers -const uint16 DSP::counter_rate[32] = { +const uint16 DSP::CounterRate[32] = { 0, 2048, 1536, 1280, 1024, 768, 640, 512, 384, @@ -22,7 +22,7 @@ const uint16 DSP::counter_rate[32] = { //counter_offset = counter offset from zero //counters do not appear to be aligned at zero for all rates -const uint16 DSP::counter_offset[32] = { +const uint16 DSP::CounterOffset[32] = { 0, 0, 1040, 536, 0, 1040, 536, 0, 1040, @@ -37,16 +37,16 @@ const uint16 DSP::counter_offset[32] = { 0, }; -inline void DSP::counter_tick() { +inline auto DSP::counterTick() -> void { state.counter--; - if(state.counter < 0) state.counter = counter_range - 1; + if(state.counter < 0) state.counter = CounterRange - 1; } //return true if counter event should trigger -inline bool DSP::counter_poll(unsigned rate) { +inline auto DSP::counterPoll(unsigned rate) -> bool { if(rate == 0) return false; - return (((unsigned)state.counter + counter_offset[rate]) % counter_rate[rate]) == 0; + return (((unsigned)state.counter + CounterOffset[rate]) % CounterRate[rate]) == 0; } #endif diff --git a/sfc/dsp/dsp.cpp b/sfc/dsp/dsp.cpp index 81604b3f..db7c9be0 100644 --- a/sfc/dsp/dsp.cpp +++ b/sfc/dsp/dsp.cpp @@ -5,8 +5,8 @@ namespace SuperFamicom { DSP dsp; -#define REG(n) state.regs[r_##n] -#define VREG(n) state.regs[v.vidx + v_##n] +#define REG(n) state.regs[n] +#define VREG(n) state.regs[v.vidx + n] #include "serialization.cpp" #include "gaussian.cpp" @@ -17,277 +17,8 @@ DSP dsp; #include "voice.cpp" #include "echo.cpp" -/* timing */ - -void DSP::step(unsigned clocks) { - clock += clocks; -} - -void DSP::synchronize_smp() { - if(SMP::Threaded == true) { - if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(smp.thread); - } else { - while(clock >= 0) smp.enter(); - } -} - -void DSP::Enter() { dsp.enter(); } - -void DSP::enter() { - while(true) { - if(scheduler.sync == Scheduler::SynchronizeMode::All) { - scheduler.exit(Scheduler::ExitReason::SynchronizeEvent); - } - - voice_5(voice[0]); - voice_2(voice[1]); - tick(); - - voice_6(voice[0]); - voice_3(voice[1]); - tick(); - - voice_7(voice[0]); - voice_4(voice[1]); - voice_1(voice[3]); - tick(); - - voice_8(voice[0]); - voice_5(voice[1]); - voice_2(voice[2]); - tick(); - - voice_9(voice[0]); - voice_6(voice[1]); - voice_3(voice[2]); - tick(); - - voice_7(voice[1]); - voice_4(voice[2]); - voice_1(voice[4]); - tick(); - - voice_8(voice[1]); - voice_5(voice[2]); - voice_2(voice[3]); - tick(); - - voice_9(voice[1]); - voice_6(voice[2]); - voice_3(voice[3]); - tick(); - - voice_7(voice[2]); - voice_4(voice[3]); - voice_1(voice[5]); - tick(); - - voice_8(voice[2]); - voice_5(voice[3]); - voice_2(voice[4]); - tick(); - - voice_9(voice[2]); - voice_6(voice[3]); - voice_3(voice[4]); - tick(); - - voice_7(voice[3]); - voice_4(voice[4]); - voice_1(voice[6]); - tick(); - - voice_8(voice[3]); - voice_5(voice[4]); - voice_2(voice[5]); - tick(); - - voice_9(voice[3]); - voice_6(voice[4]); - voice_3(voice[5]); - tick(); - - voice_7(voice[4]); - voice_4(voice[5]); - voice_1(voice[7]); - tick(); - - voice_8(voice[4]); - voice_5(voice[5]); - voice_2(voice[6]); - tick(); - - voice_9(voice[4]); - voice_6(voice[5]); - voice_3(voice[6]); - tick(); - - voice_1(voice[0]); - voice_7(voice[5]); - voice_4(voice[6]); - tick(); - - voice_8(voice[5]); - voice_5(voice[6]); - voice_2(voice[7]); - tick(); - - voice_9(voice[5]); - voice_6(voice[6]); - voice_3(voice[7]); - tick(); - - voice_1(voice[1]); - voice_7(voice[6]); - voice_4(voice[7]); - tick(); - - voice_8(voice[6]); - voice_5(voice[7]); - voice_2(voice[0]); - tick(); - - voice_3a(voice[0]); - voice_9(voice[6]); - voice_6(voice[7]); - echo_22(); - tick(); - - voice_7(voice[7]); - echo_23(); - tick(); - - voice_8(voice[7]); - echo_24(); - tick(); - - voice_3b(voice[0]); - voice_9(voice[7]); - echo_25(); - tick(); - - echo_26(); - tick(); - - misc_27(); - echo_27(); - tick(); - - misc_28(); - echo_28(); - tick(); - - misc_29(); - echo_29(); - tick(); - - misc_30(); - voice_3c(voice[0]); - echo_30(); - tick(); - - voice_4(voice[0]); - voice_1(voice[2]); - tick(); - } -} - -void DSP::tick() { - step(3 * 8); - synchronize_smp(); -} - -/* register interface for S-SMP $00f2,$00f3 */ - -bool DSP::mute() { - return state.regs[r_flg] & 0x40; -} - -uint8 DSP::read(uint8 addr) { - return state.regs[addr]; -} - -void DSP::write(uint8 addr, uint8 data) { - state.regs[addr] = data; - - if((addr & 0x0f) == v_envx) { - state.envx_buf = data; - } else if((addr & 0x0f) == v_outx) { - state.outx_buf = data; - } else if(addr == r_kon) { - state.new_kon = data; - } else if(addr == r_endx) { - //always cleared, regardless of data written - state.endx_buf = 0; - state.regs[r_endx] = 0; - } -} - -/* initialization */ - -void DSP::power() { - memset(&state.regs, 0, sizeof state.regs); - state.echo_hist_pos = 0; - state.every_other_sample = false; - state.kon = 0; - state.noise = 0; - state.counter = 0; - state.echo_offset = 0; - state.echo_length = 0; - state.new_kon = 0; - state.endx_buf = 0; - state.envx_buf = 0; - state.outx_buf = 0; - state.t_pmon = 0; - state.t_non = 0; - state.t_eon = 0; - state.t_dir = 0; - state.t_koff = 0; - state.t_brr_next_addr = 0; - state.t_adsr0 = 0; - state.t_brr_header = 0; - state.t_brr_byte = 0; - state.t_srcn = 0; - state.t_esa = 0; - state.t_echo_disabled = 0; - state.t_dir_addr = 0; - state.t_pitch = 0; - state.t_output = 0; - state.t_looped = 0; - state.t_echo_ptr = 0; - state.t_main_out[0] = state.t_main_out[1] = 0; - state.t_echo_out[0] = state.t_echo_out[1] = 0; - state.t_echo_in[0] = state.t_echo_in[1] = 0; - - for(unsigned i = 0; i < 8; i++) { - voice[i].buf_pos = 0; - voice[i].interp_pos = 0; - voice[i].brr_addr = 0; - voice[i].brr_offset = 1; - voice[i].vbit = 1 << i; - voice[i].vidx = i * 0x10; - voice[i].kon_delay = 0; - voice[i].env_mode = env_release; - voice[i].env = 0; - voice[i].t_envx_out = 0; - voice[i].hidden_env = 0; - } -} - -void DSP::reset() { - create(Enter, system.apu_frequency()); - - REG(flg) = 0xe0; - - state.noise = 0x4000; - state.echo_hist_pos = 0; - state.every_other_sample = 1; - state.echo_offset = 0; - state.counter = 0; -} - DSP::DSP() { - static_assert(sizeof(int) >= 32 / 8, "int >= 32-bits"); + static_assert(sizeof(signed) >= 32 / 8, "signed >= 32-bits"); static_assert((int8)0x80 == -0x80, "8-bit sign extension"); static_assert((int16)0x8000 == -0x8000, "16-bit sign extension"); static_assert((uint16)0xffff0000 == 0, "16-bit unsigned clip"); @@ -298,7 +29,272 @@ DSP::DSP() { assert(sclamp<16>(-0x8001) == -0x8000); } -DSP::~DSP() { +/* timing */ + +auto DSP::step(unsigned clocks) -> void { + clock += clocks; +} + +auto DSP::synchronizeSMP() -> void { + if(SMP::Threaded == true) { + if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(smp.thread); + } else { + while(clock >= 0) smp.enter(); + } +} + +auto DSP::Enter() -> void { dsp.enter(); } + +auto DSP::enter() -> void { + while(true) { + if(scheduler.sync == Scheduler::SynchronizeMode::All) { + scheduler.exit(Scheduler::ExitReason::SynchronizeEvent); + } + + voice5(voice[0]); + voice2(voice[1]); + tick(); + + voice6(voice[0]); + voice3(voice[1]); + tick(); + + voice7(voice[0]); + voice4(voice[1]); + voice1(voice[3]); + tick(); + + voice8(voice[0]); + voice5(voice[1]); + voice2(voice[2]); + tick(); + + voice9(voice[0]); + voice6(voice[1]); + voice3(voice[2]); + tick(); + + voice7(voice[1]); + voice4(voice[2]); + voice1(voice[4]); + tick(); + + voice8(voice[1]); + voice5(voice[2]); + voice2(voice[3]); + tick(); + + voice9(voice[1]); + voice6(voice[2]); + voice3(voice[3]); + tick(); + + voice7(voice[2]); + voice4(voice[3]); + voice1(voice[5]); + tick(); + + voice8(voice[2]); + voice5(voice[3]); + voice2(voice[4]); + tick(); + + voice9(voice[2]); + voice6(voice[3]); + voice3(voice[4]); + tick(); + + voice7(voice[3]); + voice4(voice[4]); + voice1(voice[6]); + tick(); + + voice8(voice[3]); + voice5(voice[4]); + voice2(voice[5]); + tick(); + + voice9(voice[3]); + voice6(voice[4]); + voice3(voice[5]); + tick(); + + voice7(voice[4]); + voice4(voice[5]); + voice1(voice[7]); + tick(); + + voice8(voice[4]); + voice5(voice[5]); + voice2(voice[6]); + tick(); + + voice9(voice[4]); + voice6(voice[5]); + voice3(voice[6]); + tick(); + + voice1(voice[0]); + voice7(voice[5]); + voice4(voice[6]); + tick(); + + voice8(voice[5]); + voice5(voice[6]); + voice2(voice[7]); + tick(); + + voice9(voice[5]); + voice6(voice[6]); + voice3(voice[7]); + tick(); + + voice1(voice[1]); + voice7(voice[6]); + voice4(voice[7]); + tick(); + + voice8(voice[6]); + voice5(voice[7]); + voice2(voice[0]); + tick(); + + voice3a(voice[0]); + voice9(voice[6]); + voice6(voice[7]); + echo22(); + tick(); + + voice7(voice[7]); + echo23(); + tick(); + + voice8(voice[7]); + echo24(); + tick(); + + voice3b(voice[0]); + voice9(voice[7]); + echo25(); + tick(); + + echo26(); + tick(); + + misc27(); + echo27(); + tick(); + + misc28(); + echo28(); + tick(); + + misc29(); + echo29(); + tick(); + + misc30(); + voice3c(voice[0]); + echo30(); + tick(); + + voice4(voice[0]); + voice1(voice[2]); + tick(); + } +} + +auto DSP::tick() -> void { + step(3 * 8); + synchronizeSMP(); +} + +/* register interface for S-SMP $00f2,$00f3 */ + +auto DSP::mute() const -> bool { + return REG(FLG) & 0x40; +} + +auto DSP::read(uint8 addr) -> uint8 { + return REG(addr); +} + +auto DSP::write(uint8 addr, uint8 data) -> void { + REG(addr) = data; + + if((addr & 0x0f) == ENVX) { + state.envxBuffer = data; + } else if((addr & 0x0f) == OUTX) { + state.outxBuffer = data; + } else if(addr == KON) { + state.konBuffer = data; + } else if(addr == ENDX) { + //always cleared, regardless of data written + state.endxBuffer = 0; + REG(ENDX) = 0; + } +} + +/* initialization */ + +auto DSP::power() -> void { + for(auto& r : state.regs) r = 0; + state.echoHistoryOffset = 0; + state.everyOtherSample = false; + state.kon = 0; + state.noise = 0; + state.counter = 0; + state.echoOffset = 0; + state.echoLength = 0; + state.konBuffer = 0; + state.endxBuffer = 0; + state.envxBuffer = 0; + state.outxBuffer = 0; + state._pmon = 0; + state._non = 0; + state._eon = 0; + state._dir = 0; + state._koff = 0; + state._brrNextAddress = 0; + state._adsr0 = 0; + state._brrHeader = 0; + state._brrByte = 0; + state._srcn = 0; + state._esa = 0; + state._echoDisabled = 0; + state._dirAddress = 0; + state._pitch = 0; + state._output = 0; + state._looped = 0; + state._echoPointer = 0; + state._mainOut[0] = state._mainOut[1] = 0; + state._echoOut[0] = state._echoOut[1] = 0; + state._echoIn[0] = state._echoIn[1] = 0; + + for(auto n : range(8)) { + voice[n].bufferOffset = 0; + voice[n].gaussianOffset = 0; + voice[n].brrAddress = 0; + voice[n].brrOffset = 1; + voice[n].vbit = 1 << n; + voice[n].vidx = n * 0x10; + voice[n].konDelay = 0; + voice[n].envelopeMode = EnvelopeRelease; + voice[n].envelope = 0; + voice[n].hiddenEnvelope = 0; + voice[n]._envxOut = 0; + } +} + +auto DSP::reset() -> void { + create(Enter, system.apu_frequency()); + + REG(FLG) = 0xe0; + state.noise = 0x4000; + state.echoHistoryOffset = 0; + state.everyOtherSample = 1; + state.echoOffset = 0; + state.counter = 0; } #undef REG diff --git a/sfc/dsp/dsp.hpp b/sfc/dsp/dsp.hpp index 8d13bfa7..aa85f065 100644 --- a/sfc/dsp/dsp.hpp +++ b/sfc/dsp/dsp.hpp @@ -1,175 +1,177 @@ struct DSP : Thread { enum : bool { Threaded = true }; - alwaysinline void step(unsigned clocks); - alwaysinline void synchronize_smp(); - bool mute(); - uint8 read(uint8 addr); - void write(uint8 addr, uint8 data); - - void enter(); - void power(); - void reset(); - - void serialize(serializer&); DSP(); - ~DSP(); + + alwaysinline auto step(unsigned clocks) -> void; + alwaysinline auto synchronizeSMP() -> void; + + auto mute() const -> bool; + auto read(uint8 addr) -> uint8; + auto write(uint8 addr, uint8 data) -> void; + + auto enter() -> void; + auto power() -> void; + auto reset() -> void; + + auto serialize(serializer&) -> void; privileged: - #include "moduloarray.hpp" + #include "modulo-array.hpp" - //global registers - enum global_reg_t { - r_mvoll = 0x0c, r_mvolr = 0x1c, - r_evoll = 0x2c, r_evolr = 0x3c, - r_kon = 0x4c, r_koff = 0x5c, - r_flg = 0x6c, r_endx = 0x7c, - r_efb = 0x0d, r_pmon = 0x2d, - r_non = 0x3d, r_eon = 0x4d, - r_dir = 0x5d, r_esa = 0x6d, - r_edl = 0x7d, r_fir = 0x0f, //8 coefficients at 0x0f, 0x1f, ... 0x7f + enum GlobalRegister : unsigned { + MVOLL = 0x0c, MVOLR = 0x1c, + EVOLL = 0x2c, EVOLR = 0x3c, + KON = 0x4c, KOFF = 0x5c, + FLG = 0x6c, ENDX = 0x7c, + EFB = 0x0d, PMON = 0x2d, + NON = 0x3d, EON = 0x4d, + DIR = 0x5d, ESA = 0x6d, + EDL = 0x7d, FIR = 0x0f, //8 coefficients at 0x0f, 0x1f, ... 0x7f }; - //voice registers - enum voice_reg_t { - v_voll = 0x00, v_volr = 0x01, - v_pitchl = 0x02, v_pitchh = 0x03, - v_srcn = 0x04, v_adsr0 = 0x05, - v_adsr1 = 0x06, v_gain = 0x07, - v_envx = 0x08, v_outx = 0x09, + enum VoiceRegister : unsigned { + VOLL = 0x00, VOLR = 0x01, + PITCHL = 0x02, PITCHH = 0x03, + SRCN = 0x04, ADSR0 = 0x05, + ADSR1 = 0x06, GAIN = 0x07, + ENVX = 0x08, OUTX = 0x09, }; - //internal envelope modes - enum env_mode_t { env_release, env_attack, env_decay, env_sustain }; + enum EnvelopeMode : unsigned { + EnvelopeRelease, + EnvelopeAttack, + EnvelopeDecay, + EnvelopeSustain, + }; - //internal constants - enum { echo_hist_size = 8 }; - enum { brr_buf_size = 12 }; - enum { brr_block_size = 9 }; + enum : unsigned { + EchoHistorySize = 8, + BrrBufferSize = 12, + BrrBlockSize = 9, + CounterRange = 2048 * 5 * 3, //30720 (0x7800) + }; - //global state - struct state_t { + struct State { uint8 regs[128]; - moduloarray echo_hist[2]; //echo history keeps most recent 8 samples - int echo_hist_pos; + ModuloArray echoHistory[2]; //echo history keeps most recent 8 samples + signed echoHistoryOffset; - bool every_other_sample; //toggles every sample - int kon; //KON value when last checked - int noise; - int counter; - int echo_offset; //offset from ESA in echo buffer - int echo_length; //number of bytes that echo_offset will stop at + bool everyOtherSample; //toggles every sample + signed kon; //KON value when last checked + signed noise; + signed counter; + signed echoOffset; //offset from ESA in echo buffer + signed echoLength; //number of bytes that echo_offset will stop at //hidden registers also written to when main register is written to - int new_kon; - int endx_buf; - int envx_buf; - int outx_buf; + signed konBuffer; + signed endxBuffer; + signed envxBuffer; + signed outxBuffer; - //temporary state between clocks + //temporary state between clocks (prefixed with _) //read once per sample - int t_pmon; - int t_non; - int t_eon; - int t_dir; - int t_koff; + signed _pmon; + signed _non; + signed _eon; + signed _dir; + signed _koff; //read a few clocks ahead before used - int t_brr_next_addr; - int t_adsr0; - int t_brr_header; - int t_brr_byte; - int t_srcn; - int t_esa; - int t_echo_disabled; + signed _brrNextAddress; + signed _adsr0; + signed _brrHeader; + signed _brrByte; + signed _srcn; + signed _esa; + signed _echoDisabled; //internal state that is recalculated every sample - int t_dir_addr; - int t_pitch; - int t_output; - int t_looped; - int t_echo_ptr; + signed _dirAddress; + signed _pitch; + signed _output; + signed _looped; + signed _echoPointer; //left/right sums - int t_main_out[2]; - int t_echo_out[2]; - int t_echo_in [2]; + signed _mainOut[2]; + signed _echoOut[2]; + signed _echoIn [2]; } state; - //voice state - struct voice_t { - moduloarray buffer; //decoded samples - int buf_pos; //place in buffer where next samples will be decoded - int interp_pos; //relative fractional position in sample (0x1000 = 1.0) - int brr_addr; //address of current BRR block - int brr_offset; //current decoding offset in BRR block - int vbit; //bitmask for voice: 0x01 for voice 0, 0x02 for voice 1, etc - int vidx; //voice channel register index: 0x00 for voice 0, 0x10 for voice 1, etc - int kon_delay; //KON delay/current setup phase - int env_mode; - int env; //current envelope level - int t_envx_out; - int hidden_env; //used by GAIN mode 7, very obscure quirk + struct Voice { + ModuloArray buffer; //decoded samples + signed bufferOffset; //place in buffer where next samples will be decoded + signed gaussianOffset; //relative fractional position in sample (0x1000 = 1.0) + signed brrAddress; //address of current BRR block + signed brrOffset; //current decoding offset in BRR block + signed vbit; //bitmask for voice: 0x01 for voice 0, 0x02 for voice 1, etc + signed vidx; //voice channel register index: 0x00 for voice 0, 0x10 for voice 1, etc + signed konDelay; //KON delay/current setup phase + signed envelopeMode; + signed envelope; //current envelope level + signed hiddenEnvelope; //used by GAIN mode 7, very obscure quirk + signed _envxOut; } voice[8]; //gaussian - static const int16 gaussian_table[512]; - int gaussian_interpolate(const voice_t& v); + static const int16 GaussianTable[512]; + auto gaussianInterpolate(const Voice& v) -> signed; //counter - enum { counter_range = 2048 * 5 * 3 }; //30720 (0x7800) - static const uint16 counter_rate[32]; - static const uint16 counter_offset[32]; - void counter_tick(); - bool counter_poll(unsigned rate); + static const uint16 CounterRate[32]; + static const uint16 CounterOffset[32]; + auto counterTick() -> void; + auto counterPoll(unsigned rate) -> bool; //envelope - void envelope_run(voice_t& v); + auto envelopeRun(Voice& v) -> void; //brr - void brr_decode(voice_t& v); + auto brrDecode(Voice& v) -> void; //misc - void misc_27(); - void misc_28(); - void misc_29(); - void misc_30(); + auto misc27() -> void; + auto misc28() -> void; + auto misc29() -> void; + auto misc30() -> void; //voice - void voice_output(voice_t& v, bool channel); - void voice_1 (voice_t &v); - void voice_2 (voice_t &v); - void voice_3 (voice_t &v); - void voice_3a(voice_t &v); - void voice_3b(voice_t &v); - void voice_3c(voice_t &v); - void voice_4 (voice_t &v); - void voice_5 (voice_t &v); - void voice_6 (voice_t &v); - void voice_7 (voice_t &v); - void voice_8 (voice_t &v); - void voice_9 (voice_t &v); + auto voiceOutput(Voice& v, bool channel) -> void; + auto voice1 (Voice& v) -> void; + auto voice2 (Voice& v) -> void; + auto voice3 (Voice& v) -> void; + auto voice3a(Voice& v) -> void; + auto voice3b(Voice& v) -> void; + auto voice3c(Voice& v) -> void; + auto voice4 (Voice& v) -> void; + auto voice5 (Voice& v) -> void; + auto voice6 (Voice& v) -> void; + auto voice7 (Voice& v) -> void; + auto voice8 (Voice& v) -> void; + auto voice9 (Voice& v) -> void; //echo - int calc_fir(int i, bool channel); - int echo_output(bool channel); - void echo_read(bool channel); - void echo_write(bool channel); - void echo_22(); - void echo_23(); - void echo_24(); - void echo_25(); - void echo_26(); - void echo_27(); - void echo_28(); - void echo_29(); - void echo_30(); + auto calculateFIR(signed i, bool channel) -> signed; + auto echoOutput(bool channel) -> signed; + auto echoRead(bool channel) -> void; + auto echoWrite(bool channel) -> void; + auto echo22() -> void; + auto echo23() -> void; + auto echo24() -> void; + auto echo25() -> void; + auto echo26() -> void; + auto echo27() -> void; + auto echo28() -> void; + auto echo29() -> void; + auto echo30() -> void; //dsp - static void Enter(); - void tick(); + static auto Enter() -> void; + auto tick() -> void; }; extern DSP dsp; diff --git a/sfc/dsp/echo.cpp b/sfc/dsp/echo.cpp index b80de85d..9cd4aec7 100644 --- a/sfc/dsp/echo.cpp +++ b/sfc/dsp/echo.cpp @@ -1,106 +1,106 @@ #ifdef DSP_CPP -int DSP::calc_fir(int i, bool channel) { - int s = state.echo_hist[channel][state.echo_hist_pos + i + 1]; - return (s * (int8)REG(fir + i * 0x10)) >> 6; +auto DSP::calculateFIR(signed i, bool channel) -> signed { + signed s = state.echoHistory[channel][state.echoHistoryOffset + i + 1]; + return (s * (int8)REG(FIR + i * 0x10)) >> 6; } -int DSP::echo_output(bool channel) { - int output = (int16)((state.t_main_out[channel] * (int8)REG(mvoll + channel * 0x10)) >> 7) - + (int16)((state.t_echo_in [channel] * (int8)REG(evoll + channel * 0x10)) >> 7); +auto DSP::echoOutput(bool channel) -> signed { + signed output = (int16)((state._mainOut[channel] * (int8)REG(MVOLL + channel * 0x10)) >> 7) + + (int16)((state._echoIn [channel] * (int8)REG(EVOLL + channel * 0x10)) >> 7); return sclamp<16>(output); } -void DSP::echo_read(bool channel) { - unsigned addr = state.t_echo_ptr + channel * 2; +auto DSP::echoRead(bool channel) -> void { + unsigned addr = state._echoPointer + channel * 2; uint8 lo = smp.apuram[(uint16)(addr + 0)]; uint8 hi = smp.apuram[(uint16)(addr + 1)]; - int s = (int16)((hi << 8) + lo); - state.echo_hist[channel].write(state.echo_hist_pos, s >> 1); + signed s = (int16)((hi << 8) + lo); + state.echoHistory[channel].write(state.echoHistoryOffset, s >> 1); } -void DSP::echo_write(bool channel) { - if(!(state.t_echo_disabled & 0x20)) { - unsigned addr = state.t_echo_ptr + channel * 2; - int s = state.t_echo_out[channel]; +auto DSP::echoWrite(bool channel) -> void { + if(!(state._echoDisabled & 0x20)) { + unsigned addr = state._echoPointer + channel * 2; + signed s = state._echoOut[channel]; smp.apuram[(uint16)(addr + 0)] = s; smp.apuram[(uint16)(addr + 1)] = s >> 8; } - state.t_echo_out[channel] = 0; + state._echoOut[channel] = 0; } -void DSP::echo_22() { +auto DSP::echo22() -> void { //history - state.echo_hist_pos++; - if(state.echo_hist_pos >= echo_hist_size) state.echo_hist_pos = 0; + state.echoHistoryOffset++; + if(state.echoHistoryOffset >= EchoHistorySize) state.echoHistoryOffset = 0; - state.t_echo_ptr = (uint16)((state.t_esa << 8) + state.echo_offset); - echo_read(0); + state._echoPointer = (uint16)((state._esa << 8) + state.echoOffset); + echoRead(0); //FIR - int l = calc_fir(0, 0); - int r = calc_fir(0, 1); + signed l = calculateFIR(0, 0); + signed r = calculateFIR(0, 1); - state.t_echo_in[0] = l; - state.t_echo_in[1] = r; + state._echoIn[0] = l; + state._echoIn[1] = r; } -void DSP::echo_23() { - int l = calc_fir(1, 0) + calc_fir(2, 0); - int r = calc_fir(1, 1) + calc_fir(2, 1); +auto DSP::echo23() -> void { + signed l = calculateFIR(1, 0) + calculateFIR(2, 0); + signed r = calculateFIR(1, 1) + calculateFIR(2, 1); - state.t_echo_in[0] += l; - state.t_echo_in[1] += r; + state._echoIn[0] += l; + state._echoIn[1] += r; - echo_read(1); + echoRead(1); } -void DSP::echo_24() { - int l = calc_fir(3, 0) + calc_fir(4, 0) + calc_fir(5, 0); - int r = calc_fir(3, 1) + calc_fir(4, 1) + calc_fir(5, 1); +auto DSP::echo24() -> void { + signed l = calculateFIR(3, 0) + calculateFIR(4, 0) + calculateFIR(5, 0); + signed r = calculateFIR(3, 1) + calculateFIR(4, 1) + calculateFIR(5, 1); - state.t_echo_in[0] += l; - state.t_echo_in[1] += r; + state._echoIn[0] += l; + state._echoIn[1] += r; } -void DSP::echo_25() { - int l = state.t_echo_in[0] + calc_fir(6, 0); - int r = state.t_echo_in[1] + calc_fir(6, 1); +auto DSP::echo25() -> void { + signed l = state._echoIn[0] + calculateFIR(6, 0); + signed r = state._echoIn[1] + calculateFIR(6, 1); l = (int16)l; r = (int16)r; - l += (int16)calc_fir(7, 0); - r += (int16)calc_fir(7, 1); + l += (int16)calculateFIR(7, 0); + r += (int16)calculateFIR(7, 1); - state.t_echo_in[0] = sclamp<16>(l) & ~1; - state.t_echo_in[1] = sclamp<16>(r) & ~1; + state._echoIn[0] = sclamp<16>(l) & ~1; + state._echoIn[1] = sclamp<16>(r) & ~1; } -void DSP::echo_26() { +auto DSP::echo26() -> void { //left output volumes //(save sample for next clock so we can output both together) - state.t_main_out[0] = echo_output(0); + state._mainOut[0] = echoOutput(0); //echo feedback - int l = state.t_echo_out[0] + (int16)((state.t_echo_in[0] * (int8)REG(efb)) >> 7); - int r = state.t_echo_out[1] + (int16)((state.t_echo_in[1] * (int8)REG(efb)) >> 7); + signed l = state._echoOut[0] + (int16)((state._echoIn[0] * (int8)REG(EFB)) >> 7); + signed r = state._echoOut[1] + (int16)((state._echoIn[1] * (int8)REG(EFB)) >> 7); - state.t_echo_out[0] = sclamp<16>(l) & ~1; - state.t_echo_out[1] = sclamp<16>(r) & ~1; + state._echoOut[0] = sclamp<16>(l) & ~1; + state._echoOut[1] = sclamp<16>(r) & ~1; } -void DSP::echo_27() { +auto DSP::echo27() -> void { //output - int outl = state.t_main_out[0]; - int outr = echo_output(1); - state.t_main_out[0] = 0; - state.t_main_out[1] = 0; + signed outl = state._mainOut[0]; + signed outr = echoOutput(1); + state._mainOut[0] = 0; + state._mainOut[1] = 0; //TODO: global muting isn't this simple //(turns DAC on and off or something, causing small ~37-sample pulse when first muted) - if(REG(flg) & 0x40) { + if(REG(FLG) & 0x40) { outl = 0; outr = 0; } @@ -109,27 +109,27 @@ void DSP::echo_27() { audio.sample(outl, outr); } -void DSP::echo_28() { - state.t_echo_disabled = REG(flg); +auto DSP::echo28() -> void { + state._echoDisabled = REG(FLG); } -void DSP::echo_29() { - state.t_esa = REG(esa); +auto DSP::echo29() -> void { + state._esa = REG(ESA); - if(!state.echo_offset) state.echo_length = (REG(edl) & 0x0f) << 11; + if(!state.echoOffset) state.echoLength = (REG(EDL) & 0x0f) << 11; - state.echo_offset += 4; - if(state.echo_offset >= state.echo_length) state.echo_offset = 0; + state.echoOffset += 4; + if(state.echoOffset >= state.echoLength) state.echoOffset = 0; //write left echo - echo_write(0); + echoWrite(0); - state.t_echo_disabled = REG(flg); + state._echoDisabled = REG(FLG); } -void DSP::echo_30() { +auto DSP::echo30() -> void { //write right echo - echo_write(1); + echoWrite(1); } #endif diff --git a/sfc/dsp/envelope.cpp b/sfc/dsp/envelope.cpp index 2965e623..c017f038 100644 --- a/sfc/dsp/envelope.cpp +++ b/sfc/dsp/envelope.cpp @@ -1,62 +1,62 @@ #ifdef DSP_CPP -void DSP::envelope_run(voice_t& v) { - int env = v.env; +auto DSP::envelopeRun(Voice& v) -> void { + signed envelope = v.envelope; - if(v.env_mode == env_release) { //60% - env -= 0x8; - if(env < 0) env = 0; - v.env = env; + if(v.envelopeMode == EnvelopeRelease) { //60% + envelope -= 0x8; + if(envelope < 0) envelope = 0; + v.envelope = envelope; return; } - int rate; - int env_data = VREG(adsr1); - if(state.t_adsr0 & 0x80) { //99% ADSR - if(v.env_mode >= env_decay) { //99% - env--; - env -= env >> 8; - rate = env_data & 0x1f; - if(v.env_mode == env_decay) { //1% - rate = ((state.t_adsr0 >> 3) & 0x0e) + 0x10; + signed rate; + signed envelopeData = VREG(ADSR1); + if(state._adsr0 & 0x80) { //99% ADSR + if(v.envelopeMode >= EnvelopeDecay) { //99% + envelope--; + envelope -= envelope >> 8; + rate = envelopeData & 0x1f; + if(v.envelopeMode == EnvelopeDecay) { //1% + rate = ((state._adsr0 >> 3) & 0x0e) + 0x10; } } else { //env_attack - rate = ((state.t_adsr0 & 0x0f) << 1) + 1; - env += rate < 31 ? 0x20 : 0x400; + rate = ((state._adsr0 & 0x0f) << 1) + 1; + envelope += rate < 31 ? 0x20 : 0x400; } } else { //GAIN - env_data = VREG(gain); - int mode = env_data >> 5; + envelopeData = VREG(GAIN); + signed mode = envelopeData >> 5; if(mode < 4) { //direct - env = env_data << 4; + envelope = envelopeData << 4; rate = 31; } else { - rate = env_data & 0x1f; + rate = envelopeData & 0x1f; if(mode == 4) { //4: linear decrease - env -= 0x20; + envelope -= 0x20; } else if(mode < 6) { //5: exponential decrease - env--; - env -= env >> 8; + envelope--; + envelope -= envelope >> 8; } else { //6, 7: linear increase - env += 0x20; - if(mode > 6 && (unsigned)v.hidden_env >= 0x600) { - env += 0x8 - 0x20; //7: two-slope linear increase + envelope += 0x20; + if(mode > 6 && (unsigned)v.hiddenEnvelope >= 0x600) { + envelope += 0x8 - 0x20; //7: two-slope linear increase } } } } //sustain level - if((env >> 8) == (env_data >> 5) && v.env_mode == env_decay) v.env_mode = env_sustain; - v.hidden_env = env; + if((envelope >> 8) == (envelopeData >> 5) && v.envelopeMode == EnvelopeDecay) v.envelopeMode = EnvelopeSustain; + v.hiddenEnvelope = envelope; //unsigned cast because linear decrease underflowing also triggers this - if((unsigned)env > 0x7ff) { - env = (env < 0 ? 0 : 0x7ff); - if(v.env_mode == env_attack) v.env_mode = env_decay; + if((unsigned)envelope > 0x7ff) { + envelope = (envelope < 0 ? 0 : 0x7ff); + if(v.envelopeMode == EnvelopeAttack) v.envelopeMode = EnvelopeDecay; } - if(counter_poll(rate) == true) v.env = env; + if(counterPoll(rate)) v.envelope = envelope; } #endif diff --git a/sfc/dsp/gaussian.cpp b/sfc/dsp/gaussian.cpp index 2b066796..dd689f59 100644 --- a/sfc/dsp/gaussian.cpp +++ b/sfc/dsp/gaussian.cpp @@ -1,6 +1,6 @@ #ifdef DSP_CPP -const int16 DSP::gaussian_table[512] = { +const int16 DSP::GaussianTable[512] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, 5, @@ -35,19 +35,19 @@ const int16 DSP::gaussian_table[512] = { 1299, 1300, 1300, 1301, 1302, 1302, 1303, 1303, 1303, 1304, 1304, 1304, 1304, 1304, 1305, 1305, }; -int DSP::gaussian_interpolate(const voice_t& v) { +auto DSP::gaussianInterpolate(const Voice& v) -> signed { //make pointers into gaussian table based on fractional position between samples - int offset = (v.interp_pos >> 4) & 0xff; - const int16* fwd = gaussian_table + 255 - offset; - const int16* rev = gaussian_table + offset; //mirror left half of gaussian table + signed offset = (v.gaussianOffset >> 4) & 0xff; + const int16* forward = GaussianTable + 255 - offset; + const int16* reverse = GaussianTable + offset; //mirror left half of gaussian table - offset = v.buf_pos + (v.interp_pos >> 12); - int output; - output = (fwd[ 0] * v.buffer[offset + 0]) >> 11; - output += (fwd[256] * v.buffer[offset + 1]) >> 11; - output += (rev[256] * v.buffer[offset + 2]) >> 11; + offset = v.bufferOffset + (v.gaussianOffset >> 12); + signed output; + output = (forward[ 0] * v.buffer[offset + 0]) >> 11; + output += (forward[256] * v.buffer[offset + 1]) >> 11; + output += (reverse[256] * v.buffer[offset + 2]) >> 11; output = (int16)output; - output += (rev[ 0] * v.buffer[offset + 3]) >> 11; + output += (reverse[ 0] * v.buffer[offset + 3]) >> 11; return sclamp<16>(output) & ~1; } diff --git a/sfc/dsp/misc.cpp b/sfc/dsp/misc.cpp index 98a0e5c3..fb7a692d 100644 --- a/sfc/dsp/misc.cpp +++ b/sfc/dsp/misc.cpp @@ -1,33 +1,33 @@ #ifdef DSP_CPP -void DSP::misc_27() { - state.t_pmon = REG(pmon) & ~1; //voice 0 doesn't support PMON +auto DSP::misc27() -> void { + state._pmon = REG(PMON) & ~1; //voice 0 doesn't support PMON } -void DSP::misc_28() { - state.t_non = REG(non); - state.t_eon = REG(eon); - state.t_dir = REG(dir); +auto DSP::misc28() -> void { + state._non = REG(NON); + state._eon = REG(EON); + state._dir = REG(DIR); } -void DSP::misc_29() { - state.every_other_sample ^= 1; - if(state.every_other_sample) { - state.new_kon &= ~state.kon; //clears KON 63 clocks after it was last read +auto DSP::misc29() -> void { + state.everyOtherSample ^= 1; + if(state.everyOtherSample) { + state.konBuffer &= ~state.kon; //clears KON 63 clocks after it was last read } } -void DSP::misc_30() { - if(state.every_other_sample) { - state.kon = state.new_kon; - state.t_koff = REG(koff); +auto DSP::misc30() -> void { + if(state.everyOtherSample) { + state.kon = state.konBuffer; + state._koff = REG(KOFF); } - counter_tick(); + counterTick(); //noise - if(counter_poll(REG(flg) & 0x1f) == true) { - int feedback = (state.noise << 13) ^ (state.noise << 14); + if(counterPoll(REG(FLG) & 0x1f)) { + signed feedback = (state.noise << 13) ^ (state.noise << 14); state.noise = (feedback & 0x4000) ^ (state.noise >> 1); } } diff --git a/sfc/dsp/modulo-array.hpp b/sfc/dsp/modulo-array.hpp new file mode 100644 index 00000000..29cb9481 --- /dev/null +++ b/sfc/dsp/modulo-array.hpp @@ -0,0 +1,31 @@ +template +struct ModuloArray { + ModuloArray() { + buffer = new T[size * 3](); + } + + ~ModuloArray() { + delete[] buffer; + } + + inline auto operator[](signed index) const -> T { + return buffer[size + index]; + } + + inline auto read(signed index) const -> T { + return buffer[size + index]; + } + + inline auto write(unsigned index, const T value) -> void { + buffer[index] = + buffer[index + size] = + buffer[index + size + size] = value; + } + + auto serialize(serializer& s) -> void { + s.array(buffer, size * 3); + } + +private: + T* buffer; +}; diff --git a/sfc/dsp/moduloarray.hpp b/sfc/dsp/moduloarray.hpp deleted file mode 100644 index d237da4f..00000000 --- a/sfc/dsp/moduloarray.hpp +++ /dev/null @@ -1,30 +0,0 @@ -template struct moduloarray { - inline T operator[](int index) const { - return buffer[size + index]; - } - - inline T read(int index) const { - return buffer[size + index]; - } - - inline void write(unsigned index, const T value) { - buffer[index] = - buffer[index + size] = - buffer[index + size + size] = value; - } - - void serialize(serializer& s) { - s.array(buffer, size * 3); - } - - moduloarray() { - buffer = new T[size * 3](); - } - - ~moduloarray() { - delete[] buffer; - } - -private: - T* buffer; -}; diff --git a/sfc/dsp/serialization.cpp b/sfc/dsp/serialization.cpp index 355c20d7..1671a9c0 100644 --- a/sfc/dsp/serialization.cpp +++ b/sfc/dsp/serialization.cpp @@ -4,62 +4,59 @@ void DSP::serialize(serializer& s) { Thread::serialize(s); s.array(state.regs, 128); - state.echo_hist[0].serialize(s); - state.echo_hist[1].serialize(s); - s.integer(state.echo_hist_pos); + state.echoHistory[0].serialize(s); + state.echoHistory[1].serialize(s); + s.integer(state.echoHistoryOffset); - s.integer(state.every_other_sample); + s.integer(state.everyOtherSample); s.integer(state.kon); s.integer(state.noise); s.integer(state.counter); - s.integer(state.echo_offset); - s.integer(state.echo_length); + s.integer(state.echoOffset); + s.integer(state.echoLength); - s.integer(state.new_kon); - s.integer(state.endx_buf); - s.integer(state.envx_buf); - s.integer(state.outx_buf); + s.integer(state.konBuffer); + s.integer(state.endxBuffer); + s.integer(state.envxBuffer); + s.integer(state.outxBuffer); - s.integer(state.t_pmon); - s.integer(state.t_non); - s.integer(state.t_eon); - s.integer(state.t_dir); - s.integer(state.t_koff); + s.integer(state._pmon); + s.integer(state._non); + s.integer(state._eon); + s.integer(state._dir); + s.integer(state._koff); - s.integer(state.t_brr_next_addr); - s.integer(state.t_adsr0); - s.integer(state.t_brr_header); - s.integer(state.t_brr_byte); - s.integer(state.t_srcn); - s.integer(state.t_esa); - s.integer(state.t_echo_disabled); + s.integer(state._brrNextAddress); + s.integer(state._adsr0); + s.integer(state._brrHeader); + s.integer(state._brrByte); + s.integer(state._srcn); + s.integer(state._esa); + s.integer(state._echoDisabled); - s.integer(state.t_dir_addr); - s.integer(state.t_pitch); - s.integer(state.t_output); - s.integer(state.t_looped); - s.integer(state.t_echo_ptr); + s.integer(state._dirAddress); + s.integer(state._pitch); + s.integer(state._output); + s.integer(state._looped); + s.integer(state._echoPointer); - s.integer(state.t_main_out[0]); - s.integer(state.t_main_out[1]); - s.integer(state.t_echo_out[0]); - s.integer(state.t_echo_out[1]); - s.integer(state.t_echo_in [0]); - s.integer(state.t_echo_in [1]); + s.array(state._mainOut, 2); + s.array(state._echoOut, 2); + s.array(state._echoIn, 2); - for(unsigned n = 0; n < 8; n++) { + for(auto n : range(8)) { voice[n].buffer.serialize(s); - s.integer(voice[n].buf_pos); - s.integer(voice[n].interp_pos); - s.integer(voice[n].brr_addr); - s.integer(voice[n].brr_offset); + s.integer(voice[n].bufferOffset); + s.integer(voice[n].gaussianOffset); + s.integer(voice[n].brrAddress); + s.integer(voice[n].brrOffset); s.integer(voice[n].vbit); s.integer(voice[n].vidx); - s.integer(voice[n].kon_delay); - s.integer(voice[n].env_mode); - s.integer(voice[n].env); - s.integer(voice[n].t_envx_out); - s.integer(voice[n].hidden_env); + s.integer(voice[n].konDelay); + s.integer(voice[n].envelopeMode); + s.integer(voice[n].envelope); + s.integer(voice[n].hiddenEnvelope); + s.integer(voice[n]._envxOut); } } diff --git a/sfc/dsp/voice.cpp b/sfc/dsp/voice.cpp index 76debf27..6c3898a9 100644 --- a/sfc/dsp/voice.cpp +++ b/sfc/dsp/voice.cpp @@ -1,174 +1,174 @@ #ifdef DSP_CPP -inline void DSP::voice_output(voice_t& v, bool channel) { +inline auto DSP::voiceOutput(Voice& v, bool channel) -> void { //apply left/right volume - int amp = (state.t_output * (int8)VREG(voll + channel)) >> 7; + signed amp = (state._output * (int8)VREG(VOLL + channel)) >> 7; //add to output total - state.t_main_out[channel] += amp; - state.t_main_out[channel] = sclamp<16>(state.t_main_out[channel]); + state._mainOut[channel] += amp; + state._mainOut[channel] = sclamp<16>(state._mainOut[channel]); //optionally add to echo total - if(state.t_eon & v.vbit) { - state.t_echo_out[channel] += amp; - state.t_echo_out[channel] = sclamp<16>(state.t_echo_out[channel]); + if(state._eon & v.vbit) { + state._echoOut[channel] += amp; + state._echoOut[channel] = sclamp<16>(state._echoOut[channel]); } } -void DSP::voice_1(voice_t& v) { - state.t_dir_addr = (state.t_dir << 8) + (state.t_srcn << 2); - state.t_srcn = VREG(srcn); +auto DSP::voice1(Voice& v) -> void { + state._dirAddress = (state._dir << 8) + (state._srcn << 2); + state._srcn = VREG(SRCN); } -void DSP::voice_2(voice_t& v) { +auto DSP::voice2(Voice& v) -> void { //read sample pointer (ignored if not needed) - uint16 addr = state.t_dir_addr; - if(!v.kon_delay) addr += 2; + uint16 addr = state._dirAddress; + if(!v.konDelay) addr += 2; uint8 lo = smp.apuram[(uint16)(addr + 0)]; uint8 hi = smp.apuram[(uint16)(addr + 1)]; - state.t_brr_next_addr = ((hi << 8) + lo); + state._brrNextAddress = ((hi << 8) + lo); - state.t_adsr0 = VREG(adsr0); + state._adsr0 = VREG(ADSR0); //read pitch, spread over two clocks - state.t_pitch = VREG(pitchl); + state._pitch = VREG(PITCHL); } -void DSP::voice_3(voice_t& v) { - voice_3a(v); - voice_3b(v); - voice_3c(v); +auto DSP::voice3(Voice& v) -> void { + voice3a(v); + voice3b(v); + voice3c(v); } -void DSP::voice_3a(voice_t& v) { - state.t_pitch += (VREG(pitchh) & 0x3f) << 8; +auto DSP::voice3a(Voice& v) -> void { + state._pitch += (VREG(PITCHH) & 0x3f) << 8; } -void DSP::voice_3b(voice_t& v) { - state.t_brr_byte = smp.apuram[(uint16)(v.brr_addr + v.brr_offset)]; - state.t_brr_header = smp.apuram[(uint16)(v.brr_addr)]; +auto DSP::voice3b(Voice& v) -> void { + state._brrByte = smp.apuram[(uint16)(v.brrAddress + v.brrOffset)]; + state._brrHeader = smp.apuram[(uint16)(v.brrAddress)]; } -void DSP::voice_3c(voice_t& v) { +auto DSP::voice3c(Voice& v) -> void { //pitch modulation using previous voice's output - if(state.t_pmon & v.vbit) { - state.t_pitch += ((state.t_output >> 5) * state.t_pitch) >> 10; + if(state._pmon & v.vbit) { + state._pitch += ((state._output >> 5) * state._pitch) >> 10; } - if(v.kon_delay) { + if(v.konDelay) { //get ready to start BRR decoding on next sample - if(v.kon_delay == 5) { - v.brr_addr = state.t_brr_next_addr; - v.brr_offset = 1; - v.buf_pos = 0; - state.t_brr_header = 0; //header is ignored on this sample + if(v.konDelay == 5) { + v.brrAddress = state._brrNextAddress; + v.brrOffset = 1; + v.bufferOffset = 0; + state._brrHeader = 0; //header is ignored on this sample } //envelope is never run during KON - v.env = 0; - v.hidden_env = 0; + v.envelope = 0; + v.hiddenEnvelope = 0; //disable BRR decoding until last three samples - v.interp_pos = 0; - v.kon_delay--; - if(v.kon_delay & 3) v.interp_pos = 0x4000; + v.gaussianOffset = 0; + v.konDelay--; + if(v.konDelay & 3) v.gaussianOffset = 0x4000; //pitch is never added during KON - state.t_pitch = 0; + state._pitch = 0; } //gaussian interpolation - int output = gaussian_interpolate(v); + signed output = gaussianInterpolate(v); //noise - if(state.t_non & v.vbit) { + if(state._non & v.vbit) { output = (int16)(state.noise << 1); } //apply envelope - state.t_output = ((output * v.env) >> 11) & ~1; - v.t_envx_out = v.env >> 4; + state._output = ((output * v.envelope) >> 11) & ~1; + v._envxOut = v.envelope >> 4; //immediate silence due to end of sample or soft reset - if(REG(flg) & 0x80 || (state.t_brr_header & 3) == 1) { - v.env_mode = env_release; - v.env = 0; + if(REG(FLG) & 0x80 || (state._brrHeader & 3) == 1) { + v.envelopeMode = EnvelopeRelease; + v.envelope = 0; } - if(state.every_other_sample) { + if(state.everyOtherSample) { //KOFF - if(state.t_koff & v.vbit) { - v.env_mode = env_release; + if(state._koff & v.vbit) { + v.envelopeMode = EnvelopeRelease; } //KON if(state.kon & v.vbit) { - v.kon_delay = 5; - v.env_mode = env_attack; + v.konDelay = 5; + v.envelopeMode = EnvelopeAttack; } } //run envelope for next sample - if(!v.kon_delay) envelope_run(v); + if(!v.konDelay) envelopeRun(v); } -void DSP::voice_4(voice_t& v) { +auto DSP::voice4(Voice& v) -> void { //decode BRR - state.t_looped = 0; - if(v.interp_pos >= 0x4000) { - brr_decode(v); - v.brr_offset += 2; - if(v.brr_offset >= 9) { + state._looped = 0; + if(v.gaussianOffset >= 0x4000) { + brrDecode(v); + v.brrOffset += 2; + if(v.brrOffset >= 9) { //start decoding next BRR block - v.brr_addr = (uint16)(v.brr_addr + 9); - if(state.t_brr_header & 1) { - v.brr_addr = state.t_brr_next_addr; - state.t_looped = v.vbit; + v.brrAddress = (uint16)(v.brrAddress + 9); + if(state._brrHeader & 1) { + v.brrAddress = state._brrNextAddress; + state._looped = v.vbit; } - v.brr_offset = 1; + v.brrOffset = 1; } } //apply pitch - v.interp_pos = (v.interp_pos & 0x3fff) + state.t_pitch; + v.gaussianOffset = (v.gaussianOffset & 0x3fff) + state._pitch; //keep from getting too far ahead (when using pitch modulation) - if(v.interp_pos > 0x7fff) v.interp_pos = 0x7fff; + if(v.gaussianOffset > 0x7fff) v.gaussianOffset = 0x7fff; //output left - voice_output(v, 0); + voiceOutput(v, 0); } -void DSP::voice_5(voice_t& v) { +auto DSP::voice5(Voice& v) -> void { //output right - voice_output(v, 1); + voiceOutput(v, 1); //ENDX, OUTX and ENVX won't update if you wrote to them 1-2 clocks earlier - state.endx_buf = REG(endx) | state.t_looped; + state.endxBuffer = REG(ENDX) | state._looped; //clear bit in ENDX if KON just began - if(v.kon_delay == 5) state.endx_buf &= ~v.vbit; + if(v.konDelay == 5) state.endxBuffer &= ~v.vbit; } -void DSP::voice_6(voice_t& v) { - state.outx_buf = state.t_output >> 8; +auto DSP::voice6(Voice& v) -> void { + state.outxBuffer = state._output >> 8; } -void DSP::voice_7(voice_t& v) { +auto DSP::voice7(Voice& v) -> void { //update ENDX - REG(endx) = (uint8)state.endx_buf; - state.envx_buf = v.t_envx_out; + REG(ENDX) = (uint8)state.endxBuffer; + state.envxBuffer = v._envxOut; } -void DSP::voice_8(voice_t& v) { +auto DSP::voice8(Voice& v) -> void { //update OUTX - VREG(outx) = (uint8)state.outx_buf; + VREG(OUTX) = (uint8)state.outxBuffer; } -void DSP::voice_9(voice_t& v) { +auto DSP::voice9(Voice& v) -> void { //update ENVX - VREG(envx) = (uint8)state.envx_buf; + VREG(ENVX) = (uint8)state.envxBuffer; } #endif diff --git a/sfc/sfc.hpp b/sfc/sfc.hpp index 6ca060fa..1a5fdc5d 100644 --- a/sfc/sfc.hpp +++ b/sfc/sfc.hpp @@ -12,7 +12,7 @@ namespace SuperFamicom { namespace Info { static const string Name = "bsnes"; - static const unsigned SerializerVersion = 28; + static const unsigned SerializerVersion = 29; } } @@ -32,7 +32,7 @@ namespace SuperFamicom { if(thread) co_delete(thread); } - auto create(void (*entrypoint)(), unsigned frequency) -> void { + auto create(auto (*entrypoint)() -> void, unsigned frequency) -> void { if(thread) co_delete(thread); thread = co_create(65536 * sizeof(void*), entrypoint); this->frequency = frequency;