From c13ae98863c1ae58165626fe524056b8dbf17ab3 Mon Sep 17 00:00:00 2001 From: byuu Date: Mon, 15 Dec 2008 16:19:04 +0000 Subject: [PATCH] Update to bsnes v038 release. - eliminated S-DD1 DMA enslavement to the S-CPU; this allows the S-DD1 to behave more like the real chip, and it also simplifies the S-CPU DMA module - eliminated S-PPU enslavement to the S-CPU; all processor cores now run independently of each other - added cycle-level S-PPU timing for OAM address reset and OBSEL; fixes scanline glitches in Mega Lo Mania and Winter Olympics - removed ppu.hack.* settings; as they are no longer needed due to above changes - corrected VRAM tiledata cache bug; fixes Super Buster Bros v1.0 reset glitch - added memory export and trace logging key bindings to user interface - removed WAV logging (to trim the emulation core) - embedded readme and license texts inside executable - simplified S-CPU, S-SMP flag register handling - source code cleanup for S-CPU timing module - GUI-Linux: added style improvements to the listbox and combo box controls - GUI-Linux: finally added filetype filter support to the file open dialog - GUI-all: shrunk configuration panel [FitzRoy] - GUI-all: modified paths panel descriptions for clarity [FitzRoy] --- license.txt | 84 -- readme.txt | 108 --- src/Makefile | 12 +- src/{base.h => base.hpp} | 22 +- src/cart/cart.cpp | 2 +- src/cart/{cart.h => cart.hpp} | 22 +- src/cart/cart_file.cpp | 49 +- src/cart/cart_header.cpp | 104 +-- src/cheat/cheat.cpp | 2 +- src/cheat/{cheat.h => cheat.hpp} | 0 src/chip/bsx/bsx.cpp | 2 +- src/chip/bsx/{bsx.h => bsx.hpp} | 0 src/chip/chip.h | 11 - src/chip/chip.hpp | 11 + src/chip/cx4/cx4.cpp | 2 +- src/chip/cx4/{cx4.h => cx4.hpp} | 0 src/chip/dsp1/dsp1.cpp | 2 +- src/chip/dsp1/{dsp1.h => dsp1.hpp} | 2 +- src/chip/dsp1/{dsp1emu.h => dsp1emu.hpp} | 0 src/chip/dsp2/dsp2.cpp | 2 +- src/chip/dsp2/{dsp2.h => dsp2.hpp} | 0 src/chip/dsp3/dsp3.cpp | 2 +- src/chip/dsp3/{dsp3.h => dsp3.hpp} | 0 src/chip/dsp4/dsp4.cpp | 2 +- src/chip/dsp4/{dsp4.h => dsp4.hpp} | 0 src/chip/obc1/obc1.cpp | 2 +- src/chip/obc1/{obc1.h => obc1.hpp} | 0 src/chip/sdd1/sdd1.cpp | 188 ++-- src/chip/sdd1/sdd1.h | 39 - src/chip/sdd1/sdd1.hpp | 40 + src/chip/sdd1/sdd1emu.cpp | 2 +- src/chip/sdd1/{sdd1emu.h => sdd1emu.hpp} | 0 src/chip/spc7110/{decomp.h => decomp.hpp} | 0 src/chip/spc7110/spc7110.cpp | 2 +- src/chip/spc7110/{spc7110.h => spc7110.hpp} | 2 +- src/chip/srtc/srtc.cpp | 2 +- src/chip/srtc/{srtc.h => srtc.hpp} | 0 src/chip/st010/st010.cpp | 4 +- src/chip/st010/{st010.h => st010.hpp} | 0 .../st010/{st010_data.h => st010_data.hpp} | 0 src/config/config.cpp | 133 ++- src/config/config.h | 50 -- src/config/config.hpp | 34 + src/cpu/cpu.cpp | 2 +- src/cpu/{cpu.h => cpu.hpp} | 20 +- src/cpu/cpuregs.h | 88 -- src/cpu/cpuregs.hpp | 74 ++ src/cpu/dcpu.cpp | 4 +- src/cpu/scpu/core/core.cpp | 9 +- src/cpu/scpu/core/{core.h => core.hpp} | 2 +- src/cpu/scpu/dma/dma.cpp | 74 +- src/cpu/scpu/dma/{dma.h => dma.hpp} | 3 - src/cpu/scpu/memory/{memory.h => memory.hpp} | 0 src/cpu/scpu/mmio/mmio.cpp | 6 +- src/cpu/scpu/mmio/{mmio.h => mmio.hpp} | 0 src/cpu/scpu/scpu.cpp | 2 +- src/cpu/scpu/{scpu.h => scpu.hpp} | 17 +- src/cpu/scpu/timing/irq.cpp | 50 +- src/cpu/scpu/timing/joypad.cpp | 2 +- src/cpu/scpu/timing/timing.cpp | 116 +-- src/cpu/scpu/timing/timing.h | 57 -- src/cpu/scpu/timing/timing.hpp | 27 + src/dsp/adsp/adsp.cpp | 2 +- src/dsp/adsp/{adsp.h => adsp.hpp} | 0 src/dsp/bdsp/bdsp.cpp | 681 -------------- src/dsp/bdsp/bdsp.h | 172 ---- src/dsp/bdsp/reference/bdsp.cpp | 32 - src/dsp/bdsp/reference/bdsp.h | 10 - src/dsp/bdsp/reference/readme.txt | 64 -- src/dsp/bdsp/reference/spc_dsp.cpp | 828 ------------------ src/dsp/bdsp/reference/spc_dsp.h | 162 ---- src/dsp/bdsp/reference/spc_dsp_timing.h | 46 - src/dsp/bdsp/reference/spc_endian.h | 43 - src/dsp/{dsp.h => dsp.hpp} | 0 src/dsp/sdsp/sdsp.cpp | 3 +- src/dsp/sdsp/{sdsp.h => sdsp.hpp} | 0 src/interface.h | 39 - src/interface.hpp | 29 + src/lib/bbase.h | 79 -- src/lib/hiro/gtk/editbox.cpp | 8 +- src/lib/hiro/gtk/hiro.cpp | 51 ++ src/lib/hiro/gtk/listbox.cpp | 22 +- src/lib/hiro/gtk/listbox.hpp | 9 +- src/lib/hiro/hiro.hpp | 2 +- src/lib/hiro/win/editbox.cpp | 4 +- .../{colortable.h => colortable.hpp} | 0 src/lib/libfilter/{direct.h => direct.hpp} | 0 src/lib/libfilter/{filter.h => filter.hpp} | 0 src/lib/libfilter/hq2x.cpp | 2 +- src/lib/libfilter/{hq2x.h => hq2x.hpp} | 0 .../{hq2x_table.h => hq2x_table.hpp} | 0 src/lib/libfilter/libfilter.cpp | 2 +- src/lib/libfilter/libfilter.hpp | 18 +- src/lib/libfilter/ntsc.cpp | 4 +- src/lib/libfilter/{ntsc.h => ntsc.hpp} | 0 src/lib/libfilter/{scale2x.h => scale2x.hpp} | 0 .../libfilter/{scanline.h => scanline.hpp} | 0 src/lib/nall/array.hpp | 56 +- src/lib/nall/dictionary.hpp | 2 + src/lib/nall/platform.hpp | 73 ++ src/lib/nall/sort.hpp | 2 + src/lib/nall/vector.hpp | 251 ++---- src/lib/ruby/audio/pulseaudio.cpp | 121 +++ src/lib/ruby/audio/pulseaudio.hpp | 23 + src/lib/ruby/input/sdl.cpp | 3 +- src/lib/ruby/ruby.cpp | 16 +- src/lib/ruby/ruby_impl.cpp | 4 + src/lib/ruby/video/wgl.cpp | 2 +- src/lib/sync.sh | 5 + src/license.hpp | 87 ++ src/memory/memory.cpp | 6 +- src/memory/{memory.h => memory.hpp} | 1 + src/memory/smemory/smemory.cpp | 2 +- src/memory/smemory/{smemory.h => smemory.hpp} | 0 src/ppu/bppu/bppu.cpp | 98 ++- src/ppu/bppu/{bppu.h => bppu.hpp} | 16 +- src/ppu/bppu/bppu_mmio.cpp | 215 ++--- src/ppu/bppu/bppu_render.cpp | 51 +- .../bppu/{bppu_render.h => bppu_render.hpp} | 9 +- src/ppu/bppu/bppu_render_addsub.cpp | 2 +- src/ppu/bppu/bppu_render_bg.cpp | 8 +- src/ppu/bppu/bppu_render_cache.cpp | 187 ++-- src/ppu/bppu/bppu_render_line.cpp | 41 +- src/ppu/bppu/bppu_render_mode7.cpp | 2 +- src/ppu/bppu/bppu_render_oam.cpp | 114 +-- src/ppu/bppu/bppu_render_windows.cpp | 79 +- src/ppu/counter.cpp | 50 ++ src/ppu/counter.hpp | 72 ++ src/ppu/ppu.cpp | 5 +- src/ppu/{ppu.h => ppu.hpp} | 7 +- src/reader/filereader.cpp | 2 +- src/reader/{filereader.h => filereader.hpp} | 0 src/reader/gzreader.cpp | 2 +- src/reader/{gzreader.h => gzreader.hpp} | 0 src/reader/jmareader.cpp | 2 +- src/reader/{jmareader.h => jmareader.hpp} | 0 src/reader/reader.cpp | 2 +- src/reader/{reader.h => reader.hpp} | 0 src/reader/zipreader.cpp | 2 +- src/reader/{zipreader.h => zipreader.hpp} | 0 src/readme.hpp | 80 ++ src/smp/iplrom.h | 41 - src/smp/iplrom.hpp | 40 + src/smp/smp.cpp | 4 +- src/smp/{smp.h => smp.hpp} | 11 +- src/smp/smpregs.h | 42 - src/smp/smpregs.hpp | 31 + src/smp/ssmp/core/{core.h => core.hpp} | 0 src/smp/ssmp/memory/{memory.h => memory.hpp} | 0 src/smp/ssmp/ssmp.cpp | 2 +- src/smp/ssmp/{ssmp.h => ssmp.hpp} | 6 +- src/smp/ssmp/timing/{timing.h => timing.hpp} | 0 src/snes/audio/audio.cpp | 128 +-- src/snes/audio/audio.h | 16 - src/snes/audio/audio.hpp | 7 + src/snes/input/input.cpp | 2 +- src/snes/input/{input.h => input.hpp} | 4 +- .../interface/{interface.h => interface.hpp} | 0 src/snes/scheduler/scheduler.cpp | 13 +- .../scheduler/{scheduler.h => scheduler.hpp} | 52 +- src/snes/snes.cpp | 16 +- src/snes/{snes.h => snes.hpp} | 12 +- src/snes/tracer/tracer.cpp | 3 +- src/snes/tracer/{tracer.h => tracer.hpp} | 4 - src/snes/video/video.cpp | 6 +- src/snes/video/{video.h => video.hpp} | 0 src/ui/base/{about.h => about.hpp} | 0 src/ui/base/main.cpp | 30 +- src/ui/base/{main.h => main.hpp} | 9 +- src/ui/base/textview.cpp | 17 + src/ui/base/textview.hpp | 8 + src/ui/config.cpp | 63 +- src/ui/event/debugger.cpp | 70 ++ src/ui/event/debugger.hpp | 5 + src/ui/{ => event}/event.cpp | 17 +- src/ui/{event.h => event/event.hpp} | 2 + src/ui/inputmanager.cpp | 1 + src/ui/inputui.cpp | 21 + src/ui/loader/{bsxloader.h => bsxloader.hpp} | 0 src/ui/loader/{stloader.h => stloader.hpp} | 0 src/ui/main.cpp | 53 +- src/ui/{main.h => main.hpp} | 0 src/ui/settings/advanced.cpp | 20 +- src/ui/settings/{advanced.h => advanced.hpp} | 0 src/ui/settings/audiosettings.cpp | 42 +- .../{audiosettings.h => audiosettings.hpp} | 0 src/ui/settings/cheateditor.cpp | 24 +- .../{cheateditor.h => cheateditor.hpp} | 0 src/ui/settings/driverselect.cpp | 58 +- .../{driverselect.h => driverselect.hpp} | 0 src/ui/settings/inputconfig.cpp | 36 +- .../{inputconfig.h => inputconfig.hpp} | 0 src/ui/settings/inputconfig.txt | 360 -------- src/ui/settings/pathsettings.cpp | 172 ++-- .../{pathsettings.h => pathsettings.hpp} | 43 +- src/ui/settings/settings.cpp | 18 +- src/ui/settings/{settings.h => settings.hpp} | 0 src/ui/settings/videosettings.cpp | 32 +- .../{videosettings.h => videosettings.hpp} | 0 src/ui/{status.h => status.hpp} | 0 src/ui/ui.cpp | 2 + src/ui/ui.h | 20 - src/ui/ui.hpp | 24 + 203 files changed, 2424 insertions(+), 4667 deletions(-) delete mode 100644 license.txt delete mode 100644 readme.txt rename src/{base.h => base.hpp} (77%) rename src/cart/{cart.h => cart.hpp} (89%) rename src/cheat/{cheat.h => cheat.hpp} (100%) rename src/chip/bsx/{bsx.h => bsx.hpp} (100%) delete mode 100644 src/chip/chip.h create mode 100644 src/chip/chip.hpp rename src/chip/cx4/{cx4.h => cx4.hpp} (100%) rename src/chip/dsp1/{dsp1.h => dsp1.hpp} (85%) rename src/chip/dsp1/{dsp1emu.h => dsp1emu.hpp} (100%) rename src/chip/dsp2/{dsp2.h => dsp2.hpp} (100%) rename src/chip/dsp3/{dsp3.h => dsp3.hpp} (100%) rename src/chip/dsp4/{dsp4.h => dsp4.hpp} (100%) rename src/chip/obc1/{obc1.h => obc1.hpp} (100%) delete mode 100644 src/chip/sdd1/sdd1.h create mode 100644 src/chip/sdd1/sdd1.hpp rename src/chip/sdd1/{sdd1emu.h => sdd1emu.hpp} (100%) rename src/chip/spc7110/{decomp.h => decomp.hpp} (100%) rename src/chip/spc7110/{spc7110.h => spc7110.hpp} (99%) rename src/chip/srtc/{srtc.h => srtc.hpp} (100%) rename src/chip/st010/{st010.h => st010.hpp} (100%) rename src/chip/st010/{st010_data.h => st010_data.hpp} (100%) delete mode 100644 src/config/config.h create mode 100644 src/config/config.hpp rename src/cpu/{cpu.h => cpu.hpp} (83%) delete mode 100644 src/cpu/cpuregs.h create mode 100644 src/cpu/cpuregs.hpp rename src/cpu/scpu/core/{core.h => core.hpp} (93%) rename src/cpu/scpu/dma/{dma.h => dma.hpp} (85%) rename src/cpu/scpu/memory/{memory.h => memory.hpp} (100%) rename src/cpu/scpu/mmio/{mmio.h => mmio.hpp} (100%) rename src/cpu/scpu/{scpu.h => scpu.hpp} (83%) delete mode 100644 src/cpu/scpu/timing/timing.h create mode 100644 src/cpu/scpu/timing/timing.hpp rename src/dsp/adsp/{adsp.h => adsp.hpp} (100%) delete mode 100644 src/dsp/bdsp/bdsp.cpp delete mode 100644 src/dsp/bdsp/bdsp.h delete mode 100644 src/dsp/bdsp/reference/bdsp.cpp delete mode 100644 src/dsp/bdsp/reference/bdsp.h delete mode 100644 src/dsp/bdsp/reference/readme.txt delete mode 100644 src/dsp/bdsp/reference/spc_dsp.cpp delete mode 100644 src/dsp/bdsp/reference/spc_dsp.h delete mode 100644 src/dsp/bdsp/reference/spc_dsp_timing.h delete mode 100644 src/dsp/bdsp/reference/spc_endian.h rename src/dsp/{dsp.h => dsp.hpp} (100%) rename src/dsp/sdsp/{sdsp.h => sdsp.hpp} (100%) delete mode 100644 src/interface.h create mode 100644 src/interface.hpp delete mode 100644 src/lib/bbase.h rename src/lib/libfilter/{colortable.h => colortable.hpp} (100%) rename src/lib/libfilter/{direct.h => direct.hpp} (100%) rename src/lib/libfilter/{filter.h => filter.hpp} (100%) rename src/lib/libfilter/{hq2x.h => hq2x.hpp} (100%) rename src/lib/libfilter/{hq2x_table.h => hq2x_table.hpp} (100%) rename src/lib/libfilter/{ntsc.h => ntsc.hpp} (100%) rename src/lib/libfilter/{scale2x.h => scale2x.hpp} (100%) rename src/lib/libfilter/{scanline.h => scanline.hpp} (100%) create mode 100644 src/lib/nall/platform.hpp create mode 100644 src/lib/ruby/audio/pulseaudio.cpp create mode 100644 src/lib/ruby/audio/pulseaudio.hpp create mode 100644 src/license.hpp rename src/memory/{memory.h => memory.hpp} (95%) rename src/memory/smemory/{smemory.h => smemory.hpp} (100%) rename src/ppu/bppu/{bppu.h => bppu.hpp} (93%) rename src/ppu/bppu/{bppu_render.h => bppu_render.hpp} (91%) create mode 100644 src/ppu/counter.cpp create mode 100644 src/ppu/counter.hpp rename src/ppu/{ppu.h => ppu.hpp} (85%) rename src/reader/{filereader.h => filereader.hpp} (100%) rename src/reader/{gzreader.h => gzreader.hpp} (100%) rename src/reader/{jmareader.h => jmareader.hpp} (100%) rename src/reader/{reader.h => reader.hpp} (100%) rename src/reader/{zipreader.h => zipreader.hpp} (100%) create mode 100644 src/readme.hpp delete mode 100644 src/smp/iplrom.h create mode 100644 src/smp/iplrom.hpp rename src/smp/{smp.h => smp.hpp} (67%) delete mode 100644 src/smp/smpregs.h create mode 100644 src/smp/smpregs.hpp rename src/smp/ssmp/core/{core.h => core.hpp} (100%) rename src/smp/ssmp/memory/{memory.h => memory.hpp} (100%) rename src/smp/ssmp/{ssmp.h => ssmp.hpp} (78%) rename src/smp/ssmp/timing/{timing.h => timing.hpp} (100%) delete mode 100644 src/snes/audio/audio.h create mode 100644 src/snes/audio/audio.hpp rename src/snes/input/{input.h => input.hpp} (95%) rename src/snes/interface/{interface.h => interface.hpp} (100%) rename src/snes/scheduler/{scheduler.h => scheduler.hpp} (60%) rename src/snes/{snes.h => snes.hpp} (75%) rename src/snes/tracer/{tracer.h => tracer.hpp} (82%) rename src/snes/video/{video.h => video.hpp} (100%) rename src/ui/base/{about.h => about.hpp} (100%) rename src/ui/base/{main.h => main.hpp} (94%) create mode 100644 src/ui/base/textview.cpp create mode 100644 src/ui/base/textview.hpp create mode 100644 src/ui/event/debugger.cpp create mode 100644 src/ui/event/debugger.hpp rename src/ui/{ => event}/event.cpp (96%) rename src/ui/{event.h => event/event.hpp} (97%) rename src/ui/loader/{bsxloader.h => bsxloader.hpp} (100%) rename src/ui/loader/{stloader.h => stloader.hpp} (100%) rename src/ui/{main.h => main.hpp} (100%) rename src/ui/settings/{advanced.h => advanced.hpp} (100%) rename src/ui/settings/{audiosettings.h => audiosettings.hpp} (100%) rename src/ui/settings/{cheateditor.h => cheateditor.hpp} (100%) rename src/ui/settings/{driverselect.h => driverselect.hpp} (100%) rename src/ui/settings/{inputconfig.h => inputconfig.hpp} (100%) delete mode 100644 src/ui/settings/inputconfig.txt rename src/ui/settings/{pathsettings.h => pathsettings.hpp} (51%) rename src/ui/settings/{settings.h => settings.hpp} (100%) rename src/ui/settings/{videosettings.h => videosettings.hpp} (100%) rename src/ui/{status.h => status.hpp} (100%) delete mode 100644 src/ui/ui.h create mode 100644 src/ui/ui.hpp diff --git a/license.txt b/license.txt deleted file mode 100644 index 8bfe339e..00000000 --- a/license.txt +++ /dev/null @@ -1,84 +0,0 @@ -bsnes (TM) Reference License -Copyright (C) 2004 - 2008 byuu -All rights reserved - -1. Definitions - -The terms "reproduce", "reproduction", "distribute" and "distribution" have the -same meaning here as under U.S. copyright law. - -"The software" means this software package as a whole, including, but not -limited to, this license, binaries, source code, documentation, and data. - -"You" means the licensee of the software. - -"The licensor" means the copyright holder of the software, byuu. - -2. Grant of Rights - -Subject to the terms of this license, the licensor grants you a -non-transferable, non-exclusive, worldwide, royalty-free copyright license to -reproduce the software for non-commercial use only, provided the software -remains unmodified, and there is no charge for the software itself, its' use, -nor for the medium upon which the software is distributed. The reproduction of -modified or derivative works of the software is strictly prohibited, except when -transmitted solely to the licensor. - -3. Limitations - -This license does not grant you any rights to use the licensor's name, logo or -trademarks. - -The software is provided "as is", and any express or implied warranties, -including, but not limited to, the implied warranties of merchantability and -fitness for a particular purpose are disclaimed. In no event shall the licensor -be liable for any direct, indirect, incidental, special, exemplary, or -consequential damages (including, but not limited to, procurement of substitute -goods or services; loss of use, data, or profits; or business interruption) -however caused and on any theory of liability, whether in contract, strict -liability, or tort (including negligence or otherwise) arising in any way out of -the use of the software, even if advised of the possibility of such damage. - -In the event that this license is determined to be invalid or unenforceable, the -Grant of Rights will become null and void, and no rights shall be granted to the -licensee, within the scope of U.S. copyright law. - -4. Exemptions - -The software includes the work of other copyright holders, which is licensed -under different agreements, and exempt from this license. Below is a complete -list of all such software, and their respective copyright holders and licenses. -Further, respective source code files are labeled with their correct licensing -information in the header. The lack of such a header indicates said file falls -under the bsnes license. - -HQ2x filter, author: MaxST, license: LGPL -JMA decompressor, author: NSRT Team, license: GPL* -NTSC filter, author: blargg, license: LGPL -zlib decompressor, license: zlib license - -(* bsnes has received an exemption from the copyright holder to use this work.) - -The software also includes works which have been released to the public domain, -which are not bound to any licensing agreements. Below is a complete list of all -such software. - -libco, author: byuu -S-DD1 decompressor, author: Andreas Naive -SPC7110 decompressor, author: neviksti - -Any software listed above as exemptions may be relicensed individually from -bsnes under their respective terms. However, no bsnes licensed portions can be -combined with such a derivative work. - -The software also includes the work of other copyright holders, which is -licensed under the terms of the bsnes license, with permission to do so from the -respective authors. Below is a complete list of all such software. - -Cx4 emu, authors: anomie, Overload, zsKnight, Nach -DSP-1 emu, authors: Overload, John Weidman, Neviksti, Andreas Naive -DSP-2 emu, author: Overload -DSP-3 emu, authors: John Weidman, Kris Bleakley, Lancer, z80 gaiden -DSP-4 emu, authors: Dreamer Nom, John Weidman, Kris Bleakley, Nach, z80 gaiden -S-DSP emu, author: blargg -ST-010 emu, authors: John Weidman, Matthew Kendora, Overload, Feather diff --git a/readme.txt b/readme.txt deleted file mode 100644 index c8bdc98b..00000000 --- a/readme.txt +++ /dev/null @@ -1,108 +0,0 @@ -bsnes -Version: 0.037a -Author: byuu - -======== -General: -======== - -bsnes is a Super Nintendo / Super Famicom emulator that began on -October 14th, 2004. - -The latest version can be downloaded from: -http://byuu.org/ - -Please see license.txt for important licensing information. - -============== -Configuration: -============== - -bsnes has two configuration files: bsnes.cfg, for program settings; and -locale.cfg, for localization. - -For each file, bsnes will start by looking inside the same folder where the -bsnes executable is located. If said file is not found, it will then check your -user profile folder. On Windows, this is located at "%APPDATA%/.bsnes". On all -other operating systems, this is located at "~/.bsnes". If said file is still -not found, it will automatically be created in your user profile folder. - -If you wish to use bsnes in single-user mode, be sure that both files exist -inside the same folder as the bsnes executable. If they do not, you can simply -create new blank files and bsnes will use them in the future. - -If you wish to use bsnes in multi-user mode, simply delete these two files from -the bsnes executable directory if they exist. - -If you wish to have multiple configuration profiles for the same user, you will -need to make copies of the bsnes executable, and use each one in single-user -mode. - -==================== -Known Limitation(s): -==================== - -S-CPU -- Multiply / divide register delays not implemented -- "Glitch" when reading joypad registers during auto polling not implemented - -S-PPU -- Uses scanline-based renderer. This is very inaccurate, but few (if any) - games rely on mid-scanline writes to function correctly -- Does not support FirstSprite+Y priority -- OAM / CGRAM accesses during active display not supported correctly -- RTO flags are not calculated on frames that are skipped when frameskipping - is enabled. This provides a major speedup, however it will cause in issues - in games that test these flags, eg the SNES Test Program Electronics Test. - Turning frameskipping off will allow RTO flag calculation on every frame - -Hardware Bugs -- S-CPU.r1 HDMA crashing bug not emulated -- S-CPU<>S-SMP communication bus conflicts not emulated - -=============== -Known Issue(s): -=============== - -On Windows, attempting to load a ZIP, GZ or JMA compressed archive with -non-ANSI characters in the filename will fail. This is because Windows -requires UTF-16 encoding, but these libraries only work with UTF-8. -Note that loading uncompressed images (SMC, SFC, etc) with non-ANSI characters -works properly on all platforms. - -===================== -Unsupported Hardware: -===================== - -SA-1 -Coprocessor used in many popular games, including: -- Dragon Ball Z Hyper Dimension -- Kirby Super Star -- Kirby's Dreamland 3 -- Marvelous -- SD Gundam G-NEXT -- Super Mario RPG - -Super FX -Coprocessor used in many popular games, including: -- Doom -- Star Fox -- Star Fox 2 (unreleased beta) -- Super Mario World 2: Yoshi's Island - -ST-011 -SETA DSP used by Quick-move Shogi Match with Nidan Rank-holder Morita - -ST-018 -SETA RISC CPU used by Quick-move Shogi Match with Nidan Rank-holder Morita 2 - -Super Gameboy -Cartridge passthrough used for playing Gameboy games - -============= -Contributors: -============= - -Andreas Naive, anomie, blargg, DMV27, FitzRoy, GIGO, Jonas Quinn, kode54, krom, -Matthew Callis, mudlord, Nach, neviksti, Overload, RedDwarf, Richard Bannister, -tetsuo55, TRAC, zones diff --git a/src/Makefile b/src/Makefile index ad322f3e..0e964a61 100644 --- a/src/Makefile +++ b/src/Makefile @@ -6,7 +6,7 @@ prefix = /usr/local ################ ifneq ($(findstring gcc,$(compiler)),) # GCC family - flags = -O3 -fomit-frame-pointer -Ilib + flags = -O3 -fomit-frame-pointer $(if $(call streq,$(platform),x),-mtune=native,) -Ilib c = $(compiler) $(flags) cpp = $(subst cc,++,$(compiler)) $(flags) obj = o @@ -34,7 +34,7 @@ endif ########## ifeq ($(platform),x) # X11 - ruby = video.glx video.xv video.sdl audio.openal audio.oss audio.alsa audio.ao input.sdl input.x + ruby = video.glx video.xv video.sdl audio.alsa audio.openal audio.oss audio.pulseaudio audio.ao input.sdl input.x link += `pkg-config --libs gtk+-2.0` link += $(call mklib,Xtst) delete = rm -f $1 @@ -70,6 +70,7 @@ link += $(if $(findstring audio.alsa,$(ruby)),$(call mklib,asound)) link += $(if $(findstring audio.ao,$(ruby)),$(call mklib,ao)) link += $(if $(findstring audio.directsound,$(ruby)),$(call mklib,dsound)) link += $(if $(findstring audio.openal,$(ruby)),$(if $(call streq,$(platform),x),$(call mklib,openal),$(call mklib,openal32))) +link += $(if $(findstring audio.pulseaudio,$(ruby)),$(call mklib,pulse-simple)) link += $(if $(findstring input.directinput,$(ruby)),$(call mklib,dinput8) $(call mklib,dxguid)) link += $(if $(findstring input.sdl,$(ruby)),`sdl-config --libs`) @@ -77,7 +78,8 @@ link += $(if $(findstring input.sdl,$(ruby)),`sdl-config --libs`) ### main target and dependencies ### #################################### -objects = main libco hiro ruby libfilter string reader cart cheat \ +objects = main libco hiro ruby libfilter string \ + config reader cart cheat \ memory smemory cpu scpu smp ssmp sdsp ppu bppu snes \ bsx srtc sdd1 spc7110 cx4 dsp1 dsp2 dsp3 dsp4 obc1 st010 @@ -125,7 +127,7 @@ all: build; ### main ### ############ -obj/main.$(obj): ui/main.cpp ui/* ui/base/* ui/loader/* ui/settings/* +obj/main.$(obj): ui/main.cpp ui/* ui/base/* ui/event/* ui/loader/* ui/settings/* obj/bsnes.res: ui/bsnes.rc; rc /r /foobj/bsnes.res ui/bsnes.rc obj/bsnesrc.$(obj): ui/bsnes.rc; windres ui/bsnes.rc obj/bsnesrc.$(obj) @@ -146,6 +148,7 @@ obj/string.$(obj): lib/nall/string.cpp lib/nall/* ### utilities ### ################# +obj/config.$(obj): config/config.cpp config/* obj/reader.$(obj): reader/reader.cpp reader/* obj/cart.$(obj) : cart/cart.cpp cart/* obj/cheat.$(obj) : cheat/cheat.cpp cheat/* @@ -176,7 +179,6 @@ obj/ssmp.$(obj): smp/ssmp/ssmp.cpp smp/ssmp/* smp/ssmp/core/* smp/ssmp/memory/* ########### obj/adsp.$(obj): dsp/adsp/adsp.cpp dsp/adsp/* -obj/bdsp.$(obj): dsp/bdsp/bdsp.cpp dsp/bdsp/* obj/sdsp.$(obj): dsp/sdsp/sdsp.cpp dsp/sdsp/* ########### diff --git a/src/base.h b/src/base.hpp similarity index 77% rename from src/base.h rename to src/base.hpp index 170c8ba8..72e7caef 100644 --- a/src/base.h +++ b/src/base.hpp @@ -1,4 +1,4 @@ -#define BSNES_VERSION "0.037a" +#define BSNES_VERSION "0.038" #define BSNES_TITLE "bsnes v" BSNES_VERSION #define BUSCORE sBus @@ -19,6 +19,8 @@ //game genie + pro action replay code support (~2% speed hit) #define CHEAT_SYSTEM +#include + #include #include #include @@ -29,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -36,11 +39,14 @@ #include using namespace nall; -#include -#include +typedef int8_t int8; +typedef int16_t int16; +typedef int32_t int32; +typedef int64_t int64; +typedef uint8_t uint8; +typedef uint16_t uint16; +typedef uint32_t uint32; +typedef uint64_t uint64; +typedef unsigned uint; -//platform-specific global functions -void alert(const char*, ...); -void dprintf(const char*, ...); - -#include "interface.h" +#include "interface.hpp" diff --git a/src/cart/cart.cpp b/src/cart/cart.cpp index 2acf7496..e152baf2 100644 --- a/src/cart/cart.cpp +++ b/src/cart/cart.cpp @@ -1,4 +1,4 @@ -#include "../base.h" +#include <../base.hpp> #define CART_CPP #include diff --git a/src/cart/cart.h b/src/cart/cart.hpp similarity index 89% rename from src/cart/cart.h rename to src/cart/cart.hpp index 9198d675..83e35ccb 100644 --- a/src/cart/cart.h +++ b/src/cart/cart.hpp @@ -18,17 +18,17 @@ public: }; enum HeaderField { - CART_NAME = 0x00, - MAPPER = 0x15, - ROM_TYPE = 0x16, - ROM_SIZE = 0x17, - RAM_SIZE = 0x18, - REGION = 0x19, - COMPANY = 0x1a, - VERSION = 0x1b, - ICKSUM = 0x1c, - CKSUM = 0x1e, - RESETV = 0x3c, + CartName = 0x00, + Mapper = 0x15, + RomType = 0x16, + RomSize = 0x17, + RamSize = 0x18, + CartRegion = 0x19, + Company = 0x1a, + Version = 0x1b, + Complement = 0x1c, //inverse checksum + Checksum = 0x1e, + ResetVector = 0x3c, }; enum Region { diff --git a/src/cart/cart_file.cpp b/src/cart/cart_file.cpp index ae0e5e83..9ea88ffa 100644 --- a/src/cart/cart_file.cpp +++ b/src/cart/cart_file.cpp @@ -1,14 +1,14 @@ #ifdef CART_CPP -#include "../reader/filereader.h" +#include "../reader/filereader.hpp" #if defined(GZIP_SUPPORT) - #include "../reader/gzreader.h" - #include "../reader/zipreader.h" + #include "../reader/gzreader.hpp" + #include "../reader/zipreader.hpp" #endif #if defined(JMA_SUPPORT) - #include "../reader/jmareader.h" + #include "../reader/jmareader.hpp" #endif char* Cartridge::modify_extension(char *filename, const char *extension) { @@ -53,26 +53,8 @@ char* Cartridge::get_base_filename(char *filename) { char* Cartridge::get_path_filename(char *filename, const char *path, const char *source, const char *extension) { strcpy(filename, source); - for(char *p = filename; *p; p++) { if(*p == '\\') *p = '/'; } modify_extension(filename, extension); - - //override path with user-specified folder, if one was defined - if(*path) { - lstring part; - split(part, "/", filename); - string fn = path; - if(strend(fn, "/") == false) strcat(fn, "/"); - strcat(fn, part[count(part) - 1]); - strcpy(filename, fn); - - //resolve relative path, if found - if(strbegin(fn, "./") == true) { - ltrim(fn, "./"); - strcpy(filename, config::path.base); - strcat(filename, fn); - } - } - + strcpy(filename, config::filepath(filename, path)); return filename; } @@ -95,16 +77,10 @@ bool Cartridge::load_file(const char *fn, uint8 *&data, uint &size, CompressionM if(compression == CompressionInspect) filetype = Reader::detect(fn, true); if(compression == CompressionAuto) filetype = Reader::detect(fn, config::file.autodetect_type); - switch(filetype) { - default: - dprintf("* Warning: filetype detected as unsupported compression type."); - dprintf("* Will attempt to load as uncompressed file -- may fail."); + switch(filetype) { default: case Reader::Normal: { FileReader ff(fn); - if(!ff.ready()) { - alert("Error loading image file (%s)!", fn); - return false; - } + if(!ff.ready()) return false; size = ff.size(); data = ff.read(); } break; @@ -112,20 +88,14 @@ bool Cartridge::load_file(const char *fn, uint8 *&data, uint &size, CompressionM #ifdef GZIP_SUPPORT case Reader::GZIP: { GZReader gf(fn); - if(!gf.ready()) { - alert("Error loading image file (%s)!", fn); - return false; - } + if(!gf.ready()) return false; size = gf.size(); data = gf.read(); } break; case Reader::ZIP: { ZipReader zf(fn); - if(!zf.ready()) { - alert("Error loading image file (%s)!", fn); - return false; - } + if(!zf.ready()) return false; size = zf.size(); data = zf.read(); } break; @@ -138,7 +108,6 @@ bool Cartridge::load_file(const char *fn, uint8 *&data, uint &size, CompressionM size = jf.size(); data = jf.read(); } catch(JMA::jma_errors jma_error) { - alert("Error loading image file (%s)!", fn); return false; } } break; diff --git a/src/cart/cart_header.cpp b/src/cart/cart_header.cpp index b213f704..b628e3d7 100644 --- a/src/cart/cart_header.cpp +++ b/src/cart/cart_header.cpp @@ -32,11 +32,11 @@ void Cartridge::read_header(cartinfo_t &info, const uint8_t *data, unsigned size } //standard cart - uint8 mapper = data[index + MAPPER]; - uint8 rom_type = data[index + ROM_TYPE]; - uint8 rom_size = data[index + ROM_SIZE]; - uint8 company = data[index + COMPANY]; - uint8 region = data[index + REGION] & 0x7f; + uint8 mapper = data[index + Mapper]; + uint8 rom_type = data[index + RomType]; + uint8 rom_size = data[index + RomSize]; + uint8 company = data[index + Company]; + uint8 region = data[index + CartRegion] & 0x7f; //detect presence of BS-X flash cartridge connector (reads extended header information) if(data[index - 14] == 'Z') { @@ -152,8 +152,8 @@ void Cartridge::read_header(cartinfo_t &info, const uint8_t *data, unsigned size info.st018 = true; } - if(data[index + RAM_SIZE] & 7) { - info.ram_size = 1024 << (data[index + RAM_SIZE] & 7); + if(data[index + RamSize] & 7) { + info.ram_size = 1024 << (data[index + RamSize] & 7); } else { info.ram_size = 0; } @@ -166,7 +166,7 @@ unsigned Cartridge::find_header(const uint8_t *data, unsigned size) { unsigned score_lo = score_header(data, size, 0x007fc0); unsigned score_hi = score_header(data, size, 0x00ffc0); unsigned score_ex = score_header(data, size, 0x40ffc0); - if(score_ex) score_ex += 4; //favor ExHiROM on images > 32mbits + if(score_ex) score_ex += 4; //favor ExHiROM on images > 32mbits if(score_lo >= score_hi && score_lo >= score_ex) { return 0x007fc0; @@ -178,15 +178,15 @@ unsigned Cartridge::find_header(const uint8_t *data, unsigned size) { } unsigned Cartridge::score_header(const uint8_t *data, unsigned size, unsigned addr) { - if(size < addr + 64) return 0; //image too small to contain header at this location? + if(size < addr + 64) return 0; //image too small to contain header at this location? int score = 0; - uint16 resetvector = data[addr + RESETV] | (data[addr + RESETV + 1] << 8); - uint16 checksum = data[addr + CKSUM] | (data[addr + CKSUM + 1] << 8); - uint16 ichecksum = data[addr + ICKSUM] | (data[addr + ICKSUM + 1] << 8); + uint16 resetvector = data[addr + ResetVector] | (data[addr + ResetVector + 1] << 8); + uint16 checksum = data[addr + Checksum ] | (data[addr + Checksum + 1] << 8); + uint16 complement = data[addr + Complement ] | (data[addr + Complement + 1] << 8); - uint8 resetop = data[(addr & ~0x7fff) | (resetvector & 0x7fff)]; //first opcode executed upon reset - uint8 mapper = data[addr + MAPPER] & ~0x10; //mask off irrelevent FastROM-capable bit + uint8 resetop = data[(addr & ~0x7fff) | (resetvector & 0x7fff)]; //first opcode executed upon reset + uint8 mapper = data[addr + Mapper] & ~0x10; //mask off irrelevent FastROM-capable bit //$00:[000-7fff] contains uninitialized RAM and MMIO. //reset vector must point to ROM at $00:[8000-ffff] to be considered valid. @@ -198,61 +198,61 @@ unsigned Cartridge::score_header(const uint8_t *data, unsigned size, unsigned ad //determine the probability that this is the correct header. //most likely opcodes - if(resetop == 0x78 //sei - || resetop == 0x18 //clc (clc; xce) - || resetop == 0x38 //sec (sec; xce) - || resetop == 0x9c //stz $nnnn (stz $4200) - || resetop == 0x4c //jmp $nnnn - || resetop == 0x5c //jml $nnnnnn + if(resetop == 0x78 //sei + || resetop == 0x18 //clc (clc; xce) + || resetop == 0x38 //sec (sec; xce) + || resetop == 0x9c //stz $nnnn (stz $4200) + || resetop == 0x4c //jmp $nnnn + || resetop == 0x5c //jml $nnnnnn ) score += 8; //plausible opcodes - if(resetop == 0xc2 //rep #$nn - || resetop == 0xe2 //sep #$nn - || resetop == 0xad //lda $nnnn - || resetop == 0xae //ldx $nnnn - || resetop == 0xac //ldy $nnnn - || resetop == 0xaf //lda $nnnnnn - || resetop == 0xa9 //lda #$nn - || resetop == 0xa2 //ldx #$nn - || resetop == 0xa0 //ldy #$nn - || resetop == 0x20 //jsr $nnnn - || resetop == 0x22 //jsl $nnnnnn + if(resetop == 0xc2 //rep #$nn + || resetop == 0xe2 //sep #$nn + || resetop == 0xad //lda $nnnn + || resetop == 0xae //ldx $nnnn + || resetop == 0xac //ldy $nnnn + || resetop == 0xaf //lda $nnnnnn + || resetop == 0xa9 //lda #$nn + || resetop == 0xa2 //ldx #$nn + || resetop == 0xa0 //ldy #$nn + || resetop == 0x20 //jsr $nnnn + || resetop == 0x22 //jsl $nnnnnn ) score += 4; //implausible opcodes - if(resetop == 0x40 //rti - || resetop == 0x60 //rts - || resetop == 0x6b //rtl - || resetop == 0xcd //cmp $nnnn - || resetop == 0xec //cpx $nnnn - || resetop == 0xcc //cpy $nnnn + if(resetop == 0x40 //rti + || resetop == 0x60 //rts + || resetop == 0x6b //rtl + || resetop == 0xcd //cmp $nnnn + || resetop == 0xec //cpx $nnnn + || resetop == 0xcc //cpy $nnnn ) score -= 4; //least likely opcodes - if(resetop == 0x00 //brk #$nn - || resetop == 0x02 //cop #$nn - || resetop == 0xdb //stp - || resetop == 0x42 //wdm - || resetop == 0xff //sbc $nnnnnn,x + if(resetop == 0x00 //brk #$nn + || resetop == 0x02 //cop #$nn + || resetop == 0xdb //stp + || resetop == 0x42 //wdm + || resetop == 0xff //sbc $nnnnnn,x ) score -= 8; //at times, both the header and reset vector's first opcode will match ... //fallback and rely on info validity in these cases to determine more likely header. //a valid checksum is the biggest indicator of a valid header. - if((checksum + ichecksum) == 0xffff && (checksum != 0) && (ichecksum != 0)) score += 4; + if((checksum + complement) == 0xffff && (checksum != 0) && (complement != 0)) score += 4; - if(addr == 0x007fc0 && mapper == 0x20) score += 2; //0x20 is usually LoROM - if(addr == 0x00ffc0 && mapper == 0x21) score += 2; //0x21 is usually HiROM - if(addr == 0x007fc0 && mapper == 0x22) score += 2; //0x22 is usually ExLoROM - if(addr == 0x40ffc0 && mapper == 0x25) score += 2; //0x25 is usually ExHiROM + if(addr == 0x007fc0 && mapper == 0x20) score += 2; //0x20 is usually LoROM + if(addr == 0x00ffc0 && mapper == 0x21) score += 2; //0x21 is usually HiROM + if(addr == 0x007fc0 && mapper == 0x22) score += 2; //0x22 is usually ExLoROM + if(addr == 0x40ffc0 && mapper == 0x25) score += 2; //0x25 is usually ExHiROM - if(data[addr + COMPANY] == 0x33) score += 2; //0x33 indicates extended header - if(data[addr + ROM_TYPE] < 0x08) score++; - if(data[addr + ROM_SIZE] < 0x10) score++; - if(data[addr + RAM_SIZE] < 0x08) score++; - if(data[addr + REGION] < 14) score++; + if(data[addr + Company] == 0x33) score += 2; //0x33 indicates extended header + if(data[addr + RomType] < 0x08) score++; + if(data[addr + RomSize] < 0x10) score++; + if(data[addr + RamSize] < 0x08) score++; + if(data[addr + CartRegion] < 14) score++; if(score < 0) score = 0; return score; diff --git a/src/cheat/cheat.cpp b/src/cheat/cheat.cpp index 9ee8ccac..d9f838d6 100644 --- a/src/cheat/cheat.cpp +++ b/src/cheat/cheat.cpp @@ -1,4 +1,4 @@ -#include "../base.h" +#include <../base.hpp> Cheat cheat; diff --git a/src/cheat/cheat.h b/src/cheat/cheat.hpp similarity index 100% rename from src/cheat/cheat.h rename to src/cheat/cheat.hpp diff --git a/src/chip/bsx/bsx.cpp b/src/chip/bsx/bsx.cpp index 1b30ace5..c4e16325 100644 --- a/src/chip/bsx/bsx.cpp +++ b/src/chip/bsx/bsx.cpp @@ -1,4 +1,4 @@ -#include "../../base.h" +#include <../base.hpp> #define BSX_CPP #include "bsx_base.cpp" diff --git a/src/chip/bsx/bsx.h b/src/chip/bsx/bsx.hpp similarity index 100% rename from src/chip/bsx/bsx.h rename to src/chip/bsx/bsx.hpp diff --git a/src/chip/chip.h b/src/chip/chip.h deleted file mode 100644 index f3393af6..00000000 --- a/src/chip/chip.h +++ /dev/null @@ -1,11 +0,0 @@ -#include "bsx/bsx.h" -#include "srtc/srtc.h" -#include "sdd1/sdd1.h" -#include "spc7110/spc7110.h" -#include "cx4/cx4.h" -#include "dsp1/dsp1.h" -#include "dsp2/dsp2.h" -#include "dsp3/dsp3.h" -#include "dsp4/dsp4.h" -#include "obc1/obc1.h" -#include "st010/st010.h" diff --git a/src/chip/chip.hpp b/src/chip/chip.hpp new file mode 100644 index 00000000..17698626 --- /dev/null +++ b/src/chip/chip.hpp @@ -0,0 +1,11 @@ +#include "bsx/bsx.hpp" +#include "srtc/srtc.hpp" +#include "sdd1/sdd1.hpp" +#include "spc7110/spc7110.hpp" +#include "cx4/cx4.hpp" +#include "dsp1/dsp1.hpp" +#include "dsp2/dsp2.hpp" +#include "dsp3/dsp3.hpp" +#include "dsp4/dsp4.hpp" +#include "obc1/obc1.hpp" +#include "st010/st010.hpp" diff --git a/src/chip/cx4/cx4.cpp b/src/chip/cx4/cx4.cpp index a0f32358..5335e217 100644 --- a/src/chip/cx4/cx4.cpp +++ b/src/chip/cx4/cx4.cpp @@ -5,7 +5,7 @@ Portions (c) anomie, Overload, zsKnight, Nach, byuu */ -#include "../../base.h" +#include <../base.hpp> #define CX4_CPP #include "cx4data.cpp" diff --git a/src/chip/cx4/cx4.h b/src/chip/cx4/cx4.hpp similarity index 100% rename from src/chip/cx4/cx4.h rename to src/chip/cx4/cx4.hpp diff --git a/src/chip/dsp1/dsp1.cpp b/src/chip/dsp1/dsp1.cpp index aa375106..de403abf 100644 --- a/src/chip/dsp1/dsp1.cpp +++ b/src/chip/dsp1/dsp1.cpp @@ -1,4 +1,4 @@ -#include "../../base.h" +#include <../base.hpp> #define DSP1_CPP #include "dsp1emu.cpp" diff --git a/src/chip/dsp1/dsp1.h b/src/chip/dsp1/dsp1.hpp similarity index 85% rename from src/chip/dsp1/dsp1.h rename to src/chip/dsp1/dsp1.hpp index a90c30c8..95c72a4b 100644 --- a/src/chip/dsp1/dsp1.h +++ b/src/chip/dsp1/dsp1.hpp @@ -1,4 +1,4 @@ -#include "dsp1emu.h" +#include "dsp1emu.hpp" class DSP1 : public Memory { private: diff --git a/src/chip/dsp1/dsp1emu.h b/src/chip/dsp1/dsp1emu.hpp similarity index 100% rename from src/chip/dsp1/dsp1emu.h rename to src/chip/dsp1/dsp1emu.hpp diff --git a/src/chip/dsp2/dsp2.cpp b/src/chip/dsp2/dsp2.cpp index 54c0a252..2d36d657 100644 --- a/src/chip/dsp2/dsp2.cpp +++ b/src/chip/dsp2/dsp2.cpp @@ -1,4 +1,4 @@ -#include "../../base.h" +#include <../base.hpp> #define DSP2_CPP #include "dsp2_op.cpp" diff --git a/src/chip/dsp2/dsp2.h b/src/chip/dsp2/dsp2.hpp similarity index 100% rename from src/chip/dsp2/dsp2.h rename to src/chip/dsp2/dsp2.hpp diff --git a/src/chip/dsp3/dsp3.cpp b/src/chip/dsp3/dsp3.cpp index 28d7c0a9..ad88fe23 100644 --- a/src/chip/dsp3/dsp3.cpp +++ b/src/chip/dsp3/dsp3.cpp @@ -1,4 +1,4 @@ -#include "../../base.h" +#include <../base.hpp> #define DSP3_CPP namespace DSP3i { diff --git a/src/chip/dsp3/dsp3.h b/src/chip/dsp3/dsp3.hpp similarity index 100% rename from src/chip/dsp3/dsp3.h rename to src/chip/dsp3/dsp3.hpp diff --git a/src/chip/dsp4/dsp4.cpp b/src/chip/dsp4/dsp4.cpp index 3f653571..9e43f431 100644 --- a/src/chip/dsp4/dsp4.cpp +++ b/src/chip/dsp4/dsp4.cpp @@ -1,4 +1,4 @@ -#include "../../base.h" +#include <../base.hpp> #define DSP4_CPP namespace DSP4i { diff --git a/src/chip/dsp4/dsp4.h b/src/chip/dsp4/dsp4.hpp similarity index 100% rename from src/chip/dsp4/dsp4.h rename to src/chip/dsp4/dsp4.hpp diff --git a/src/chip/obc1/obc1.cpp b/src/chip/obc1/obc1.cpp index dd656aaa..eeb5a2ce 100644 --- a/src/chip/obc1/obc1.cpp +++ b/src/chip/obc1/obc1.cpp @@ -1,4 +1,4 @@ -#include "../../base.h" +#include <../base.hpp> void OBC1::init() {} void OBC1::enable() {} diff --git a/src/chip/obc1/obc1.h b/src/chip/obc1/obc1.hpp similarity index 100% rename from src/chip/obc1/obc1.h rename to src/chip/obc1/obc1.hpp diff --git a/src/chip/sdd1/sdd1.cpp b/src/chip/sdd1/sdd1.cpp index b69d022a..e845f13b 100644 --- a/src/chip/sdd1/sdd1.cpp +++ b/src/chip/sdd1/sdd1.cpp @@ -1,4 +1,4 @@ -#include "../../base.h" +#include <../base.hpp> #define SDD1_CPP #include "sdd1emu.cpp" @@ -6,7 +6,17 @@ void SDD1::init() {} void SDD1::enable() { - for(int i = 0x4800; i <= 0x4807; i++) memory::mmio.map(i, *this); + //hook S-CPU DMA MMIO registers to gather information for struct dma[]; + //buffer address and transfer size information for use in SDD1::read() + for(unsigned i = 0x4300; i <= 0x437f; i++) { + cpu_mmio[i & 0x7f] = memory::mmio.get(i); + memory::mmio.map(i, *this); + } + + //hook S-DD1 MMIO registers + for(unsigned i = 0x4800; i <= 0x4807; i++) { + memory::mmio.map(i, *this); + } } void SDD1::power() { @@ -14,97 +24,133 @@ void SDD1::power() { } void SDD1::reset() { - sdd1.dma_active = false; + sdd1_enable = 0x00; + xfer_enable = 0x00; - regs.r4800 = 0x00; - regs.r4801 = 0x00; + mmc[0] = 0 << 20; + mmc[1] = 1 << 20; + mmc[2] = 2 << 20; + mmc[3] = 3 << 20; - regs.r4804 = 0x00; - regs.r4805 = 0x01; - regs.r4806 = 0x02; - regs.r4807 = 0x03; + for(unsigned i = 0; i < 8; i++) { + dma[i].addr = 0; + dma[i].size = 0; + } - bus.map(Bus::MapLinear, 0xc0, 0xcf, 0x0000, 0xffff, memory::cartrom, (regs.r4804 & 7) << 20); - bus.map(Bus::MapLinear, 0xd0, 0xdf, 0x0000, 0xffff, memory::cartrom, (regs.r4805 & 7) << 20); - bus.map(Bus::MapLinear, 0xe0, 0xef, 0x0000, 0xffff, memory::cartrom, (regs.r4806 & 7) << 20); - bus.map(Bus::MapLinear, 0xf0, 0xff, 0x0000, 0xffff, memory::cartrom, (regs.r4807 & 7) << 20); + buffer.ready = false; + + bus.map(Bus::MapDirect, 0xc0, 0xff, 0x0000, 0xffff, *this); } uint8 SDD1::mmio_read(uint addr) { - switch(addr & 0xffff) { - case 0x4804: return regs.r4804; - case 0x4805: return regs.r4805; - case 0x4806: return regs.r4806; - case 0x4807: return regs.r4807; + addr &= 0xffff; + + if((addr & 0x4380) == 0x4300) { + return cpu_mmio[addr & 0x7f]->mmio_read(addr); + } + + switch(addr) { + case 0x4804: return (mmc[0] >> 20) & 7; + case 0x4805: return (mmc[1] >> 20) & 7; + case 0x4806: return (mmc[2] >> 20) & 7; + case 0x4807: return (mmc[3] >> 20) & 7; } return cpu.regs.mdr; } void SDD1::mmio_write(uint addr, uint8 data) { - switch(addr & 0xffff) { - case 0x4800: { - regs.r4800 = data; - } break; + addr &= 0xffff; - case 0x4801: { - regs.r4801 = data; - } break; + if((addr & 0x4380) == 0x4300) { + unsigned channel = (addr >> 4) & 7; + switch(addr & 15) { + case 2: dma[channel].addr = (dma[channel].addr & 0xffff00) + (data << 0); break; + case 3: dma[channel].addr = (dma[channel].addr & 0xff00ff) + (data << 8); break; + case 4: dma[channel].addr = (dma[channel].addr & 0x00ffff) + (data << 16); break; - case 0x4804: { - if(regs.r4804 != data) { - regs.r4804 = data; - bus.map(Bus::MapLinear, 0xc0, 0xcf, 0x0000, 0xffff, - memory::cartrom, (regs.r4804 & 7) << 20); - } - } break; + case 5: dma[channel].size = (dma[channel].size & 0xff00) + (data << 0); break; + case 6: dma[channel].size = (dma[channel].size & 0x00ff) + (data << 8); break; + } + return cpu_mmio[addr & 0x7f]->mmio_write(addr, data); + } - case 0x4805: { - if(regs.r4805 != data) { - regs.r4805 = data; - bus.map(Bus::MapLinear, 0xd0, 0xdf, 0x0000, 0xffff, - memory::cartrom, (regs.r4805 & 7) << 20); - } - } break; + switch(addr) { + case 0x4800: sdd1_enable = data; break; + case 0x4801: xfer_enable = data; break; - case 0x4806: { - if(regs.r4806 != data) { - regs.r4806 = data; - bus.map(Bus::MapLinear, 0xe0, 0xef, 0x0000, 0xffff, - memory::cartrom, (regs.r4806 & 7) << 20); - } - } break; - - case 0x4807: { - if(regs.r4807 != data) { - regs.r4807 = data; - bus.map(Bus::MapLinear, 0xf0, 0xff, 0x0000, 0xffff, - memory::cartrom, (regs.r4807 & 7) << 20); - } - } break; + case 0x4804: mmc[0] = (data & 7) << 20; break; + case 0x4805: mmc[1] = (data & 7) << 20; break; + case 0x4806: mmc[2] = (data & 7) << 20; break; + case 0x4807: mmc[3] = (data & 7) << 20; break; } } -void SDD1::dma_begin(uint8 channel, uint32 addr, uint16 length) { - if(regs.r4800 & (1 << channel) && regs.r4801 & (1 << channel)) { - regs.r4801 &= ~(1 << channel); - sdd1.dma_active = true; - sdd1.buffer_index = 0; - sdd1.buffer_size = length; - sdd1emu.decompress(addr, (length) ? length : 65536, sdd1.buffer); - } +//SDD1::read() is mapped to $[c0-ff]:[0000-ffff] +//the design is meant to be as close to the hardware design as possible, thus this code +//avoids adding S-DD1 hooks inside S-CPU::DMA emulation. +// +//the real S-DD1 cannot see $420b (DMA enable) writes, as they are not placed on the bus. +//however, $43x0-$43xf writes (DMAx channel settings) most likely do appear on the bus. +//the S-DD1 also requires fixed addresses for transfers, which wouldn't be necessary if +//it could see $420b writes (eg it would know when the transfer should begin.) +// +//the hardware needs a way to distinguish program code after $4801 writes from DMA +//decompression that follows soon after. +// +//the only plausible design for hardware would be for the S-DD1 to spy on DMAx settings, +//and begin spooling decompression on writes to $4801 that activate a channel. after that, +//it feeds decompressed data only when the ROM read address matches the DMA channel address. +// +//the actual S-DD1 transfer can occur on any channel, but it is most likely limited to +//one transfer per $420b write (for spooling purposes). however, this is not known for certain. +uint8 SDD1::read(unsigned addr) { + if(sdd1_enable & xfer_enable) { + //at least one channel has S-DD1 decompression enabled ... + for(unsigned i = 0; i < 8; i++) { + if(sdd1_enable & xfer_enable & (1 << i)) { + //S-DD1 always uses fixed transfer mode, so address will not change during transfer + if(addr == dma[i].addr) { + if(!buffer.ready) { + //first byte read for channel performs full decompression. + //this really should stream byte-by-byte, but it's not necessary since the size is known + buffer.offset = 0; + buffer.size = dma[i].size ? dma[i].size : 65536; + + //sdd1emu calls this function; it needs to access uncompressed data; + //so temporarily disable decompression mode for decompress() call. + uint8 temp = sdd1_enable; + sdd1_enable = false; + sdd1emu.decompress(addr, buffer.size, buffer.data); + sdd1_enable = temp; + + buffer.ready = true; + } + + //fetch a decompressed byte; once buffer is depleted, disable channel and invalidate buffer + uint8 data = buffer.data[(uint16)buffer.offset++]; + if(buffer.offset >= buffer.size) { + buffer.ready = false; + xfer_enable &= ~(1 << i); + } + + return data; + } //address matched + } //channel enabled + } //channel loop + } //S-DD1 decompressor enabled + + //S-DD1 decompression mode inactive; return ROM data + return memory::cartrom.read(mmc[(addr >> 20) & 3] + (addr & 0x0fffff)); } -bool SDD1::dma_active() { - return sdd1.dma_active; +void SDD1::write(unsigned addr, uint8 data) { } -uint8 SDD1::dma_read() { - if(--sdd1.buffer_size == 0) sdd1.dma_active = false; - -//sdd1.buffer[] is 65536 bytes, and sdd1.buffer_index -//is of type uint16, so no buffer overflow is possible - return sdd1.buffer[sdd1.buffer_index++]; +SDD1::SDD1() { + buffer.data = new uint8[65536]; } -SDD1::SDD1() {} +SDD1::~SDD1() { + delete[] buffer.data; +} diff --git a/src/chip/sdd1/sdd1.h b/src/chip/sdd1/sdd1.h deleted file mode 100644 index 4901f4ba..00000000 --- a/src/chip/sdd1/sdd1.h +++ /dev/null @@ -1,39 +0,0 @@ -#include "sdd1emu.h" - -class SDD1 : public MMIO { -public: - void init(); - void enable(); - void power(); - void reset(); - - uint8 mmio_read (uint addr); - void mmio_write(uint addr, uint8 data); - - void dma_begin(uint8 channel, uint32 addr, uint16 length); - bool dma_active(); - uint8 dma_read(); - - SDD1(); - -private: - SDD1emu sdd1emu; - - struct { - uint8 buffer[65536]; //pointer to decompressed S-DD1 data, max DMA length is 65536 - uint16 buffer_index; //DMA read index into S-DD1 decompression buffer - uint16 buffer_size; - bool dma_active; - } sdd1; - - struct { - uint8 r4800; - uint8 r4801; - uint8 r4804; - uint8 r4805; - uint8 r4806; - uint8 r4807; - } regs; -}; - -extern SDD1 sdd1; diff --git a/src/chip/sdd1/sdd1.hpp b/src/chip/sdd1/sdd1.hpp new file mode 100644 index 00000000..9588bc07 --- /dev/null +++ b/src/chip/sdd1/sdd1.hpp @@ -0,0 +1,40 @@ +#include "sdd1emu.hpp" + +class SDD1 : public MMIO, public Memory { +public: + void init(); + void enable(); + void power(); + void reset(); + + uint8 mmio_read(unsigned addr); + void mmio_write(unsigned addr, uint8 data); + + uint8 read(unsigned addr); + void write(unsigned addr, uint8 data); + + SDD1(); + ~SDD1(); + +private: + MMIO *cpu_mmio[0x80]; //bus spying hooks to glean information for struct dma[] + + uint8 sdd1_enable; //channel bit-mask + uint8 xfer_enable; //channel bit-mask + unsigned mmc[4]; //memory map controller ROM indices + + struct { + unsigned addr; //$43x2-$43x4 -- DMA transfer address + uint16 size; //$43x5-$43x6 -- DMA transfer size + } dma[8]; + + SDD1emu sdd1emu; + struct { + uint8 *data; //pointer to decompressed S-DD1 data (65536 bytes) + uint16 offset; //read index into S-DD1 decompression buffer + unsigned size; //length of data buffer; reads decrement counter, set ready to false at 0 + bool ready; //true when data[] is valid; false to invoke sdd1emu.decompress() + } buffer; +}; + +extern SDD1 sdd1; diff --git a/src/chip/sdd1/sdd1emu.cpp b/src/chip/sdd1/sdd1emu.cpp index 30f821c9..449082ab 100644 --- a/src/chip/sdd1/sdd1emu.cpp +++ b/src/chip/sdd1/sdd1emu.cpp @@ -30,7 +30,7 @@ understood. ************************************************************************/ -#define SDD1_read(__addr) (bus.read(__addr)) +#define SDD1_read(__addr) (sdd1.read(__addr)) //////////////////////////////////////////////////// diff --git a/src/chip/sdd1/sdd1emu.h b/src/chip/sdd1/sdd1emu.hpp similarity index 100% rename from src/chip/sdd1/sdd1emu.h rename to src/chip/sdd1/sdd1emu.hpp diff --git a/src/chip/spc7110/decomp.h b/src/chip/spc7110/decomp.hpp similarity index 100% rename from src/chip/spc7110/decomp.h rename to src/chip/spc7110/decomp.hpp diff --git a/src/chip/spc7110/spc7110.cpp b/src/chip/spc7110/spc7110.cpp index 1d5c8910..d6a84660 100644 --- a/src/chip/spc7110/spc7110.cpp +++ b/src/chip/spc7110/spc7110.cpp @@ -1,4 +1,4 @@ -#include "../../base.h" +#include <../base.hpp> #define SPC7110_CPP #include "decomp.cpp" diff --git a/src/chip/spc7110/spc7110.h b/src/chip/spc7110/spc7110.hpp similarity index 99% rename from src/chip/spc7110/spc7110.h rename to src/chip/spc7110/spc7110.hpp index 5646b1f7..2c9b6bf1 100644 --- a/src/chip/spc7110/spc7110.h +++ b/src/chip/spc7110/spc7110.hpp @@ -15,7 +15,7 @@ * or in connection with the use or performance of this software. *****/ -#include "decomp.h" +#include "decomp.hpp" class SPC7110 : public MMIO, public Memory { public: diff --git a/src/chip/srtc/srtc.cpp b/src/chip/srtc/srtc.cpp index 04b20057..335cb965 100644 --- a/src/chip/srtc/srtc.cpp +++ b/src/chip/srtc/srtc.cpp @@ -1,4 +1,4 @@ -#include "../../base.h" +#include <../base.hpp> const unsigned SRTC::months[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; diff --git a/src/chip/srtc/srtc.h b/src/chip/srtc/srtc.hpp similarity index 100% rename from src/chip/srtc/srtc.h rename to src/chip/srtc/srtc.hpp diff --git a/src/chip/st010/st010.cpp b/src/chip/st010/st010.cpp index 38a5e1c7..1fa4a724 100644 --- a/src/chip/st010/st010.cpp +++ b/src/chip/st010/st010.cpp @@ -1,7 +1,7 @@ -#include "../../base.h" +#include <../base.hpp> #define ST010_CPP -#include "st010_data.h" +#include "st010_data.hpp" #include "st010_op.cpp" int16 ST010::sin(int16 theta) { diff --git a/src/chip/st010/st010.h b/src/chip/st010/st010.hpp similarity index 100% rename from src/chip/st010/st010.h rename to src/chip/st010/st010.hpp diff --git a/src/chip/st010/st010_data.h b/src/chip/st010/st010_data.hpp similarity index 100% rename from src/chip/st010/st010_data.h rename to src/chip/st010/st010_data.hpp diff --git a/src/config/config.cpp b/src/config/config.cpp index 8fc3565a..ce4fb4d7 100644 --- a/src/config/config.cpp +++ b/src/config/config.cpp @@ -1,3 +1,5 @@ +#include <../base.hpp> + namespace config { configuration& config() { @@ -5,56 +7,59 @@ configuration& config() { return config; } +string filepath(const char *filename, const char *pathname) { + //if no pathname, return filename as-is + string file(filename); + replace(file, "\\", "/"); + if(!pathname || !*pathname) return file; + + //ensure path ends with trailing '/' + string path(pathname); + replace(path, "\\", "/"); + if(!strend(path, "/")) strcat(path, "/"); + + //replace relative path with absolute path + if(strbegin(path, "./")) { + ltrim(path, "./"); + path = string() << config::path.base << path; + } + + //remove folder part of filename + lstring part; + split(part, "/", file); + return path << part[count(part) - 1]; +} + integral_setting File::autodetect_type(config(), "file.autodetect_type", "Auto-detect file type by inspecting file header, rather than by file extension.\n" - "In other words, if a .zip file is renamed to .smc, it will still be correctly\n" - "identified as a .zip file. However, there is an infinitesimal (1:~500,000,000)\n" - "chance of a false detection when loading an uncompressed image file, if this\n" + "In other words, if a .zip file is renamed to .smc, it will still be correctly " + "identified as a .zip file. However, there is an infinitesimal (1:~500,000,000) " + "chance of a false detection when loading an uncompressed image file, if this " "option is enabled.", integral_setting::boolean, false); integral_setting File::bypass_patch_crc32(config(), "file.bypass_patch_crc32", "UPS patches contain CRC32s to validate that a patch was applied successfully.\n" - "By default, if this validation fails, said patch will not be applied.\n" - "Setting this option to true will bypass the validation,\n" - "which may or may not result in a working image.\n" - "Enabling this option is strongly discouraged.", + "By default, if this validation fails, said patch will not be applied. " + "Setting this option to true will bypass the validation, " + "which may or may not result in a working image. " + "Thus, enabling this option is strongly discouraged.", integral_setting::boolean, false); -string file_updatepath(const char *req_file, const char *req_path) { - string file(req_file); - replace(file, "\\", "/"); - if(!req_path || strlen(req_path) == 0) { return file; } - - string path(req_path); - replace(path, "\\", "/"); - if(!strend(path, "/")) { strcat(path, "/"); } - - if(strbegin(path, "./")) { - ltrim(path(), "./"); - string temp; - strcpy(temp, config::path.base); - strcat(temp, path); - strcpy(path, temp); - } - - lstring part; - split(part, "/", file); - strcat(path, part[count(part) - 1]); - return path; -} - string_setting Path::base("path.base", "Path that bsnes resides in", ""); string_setting Path::user("path.user", "Path to user folder", ""); string_setting Path::rom(config(), "path.rom", "Default path to look for ROM files in (\"\" = use default directory)", ""); -string_setting Path::patch(config(), "path.patch", - "Default path for all UPS patch files (\"\" = use current directory)", ""); string_setting Path::save(config(), "path.save", "Default path for all save RAM files (\"\" = use current directory)", ""); +string_setting Path::patch(config(), "path.patch", + "Default path for all UPS patch files (\"\" = use current directory)", ""); string_setting Path::cheat(config(), "path.cheat", "Default path for all cheat files (\"\" = use current directory)", ""); +string_setting Path::exportdata(config(), "path.export", + "Default path for all exported data files\n", ""); + string_setting Path::bsx(config(), "path.bsx", "", ""); string_setting Path::st(config(), "path.st", "", ""); @@ -65,14 +70,14 @@ integral_setting SNES::controller_port2(config(), "snes.controller_port2", integral_setting SNES::expansion_port(config(), "snes.expansion_port", "Device attached to SNES expansion port\n" "0 = None\n" - "1 = Satellaview BS-X\n" - "", integral_setting::decimal, ::SNES::ExpansionBSX); + "1 = Satellaview BS-X", + integral_setting::decimal, ::SNES::ExpansionBSX); integral_setting SNES::region(config(), "snes.region", "SNES regional model\n" "0 = Auto-detect based on cartridge\n" "1 = NTSC\n" - "2 = PAL\n" - "", integral_setting::decimal, ::SNES::Autodetect); + "2 = PAL", + integral_setting::decimal, ::SNES::Autodetect); integral_setting CPU::ntsc_clock_rate(config(), "cpu.ntsc_clock_rate", "NTSC S-CPU clock rate (in hz)", integral_setting::decimal, 21477272); @@ -80,16 +85,16 @@ integral_setting CPU::pal_clock_rate(config(), "cpu.pal_clock_rate", "PAL S-CPU clock rate (in hz)", integral_setting::decimal, 21281370); integral_setting CPU::wram_init_value(config(), "cpu.wram_init_value", "Value to initialize 128k WRAM to upon power cycle.\n" - "Note that on real hardware, this value is undefined; meaning it can vary\n" - "per power-on, and per SNES unit. Such randomness is undesirable for an\n" - "emulator, so a static value is needed. There is also some form of pattern\n" + "Note that on real hardware, this value is undefined; meaning it can vary " + "per power-on, and per SNES unit. Such randomness is undesirable for an " + "emulator, so a static value is needed. There is also some form of pattern " "to the randomness that has yet to be determined, which some games rely upon.\n" - "A value of 0x55 is safe for all known commercial software, and should be used.\n" - "However, some software written for SNES copiers, or backup units, relies on\n" - "WRAM being initialized to 0x00; which was a side-effect of the BIOS program\n" - "which executed on these copiers. Using 0x00 will therefore fix many homebrew\n" - "programs, but *will* break some poorly programmed commercial software titles,\n" - "which do not properly initialize WRAM upon power cycle.\n", + "A value of 0x55 is safe for all known commercial software, and should be used. " + "However, some software written for SNES copiers, or backup units, relies on " + "WRAM being initialized to 0x00; which was a side-effect of the BIOS program " + "which executed on these copiers. Using 0x00 will therefore fix many homebrew " + "programs, but *will* break some poorly programmed commercial software titles, " + "which do not properly initialize WRAM upon power cycle.", integral_setting::hex, 0x55); integral_setting SMP::ntsc_clock_rate(config(), "smp.ntsc_clock_rate", @@ -97,42 +102,4 @@ integral_setting SMP::ntsc_clock_rate(config(), "smp.ntsc_clock_rate", integral_setting SMP::pal_clock_rate(config(), "smp.pal_clock_rate", "PAL S-SMP clock rate (in hz)", integral_setting::decimal, 32041 * 768); -integral_setting PPU::Hack::render_scanline_position(config(), "ppu.hack.render_scanline_position", - "Approximate HCLOCK position to render at for scanline-based renderers", - integral_setting::decimal, 512); -integral_setting PPU::Hack::obj_cache(config(), "ppu.hack.obj_cache", - "Cache OAM OBJ attributes one scanline before rendering\n" - "This is technically closer to the actual operation of the SNES,\n" - "but can cause problems in some games if enabled", - integral_setting::boolean, false); -integral_setting PPU::Hack::oam_address_invalidation(config(), "ppu.hack.oam_address_invalidation", - "OAM access address changes during active display, as the S-PPU reads\n" - "data to render the display. Thusly, the address retrieved when accessing\n" - "OAM during active display is unpredictable. Unfortunately, the exact\n" - "algorithm for this is completely unknown at this time. It is more hardware\n" - "accurate to enable this setting, but one must *not* rely on the actual\n" - "address to match hardware under emulation.", - integral_setting::boolean, true); -integral_setting PPU::Hack::cgram_address_invalidation(config(), "ppu.hack.cgram_address_invalidation", - "CGRAM access address changes during active display (excluding hblank), as\n" - "the S-PPU reads data to render the display. Thusly, as with OAM, the access\n" - "address is unpredictable. Again, enabling this setting is more hardware\n" - "accurate, but one must *not* rely on the actual address to match hardware\n" - "under emulation.", - integral_setting::boolean, true); - -integral_setting PPU::opt_enable("ppu.opt_enable", "Enable offset-per-tile effects", integral_setting::boolean, true); -integral_setting PPU::bg1_pri0_enable("ppu.bg1_pri0_enable", "Enable BG1 Priority 0", integral_setting::boolean, true); -integral_setting PPU::bg1_pri1_enable("ppu.bg1_pri1_enable", "Enable BG1 Priority 1", integral_setting::boolean, true); -integral_setting PPU::bg2_pri0_enable("ppu.bg2_pri0_enable", "Enable BG2 Priority 0", integral_setting::boolean, true); -integral_setting PPU::bg2_pri1_enable("ppu.bg2_pri1_enable", "Enable BG2 Priority 1", integral_setting::boolean, true); -integral_setting PPU::bg3_pri0_enable("ppu.bg3_pri0_enable", "Enable BG3 Priority 0", integral_setting::boolean, true); -integral_setting PPU::bg3_pri1_enable("ppu.bg3_pri1_enable", "Enable BG3 Priority 1", integral_setting::boolean, true); -integral_setting PPU::bg4_pri0_enable("ppu.bg4_pri0_enable", "Enable BG4 Priority 0", integral_setting::boolean, true); -integral_setting PPU::bg4_pri1_enable("ppu.bg4_pri1_enable", "Enable BG4 Priority 1", integral_setting::boolean, true); -integral_setting PPU::oam_pri0_enable("ppu.oam_pri0_enable", "Enable OAM Priority 0", integral_setting::boolean, true); -integral_setting PPU::oam_pri1_enable("ppu.oam_pri1_enable", "Enable OAM Priority 1", integral_setting::boolean, true); -integral_setting PPU::oam_pri2_enable("ppu.oam_pri2_enable", "Enable OAM Priority 2", integral_setting::boolean, true); -integral_setting PPU::oam_pri3_enable("ppu.oam_pri3_enable", "Enable OAM Priority 3", integral_setting::boolean, true); - } //namespace config diff --git a/src/config/config.h b/src/config/config.h deleted file mode 100644 index 5f865fb8..00000000 --- a/src/config/config.h +++ /dev/null @@ -1,50 +0,0 @@ -namespace config { - -extern configuration& config(); - -string file_updatepath(const char*, const char*); - -extern struct File { - static integral_setting autodetect_type; - static integral_setting bypass_patch_crc32; -} file; - -extern struct Path { - static string_setting base, user, rom, patch, save, cheat; - static string_setting bsx, st; -} path; - -extern struct SNES { - static integral_setting controller_port1; - static integral_setting controller_port2; - static integral_setting expansion_port; - static integral_setting region; -} snes; - -extern struct CPU { - static integral_setting ntsc_clock_rate, pal_clock_rate; - static integral_setting wram_init_value; -} cpu; - -extern struct SMP { - static integral_setting ntsc_clock_rate, pal_clock_rate; -} smp; - -extern struct PPU { - struct Hack { - static integral_setting render_scanline_position; - static integral_setting obj_cache; - static integral_setting oam_address_invalidation; - static integral_setting cgram_address_invalidation; - } hack; - - static integral_setting opt_enable; - static integral_setting bg1_pri0_enable, bg1_pri1_enable; - static integral_setting bg2_pri0_enable, bg2_pri1_enable; - static integral_setting bg3_pri0_enable, bg3_pri1_enable; - static integral_setting bg4_pri0_enable, bg4_pri1_enable; - static integral_setting oam_pri0_enable, oam_pri1_enable; - static integral_setting oam_pri2_enable, oam_pri3_enable; -} ppu; - -}; diff --git a/src/config/config.hpp b/src/config/config.hpp new file mode 100644 index 00000000..81b381ea --- /dev/null +++ b/src/config/config.hpp @@ -0,0 +1,34 @@ +namespace config { + +extern configuration& config(); + +string filepath(const char *filename, const char *pathname); + +extern struct File { + static integral_setting autodetect_type; + static integral_setting bypass_patch_crc32; +} file; + +extern struct Path { + static string_setting base, user; + static string_setting rom, save, patch, cheat, exportdata; + static string_setting bsx, st; +} path; + +extern struct SNES { + static integral_setting controller_port1; + static integral_setting controller_port2; + static integral_setting expansion_port; + static integral_setting region; +} snes; + +extern struct CPU { + static integral_setting ntsc_clock_rate, pal_clock_rate; + static integral_setting wram_init_value; +} cpu; + +extern struct SMP { + static integral_setting ntsc_clock_rate, pal_clock_rate; +} smp; + +}; diff --git a/src/cpu/cpu.cpp b/src/cpu/cpu.cpp index f2013040..9c634fa8 100644 --- a/src/cpu/cpu.cpp +++ b/src/cpu/cpu.cpp @@ -1,4 +1,4 @@ -#include "../base.h" +#include <../base.hpp> #define CPU_CPP #include "dcpu.cpp" diff --git a/src/cpu/cpu.h b/src/cpu/cpu.hpp similarity index 83% rename from src/cpu/cpu.h rename to src/cpu/cpu.hpp index 3d750ca5..825b8ab5 100644 --- a/src/cpu/cpu.h +++ b/src/cpu/cpu.hpp @@ -1,5 +1,3 @@ -#include "cpuregs.h" - class CPU : public MMIO { public: virtual void enter() = 0; @@ -7,26 +5,16 @@ public: //CPU version number //* 1 and 2 are known //* reported by $4210 - //* affects DRAM refresh behavior + //* affects timing (DRAM refresh, HDMA init, etc) uint8 cpu_version; - //timing - virtual uint16 vcounter() = 0; - virtual uint16 hcounter() = 0; - virtual uint16 hdot() = 0; - virtual uint8 pio() = 0; virtual bool joylatch() = 0; virtual uint8 port_read(uint8 port) = 0; virtual void port_write(uint8 port, uint8 value) = 0; - - CPURegs regs; - enum { - FLAG_N = 0x80, FLAG_V = 0x40, - FLAG_M = 0x20, FLAG_X = 0x10, - FLAG_D = 0x08, FLAG_I = 0x04, - FLAG_Z = 0x02, FLAG_C = 0x01 - }; + + #include "cpuregs.hpp" + regs_t regs; virtual void scanline() = 0; virtual void frame() = 0; diff --git a/src/cpu/cpuregs.h b/src/cpu/cpuregs.h deleted file mode 100644 index 727aa60b..00000000 --- a/src/cpu/cpuregs.h +++ /dev/null @@ -1,88 +0,0 @@ -template -struct CPUFlag { - uint8 &data; - - inline operator bool() const { return data & mask; } - inline CPUFlag& operator=(bool i) { data = (data & ~mask) | (-i & mask); return *this; } - - CPUFlag(uint8 &data_) : data(data_) {} -}; - -class CPURegFlags { -public: - uint8 data; - CPUFlag<0x80> n; - CPUFlag<0x40> v; - CPUFlag<0x20> m; - CPUFlag<0x10> x; - CPUFlag<0x08> d; - CPUFlag<0x04> i; - CPUFlag<0x02> z; - CPUFlag<0x01> c; - - inline operator unsigned() const { return data; } - inline unsigned operator = (unsigned i) { data = i; return data; } - inline unsigned operator |= (unsigned i) { data |= i; return data; } - inline unsigned operator ^= (unsigned i) { data ^= i; return data; } - inline unsigned operator &= (unsigned i) { data &= i; return data; } - - CPURegFlags() : data(0), n(data), v(data), m(data), x(data), d(data), i(data), z(data), c(data) {} -}; - -class CPUReg16 { -public: - union { - uint16 w; - struct { uint8 order_lsb2(l, h); }; - }; - - inline operator unsigned() const { return w; } - inline unsigned operator = (unsigned i) { w = i; return w; } - inline unsigned operator |= (unsigned i) { w |= i; return w; } - inline unsigned operator ^= (unsigned i) { w ^= i; return w; } - inline unsigned operator &= (unsigned i) { w &= i; return w; } - inline unsigned operator <<= (unsigned i) { w <<= i; return w; } - inline unsigned operator >>= (unsigned i) { w >>= i; return w; } - inline unsigned operator += (unsigned i) { w += i; return w; } - inline unsigned operator -= (unsigned i) { w -= i; return w; } - inline unsigned operator *= (unsigned i) { w *= i; return w; } - inline unsigned operator /= (unsigned i) { w /= i; return w; } - inline unsigned operator %= (unsigned i) { w %= i; return w; } - - CPUReg16() : w(0) {} -}; - -class CPUReg24 { -public: - union { - uint32 d; - struct { uint16 order_lsb2(w, wh); }; - struct { uint8 order_lsb4(l, h, b, bh); }; - }; - - inline operator unsigned() const { return d; } - inline unsigned operator = (unsigned i) { d = uclip<24>(i); return d; } - inline unsigned operator |= (unsigned i) { d = uclip<24>(d | i); return d; } - inline unsigned operator ^= (unsigned i) { d = uclip<24>(d ^ i); return d; } - inline unsigned operator &= (unsigned i) { d = uclip<24>(d & i); return d; } - inline unsigned operator <<= (unsigned i) { d = uclip<24>(d << i); return d; } - inline unsigned operator >>= (unsigned i) { d = uclip<24>(d >> i); return d; } - inline unsigned operator += (unsigned i) { d = uclip<24>(d + i); return d; } - inline unsigned operator -= (unsigned i) { d = uclip<24>(d - i); return d; } - inline unsigned operator *= (unsigned i) { d = uclip<24>(d * i); return d; } - inline unsigned operator /= (unsigned i) { d = uclip<24>(d / i); return d; } - inline unsigned operator %= (unsigned i) { d = uclip<24>(d % i); return d; } - - CPUReg24() : d(0) {} -}; - -class CPURegs { -public: - CPUReg24 pc; - CPUReg16 a, x, y, s, d; - CPURegFlags p; - uint8 db; - uint8 mdr; - bool e; - CPURegs() : db(0), mdr(0), e(false) {} -}; diff --git a/src/cpu/cpuregs.hpp b/src/cpu/cpuregs.hpp new file mode 100644 index 00000000..88360aa7 --- /dev/null +++ b/src/cpu/cpuregs.hpp @@ -0,0 +1,74 @@ +struct flag_t { + bool n, v, m, x, d, i, z, c; + + inline operator unsigned() const { + return (n << 7) + (v << 6) + (m << 5) + (x << 4) + + (d << 3) + (i << 2) + (z << 1) + (c << 0); + } + + inline unsigned operator=(uint8_t data) { + n = data & 0x80; v = data & 0x40; m = data & 0x20; x = data & 0x10; + d = data & 0x08; i = data & 0x04; z = data & 0x02; c = data & 0x01; + return data; + } + + inline unsigned operator|=(unsigned data) { return operator=(operator unsigned() | data); } + inline unsigned operator^=(unsigned data) { return operator=(operator unsigned() ^ data); } + inline unsigned operator&=(unsigned data) { return operator=(operator unsigned() & data); } + + flag_t() : n(0), v(0), m(0), x(0), d(0), i(0), z(0), c(0) {} +}; + +struct reg16_t { + union { + uint16 w; + struct { uint8 order_lsb2(l, h); }; + }; + + inline operator unsigned() const { return w; } + inline unsigned operator = (unsigned i) { return w = i; } + inline unsigned operator |= (unsigned i) { return w |= i; } + inline unsigned operator ^= (unsigned i) { return w ^= i; } + inline unsigned operator &= (unsigned i) { return w &= i; } + inline unsigned operator <<= (unsigned i) { return w <<= i; } + inline unsigned operator >>= (unsigned i) { return w >>= i; } + inline unsigned operator += (unsigned i) { return w += i; } + inline unsigned operator -= (unsigned i) { return w -= i; } + inline unsigned operator *= (unsigned i) { return w *= i; } + inline unsigned operator /= (unsigned i) { return w /= i; } + inline unsigned operator %= (unsigned i) { return w %= i; } + + reg16_t() : w(0) {} +}; + +struct reg24_t { + union { + uint32 d; + struct { uint16 order_lsb2(w, wh); }; + struct { uint8 order_lsb4(l, h, b, bh); }; + }; + + inline operator unsigned() const { return d; } + inline unsigned operator = (unsigned i) { return d = uclip<24>(i); } + inline unsigned operator |= (unsigned i) { return d = uclip<24>(d | i); } + inline unsigned operator ^= (unsigned i) { return d = uclip<24>(d ^ i); } + inline unsigned operator &= (unsigned i) { return d = uclip<24>(d & i); } + inline unsigned operator <<= (unsigned i) { return d = uclip<24>(d << i); } + inline unsigned operator >>= (unsigned i) { return d = uclip<24>(d >> i); } + inline unsigned operator += (unsigned i) { return d = uclip<24>(d + i); } + inline unsigned operator -= (unsigned i) { return d = uclip<24>(d - i); } + inline unsigned operator *= (unsigned i) { return d = uclip<24>(d * i); } + inline unsigned operator /= (unsigned i) { return d = uclip<24>(d / i); } + inline unsigned operator %= (unsigned i) { return d = uclip<24>(d % i); } + + reg24_t() : d(0) {} +}; + +struct regs_t { + reg24_t pc; + reg16_t a, x, y, s, d; + flag_t p; + uint8_t db, mdr; + bool e; + regs_t() : db(0), mdr(0), e(false) {} +}; diff --git a/src/cpu/dcpu.cpp b/src/cpu/dcpu.cpp index 98c4b38c..2c3281a5 100644 --- a/src/cpu/dcpu.cpp +++ b/src/cpu/dcpu.cpp @@ -103,7 +103,7 @@ uint32 r = 0; } void CPU::disassemble_opcode(char *output) { -static CPUReg24 pc; +static reg24_t pc; char t[256]; char *s = output; @@ -425,7 +425,7 @@ uint8 op2 = dreadb(pc.d); strcat(s, t); strcat(s, " "); - sprintf(t, "V:%3d H:%4d", vcounter(), hcounter()); + sprintf(t, "V:%3d H:%4d", ppucounter.vcounter(), ppucounter.hcounter()); strcat(s, t); } diff --git a/src/cpu/scpu/core/core.cpp b/src/cpu/scpu/core/core.cpp index 68f013fb..4ec0d0c4 100644 --- a/src/cpu/scpu/core/core.cpp +++ b/src/cpu/scpu/core/core.cpp @@ -2,7 +2,14 @@ #include "opfn.cpp" -void sCPU::enter() { loop: +void sCPU::enter() { + initialize: + //initial latch values for $213c/$213d + //[x]0035 : [y]0000 (53.0 -> 212) [lda $2137] + //[x]0038 : [y]0000 (56.5 -> 226) [nop : lda $2137] + add_clocks(186); + + loop: if(event.irq) { event.irq = false; if(status.nmi_pending == true) { diff --git a/src/cpu/scpu/core/core.h b/src/cpu/scpu/core/core.hpp similarity index 93% rename from src/cpu/scpu/core/core.h rename to src/cpu/scpu/core/core.hpp index bf5adbc1..945c9177 100644 --- a/src/cpu/scpu/core/core.h +++ b/src/cpu/scpu/core/core.hpp @@ -1,4 +1,4 @@ - CPUReg24 aa, rd; + reg24_t aa, rd; uint8_t dp, sp; void op_irq(); diff --git a/src/cpu/scpu/dma/dma.cpp b/src/cpu/scpu/dma/dma.cpp index c259a35b..9fc9125f 100644 --- a/src/cpu/scpu/dma/dma.cpp +++ b/src/cpu/scpu/dma/dma.cpp @@ -7,15 +7,15 @@ void sCPU::dma_add_clocks(uint clocks) { bool sCPU::dma_addr_valid(uint32 abus) { //reads from B-bus or S-CPU registers are invalid - if((abus & 0x40ff00) == 0x2100) return false; //$[00-3f|80-bf]:[2100-21ff] - if((abus & 0x40fe00) == 0x4000) return false; //$[00-3f|80-bf]:[4000-41ff] - if((abus & 0x40ffe0) == 0x4200) return false; //$[00-3f|80-bf]:[4200-421f] - if((abus & 0x40ff80) == 0x4300) return false; //$[00-3f|80-bf]:[4300-437f] + if((abus & 0x40ff00) == 0x2100) return false; //$[00-3f|80-bf]:[2100-21ff] + if((abus & 0x40fe00) == 0x4000) return false; //$[00-3f|80-bf]:[4000-41ff] + if((abus & 0x40ffe0) == 0x4200) return false; //$[00-3f|80-bf]:[4200-421f] + if((abus & 0x40ff80) == 0x4300) return false; //$[00-3f|80-bf]:[4300-437f] return true; } uint8 sCPU::dma_read(uint32 abus) { - if(dma_addr_valid(abus) == false) return 0x00; //does not return S-CPU MDR + if(dma_addr_valid(abus) == false) return 0x00; //does not return S-CPU MDR return bus.read(abus); } @@ -39,7 +39,7 @@ void sCPU::dma_transfer(bool direction, uint8 bbus, uint32 abus) { //illegal WRAM->WRAM transfer (bus conflict) //no read occurs; write does occur dma_add_clocks(8); - bus.write(abus, 0x00); //does not write S-CPU MDR + bus.write(abus, 0x00); //does not write S-CPU MDR } else { dma_add_clocks(4); uint8 data = bus.read(0x2100 | bbus); @@ -59,14 +59,14 @@ void sCPU::dma_transfer(bool direction, uint8 bbus, uint32 abus) { uint8 sCPU::dma_bbus(uint8 i, uint8 index) { switch(channel[i].xfermode) { default: - case 0: return (channel[i].destaddr); //0 - case 1: return (channel[i].destaddr + (index & 1)); //0,1 - case 2: return (channel[i].destaddr); //0,0 - case 3: return (channel[i].destaddr + ((index >> 1) & 1)); //0,0,1,1 - case 4: return (channel[i].destaddr + (index & 3)); //0,1,2,3 - case 5: return (channel[i].destaddr + (index & 1)); //0,1,0,1 - case 6: return (channel[i].destaddr); //0,0 [2] - case 7: return (channel[i].destaddr + ((index >> 1) & 1)); //0,0,1,1 [3] + case 0: return (channel[i].destaddr); //0 + case 1: return (channel[i].destaddr + (index & 1)); //0,1 + case 2: return (channel[i].destaddr); //0,0 + case 3: return (channel[i].destaddr + ((index >> 1) & 1)); //0,0,1,1 + case 4: return (channel[i].destaddr + (index & 3)); //0,1,2,3 + case 5: return (channel[i].destaddr + (index & 1)); //0,1,0,1 + case 6: return (channel[i].destaddr); //0,0 [2] + case 7: return (channel[i].destaddr + ((index >> 1) & 1)); //0,0,1,1 [3] } } @@ -96,29 +96,6 @@ inline uint32 sCPU::hdma_iaddr(uint8 i) { * DMA functions *****/ -void sCPU::dma_transfertobusb(uint8 i, uint8 bbus) { - if(cartridge.info.sdd1 == true && sdd1.dma_active() == true) { - bus.write(0x2100 | bbus, sdd1.dma_read()); - } else { - dma_transfer(0, bbus, dma_addr(i)); - } - channel[i].xfersize--; -} - -void sCPU::dma_transfertobusa(uint8 i, uint8 bbus) { - dma_transfer(1, bbus, dma_addr(i)); - channel[i].xfersize--; -} - -inline void sCPU::dma_write(uint8 i, uint8 index) { - //cannot use dma_transfer() directly, due to current S-DD1 implementation - if(channel[i].direction == 0) { - dma_transfertobusb(i, index); - } else { - dma_transfertobusa(i, index); - } -} - uint8 sCPU::dma_enabled_channels() { uint8 r = 0; for(unsigned i = 0; i < 8; i++) { @@ -136,23 +113,10 @@ void sCPU::dma_run() { dma_add_clocks(8); cycle_edge(); - if(cartridge.info.sdd1 == true) { - sdd1.dma_begin(i, (channel[i].srcbank << 16) | (channel[i].srcaddr), channel[i].xfersize); - } - - if(tracer.enabled() == true && tracer.cpudma_enabled() == true) { - tprintf("[DMA] channel:%d direction:%s reverse:%c fixed:%c mode:%d b_addr:$21%0.2x " - "a_addr:$%0.2x%0.4x length:$%0.4x (%5d)", - i, channel[i].direction ? "b->a" : "a->b", channel[i].reversexfer ? '1' : '0', - channel[i].fixedxfer ? '1' : '0', channel[i].xfermode, channel[i].destaddr, - channel[i].srcbank, channel[i].srcaddr, - channel[i].xfersize, channel[i].xfersize ? channel[i].xfersize : 65536); - } - unsigned index = 0; do { - dma_write(i, dma_bbus(i, index++)); - } while(channel[i].dma_enabled && channel[i].xfersize); + dma_transfer(channel[i].direction, dma_bbus(i, index++), dma_addr(i)); + } while(channel[i].dma_enabled && --channel[i].xfersize); channel[i].dma_enabled = false; } @@ -215,7 +179,7 @@ void sCPU::hdma_run() { for(unsigned i = 0; i < 8; i++) { if(hdma_active(i) == false) continue; - channel[i].dma_enabled = false; //HDMA run during DMA will stop DMA mid-transfer + channel[i].dma_enabled = false; //HDMA run during DMA will stop DMA mid-transfer if(channel[i].hdma_do_transfer) { static const unsigned transfer_length[8] = { 1, 2, 2, 4, 4, 4, 2, 4 }; @@ -254,7 +218,7 @@ void sCPU::hdma_init() { for(unsigned i = 0; i < 8; i++) { if(!channel[i].hdma_enabled) continue; - channel[i].dma_enabled = false; //HDMA init during DMA will stop DMA mid-transfer + channel[i].dma_enabled = false; //HDMA init during DMA will stop DMA mid-transfer channel[i].hdma_addr = channel[i].srcaddr; hdma_update(i); @@ -282,7 +246,7 @@ void sCPU::dma_power() { channel[i].srcbank = 0xff; channel[i].xfersize = 0xffff; - //channel[i].hdma_iaddr = 0xffff; //union with xfersize + //channel[i].hdma_iaddr = 0xffff; //union with xfersize channel[i].hdma_ibank = 0xff; channel[i].hdma_addr = 0xffff; diff --git a/src/cpu/scpu/dma/dma.h b/src/cpu/scpu/dma/dma.hpp similarity index 85% rename from src/cpu/scpu/dma/dma.h rename to src/cpu/scpu/dma/dma.hpp index f331b9b7..3a7af05e 100644 --- a/src/cpu/scpu/dma/dma.h +++ b/src/cpu/scpu/dma/dma.hpp @@ -55,9 +55,6 @@ uint32 hdma_addr(uint8 i); uint32 hdma_iaddr(uint8 i); - void dma_transfertobusb(uint8 i, uint8 bbus); - void dma_transfertobusa(uint8 i, uint8 bbus); - void dma_write(uint8 i, uint8 index); uint8 dma_enabled_channels(); void dma_run(); diff --git a/src/cpu/scpu/memory/memory.h b/src/cpu/scpu/memory/memory.hpp similarity index 100% rename from src/cpu/scpu/memory/memory.h rename to src/cpu/scpu/memory/memory.hpp diff --git a/src/cpu/scpu/mmio/mmio.cpp b/src/cpu/scpu/mmio/mmio.cpp index d0bae834..1e3698cb 100644 --- a/src/cpu/scpu/mmio/mmio.cpp +++ b/src/cpu/scpu/mmio/mmio.cpp @@ -190,13 +190,13 @@ uint8 sCPU::mmio_r4212() { uint16 vs = ppu.overscan() == false ? 225 : 240; //auto joypad polling - if(status.vcounter >= vs && status.vcounter <= (vs + 2))r |= 0x01; + if(ppucounter.vcounter() >= vs && ppucounter.vcounter() <= (vs + 2))r |= 0x01; //hblank - if(status.hcounter <= 2 || status.hcounter >= 1096)r |= 0x40; + if(ppucounter.hcounter() <= 2 || ppucounter.hcounter() >= 1096)r |= 0x40; //vblank - if(status.vcounter >= vs)r |= 0x80; + if(ppucounter.vcounter() >= vs)r |= 0x80; return r; } diff --git a/src/cpu/scpu/mmio/mmio.h b/src/cpu/scpu/mmio/mmio.hpp similarity index 100% rename from src/cpu/scpu/mmio/mmio.h rename to src/cpu/scpu/mmio/mmio.hpp diff --git a/src/cpu/scpu/scpu.cpp b/src/cpu/scpu/scpu.cpp index 518ae086..b5bd2329 100644 --- a/src/cpu/scpu/scpu.cpp +++ b/src/cpu/scpu/scpu.cpp @@ -1,4 +1,4 @@ -#include "../../base.h" +#include <../base.hpp> #define SCPU_CPP #include "core/core.cpp" diff --git a/src/cpu/scpu/scpu.h b/src/cpu/scpu/scpu.hpp similarity index 83% rename from src/cpu/scpu/scpu.h rename to src/cpu/scpu/scpu.hpp index 25b274f9..f0f81b52 100644 --- a/src/cpu/scpu/scpu.h +++ b/src/cpu/scpu/scpu.hpp @@ -2,11 +2,11 @@ class sCPU : public CPU { public: void enter(); - #include "core/core.h" - #include "dma/dma.h" - #include "memory/memory.h" - #include "mmio/mmio.h" - #include "timing/timing.h" + #include "core/core.hpp" + #include "dma/dma.hpp" + #include "memory/memory.hpp" + #include "mmio/mmio.hpp" + #include "timing/timing.hpp" struct { bool wai; @@ -44,14 +44,9 @@ public: bool in_opcode; unsigned clock_count; + unsigned line_clocks; //timing - uint16 vcounter, hcounter; - uint16 field_lines, line_clocks; - - bool line_rendered; - uint16 line_render_position; - bool dram_refreshed; uint16 dram_refresh_position; diff --git a/src/cpu/scpu/timing/irq.cpp b/src/cpu/scpu/timing/irq.cpp index 5b151474..f1c4d068 100644 --- a/src/cpu/scpu/timing/irq.cpp +++ b/src/cpu/scpu/timing/irq.cpp @@ -1,13 +1,20 @@ -#ifdef SCPU_CPP - +#ifdef SCPU_CPP + void sCPU::update_interrupts() { - if(irq_pos_valid() == true) { - status.virq_trigger_pos = status.virq_pos; - status.hirq_trigger_pos = 4 * ((status.hirq_enabled) ? (status.hirq_pos + 1) : 0); - } else { - status.virq_trigger_pos = IRQ_TRIGGER_NEVER; - status.hirq_trigger_pos = IRQ_TRIGGER_NEVER; + unsigned vtime = status.virq_pos; + unsigned htime = status.hirq_enabled ? status.hirq_pos : 0; + unsigned vlimit = (snes.region() == SNES::NTSC ? 525 : 625) >> 1; + + //an IRQ for the very last dot of a field cannot trigger an IRQ + if((vtime == (vlimit - 1) && htime == 339 && ppu.interlace() == false) + || (vtime == vlimit && htime == 339) + ) { + vtime = 0x03ff; + htime = 0x03ff; } + + status.virq_trigger_pos = vtime; + status.hirq_trigger_pos = 4 * (status.hirq_enabled ? htime + 1 : 0); } alwaysinline void sCPU::poll_interrupts() { @@ -22,7 +29,8 @@ alwaysinline void sCPU::poll_interrupts() { } //NMI test - history.query(2, vpos, hpos); + vpos = ppucounter.vcounter(2); + hpos = ppucounter.hcounter(2); bool nmi_valid = (vpos >= (!ppu.overscan() ? 225 : 240)); if(status.nmi_valid == false && nmi_valid == true) { //0->1 edge sensitive transition @@ -41,7 +49,8 @@ alwaysinline void sCPU::poll_interrupts() { } //IRQ test - history.query(10, vpos, hpos); + vpos = ppucounter.vcounter(10); + hpos = ppucounter.hcounter(10); bool irq_valid = (status.virq_enabled == true || status.hirq_enabled == true); if(irq_valid == true) { if(status.virq_enabled == true && vpos != status.virq_trigger_pos) irq_valid = false; @@ -103,23 +112,6 @@ bool sCPU::timeup() { return result; } -bool sCPU::irq_pos_valid() { - uint vpos = status.virq_pos; - uint hpos = (status.hirq_enabled) ? status.hirq_pos : 0; - uint vlimit = (snes.region() == SNES::NTSC ? 525 : 625) >> 1; - //positions that can never be latched - //vlimit = 262/NTSC, 312/PAL - //PAL results are unverified on hardware - if(vpos == 240 && hpos == 339 && ppu.interlace() == false && ppu.field() == 1) return false; - if(vpos == (vlimit - 1) && hpos == 339 && ppu.interlace() == false) return false; - if(vpos == vlimit && ppu.interlace() == false) return false; - if(vpos == vlimit && hpos == 339) return false; - if(vpos > vlimit) return false; - if(hpos > 339) return false; - - return true; -} - alwaysinline bool sCPU::nmi_test() { if(status.nmi_transition == false) return false; status.nmi_transition = false; @@ -131,7 +123,7 @@ alwaysinline bool sCPU::irq_test() { if(status.irq_transition == false) return false; status.irq_transition = false; event.wai = false; - return regs.p.i ? false : true; + return !regs.p.i; } -#endif //ifdef SCPU_CPP +#endif //ifdef SCPU_CPP diff --git a/src/cpu/scpu/timing/joypad.cpp b/src/cpu/scpu/timing/joypad.cpp index 6f42b004..34b9e772 100644 --- a/src/cpu/scpu/timing/joypad.cpp +++ b/src/cpu/scpu/timing/joypad.cpp @@ -25,4 +25,4 @@ void sCPU::run_auto_joypad_poll() { status.joy4h = joy4 >> 8; } -#endif //ifdef SCPU_CPP +#endif //ifdef SCPU_CPP diff --git a/src/cpu/scpu/timing/timing.cpp b/src/cpu/scpu/timing/timing.cpp index 45c44a96..89d2522b 100644 --- a/src/cpu/scpu/timing/timing.cpp +++ b/src/cpu/scpu/timing/timing.cpp @@ -4,85 +4,49 @@ #include "joypad.cpp" unsigned sCPU::dma_counter() { - return (status.dma_counter + status.hcounter) & 7; -} - -/***** - * One PPU dot = 4 CPU clocks - * - * PPU dots 323 and 327 are 6 CPU clocks long. - * This does not apply to NTSC non-interlace scanline 240 on odd fields. This is - * because the PPU skips one dot to alter the color burst phase of the video signal. - * - * Dot 323 range = { 1292, 1294, 1296 } - * Dot 327 range = { 1310, 1312, 1314 } - *****/ - -#define ntsc_color_burst_phase_shift_scanline() ( \ - snes.region() == SNES::NTSC && status.vcounter == 240 && \ - ppu.interlace() == false && ppu.field() == 1 \ -) - -uint16 sCPU::hdot() { - if(ntsc_color_burst_phase_shift_scanline() == true) return (status.hcounter >> 2); - return (status.hcounter - ((status.hcounter > 1292) << 1) - ((status.hcounter > 1310) << 1)) >> 2; + return (status.dma_counter + ppucounter.hcounter()) & 7; } void sCPU::add_clocks(unsigned clocks) { if(status.dram_refreshed == false) { - if(status.hcounter + clocks >= status.dram_refresh_position) { + if(ppucounter.hcounter() + clocks >= status.dram_refresh_position) { status.dram_refreshed = true; clocks += 40; } } counter.sub(counter.irq_delay, clocks); - scheduler.addclocks_cpu(clocks); - - clocks >>= 1; - while(clocks--) { - history.enqueue(status.vcounter, status.hcounter); - status.hcounter += 2; - if(status.hcounter >= status.line_clocks) scanline(); - poll_interrupts(); + unsigned ticks = clocks >> 1; + while(ticks--) { + ppucounter.tick(); snes.input.tick(); + poll_interrupts(); } + + scheduler.addclocks_cpu(clocks); } void sCPU::scanline() { - status.hcounter = 0; status.dma_counter = (status.dma_counter + status.line_clocks) & 7; - if(++status.vcounter >= status.field_lines) frame(); - status.line_clocks = (ntsc_color_burst_phase_shift_scanline() == false) ? 1364 : 1360; + status.line_clocks = ppucounter.lineclocks(); + if(ppucounter.vcounter() == 0) frame(); //dram refresh occurs once every scanline status.dram_refreshed = false; if(cpu_version == 2) status.dram_refresh_position = 530 + 8 - dma_counter(); //hdma triggers once every visible scanline - status.line_rendered = false; - status.hdma_triggered = (status.vcounter <= (ppu.overscan() == false ? 224 : 239)) ? false : true; - - ppu.scanline(); - snes.scanline(); + status.hdma_triggered = (ppucounter.vcounter() <= (ppu.overscan() == false ? 224 : 239)) ? false : true; update_interrupts(); - if(status.auto_joypad_poll == true && status.vcounter == (ppu.overscan() == false ? 227 : 242)) { + if(status.auto_joypad_poll == true && ppucounter.vcounter() == (ppu.overscan() == false ? 227 : 242)) { snes.input.poll(); run_auto_joypad_poll(); } } void sCPU::frame() { - ppu.frame(); - snes.frame(); - - status.vcounter = 0; - status.field_lines = (snes.region() == SNES::NTSC ? 525 : 625) >> 1; - //interlaced even fields have one extra scanline (263+262=525 NTSC, 313+312=625 PAL) - if(ppu.interlace() == true && ppu.field() == 0) status.field_lines++; - status.hdmainit_triggered = false; if(cpu_version == 1) { status.hdmainit_trigger_position = 12 + 8 - dma_counter(); @@ -91,11 +55,7 @@ void sCPU::frame() { } } -/***** - * precycle_edge() - * - * Used for H/DMA bus synchronization - *****/ +//used for H/DMA bus synchronization void sCPU::precycle_edge() { if(status.dma_state == DMA_CPUsync) { add_clocks(status.clock_count - (status.dma_clocks % status.clock_count)); @@ -103,21 +63,10 @@ void sCPU::precycle_edge() { } } -/***** - * cycle_edge() - * - * Used to test for H/DMA, which can trigger on the edge of every opcode cycle. - *****/ +//used to test for H/DMA, which can trigger on the edge of every opcode cycle. void sCPU::cycle_edge() { - if(status.line_rendered == false) { - if(status.hcounter >= status.line_render_position) { - status.line_rendered = true; - ppu.render_scanline(); - } - } - if(status.hdmainit_triggered == false) { - if(status.hcounter >= status.hdmainit_trigger_position || status.vcounter) { + if(ppucounter.hcounter() >= status.hdmainit_trigger_position || ppucounter.vcounter()) { status.hdmainit_triggered = true; hdma_init_reset(); if(hdma_enabled_channels()) { @@ -128,7 +77,7 @@ void sCPU::cycle_edge() { } if(status.hdma_triggered == false) { - if(status.hcounter >= 1104) { + if(ppucounter.hcounter() >= 1104) { status.hdma_triggered = true; if(hdma_active_channels()) { status.hdma_pending = true; @@ -173,15 +122,11 @@ void sCPU::cycle_edge() { } } -/***** - * last_cycle() - * - * Used to test for NMI/IRQ, which can trigger on the edge of every opcode. - * Test one cycle early to simulate two-stage pipeline of x816 CPU. - * - * status.irq_delay is used to simulate hardware delay before interrupts can - * trigger during certain events (immediately after DMA, writes to $4200, etc) - *****/ +//used to test for NMI/IRQ, which can trigger on the edge of every opcode. +//test one cycle early to simulate two-stage pipeline of x816 CPU. +// +//status.irq_delay is used to simulate hardware delay before interrupts can +//trigger during certain events (immediately after DMA, writes to $4200, etc) void sCPU::last_cycle() { if(counter.irq_delay) return; @@ -204,15 +149,7 @@ void sCPU::timing_reset() { counter.hw_math = 0; status.clock_count = 0; - - status.vcounter = 0; - status.hcounter = 0; - - status.field_lines = (snes.region() == SNES::NTSC ? 525 : 625) >> 1; - status.line_clocks = 1364; - - status.line_rendered = false; - status.line_render_position = min(1112U, (unsigned)config::ppu.hack.render_scanline_position); + status.line_clocks = ppucounter.lineclocks(); status.dram_refreshed = false; status.dram_refresh_position = (cpu_version == 1) ? 530 : 538; @@ -242,15 +179,8 @@ void sCPU::timing_reset() { status.hdma_pending = false; status.hdma_mode = 0; status.dma_state = DMA_Inactive; - - history.reset(); - - //initial latch values for $213c/$213d - //[x]0035 : [y]0000 (53.0 -> 212) [lda $2137] - //[x]0038 : [y]0000 (56.5 -> 226) [nop : lda $2137] - add_clocks(186); } #undef ntsc_color_burst_phase_shift_scanline -#endif //ifdef SCPU_CPP +#endif //ifdef SCPU_CPP diff --git a/src/cpu/scpu/timing/timing.h b/src/cpu/scpu/timing/timing.h deleted file mode 100644 index 347c0288..00000000 --- a/src/cpu/scpu/timing/timing.h +++ /dev/null @@ -1,57 +0,0 @@ - alwaysinline uint16 vcounter() { return status.vcounter; } - alwaysinline uint16 hcounter() { return status.hcounter; } - uint16 hdot(); - unsigned dma_counter(); - - void add_clocks(unsigned clocks); - void scanline(); - void frame(); - - void precycle_edge(); - void cycle_edge(); - void last_cycle(); - uint32 clocks_executed(); - - void timing_power(); - void timing_reset(); - - //timeshifting -- needed by NMI and IRQ timing - struct History { - struct Time { - uint16 vcounter; - uint16 hcounter; - } time[32]; - unsigned index; - alwaysinline void enqueue(uint16 vcounter, uint16 hcounter) { - Time &t = time[index++]; - index &= 31; - t.vcounter = vcounter; - t.hcounter = hcounter; - } - alwaysinline void query(unsigned offset, uint16 &vcounter, uint16 &hcounter) { - Time &t = time[(index - (offset >> 1)) & 31]; - vcounter = t.vcounter; - hcounter = t.hcounter; - } - void reset() { - index = 0; - for(unsigned i = 0; i < 32; i++) time[i].vcounter = time[i].hcounter = 0; - } - History() { reset(); } - } history; - - //irq.cpp - enum { IRQ_TRIGGER_NEVER = 0x3fff }; - void update_interrupts(); - void poll_interrupts(); - void nmitimen_update(uint8 data); - void hvtime_update(uint16 addr); - bool rdnmi(); - bool timeup(); - - bool irq_pos_valid(); - bool nmi_test(); - bool irq_test(); - - //joypad.cpp - void run_auto_joypad_poll(); diff --git a/src/cpu/scpu/timing/timing.hpp b/src/cpu/scpu/timing/timing.hpp new file mode 100644 index 00000000..d0431f08 --- /dev/null +++ b/src/cpu/scpu/timing/timing.hpp @@ -0,0 +1,27 @@ + //timing.cpp + unsigned dma_counter(); + + void add_clocks(unsigned clocks); + void scanline(); + void frame(); + + void precycle_edge(); + void cycle_edge(); + void last_cycle(); + + void timing_power(); + void timing_reset(); + + //irq.cpp + void update_interrupts(); + void poll_interrupts(); + void nmitimen_update(uint8 data); + void hvtime_update(uint16 addr); + bool rdnmi(); + bool timeup(); + + bool nmi_test(); + bool irq_test(); + + //joypad.cpp + void run_auto_joypad_poll(); diff --git a/src/dsp/adsp/adsp.cpp b/src/dsp/adsp/adsp.cpp index e29edb1f..d758ca1b 100644 --- a/src/dsp/adsp/adsp.cpp +++ b/src/dsp/adsp/adsp.cpp @@ -1,4 +1,4 @@ -#include "../../base.h" +#include <../base.hpp> #define ADSP_CPP #include "adsp_tables.cpp" diff --git a/src/dsp/adsp/adsp.h b/src/dsp/adsp/adsp.hpp similarity index 100% rename from src/dsp/adsp/adsp.h rename to src/dsp/adsp/adsp.hpp diff --git a/src/dsp/bdsp/bdsp.cpp b/src/dsp/bdsp/bdsp.cpp deleted file mode 100644 index 6e984dc9..00000000 --- a/src/dsp/bdsp/bdsp.cpp +++ /dev/null @@ -1,681 +0,0 @@ -/* This code is heavily customized for bsnes and requires cothreads. -Original portable snes_spc library available at http://www.slack.net/~ant/ -Copyright (C) 2007 Shay Green. See license.txt. */ - -#include "../../base.h" - -int const brr_block_size = 9; - -// Accesses global DSP register -#define REG(n) m.regs [r_##n] - -// Accesses voice DSP register -#define VREG(r,n) r [v_##n] - -// Volume registers and efb are signed! Easy to forget int8 cast. -// Prefixes are to avoid accidental use of locals with same names. - -// Gaussian interpolation - -static short const gauss [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, - 6, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, - 11, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 15, 16, 16, 17, 17, - 18, 19, 19, 20, 20, 21, 21, 22, 23, 23, 24, 24, 25, 26, 27, 27, - 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 36, 36, 37, 38, 39, 40, - 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, - 58, 59, 60, 61, 62, 64, 65, 66, 67, 69, 70, 71, 73, 74, 76, 77, - 78, 80, 81, 83, 84, 86, 87, 89, 90, 92, 94, 95, 97, 99, 100, 102, - 104, 106, 107, 109, 111, 113, 115, 117, 118, 120, 122, 124, 126, 128, 130, 132, - 134, 137, 139, 141, 143, 145, 147, 150, 152, 154, 156, 159, 161, 163, 166, 168, - 171, 173, 175, 178, 180, 183, 186, 188, 191, 193, 196, 199, 201, 204, 207, 210, - 212, 215, 218, 221, 224, 227, 230, 233, 236, 239, 242, 245, 248, 251, 254, 257, - 260, 263, 267, 270, 273, 276, 280, 283, 286, 290, 293, 297, 300, 304, 307, 311, - 314, 318, 321, 325, 328, 332, 336, 339, 343, 347, 351, 354, 358, 362, 366, 370, - 374, 378, 381, 385, 389, 393, 397, 401, 405, 410, 414, 418, 422, 426, 430, 434, - 439, 443, 447, 451, 456, 460, 464, 469, 473, 477, 482, 486, 491, 495, 499, 504, - 508, 513, 517, 522, 527, 531, 536, 540, 545, 550, 554, 559, 563, 568, 573, 577, - 582, 587, 592, 596, 601, 606, 611, 615, 620, 625, 630, 635, 640, 644, 649, 654, - 659, 664, 669, 674, 678, 683, 688, 693, 698, 703, 708, 713, 718, 723, 728, 732, - 737, 742, 747, 752, 757, 762, 767, 772, 777, 782, 787, 792, 797, 802, 806, 811, - 816, 821, 826, 831, 836, 841, 846, 851, 855, 860, 865, 870, 875, 880, 884, 889, - 894, 899, 904, 908, 913, 918, 923, 927, 932, 937, 941, 946, 951, 955, 960, 965, - 969, 974, 978, 983, 988, 992, 997,1001,1005,1010,1014,1019,1023,1027,1032,1036, -1040,1045,1049,1053,1057,1061,1066,1070,1074,1078,1082,1086,1090,1094,1098,1102, -1106,1109,1113,1117,1121,1125,1128,1132,1136,1139,1143,1146,1150,1153,1157,1160, -1164,1167,1170,1174,1177,1180,1183,1186,1190,1193,1196,1199,1202,1205,1207,1210, -1213,1216,1219,1221,1224,1227,1229,1232,1234,1237,1239,1241,1244,1246,1248,1251, -1253,1255,1257,1259,1261,1263,1265,1267,1269,1270,1272,1274,1275,1277,1279,1280, -1282,1283,1284,1286,1287,1288,1290,1291,1292,1293,1294,1295,1296,1297,1297,1298, -1299,1300,1300,1301,1302,1302,1303,1303,1303,1304,1304,1304,1304,1304,1305,1305, -}; - - -//// Counters - -int const simple_counter_range = 2048 * 5 * 3; // 30720 - -static unsigned const counter_rates [32] = -{ - simple_counter_range + 1, // never fires - 2048, 1536, - 1280, 1024, 768, - 640, 512, 384, - 320, 256, 192, - 160, 128, 96, - 80, 64, 48, - 40, 32, 24, - 20, 16, 12, - 10, 8, 6, - 5, 4, 3, - 2, - 1 -}; - -static unsigned const counter_offsets [32] = -{ - 1, 0, 1040, - 536, 0, 1040, - 536, 0, 1040, - 536, 0, 1040, - 536, 0, 1040, - 536, 0, 1040, - 536, 0, 1040, - 536, 0, 1040, - 536, 0, 1040, - 536, 0, 1040, - 0, - 0 -}; - -inline unsigned bDSP::read_counter( int rate ) -{ - return ((unsigned) m.counter + counter_offsets [rate]) % counter_rates [rate]; -} - - -//// Envelope - -inline void bDSP::run_envelope( voice_t* const v ) -{ - int env = v->env; - if ( v->env_mode == env_release ) - { - if ( (env -= 0x8) < 0 ) - env = 0; - v->env = env; - } - else - { - int rate; - int env_data = VREG(v->regs,adsr1); - if ( m.t_adsr0 & 0x80 ) // ADSR - { - if ( v->env_mode >= env_decay ) - { - env--; - env -= asr<8>( env ); - rate = env_data & 0x1F; - if ( v->env_mode == env_decay ) - rate = (m.t_adsr0 >> 3 & 0x0E) + 0x10; - } - else // env_attack - { - rate = (m.t_adsr0 & 0x0F) * 2 + 1; - env += rate < 31 ? 0x20 : 0x400; - } - } - else // GAIN - { - env_data = VREG(v->regs,gain); - int mode = env_data >> 5; - if ( mode < 4 ) // direct - { - env = env_data * 0x10; - rate = 31; - } - else - { - rate = env_data & 0x1F; - if ( mode == 4 ) // 4: linear decrease - { - env -= 0x20; - } - else if ( mode < 6 ) // 5: exponential decrease - { - env--; - env -= asr<8>( env ); - } - else // 6,7: linear increase - { - env += 0x20; - if ( mode > 6 && (unsigned) v->hidden_env >= 0x600 ) - env += 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; - - // unsigned cast because linear decrease going negative also triggers this - if ( (unsigned) env > 0x7FF ) - { - env = (env < 0 ? 0 : 0x7FF); - if ( v->env_mode == env_attack ) - v->env_mode = env_decay; - } - - if ( !read_counter( rate ) ) - v->env = env; // nothing else is controlled by the counter - } -} - - -//// BRR Decoding - -inline void bDSP::decode_brr( voice_t* v ) -{ - // Arrange the four input nybbles in 0xABCD order for easy decoding - int nybbles = m.t_brr_byte * 0x100 + ram [(v->brr_addr + v->brr_offset + 1) & 0xFFFF]; - - // Write to next four samples in circular buffer - int* pos = &v->buf [v->buf_pos]; - if ( (v->buf_pos += 4) >= brr_buf_size ) - v->buf_pos = 0; - - // Decode four samples - for ( int* end = pos + 4; pos < end; pos++ ) - { - // Extract nybble and sign-extend - int s = asr<12>( sclip<16>( nybbles ) ); - nybbles <<= 4; - - // Shift sample based on header - int const shift = m.t_brr_header >> 4; - s = asr<1>( s << shift ); - if ( shift >= 0xD ) // handle invalid range - s = (s < 0 ? -0x800 : 0); - - // Apply IIR filter (8 is the most commonly used) - int const p1 = pos [brr_buf_size - 1]; - int const p2 = asr<1>( pos [brr_buf_size - 2] ); - switch ( m.t_brr_header >> 2 & 3 ) - { - case 1: s += asr<1>( p1 ) + asr<5>( -p1 ); break; // s += p1 * 0.4687500 - case 2: s += p1 + asr<6>( p1 * -3 ) - p2 + asr<4>( p2 ); break; // s += p1 * 0.9531250 - p2 * 0.46875 - case 3: s += p1 + asr<7>( p1 * -13 ) - p2 + asr<4>( p2 * 3 ); break; // s += p1 * 0.8984375 - p2 * 0.40625 - } - - // Adjust and write sample - s = sclip<16>( sclamp<16>( s ) * 2 ); - pos [brr_buf_size] = pos [0] = s; // second copy simplifies wrap-around - } -} - - -//// Voices - -#define VOICE_CLOCK( n ) void bDSP::voice_##n( voice_t* const v ) - -inline VOICE_CLOCK( V1 ) -{ - m.t_dir_addr = m.t_dir * 0x100 + m.t_srcn * 4; - m.t_srcn = VREG(v->regs,srcn); -} -inline VOICE_CLOCK( V2 ) -{ - // Read sample pointer (ignored if not needed) - uint8 const* entry = &ram [m.t_dir_addr]; - if ( !v->kon_delay ) - entry += 2; - m.t_brr_next_addr = entry [0] | entry [1] << 8; - - m.t_adsr0 = VREG(v->regs,adsr0); - - // Read pitch, spread over two clocks - m.t_pitch = VREG(v->regs,pitchl); -} -inline VOICE_CLOCK( V3a ) -{ - m.t_pitch += (VREG(v->regs,pitchh) & 0x3F) << 8; -} -inline VOICE_CLOCK( V3b ) -{ - // Read BRR header and byte - m.t_brr_byte = ram [(v->brr_addr + v->brr_offset) & 0xFFFF]; - m.t_brr_header = ram [v->brr_addr]; // brr_addr doesn't need masking -} -VOICE_CLOCK( V3c ) -{ - // Pitch modulation using previous voice's output - if ( m.t_pmon & v->vbit ) - m.t_pitch += asr<10>( asr<5>( m.t_output ) * m.t_pitch ); - - if ( v->kon_delay ) - { - // Get ready to start BRR decoding on next sample - if ( v->kon_delay == 5 ) - { - v->brr_addr = m.t_brr_next_addr; - v->brr_offset = 1; - v->buf_pos = 0; - m.t_brr_header = 0; // header is ignored on this sample - } - - // Envelope is never run during KON - v->env = 0; - v->hidden_env = 0; - - // Disable BRR decoding until last three samples - v->interp_pos = 0; - if ( --v->kon_delay & 3 ) - v->interp_pos = 0x4000; - - // Pitch is never added during KON - m.t_pitch = 0; - } - - // Gaussian interpolation - { - // Make pointers into gaussian based on fractional position between samples - int offset = v->interp_pos >> 4 & 0xFF; - short const* fwd = gauss + 255 - offset; - short const* rev = gauss + offset; // mirror left half of gaussian - - int const* in = &v->buf [(v->interp_pos >> 12) + v->buf_pos]; - int out; - out = asr<11>( fwd [ 0] * in [0] ); - out += asr<11>( fwd [256] * in [1] ); - out += asr<11>( rev [256] * in [2] ); - out = sclip<16>( out ); - out += asr<11>( rev [ 0] * in [3] ); - - out = sclamp<16>( out ) & ~1; - - // Noise - if ( m.t_non & v->vbit ) - out = sclip<16>( m.noise * 2 ); - - // Apply envelope - m.t_output = asr<11>( out * v->env ) & ~1; - v->t_envx_out = (uint8) (v->env >> 4); - } - - // Immediate silence due to end of sample or soft reset - if ( REG(flg) & 0x80 || (m.t_brr_header & 3) == 1 ) - { - v->env_mode = env_release; - v->env = 0; - } - - if ( m.every_other_sample ) - { - // KOFF - if ( m.t_koff & v->vbit ) - v->env_mode = env_release; - - // KON - if ( m.kon & v->vbit ) - { - v->kon_delay = 5; - v->env_mode = env_attack; - } - } - - // Run envelope for next sample - if ( !v->kon_delay ) - run_envelope( v ); -} -inline void bDSP::voice_output( voice_t const* v, int ch ) -{ - // Apply left/right volume - int amp = asr<7>( m.t_output * (int8) VREG(v->regs,voll + ch) ); - - // Add to output total - m.t_main_out [ch] = sclamp<16>( m.t_main_out [ch] + amp ); - - // Optionally add to echo total - if ( m.t_eon & v->vbit ) - m.t_echo_out [ch] = sclamp<16>( m.t_echo_out [ch] + amp ); -} -VOICE_CLOCK( V4 ) -{ - // Decode BRR - m.t_looped = 0; - if ( v->interp_pos >= 0x4000 ) - { - decode_brr( v ); - - if ( (v->brr_offset += 2) >= brr_block_size ) - { - // Start decoding next BRR block - assert( v->brr_offset == brr_block_size ); - v->brr_addr = (v->brr_addr + brr_block_size) & 0xFFFF; - if ( m.t_brr_header & 1 ) - { - v->brr_addr = m.t_brr_next_addr; - m.t_looped = v->vbit; - } - v->brr_offset = 1; - } - } - - // Apply pitch - v->interp_pos = (v->interp_pos & 0x3FFF) + m.t_pitch; - - // Keep from getting too far ahead (when using pitch modulation) - if ( v->interp_pos > 0x7FFF ) - v->interp_pos = 0x7FFF; - - // Output left - voice_output( v, 0 ); -} -inline VOICE_CLOCK( V5 ) -{ - // Output right - voice_output( v, 1 ); - - // ENDX, OUTX, and ENVX won't update if you wrote to them 1-2 clocks earlier - int endx_buf = REG(endx) | m.t_looped; - - // Clear bit in ENDX if KON just began - if ( v->kon_delay == 5 ) - endx_buf &= ~v->vbit; - m.endx_buf = (uint8) endx_buf; -} -inline VOICE_CLOCK( V6 ) -{ - (void) v; // avoid compiler warning about unused v - m.outx_buf = (uint8) (m.t_output >> 8); -} -inline VOICE_CLOCK( V7 ) -{ - // Update ENDX - REG(endx) = m.endx_buf; - - m.envx_buf = v->t_envx_out; -} -inline VOICE_CLOCK( V8 ) -{ - // Update OUTX - VREG(v->regs,outx) = m.outx_buf; -} -inline VOICE_CLOCK( V9 ) -{ - // Update ENVX - VREG(v->regs,envx) = m.envx_buf; -} - -// Most voices do all these in one clock, so make a handy composite -inline VOICE_CLOCK( V3 ) -{ - voice_V3a( v ); - voice_V3b( v ); - voice_V3c( v ); -} - -// Common combinations of voice steps on different voices. This greatly reduces -// code size and allows everything to be inlined in these functions. -VOICE_CLOCK(V7_V4_V1) { voice_V7(v); voice_V1(v+3); voice_V4(v+1); } -VOICE_CLOCK(V8_V5_V2) { voice_V8(v); voice_V5(v+1); voice_V2(v+2); } -VOICE_CLOCK(V9_V6_V3) { voice_V9(v); voice_V6(v+1); voice_V3(v+2); } - - -//// Echo - -// Current echo buffer pointer for left/right channel -#define ECHO_PTR( ch ) (&ram [echo_ptr + ch * 2]) - -// Sample in echo history buffer, where 0 is the oldest -#define ECHO_FIR( i ) (m.echo_hist [(m.echo_hist_pos + (i)) & (echo_hist_size - 1)]) - -// Calculate FIR point for left/right channel -#define CALC_FIR( i, ch ) asr<6>( ECHO_FIR( i + 1 ) [ch] * (int8) REG(fir + i * 0x10) ) - -inline int get_echo_sample( void const* p ) -{ - return ((uint8 const*) p) [0] | - (( int8 const*) p) [1] << 8; -} - -inline void set_echo_sample( void* p, unsigned n ) -{ - ((uint8*) p) [0] = (uint8) n; - ((uint8*) p) [1] = (uint8) (n >> 8); -} - -inline int bDSP::calc_echo_output( int ch, int echo_in ) -{ - return sclamp<16>( - sclip<16>( asr<7>( m.t_main_out [ch] * (int8) REG(mvoll + ch * 0x10) ) ) + - sclip<16>( asr<7>( echo_in * (int8) REG(evoll + ch * 0x10) ) ) ); -} - - -//// Timing - -void bDSP::enter() -{ - int t_esa = REG(esa); - - while ( 1 ) - { - // n is currently ignored - #define NEXT_CLOCK( n ) \ - scheduler.addclocks_dsp( 3 * 8 ); - - // Execute clock for a particular voice - #define V( clock, voice ) voice_##clock( &m.voices [voice] ); - - /* The most common sequence of clocks uses composite operations - for efficiency. For example, the following are equivalent to the - individual steps on the right: - - V(V7_V4_V1,2) -> V(V7,2) V(V4,3) V(V1,5) - V(V8_V5_V2,2) -> V(V8,2) V(V5,3) V(V2,4) - V(V9_V6_V3,2) -> V(V9,2) V(V6,3) V(V3,4) */ - - NEXT_CLOCK( 0) V(V5,0)V(V2,1) - NEXT_CLOCK( 1) V(V6,0)V(V3,1) - NEXT_CLOCK( 2) V(V7_V4_V1,0) - NEXT_CLOCK( 3) V(V8_V5_V2,0) - NEXT_CLOCK( 4) V(V9_V6_V3,0) - NEXT_CLOCK( 5) V(V7_V4_V1,1) - NEXT_CLOCK( 6) V(V8_V5_V2,1) - NEXT_CLOCK( 7) V(V9_V6_V3,1) - NEXT_CLOCK( 8) V(V7_V4_V1,2) - NEXT_CLOCK( 9) V(V8_V5_V2,2) - NEXT_CLOCK(10) V(V9_V6_V3,2) - NEXT_CLOCK(11) V(V7_V4_V1,3) - NEXT_CLOCK(12) V(V8_V5_V2,3) - NEXT_CLOCK(13) V(V9_V6_V3,3) - NEXT_CLOCK(14) V(V7_V4_V1,4) - NEXT_CLOCK(15) V(V8_V5_V2,4) - NEXT_CLOCK(16) V(V9_V6_V3,4) - NEXT_CLOCK(17) V(V1,0) V(V7,5)V(V4,6) - NEXT_CLOCK(18) V(V8_V5_V2,5) - NEXT_CLOCK(19) V(V9_V6_V3,5) - NEXT_CLOCK(20) V(V1,1) V(V7,6)V(V4,7) - NEXT_CLOCK(21) V(V8,6)V(V5,7) V(V2,0) /* t_brr_next_addr order dependency */ - NEXT_CLOCK(22) V(V3a,0) V(V9,6)V(V6,7) - - // History - if ( ++m.echo_hist_pos >= echo_hist_size ) - m.echo_hist_pos = 0; - - int const echo_ptr = (t_esa * 0x100 + m.echo_offset) & 0xFFFF; - - // FIR - int echo_in_l = CALC_FIR( 0, 0 ); - int echo_in_r = CALC_FIR( 0, 1 ); - - ECHO_FIR( 0 ) [0] = asr<1>( get_echo_sample( ECHO_PTR( 0 ) ) ); - - NEXT_CLOCK(23) V(V7,7) - - echo_in_l += CALC_FIR( 1, 0 ) + CALC_FIR( 2, 0 ); - echo_in_r += CALC_FIR( 1, 1 ) + CALC_FIR( 2, 1 ); - - ECHO_FIR( 0 ) [1] = asr<1>( get_echo_sample( ECHO_PTR( 1 ) ) ); - - NEXT_CLOCK(24) V(V8,7) - - echo_in_l += CALC_FIR( 3, 0 ) + CALC_FIR( 4, 0 ) + CALC_FIR( 5, 0 ); - echo_in_r += CALC_FIR( 3, 1 ) + CALC_FIR( 4, 1 ) + CALC_FIR( 5, 1 ); - - NEXT_CLOCK(25) V(V3b,0) V(V9,7) - - echo_in_l = sclip<16>( echo_in_l + CALC_FIR( 6, 0 ) ) + sclip<16>( CALC_FIR( 7, 0 ) ); - echo_in_r = sclip<16>( echo_in_r + CALC_FIR( 6, 1 ) ) + sclip<16>( CALC_FIR( 7, 1 ) ); - - echo_in_l = sclamp<16>( echo_in_l ) & ~1; - echo_in_r = sclamp<16>( echo_in_r ) & ~1; - - NEXT_CLOCK(26) - - // Echo feedback - int echo_out_l = m.t_echo_out [0] + sclip<16>( asr<7>( echo_in_l * (int8) REG(efb) ) ); - int echo_out_r = m.t_echo_out [1] + sclip<16>( asr<7>( echo_in_r * (int8) REG(efb) ) ); - - echo_out_l = sclamp<16>( echo_out_l ) & ~1; - echo_out_r = sclamp<16>( echo_out_r ) & ~1; - - // Output - int main_out_l = calc_echo_output( 0, echo_in_l ); - - NEXT_CLOCK(27) - - int main_out_r = calc_echo_output( 1, echo_in_r ); - - // 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 ) - { - main_out_l = 0; - main_out_r = 0; - } - - // Output sample to DAC - snes.audio.update( main_out_l, main_out_r ); - - m.t_main_out [0] = 0; - m.t_main_out [1] = 0; - - m.t_pmon = REG(pmon) & 0xFE; // voice 0 doesn't support PMON - - NEXT_CLOCK(28) - - m.t_non = REG(non); - m.t_eon = REG(eon); - m.t_dir = REG(dir); - - int echo_disabled = REG(flg); - - NEXT_CLOCK(29) - - // Write left echo - if ( !(echo_disabled & 0x20) ) - set_echo_sample( ECHO_PTR( 0 ), echo_out_l ); - m.t_echo_out [0] = 0; - - t_esa = REG(esa); - - if ( !m.echo_offset ) - m.echo_length = (REG(edl) & 0x0F) * 0x800; - - m.echo_offset += 4; - if ( m.echo_offset >= m.echo_length ) - m.echo_offset = 0; - - if ( (m.every_other_sample ^= 1) != 0 ) - m.new_kon &= ~m.kon; // clears KON 63 clocks after it was last read - - echo_disabled = REG(flg); - - NEXT_CLOCK(30) - - // Write right echo - if ( !(echo_disabled & 0x20) ) - set_echo_sample( ECHO_PTR( 1 ), echo_out_r ); - m.t_echo_out [1] = 0; - - if ( m.every_other_sample ) - { - m.kon = m.new_kon; - m.t_koff = REG(koff); - } - - if ( --m.counter < 0 ) - m.counter = simple_counter_range - 1; - - // Noise - if ( !read_counter( REG(flg) & 0x1F ) ) - { - int feedback = (m.noise << 13) ^ (m.noise << 14); - m.noise = (feedback & 0x4000) ^ (m.noise >> 1); - } - - V(V3c,0) - - NEXT_CLOCK(31) V(V4,0) V(V1,2) - } -} - - -//// Setup - -bDSP::bDSP() { } - -bDSP::~bDSP() { } - -void bDSP::reset() -{ - REG(flg) = 0xE0; - - m.noise = 0x4000; - m.echo_hist_pos = 0; - m.every_other_sample = 1; - m.echo_offset = 0; - m.counter = 0; -} - -static uint8 const initial_regs [bDSP::register_count] = -{ - 0x45,0x8B,0x5A,0x9A,0xE4,0x82,0x1B,0x78,0x00,0x00,0xAA,0x96,0x89,0x0E,0xE0,0x80, - 0x2A,0x49,0x3D,0xBA,0x14,0xA0,0xAC,0xC5,0x00,0x00,0x51,0xBB,0x9C,0x4E,0x7B,0xFF, - 0xF4,0xFD,0x57,0x32,0x37,0xD9,0x42,0x22,0x00,0x00,0x5B,0x3C,0x9F,0x1B,0x87,0x9A, - 0x6F,0x27,0xAF,0x7B,0xE5,0x68,0x0A,0xD9,0x00,0x00,0x9A,0xC5,0x9C,0x4E,0x7B,0xFF, - 0xEA,0x21,0x78,0x4F,0xDD,0xED,0x24,0x14,0x00,0x00,0x77,0xB1,0xD1,0x36,0xC1,0x67, - 0x52,0x57,0x46,0x3D,0x59,0xF4,0x87,0xA4,0x00,0x00,0x7E,0x44,0x9C,0x4E,0x7B,0xFF, - 0x75,0xF5,0x06,0x97,0x10,0xC3,0x24,0xBB,0x00,0x00,0x7B,0x7A,0xE0,0x60,0x12,0x0F, - 0xF7,0x74,0x1C,0xE5,0x39,0x3D,0x73,0xC1,0x00,0x00,0x7A,0xB3,0xFF,0x4E,0x7B,0xFF -}; - -void bDSP::power() -{ - ram = (uint8*) smp.get_spcram_handle(); - memset( &m, 0, sizeof m ); - //memcpy( m.regs, initial_regs, sizeof m.regs ); - memset(m.regs, 0, sizeof m.regs); - REG(flg) = 0xe0; - - // Internal state - for ( int i = voice_count; --i >= 0; ) - { - voice_t* v = &m.voices [i]; - v->brr_offset = 1; - v->vbit = 1 << i; - v->regs = &m.regs [i * 0x10]; - } - m.new_kon = REG(kon); - m.t_dir = REG(dir); - - reset(); -} diff --git a/src/dsp/bdsp/bdsp.h b/src/dsp/bdsp/bdsp.h deleted file mode 100644 index 70b2ce57..00000000 --- a/src/dsp/bdsp/bdsp.h +++ /dev/null @@ -1,172 +0,0 @@ -class bDSP : public DSP { -public: - void enter(); - - uint8 read( uint8 addr ); - void write( uint8 addr, uint8 data ); - - void power(); - void reset(); - - bDSP(); - ~bDSP(); - - template inline T asr(const T x) { - enum { bits = (sizeof(T) << 3) - n }; - return sclip(x >> n); - } - -public: - - enum { echo_hist_size = 8 }; - enum { register_count = 128 }; - enum { voice_count = 8 }; - - enum env_mode_t { env_release, env_attack, env_decay, env_sustain }; - enum { brr_buf_size = 12 }; - struct voice_t - { - int buf [brr_buf_size*2];// decoded samples (twice the size to simplify wrap handling) - 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 - uint8* regs; // pointer to voice's DSP registers - int vbit; // bitmask for voice: 0x01 for voice 0, 0x02 for voice 1, etc. - int kon_delay; // KON delay/current setup phase - env_mode_t env_mode; - int env; // current envelope level - int hidden_env; // used by GAIN mode 7, very obscure quirk - uint8 t_envx_out; - }; -private: - - struct state_t - { - uint8 regs [register_count]; - - // Echo history keeps most recent 8 samples - int echo_hist [echo_hist_size] [2]; - int echo_hist_pos; - - int 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 - - // Hidden registers also written to when main register is written to - int new_kon; - uint8 endx_buf; - uint8 envx_buf; - uint8 outx_buf; - - // Temporary state between clocks - - // read once per sample - int t_pmon; - int t_non; - int t_eon; - int t_dir; - int t_koff; - - // read a few clocks ahead then used - int t_brr_next_addr; - int t_adsr0; - int t_brr_header; - int t_brr_byte; - int t_srcn; - - // internal state that is recalculated every sample - int t_dir_addr; - int t_pitch; - int t_output; - int t_looped; - - // left/right sums - int t_main_out [2]; - int t_echo_out [2]; - - voice_t voices [voice_count]; - }; - state_t m; - uint8* ram; - - unsigned read_counter( int rate ); - - void run_envelope( voice_t* const v ); - void decode_brr( voice_t* v ); - - void voice_output( voice_t const* v, int ch ); - void voice_V1( voice_t* const ); - void voice_V2( voice_t* const ); - void voice_V3( voice_t* const ); - void voice_V3a( voice_t* const ); - void voice_V3b( voice_t* const ); - void voice_V3c( voice_t* const ); - void voice_V4( voice_t* const ); - void voice_V5( voice_t* const ); - void voice_V6( voice_t* const ); - void voice_V7( voice_t* const ); - void voice_V8( voice_t* const ); - void voice_V9( voice_t* const ); - void voice_V7_V4_V1( voice_t* const ); - void voice_V8_V5_V2( voice_t* const ); - void voice_V9_V6_V3( voice_t* const ); - - int calc_echo_output( int ch, int sample ); - - // Global registers - enum { - 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 - }; - - // Voice registers - enum { - 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 - }; -}; - -inline uint8 bDSP::read( uint8 addr ) -{ - return m.regs [addr]; -} - -inline void bDSP::write( uint8 addr, uint8 data ) -{ - m.regs [addr] = data; - switch ( addr & 0x0F ) - { - case v_envx: - m.envx_buf = data; - break; - - case v_outx: - m.outx_buf = data; - break; - - case 0x0C: - if ( addr == r_kon ) - m.new_kon = data; - - if ( addr == r_endx ) // always cleared, regardless of data written - { - m.endx_buf = 0; - m.regs [r_endx] = 0; - } - break; - } -} diff --git a/src/dsp/bdsp/reference/bdsp.cpp b/src/dsp/bdsp/reference/bdsp.cpp deleted file mode 100644 index f5d0bd0c..00000000 --- a/src/dsp/bdsp/reference/bdsp.cpp +++ /dev/null @@ -1,32 +0,0 @@ -#include "../../base.h" -#include "spc_dsp.h" - -void bDSP::power() { - spc_dsp_init(r_smp->get_spcram_handle()); - spc_dsp_reset(); -} - -void bDSP::reset() { - spc_dsp_soft_reset(); -} - -uint8 bDSP::read(uint8 addr) { - return spc_dsp_read(addr); -} - -void bDSP::write(uint8 addr, uint8 data) { - spc_dsp_write(addr, data); -} - -#define SPC_DSP_CUSTOM_RUN 1 //causes spc_dsp_run() to not be defined since it's huge and we don't need it -#define SPC_DSP_OUT_HOOK(left, right) snes.audio_update(left, right); -#include "spc_dsp.cpp" - -void bDSP::enter() { loop: -#define PHASE(n) scheduler.addclocks_dsp(3); -#include "spc_dsp_timing.h" - goto loop; -} - -bDSP::bDSP() {} -bDSP::~bDSP() {} diff --git a/src/dsp/bdsp/reference/bdsp.h b/src/dsp/bdsp/reference/bdsp.h deleted file mode 100644 index 645efaec..00000000 --- a/src/dsp/bdsp/reference/bdsp.h +++ /dev/null @@ -1,10 +0,0 @@ -class bDSP : public DSP { public: - void enter(); - uint8 read(uint8 addr); - void write(uint8 addr, uint8 data); - void power(); - void reset(); - - bDSP(); - ~bDSP(); -}; diff --git a/src/dsp/bdsp/reference/readme.txt b/src/dsp/bdsp/reference/readme.txt deleted file mode 100644 index b080d1a8..00000000 --- a/src/dsp/bdsp/reference/readme.txt +++ /dev/null @@ -1,64 +0,0 @@ -Overall operation ------------------ -This DSP emulator fundamentally emulates the different options the DSP -performs on each clock. The pattern of operations repeats every 32 -clocks (except one minor detail, which repeats every 64 clocks instead). -There are three main types of operations: - -- Miscellaneous processing -- Voice processing -- Echo processing - -Each is done over several clocks, and several operations are done on -each clock. Each clock is defined as a separate function, then called -from a large switch block in a loop. - -Many times a value is read on one clock but not used until a later -clock, so many non-local temporary variables are used in the code to -store these values. These are named with t_ to make it clear that they -don't store long-term state. - - -Circular buffers ----------------- -Two circular buffers are used in the code (echo history and BRR decode). -Both need efficient index-based access with wrap-around. Things are -greatly simplified by repeating the contents of buffer twice, so instead -of - - 0 1 2 3 4 5 6 7 - -it stores - - 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 - -The position in this case would always be 0 to 7, so reading up to +8 -won't go outside buffer. This duplication is maintained by simply -writing data twice when filling buffer: - - 0 1 2 3 4 # 6 7 0 1 2 3 4 # 6 7 -new data -----^---------------^ - -No wrap checking needs to be done when writing either, since the above -reasoning holds. When making a state snapshot, only the first copy needs -to be saved. When restoring, simply duplicate the data twice. - - -Code ----- -- Currently all state is in static variables. They have either a t_ or -m_ prefix to allow easy migration to a structure. - -- Static state that persists over several samples or more is prefixed -with m_. - -- State which is temporary to the current sample is prefixed with t_. -These are usually just overwritten with new data on the next sample. -These generally correspond to temporaries/registers in actual DSP -itself. - -- Minimal stdint.h included in case your system doesn't have one. - - --- -Shay Green diff --git a/src/dsp/bdsp/reference/spc_dsp.cpp b/src/dsp/bdsp/reference/spc_dsp.cpp deleted file mode 100644 index 83715d89..00000000 --- a/src/dsp/bdsp/reference/spc_dsp.cpp +++ /dev/null @@ -1,828 +0,0 @@ -// http://www.slack.net/~ant/ - -#include "spc_dsp.h" - -#include -#include - -/* Copyright (C) 2007 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -// Volume registers and efb are signed! Easy to forget int8_t cast. -// Prefixes are to avoid accidental use of locals with same names. - -// Global registers -enum { - 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 -}; - -// Voice registers -enum { - 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 -}; - -// Internal envelope modes -enum env_mode_t { env_release, env_attack, env_decay, env_sustain }; - -// Internal voice state -enum { brr_buf_size = 12 }; -enum { brr_block_size = 9 }; -typedef struct voice_t -{ - int buf [brr_buf_size*2];// decoded samples (twice the size to simplify wrap handling) - 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 - uint8_t* regs; // pointer to voice's DSP registers - int vbit; // bitmask for voice: 0x01 for voice 0, 0x02 for voice 1, etc. - int kon_delay; // KON delay/current setup phase - enum env_mode_t env_mode; - int env; // current envelope level - int t_envx_out; - int hidden_env; // used by GAIN mode 7, very obscure quirk -} voice_t; - -static voice_t m_voice_state [spc_dsp_voice_count]; -static uint8_t* m_ram; // 64K shared RAM between DSP and SMP -spc_dsp_t m_spc_dsp; -spc_dsp_sample_t* m_spc_dsp_out_begin; -spc_dsp_sample_t* m_spc_dsp_out; -spc_dsp_sample_t* m_spc_dsp_out_end; - -// "Member" access -#define m m_spc_dsp - -// Access global DSP register -#define REG(n) m.regs [r_##n] - -// Access voice DSP register -#define VREG(r,n) r [v_##n] - -// if ( io < -32768 ) io = -32768; -// if ( io > 32767 ) io = 32767; -#define CLAMP16( io )\ -{\ - if ( (int16_t) io != io )\ - io = 0x7FFF ^ (io >> 31);\ -} - -// Gaussian interpolation - -static short const gauss [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, - 6, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, - 11, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 15, 16, 16, 17, 17, - 18, 19, 19, 20, 20, 21, 21, 22, 23, 23, 24, 24, 25, 26, 27, 27, - 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 36, 36, 37, 38, 39, 40, - 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, - 58, 59, 60, 61, 62, 64, 65, 66, 67, 69, 70, 71, 73, 74, 76, 77, - 78, 80, 81, 83, 84, 86, 87, 89, 90, 92, 94, 95, 97, 99, 100, 102, - 104, 106, 107, 109, 111, 113, 115, 117, 118, 120, 122, 124, 126, 128, 130, 132, - 134, 137, 139, 141, 143, 145, 147, 150, 152, 154, 156, 159, 161, 163, 166, 168, - 171, 173, 175, 178, 180, 183, 186, 188, 191, 193, 196, 199, 201, 204, 207, 210, - 212, 215, 218, 221, 224, 227, 230, 233, 236, 239, 242, 245, 248, 251, 254, 257, - 260, 263, 267, 270, 273, 276, 280, 283, 286, 290, 293, 297, 300, 304, 307, 311, - 314, 318, 321, 325, 328, 332, 336, 339, 343, 347, 351, 354, 358, 362, 366, 370, - 374, 378, 381, 385, 389, 393, 397, 401, 405, 410, 414, 418, 422, 426, 430, 434, - 439, 443, 447, 451, 456, 460, 464, 469, 473, 477, 482, 486, 491, 495, 499, 504, - 508, 513, 517, 522, 527, 531, 536, 540, 545, 550, 554, 559, 563, 568, 573, 577, - 582, 587, 592, 596, 601, 606, 611, 615, 620, 625, 630, 635, 640, 644, 649, 654, - 659, 664, 669, 674, 678, 683, 688, 693, 698, 703, 708, 713, 718, 723, 728, 732, - 737, 742, 747, 752, 757, 762, 767, 772, 777, 782, 787, 792, 797, 802, 806, 811, - 816, 821, 826, 831, 836, 841, 846, 851, 855, 860, 865, 870, 875, 880, 884, 889, - 894, 899, 904, 908, 913, 918, 923, 927, 932, 937, 941, 946, 951, 955, 960, 965, - 969, 974, 978, 983, 988, 992, 997,1001,1005,1010,1014,1019,1023,1027,1032,1036, -1040,1045,1049,1053,1057,1061,1066,1070,1074,1078,1082,1086,1090,1094,1098,1102, -1106,1109,1113,1117,1121,1125,1128,1132,1136,1139,1143,1146,1150,1153,1157,1160, -1164,1167,1170,1174,1177,1180,1183,1186,1190,1193,1196,1199,1202,1205,1207,1210, -1213,1216,1219,1221,1224,1227,1229,1232,1234,1237,1239,1241,1244,1246,1248,1251, -1253,1255,1257,1259,1261,1263,1265,1267,1269,1270,1272,1274,1275,1277,1279,1280, -1282,1283,1284,1286,1287,1288,1290,1291,1292,1293,1294,1295,1296,1297,1297,1298, -1299,1300,1300,1301,1302,1302,1303,1303,1303,1304,1304,1304,1304,1304,1305,1305, -}; - -static inline int interpolate( voice_t const* const v ) -{ - // Make pointers into gaussian based on fractional position between samples - int offset = v->interp_pos >> 4 & 0xFF; - short const* fwd = gauss + 255 - offset; - short const* rev = gauss + offset; // mirror left half of gaussian - - int const* in = &v->buf [(v->interp_pos >> 12) + v->buf_pos]; - int out; - out = (fwd [ 0] * in [0]) >> 11; - out += (fwd [256] * in [1]) >> 11; - out += (rev [256] * in [2]) >> 11; - out = (int16_t) out; - out += (rev [ 0] * in [3]) >> 11; - - CLAMP16( out ); - out &= ~1; - return out; -} - - -//// Counters - -enum { simple_counter_range = 2048 * 5 * 3 }; // 30720 - -static unsigned short const counter_rates [32] = -{ - simple_counter_range + 1, // never fires - 2048, 1536, - 1280, 1024, 768, - 640, 512, 384, - 320, 256, 192, - 160, 128, 96, - 80, 64, 48, - 40, 32, 24, - 20, 16, 12, - 10, 8, 6, - 5, 4, 3, - 2, - 1 -}; - -static unsigned short const counter_offsets [32] = -{ - 1, 0, 1040, - 536, 0, 1040, - 536, 0, 1040, - 536, 0, 1040, - 536, 0, 1040, - 536, 0, 1040, - 536, 0, 1040, - 536, 0, 1040, - 536, 0, 1040, - 536, 0, 1040, - 0, - 0 -}; - -static inline void init_counters( void ) { } - -static inline void run_counters( void ) -{ - if ( --m.counter < 0 ) - m.counter = simple_counter_range - 1; -} - -static inline unsigned read_counter( int rate ) -{ - return ((unsigned) m.counter + counter_offsets [rate]) % counter_rates [rate]; -} - - -//// Envelope - -static inline void run_envelope( voice_t* const v ) -{ - int env = v->env; - if ( v->env_mode == env_release ) // 60% - { - if ( (env -= 0x8) < 0 ) - env = 0; - v->env = env; - } - else - { - int rate; - int env_data = VREG(v->regs,adsr1); - if ( m.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 = (m.t_adsr0 >> 3 & 0x0E) + 0x10; - } - else // env_attack - { - rate = (m.t_adsr0 & 0x0F) * 2 + 1; - env += rate < 31 ? 0x20 : 0x400; - } - } - else // GAIN - { - env_data = VREG(v->regs,gain); - int mode = env_data >> 5; - if ( mode < 4 ) // direct - { - env = env_data * 0x10; - rate = 31; - } - else - { - rate = env_data & 0x1F; - if ( mode == 4 ) // 4: linear decrease - { - env -= 0x20; - } - else if ( mode < 6 ) // 5: exponential decrease - { - env--; - env -= env >> 8; - } - else // 6,7: linear increase - { - env += 0x20; - if ( mode > 6 && (unsigned) v->hidden_env >= 0x600 ) - env += 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; - - // unsigned cast because linear decrease going negative also triggers this - if ( (unsigned) env > 0x7FF ) - { - env = (env < 0 ? 0 : 0x7FF); - if ( v->env_mode == env_attack ) - v->env_mode = env_decay; - } - - if ( !read_counter( rate ) ) - v->env = env; // nothing else is controlled by the counter - } -} - - -//// BRR Decoding - -static inline void decode_brr( voice_t* v ) -{ - // Arrange the four input nybbles in 0xABCD order for easy decoding - int nybbles = m.t_brr_byte * 0x100 + m_ram [(v->brr_addr + v->brr_offset + 1) & 0xFFFF]; - - int const header = m.t_brr_header; - - // 0: >>1 1: <<0 2: <<1 ... 12: <<11 13-15: >>4 <<11 - static unsigned char const shifts [16 * 2] = { - 13,12,12,12,12,12,12,12,12,12,12, 12, 12, 16, 16, 16, - 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 11, 11, 11 - }; - int const scale = header >> 4; - int const right_shift = shifts [scale]; - int const left_shift = shifts [scale + 16]; - - // Write to next four samples in circular buffer - int* pos = &v->buf [v->buf_pos]; - if ( (v->buf_pos += 4) >= brr_buf_size ) - v->buf_pos = 0; - - // Decode four samples - for ( int* end = pos + 4; pos < end; pos++ ) - { - // Extract upper nybble and scale appropriately - int s = ((int16_t) nybbles >> right_shift) << left_shift; - nybbles <<= 4; - - // Apply IIR filter (8 is the most commonly used) - int const filter = header & 0x0C; - int const p1 = pos [brr_buf_size - 1]; - int const p2 = pos [brr_buf_size - 2] >> 1; - if ( filter >= 8 ) - { - s += p1; - s -= p2; - if ( filter == 8 ) // s += p1 * 0.953125 - p2 * 0.46875 - { - s += p2 >> 4; - s += (p1 * -3) >> 6; - } - else // s += p1 * 0.8984375 - p2 * 0.40625 - { - s += (p1 * -13) >> 7; - s += (p2 * 3) >> 4; - } - } - else if ( filter ) // s += p1 * 0.46875 - { - s += p1 >> 1; - s += (-p1) >> 5; - } - - // Adjust and write sample - CLAMP16( s ); - s = (int16_t) (s * 2); - pos [brr_buf_size] = pos [0] = s; // second copy simplifies wrap-around - } -} - - -//// Misc - -#define MISC_CLOCK( n ) inline static void misc_##n( void ) - -MISC_CLOCK( 27 ) -{ - m.t_pmon = REG(pmon) & 0xFE; // voice 0 doesn't support PMON -} -MISC_CLOCK( 28 ) -{ - m.t_non = REG(non); - m.t_eon = REG(eon); - m.t_dir = REG(dir); -} -MISC_CLOCK( 29 ) -{ - if ( (m.every_other_sample ^= 1) != 0 ) - m.new_kon &= ~m.kon; // clears KON 63 clocks after it was last read -} -MISC_CLOCK( 30 ) -{ - if ( m.every_other_sample ) - { - m.kon = m.new_kon; - m.t_koff = REG(koff) | m.mute_mask; - } - - run_counters(); - - // Noise - if ( !read_counter( REG(flg) & 0x1F ) ) - { - int feedback = (m.noise << 13) ^ (m.noise << 14); - m.noise = (feedback & 0x4000) ^ (m.noise >> 1); - } -} - - -//// Voices - -#define VOICE_CLOCK( n ) static void voice_##n( voice_t* const v ) - -inline VOICE_CLOCK( V1 ) -{ - m.t_dir_addr = m.t_dir * 0x100 + m.t_srcn * 4; - m.t_srcn = VREG(v->regs,srcn); -} -inline VOICE_CLOCK( V2 ) -{ - // Read sample pointer (ignored if not needed) - uint8_t const* entry = &m_ram [m.t_dir_addr]; - if ( !v->kon_delay ) - entry += 2; - m.t_brr_next_addr = entry [1] * 0x100 + entry [0]; - - m.t_adsr0 = VREG(v->regs,adsr0); - - // Read pitch, spread over two clocks - m.t_pitch = VREG(v->regs,pitchl); -} -inline VOICE_CLOCK( V3a ) -{ - m.t_pitch += (VREG(v->regs,pitchh) & 0x3F) << 8; -} -inline VOICE_CLOCK( V3b ) -{ - // Read BRR header and byte - m.t_brr_byte = m_ram [(v->brr_addr + v->brr_offset) & 0xFFFF]; - m.t_brr_header = m_ram [v->brr_addr]; // brr_addr doesn't need masking -} -VOICE_CLOCK( V3c ) -{ - // Pitch modulation using previous voice's output - if ( m.t_pmon & v->vbit ) - m.t_pitch += ((m.t_output >> 5) * m.t_pitch) >> 10; - - if ( v->kon_delay ) - { - // Get ready to start BRR decoding on next sample - if ( v->kon_delay == 5 ) - { - v->brr_addr = m.t_brr_next_addr; - v->brr_offset = 1; - v->buf_pos = 0; - m.t_brr_header = 0; // header is ignored on this sample - } - - // Envelope is never run during KON - v->env = 0; - v->hidden_env = 0; - - // Disable BRR decoding until last three samples - v->interp_pos = 0; - if ( --v->kon_delay & 3 ) - v->interp_pos = 0x4000; - - // Pitch is never added during KON - m.t_pitch = 0; - } - - // Gaussian interpolation - int output = interpolate( v ); - - // Noise - if ( m.t_non & v->vbit ) - output = (int16_t) (m.noise * 2); - - // Apply envelope - m.t_output = (output * v->env) >> 11 & ~1; - v->t_envx_out = v->env >> 4; - - // Immediate silence due to end of sample or soft reset - if ( REG(flg) & 0x80 || (m.t_brr_header & 3) == 1 ) - { - v->env_mode = env_release; - v->env = 0; - } - - if ( m.every_other_sample ) - { - // KOFF - if ( m.t_koff & v->vbit ) - v->env_mode = env_release; - - // KON - if ( m.kon & v->vbit ) - { - v->kon_delay = 5; - v->env_mode = env_attack; - } - } - - // Run envelope for next sample - if ( !v->kon_delay ) - run_envelope( v ); -} -static inline void voice_output( voice_t const* v, int ch ) -{ - // Apply left/right volume - int amp = (m.t_output * (int8_t) VREG(v->regs,voll + ch)) >> 7; - - // Add to output total - m.t_main_out [ch] += amp; - CLAMP16( m.t_main_out [ch] ); - - // Optionally add to echo total - if ( m.t_eon & v->vbit ) - { - m.t_echo_out [ch] += amp; - CLAMP16( m.t_echo_out [ch] ); - } -} -VOICE_CLOCK( V4 ) -{ - // Decode BRR - m.t_looped = 0; - if ( v->interp_pos >= 0x4000 ) - { - decode_brr( v ); - - if ( (v->brr_offset += 2) >= brr_block_size ) - { - // Start decoding next BRR block - assert( v->brr_offset == brr_block_size ); - v->brr_addr = (v->brr_addr + brr_block_size) & 0xFFFF; - if ( m.t_brr_header & 1 ) - { - v->brr_addr = m.t_brr_next_addr; - m.t_looped = v->vbit; - } - v->brr_offset = 1; - } - } - - // Apply pitch - v->interp_pos = (v->interp_pos & 0x3FFF) + m.t_pitch; - - // Keep from getting too far ahead (when using pitch modulation) - if ( v->interp_pos > 0x7FFF ) - v->interp_pos = 0x7FFF; - - // Output left - voice_output( v, 0 ); -} -inline VOICE_CLOCK( V5 ) -{ - // Output right - voice_output( v, 1 ); - - // ENDX, OUTX, and ENVX won't update if you wrote to them 1-2 clocks earlier - - m.endx_buf = REG(endx) | m.t_looped; - - // Clear bit in ENDX if KON just began - if ( v->kon_delay == 5 ) - m.endx_buf &= ~v->vbit; -} -inline VOICE_CLOCK( V6 ) -{ - m.outx_buf = m.t_output >> 8; -} -inline VOICE_CLOCK( V7 ) -{ - // Update ENDX - REG(endx) = (uint8_t) m.endx_buf; - - m.envx_buf = v->t_envx_out; -} -inline VOICE_CLOCK( V8 ) -{ - // Update OUTX - VREG(v->regs,outx) = (uint8_t) m.outx_buf; -} -inline VOICE_CLOCK( V9 ) -{ - // Update ENVX - VREG(v->regs,envx) = (uint8_t) m.envx_buf; -} - -// Most voices do all these in one clock, so make a handy composite -inline VOICE_CLOCK( V3 ) -{ - voice_V3a( v ); - voice_V3b( v ); - voice_V3c( v ); -} - -// Common combinations of voice steps on different voices. This greatly reduces -// code size and allows everything to be inlined in these functions. -VOICE_CLOCK(V7_V4_V1) { voice_V7(v); voice_V1(v+3); voice_V4(v+1); } -VOICE_CLOCK(V8_V5_V2) { voice_V8(v); voice_V5(v+1); voice_V2(v+2); } -VOICE_CLOCK(V9_V6_V3) { voice_V9(v); voice_V6(v+1); voice_V3(v+2); } - - -//// Echo - -// Current echo buffer pointer for left/right channel -#define ECHO_PTR( ch ) (&m_ram [m.t_echo_ptr + ch * 2]) - -// Sample in echo history buffer, where 0 is the oldest -#define ECHO_FIR( i ) (m.echo_hist_pos [i]) - -// Calculate FIR point for left/right channel -#define CALC_FIR( i, ch ) ((ECHO_FIR( i + 1 ) [ch] * (int8_t) REG(fir + i * 0x10)) >> 6) - -#define ECHO_CLOCK( n ) inline static void echo_##n( void ) - -static inline void echo_read( int ch ) -{ - uint8_t const* in = ECHO_PTR( ch ); - int s = (int8_t) in [1] * 0x100 + in [0]; - // second copy simplifies wrap-around handling - ECHO_FIR( 0 ) [ch] = ECHO_FIR( 8 ) [ch] = s >> 1; -} - -ECHO_CLOCK( 22 ) -{ - // History - if ( ++m.echo_hist_pos >= &m.echo_hist [spc_dsp_echo_hist_size] ) - m.echo_hist_pos = m.echo_hist; - - m.t_echo_ptr = (m.t_esa * 0x100 + m.echo_offset) & 0xFFFF; - echo_read( 0 ); - - // FIR (using l and r temporaries below helps compiler optimize) - int l = CALC_FIR( 0, 0 ); - int r = CALC_FIR( 0, 1 ); - - m.t_echo_in [0] = l; - m.t_echo_in [1] = r; -} -ECHO_CLOCK( 23 ) -{ - int l = CALC_FIR( 1, 0 ) + CALC_FIR( 2, 0 ); - int r = CALC_FIR( 1, 1 ) + CALC_FIR( 2, 1 ); - - m.t_echo_in [0] += l; - m.t_echo_in [1] += r; - - echo_read( 1 ); -} -ECHO_CLOCK( 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 ); - - m.t_echo_in [0] += l; - m.t_echo_in [1] += r; -} -ECHO_CLOCK( 25 ) -{ - int l = m.t_echo_in [0] + CALC_FIR( 6, 0 ); - int r = m.t_echo_in [1] + CALC_FIR( 6, 1 ); - - l = (int16_t) l; - r = (int16_t) r; - - l += (int16_t) CALC_FIR( 7, 0 ); - r += (int16_t) CALC_FIR( 7, 1 ); - - CLAMP16( l ); - CLAMP16( r ); - - m.t_echo_in [0] = l & ~1; - m.t_echo_in [1] = r & ~1; -} -static inline int echo_output( int ch ) -{ - int out = (int16_t) ((m.t_main_out [ch] * (int8_t) REG(mvoll + ch * 0x10)) >> 7) + - (int16_t) ((m.t_echo_in [ch] * (int8_t) REG(evoll + ch * 0x10)) >> 7); - CLAMP16( out ); - return out; -} -ECHO_CLOCK( 26 ) -{ - // Left output volumes - // (save sample for next clock so we can output both together) - m.t_main_out [0] = echo_output( 0 ); - - // Echo feedback - int l = m.t_echo_out [0] + (int16_t) ((m.t_echo_in [0] * (int8_t) REG(efb)) >> 7); - int r = m.t_echo_out [1] + (int16_t) ((m.t_echo_in [1] * (int8_t) REG(efb)) >> 7); - - CLAMP16( l ); - CLAMP16( r ); - - m.t_echo_out [0] = l & ~1; - m.t_echo_out [1] = r & ~1; -} -ECHO_CLOCK( 27 ) -{ - // Output - int outl = m.t_main_out [0]; - int outr = echo_output( 1 ); - m.t_main_out [0] = 0; - m.t_main_out [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 ) - { - outl = 0; - outr = 0; - } - - // Output sample to DAC - #ifdef SPC_DSP_OUT_HOOK - SPC_DSP_OUT_HOOK( outl, outr ); - #else - spc_dsp_sample_t* out = m_spc_dsp_out; - assert( !out || out < m_spc_dsp_out_end ); // fails if output buffer is too small - if ( out != m_spc_dsp_out_end ) - { - out [0] = outl; - out [1] = outr; - m_spc_dsp_out = out + 2; - } - #endif -} -ECHO_CLOCK( 28 ) -{ - m.t_echo_enabled = REG(flg); -} -static inline void echo_write( int ch ) -{ - if ( !(m.t_echo_enabled & 0x20) ) - { - uint8_t* out = ECHO_PTR( ch ); - int s = m.t_echo_out [ch]; - out [0] = (uint8_t) s; - out [1] = (uint8_t) (s >> 8); - } - m.t_echo_out [ch] = 0; -} -ECHO_CLOCK( 29 ) -{ - m.t_esa = REG(esa); - - if ( !m.echo_offset ) - m.echo_length = (REG(edl) & 0x0F) * 0x800; - - m.echo_offset += 4; - if ( m.echo_offset >= m.echo_length ) - m.echo_offset = 0; - - // Write left echo - echo_write( 0 ); - - m.t_echo_enabled = REG(flg); -} -ECHO_CLOCK( 30 ) -{ - // Write right echo - echo_write( 1 ); -} - - -//// Timing - -#if !SPC_DSP_CUSTOM_RUN - -void spc_dsp_run( int clocks_remain ) -{ - assert( clocks_remain > 0 ); - - int const phase = m.phase; - m.phase = (phase + clocks_remain) & 31; - switch ( phase ) - { - loop: - - #define PHASE( n ) if ( n && !--clocks_remain ) break; case n: - #include "spc_dsp_timing.h" - #undef PHASE - - if ( --clocks_remain ) - goto loop; - } -} - -#endif - - -//// Setup - -void spc_dsp_reset( void ) -{ - // Clear everything to zero, then set things which must be non-zero - memset( m_voice_state, 0, sizeof m_voice_state ); - memset( &m_spc_dsp, 0, sizeof m_spc_dsp ); - - m.noise = 1; - m.echo_hist_pos = m.echo_hist; - m.every_other_sample = 1; - init_counters(); - - int i; - for ( i = spc_dsp_voice_count; --i >= 0; ) - { - voice_t* v = &m_voice_state [i]; - v->regs = &m.regs [i * 0x10]; - v->vbit = 1 << i; - v->brr_offset = 1; - } - - REG(flg) = 0xE0; -} - -void spc_dsp_soft_reset( void ) -{ - // TODO: doesn't reset everything - spc_dsp_reset(); -} - -void spc_dsp_init( void* ram_64k ) -{ - m_ram = (uint8_t*) ram_64k; - spc_dsp_reset(); - - #if INT_MAX < 0x7FFFFFFF - #error "Requires that int have at least 32 bits" - #endif - - #ifndef NDEBUG - // be sure this sign-extends - assert( (int16_t) 0x8000 == -0x8000 ); - - // be sure right shift preserves sign - assert( (-1 >> 1) == -1 ); - - // check clamp macro - int i; - i = +0x8000; CLAMP16( i ); assert( i == +0x7FFF ); - i = -0x8001; CLAMP16( i ); assert( i == -0x8000 ); - #endif -} - -void spc_dsp_load( uint8_t const regs [spc_dsp_register_count] ) -{ - int i; - for ( i = 0; i < 0x80; i++ ) - spc_dsp_write( i, regs [i] ); - m.t_esa = regs [r_esa]; - m.t_dir = regs [r_dir]; -} diff --git a/src/dsp/bdsp/reference/spc_dsp.h b/src/dsp/bdsp/reference/spc_dsp.h deleted file mode 100644 index e0ba0582..00000000 --- a/src/dsp/bdsp/reference/spc_dsp.h +++ /dev/null @@ -1,162 +0,0 @@ -// SNES SPC-700 DSP emulator - -#ifndef SPC_DSP_H -#define SPC_DSP_H - -#include -#include - -//// Setup - -// Initializes DSP and has it use the 64K RAM provided -void spc_dsp_init( void* ram_64k ); - -// Restores DSP registers using supplied values -enum { spc_dsp_register_count = 128 }; -void spc_dsp_load( uint8_t const regs [spc_dsp_register_count] ); - -// Mutes voice n if bit 1<