From 6e4f086bd0b8db4eeb53cd5ebdfd6409a245dc36 Mon Sep 17 00:00:00 2001 From: clobber Date: Sun, 26 Feb 2012 00:49:03 -0600 Subject: [PATCH] BSNES now compiles from source --- BSNES.xcodeproj/project.pbxproj | 2026 ++++++++++++++++- libsnes/Makefile | 83 + libsnes/README | 7 + libsnes/gameboy/Makefile | 16 + libsnes/gameboy/apu/apu.hpp | 28 + libsnes/gameboy/apu/apu_gb.cpp | 107 + libsnes/gameboy/apu/master/master.cpp | 119 + libsnes/gameboy/apu/master/master.hpp | 24 + libsnes/gameboy/apu/noise/noise.cpp | 108 + libsnes/gameboy/apu/noise/noise.hpp | 27 + libsnes/gameboy/apu/serialization.cpp | 17 + libsnes/gameboy/apu/square1/square1.cpp | 160 ++ libsnes/gameboy/apu/square1/square1.hpp | 36 + libsnes/gameboy/apu/square2/square2.cpp | 110 + libsnes/gameboy/apu/square2/square2.hpp | 27 + libsnes/gameboy/apu/wave/wave.cpp | 99 + libsnes/gameboy/apu/wave/wave.hpp | 22 + libsnes/gameboy/cartridge/cartridge.hpp | 68 + libsnes/gameboy/cartridge/cartridge_gb.cpp | 153 ++ libsnes/gameboy/cartridge/huc1/huc1.cpp | 54 + libsnes/gameboy/cartridge/huc1/huc1.hpp | 10 + libsnes/gameboy/cartridge/huc3/huc3.cpp | 53 + libsnes/gameboy/cartridge/huc3/huc3.hpp | 9 + libsnes/gameboy/cartridge/mbc0/mbc0.cpp | 25 + libsnes/gameboy/cartridge/mbc0/mbc0.hpp | 5 + libsnes/gameboy/cartridge/mbc1/mbc1.cpp | 70 + libsnes/gameboy/cartridge/mbc1/mbc1.hpp | 10 + libsnes/gameboy/cartridge/mbc2/mbc2.cpp | 42 + libsnes/gameboy/cartridge/mbc2/mbc2.hpp | 8 + libsnes/gameboy/cartridge/mbc3/mbc3.cpp | 120 + libsnes/gameboy/cartridge/mbc3/mbc3.hpp | 24 + libsnes/gameboy/cartridge/mbc5/mbc5.cpp | 53 + libsnes/gameboy/cartridge/mbc5/mbc5.hpp | 9 + libsnes/gameboy/cartridge/mmm01/mmm01.cpp | 65 + libsnes/gameboy/cartridge/mmm01/mmm01.hpp | 12 + libsnes/gameboy/cartridge/serialization.cpp | 54 + libsnes/gameboy/cheat/cheat.hpp | 14 + libsnes/gameboy/cheat/cheat_gb.cpp | 91 + libsnes/gameboy/cpu/core/core.cpp | 681 ++++++ libsnes/gameboy/cpu/core/core.hpp | 145 ++ libsnes/gameboy/cpu/core/registers.hpp | 101 + libsnes/gameboy/cpu/core/table.cpp | 519 +++++ libsnes/gameboy/cpu/cpu.hpp | 107 + libsnes/gameboy/cpu/cpu_gb.cpp | 202 ++ libsnes/gameboy/cpu/mmio/mmio.cpp | 271 +++ libsnes/gameboy/cpu/mmio/mmio.hpp | 4 + libsnes/gameboy/cpu/serialization.cpp | 76 + libsnes/gameboy/cpu/timing/opcode.cpp | 28 + libsnes/gameboy/cpu/timing/timing.cpp | 90 + libsnes/gameboy/cpu/timing/timing.hpp | 13 + libsnes/gameboy/gameboy.hpp | 107 + libsnes/gameboy/interface/interface.hpp | 12 + libsnes/gameboy/interface/interface_gb.cpp | 27 + libsnes/gameboy/lcd/cgb.cpp | 185 ++ libsnes/gameboy/lcd/dmg.cpp | 145 ++ libsnes/gameboy/lcd/lcd.hpp | 94 + libsnes/gameboy/lcd/lcd_gb.cpp | 158 ++ libsnes/gameboy/lcd/mmio/mmio.cpp | 203 ++ libsnes/gameboy/lcd/mmio/mmio.hpp | 3 + libsnes/gameboy/lcd/serialization.cpp | 53 + libsnes/gameboy/memory/memory.hpp | 32 + libsnes/gameboy/memory/memory_gb.cpp | 69 + libsnes/gameboy/scheduler/scheduler.hpp | 27 + libsnes/gameboy/scheduler/scheduler_gb.cpp | 35 + libsnes/gameboy/system/bootrom-cgb.cpp | 135 ++ libsnes/gameboy/system/bootrom-dmg.cpp | 23 + libsnes/gameboy/system/bootrom-sgb.cpp | 23 + libsnes/gameboy/system/serialization.cpp | 64 + libsnes/gameboy/system/system.hpp | 51 + libsnes/gameboy/system/system_gb.cpp | 57 + libsnes/gameboy/video/video.hpp | 17 + libsnes/gameboy/video/video_gb.cpp | 82 + libsnes/libco/amd64.c | 142 ++ libsnes/libco/amd64_msvc.c | 84 + libsnes/libco/fiber.c | 51 + libsnes/libco/libco.c | 28 + libsnes/libco/libco.h | 34 + libsnes/libco/ppc.c | 407 ++++ libsnes/libco/sjlj.c | 102 + libsnes/libco/ucontext.c | 67 + libsnes/libco/x86.c | 99 + libsnes/libco/x86_msvc.c | 73 + libsnes/link.T | 4 + .../libsnes-compat/libsnes-compat.vcxproj | 193 ++ .../libsnes-compat.vcxproj.filters | 136 ++ .../libsnes-performance.vcxproj | 195 ++ .../libsnes-performance.vcxproj.filters | 136 ++ libsnes/msvc/libsnes.sln | 46 + libsnes/msvc/libsnes/libsnes.vcxproj | 196 ++ libsnes/msvc/libsnes/libsnes.vcxproj.filters | 136 ++ libsnes/nall/C++98.hpp | 45 + libsnes/nall/Makefile | 108 + libsnes/nall/algorithm.hpp | 17 + libsnes/nall/array.hpp | 125 + libsnes/nall/base64.hpp | 90 + libsnes/nall/bit.hpp | 51 + libsnes/nall/concept.hpp | 20 + libsnes/nall/config.hpp | 123 + libsnes/nall/crc32.hpp | 66 + libsnes/nall/detect.hpp | 30 + libsnes/nall/dictionary.hpp | 72 + libsnes/nall/directory.hpp | 129 ++ libsnes/nall/dl.hpp | 115 + libsnes/nall/dsp.hpp | 13 + libsnes/nall/dsp/buffer.hpp | 51 + libsnes/nall/dsp/core.hpp | 170 ++ libsnes/nall/dsp/resample/average.hpp | 72 + libsnes/nall/dsp/resample/cosine.hpp | 44 + libsnes/nall/dsp/resample/cubic.hpp | 50 + libsnes/nall/dsp/resample/hermite.hpp | 62 + libsnes/nall/dsp/resample/lib/sinc.hpp | 600 +++++ libsnes/nall/dsp/resample/linear.hpp | 43 + libsnes/nall/dsp/resample/nearest.hpp | 43 + libsnes/nall/dsp/resample/sinc.hpp | 54 + libsnes/nall/dsp/settings.hpp | 50 + libsnes/nall/endian.hpp | 38 + libsnes/nall/file.hpp | 345 +++ libsnes/nall/filemap.hpp | 200 ++ libsnes/nall/foreach.hpp | 18 + libsnes/nall/function.hpp | 190 ++ libsnes/nall/gameboy/cartridge.hpp | 115 + libsnes/nall/input.hpp | 386 ++++ libsnes/nall/libmalloc.c | 71 + libsnes/nall/lzss.hpp | 81 + libsnes/nall/memory_debug.hpp | 10 + libsnes/nall/moduloarray.hpp | 40 + libsnes/nall/platform.hpp | 124 + libsnes/nall/priorityqueue.hpp | 120 + libsnes/nall/property.hpp | 91 + libsnes/nall/public_cast.hpp | 34 + libsnes/nall/random.hpp | 28 + libsnes/nall/reference_array.hpp | 105 + libsnes/nall/serial.hpp | 85 + libsnes/nall/serializer.hpp | 127 ++ libsnes/nall/sha256.hpp | 145 ++ libsnes/nall/snes/cartridge.hpp | 885 +++++++ libsnes/nall/snes/cpu.hpp | 458 ++++ libsnes/nall/snes/smp.hpp | 639 ++++++ libsnes/nall/sort.hpp | 62 + libsnes/nall/static.hpp | 20 + libsnes/nall/stdint.hpp | 32 + libsnes/nall/string.hpp | 35 + libsnes/nall/string/base.hpp | 267 +++ libsnes/nall/string/bml.hpp | 160 ++ libsnes/nall/string/bsv.hpp | 75 + libsnes/nall/string/cast.hpp | 31 + libsnes/nall/string/compare.hpp | 112 + libsnes/nall/string/convert.hpp | 129 ++ libsnes/nall/string/core.hpp | 335 +++ libsnes/nall/string/cstring.hpp | 21 + libsnes/nall/string/filename.hpp | 61 + libsnes/nall/string/match.hpp | 76 + libsnes/nall/string/math.hpp | 164 ++ libsnes/nall/string/replace.hpp | 103 + libsnes/nall/string/split.hpp | 56 + libsnes/nall/string/strl.hpp | 52 + libsnes/nall/string/strpos.hpp | 34 + libsnes/nall/string/trim.hpp | 61 + libsnes/nall/string/utility.hpp | 287 +++ libsnes/nall/string/variadic.hpp | 96 + libsnes/nall/string/wrapper.hpp | 12 + libsnes/nall/string/xml.hpp | 255 +++ libsnes/nall/ups.hpp | 190 ++ libsnes/nall/utf8.hpp | 96 + libsnes/nall/utility.hpp | 38 + libsnes/nall/varint.hpp | 117 + libsnes/nall/vector.hpp | 271 +++ libsnes/snes/Makefile | 108 + libsnes/snes/alt/cpu/cpu.cpp | 194 ++ libsnes/snes/alt/cpu/cpu.hpp | 157 ++ libsnes/snes/alt/cpu/debugger/debugger.cpp | 156 ++ libsnes/snes/alt/cpu/debugger/debugger.hpp | 24 + libsnes/snes/alt/cpu/dma.cpp | 204 ++ libsnes/snes/alt/cpu/memory.cpp | 48 + libsnes/snes/alt/cpu/mmio.cpp | 303 +++ libsnes/snes/alt/cpu/serialization.cpp | 86 + libsnes/snes/alt/cpu/timing.cpp | 119 + libsnes/snes/alt/dsp/SPC_DSP.cpp | 1027 +++++++++ libsnes/snes/alt/dsp/SPC_DSP.h | 304 +++ libsnes/snes/alt/dsp/blargg_common.h | 186 ++ libsnes/snes/alt/dsp/blargg_config.h | 24 + libsnes/snes/alt/dsp/blargg_endian.h | 185 ++ libsnes/snes/alt/dsp/blargg_source.h | 100 + libsnes/snes/alt/dsp/dsp.cpp | 66 + libsnes/snes/alt/dsp/dsp.hpp | 28 + libsnes/snes/alt/dsp/serialization.cpp | 31 + .../ppu-compatibility/debugger/debugger.cpp | 349 +++ .../ppu-compatibility/debugger/debugger.hpp | 15 + .../alt/ppu-compatibility/memory/memory.cpp | 157 ++ .../alt/ppu-compatibility/memory/memory.hpp | 10 + .../snes/alt/ppu-compatibility/mmio/mmio.cpp | 671 ++++++ .../snes/alt/ppu-compatibility/mmio/mmio.hpp | 202 ++ libsnes/snes/alt/ppu-compatibility/ppu.cpp | 434 ++++ libsnes/snes/alt/ppu-compatibility/ppu.hpp | 84 + .../alt/ppu-compatibility/render/addsub.cpp | 25 + .../snes/alt/ppu-compatibility/render/bg.cpp | 209 ++ .../alt/ppu-compatibility/render/cache.cpp | 147 ++ .../alt/ppu-compatibility/render/line.cpp | 118 + .../alt/ppu-compatibility/render/mode7.cpp | 143 ++ .../snes/alt/ppu-compatibility/render/oam.cpp | 237 ++ .../alt/ppu-compatibility/render/render.cpp | 129 ++ .../alt/ppu-compatibility/render/render.hpp | 99 + .../alt/ppu-compatibility/render/windows.cpp | 70 + .../alt/ppu-compatibility/serialization.cpp | 207 ++ libsnes/snes/alt/ppu-parallel/mmio.cpp | 14 + libsnes/snes/alt/ppu-parallel/ppu.cpp | 81 + libsnes/snes/alt/ppu-parallel/ppu.hpp | 40 + .../ppu-performance/background/background.cpp | 184 ++ .../ppu-performance/background/background.hpp | 65 + .../alt/ppu-performance/background/mode7.cpp | 105 + .../snes/alt/ppu-performance/cache/cache.cpp | 124 + .../snes/alt/ppu-performance/cache/cache.hpp | 16 + .../alt/ppu-performance/debugger/debugger.cpp | 7 + .../alt/ppu-performance/debugger/debugger.hpp | 4 + .../snes/alt/ppu-performance/mmio/mmio.cpp | 891 ++++++++ .../snes/alt/ppu-performance/mmio/mmio.hpp | 95 + libsnes/snes/alt/ppu-performance/ppu.cpp | 156 ++ libsnes/snes/alt/ppu-performance/ppu.hpp | 81 + .../alt/ppu-performance/screen/screen.cpp | 155 ++ .../alt/ppu-performance/screen/screen.hpp | 43 + .../alt/ppu-performance/serialization.cpp | 249 ++ .../alt/ppu-performance/sprite/sprite.cpp | 190 ++ .../alt/ppu-performance/sprite/sprite.hpp | 70 + .../alt/ppu-performance/window/window.cpp | 98 + .../alt/ppu-performance/window/window.hpp | 35 + libsnes/snes/alt/smp/algorithms.cpp | 122 + libsnes/snes/alt/smp/core.cpp | 104 + libsnes/snes/alt/smp/core/cc.sh | 1 + libsnes/snes/alt/smp/core/generate.cpp | 154 ++ libsnes/snes/alt/smp/core/op_misc.b | 163 ++ libsnes/snes/alt/smp/core/op_misc.cpp | 346 +++ libsnes/snes/alt/smp/core/op_mov.b | 217 ++ libsnes/snes/alt/smp/core/op_mov.cpp | 389 ++++ libsnes/snes/alt/smp/core/op_pc.b | 179 ++ libsnes/snes/alt/smp/core/op_pc.cpp | 603 +++++ libsnes/snes/alt/smp/core/op_read.b | 205 ++ libsnes/snes/alt/smp/core/op_read.cpp | 744 ++++++ libsnes/snes/alt/smp/core/op_rmw.b | 74 + libsnes/snes/alt/smp/core/op_rmw.cpp | 262 +++ libsnes/snes/alt/smp/core/opcycle_misc.cpp | 696 ++++++ libsnes/snes/alt/smp/core/opcycle_mov.cpp | 806 +++++++ libsnes/snes/alt/smp/core/opcycle_pc.cpp | 1347 +++++++++++ libsnes/snes/alt/smp/core/opcycle_read.cpp | 1599 +++++++++++++ libsnes/snes/alt/smp/core/opcycle_rmw.cpp | 550 +++++ libsnes/snes/alt/smp/iplrom.cpp | 44 + libsnes/snes/alt/smp/memory.cpp | 130 ++ libsnes/snes/alt/smp/smp.cpp | 154 ++ libsnes/snes/alt/smp/smp.hpp | 120 + libsnes/snes/alt/smp/timing.cpp | 26 + libsnes/snes/cartridge/cartridge.cpp | 88 + libsnes/snes/cartridge/cartridge.hpp | 119 + libsnes/snes/cartridge/markup.cpp | 561 +++++ libsnes/snes/cartridge/serialization.cpp | 9 + libsnes/snes/cheat/cheat.cpp | 110 + libsnes/snes/cheat/cheat.hpp | 27 + libsnes/snes/chip/bsx/bsx.cpp | 8 + libsnes/snes/chip/bsx/bsx.hpp | 3 + libsnes/snes/chip/bsx/cartridge/cartridge.cpp | 150 ++ libsnes/snes/chip/bsx/cartridge/cartridge.hpp | 32 + libsnes/snes/chip/bsx/flash/flash.cpp | 127 ++ libsnes/snes/chip/bsx/flash/flash.hpp | 27 + .../snes/chip/bsx/satellaview/satellaview.cpp | 144 ++ .../snes/chip/bsx/satellaview/satellaview.hpp | 26 + libsnes/snes/chip/chip.hpp | 28 + libsnes/snes/chip/hitachidsp/hitachidsp.cpp | 78 + libsnes/snes/chip/hitachidsp/hitachidsp.hpp | 49 + libsnes/snes/chip/hitachidsp/memory.cpp | 133 ++ libsnes/snes/chip/hitachidsp/opcodes.cpp | 356 +++ libsnes/snes/chip/hitachidsp/registers.cpp | 78 + libsnes/snes/chip/hitachidsp/registers.hpp | 31 + .../snes/chip/hitachidsp/serialization.cpp | 44 + libsnes/snes/chip/icd2/icd2.cpp | 75 + libsnes/snes/chip/icd2/icd2.hpp | 24 + .../snes/chip/icd2/interface/interface.cpp | 115 + .../snes/chip/icd2/interface/interface.hpp | 22 + libsnes/snes/chip/icd2/mmio/mmio.cpp | 96 + libsnes/snes/chip/icd2/mmio/mmio.hpp | 19 + libsnes/snes/chip/icd2/serialization.cpp | 38 + libsnes/snes/chip/link/link.cpp | 59 + libsnes/snes/chip/link/link.hpp | 24 + libsnes/snes/chip/msu1/msu1.cpp | 136 ++ libsnes/snes/chip/msu1/msu1.hpp | 43 + libsnes/snes/chip/msu1/serialization.cpp | 29 + libsnes/snes/chip/necdsp/memory.cpp | 74 + libsnes/snes/chip/necdsp/necdsp.cpp | 300 +++ libsnes/snes/chip/necdsp/necdsp.hpp | 50 + libsnes/snes/chip/necdsp/registers.hpp | 51 + libsnes/snes/chip/necdsp/serialization.cpp | 55 + libsnes/snes/chip/nss/nss.cpp | 41 + libsnes/snes/chip/nss/nss.hpp | 17 + libsnes/snes/chip/obc1/obc1.cpp | 79 + libsnes/snes/chip/obc1/obc1.hpp | 25 + libsnes/snes/chip/obc1/serialization.cpp | 9 + libsnes/snes/chip/sa1/bus/bus.cpp | 34 + libsnes/snes/chip/sa1/bus/bus.hpp | 14 + libsnes/snes/chip/sa1/dma/dma.cpp | 139 ++ libsnes/snes/chip/sa1/dma/dma.hpp | 11 + libsnes/snes/chip/sa1/memory/memory.cpp | 268 +++ libsnes/snes/chip/sa1/memory/memory.hpp | 19 + libsnes/snes/chip/sa1/mmio/mmio.cpp | 554 +++++ libsnes/snes/chip/sa1/mmio/mmio.hpp | 255 +++ libsnes/snes/chip/sa1/sa1.cpp | 330 +++ libsnes/snes/chip/sa1/sa1.hpp | 37 + libsnes/snes/chip/sa1/serialization.cpp | 148 ++ libsnes/snes/chip/sdd1/decomp.cpp | 289 +++ libsnes/snes/chip/sdd1/decomp.hpp | 82 + libsnes/snes/chip/sdd1/sdd1.cpp | 150 ++ libsnes/snes/chip/sdd1/sdd1.hpp | 36 + libsnes/snes/chip/sdd1/serialization.cpp | 15 + libsnes/snes/chip/spc7110/decomp.cpp | 498 ++++ libsnes/snes/chip/spc7110/decomp.hpp | 44 + libsnes/snes/chip/spc7110/serialization.cpp | 82 + libsnes/snes/chip/spc7110/spc7110.cpp | 676 ++++++ libsnes/snes/chip/spc7110/spc7110.hpp | 131 ++ libsnes/snes/chip/srtc/serialization.cpp | 9 + libsnes/snes/chip/srtc/srtc.cpp | 234 ++ libsnes/snes/chip/srtc/srtc.hpp | 27 + libsnes/snes/chip/st0018/st0018.cpp | 125 + libsnes/snes/chip/st0018/st0018.hpp | 52 + .../snes/chip/sufamiturbo/serialization.cpp | 8 + libsnes/snes/chip/sufamiturbo/sufamiturbo.cpp | 33 + libsnes/snes/chip/sufamiturbo/sufamiturbo.hpp | 13 + libsnes/snes/chip/superfx/bus/bus.cpp | 37 + libsnes/snes/chip/superfx/bus/bus.hpp | 11 + libsnes/snes/chip/superfx/core/core.cpp | 107 + libsnes/snes/chip/superfx/core/core.hpp | 92 + .../snes/chip/superfx/core/opcode_table.cpp | 270 +++ libsnes/snes/chip/superfx/core/opcodes.cpp | 661 ++++++ libsnes/snes/chip/superfx/core/registers.hpp | 176 ++ libsnes/snes/chip/superfx/disasm/disasm.cpp | 279 +++ libsnes/snes/chip/superfx/disasm/disasm.hpp | 5 + libsnes/snes/chip/superfx/memory/memory.cpp | 112 + libsnes/snes/chip/superfx/memory/memory.hpp | 14 + libsnes/snes/chip/superfx/mmio/mmio.cpp | 118 + libsnes/snes/chip/superfx/mmio/mmio.hpp | 2 + libsnes/snes/chip/superfx/serialization.cpp | 96 + libsnes/snes/chip/superfx/superfx.cpp | 82 + libsnes/snes/chip/superfx/superfx.hpp | 24 + libsnes/snes/chip/superfx/timing/timing.cpp | 97 + libsnes/snes/chip/superfx/timing/timing.hpp | 19 + libsnes/snes/config/config.cpp | 26 + libsnes/snes/config/config.hpp | 35 + libsnes/snes/controller/controller.cpp | 55 + libsnes/snes/controller/controller.hpp | 36 + libsnes/snes/controller/gamepad/gamepad.cpp | 35 + libsnes/snes/controller/gamepad/gamepad.hpp | 10 + .../snes/controller/justifier/justifier.cpp | 130 ++ .../snes/controller/justifier/justifier.hpp | 17 + libsnes/snes/controller/mouse/mouse.cpp | 82 + libsnes/snes/controller/mouse/mouse.hpp | 10 + libsnes/snes/controller/multitap/multitap.cpp | 54 + libsnes/snes/controller/multitap/multitap.hpp | 11 + libsnes/snes/controller/serial/serial.cpp | 148 ++ libsnes/snes/controller/serial/serial.hpp | 19 + .../snes/controller/superscope/superscope.cpp | 155 ++ .../snes/controller/superscope/superscope.hpp | 23 + libsnes/snes/cpu/core/algorithms.cpp | 331 +++ libsnes/snes/cpu/core/core.cpp | 88 + libsnes/snes/cpu/core/core.hpp | 216 ++ libsnes/snes/cpu/core/memory.hpp | 77 + libsnes/snes/cpu/core/opcode_misc.cpp | 338 +++ libsnes/snes/cpu/core/opcode_pc.cpp | 181 ++ libsnes/snes/cpu/core/opcode_read.cpp | 279 +++ libsnes/snes/cpu/core/opcode_rmw.cpp | 169 ++ libsnes/snes/cpu/core/opcode_write.cpp | 199 ++ libsnes/snes/cpu/core/registers.hpp | 83 + libsnes/snes/cpu/core/serialization.cpp | 37 + libsnes/snes/cpu/core/table.cpp | 312 +++ libsnes/snes/cpu/cpu.cpp | 169 ++ libsnes/snes/cpu/cpu.hpp | 145 ++ libsnes/snes/cpu/debugger/debugger.cpp | 156 ++ libsnes/snes/cpu/debugger/debugger.hpp | 24 + libsnes/snes/cpu/dma/dma.cpp | 289 +++ libsnes/snes/cpu/dma/dma.hpp | 79 + libsnes/snes/cpu/memory/memory.cpp | 41 + libsnes/snes/cpu/memory/memory.hpp | 4 + libsnes/snes/cpu/mmio/mmio.cpp | 519 +++++ libsnes/snes/cpu/mmio/mmio.hpp | 71 + libsnes/snes/cpu/serialization.cpp | 118 + libsnes/snes/cpu/timing/irq.cpp | 106 + libsnes/snes/cpu/timing/joypad.cpp | 31 + libsnes/snes/cpu/timing/timing.cpp | 199 ++ libsnes/snes/cpu/timing/timing.hpp | 24 + libsnes/snes/debugger/debugger.cpp | 112 + libsnes/snes/debugger/debugger.hpp | 28 + libsnes/snes/dsp/brr.cpp | 62 + libsnes/snes/dsp/counter.cpp | 52 + libsnes/snes/dsp/debugger/debugger.cpp | 53 + libsnes/snes/dsp/debugger/debugger.hpp | 4 + libsnes/snes/dsp/dsp.cpp | 300 +++ libsnes/snes/dsp/dsp.hpp | 179 ++ libsnes/snes/dsp/echo.cpp | 135 ++ libsnes/snes/dsp/envelope.cpp | 62 + libsnes/snes/dsp/gaussian.cpp | 54 + libsnes/snes/dsp/misc.cpp | 35 + libsnes/snes/dsp/serialization.cpp | 66 + libsnes/snes/dsp/voice.cpp | 174 ++ libsnes/snes/interface/interface.cpp | 29 + libsnes/snes/interface/interface.hpp | 12 + libsnes/snes/libsnes/libsnes.cpp | 401 ++++ libsnes/snes/libsnes/libsnes.hpp | 1297 +++++++++++ libsnes/snes/memory/memory-inline.hpp | 61 + libsnes/snes/memory/memory.cpp | 85 + libsnes/snes/memory/memory.hpp | 74 + libsnes/snes/ppu/background/background.cpp | 278 +++ libsnes/snes/ppu/background/background.hpp | 77 + libsnes/snes/ppu/background/mode7.cpp | 110 + libsnes/snes/ppu/counter/counter-inline.hpp | 85 + libsnes/snes/ppu/counter/counter.hpp | 49 + libsnes/snes/ppu/debugger/debugger.cpp | 17 + libsnes/snes/ppu/debugger/debugger.hpp | 4 + libsnes/snes/ppu/mmio/mmio.cpp | 865 +++++++ libsnes/snes/ppu/mmio/mmio.hpp | 162 ++ libsnes/snes/ppu/ppu.cpp | 162 ++ libsnes/snes/ppu/ppu.hpp | 68 + libsnes/snes/ppu/screen/screen.cpp | 210 ++ libsnes/snes/ppu/screen/screen.hpp | 36 + libsnes/snes/ppu/serialization.cpp | 284 +++ libsnes/snes/ppu/sprite/list.cpp | 60 + libsnes/snes/ppu/sprite/sprite.cpp | 223 ++ libsnes/snes/ppu/sprite/sprite.hpp | 81 + libsnes/snes/ppu/window/window.cpp | 169 ++ libsnes/snes/ppu/window/window.hpp | 87 + libsnes/snes/profile-accuracy.hpp | 8 + libsnes/snes/profile-compatibility.hpp | 8 + libsnes/snes/profile-performance.hpp | 12 + libsnes/snes/random/random.cpp | 18 + libsnes/snes/random/random.hpp | 11 + libsnes/snes/scheduler/scheduler.cpp | 28 + libsnes/snes/scheduler/scheduler.hpp | 31 + libsnes/snes/smp/core-old/algorithms.cpp | 126 + libsnes/snes/smp/core-old/core.cpp | 35 + libsnes/snes/smp/core-old/core.hpp | 118 + libsnes/snes/smp/core-old/memory.hpp | 19 + libsnes/snes/smp/core-old/opcode_misc.cpp | 148 ++ libsnes/snes/smp/core-old/opcode_mov.cpp | 200 ++ libsnes/snes/smp/core-old/opcode_pc.cpp | 152 ++ libsnes/snes/smp/core-old/opcode_read.cpp | 154 ++ libsnes/snes/smp/core-old/opcode_rmw.cpp | 58 + libsnes/snes/smp/core-old/registers.hpp | 44 + libsnes/snes/smp/core-old/serialization.cpp | 26 + libsnes/snes/smp/core-old/table.cpp | 264 +++ libsnes/snes/smp/core/algorithms.cpp | 138 ++ libsnes/snes/smp/core/core.hpp | 96 + libsnes/snes/smp/core/memory.hpp | 19 + libsnes/snes/smp/core/opcodes.cpp | 559 +++++ libsnes/snes/smp/core/registers.hpp | 54 + libsnes/snes/smp/core/serialization.cpp | 27 + libsnes/snes/smp/core/smp_core.cpp | 271 +++ libsnes/snes/smp/debugger/debugger.cpp | 75 + libsnes/snes/smp/debugger/debugger.hpp | 22 + libsnes/snes/smp/iplrom.cpp | 44 + libsnes/snes/smp/memory/memory.cpp | 197 ++ libsnes/snes/smp/memory/memory.hpp | 9 + libsnes/snes/smp/serialization.cpp | 52 + libsnes/snes/smp/smp.cpp | 127 ++ libsnes/snes/smp/smp.hpp | 61 + libsnes/snes/smp/timing/timing.cpp | 58 + libsnes/snes/smp/timing/timing.hpp | 21 + libsnes/snes/snes.hpp | 157 ++ libsnes/snes/system/audio.cpp | 69 + libsnes/snes/system/audio.hpp | 20 + libsnes/snes/system/input.cpp | 39 + libsnes/snes/system/input.hpp | 49 + libsnes/snes/system/serialization.cpp | 96 + libsnes/snes/system/system.cpp | 237 ++ libsnes/snes/system/system.hpp | 66 + libsnes/snes/system/video.cpp | 150 ++ libsnes/snes/system/video.hpp | 24 + 469 files changed, 63899 insertions(+), 8 deletions(-) create mode 100644 libsnes/Makefile create mode 100644 libsnes/README create mode 100755 libsnes/gameboy/Makefile create mode 100755 libsnes/gameboy/apu/apu.hpp create mode 100644 libsnes/gameboy/apu/apu_gb.cpp create mode 100755 libsnes/gameboy/apu/master/master.cpp create mode 100755 libsnes/gameboy/apu/master/master.hpp create mode 100755 libsnes/gameboy/apu/noise/noise.cpp create mode 100755 libsnes/gameboy/apu/noise/noise.hpp create mode 100755 libsnes/gameboy/apu/serialization.cpp create mode 100755 libsnes/gameboy/apu/square1/square1.cpp create mode 100755 libsnes/gameboy/apu/square1/square1.hpp create mode 100755 libsnes/gameboy/apu/square2/square2.cpp create mode 100755 libsnes/gameboy/apu/square2/square2.hpp create mode 100755 libsnes/gameboy/apu/wave/wave.cpp create mode 100755 libsnes/gameboy/apu/wave/wave.hpp create mode 100755 libsnes/gameboy/cartridge/cartridge.hpp create mode 100644 libsnes/gameboy/cartridge/cartridge_gb.cpp create mode 100755 libsnes/gameboy/cartridge/huc1/huc1.cpp create mode 100755 libsnes/gameboy/cartridge/huc1/huc1.hpp create mode 100755 libsnes/gameboy/cartridge/huc3/huc3.cpp create mode 100755 libsnes/gameboy/cartridge/huc3/huc3.hpp create mode 100755 libsnes/gameboy/cartridge/mbc0/mbc0.cpp create mode 100755 libsnes/gameboy/cartridge/mbc0/mbc0.hpp create mode 100755 libsnes/gameboy/cartridge/mbc1/mbc1.cpp create mode 100755 libsnes/gameboy/cartridge/mbc1/mbc1.hpp create mode 100755 libsnes/gameboy/cartridge/mbc2/mbc2.cpp create mode 100755 libsnes/gameboy/cartridge/mbc2/mbc2.hpp create mode 100755 libsnes/gameboy/cartridge/mbc3/mbc3.cpp create mode 100755 libsnes/gameboy/cartridge/mbc3/mbc3.hpp create mode 100755 libsnes/gameboy/cartridge/mbc5/mbc5.cpp create mode 100755 libsnes/gameboy/cartridge/mbc5/mbc5.hpp create mode 100755 libsnes/gameboy/cartridge/mmm01/mmm01.cpp create mode 100755 libsnes/gameboy/cartridge/mmm01/mmm01.hpp create mode 100755 libsnes/gameboy/cartridge/serialization.cpp create mode 100644 libsnes/gameboy/cheat/cheat.hpp create mode 100644 libsnes/gameboy/cheat/cheat_gb.cpp create mode 100755 libsnes/gameboy/cpu/core/core.cpp create mode 100755 libsnes/gameboy/cpu/core/core.hpp create mode 100755 libsnes/gameboy/cpu/core/registers.hpp create mode 100755 libsnes/gameboy/cpu/core/table.cpp create mode 100755 libsnes/gameboy/cpu/cpu.hpp create mode 100644 libsnes/gameboy/cpu/cpu_gb.cpp create mode 100755 libsnes/gameboy/cpu/mmio/mmio.cpp create mode 100755 libsnes/gameboy/cpu/mmio/mmio.hpp create mode 100755 libsnes/gameboy/cpu/serialization.cpp create mode 100755 libsnes/gameboy/cpu/timing/opcode.cpp create mode 100755 libsnes/gameboy/cpu/timing/timing.cpp create mode 100755 libsnes/gameboy/cpu/timing/timing.hpp create mode 100755 libsnes/gameboy/gameboy.hpp create mode 100755 libsnes/gameboy/interface/interface.hpp create mode 100644 libsnes/gameboy/interface/interface_gb.cpp create mode 100644 libsnes/gameboy/lcd/cgb.cpp create mode 100644 libsnes/gameboy/lcd/dmg.cpp create mode 100755 libsnes/gameboy/lcd/lcd.hpp create mode 100644 libsnes/gameboy/lcd/lcd_gb.cpp create mode 100755 libsnes/gameboy/lcd/mmio/mmio.cpp create mode 100755 libsnes/gameboy/lcd/mmio/mmio.hpp create mode 100755 libsnes/gameboy/lcd/serialization.cpp create mode 100755 libsnes/gameboy/memory/memory.hpp create mode 100644 libsnes/gameboy/memory/memory_gb.cpp create mode 100755 libsnes/gameboy/scheduler/scheduler.hpp create mode 100644 libsnes/gameboy/scheduler/scheduler_gb.cpp create mode 100644 libsnes/gameboy/system/bootrom-cgb.cpp create mode 100755 libsnes/gameboy/system/bootrom-dmg.cpp create mode 100755 libsnes/gameboy/system/bootrom-sgb.cpp create mode 100755 libsnes/gameboy/system/serialization.cpp create mode 100755 libsnes/gameboy/system/system.hpp create mode 100644 libsnes/gameboy/system/system_gb.cpp create mode 100644 libsnes/gameboy/video/video.hpp create mode 100644 libsnes/gameboy/video/video_gb.cpp create mode 100644 libsnes/libco/amd64.c create mode 100644 libsnes/libco/amd64_msvc.c create mode 100644 libsnes/libco/fiber.c create mode 100644 libsnes/libco/libco.c create mode 100644 libsnes/libco/libco.h create mode 100644 libsnes/libco/ppc.c create mode 100644 libsnes/libco/sjlj.c create mode 100644 libsnes/libco/ucontext.c create mode 100644 libsnes/libco/x86.c create mode 100644 libsnes/libco/x86_msvc.c create mode 100644 libsnes/link.T create mode 100644 libsnes/msvc/libsnes-compat/libsnes-compat.vcxproj create mode 100644 libsnes/msvc/libsnes-compat/libsnes-compat.vcxproj.filters create mode 100644 libsnes/msvc/libsnes-performance/libsnes-performance.vcxproj create mode 100644 libsnes/msvc/libsnes-performance/libsnes-performance.vcxproj.filters create mode 100644 libsnes/msvc/libsnes.sln create mode 100644 libsnes/msvc/libsnes/libsnes.vcxproj create mode 100644 libsnes/msvc/libsnes/libsnes.vcxproj.filters create mode 100644 libsnes/nall/C++98.hpp create mode 100644 libsnes/nall/Makefile create mode 100644 libsnes/nall/algorithm.hpp create mode 100644 libsnes/nall/array.hpp create mode 100644 libsnes/nall/base64.hpp create mode 100644 libsnes/nall/bit.hpp create mode 100644 libsnes/nall/concept.hpp create mode 100644 libsnes/nall/config.hpp create mode 100644 libsnes/nall/crc32.hpp create mode 100644 libsnes/nall/detect.hpp create mode 100644 libsnes/nall/dictionary.hpp create mode 100644 libsnes/nall/directory.hpp create mode 100644 libsnes/nall/dl.hpp create mode 100755 libsnes/nall/dsp.hpp create mode 100755 libsnes/nall/dsp/buffer.hpp create mode 100755 libsnes/nall/dsp/core.hpp create mode 100755 libsnes/nall/dsp/resample/average.hpp create mode 100755 libsnes/nall/dsp/resample/cosine.hpp create mode 100755 libsnes/nall/dsp/resample/cubic.hpp create mode 100755 libsnes/nall/dsp/resample/hermite.hpp create mode 100755 libsnes/nall/dsp/resample/lib/sinc.hpp create mode 100755 libsnes/nall/dsp/resample/linear.hpp create mode 100755 libsnes/nall/dsp/resample/nearest.hpp create mode 100755 libsnes/nall/dsp/resample/sinc.hpp create mode 100755 libsnes/nall/dsp/settings.hpp create mode 100644 libsnes/nall/endian.hpp create mode 100644 libsnes/nall/file.hpp create mode 100644 libsnes/nall/filemap.hpp create mode 100644 libsnes/nall/foreach.hpp create mode 100644 libsnes/nall/function.hpp create mode 100644 libsnes/nall/gameboy/cartridge.hpp create mode 100644 libsnes/nall/input.hpp create mode 100644 libsnes/nall/libmalloc.c create mode 100644 libsnes/nall/lzss.hpp create mode 100644 libsnes/nall/memory_debug.hpp create mode 100644 libsnes/nall/moduloarray.hpp create mode 100644 libsnes/nall/platform.hpp create mode 100644 libsnes/nall/priorityqueue.hpp create mode 100644 libsnes/nall/property.hpp create mode 100644 libsnes/nall/public_cast.hpp create mode 100644 libsnes/nall/random.hpp create mode 100644 libsnes/nall/reference_array.hpp create mode 100644 libsnes/nall/serial.hpp create mode 100644 libsnes/nall/serializer.hpp create mode 100644 libsnes/nall/sha256.hpp create mode 100644 libsnes/nall/snes/cartridge.hpp create mode 100644 libsnes/nall/snes/cpu.hpp create mode 100644 libsnes/nall/snes/smp.hpp create mode 100644 libsnes/nall/sort.hpp create mode 100644 libsnes/nall/static.hpp create mode 100644 libsnes/nall/stdint.hpp create mode 100644 libsnes/nall/string.hpp create mode 100644 libsnes/nall/string/base.hpp create mode 100755 libsnes/nall/string/bml.hpp create mode 100644 libsnes/nall/string/bsv.hpp create mode 100644 libsnes/nall/string/cast.hpp create mode 100644 libsnes/nall/string/compare.hpp create mode 100644 libsnes/nall/string/convert.hpp create mode 100644 libsnes/nall/string/core.hpp create mode 100755 libsnes/nall/string/cstring.hpp create mode 100644 libsnes/nall/string/filename.hpp create mode 100644 libsnes/nall/string/match.hpp create mode 100644 libsnes/nall/string/math.hpp create mode 100644 libsnes/nall/string/replace.hpp create mode 100644 libsnes/nall/string/split.hpp create mode 100644 libsnes/nall/string/strl.hpp create mode 100644 libsnes/nall/string/strpos.hpp create mode 100644 libsnes/nall/string/trim.hpp create mode 100644 libsnes/nall/string/utility.hpp create mode 100644 libsnes/nall/string/variadic.hpp create mode 100644 libsnes/nall/string/wrapper.hpp create mode 100644 libsnes/nall/string/xml.hpp create mode 100644 libsnes/nall/ups.hpp create mode 100644 libsnes/nall/utf8.hpp create mode 100644 libsnes/nall/utility.hpp create mode 100644 libsnes/nall/varint.hpp create mode 100644 libsnes/nall/vector.hpp create mode 100644 libsnes/snes/Makefile create mode 100644 libsnes/snes/alt/cpu/cpu.cpp create mode 100644 libsnes/snes/alt/cpu/cpu.hpp create mode 100644 libsnes/snes/alt/cpu/debugger/debugger.cpp create mode 100644 libsnes/snes/alt/cpu/debugger/debugger.hpp create mode 100644 libsnes/snes/alt/cpu/dma.cpp create mode 100644 libsnes/snes/alt/cpu/memory.cpp create mode 100644 libsnes/snes/alt/cpu/mmio.cpp create mode 100644 libsnes/snes/alt/cpu/serialization.cpp create mode 100644 libsnes/snes/alt/cpu/timing.cpp create mode 100644 libsnes/snes/alt/dsp/SPC_DSP.cpp create mode 100644 libsnes/snes/alt/dsp/SPC_DSP.h create mode 100644 libsnes/snes/alt/dsp/blargg_common.h create mode 100644 libsnes/snes/alt/dsp/blargg_config.h create mode 100644 libsnes/snes/alt/dsp/blargg_endian.h create mode 100644 libsnes/snes/alt/dsp/blargg_source.h create mode 100644 libsnes/snes/alt/dsp/dsp.cpp create mode 100644 libsnes/snes/alt/dsp/dsp.hpp create mode 100644 libsnes/snes/alt/dsp/serialization.cpp create mode 100644 libsnes/snes/alt/ppu-compatibility/debugger/debugger.cpp create mode 100644 libsnes/snes/alt/ppu-compatibility/debugger/debugger.hpp create mode 100644 libsnes/snes/alt/ppu-compatibility/memory/memory.cpp create mode 100644 libsnes/snes/alt/ppu-compatibility/memory/memory.hpp create mode 100644 libsnes/snes/alt/ppu-compatibility/mmio/mmio.cpp create mode 100644 libsnes/snes/alt/ppu-compatibility/mmio/mmio.hpp create mode 100644 libsnes/snes/alt/ppu-compatibility/ppu.cpp create mode 100644 libsnes/snes/alt/ppu-compatibility/ppu.hpp create mode 100644 libsnes/snes/alt/ppu-compatibility/render/addsub.cpp create mode 100644 libsnes/snes/alt/ppu-compatibility/render/bg.cpp create mode 100644 libsnes/snes/alt/ppu-compatibility/render/cache.cpp create mode 100644 libsnes/snes/alt/ppu-compatibility/render/line.cpp create mode 100644 libsnes/snes/alt/ppu-compatibility/render/mode7.cpp create mode 100644 libsnes/snes/alt/ppu-compatibility/render/oam.cpp create mode 100644 libsnes/snes/alt/ppu-compatibility/render/render.cpp create mode 100644 libsnes/snes/alt/ppu-compatibility/render/render.hpp create mode 100644 libsnes/snes/alt/ppu-compatibility/render/windows.cpp create mode 100644 libsnes/snes/alt/ppu-compatibility/serialization.cpp create mode 100644 libsnes/snes/alt/ppu-parallel/mmio.cpp create mode 100644 libsnes/snes/alt/ppu-parallel/ppu.cpp create mode 100644 libsnes/snes/alt/ppu-parallel/ppu.hpp create mode 100644 libsnes/snes/alt/ppu-performance/background/background.cpp create mode 100644 libsnes/snes/alt/ppu-performance/background/background.hpp create mode 100644 libsnes/snes/alt/ppu-performance/background/mode7.cpp create mode 100644 libsnes/snes/alt/ppu-performance/cache/cache.cpp create mode 100644 libsnes/snes/alt/ppu-performance/cache/cache.hpp create mode 100644 libsnes/snes/alt/ppu-performance/debugger/debugger.cpp create mode 100644 libsnes/snes/alt/ppu-performance/debugger/debugger.hpp create mode 100644 libsnes/snes/alt/ppu-performance/mmio/mmio.cpp create mode 100644 libsnes/snes/alt/ppu-performance/mmio/mmio.hpp create mode 100644 libsnes/snes/alt/ppu-performance/ppu.cpp create mode 100644 libsnes/snes/alt/ppu-performance/ppu.hpp create mode 100644 libsnes/snes/alt/ppu-performance/screen/screen.cpp create mode 100644 libsnes/snes/alt/ppu-performance/screen/screen.hpp create mode 100644 libsnes/snes/alt/ppu-performance/serialization.cpp create mode 100644 libsnes/snes/alt/ppu-performance/sprite/sprite.cpp create mode 100644 libsnes/snes/alt/ppu-performance/sprite/sprite.hpp create mode 100644 libsnes/snes/alt/ppu-performance/window/window.cpp create mode 100644 libsnes/snes/alt/ppu-performance/window/window.hpp create mode 100644 libsnes/snes/alt/smp/algorithms.cpp create mode 100644 libsnes/snes/alt/smp/core.cpp create mode 100644 libsnes/snes/alt/smp/core/cc.sh create mode 100644 libsnes/snes/alt/smp/core/generate.cpp create mode 100644 libsnes/snes/alt/smp/core/op_misc.b create mode 100644 libsnes/snes/alt/smp/core/op_misc.cpp create mode 100644 libsnes/snes/alt/smp/core/op_mov.b create mode 100644 libsnes/snes/alt/smp/core/op_mov.cpp create mode 100644 libsnes/snes/alt/smp/core/op_pc.b create mode 100644 libsnes/snes/alt/smp/core/op_pc.cpp create mode 100644 libsnes/snes/alt/smp/core/op_read.b create mode 100644 libsnes/snes/alt/smp/core/op_read.cpp create mode 100644 libsnes/snes/alt/smp/core/op_rmw.b create mode 100644 libsnes/snes/alt/smp/core/op_rmw.cpp create mode 100644 libsnes/snes/alt/smp/core/opcycle_misc.cpp create mode 100644 libsnes/snes/alt/smp/core/opcycle_mov.cpp create mode 100644 libsnes/snes/alt/smp/core/opcycle_pc.cpp create mode 100644 libsnes/snes/alt/smp/core/opcycle_read.cpp create mode 100644 libsnes/snes/alt/smp/core/opcycle_rmw.cpp create mode 100644 libsnes/snes/alt/smp/iplrom.cpp create mode 100644 libsnes/snes/alt/smp/memory.cpp create mode 100644 libsnes/snes/alt/smp/smp.cpp create mode 100644 libsnes/snes/alt/smp/smp.hpp create mode 100644 libsnes/snes/alt/smp/timing.cpp create mode 100644 libsnes/snes/cartridge/cartridge.cpp create mode 100644 libsnes/snes/cartridge/cartridge.hpp create mode 100644 libsnes/snes/cartridge/markup.cpp create mode 100644 libsnes/snes/cartridge/serialization.cpp create mode 100644 libsnes/snes/cheat/cheat.cpp create mode 100644 libsnes/snes/cheat/cheat.hpp create mode 100644 libsnes/snes/chip/bsx/bsx.cpp create mode 100644 libsnes/snes/chip/bsx/bsx.hpp create mode 100644 libsnes/snes/chip/bsx/cartridge/cartridge.cpp create mode 100644 libsnes/snes/chip/bsx/cartridge/cartridge.hpp create mode 100644 libsnes/snes/chip/bsx/flash/flash.cpp create mode 100644 libsnes/snes/chip/bsx/flash/flash.hpp create mode 100644 libsnes/snes/chip/bsx/satellaview/satellaview.cpp create mode 100644 libsnes/snes/chip/bsx/satellaview/satellaview.hpp create mode 100644 libsnes/snes/chip/chip.hpp create mode 100755 libsnes/snes/chip/hitachidsp/hitachidsp.cpp create mode 100755 libsnes/snes/chip/hitachidsp/hitachidsp.hpp create mode 100755 libsnes/snes/chip/hitachidsp/memory.cpp create mode 100755 libsnes/snes/chip/hitachidsp/opcodes.cpp create mode 100755 libsnes/snes/chip/hitachidsp/registers.cpp create mode 100755 libsnes/snes/chip/hitachidsp/registers.hpp create mode 100755 libsnes/snes/chip/hitachidsp/serialization.cpp create mode 100644 libsnes/snes/chip/icd2/icd2.cpp create mode 100644 libsnes/snes/chip/icd2/icd2.hpp create mode 100644 libsnes/snes/chip/icd2/interface/interface.cpp create mode 100644 libsnes/snes/chip/icd2/interface/interface.hpp create mode 100644 libsnes/snes/chip/icd2/mmio/mmio.cpp create mode 100644 libsnes/snes/chip/icd2/mmio/mmio.hpp create mode 100644 libsnes/snes/chip/icd2/serialization.cpp create mode 100644 libsnes/snes/chip/link/link.cpp create mode 100644 libsnes/snes/chip/link/link.hpp create mode 100644 libsnes/snes/chip/msu1/msu1.cpp create mode 100644 libsnes/snes/chip/msu1/msu1.hpp create mode 100644 libsnes/snes/chip/msu1/serialization.cpp create mode 100644 libsnes/snes/chip/necdsp/memory.cpp create mode 100644 libsnes/snes/chip/necdsp/necdsp.cpp create mode 100644 libsnes/snes/chip/necdsp/necdsp.hpp create mode 100644 libsnes/snes/chip/necdsp/registers.hpp create mode 100644 libsnes/snes/chip/necdsp/serialization.cpp create mode 100644 libsnes/snes/chip/nss/nss.cpp create mode 100644 libsnes/snes/chip/nss/nss.hpp create mode 100644 libsnes/snes/chip/obc1/obc1.cpp create mode 100644 libsnes/snes/chip/obc1/obc1.hpp create mode 100644 libsnes/snes/chip/obc1/serialization.cpp create mode 100644 libsnes/snes/chip/sa1/bus/bus.cpp create mode 100644 libsnes/snes/chip/sa1/bus/bus.hpp create mode 100644 libsnes/snes/chip/sa1/dma/dma.cpp create mode 100644 libsnes/snes/chip/sa1/dma/dma.hpp create mode 100644 libsnes/snes/chip/sa1/memory/memory.cpp create mode 100644 libsnes/snes/chip/sa1/memory/memory.hpp create mode 100644 libsnes/snes/chip/sa1/mmio/mmio.cpp create mode 100644 libsnes/snes/chip/sa1/mmio/mmio.hpp create mode 100644 libsnes/snes/chip/sa1/sa1.cpp create mode 100644 libsnes/snes/chip/sa1/sa1.hpp create mode 100644 libsnes/snes/chip/sa1/serialization.cpp create mode 100644 libsnes/snes/chip/sdd1/decomp.cpp create mode 100644 libsnes/snes/chip/sdd1/decomp.hpp create mode 100644 libsnes/snes/chip/sdd1/sdd1.cpp create mode 100644 libsnes/snes/chip/sdd1/sdd1.hpp create mode 100644 libsnes/snes/chip/sdd1/serialization.cpp create mode 100644 libsnes/snes/chip/spc7110/decomp.cpp create mode 100644 libsnes/snes/chip/spc7110/decomp.hpp create mode 100644 libsnes/snes/chip/spc7110/serialization.cpp create mode 100644 libsnes/snes/chip/spc7110/spc7110.cpp create mode 100644 libsnes/snes/chip/spc7110/spc7110.hpp create mode 100644 libsnes/snes/chip/srtc/serialization.cpp create mode 100644 libsnes/snes/chip/srtc/srtc.cpp create mode 100644 libsnes/snes/chip/srtc/srtc.hpp create mode 100644 libsnes/snes/chip/st0018/st0018.cpp create mode 100644 libsnes/snes/chip/st0018/st0018.hpp create mode 100644 libsnes/snes/chip/sufamiturbo/serialization.cpp create mode 100644 libsnes/snes/chip/sufamiturbo/sufamiturbo.cpp create mode 100644 libsnes/snes/chip/sufamiturbo/sufamiturbo.hpp create mode 100644 libsnes/snes/chip/superfx/bus/bus.cpp create mode 100644 libsnes/snes/chip/superfx/bus/bus.hpp create mode 100644 libsnes/snes/chip/superfx/core/core.cpp create mode 100644 libsnes/snes/chip/superfx/core/core.hpp create mode 100644 libsnes/snes/chip/superfx/core/opcode_table.cpp create mode 100644 libsnes/snes/chip/superfx/core/opcodes.cpp create mode 100644 libsnes/snes/chip/superfx/core/registers.hpp create mode 100644 libsnes/snes/chip/superfx/disasm/disasm.cpp create mode 100644 libsnes/snes/chip/superfx/disasm/disasm.hpp create mode 100644 libsnes/snes/chip/superfx/memory/memory.cpp create mode 100644 libsnes/snes/chip/superfx/memory/memory.hpp create mode 100644 libsnes/snes/chip/superfx/mmio/mmio.cpp create mode 100644 libsnes/snes/chip/superfx/mmio/mmio.hpp create mode 100644 libsnes/snes/chip/superfx/serialization.cpp create mode 100644 libsnes/snes/chip/superfx/superfx.cpp create mode 100644 libsnes/snes/chip/superfx/superfx.hpp create mode 100644 libsnes/snes/chip/superfx/timing/timing.cpp create mode 100644 libsnes/snes/chip/superfx/timing/timing.hpp create mode 100644 libsnes/snes/config/config.cpp create mode 100644 libsnes/snes/config/config.hpp create mode 100644 libsnes/snes/controller/controller.cpp create mode 100644 libsnes/snes/controller/controller.hpp create mode 100644 libsnes/snes/controller/gamepad/gamepad.cpp create mode 100644 libsnes/snes/controller/gamepad/gamepad.hpp create mode 100644 libsnes/snes/controller/justifier/justifier.cpp create mode 100644 libsnes/snes/controller/justifier/justifier.hpp create mode 100644 libsnes/snes/controller/mouse/mouse.cpp create mode 100644 libsnes/snes/controller/mouse/mouse.hpp create mode 100644 libsnes/snes/controller/multitap/multitap.cpp create mode 100644 libsnes/snes/controller/multitap/multitap.hpp create mode 100644 libsnes/snes/controller/serial/serial.cpp create mode 100644 libsnes/snes/controller/serial/serial.hpp create mode 100644 libsnes/snes/controller/superscope/superscope.cpp create mode 100644 libsnes/snes/controller/superscope/superscope.hpp create mode 100644 libsnes/snes/cpu/core/algorithms.cpp create mode 100644 libsnes/snes/cpu/core/core.cpp create mode 100644 libsnes/snes/cpu/core/core.hpp create mode 100644 libsnes/snes/cpu/core/memory.hpp create mode 100644 libsnes/snes/cpu/core/opcode_misc.cpp create mode 100644 libsnes/snes/cpu/core/opcode_pc.cpp create mode 100644 libsnes/snes/cpu/core/opcode_read.cpp create mode 100644 libsnes/snes/cpu/core/opcode_rmw.cpp create mode 100644 libsnes/snes/cpu/core/opcode_write.cpp create mode 100644 libsnes/snes/cpu/core/registers.hpp create mode 100644 libsnes/snes/cpu/core/serialization.cpp create mode 100644 libsnes/snes/cpu/core/table.cpp create mode 100644 libsnes/snes/cpu/cpu.cpp create mode 100644 libsnes/snes/cpu/cpu.hpp create mode 100644 libsnes/snes/cpu/debugger/debugger.cpp create mode 100644 libsnes/snes/cpu/debugger/debugger.hpp create mode 100644 libsnes/snes/cpu/dma/dma.cpp create mode 100644 libsnes/snes/cpu/dma/dma.hpp create mode 100644 libsnes/snes/cpu/memory/memory.cpp create mode 100644 libsnes/snes/cpu/memory/memory.hpp create mode 100644 libsnes/snes/cpu/mmio/mmio.cpp create mode 100644 libsnes/snes/cpu/mmio/mmio.hpp create mode 100644 libsnes/snes/cpu/serialization.cpp create mode 100644 libsnes/snes/cpu/timing/irq.cpp create mode 100644 libsnes/snes/cpu/timing/joypad.cpp create mode 100644 libsnes/snes/cpu/timing/timing.cpp create mode 100644 libsnes/snes/cpu/timing/timing.hpp create mode 100644 libsnes/snes/debugger/debugger.cpp create mode 100644 libsnes/snes/debugger/debugger.hpp create mode 100644 libsnes/snes/dsp/brr.cpp create mode 100644 libsnes/snes/dsp/counter.cpp create mode 100644 libsnes/snes/dsp/debugger/debugger.cpp create mode 100644 libsnes/snes/dsp/debugger/debugger.hpp create mode 100644 libsnes/snes/dsp/dsp.cpp create mode 100644 libsnes/snes/dsp/dsp.hpp create mode 100644 libsnes/snes/dsp/echo.cpp create mode 100644 libsnes/snes/dsp/envelope.cpp create mode 100644 libsnes/snes/dsp/gaussian.cpp create mode 100644 libsnes/snes/dsp/misc.cpp create mode 100644 libsnes/snes/dsp/serialization.cpp create mode 100644 libsnes/snes/dsp/voice.cpp create mode 100644 libsnes/snes/interface/interface.cpp create mode 100644 libsnes/snes/interface/interface.hpp create mode 100644 libsnes/snes/libsnes/libsnes.cpp create mode 100644 libsnes/snes/libsnes/libsnes.hpp create mode 100644 libsnes/snes/memory/memory-inline.hpp create mode 100644 libsnes/snes/memory/memory.cpp create mode 100644 libsnes/snes/memory/memory.hpp create mode 100644 libsnes/snes/ppu/background/background.cpp create mode 100644 libsnes/snes/ppu/background/background.hpp create mode 100644 libsnes/snes/ppu/background/mode7.cpp create mode 100644 libsnes/snes/ppu/counter/counter-inline.hpp create mode 100644 libsnes/snes/ppu/counter/counter.hpp create mode 100644 libsnes/snes/ppu/debugger/debugger.cpp create mode 100644 libsnes/snes/ppu/debugger/debugger.hpp create mode 100644 libsnes/snes/ppu/mmio/mmio.cpp create mode 100644 libsnes/snes/ppu/mmio/mmio.hpp create mode 100644 libsnes/snes/ppu/ppu.cpp create mode 100644 libsnes/snes/ppu/ppu.hpp create mode 100644 libsnes/snes/ppu/screen/screen.cpp create mode 100644 libsnes/snes/ppu/screen/screen.hpp create mode 100644 libsnes/snes/ppu/serialization.cpp create mode 100644 libsnes/snes/ppu/sprite/list.cpp create mode 100644 libsnes/snes/ppu/sprite/sprite.cpp create mode 100644 libsnes/snes/ppu/sprite/sprite.hpp create mode 100644 libsnes/snes/ppu/window/window.cpp create mode 100644 libsnes/snes/ppu/window/window.hpp create mode 100644 libsnes/snes/profile-accuracy.hpp create mode 100644 libsnes/snes/profile-compatibility.hpp create mode 100644 libsnes/snes/profile-performance.hpp create mode 100644 libsnes/snes/random/random.cpp create mode 100644 libsnes/snes/random/random.hpp create mode 100755 libsnes/snes/scheduler/scheduler.cpp create mode 100755 libsnes/snes/scheduler/scheduler.hpp create mode 100644 libsnes/snes/smp/core-old/algorithms.cpp create mode 100644 libsnes/snes/smp/core-old/core.cpp create mode 100644 libsnes/snes/smp/core-old/core.hpp create mode 100644 libsnes/snes/smp/core-old/memory.hpp create mode 100644 libsnes/snes/smp/core-old/opcode_misc.cpp create mode 100644 libsnes/snes/smp/core-old/opcode_mov.cpp create mode 100644 libsnes/snes/smp/core-old/opcode_pc.cpp create mode 100644 libsnes/snes/smp/core-old/opcode_read.cpp create mode 100644 libsnes/snes/smp/core-old/opcode_rmw.cpp create mode 100644 libsnes/snes/smp/core-old/registers.hpp create mode 100644 libsnes/snes/smp/core-old/serialization.cpp create mode 100644 libsnes/snes/smp/core-old/table.cpp create mode 100755 libsnes/snes/smp/core/algorithms.cpp create mode 100755 libsnes/snes/smp/core/core.hpp create mode 100755 libsnes/snes/smp/core/memory.hpp create mode 100644 libsnes/snes/smp/core/opcodes.cpp create mode 100755 libsnes/snes/smp/core/registers.hpp create mode 100755 libsnes/snes/smp/core/serialization.cpp create mode 100644 libsnes/snes/smp/core/smp_core.cpp create mode 100755 libsnes/snes/smp/debugger/debugger.cpp create mode 100755 libsnes/snes/smp/debugger/debugger.hpp create mode 100755 libsnes/snes/smp/iplrom.cpp create mode 100755 libsnes/snes/smp/memory/memory.cpp create mode 100755 libsnes/snes/smp/memory/memory.hpp create mode 100755 libsnes/snes/smp/serialization.cpp create mode 100755 libsnes/snes/smp/smp.cpp create mode 100755 libsnes/snes/smp/smp.hpp create mode 100755 libsnes/snes/smp/timing/timing.cpp create mode 100755 libsnes/snes/smp/timing/timing.hpp create mode 100644 libsnes/snes/snes.hpp create mode 100644 libsnes/snes/system/audio.cpp create mode 100644 libsnes/snes/system/audio.hpp create mode 100644 libsnes/snes/system/input.cpp create mode 100644 libsnes/snes/system/input.hpp create mode 100755 libsnes/snes/system/serialization.cpp create mode 100755 libsnes/snes/system/system.cpp create mode 100755 libsnes/snes/system/system.hpp create mode 100644 libsnes/snes/system/video.cpp create mode 100644 libsnes/snes/system/video.hpp diff --git a/BSNES.xcodeproj/project.pbxproj b/BSNES.xcodeproj/project.pbxproj index 003b14f..f55ace4 100644 --- a/BSNES.xcodeproj/project.pbxproj +++ b/BSNES.xcodeproj/project.pbxproj @@ -50,9 +50,46 @@ 82B9198810150FA9007BD6DB /* bsnes.icns in Resources */ = {isa = PBXBuildFile; fileRef = 82B9198710150FA9007BD6DB /* bsnes.icns */; }; 8D5B49B0048680CD000E48DA /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C167DFE841241C02AAC07 /* InfoPlist.strings */; }; 8D5B49B4048680CD000E48DA /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7ADFEA557BF11CA2CBB /* Cocoa.framework */; }; + 9411524114F9DA8B00621C3E /* cpu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9411507D14F9DA8A00621C3E /* cpu.cpp */; }; + 9411524814F9DA8B00621C3E /* dsp.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9411508C14F9DA8A00621C3E /* dsp.cpp */; }; + 9411525F14F9DA8B00621C3E /* ppu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 941150BA14F9DA8A00621C3E /* ppu.cpp */; }; + 9411527914F9DA8B00621C3E /* smp.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 941150DD14F9DA8A00621C3E /* smp.cpp */; }; + 9411527B14F9DA8B00621C3E /* cartridge.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 941150E114F9DA8A00621C3E /* cartridge.cpp */; }; + 9411527E14F9DA8B00621C3E /* cheat.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 941150E614F9DA8A00621C3E /* cheat.cpp */; }; + 9411527F14F9DA8B00621C3E /* bsx.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 941150EA14F9DA8A00621C3E /* bsx.cpp */; }; + 9411528314F9DA8B00621C3E /* hitachidsp.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 941150F714F9DA8A00621C3E /* hitachidsp.cpp */; }; + 9411528814F9DA8B00621C3E /* icd2.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 941150FF14F9DA8A00621C3E /* icd2.cpp */; }; + 9411528C14F9DA8B00621C3E /* link.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9411510914F9DA8A00621C3E /* link.cpp */; }; + 9411528D14F9DA8B00621C3E /* msu1.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9411510C14F9DA8A00621C3E /* msu1.cpp */; }; + 9411529014F9DA8B00621C3E /* necdsp.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9411511114F9DA8A00621C3E /* necdsp.cpp */; }; + 9411529214F9DA8B00621C3E /* nss.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9411511614F9DA8A00621C3E /* nss.cpp */; }; + 9411529314F9DA8B00621C3E /* obc1.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9411511914F9DA8A00621C3E /* obc1.cpp */; }; + 9411529914F9DA8B00621C3E /* sa1.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9411512914F9DA8A00621C3E /* sa1.cpp */; }; + 9411529C14F9DA8B00621C3E /* sdd1.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9411512F14F9DA8A00621C3E /* sdd1.cpp */; }; + 941152A014F9DA8B00621C3E /* spc7110.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9411513614F9DA8A00621C3E /* spc7110.cpp */; }; + 941152A214F9DA8B00621C3E /* srtc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9411513A14F9DA8A00621C3E /* srtc.cpp */; }; + 941152A314F9DA8B00621C3E /* st0018.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9411513D14F9DA8B00621C3E /* st0018.cpp */; }; + 941152A514F9DA8B00621C3E /* sufamiturbo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9411514114F9DA8B00621C3E /* sufamiturbo.cpp */; }; + 941152AE14F9DA8B00621C3E /* superfx.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9411515714F9DA8B00621C3E /* superfx.cpp */; }; + 941152B114F9DA8B00621C3E /* controller.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9411516014F9DA8B00621C3E /* controller.cpp */; }; + 941152D514F9DA8B00621C3E /* interface.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 941151A814F9DA8B00621C3E /* interface.cpp */; }; + 941152D614F9DA8B00621C3E /* libsnes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 941151AB14F9DA8B00621C3E /* libsnes.cpp */; }; + 941152D814F9DA8B00621C3E /* memory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 941151B014F9DA8B00621C3E /* memory.cpp */; }; + 941152FB14F9DA8C00621C3E /* system.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9411520014F9DA8B00621C3E /* system.cpp */; }; + 941153C814F9E32900621C3E /* apu_gb.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9411536A14F9E32900621C3E /* apu_gb.cpp */; }; + 941153CF14F9E32900621C3E /* cartridge_gb.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9411537D14F9E32900621C3E /* cartridge_gb.cpp */; }; + 941153D914F9E32900621C3E /* cheat_gb.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9411539914F9E32900621C3E /* cheat_gb.cpp */; }; + 941153DC14F9E32900621C3E /* cpu_gb.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 941153A114F9E32900621C3E /* cpu_gb.cpp */; }; + 941153E114F9E32900621C3E /* interface_gb.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 941153AD14F9E32900621C3E /* interface_gb.cpp */; }; + 941153E414F9E32900621C3E /* lcd_gb.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 941153B214F9E32900621C3E /* lcd_gb.cpp */; }; + 941153E814F9E32900621C3E /* memory_gb.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 941153BA14F9E32900621C3E /* memory_gb.cpp */; }; + 941153E914F9E32900621C3E /* scheduler_gb.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 941153BD14F9E32900621C3E /* scheduler_gb.cpp */; }; + 941153EE14F9E32900621C3E /* system_gb.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 941153C414F9E32900621C3E /* system_gb.cpp */; }; + 941153EF14F9E32900621C3E /* video_gb.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 941153C714F9E32900621C3E /* video_gb.cpp */; }; + 941153F014F9ED5C00621C3E /* core.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9411517714F9DA8B00621C3E /* core.cpp */; }; + 941153F114F9ED5C00621C3E /* smp_core.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 941151DE14F9DA8B00621C3E /* smp_core.cpp */; }; 9483027314CCA6B1003DAED8 /* libco.c in Sources */ = {isa = PBXBuildFile; fileRef = 9490639714CAB244006C5630 /* libco.c */; }; 949063A214CAB244006C5630 /* libco.h in Headers */ = {isa = PBXBuildFile; fileRef = 9490639814CAB244006C5630 /* libco.h */; }; - 94C0360A14D67FC2001249FA /* libsnes-performance.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 94C0360914D67FC2001249FA /* libsnes-performance.a */; }; 94D9257314CA9879008F697D /* BSNESGameCore.mm in Sources */ = {isa = PBXBuildFile; fileRef = 826FE0F41014D8930023A8E9 /* BSNESGameCore.mm */; }; /* End PBXBuildFile section */ @@ -222,6 +259,453 @@ 82BFB26E1015430A00AC34CF /* libco.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libco.a; sourceTree = BUILT_PRODUCTS_DIR; }; 8D5B49B6048680CD000E48DA /* BSNES.oecoreplugin */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = BSNES.oecoreplugin; sourceTree = BUILT_PRODUCTS_DIR; }; 8D5B49B7048680CD000E48DA /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 9411507D14F9DA8A00621C3E /* cpu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = cpu.cpp; sourceTree = ""; }; + 9411507E14F9DA8A00621C3E /* cpu.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = cpu.hpp; sourceTree = ""; }; + 9411508014F9DA8A00621C3E /* debugger.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = debugger.cpp; sourceTree = ""; }; + 9411508114F9DA8A00621C3E /* debugger.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = debugger.hpp; sourceTree = ""; }; + 9411508214F9DA8A00621C3E /* dma.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dma.cpp; sourceTree = ""; }; + 9411508314F9DA8A00621C3E /* memory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = memory.cpp; sourceTree = ""; }; + 9411508414F9DA8A00621C3E /* mmio.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mmio.cpp; sourceTree = ""; }; + 9411508514F9DA8A00621C3E /* serialization.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = serialization.cpp; sourceTree = ""; }; + 9411508614F9DA8A00621C3E /* timing.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = timing.cpp; sourceTree = ""; }; + 9411508814F9DA8A00621C3E /* blargg_common.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = blargg_common.h; sourceTree = ""; }; + 9411508914F9DA8A00621C3E /* blargg_config.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = blargg_config.h; sourceTree = ""; }; + 9411508A14F9DA8A00621C3E /* blargg_endian.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = blargg_endian.h; sourceTree = ""; }; + 9411508B14F9DA8A00621C3E /* blargg_source.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = blargg_source.h; sourceTree = ""; }; + 9411508C14F9DA8A00621C3E /* dsp.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dsp.cpp; sourceTree = ""; }; + 9411508D14F9DA8A00621C3E /* dsp.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = dsp.hpp; sourceTree = ""; }; + 9411508E14F9DA8A00621C3E /* serialization.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = serialization.cpp; sourceTree = ""; }; + 9411508F14F9DA8A00621C3E /* SPC_DSP.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SPC_DSP.cpp; sourceTree = ""; }; + 9411509014F9DA8A00621C3E /* SPC_DSP.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPC_DSP.h; sourceTree = ""; }; + 9411509314F9DA8A00621C3E /* debugger.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = debugger.cpp; sourceTree = ""; }; + 9411509414F9DA8A00621C3E /* debugger.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = debugger.hpp; sourceTree = ""; }; + 9411509614F9DA8A00621C3E /* memory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = memory.cpp; sourceTree = ""; }; + 9411509714F9DA8A00621C3E /* memory.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = memory.hpp; sourceTree = ""; }; + 9411509914F9DA8A00621C3E /* mmio.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mmio.cpp; sourceTree = ""; }; + 9411509A14F9DA8A00621C3E /* mmio.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = mmio.hpp; sourceTree = ""; }; + 9411509B14F9DA8A00621C3E /* ppu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ppu.cpp; sourceTree = ""; }; + 9411509C14F9DA8A00621C3E /* ppu.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ppu.hpp; sourceTree = ""; }; + 9411509E14F9DA8A00621C3E /* addsub.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = addsub.cpp; sourceTree = ""; }; + 9411509F14F9DA8A00621C3E /* bg.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = bg.cpp; sourceTree = ""; }; + 941150A014F9DA8A00621C3E /* cache.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = cache.cpp; sourceTree = ""; }; + 941150A114F9DA8A00621C3E /* line.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = line.cpp; sourceTree = ""; }; + 941150A214F9DA8A00621C3E /* mode7.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mode7.cpp; sourceTree = ""; }; + 941150A314F9DA8A00621C3E /* oam.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = oam.cpp; sourceTree = ""; }; + 941150A414F9DA8A00621C3E /* render.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = render.cpp; sourceTree = ""; }; + 941150A514F9DA8A00621C3E /* render.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = render.hpp; sourceTree = ""; }; + 941150A614F9DA8A00621C3E /* windows.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = windows.cpp; sourceTree = ""; }; + 941150A714F9DA8A00621C3E /* serialization.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = serialization.cpp; sourceTree = ""; }; + 941150A914F9DA8A00621C3E /* mmio.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mmio.cpp; sourceTree = ""; }; + 941150AA14F9DA8A00621C3E /* ppu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ppu.cpp; sourceTree = ""; }; + 941150AB14F9DA8A00621C3E /* ppu.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ppu.hpp; sourceTree = ""; }; + 941150AE14F9DA8A00621C3E /* background.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = background.cpp; sourceTree = ""; }; + 941150AF14F9DA8A00621C3E /* background.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = background.hpp; sourceTree = ""; }; + 941150B014F9DA8A00621C3E /* mode7.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mode7.cpp; sourceTree = ""; }; + 941150B214F9DA8A00621C3E /* cache.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = cache.cpp; sourceTree = ""; }; + 941150B314F9DA8A00621C3E /* cache.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = cache.hpp; sourceTree = ""; }; + 941150B514F9DA8A00621C3E /* debugger.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = debugger.cpp; sourceTree = ""; }; + 941150B614F9DA8A00621C3E /* debugger.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = debugger.hpp; sourceTree = ""; }; + 941150B814F9DA8A00621C3E /* mmio.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mmio.cpp; sourceTree = ""; }; + 941150B914F9DA8A00621C3E /* mmio.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = mmio.hpp; sourceTree = ""; }; + 941150BA14F9DA8A00621C3E /* ppu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ppu.cpp; sourceTree = ""; }; + 941150BB14F9DA8A00621C3E /* ppu.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ppu.hpp; sourceTree = ""; }; + 941150BD14F9DA8A00621C3E /* screen.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = screen.cpp; sourceTree = ""; }; + 941150BE14F9DA8A00621C3E /* screen.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = screen.hpp; sourceTree = ""; }; + 941150BF14F9DA8A00621C3E /* serialization.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = serialization.cpp; sourceTree = ""; }; + 941150C114F9DA8A00621C3E /* sprite.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = sprite.cpp; sourceTree = ""; }; + 941150C214F9DA8A00621C3E /* sprite.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = sprite.hpp; sourceTree = ""; }; + 941150C414F9DA8A00621C3E /* window.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = window.cpp; sourceTree = ""; }; + 941150C514F9DA8A00621C3E /* window.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = window.hpp; sourceTree = ""; }; + 941150C714F9DA8A00621C3E /* algorithms.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = algorithms.cpp; sourceTree = ""; }; + 941150C914F9DA8A00621C3E /* cc.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = cc.sh; sourceTree = ""; }; + 941150CA14F9DA8A00621C3E /* generate.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = generate.cpp; sourceTree = ""; }; + 941150CB14F9DA8A00621C3E /* op_misc.b */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = op_misc.b; sourceTree = ""; }; + 941150CC14F9DA8A00621C3E /* op_misc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = op_misc.cpp; sourceTree = ""; }; + 941150CD14F9DA8A00621C3E /* op_mov.b */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = op_mov.b; sourceTree = ""; }; + 941150CE14F9DA8A00621C3E /* op_mov.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = op_mov.cpp; sourceTree = ""; }; + 941150CF14F9DA8A00621C3E /* op_pc.b */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = op_pc.b; sourceTree = ""; }; + 941150D014F9DA8A00621C3E /* op_pc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = op_pc.cpp; sourceTree = ""; }; + 941150D114F9DA8A00621C3E /* op_read.b */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = op_read.b; sourceTree = ""; }; + 941150D214F9DA8A00621C3E /* op_read.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = op_read.cpp; sourceTree = ""; }; + 941150D314F9DA8A00621C3E /* op_rmw.b */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = op_rmw.b; sourceTree = ""; }; + 941150D414F9DA8A00621C3E /* op_rmw.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = op_rmw.cpp; sourceTree = ""; }; + 941150D514F9DA8A00621C3E /* opcycle_misc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opcycle_misc.cpp; sourceTree = ""; }; + 941150D614F9DA8A00621C3E /* opcycle_mov.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opcycle_mov.cpp; sourceTree = ""; }; + 941150D714F9DA8A00621C3E /* opcycle_pc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opcycle_pc.cpp; sourceTree = ""; }; + 941150D814F9DA8A00621C3E /* opcycle_read.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opcycle_read.cpp; sourceTree = ""; }; + 941150D914F9DA8A00621C3E /* opcycle_rmw.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opcycle_rmw.cpp; sourceTree = ""; }; + 941150DA14F9DA8A00621C3E /* core.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = core.cpp; sourceTree = ""; }; + 941150DB14F9DA8A00621C3E /* iplrom.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = iplrom.cpp; sourceTree = ""; }; + 941150DC14F9DA8A00621C3E /* memory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = memory.cpp; sourceTree = ""; }; + 941150DD14F9DA8A00621C3E /* smp.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = smp.cpp; sourceTree = ""; }; + 941150DE14F9DA8A00621C3E /* smp.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = smp.hpp; sourceTree = ""; }; + 941150DF14F9DA8A00621C3E /* timing.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = timing.cpp; sourceTree = ""; }; + 941150E114F9DA8A00621C3E /* cartridge.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = cartridge.cpp; sourceTree = ""; }; + 941150E214F9DA8A00621C3E /* cartridge.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = cartridge.hpp; sourceTree = ""; }; + 941150E314F9DA8A00621C3E /* markup.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = markup.cpp; sourceTree = ""; }; + 941150E414F9DA8A00621C3E /* serialization.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = serialization.cpp; sourceTree = ""; }; + 941150E614F9DA8A00621C3E /* cheat.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = cheat.cpp; sourceTree = ""; }; + 941150E714F9DA8A00621C3E /* cheat.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = cheat.hpp; sourceTree = ""; }; + 941150EA14F9DA8A00621C3E /* bsx.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = bsx.cpp; sourceTree = ""; }; + 941150EB14F9DA8A00621C3E /* bsx.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = bsx.hpp; sourceTree = ""; }; + 941150ED14F9DA8A00621C3E /* cartridge.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = cartridge.cpp; sourceTree = ""; }; + 941150EE14F9DA8A00621C3E /* cartridge.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = cartridge.hpp; sourceTree = ""; }; + 941150F014F9DA8A00621C3E /* flash.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = flash.cpp; sourceTree = ""; }; + 941150F114F9DA8A00621C3E /* flash.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = flash.hpp; sourceTree = ""; }; + 941150F314F9DA8A00621C3E /* satellaview.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = satellaview.cpp; sourceTree = ""; }; + 941150F414F9DA8A00621C3E /* satellaview.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = satellaview.hpp; sourceTree = ""; }; + 941150F514F9DA8A00621C3E /* chip.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = chip.hpp; sourceTree = ""; }; + 941150F714F9DA8A00621C3E /* hitachidsp.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = hitachidsp.cpp; sourceTree = ""; }; + 941150F814F9DA8A00621C3E /* hitachidsp.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = hitachidsp.hpp; sourceTree = ""; }; + 941150F914F9DA8A00621C3E /* memory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = memory.cpp; sourceTree = ""; }; + 941150FA14F9DA8A00621C3E /* opcodes.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opcodes.cpp; sourceTree = ""; }; + 941150FB14F9DA8A00621C3E /* registers.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = registers.cpp; sourceTree = ""; }; + 941150FC14F9DA8A00621C3E /* registers.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = registers.hpp; sourceTree = ""; }; + 941150FD14F9DA8A00621C3E /* serialization.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = serialization.cpp; sourceTree = ""; }; + 941150FF14F9DA8A00621C3E /* icd2.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = icd2.cpp; sourceTree = ""; }; + 9411510014F9DA8A00621C3E /* icd2.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = icd2.hpp; sourceTree = ""; }; + 9411510214F9DA8A00621C3E /* interface.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = interface.cpp; sourceTree = ""; }; + 9411510314F9DA8A00621C3E /* interface.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = interface.hpp; sourceTree = ""; }; + 9411510514F9DA8A00621C3E /* mmio.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mmio.cpp; sourceTree = ""; }; + 9411510614F9DA8A00621C3E /* mmio.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = mmio.hpp; sourceTree = ""; }; + 9411510714F9DA8A00621C3E /* serialization.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = serialization.cpp; sourceTree = ""; }; + 9411510914F9DA8A00621C3E /* link.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = link.cpp; sourceTree = ""; }; + 9411510A14F9DA8A00621C3E /* link.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = link.hpp; sourceTree = ""; }; + 9411510C14F9DA8A00621C3E /* msu1.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = msu1.cpp; sourceTree = ""; }; + 9411510D14F9DA8A00621C3E /* msu1.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = msu1.hpp; sourceTree = ""; }; + 9411510E14F9DA8A00621C3E /* serialization.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = serialization.cpp; sourceTree = ""; }; + 9411511014F9DA8A00621C3E /* memory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = memory.cpp; sourceTree = ""; }; + 9411511114F9DA8A00621C3E /* necdsp.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = necdsp.cpp; sourceTree = ""; }; + 9411511214F9DA8A00621C3E /* necdsp.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = necdsp.hpp; sourceTree = ""; }; + 9411511314F9DA8A00621C3E /* registers.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = registers.hpp; sourceTree = ""; }; + 9411511414F9DA8A00621C3E /* serialization.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = serialization.cpp; sourceTree = ""; }; + 9411511614F9DA8A00621C3E /* nss.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = nss.cpp; sourceTree = ""; }; + 9411511714F9DA8A00621C3E /* nss.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = nss.hpp; sourceTree = ""; }; + 9411511914F9DA8A00621C3E /* obc1.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = obc1.cpp; sourceTree = ""; }; + 9411511A14F9DA8A00621C3E /* obc1.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = obc1.hpp; sourceTree = ""; }; + 9411511B14F9DA8A00621C3E /* serialization.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = serialization.cpp; sourceTree = ""; }; + 9411511E14F9DA8A00621C3E /* bus.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = bus.cpp; sourceTree = ""; }; + 9411511F14F9DA8A00621C3E /* bus.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = bus.hpp; sourceTree = ""; }; + 9411512114F9DA8A00621C3E /* dma.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dma.cpp; sourceTree = ""; }; + 9411512214F9DA8A00621C3E /* dma.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = dma.hpp; sourceTree = ""; }; + 9411512414F9DA8A00621C3E /* memory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = memory.cpp; sourceTree = ""; }; + 9411512514F9DA8A00621C3E /* memory.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = memory.hpp; sourceTree = ""; }; + 9411512714F9DA8A00621C3E /* mmio.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mmio.cpp; sourceTree = ""; }; + 9411512814F9DA8A00621C3E /* mmio.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = mmio.hpp; sourceTree = ""; }; + 9411512914F9DA8A00621C3E /* sa1.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = sa1.cpp; sourceTree = ""; }; + 9411512A14F9DA8A00621C3E /* sa1.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = sa1.hpp; sourceTree = ""; }; + 9411512B14F9DA8A00621C3E /* serialization.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = serialization.cpp; sourceTree = ""; }; + 9411512D14F9DA8A00621C3E /* decomp.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = decomp.cpp; sourceTree = ""; }; + 9411512E14F9DA8A00621C3E /* decomp.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = decomp.hpp; sourceTree = ""; }; + 9411512F14F9DA8A00621C3E /* sdd1.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = sdd1.cpp; sourceTree = ""; }; + 9411513014F9DA8A00621C3E /* sdd1.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = sdd1.hpp; sourceTree = ""; }; + 9411513114F9DA8A00621C3E /* serialization.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = serialization.cpp; sourceTree = ""; }; + 9411513314F9DA8A00621C3E /* decomp.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = decomp.cpp; sourceTree = ""; }; + 9411513414F9DA8A00621C3E /* decomp.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = decomp.hpp; sourceTree = ""; }; + 9411513514F9DA8A00621C3E /* serialization.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = serialization.cpp; sourceTree = ""; }; + 9411513614F9DA8A00621C3E /* spc7110.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = spc7110.cpp; sourceTree = ""; }; + 9411513714F9DA8A00621C3E /* spc7110.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = spc7110.hpp; sourceTree = ""; }; + 9411513914F9DA8A00621C3E /* serialization.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = serialization.cpp; sourceTree = ""; }; + 9411513A14F9DA8A00621C3E /* srtc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = srtc.cpp; sourceTree = ""; }; + 9411513B14F9DA8A00621C3E /* srtc.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = srtc.hpp; sourceTree = ""; }; + 9411513D14F9DA8B00621C3E /* st0018.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = st0018.cpp; sourceTree = ""; }; + 9411513E14F9DA8B00621C3E /* st0018.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = st0018.hpp; sourceTree = ""; }; + 9411514014F9DA8B00621C3E /* serialization.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = serialization.cpp; sourceTree = ""; }; + 9411514114F9DA8B00621C3E /* sufamiturbo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = sufamiturbo.cpp; sourceTree = ""; }; + 9411514214F9DA8B00621C3E /* sufamiturbo.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = sufamiturbo.hpp; sourceTree = ""; }; + 9411514514F9DA8B00621C3E /* bus.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = bus.cpp; sourceTree = ""; }; + 9411514614F9DA8B00621C3E /* bus.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = bus.hpp; sourceTree = ""; }; + 9411514814F9DA8B00621C3E /* core.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = core.cpp; sourceTree = ""; }; + 9411514914F9DA8B00621C3E /* core.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = core.hpp; sourceTree = ""; }; + 9411514A14F9DA8B00621C3E /* opcode_table.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opcode_table.cpp; sourceTree = ""; }; + 9411514B14F9DA8B00621C3E /* opcodes.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opcodes.cpp; sourceTree = ""; }; + 9411514C14F9DA8B00621C3E /* registers.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = registers.hpp; sourceTree = ""; }; + 9411514E14F9DA8B00621C3E /* disasm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = disasm.cpp; sourceTree = ""; }; + 9411514F14F9DA8B00621C3E /* disasm.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = disasm.hpp; sourceTree = ""; }; + 9411515114F9DA8B00621C3E /* memory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = memory.cpp; sourceTree = ""; }; + 9411515214F9DA8B00621C3E /* memory.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = memory.hpp; sourceTree = ""; }; + 9411515414F9DA8B00621C3E /* mmio.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mmio.cpp; sourceTree = ""; }; + 9411515514F9DA8B00621C3E /* mmio.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = mmio.hpp; sourceTree = ""; }; + 9411515614F9DA8B00621C3E /* serialization.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = serialization.cpp; sourceTree = ""; }; + 9411515714F9DA8B00621C3E /* superfx.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = superfx.cpp; sourceTree = ""; }; + 9411515814F9DA8B00621C3E /* superfx.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = superfx.hpp; sourceTree = ""; }; + 9411515A14F9DA8B00621C3E /* timing.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = timing.cpp; sourceTree = ""; }; + 9411515B14F9DA8B00621C3E /* timing.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = timing.hpp; sourceTree = ""; }; + 9411515D14F9DA8B00621C3E /* config.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = config.cpp; sourceTree = ""; }; + 9411515E14F9DA8B00621C3E /* config.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = config.hpp; sourceTree = ""; }; + 9411516014F9DA8B00621C3E /* controller.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = controller.cpp; sourceTree = ""; }; + 9411516114F9DA8B00621C3E /* controller.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = controller.hpp; sourceTree = ""; }; + 9411516314F9DA8B00621C3E /* gamepad.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = gamepad.cpp; sourceTree = ""; }; + 9411516414F9DA8B00621C3E /* gamepad.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = gamepad.hpp; sourceTree = ""; }; + 9411516614F9DA8B00621C3E /* justifier.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = justifier.cpp; sourceTree = ""; }; + 9411516714F9DA8B00621C3E /* justifier.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = justifier.hpp; sourceTree = ""; }; + 9411516914F9DA8B00621C3E /* mouse.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mouse.cpp; sourceTree = ""; }; + 9411516A14F9DA8B00621C3E /* mouse.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = mouse.hpp; sourceTree = ""; }; + 9411516C14F9DA8B00621C3E /* multitap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = multitap.cpp; sourceTree = ""; }; + 9411516D14F9DA8B00621C3E /* multitap.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = multitap.hpp; sourceTree = ""; }; + 9411516F14F9DA8B00621C3E /* serial.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = serial.cpp; sourceTree = ""; }; + 9411517014F9DA8B00621C3E /* serial.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = serial.hpp; sourceTree = ""; }; + 9411517214F9DA8B00621C3E /* superscope.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = superscope.cpp; sourceTree = ""; }; + 9411517314F9DA8B00621C3E /* superscope.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = superscope.hpp; sourceTree = ""; }; + 9411517614F9DA8B00621C3E /* algorithms.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = algorithms.cpp; sourceTree = ""; }; + 9411517714F9DA8B00621C3E /* core.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = core.cpp; sourceTree = ""; }; + 9411517814F9DA8B00621C3E /* core.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = core.hpp; sourceTree = ""; }; + 9411517914F9DA8B00621C3E /* memory.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = memory.hpp; sourceTree = ""; }; + 9411517A14F9DA8B00621C3E /* opcode_misc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opcode_misc.cpp; sourceTree = ""; }; + 9411517B14F9DA8B00621C3E /* opcode_pc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opcode_pc.cpp; sourceTree = ""; }; + 9411517C14F9DA8B00621C3E /* opcode_read.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opcode_read.cpp; sourceTree = ""; }; + 9411517D14F9DA8B00621C3E /* opcode_rmw.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opcode_rmw.cpp; sourceTree = ""; }; + 9411517E14F9DA8B00621C3E /* opcode_write.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opcode_write.cpp; sourceTree = ""; }; + 9411517F14F9DA8B00621C3E /* registers.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = registers.hpp; sourceTree = ""; }; + 9411518014F9DA8B00621C3E /* serialization.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = serialization.cpp; sourceTree = ""; }; + 9411518114F9DA8B00621C3E /* table.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = table.cpp; sourceTree = ""; }; + 9411518214F9DA8B00621C3E /* cpu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = cpu.cpp; sourceTree = ""; }; + 9411518314F9DA8B00621C3E /* cpu.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = cpu.hpp; sourceTree = ""; }; + 9411518514F9DA8B00621C3E /* debugger.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = debugger.cpp; sourceTree = ""; }; + 9411518614F9DA8B00621C3E /* debugger.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = debugger.hpp; sourceTree = ""; }; + 9411518814F9DA8B00621C3E /* dma.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dma.cpp; sourceTree = ""; }; + 9411518914F9DA8B00621C3E /* dma.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = dma.hpp; sourceTree = ""; }; + 9411518B14F9DA8B00621C3E /* memory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = memory.cpp; sourceTree = ""; }; + 9411518C14F9DA8B00621C3E /* memory.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = memory.hpp; sourceTree = ""; }; + 9411518E14F9DA8B00621C3E /* mmio.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mmio.cpp; sourceTree = ""; }; + 9411518F14F9DA8B00621C3E /* mmio.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = mmio.hpp; sourceTree = ""; }; + 9411519014F9DA8B00621C3E /* serialization.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = serialization.cpp; sourceTree = ""; }; + 9411519214F9DA8B00621C3E /* irq.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = irq.cpp; sourceTree = ""; }; + 9411519314F9DA8B00621C3E /* joypad.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = joypad.cpp; sourceTree = ""; }; + 9411519414F9DA8B00621C3E /* timing.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = timing.cpp; sourceTree = ""; }; + 9411519514F9DA8B00621C3E /* timing.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = timing.hpp; sourceTree = ""; }; + 9411519714F9DA8B00621C3E /* debugger.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = debugger.cpp; sourceTree = ""; }; + 9411519814F9DA8B00621C3E /* debugger.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = debugger.hpp; sourceTree = ""; }; + 9411519A14F9DA8B00621C3E /* brr.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = brr.cpp; sourceTree = ""; }; + 9411519B14F9DA8B00621C3E /* counter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = counter.cpp; sourceTree = ""; }; + 9411519D14F9DA8B00621C3E /* debugger.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = debugger.cpp; sourceTree = ""; }; + 9411519E14F9DA8B00621C3E /* debugger.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = debugger.hpp; sourceTree = ""; }; + 9411519F14F9DA8B00621C3E /* dsp.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dsp.cpp; sourceTree = ""; }; + 941151A014F9DA8B00621C3E /* dsp.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = dsp.hpp; sourceTree = ""; }; + 941151A114F9DA8B00621C3E /* echo.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = echo.cpp; sourceTree = ""; }; + 941151A214F9DA8B00621C3E /* envelope.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = envelope.cpp; sourceTree = ""; }; + 941151A314F9DA8B00621C3E /* gaussian.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = gaussian.cpp; sourceTree = ""; }; + 941151A414F9DA8B00621C3E /* misc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = misc.cpp; sourceTree = ""; }; + 941151A514F9DA8B00621C3E /* serialization.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = serialization.cpp; sourceTree = ""; }; + 941151A614F9DA8B00621C3E /* voice.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = voice.cpp; sourceTree = ""; }; + 941151A814F9DA8B00621C3E /* interface.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = interface.cpp; sourceTree = ""; }; + 941151A914F9DA8B00621C3E /* interface.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = interface.hpp; sourceTree = ""; }; + 941151AB14F9DA8B00621C3E /* libsnes.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = libsnes.cpp; sourceTree = ""; }; + 941151AD14F9DA8B00621C3E /* Makefile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.make; path = Makefile; sourceTree = ""; }; + 941151AF14F9DA8B00621C3E /* memory-inline.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = "memory-inline.hpp"; sourceTree = ""; }; + 941151B014F9DA8B00621C3E /* memory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = memory.cpp; sourceTree = ""; }; + 941151B114F9DA8B00621C3E /* memory.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = memory.hpp; sourceTree = ""; }; + 941151B414F9DA8B00621C3E /* background.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = background.cpp; sourceTree = ""; }; + 941151B514F9DA8B00621C3E /* background.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = background.hpp; sourceTree = ""; }; + 941151B614F9DA8B00621C3E /* mode7.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mode7.cpp; sourceTree = ""; }; + 941151B814F9DA8B00621C3E /* counter-inline.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = "counter-inline.hpp"; sourceTree = ""; }; + 941151B914F9DA8B00621C3E /* counter.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = counter.hpp; sourceTree = ""; }; + 941151BB14F9DA8B00621C3E /* debugger.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = debugger.cpp; sourceTree = ""; }; + 941151BC14F9DA8B00621C3E /* debugger.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = debugger.hpp; sourceTree = ""; }; + 941151BE14F9DA8B00621C3E /* mmio.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mmio.cpp; sourceTree = ""; }; + 941151BF14F9DA8B00621C3E /* mmio.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = mmio.hpp; sourceTree = ""; }; + 941151C014F9DA8B00621C3E /* ppu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ppu.cpp; sourceTree = ""; }; + 941151C114F9DA8B00621C3E /* ppu.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ppu.hpp; sourceTree = ""; }; + 941151C314F9DA8B00621C3E /* screen.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = screen.cpp; sourceTree = ""; }; + 941151C414F9DA8B00621C3E /* screen.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = screen.hpp; sourceTree = ""; }; + 941151C514F9DA8B00621C3E /* serialization.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = serialization.cpp; sourceTree = ""; }; + 941151C714F9DA8B00621C3E /* list.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = list.cpp; sourceTree = ""; }; + 941151C814F9DA8B00621C3E /* sprite.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = sprite.cpp; sourceTree = ""; }; + 941151C914F9DA8B00621C3E /* sprite.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = sprite.hpp; sourceTree = ""; }; + 941151CB14F9DA8B00621C3E /* window.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = window.cpp; sourceTree = ""; }; + 941151CC14F9DA8B00621C3E /* window.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = window.hpp; sourceTree = ""; }; + 941151CD14F9DA8B00621C3E /* profile-accuracy.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = "profile-accuracy.hpp"; sourceTree = ""; }; + 941151CE14F9DA8B00621C3E /* profile-compatibility.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = "profile-compatibility.hpp"; sourceTree = ""; }; + 941151CF14F9DA8B00621C3E /* profile-performance.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = "profile-performance.hpp"; sourceTree = ""; }; + 941151D114F9DA8B00621C3E /* random.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = random.cpp; sourceTree = ""; }; + 941151D214F9DA8B00621C3E /* random.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = random.hpp; sourceTree = ""; }; + 941151D414F9DA8B00621C3E /* scheduler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = scheduler.cpp; sourceTree = ""; }; + 941151D514F9DA8B00621C3E /* scheduler.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = scheduler.hpp; sourceTree = ""; }; + 941151D814F9DA8B00621C3E /* algorithms.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = algorithms.cpp; sourceTree = ""; }; + 941151D914F9DA8B00621C3E /* core.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = core.hpp; sourceTree = ""; }; + 941151DA14F9DA8B00621C3E /* memory.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = memory.hpp; sourceTree = ""; }; + 941151DB14F9DA8B00621C3E /* opcodes.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opcodes.cpp; sourceTree = ""; }; + 941151DC14F9DA8B00621C3E /* registers.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = registers.hpp; sourceTree = ""; }; + 941151DD14F9DA8B00621C3E /* serialization.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = serialization.cpp; sourceTree = ""; }; + 941151DE14F9DA8B00621C3E /* smp_core.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = smp_core.cpp; sourceTree = ""; }; + 941151E014F9DA8B00621C3E /* algorithms.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = algorithms.cpp; sourceTree = ""; }; + 941151E114F9DA8B00621C3E /* core.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = core.cpp; sourceTree = ""; }; + 941151E214F9DA8B00621C3E /* core.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = core.hpp; sourceTree = ""; }; + 941151E314F9DA8B00621C3E /* memory.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = memory.hpp; sourceTree = ""; }; + 941151E414F9DA8B00621C3E /* opcode_misc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opcode_misc.cpp; sourceTree = ""; }; + 941151E514F9DA8B00621C3E /* opcode_mov.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opcode_mov.cpp; sourceTree = ""; }; + 941151E614F9DA8B00621C3E /* opcode_pc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opcode_pc.cpp; sourceTree = ""; }; + 941151E714F9DA8B00621C3E /* opcode_read.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opcode_read.cpp; sourceTree = ""; }; + 941151E814F9DA8B00621C3E /* opcode_rmw.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opcode_rmw.cpp; sourceTree = ""; }; + 941151E914F9DA8B00621C3E /* registers.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = registers.hpp; sourceTree = ""; }; + 941151EA14F9DA8B00621C3E /* serialization.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = serialization.cpp; sourceTree = ""; }; + 941151EB14F9DA8B00621C3E /* table.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = table.cpp; sourceTree = ""; }; + 941151ED14F9DA8B00621C3E /* debugger.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = debugger.cpp; sourceTree = ""; }; + 941151EE14F9DA8B00621C3E /* debugger.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = debugger.hpp; sourceTree = ""; }; + 941151EF14F9DA8B00621C3E /* iplrom.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = iplrom.cpp; sourceTree = ""; }; + 941151F114F9DA8B00621C3E /* memory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = memory.cpp; sourceTree = ""; }; + 941151F214F9DA8B00621C3E /* memory.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = memory.hpp; sourceTree = ""; }; + 941151F314F9DA8B00621C3E /* serialization.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = serialization.cpp; sourceTree = ""; }; + 941151F414F9DA8B00621C3E /* smp.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = smp.cpp; sourceTree = ""; }; + 941151F514F9DA8B00621C3E /* smp.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = smp.hpp; sourceTree = ""; }; + 941151F714F9DA8B00621C3E /* timing.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = timing.cpp; sourceTree = ""; }; + 941151F814F9DA8B00621C3E /* timing.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = timing.hpp; sourceTree = ""; }; + 941151F914F9DA8B00621C3E /* snes.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = snes.hpp; sourceTree = ""; }; + 941151FB14F9DA8B00621C3E /* audio.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = audio.cpp; sourceTree = ""; }; + 941151FC14F9DA8B00621C3E /* audio.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = audio.hpp; sourceTree = ""; }; + 941151FD14F9DA8B00621C3E /* input.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = input.cpp; sourceTree = ""; }; + 941151FE14F9DA8B00621C3E /* input.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = input.hpp; sourceTree = ""; }; + 941151FF14F9DA8B00621C3E /* serialization.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = serialization.cpp; sourceTree = ""; }; + 9411520014F9DA8B00621C3E /* system.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = system.cpp; sourceTree = ""; }; + 9411520114F9DA8B00621C3E /* system.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = system.hpp; sourceTree = ""; }; + 9411520214F9DA8B00621C3E /* video.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = video.cpp; sourceTree = ""; }; + 9411520314F9DA8B00621C3E /* video.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = video.hpp; sourceTree = ""; }; + 9411531214F9E2E000621C3E /* algorithm.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = algorithm.hpp; sourceTree = ""; }; + 9411531314F9E2E000621C3E /* array.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = array.hpp; sourceTree = ""; }; + 9411531414F9E2E000621C3E /* base64.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = base64.hpp; sourceTree = ""; }; + 9411531514F9E2E000621C3E /* bit.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = bit.hpp; sourceTree = ""; }; + 9411531614F9E2E000621C3E /* C++98.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = "C++98.hpp"; sourceTree = ""; }; + 9411531714F9E2E000621C3E /* concept.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = concept.hpp; sourceTree = ""; }; + 9411531814F9E2E000621C3E /* config.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = config.hpp; sourceTree = ""; }; + 9411531914F9E2E000621C3E /* crc32.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = crc32.hpp; sourceTree = ""; }; + 9411531A14F9E2E000621C3E /* detect.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = detect.hpp; sourceTree = ""; }; + 9411531B14F9E2E000621C3E /* dictionary.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = dictionary.hpp; sourceTree = ""; }; + 9411531C14F9E2E000621C3E /* directory.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = directory.hpp; sourceTree = ""; }; + 9411531D14F9E2E000621C3E /* dl.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = dl.hpp; sourceTree = ""; }; + 9411531F14F9E2E000621C3E /* buffer.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = buffer.hpp; sourceTree = ""; }; + 9411532014F9E2E000621C3E /* core.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = core.hpp; sourceTree = ""; }; + 9411532214F9E2E000621C3E /* average.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = average.hpp; sourceTree = ""; }; + 9411532314F9E2E000621C3E /* cosine.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = cosine.hpp; sourceTree = ""; }; + 9411532414F9E2E000621C3E /* cubic.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = cubic.hpp; sourceTree = ""; }; + 9411532514F9E2E000621C3E /* hermite.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = hermite.hpp; sourceTree = ""; }; + 9411532714F9E2E000621C3E /* sinc.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = sinc.hpp; sourceTree = ""; }; + 9411532814F9E2E000621C3E /* linear.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = linear.hpp; sourceTree = ""; }; + 9411532914F9E2E000621C3E /* nearest.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = nearest.hpp; sourceTree = ""; }; + 9411532A14F9E2E000621C3E /* sinc.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = sinc.hpp; sourceTree = ""; }; + 9411532B14F9E2E000621C3E /* settings.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = settings.hpp; sourceTree = ""; }; + 9411532C14F9E2E000621C3E /* dsp.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = dsp.hpp; sourceTree = ""; }; + 9411532D14F9E2E000621C3E /* endian.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = endian.hpp; sourceTree = ""; }; + 9411532E14F9E2E000621C3E /* file.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = file.hpp; sourceTree = ""; }; + 9411532F14F9E2E000621C3E /* filemap.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = filemap.hpp; sourceTree = ""; }; + 9411533014F9E2E000621C3E /* foreach.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = foreach.hpp; sourceTree = ""; }; + 9411533114F9E2E000621C3E /* function.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = function.hpp; sourceTree = ""; }; + 9411533314F9E2E000621C3E /* cartridge.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = cartridge.hpp; sourceTree = ""; }; + 9411533414F9E2E000621C3E /* input.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = input.hpp; sourceTree = ""; }; + 9411533514F9E2E000621C3E /* libmalloc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = libmalloc.c; sourceTree = ""; }; + 9411533614F9E2E000621C3E /* lzss.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = lzss.hpp; sourceTree = ""; }; + 9411533714F9E2E000621C3E /* Makefile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.make; path = Makefile; sourceTree = ""; }; + 9411533814F9E2E000621C3E /* memory_debug.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = memory_debug.hpp; sourceTree = ""; }; + 9411533914F9E2E000621C3E /* moduloarray.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = moduloarray.hpp; sourceTree = ""; }; + 9411533A14F9E2E000621C3E /* platform.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = platform.hpp; sourceTree = ""; }; + 9411533B14F9E2E000621C3E /* priorityqueue.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = priorityqueue.hpp; sourceTree = ""; }; + 9411533C14F9E2E000621C3E /* property.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = property.hpp; sourceTree = ""; }; + 9411533D14F9E2E000621C3E /* public_cast.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = public_cast.hpp; sourceTree = ""; }; + 9411533E14F9E2E000621C3E /* random.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = random.hpp; sourceTree = ""; }; + 9411533F14F9E2E000621C3E /* reference_array.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = reference_array.hpp; sourceTree = ""; }; + 9411534014F9E2E000621C3E /* serial.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = serial.hpp; sourceTree = ""; }; + 9411534114F9E2E000621C3E /* serializer.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = serializer.hpp; sourceTree = ""; }; + 9411534214F9E2E000621C3E /* sha256.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = sha256.hpp; sourceTree = ""; }; + 9411534414F9E2E000621C3E /* cartridge.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = cartridge.hpp; sourceTree = ""; }; + 9411534514F9E2E000621C3E /* cpu.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = cpu.hpp; sourceTree = ""; }; + 9411534614F9E2E000621C3E /* smp.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = smp.hpp; sourceTree = ""; }; + 9411534714F9E2E000621C3E /* sort.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = sort.hpp; sourceTree = ""; }; + 9411534814F9E2E000621C3E /* static.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = static.hpp; sourceTree = ""; }; + 9411534914F9E2E000621C3E /* stdint.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = stdint.hpp; sourceTree = ""; }; + 9411534B14F9E2E000621C3E /* base.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = base.hpp; sourceTree = ""; }; + 9411534C14F9E2E000621C3E /* bml.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = bml.hpp; sourceTree = ""; }; + 9411534D14F9E2E000621C3E /* bsv.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = bsv.hpp; sourceTree = ""; }; + 9411534E14F9E2E000621C3E /* cast.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = cast.hpp; sourceTree = ""; }; + 9411534F14F9E2E000621C3E /* compare.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = compare.hpp; sourceTree = ""; }; + 9411535014F9E2E000621C3E /* convert.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = convert.hpp; sourceTree = ""; }; + 9411535114F9E2E000621C3E /* core.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = core.hpp; sourceTree = ""; }; + 9411535214F9E2E000621C3E /* cstring.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = cstring.hpp; sourceTree = ""; }; + 9411535314F9E2E000621C3E /* filename.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = filename.hpp; sourceTree = ""; }; + 9411535414F9E2E000621C3E /* match.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = match.hpp; sourceTree = ""; }; + 9411535514F9E2E000621C3E /* math.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = math.hpp; sourceTree = ""; }; + 9411535614F9E2E000621C3E /* replace.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = replace.hpp; sourceTree = ""; }; + 9411535714F9E2E000621C3E /* split.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = split.hpp; sourceTree = ""; }; + 9411535814F9E2E000621C3E /* strl.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = strl.hpp; sourceTree = ""; }; + 9411535914F9E2E000621C3E /* strpos.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = strpos.hpp; sourceTree = ""; }; + 9411535A14F9E2E000621C3E /* trim.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = trim.hpp; sourceTree = ""; }; + 9411535B14F9E2E000621C3E /* utility.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = utility.hpp; sourceTree = ""; }; + 9411535C14F9E2E000621C3E /* variadic.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = variadic.hpp; sourceTree = ""; }; + 9411535D14F9E2E000621C3E /* wrapper.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = wrapper.hpp; sourceTree = ""; }; + 9411535E14F9E2E000621C3E /* xml.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = xml.hpp; sourceTree = ""; }; + 9411535F14F9E2E000621C3E /* string.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = string.hpp; sourceTree = ""; }; + 9411536014F9E2E000621C3E /* ups.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ups.hpp; sourceTree = ""; }; + 9411536114F9E2E000621C3E /* utf8.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = utf8.hpp; sourceTree = ""; }; + 9411536214F9E2E000621C3E /* utility.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = utility.hpp; sourceTree = ""; }; + 9411536314F9E2E000621C3E /* varint.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = varint.hpp; sourceTree = ""; }; + 9411536414F9E2E000621C3E /* vector.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = vector.hpp; sourceTree = ""; }; + 9411536914F9E32900621C3E /* apu.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = apu.hpp; sourceTree = ""; }; + 9411536A14F9E32900621C3E /* apu_gb.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = apu_gb.cpp; sourceTree = ""; }; + 9411536C14F9E32900621C3E /* master.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = master.cpp; sourceTree = ""; }; + 9411536D14F9E32900621C3E /* master.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = master.hpp; sourceTree = ""; }; + 9411536F14F9E32900621C3E /* noise.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = noise.cpp; sourceTree = ""; }; + 9411537014F9E32900621C3E /* noise.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = noise.hpp; sourceTree = ""; }; + 9411537114F9E32900621C3E /* serialization.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = serialization.cpp; sourceTree = ""; }; + 9411537314F9E32900621C3E /* square1.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = square1.cpp; sourceTree = ""; }; + 9411537414F9E32900621C3E /* square1.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = square1.hpp; sourceTree = ""; }; + 9411537614F9E32900621C3E /* square2.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = square2.cpp; sourceTree = ""; }; + 9411537714F9E32900621C3E /* square2.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = square2.hpp; sourceTree = ""; }; + 9411537914F9E32900621C3E /* wave.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = wave.cpp; sourceTree = ""; }; + 9411537A14F9E32900621C3E /* wave.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = wave.hpp; sourceTree = ""; }; + 9411537C14F9E32900621C3E /* cartridge.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = cartridge.hpp; sourceTree = ""; }; + 9411537D14F9E32900621C3E /* cartridge_gb.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = cartridge_gb.cpp; sourceTree = ""; }; + 9411537F14F9E32900621C3E /* huc1.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = huc1.cpp; sourceTree = ""; }; + 9411538014F9E32900621C3E /* huc1.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = huc1.hpp; sourceTree = ""; }; + 9411538214F9E32900621C3E /* huc3.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = huc3.cpp; sourceTree = ""; }; + 9411538314F9E32900621C3E /* huc3.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = huc3.hpp; sourceTree = ""; }; + 9411538514F9E32900621C3E /* mbc0.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mbc0.cpp; sourceTree = ""; }; + 9411538614F9E32900621C3E /* mbc0.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = mbc0.hpp; sourceTree = ""; }; + 9411538814F9E32900621C3E /* mbc1.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mbc1.cpp; sourceTree = ""; }; + 9411538914F9E32900621C3E /* mbc1.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = mbc1.hpp; sourceTree = ""; }; + 9411538B14F9E32900621C3E /* mbc2.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mbc2.cpp; sourceTree = ""; }; + 9411538C14F9E32900621C3E /* mbc2.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = mbc2.hpp; sourceTree = ""; }; + 9411538E14F9E32900621C3E /* mbc3.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mbc3.cpp; sourceTree = ""; }; + 9411538F14F9E32900621C3E /* mbc3.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = mbc3.hpp; sourceTree = ""; }; + 9411539114F9E32900621C3E /* mbc5.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mbc5.cpp; sourceTree = ""; }; + 9411539214F9E32900621C3E /* mbc5.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = mbc5.hpp; sourceTree = ""; }; + 9411539414F9E32900621C3E /* mmm01.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mmm01.cpp; sourceTree = ""; }; + 9411539514F9E32900621C3E /* mmm01.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = mmm01.hpp; sourceTree = ""; }; + 9411539614F9E32900621C3E /* serialization.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = serialization.cpp; sourceTree = ""; }; + 9411539814F9E32900621C3E /* cheat.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = cheat.hpp; sourceTree = ""; }; + 9411539914F9E32900621C3E /* cheat_gb.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = cheat_gb.cpp; sourceTree = ""; }; + 9411539C14F9E32900621C3E /* core.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = core.cpp; sourceTree = ""; }; + 9411539D14F9E32900621C3E /* core.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = core.hpp; sourceTree = ""; }; + 9411539E14F9E32900621C3E /* registers.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = registers.hpp; sourceTree = ""; }; + 9411539F14F9E32900621C3E /* table.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = table.cpp; sourceTree = ""; }; + 941153A014F9E32900621C3E /* cpu.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = cpu.hpp; sourceTree = ""; }; + 941153A114F9E32900621C3E /* cpu_gb.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = cpu_gb.cpp; sourceTree = ""; }; + 941153A314F9E32900621C3E /* mmio.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mmio.cpp; sourceTree = ""; }; + 941153A414F9E32900621C3E /* mmio.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = mmio.hpp; sourceTree = ""; }; + 941153A514F9E32900621C3E /* serialization.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = serialization.cpp; sourceTree = ""; }; + 941153A714F9E32900621C3E /* opcode.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opcode.cpp; sourceTree = ""; }; + 941153A814F9E32900621C3E /* timing.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = timing.cpp; sourceTree = ""; }; + 941153A914F9E32900621C3E /* timing.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = timing.hpp; sourceTree = ""; }; + 941153AA14F9E32900621C3E /* gameboy.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = gameboy.hpp; sourceTree = ""; }; + 941153AC14F9E32900621C3E /* interface.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = interface.hpp; sourceTree = ""; }; + 941153AD14F9E32900621C3E /* interface_gb.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = interface_gb.cpp; sourceTree = ""; }; + 941153AF14F9E32900621C3E /* cgb.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = cgb.cpp; sourceTree = ""; }; + 941153B014F9E32900621C3E /* dmg.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dmg.cpp; sourceTree = ""; }; + 941153B114F9E32900621C3E /* lcd.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = lcd.hpp; sourceTree = ""; }; + 941153B214F9E32900621C3E /* lcd_gb.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = lcd_gb.cpp; sourceTree = ""; }; + 941153B414F9E32900621C3E /* mmio.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mmio.cpp; sourceTree = ""; }; + 941153B514F9E32900621C3E /* mmio.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = mmio.hpp; sourceTree = ""; }; + 941153B614F9E32900621C3E /* serialization.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = serialization.cpp; sourceTree = ""; }; + 941153B714F9E32900621C3E /* Makefile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.make; path = Makefile; sourceTree = ""; }; + 941153B914F9E32900621C3E /* memory.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = memory.hpp; sourceTree = ""; }; + 941153BA14F9E32900621C3E /* memory_gb.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = memory_gb.cpp; sourceTree = ""; }; + 941153BC14F9E32900621C3E /* scheduler.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = scheduler.hpp; sourceTree = ""; }; + 941153BD14F9E32900621C3E /* scheduler_gb.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = scheduler_gb.cpp; sourceTree = ""; }; + 941153BF14F9E32900621C3E /* bootrom-cgb.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = "bootrom-cgb.cpp"; sourceTree = ""; }; + 941153C014F9E32900621C3E /* bootrom-dmg.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = "bootrom-dmg.cpp"; sourceTree = ""; }; + 941153C114F9E32900621C3E /* bootrom-sgb.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = "bootrom-sgb.cpp"; sourceTree = ""; }; + 941153C214F9E32900621C3E /* serialization.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = serialization.cpp; sourceTree = ""; }; + 941153C314F9E32900621C3E /* system.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = system.hpp; sourceTree = ""; }; + 941153C414F9E32900621C3E /* system_gb.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = system_gb.cpp; sourceTree = ""; }; + 941153C614F9E32900621C3E /* video.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = video.hpp; sourceTree = ""; }; + 941153C714F9E32900621C3E /* video_gb.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = video_gb.cpp; sourceTree = ""; }; 9490639414CAB244006C5630 /* amd64.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = amd64.c; sourceTree = ""; }; 9490639514CAB244006C5630 /* amd64_msvc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = amd64_msvc.c; sourceTree = ""; }; 9490639614CAB244006C5630 /* fiber.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = fiber.c; sourceTree = ""; }; @@ -253,7 +737,6 @@ 8D5B49B4048680CD000E48DA /* Cocoa.framework in Frameworks */, 826FE1291014D8F60023A8E9 /* OpenEmuBase.framework in Frameworks */, 822837B1101A20B10031D125 /* libco.a in Frameworks */, - 94C0360A14D67FC2001249FA /* libsnes-performance.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -300,6 +783,7 @@ 826FE0F31014D8930023A8E9 /* BSNESGameCore.h */, 826FE0F41014D8930023A8E9 /* BSNESGameCore.mm */, 949063A814CAB269006C5630 /* libsnes.hpp */, + 94114FA914F9DA8A00621C3E /* libsnes */, 9490639314CAB244006C5630 /* libco */, 826FDD6610142B230023A8E9 /* data */, ); @@ -380,6 +864,1476 @@ name = Products; sourceTree = ""; }; + 94114FA914F9DA8A00621C3E /* libsnes */ = { + isa = PBXGroup; + children = ( + 9411536714F9E32800621C3E /* gameboy */, + 9411531114F9E2E000621C3E /* nall */, + 9411507A14F9DA8A00621C3E /* snes */, + ); + path = libsnes; + sourceTree = ""; + }; + 9411507A14F9DA8A00621C3E /* snes */ = { + isa = PBXGroup; + children = ( + 9411507B14F9DA8A00621C3E /* alt */, + 941150E014F9DA8A00621C3E /* cartridge */, + 941150E514F9DA8A00621C3E /* cheat */, + 941150E814F9DA8A00621C3E /* chip */, + 9411515C14F9DA8B00621C3E /* config */, + 9411515F14F9DA8B00621C3E /* controller */, + 9411517414F9DA8B00621C3E /* cpu */, + 9411519614F9DA8B00621C3E /* debugger */, + 9411519914F9DA8B00621C3E /* dsp */, + 941151A714F9DA8B00621C3E /* interface */, + 941151AA14F9DA8B00621C3E /* libsnes */, + 941151AD14F9DA8B00621C3E /* Makefile */, + 941151AE14F9DA8B00621C3E /* memory */, + 941151B214F9DA8B00621C3E /* ppu */, + 941151CD14F9DA8B00621C3E /* profile-accuracy.hpp */, + 941151CE14F9DA8B00621C3E /* profile-compatibility.hpp */, + 941151CF14F9DA8B00621C3E /* profile-performance.hpp */, + 941151D014F9DA8B00621C3E /* random */, + 941151D314F9DA8B00621C3E /* scheduler */, + 941151D614F9DA8B00621C3E /* smp */, + 941151F914F9DA8B00621C3E /* snes.hpp */, + 941151FA14F9DA8B00621C3E /* system */, + ); + path = snes; + sourceTree = ""; + }; + 9411507B14F9DA8A00621C3E /* alt */ = { + isa = PBXGroup; + children = ( + 9411507C14F9DA8A00621C3E /* cpu */, + 9411508714F9DA8A00621C3E /* dsp */, + 9411509114F9DA8A00621C3E /* ppu-compatibility */, + 941150A814F9DA8A00621C3E /* ppu-parallel */, + 941150AC14F9DA8A00621C3E /* ppu-performance */, + 941150C614F9DA8A00621C3E /* smp */, + ); + path = alt; + sourceTree = ""; + }; + 9411507C14F9DA8A00621C3E /* cpu */ = { + isa = PBXGroup; + children = ( + 9411507D14F9DA8A00621C3E /* cpu.cpp */, + 9411507E14F9DA8A00621C3E /* cpu.hpp */, + 9411507F14F9DA8A00621C3E /* debugger */, + 9411508214F9DA8A00621C3E /* dma.cpp */, + 9411508314F9DA8A00621C3E /* memory.cpp */, + 9411508414F9DA8A00621C3E /* mmio.cpp */, + 9411508514F9DA8A00621C3E /* serialization.cpp */, + 9411508614F9DA8A00621C3E /* timing.cpp */, + ); + path = cpu; + sourceTree = ""; + }; + 9411507F14F9DA8A00621C3E /* debugger */ = { + isa = PBXGroup; + children = ( + 9411508014F9DA8A00621C3E /* debugger.cpp */, + 9411508114F9DA8A00621C3E /* debugger.hpp */, + ); + path = debugger; + sourceTree = ""; + }; + 9411508714F9DA8A00621C3E /* dsp */ = { + isa = PBXGroup; + children = ( + 9411508814F9DA8A00621C3E /* blargg_common.h */, + 9411508914F9DA8A00621C3E /* blargg_config.h */, + 9411508A14F9DA8A00621C3E /* blargg_endian.h */, + 9411508B14F9DA8A00621C3E /* blargg_source.h */, + 9411508C14F9DA8A00621C3E /* dsp.cpp */, + 9411508D14F9DA8A00621C3E /* dsp.hpp */, + 9411508E14F9DA8A00621C3E /* serialization.cpp */, + 9411508F14F9DA8A00621C3E /* SPC_DSP.cpp */, + 9411509014F9DA8A00621C3E /* SPC_DSP.h */, + ); + path = dsp; + sourceTree = ""; + }; + 9411509114F9DA8A00621C3E /* ppu-compatibility */ = { + isa = PBXGroup; + children = ( + 9411509214F9DA8A00621C3E /* debugger */, + 9411509514F9DA8A00621C3E /* memory */, + 9411509814F9DA8A00621C3E /* mmio */, + 9411509B14F9DA8A00621C3E /* ppu.cpp */, + 9411509C14F9DA8A00621C3E /* ppu.hpp */, + 9411509D14F9DA8A00621C3E /* render */, + 941150A714F9DA8A00621C3E /* serialization.cpp */, + ); + path = "ppu-compatibility"; + sourceTree = ""; + }; + 9411509214F9DA8A00621C3E /* debugger */ = { + isa = PBXGroup; + children = ( + 9411509314F9DA8A00621C3E /* debugger.cpp */, + 9411509414F9DA8A00621C3E /* debugger.hpp */, + ); + path = debugger; + sourceTree = ""; + }; + 9411509514F9DA8A00621C3E /* memory */ = { + isa = PBXGroup; + children = ( + 9411509614F9DA8A00621C3E /* memory.cpp */, + 9411509714F9DA8A00621C3E /* memory.hpp */, + ); + path = memory; + sourceTree = ""; + }; + 9411509814F9DA8A00621C3E /* mmio */ = { + isa = PBXGroup; + children = ( + 9411509914F9DA8A00621C3E /* mmio.cpp */, + 9411509A14F9DA8A00621C3E /* mmio.hpp */, + ); + path = mmio; + sourceTree = ""; + }; + 9411509D14F9DA8A00621C3E /* render */ = { + isa = PBXGroup; + children = ( + 9411509E14F9DA8A00621C3E /* addsub.cpp */, + 9411509F14F9DA8A00621C3E /* bg.cpp */, + 941150A014F9DA8A00621C3E /* cache.cpp */, + 941150A114F9DA8A00621C3E /* line.cpp */, + 941150A214F9DA8A00621C3E /* mode7.cpp */, + 941150A314F9DA8A00621C3E /* oam.cpp */, + 941150A414F9DA8A00621C3E /* render.cpp */, + 941150A514F9DA8A00621C3E /* render.hpp */, + 941150A614F9DA8A00621C3E /* windows.cpp */, + ); + path = render; + sourceTree = ""; + }; + 941150A814F9DA8A00621C3E /* ppu-parallel */ = { + isa = PBXGroup; + children = ( + 941150A914F9DA8A00621C3E /* mmio.cpp */, + 941150AA14F9DA8A00621C3E /* ppu.cpp */, + 941150AB14F9DA8A00621C3E /* ppu.hpp */, + ); + path = "ppu-parallel"; + sourceTree = ""; + }; + 941150AC14F9DA8A00621C3E /* ppu-performance */ = { + isa = PBXGroup; + children = ( + 941150AD14F9DA8A00621C3E /* background */, + 941150B114F9DA8A00621C3E /* cache */, + 941150B414F9DA8A00621C3E /* debugger */, + 941150B714F9DA8A00621C3E /* mmio */, + 941150BA14F9DA8A00621C3E /* ppu.cpp */, + 941150BB14F9DA8A00621C3E /* ppu.hpp */, + 941150BC14F9DA8A00621C3E /* screen */, + 941150BF14F9DA8A00621C3E /* serialization.cpp */, + 941150C014F9DA8A00621C3E /* sprite */, + 941150C314F9DA8A00621C3E /* window */, + ); + path = "ppu-performance"; + sourceTree = ""; + }; + 941150AD14F9DA8A00621C3E /* background */ = { + isa = PBXGroup; + children = ( + 941150AE14F9DA8A00621C3E /* background.cpp */, + 941150AF14F9DA8A00621C3E /* background.hpp */, + 941150B014F9DA8A00621C3E /* mode7.cpp */, + ); + path = background; + sourceTree = ""; + }; + 941150B114F9DA8A00621C3E /* cache */ = { + isa = PBXGroup; + children = ( + 941150B214F9DA8A00621C3E /* cache.cpp */, + 941150B314F9DA8A00621C3E /* cache.hpp */, + ); + path = cache; + sourceTree = ""; + }; + 941150B414F9DA8A00621C3E /* debugger */ = { + isa = PBXGroup; + children = ( + 941150B514F9DA8A00621C3E /* debugger.cpp */, + 941150B614F9DA8A00621C3E /* debugger.hpp */, + ); + path = debugger; + sourceTree = ""; + }; + 941150B714F9DA8A00621C3E /* mmio */ = { + isa = PBXGroup; + children = ( + 941150B814F9DA8A00621C3E /* mmio.cpp */, + 941150B914F9DA8A00621C3E /* mmio.hpp */, + ); + path = mmio; + sourceTree = ""; + }; + 941150BC14F9DA8A00621C3E /* screen */ = { + isa = PBXGroup; + children = ( + 941150BD14F9DA8A00621C3E /* screen.cpp */, + 941150BE14F9DA8A00621C3E /* screen.hpp */, + ); + path = screen; + sourceTree = ""; + }; + 941150C014F9DA8A00621C3E /* sprite */ = { + isa = PBXGroup; + children = ( + 941150C114F9DA8A00621C3E /* sprite.cpp */, + 941150C214F9DA8A00621C3E /* sprite.hpp */, + ); + path = sprite; + sourceTree = ""; + }; + 941150C314F9DA8A00621C3E /* window */ = { + isa = PBXGroup; + children = ( + 941150C414F9DA8A00621C3E /* window.cpp */, + 941150C514F9DA8A00621C3E /* window.hpp */, + ); + path = window; + sourceTree = ""; + }; + 941150C614F9DA8A00621C3E /* smp */ = { + isa = PBXGroup; + children = ( + 941150C714F9DA8A00621C3E /* algorithms.cpp */, + 941150C814F9DA8A00621C3E /* core */, + 941150DA14F9DA8A00621C3E /* core.cpp */, + 941150DB14F9DA8A00621C3E /* iplrom.cpp */, + 941150DC14F9DA8A00621C3E /* memory.cpp */, + 941150DD14F9DA8A00621C3E /* smp.cpp */, + 941150DE14F9DA8A00621C3E /* smp.hpp */, + 941150DF14F9DA8A00621C3E /* timing.cpp */, + ); + path = smp; + sourceTree = ""; + }; + 941150C814F9DA8A00621C3E /* core */ = { + isa = PBXGroup; + children = ( + 941150C914F9DA8A00621C3E /* cc.sh */, + 941150CA14F9DA8A00621C3E /* generate.cpp */, + 941150CB14F9DA8A00621C3E /* op_misc.b */, + 941150CC14F9DA8A00621C3E /* op_misc.cpp */, + 941150CD14F9DA8A00621C3E /* op_mov.b */, + 941150CE14F9DA8A00621C3E /* op_mov.cpp */, + 941150CF14F9DA8A00621C3E /* op_pc.b */, + 941150D014F9DA8A00621C3E /* op_pc.cpp */, + 941150D114F9DA8A00621C3E /* op_read.b */, + 941150D214F9DA8A00621C3E /* op_read.cpp */, + 941150D314F9DA8A00621C3E /* op_rmw.b */, + 941150D414F9DA8A00621C3E /* op_rmw.cpp */, + 941150D514F9DA8A00621C3E /* opcycle_misc.cpp */, + 941150D614F9DA8A00621C3E /* opcycle_mov.cpp */, + 941150D714F9DA8A00621C3E /* opcycle_pc.cpp */, + 941150D814F9DA8A00621C3E /* opcycle_read.cpp */, + 941150D914F9DA8A00621C3E /* opcycle_rmw.cpp */, + ); + path = core; + sourceTree = ""; + }; + 941150E014F9DA8A00621C3E /* cartridge */ = { + isa = PBXGroup; + children = ( + 941150E114F9DA8A00621C3E /* cartridge.cpp */, + 941150E214F9DA8A00621C3E /* cartridge.hpp */, + 941150E314F9DA8A00621C3E /* markup.cpp */, + 941150E414F9DA8A00621C3E /* serialization.cpp */, + ); + path = cartridge; + sourceTree = ""; + }; + 941150E514F9DA8A00621C3E /* cheat */ = { + isa = PBXGroup; + children = ( + 941150E614F9DA8A00621C3E /* cheat.cpp */, + 941150E714F9DA8A00621C3E /* cheat.hpp */, + ); + path = cheat; + sourceTree = ""; + }; + 941150E814F9DA8A00621C3E /* chip */ = { + isa = PBXGroup; + children = ( + 941150E914F9DA8A00621C3E /* bsx */, + 941150F514F9DA8A00621C3E /* chip.hpp */, + 941150F614F9DA8A00621C3E /* hitachidsp */, + 941150FE14F9DA8A00621C3E /* icd2 */, + 9411510814F9DA8A00621C3E /* link */, + 9411510B14F9DA8A00621C3E /* msu1 */, + 9411510F14F9DA8A00621C3E /* necdsp */, + 9411511514F9DA8A00621C3E /* nss */, + 9411511814F9DA8A00621C3E /* obc1 */, + 9411511C14F9DA8A00621C3E /* sa1 */, + 9411512C14F9DA8A00621C3E /* sdd1 */, + 9411513214F9DA8A00621C3E /* spc7110 */, + 9411513814F9DA8A00621C3E /* srtc */, + 9411513C14F9DA8B00621C3E /* st0018 */, + 9411513F14F9DA8B00621C3E /* sufamiturbo */, + 9411514314F9DA8B00621C3E /* superfx */, + ); + path = chip; + sourceTree = ""; + }; + 941150E914F9DA8A00621C3E /* bsx */ = { + isa = PBXGroup; + children = ( + 941150EA14F9DA8A00621C3E /* bsx.cpp */, + 941150EB14F9DA8A00621C3E /* bsx.hpp */, + 941150EC14F9DA8A00621C3E /* cartridge */, + 941150EF14F9DA8A00621C3E /* flash */, + 941150F214F9DA8A00621C3E /* satellaview */, + ); + path = bsx; + sourceTree = ""; + }; + 941150EC14F9DA8A00621C3E /* cartridge */ = { + isa = PBXGroup; + children = ( + 941150ED14F9DA8A00621C3E /* cartridge.cpp */, + 941150EE14F9DA8A00621C3E /* cartridge.hpp */, + ); + path = cartridge; + sourceTree = ""; + }; + 941150EF14F9DA8A00621C3E /* flash */ = { + isa = PBXGroup; + children = ( + 941150F014F9DA8A00621C3E /* flash.cpp */, + 941150F114F9DA8A00621C3E /* flash.hpp */, + ); + path = flash; + sourceTree = ""; + }; + 941150F214F9DA8A00621C3E /* satellaview */ = { + isa = PBXGroup; + children = ( + 941150F314F9DA8A00621C3E /* satellaview.cpp */, + 941150F414F9DA8A00621C3E /* satellaview.hpp */, + ); + path = satellaview; + sourceTree = ""; + }; + 941150F614F9DA8A00621C3E /* hitachidsp */ = { + isa = PBXGroup; + children = ( + 941150F714F9DA8A00621C3E /* hitachidsp.cpp */, + 941150F814F9DA8A00621C3E /* hitachidsp.hpp */, + 941150F914F9DA8A00621C3E /* memory.cpp */, + 941150FA14F9DA8A00621C3E /* opcodes.cpp */, + 941150FB14F9DA8A00621C3E /* registers.cpp */, + 941150FC14F9DA8A00621C3E /* registers.hpp */, + 941150FD14F9DA8A00621C3E /* serialization.cpp */, + ); + path = hitachidsp; + sourceTree = ""; + }; + 941150FE14F9DA8A00621C3E /* icd2 */ = { + isa = PBXGroup; + children = ( + 941150FF14F9DA8A00621C3E /* icd2.cpp */, + 9411510014F9DA8A00621C3E /* icd2.hpp */, + 9411510114F9DA8A00621C3E /* interface */, + 9411510414F9DA8A00621C3E /* mmio */, + 9411510714F9DA8A00621C3E /* serialization.cpp */, + ); + path = icd2; + sourceTree = ""; + }; + 9411510114F9DA8A00621C3E /* interface */ = { + isa = PBXGroup; + children = ( + 9411510214F9DA8A00621C3E /* interface.cpp */, + 9411510314F9DA8A00621C3E /* interface.hpp */, + ); + path = interface; + sourceTree = ""; + }; + 9411510414F9DA8A00621C3E /* mmio */ = { + isa = PBXGroup; + children = ( + 9411510514F9DA8A00621C3E /* mmio.cpp */, + 9411510614F9DA8A00621C3E /* mmio.hpp */, + ); + path = mmio; + sourceTree = ""; + }; + 9411510814F9DA8A00621C3E /* link */ = { + isa = PBXGroup; + children = ( + 9411510914F9DA8A00621C3E /* link.cpp */, + 9411510A14F9DA8A00621C3E /* link.hpp */, + ); + path = link; + sourceTree = ""; + }; + 9411510B14F9DA8A00621C3E /* msu1 */ = { + isa = PBXGroup; + children = ( + 9411510C14F9DA8A00621C3E /* msu1.cpp */, + 9411510D14F9DA8A00621C3E /* msu1.hpp */, + 9411510E14F9DA8A00621C3E /* serialization.cpp */, + ); + path = msu1; + sourceTree = ""; + }; + 9411510F14F9DA8A00621C3E /* necdsp */ = { + isa = PBXGroup; + children = ( + 9411511014F9DA8A00621C3E /* memory.cpp */, + 9411511114F9DA8A00621C3E /* necdsp.cpp */, + 9411511214F9DA8A00621C3E /* necdsp.hpp */, + 9411511314F9DA8A00621C3E /* registers.hpp */, + 9411511414F9DA8A00621C3E /* serialization.cpp */, + ); + path = necdsp; + sourceTree = ""; + }; + 9411511514F9DA8A00621C3E /* nss */ = { + isa = PBXGroup; + children = ( + 9411511614F9DA8A00621C3E /* nss.cpp */, + 9411511714F9DA8A00621C3E /* nss.hpp */, + ); + path = nss; + sourceTree = ""; + }; + 9411511814F9DA8A00621C3E /* obc1 */ = { + isa = PBXGroup; + children = ( + 9411511914F9DA8A00621C3E /* obc1.cpp */, + 9411511A14F9DA8A00621C3E /* obc1.hpp */, + 9411511B14F9DA8A00621C3E /* serialization.cpp */, + ); + path = obc1; + sourceTree = ""; + }; + 9411511C14F9DA8A00621C3E /* sa1 */ = { + isa = PBXGroup; + children = ( + 9411511D14F9DA8A00621C3E /* bus */, + 9411512014F9DA8A00621C3E /* dma */, + 9411512314F9DA8A00621C3E /* memory */, + 9411512614F9DA8A00621C3E /* mmio */, + 9411512914F9DA8A00621C3E /* sa1.cpp */, + 9411512A14F9DA8A00621C3E /* sa1.hpp */, + 9411512B14F9DA8A00621C3E /* serialization.cpp */, + ); + path = sa1; + sourceTree = ""; + }; + 9411511D14F9DA8A00621C3E /* bus */ = { + isa = PBXGroup; + children = ( + 9411511E14F9DA8A00621C3E /* bus.cpp */, + 9411511F14F9DA8A00621C3E /* bus.hpp */, + ); + path = bus; + sourceTree = ""; + }; + 9411512014F9DA8A00621C3E /* dma */ = { + isa = PBXGroup; + children = ( + 9411512114F9DA8A00621C3E /* dma.cpp */, + 9411512214F9DA8A00621C3E /* dma.hpp */, + ); + path = dma; + sourceTree = ""; + }; + 9411512314F9DA8A00621C3E /* memory */ = { + isa = PBXGroup; + children = ( + 9411512414F9DA8A00621C3E /* memory.cpp */, + 9411512514F9DA8A00621C3E /* memory.hpp */, + ); + path = memory; + sourceTree = ""; + }; + 9411512614F9DA8A00621C3E /* mmio */ = { + isa = PBXGroup; + children = ( + 9411512714F9DA8A00621C3E /* mmio.cpp */, + 9411512814F9DA8A00621C3E /* mmio.hpp */, + ); + path = mmio; + sourceTree = ""; + }; + 9411512C14F9DA8A00621C3E /* sdd1 */ = { + isa = PBXGroup; + children = ( + 9411512D14F9DA8A00621C3E /* decomp.cpp */, + 9411512E14F9DA8A00621C3E /* decomp.hpp */, + 9411512F14F9DA8A00621C3E /* sdd1.cpp */, + 9411513014F9DA8A00621C3E /* sdd1.hpp */, + 9411513114F9DA8A00621C3E /* serialization.cpp */, + ); + path = sdd1; + sourceTree = ""; + }; + 9411513214F9DA8A00621C3E /* spc7110 */ = { + isa = PBXGroup; + children = ( + 9411513314F9DA8A00621C3E /* decomp.cpp */, + 9411513414F9DA8A00621C3E /* decomp.hpp */, + 9411513514F9DA8A00621C3E /* serialization.cpp */, + 9411513614F9DA8A00621C3E /* spc7110.cpp */, + 9411513714F9DA8A00621C3E /* spc7110.hpp */, + ); + path = spc7110; + sourceTree = ""; + }; + 9411513814F9DA8A00621C3E /* srtc */ = { + isa = PBXGroup; + children = ( + 9411513914F9DA8A00621C3E /* serialization.cpp */, + 9411513A14F9DA8A00621C3E /* srtc.cpp */, + 9411513B14F9DA8A00621C3E /* srtc.hpp */, + ); + path = srtc; + sourceTree = ""; + }; + 9411513C14F9DA8B00621C3E /* st0018 */ = { + isa = PBXGroup; + children = ( + 9411513D14F9DA8B00621C3E /* st0018.cpp */, + 9411513E14F9DA8B00621C3E /* st0018.hpp */, + ); + path = st0018; + sourceTree = ""; + }; + 9411513F14F9DA8B00621C3E /* sufamiturbo */ = { + isa = PBXGroup; + children = ( + 9411514014F9DA8B00621C3E /* serialization.cpp */, + 9411514114F9DA8B00621C3E /* sufamiturbo.cpp */, + 9411514214F9DA8B00621C3E /* sufamiturbo.hpp */, + ); + path = sufamiturbo; + sourceTree = ""; + }; + 9411514314F9DA8B00621C3E /* superfx */ = { + isa = PBXGroup; + children = ( + 9411514414F9DA8B00621C3E /* bus */, + 9411514714F9DA8B00621C3E /* core */, + 9411514D14F9DA8B00621C3E /* disasm */, + 9411515014F9DA8B00621C3E /* memory */, + 9411515314F9DA8B00621C3E /* mmio */, + 9411515614F9DA8B00621C3E /* serialization.cpp */, + 9411515714F9DA8B00621C3E /* superfx.cpp */, + 9411515814F9DA8B00621C3E /* superfx.hpp */, + 9411515914F9DA8B00621C3E /* timing */, + ); + path = superfx; + sourceTree = ""; + }; + 9411514414F9DA8B00621C3E /* bus */ = { + isa = PBXGroup; + children = ( + 9411514514F9DA8B00621C3E /* bus.cpp */, + 9411514614F9DA8B00621C3E /* bus.hpp */, + ); + path = bus; + sourceTree = ""; + }; + 9411514714F9DA8B00621C3E /* core */ = { + isa = PBXGroup; + children = ( + 9411514814F9DA8B00621C3E /* core.cpp */, + 9411514914F9DA8B00621C3E /* core.hpp */, + 9411514A14F9DA8B00621C3E /* opcode_table.cpp */, + 9411514B14F9DA8B00621C3E /* opcodes.cpp */, + 9411514C14F9DA8B00621C3E /* registers.hpp */, + ); + path = core; + sourceTree = ""; + }; + 9411514D14F9DA8B00621C3E /* disasm */ = { + isa = PBXGroup; + children = ( + 9411514E14F9DA8B00621C3E /* disasm.cpp */, + 9411514F14F9DA8B00621C3E /* disasm.hpp */, + ); + path = disasm; + sourceTree = ""; + }; + 9411515014F9DA8B00621C3E /* memory */ = { + isa = PBXGroup; + children = ( + 9411515114F9DA8B00621C3E /* memory.cpp */, + 9411515214F9DA8B00621C3E /* memory.hpp */, + ); + path = memory; + sourceTree = ""; + }; + 9411515314F9DA8B00621C3E /* mmio */ = { + isa = PBXGroup; + children = ( + 9411515414F9DA8B00621C3E /* mmio.cpp */, + 9411515514F9DA8B00621C3E /* mmio.hpp */, + ); + path = mmio; + sourceTree = ""; + }; + 9411515914F9DA8B00621C3E /* timing */ = { + isa = PBXGroup; + children = ( + 9411515A14F9DA8B00621C3E /* timing.cpp */, + 9411515B14F9DA8B00621C3E /* timing.hpp */, + ); + path = timing; + sourceTree = ""; + }; + 9411515C14F9DA8B00621C3E /* config */ = { + isa = PBXGroup; + children = ( + 9411515D14F9DA8B00621C3E /* config.cpp */, + 9411515E14F9DA8B00621C3E /* config.hpp */, + ); + path = config; + sourceTree = ""; + }; + 9411515F14F9DA8B00621C3E /* controller */ = { + isa = PBXGroup; + children = ( + 9411516014F9DA8B00621C3E /* controller.cpp */, + 9411516114F9DA8B00621C3E /* controller.hpp */, + 9411516214F9DA8B00621C3E /* gamepad */, + 9411516514F9DA8B00621C3E /* justifier */, + 9411516814F9DA8B00621C3E /* mouse */, + 9411516B14F9DA8B00621C3E /* multitap */, + 9411516E14F9DA8B00621C3E /* serial */, + 9411517114F9DA8B00621C3E /* superscope */, + ); + path = controller; + sourceTree = ""; + }; + 9411516214F9DA8B00621C3E /* gamepad */ = { + isa = PBXGroup; + children = ( + 9411516314F9DA8B00621C3E /* gamepad.cpp */, + 9411516414F9DA8B00621C3E /* gamepad.hpp */, + ); + path = gamepad; + sourceTree = ""; + }; + 9411516514F9DA8B00621C3E /* justifier */ = { + isa = PBXGroup; + children = ( + 9411516614F9DA8B00621C3E /* justifier.cpp */, + 9411516714F9DA8B00621C3E /* justifier.hpp */, + ); + path = justifier; + sourceTree = ""; + }; + 9411516814F9DA8B00621C3E /* mouse */ = { + isa = PBXGroup; + children = ( + 9411516914F9DA8B00621C3E /* mouse.cpp */, + 9411516A14F9DA8B00621C3E /* mouse.hpp */, + ); + path = mouse; + sourceTree = ""; + }; + 9411516B14F9DA8B00621C3E /* multitap */ = { + isa = PBXGroup; + children = ( + 9411516C14F9DA8B00621C3E /* multitap.cpp */, + 9411516D14F9DA8B00621C3E /* multitap.hpp */, + ); + path = multitap; + sourceTree = ""; + }; + 9411516E14F9DA8B00621C3E /* serial */ = { + isa = PBXGroup; + children = ( + 9411516F14F9DA8B00621C3E /* serial.cpp */, + 9411517014F9DA8B00621C3E /* serial.hpp */, + ); + path = serial; + sourceTree = ""; + }; + 9411517114F9DA8B00621C3E /* superscope */ = { + isa = PBXGroup; + children = ( + 9411517214F9DA8B00621C3E /* superscope.cpp */, + 9411517314F9DA8B00621C3E /* superscope.hpp */, + ); + path = superscope; + sourceTree = ""; + }; + 9411517414F9DA8B00621C3E /* cpu */ = { + isa = PBXGroup; + children = ( + 9411517514F9DA8B00621C3E /* core */, + 9411518214F9DA8B00621C3E /* cpu.cpp */, + 9411518314F9DA8B00621C3E /* cpu.hpp */, + 9411518414F9DA8B00621C3E /* debugger */, + 9411518714F9DA8B00621C3E /* dma */, + 9411518A14F9DA8B00621C3E /* memory */, + 9411518D14F9DA8B00621C3E /* mmio */, + 9411519014F9DA8B00621C3E /* serialization.cpp */, + 9411519114F9DA8B00621C3E /* timing */, + ); + path = cpu; + sourceTree = ""; + }; + 9411517514F9DA8B00621C3E /* core */ = { + isa = PBXGroup; + children = ( + 9411517614F9DA8B00621C3E /* algorithms.cpp */, + 9411517714F9DA8B00621C3E /* core.cpp */, + 9411517814F9DA8B00621C3E /* core.hpp */, + 9411517914F9DA8B00621C3E /* memory.hpp */, + 9411517A14F9DA8B00621C3E /* opcode_misc.cpp */, + 9411517B14F9DA8B00621C3E /* opcode_pc.cpp */, + 9411517C14F9DA8B00621C3E /* opcode_read.cpp */, + 9411517D14F9DA8B00621C3E /* opcode_rmw.cpp */, + 9411517E14F9DA8B00621C3E /* opcode_write.cpp */, + 9411517F14F9DA8B00621C3E /* registers.hpp */, + 9411518014F9DA8B00621C3E /* serialization.cpp */, + 9411518114F9DA8B00621C3E /* table.cpp */, + ); + path = core; + sourceTree = ""; + }; + 9411518414F9DA8B00621C3E /* debugger */ = { + isa = PBXGroup; + children = ( + 9411518514F9DA8B00621C3E /* debugger.cpp */, + 9411518614F9DA8B00621C3E /* debugger.hpp */, + ); + path = debugger; + sourceTree = ""; + }; + 9411518714F9DA8B00621C3E /* dma */ = { + isa = PBXGroup; + children = ( + 9411518814F9DA8B00621C3E /* dma.cpp */, + 9411518914F9DA8B00621C3E /* dma.hpp */, + ); + path = dma; + sourceTree = ""; + }; + 9411518A14F9DA8B00621C3E /* memory */ = { + isa = PBXGroup; + children = ( + 9411518B14F9DA8B00621C3E /* memory.cpp */, + 9411518C14F9DA8B00621C3E /* memory.hpp */, + ); + path = memory; + sourceTree = ""; + }; + 9411518D14F9DA8B00621C3E /* mmio */ = { + isa = PBXGroup; + children = ( + 9411518E14F9DA8B00621C3E /* mmio.cpp */, + 9411518F14F9DA8B00621C3E /* mmio.hpp */, + ); + path = mmio; + sourceTree = ""; + }; + 9411519114F9DA8B00621C3E /* timing */ = { + isa = PBXGroup; + children = ( + 9411519214F9DA8B00621C3E /* irq.cpp */, + 9411519314F9DA8B00621C3E /* joypad.cpp */, + 9411519414F9DA8B00621C3E /* timing.cpp */, + 9411519514F9DA8B00621C3E /* timing.hpp */, + ); + path = timing; + sourceTree = ""; + }; + 9411519614F9DA8B00621C3E /* debugger */ = { + isa = PBXGroup; + children = ( + 9411519714F9DA8B00621C3E /* debugger.cpp */, + 9411519814F9DA8B00621C3E /* debugger.hpp */, + ); + path = debugger; + sourceTree = ""; + }; + 9411519914F9DA8B00621C3E /* dsp */ = { + isa = PBXGroup; + children = ( + 9411519A14F9DA8B00621C3E /* brr.cpp */, + 9411519B14F9DA8B00621C3E /* counter.cpp */, + 9411519C14F9DA8B00621C3E /* debugger */, + 9411519F14F9DA8B00621C3E /* dsp.cpp */, + 941151A014F9DA8B00621C3E /* dsp.hpp */, + 941151A114F9DA8B00621C3E /* echo.cpp */, + 941151A214F9DA8B00621C3E /* envelope.cpp */, + 941151A314F9DA8B00621C3E /* gaussian.cpp */, + 941151A414F9DA8B00621C3E /* misc.cpp */, + 941151A514F9DA8B00621C3E /* serialization.cpp */, + 941151A614F9DA8B00621C3E /* voice.cpp */, + ); + path = dsp; + sourceTree = ""; + }; + 9411519C14F9DA8B00621C3E /* debugger */ = { + isa = PBXGroup; + children = ( + 9411519D14F9DA8B00621C3E /* debugger.cpp */, + 9411519E14F9DA8B00621C3E /* debugger.hpp */, + ); + path = debugger; + sourceTree = ""; + }; + 941151A714F9DA8B00621C3E /* interface */ = { + isa = PBXGroup; + children = ( + 941151A814F9DA8B00621C3E /* interface.cpp */, + 941151A914F9DA8B00621C3E /* interface.hpp */, + ); + path = interface; + sourceTree = ""; + }; + 941151AA14F9DA8B00621C3E /* libsnes */ = { + isa = PBXGroup; + children = ( + 941151AB14F9DA8B00621C3E /* libsnes.cpp */, + ); + path = libsnes; + sourceTree = ""; + }; + 941151AE14F9DA8B00621C3E /* memory */ = { + isa = PBXGroup; + children = ( + 941151AF14F9DA8B00621C3E /* memory-inline.hpp */, + 941151B014F9DA8B00621C3E /* memory.cpp */, + 941151B114F9DA8B00621C3E /* memory.hpp */, + ); + path = memory; + sourceTree = ""; + }; + 941151B214F9DA8B00621C3E /* ppu */ = { + isa = PBXGroup; + children = ( + 941151B314F9DA8B00621C3E /* background */, + 941151B714F9DA8B00621C3E /* counter */, + 941151BA14F9DA8B00621C3E /* debugger */, + 941151BD14F9DA8B00621C3E /* mmio */, + 941151C014F9DA8B00621C3E /* ppu.cpp */, + 941151C114F9DA8B00621C3E /* ppu.hpp */, + 941151C214F9DA8B00621C3E /* screen */, + 941151C514F9DA8B00621C3E /* serialization.cpp */, + 941151C614F9DA8B00621C3E /* sprite */, + 941151CA14F9DA8B00621C3E /* window */, + ); + path = ppu; + sourceTree = ""; + }; + 941151B314F9DA8B00621C3E /* background */ = { + isa = PBXGroup; + children = ( + 941151B414F9DA8B00621C3E /* background.cpp */, + 941151B514F9DA8B00621C3E /* background.hpp */, + 941151B614F9DA8B00621C3E /* mode7.cpp */, + ); + path = background; + sourceTree = ""; + }; + 941151B714F9DA8B00621C3E /* counter */ = { + isa = PBXGroup; + children = ( + 941151B814F9DA8B00621C3E /* counter-inline.hpp */, + 941151B914F9DA8B00621C3E /* counter.hpp */, + ); + path = counter; + sourceTree = ""; + }; + 941151BA14F9DA8B00621C3E /* debugger */ = { + isa = PBXGroup; + children = ( + 941151BB14F9DA8B00621C3E /* debugger.cpp */, + 941151BC14F9DA8B00621C3E /* debugger.hpp */, + ); + path = debugger; + sourceTree = ""; + }; + 941151BD14F9DA8B00621C3E /* mmio */ = { + isa = PBXGroup; + children = ( + 941151BE14F9DA8B00621C3E /* mmio.cpp */, + 941151BF14F9DA8B00621C3E /* mmio.hpp */, + ); + path = mmio; + sourceTree = ""; + }; + 941151C214F9DA8B00621C3E /* screen */ = { + isa = PBXGroup; + children = ( + 941151C314F9DA8B00621C3E /* screen.cpp */, + 941151C414F9DA8B00621C3E /* screen.hpp */, + ); + path = screen; + sourceTree = ""; + }; + 941151C614F9DA8B00621C3E /* sprite */ = { + isa = PBXGroup; + children = ( + 941151C714F9DA8B00621C3E /* list.cpp */, + 941151C814F9DA8B00621C3E /* sprite.cpp */, + 941151C914F9DA8B00621C3E /* sprite.hpp */, + ); + path = sprite; + sourceTree = ""; + }; + 941151CA14F9DA8B00621C3E /* window */ = { + isa = PBXGroup; + children = ( + 941151CB14F9DA8B00621C3E /* window.cpp */, + 941151CC14F9DA8B00621C3E /* window.hpp */, + ); + path = window; + sourceTree = ""; + }; + 941151D014F9DA8B00621C3E /* random */ = { + isa = PBXGroup; + children = ( + 941151D114F9DA8B00621C3E /* random.cpp */, + 941151D214F9DA8B00621C3E /* random.hpp */, + ); + path = random; + sourceTree = ""; + }; + 941151D314F9DA8B00621C3E /* scheduler */ = { + isa = PBXGroup; + children = ( + 941151D414F9DA8B00621C3E /* scheduler.cpp */, + 941151D514F9DA8B00621C3E /* scheduler.hpp */, + ); + path = scheduler; + sourceTree = ""; + }; + 941151D614F9DA8B00621C3E /* smp */ = { + isa = PBXGroup; + children = ( + 941151D714F9DA8B00621C3E /* core */, + 941151DF14F9DA8B00621C3E /* core-old */, + 941151EC14F9DA8B00621C3E /* debugger */, + 941151EF14F9DA8B00621C3E /* iplrom.cpp */, + 941151F014F9DA8B00621C3E /* memory */, + 941151F314F9DA8B00621C3E /* serialization.cpp */, + 941151F414F9DA8B00621C3E /* smp.cpp */, + 941151F514F9DA8B00621C3E /* smp.hpp */, + 941151F614F9DA8B00621C3E /* timing */, + ); + path = smp; + sourceTree = ""; + }; + 941151D714F9DA8B00621C3E /* core */ = { + isa = PBXGroup; + children = ( + 941151D814F9DA8B00621C3E /* algorithms.cpp */, + 941151D914F9DA8B00621C3E /* core.hpp */, + 941151DA14F9DA8B00621C3E /* memory.hpp */, + 941151DB14F9DA8B00621C3E /* opcodes.cpp */, + 941151DC14F9DA8B00621C3E /* registers.hpp */, + 941151DD14F9DA8B00621C3E /* serialization.cpp */, + 941151DE14F9DA8B00621C3E /* smp_core.cpp */, + ); + path = core; + sourceTree = ""; + }; + 941151DF14F9DA8B00621C3E /* core-old */ = { + isa = PBXGroup; + children = ( + 941151E014F9DA8B00621C3E /* algorithms.cpp */, + 941151E114F9DA8B00621C3E /* core.cpp */, + 941151E214F9DA8B00621C3E /* core.hpp */, + 941151E314F9DA8B00621C3E /* memory.hpp */, + 941151E414F9DA8B00621C3E /* opcode_misc.cpp */, + 941151E514F9DA8B00621C3E /* opcode_mov.cpp */, + 941151E614F9DA8B00621C3E /* opcode_pc.cpp */, + 941151E714F9DA8B00621C3E /* opcode_read.cpp */, + 941151E814F9DA8B00621C3E /* opcode_rmw.cpp */, + 941151E914F9DA8B00621C3E /* registers.hpp */, + 941151EA14F9DA8B00621C3E /* serialization.cpp */, + 941151EB14F9DA8B00621C3E /* table.cpp */, + ); + path = "core-old"; + sourceTree = ""; + }; + 941151EC14F9DA8B00621C3E /* debugger */ = { + isa = PBXGroup; + children = ( + 941151ED14F9DA8B00621C3E /* debugger.cpp */, + 941151EE14F9DA8B00621C3E /* debugger.hpp */, + ); + path = debugger; + sourceTree = ""; + }; + 941151F014F9DA8B00621C3E /* memory */ = { + isa = PBXGroup; + children = ( + 941151F114F9DA8B00621C3E /* memory.cpp */, + 941151F214F9DA8B00621C3E /* memory.hpp */, + ); + path = memory; + sourceTree = ""; + }; + 941151F614F9DA8B00621C3E /* timing */ = { + isa = PBXGroup; + children = ( + 941151F714F9DA8B00621C3E /* timing.cpp */, + 941151F814F9DA8B00621C3E /* timing.hpp */, + ); + path = timing; + sourceTree = ""; + }; + 941151FA14F9DA8B00621C3E /* system */ = { + isa = PBXGroup; + children = ( + 941151FB14F9DA8B00621C3E /* audio.cpp */, + 941151FC14F9DA8B00621C3E /* audio.hpp */, + 941151FD14F9DA8B00621C3E /* input.cpp */, + 941151FE14F9DA8B00621C3E /* input.hpp */, + 941151FF14F9DA8B00621C3E /* serialization.cpp */, + 9411520014F9DA8B00621C3E /* system.cpp */, + 9411520114F9DA8B00621C3E /* system.hpp */, + 9411520214F9DA8B00621C3E /* video.cpp */, + 9411520314F9DA8B00621C3E /* video.hpp */, + ); + path = system; + sourceTree = ""; + }; + 9411531114F9E2E000621C3E /* nall */ = { + isa = PBXGroup; + children = ( + 9411531214F9E2E000621C3E /* algorithm.hpp */, + 9411531314F9E2E000621C3E /* array.hpp */, + 9411531414F9E2E000621C3E /* base64.hpp */, + 9411531514F9E2E000621C3E /* bit.hpp */, + 9411531614F9E2E000621C3E /* C++98.hpp */, + 9411531714F9E2E000621C3E /* concept.hpp */, + 9411531814F9E2E000621C3E /* config.hpp */, + 9411531914F9E2E000621C3E /* crc32.hpp */, + 9411531A14F9E2E000621C3E /* detect.hpp */, + 9411531B14F9E2E000621C3E /* dictionary.hpp */, + 9411531C14F9E2E000621C3E /* directory.hpp */, + 9411531D14F9E2E000621C3E /* dl.hpp */, + 9411531E14F9E2E000621C3E /* dsp */, + 9411532C14F9E2E000621C3E /* dsp.hpp */, + 9411532D14F9E2E000621C3E /* endian.hpp */, + 9411532E14F9E2E000621C3E /* file.hpp */, + 9411532F14F9E2E000621C3E /* filemap.hpp */, + 9411533014F9E2E000621C3E /* foreach.hpp */, + 9411533114F9E2E000621C3E /* function.hpp */, + 9411533214F9E2E000621C3E /* gameboy */, + 9411533414F9E2E000621C3E /* input.hpp */, + 9411533514F9E2E000621C3E /* libmalloc.c */, + 9411533614F9E2E000621C3E /* lzss.hpp */, + 9411533714F9E2E000621C3E /* Makefile */, + 9411533814F9E2E000621C3E /* memory_debug.hpp */, + 9411533914F9E2E000621C3E /* moduloarray.hpp */, + 9411533A14F9E2E000621C3E /* platform.hpp */, + 9411533B14F9E2E000621C3E /* priorityqueue.hpp */, + 9411533C14F9E2E000621C3E /* property.hpp */, + 9411533D14F9E2E000621C3E /* public_cast.hpp */, + 9411533E14F9E2E000621C3E /* random.hpp */, + 9411533F14F9E2E000621C3E /* reference_array.hpp */, + 9411534014F9E2E000621C3E /* serial.hpp */, + 9411534114F9E2E000621C3E /* serializer.hpp */, + 9411534214F9E2E000621C3E /* sha256.hpp */, + 9411534314F9E2E000621C3E /* snes */, + 9411534714F9E2E000621C3E /* sort.hpp */, + 9411534814F9E2E000621C3E /* static.hpp */, + 9411534914F9E2E000621C3E /* stdint.hpp */, + 9411534A14F9E2E000621C3E /* string */, + 9411535F14F9E2E000621C3E /* string.hpp */, + 9411536014F9E2E000621C3E /* ups.hpp */, + 9411536114F9E2E000621C3E /* utf8.hpp */, + 9411536214F9E2E000621C3E /* utility.hpp */, + 9411536314F9E2E000621C3E /* varint.hpp */, + 9411536414F9E2E000621C3E /* vector.hpp */, + ); + path = nall; + sourceTree = ""; + }; + 9411531E14F9E2E000621C3E /* dsp */ = { + isa = PBXGroup; + children = ( + 9411531F14F9E2E000621C3E /* buffer.hpp */, + 9411532014F9E2E000621C3E /* core.hpp */, + 9411532114F9E2E000621C3E /* resample */, + 9411532B14F9E2E000621C3E /* settings.hpp */, + ); + path = dsp; + sourceTree = ""; + }; + 9411532114F9E2E000621C3E /* resample */ = { + isa = PBXGroup; + children = ( + 9411532214F9E2E000621C3E /* average.hpp */, + 9411532314F9E2E000621C3E /* cosine.hpp */, + 9411532414F9E2E000621C3E /* cubic.hpp */, + 9411532514F9E2E000621C3E /* hermite.hpp */, + 9411532614F9E2E000621C3E /* lib */, + 9411532814F9E2E000621C3E /* linear.hpp */, + 9411532914F9E2E000621C3E /* nearest.hpp */, + 9411532A14F9E2E000621C3E /* sinc.hpp */, + ); + path = resample; + sourceTree = ""; + }; + 9411532614F9E2E000621C3E /* lib */ = { + isa = PBXGroup; + children = ( + 9411532714F9E2E000621C3E /* sinc.hpp */, + ); + path = lib; + sourceTree = ""; + }; + 9411533214F9E2E000621C3E /* gameboy */ = { + isa = PBXGroup; + children = ( + 9411533314F9E2E000621C3E /* cartridge.hpp */, + ); + path = gameboy; + sourceTree = ""; + }; + 9411534314F9E2E000621C3E /* snes */ = { + isa = PBXGroup; + children = ( + 9411534414F9E2E000621C3E /* cartridge.hpp */, + 9411534514F9E2E000621C3E /* cpu.hpp */, + 9411534614F9E2E000621C3E /* smp.hpp */, + ); + path = snes; + sourceTree = ""; + }; + 9411534A14F9E2E000621C3E /* string */ = { + isa = PBXGroup; + children = ( + 9411534B14F9E2E000621C3E /* base.hpp */, + 9411534C14F9E2E000621C3E /* bml.hpp */, + 9411534D14F9E2E000621C3E /* bsv.hpp */, + 9411534E14F9E2E000621C3E /* cast.hpp */, + 9411534F14F9E2E000621C3E /* compare.hpp */, + 9411535014F9E2E000621C3E /* convert.hpp */, + 9411535114F9E2E000621C3E /* core.hpp */, + 9411535214F9E2E000621C3E /* cstring.hpp */, + 9411535314F9E2E000621C3E /* filename.hpp */, + 9411535414F9E2E000621C3E /* match.hpp */, + 9411535514F9E2E000621C3E /* math.hpp */, + 9411535614F9E2E000621C3E /* replace.hpp */, + 9411535714F9E2E000621C3E /* split.hpp */, + 9411535814F9E2E000621C3E /* strl.hpp */, + 9411535914F9E2E000621C3E /* strpos.hpp */, + 9411535A14F9E2E000621C3E /* trim.hpp */, + 9411535B14F9E2E000621C3E /* utility.hpp */, + 9411535C14F9E2E000621C3E /* variadic.hpp */, + 9411535D14F9E2E000621C3E /* wrapper.hpp */, + 9411535E14F9E2E000621C3E /* xml.hpp */, + ); + path = string; + sourceTree = ""; + }; + 9411536714F9E32800621C3E /* gameboy */ = { + isa = PBXGroup; + children = ( + 9411536814F9E32900621C3E /* apu */, + 9411537B14F9E32900621C3E /* cartridge */, + 9411539714F9E32900621C3E /* cheat */, + 9411539A14F9E32900621C3E /* cpu */, + 941153AA14F9E32900621C3E /* gameboy.hpp */, + 941153AB14F9E32900621C3E /* interface */, + 941153AE14F9E32900621C3E /* lcd */, + 941153B714F9E32900621C3E /* Makefile */, + 941153B814F9E32900621C3E /* memory */, + 941153BB14F9E32900621C3E /* scheduler */, + 941153BE14F9E32900621C3E /* system */, + 941153C514F9E32900621C3E /* video */, + ); + path = gameboy; + sourceTree = ""; + }; + 9411536814F9E32900621C3E /* apu */ = { + isa = PBXGroup; + children = ( + 9411536914F9E32900621C3E /* apu.hpp */, + 9411536A14F9E32900621C3E /* apu_gb.cpp */, + 9411536B14F9E32900621C3E /* master */, + 9411536E14F9E32900621C3E /* noise */, + 9411537114F9E32900621C3E /* serialization.cpp */, + 9411537214F9E32900621C3E /* square1 */, + 9411537514F9E32900621C3E /* square2 */, + 9411537814F9E32900621C3E /* wave */, + ); + path = apu; + sourceTree = ""; + }; + 9411536B14F9E32900621C3E /* master */ = { + isa = PBXGroup; + children = ( + 9411536C14F9E32900621C3E /* master.cpp */, + 9411536D14F9E32900621C3E /* master.hpp */, + ); + path = master; + sourceTree = ""; + }; + 9411536E14F9E32900621C3E /* noise */ = { + isa = PBXGroup; + children = ( + 9411536F14F9E32900621C3E /* noise.cpp */, + 9411537014F9E32900621C3E /* noise.hpp */, + ); + path = noise; + sourceTree = ""; + }; + 9411537214F9E32900621C3E /* square1 */ = { + isa = PBXGroup; + children = ( + 9411537314F9E32900621C3E /* square1.cpp */, + 9411537414F9E32900621C3E /* square1.hpp */, + ); + path = square1; + sourceTree = ""; + }; + 9411537514F9E32900621C3E /* square2 */ = { + isa = PBXGroup; + children = ( + 9411537614F9E32900621C3E /* square2.cpp */, + 9411537714F9E32900621C3E /* square2.hpp */, + ); + path = square2; + sourceTree = ""; + }; + 9411537814F9E32900621C3E /* wave */ = { + isa = PBXGroup; + children = ( + 9411537914F9E32900621C3E /* wave.cpp */, + 9411537A14F9E32900621C3E /* wave.hpp */, + ); + path = wave; + sourceTree = ""; + }; + 9411537B14F9E32900621C3E /* cartridge */ = { + isa = PBXGroup; + children = ( + 9411537C14F9E32900621C3E /* cartridge.hpp */, + 9411537D14F9E32900621C3E /* cartridge_gb.cpp */, + 9411537E14F9E32900621C3E /* huc1 */, + 9411538114F9E32900621C3E /* huc3 */, + 9411538414F9E32900621C3E /* mbc0 */, + 9411538714F9E32900621C3E /* mbc1 */, + 9411538A14F9E32900621C3E /* mbc2 */, + 9411538D14F9E32900621C3E /* mbc3 */, + 9411539014F9E32900621C3E /* mbc5 */, + 9411539314F9E32900621C3E /* mmm01 */, + 9411539614F9E32900621C3E /* serialization.cpp */, + ); + path = cartridge; + sourceTree = ""; + }; + 9411537E14F9E32900621C3E /* huc1 */ = { + isa = PBXGroup; + children = ( + 9411537F14F9E32900621C3E /* huc1.cpp */, + 9411538014F9E32900621C3E /* huc1.hpp */, + ); + path = huc1; + sourceTree = ""; + }; + 9411538114F9E32900621C3E /* huc3 */ = { + isa = PBXGroup; + children = ( + 9411538214F9E32900621C3E /* huc3.cpp */, + 9411538314F9E32900621C3E /* huc3.hpp */, + ); + path = huc3; + sourceTree = ""; + }; + 9411538414F9E32900621C3E /* mbc0 */ = { + isa = PBXGroup; + children = ( + 9411538514F9E32900621C3E /* mbc0.cpp */, + 9411538614F9E32900621C3E /* mbc0.hpp */, + ); + path = mbc0; + sourceTree = ""; + }; + 9411538714F9E32900621C3E /* mbc1 */ = { + isa = PBXGroup; + children = ( + 9411538814F9E32900621C3E /* mbc1.cpp */, + 9411538914F9E32900621C3E /* mbc1.hpp */, + ); + path = mbc1; + sourceTree = ""; + }; + 9411538A14F9E32900621C3E /* mbc2 */ = { + isa = PBXGroup; + children = ( + 9411538B14F9E32900621C3E /* mbc2.cpp */, + 9411538C14F9E32900621C3E /* mbc2.hpp */, + ); + path = mbc2; + sourceTree = ""; + }; + 9411538D14F9E32900621C3E /* mbc3 */ = { + isa = PBXGroup; + children = ( + 9411538E14F9E32900621C3E /* mbc3.cpp */, + 9411538F14F9E32900621C3E /* mbc3.hpp */, + ); + path = mbc3; + sourceTree = ""; + }; + 9411539014F9E32900621C3E /* mbc5 */ = { + isa = PBXGroup; + children = ( + 9411539114F9E32900621C3E /* mbc5.cpp */, + 9411539214F9E32900621C3E /* mbc5.hpp */, + ); + path = mbc5; + sourceTree = ""; + }; + 9411539314F9E32900621C3E /* mmm01 */ = { + isa = PBXGroup; + children = ( + 9411539414F9E32900621C3E /* mmm01.cpp */, + 9411539514F9E32900621C3E /* mmm01.hpp */, + ); + path = mmm01; + sourceTree = ""; + }; + 9411539714F9E32900621C3E /* cheat */ = { + isa = PBXGroup; + children = ( + 9411539814F9E32900621C3E /* cheat.hpp */, + 9411539914F9E32900621C3E /* cheat_gb.cpp */, + ); + path = cheat; + sourceTree = ""; + }; + 9411539A14F9E32900621C3E /* cpu */ = { + isa = PBXGroup; + children = ( + 9411539B14F9E32900621C3E /* core */, + 941153A014F9E32900621C3E /* cpu.hpp */, + 941153A114F9E32900621C3E /* cpu_gb.cpp */, + 941153A214F9E32900621C3E /* mmio */, + 941153A514F9E32900621C3E /* serialization.cpp */, + 941153A614F9E32900621C3E /* timing */, + ); + path = cpu; + sourceTree = ""; + }; + 9411539B14F9E32900621C3E /* core */ = { + isa = PBXGroup; + children = ( + 9411539C14F9E32900621C3E /* core.cpp */, + 9411539D14F9E32900621C3E /* core.hpp */, + 9411539E14F9E32900621C3E /* registers.hpp */, + 9411539F14F9E32900621C3E /* table.cpp */, + ); + path = core; + sourceTree = ""; + }; + 941153A214F9E32900621C3E /* mmio */ = { + isa = PBXGroup; + children = ( + 941153A314F9E32900621C3E /* mmio.cpp */, + 941153A414F9E32900621C3E /* mmio.hpp */, + ); + path = mmio; + sourceTree = ""; + }; + 941153A614F9E32900621C3E /* timing */ = { + isa = PBXGroup; + children = ( + 941153A714F9E32900621C3E /* opcode.cpp */, + 941153A814F9E32900621C3E /* timing.cpp */, + 941153A914F9E32900621C3E /* timing.hpp */, + ); + path = timing; + sourceTree = ""; + }; + 941153AB14F9E32900621C3E /* interface */ = { + isa = PBXGroup; + children = ( + 941153AC14F9E32900621C3E /* interface.hpp */, + 941153AD14F9E32900621C3E /* interface_gb.cpp */, + ); + path = interface; + sourceTree = ""; + }; + 941153AE14F9E32900621C3E /* lcd */ = { + isa = PBXGroup; + children = ( + 941153AF14F9E32900621C3E /* cgb.cpp */, + 941153B014F9E32900621C3E /* dmg.cpp */, + 941153B114F9E32900621C3E /* lcd.hpp */, + 941153B214F9E32900621C3E /* lcd_gb.cpp */, + 941153B314F9E32900621C3E /* mmio */, + 941153B614F9E32900621C3E /* serialization.cpp */, + ); + path = lcd; + sourceTree = ""; + }; + 941153B314F9E32900621C3E /* mmio */ = { + isa = PBXGroup; + children = ( + 941153B414F9E32900621C3E /* mmio.cpp */, + 941153B514F9E32900621C3E /* mmio.hpp */, + ); + path = mmio; + sourceTree = ""; + }; + 941153B814F9E32900621C3E /* memory */ = { + isa = PBXGroup; + children = ( + 941153B914F9E32900621C3E /* memory.hpp */, + 941153BA14F9E32900621C3E /* memory_gb.cpp */, + ); + path = memory; + sourceTree = ""; + }; + 941153BB14F9E32900621C3E /* scheduler */ = { + isa = PBXGroup; + children = ( + 941153BC14F9E32900621C3E /* scheduler.hpp */, + 941153BD14F9E32900621C3E /* scheduler_gb.cpp */, + ); + path = scheduler; + sourceTree = ""; + }; + 941153BE14F9E32900621C3E /* system */ = { + isa = PBXGroup; + children = ( + 941153BF14F9E32900621C3E /* bootrom-cgb.cpp */, + 941153C014F9E32900621C3E /* bootrom-dmg.cpp */, + 941153C114F9E32900621C3E /* bootrom-sgb.cpp */, + 941153C214F9E32900621C3E /* serialization.cpp */, + 941153C314F9E32900621C3E /* system.hpp */, + 941153C414F9E32900621C3E /* system_gb.cpp */, + ); + path = system; + sourceTree = ""; + }; + 941153C514F9E32900621C3E /* video */ = { + isa = PBXGroup; + children = ( + 941153C614F9E32900621C3E /* video.hpp */, + 941153C714F9E32900621C3E /* video_gb.cpp */, + ); + path = video; + sourceTree = ""; + }; 9490639314CAB244006C5630 /* libco */ = { isa = PBXGroup; children = ( @@ -648,6 +2602,44 @@ buildActionMask = 2147483647; files = ( 94D9257314CA9879008F697D /* BSNESGameCore.mm in Sources */, + 941153F014F9ED5C00621C3E /* core.cpp in Sources */, + 941153F114F9ED5C00621C3E /* smp_core.cpp in Sources */, + 9411524114F9DA8B00621C3E /* cpu.cpp in Sources */, + 9411524814F9DA8B00621C3E /* dsp.cpp in Sources */, + 9411525F14F9DA8B00621C3E /* ppu.cpp in Sources */, + 9411527914F9DA8B00621C3E /* smp.cpp in Sources */, + 9411527B14F9DA8B00621C3E /* cartridge.cpp in Sources */, + 9411527E14F9DA8B00621C3E /* cheat.cpp in Sources */, + 9411527F14F9DA8B00621C3E /* bsx.cpp in Sources */, + 9411528314F9DA8B00621C3E /* hitachidsp.cpp in Sources */, + 9411528814F9DA8B00621C3E /* icd2.cpp in Sources */, + 9411528C14F9DA8B00621C3E /* link.cpp in Sources */, + 9411528D14F9DA8B00621C3E /* msu1.cpp in Sources */, + 9411529014F9DA8B00621C3E /* necdsp.cpp in Sources */, + 9411529214F9DA8B00621C3E /* nss.cpp in Sources */, + 9411529314F9DA8B00621C3E /* obc1.cpp in Sources */, + 9411529914F9DA8B00621C3E /* sa1.cpp in Sources */, + 9411529C14F9DA8B00621C3E /* sdd1.cpp in Sources */, + 941152A014F9DA8B00621C3E /* spc7110.cpp in Sources */, + 941152A214F9DA8B00621C3E /* srtc.cpp in Sources */, + 941152A314F9DA8B00621C3E /* st0018.cpp in Sources */, + 941152A514F9DA8B00621C3E /* sufamiturbo.cpp in Sources */, + 941152AE14F9DA8B00621C3E /* superfx.cpp in Sources */, + 941152B114F9DA8B00621C3E /* controller.cpp in Sources */, + 941152D514F9DA8B00621C3E /* interface.cpp in Sources */, + 941152D614F9DA8B00621C3E /* libsnes.cpp in Sources */, + 941152D814F9DA8B00621C3E /* memory.cpp in Sources */, + 941152FB14F9DA8C00621C3E /* system.cpp in Sources */, + 941153C814F9E32900621C3E /* apu_gb.cpp in Sources */, + 941153CF14F9E32900621C3E /* cartridge_gb.cpp in Sources */, + 941153D914F9E32900621C3E /* cheat_gb.cpp in Sources */, + 941153DC14F9E32900621C3E /* cpu_gb.cpp in Sources */, + 941153E114F9E32900621C3E /* interface_gb.cpp in Sources */, + 941153E414F9E32900621C3E /* lcd_gb.cpp in Sources */, + 941153E814F9E32900621C3E /* memory_gb.cpp in Sources */, + 941153E914F9E32900621C3E /* scheduler_gb.cpp in Sources */, + 941153EE14F9E32900621C3E /* system_gb.cpp in Sources */, + 941153EF14F9E32900621C3E /* video_gb.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -700,6 +2692,7 @@ "\"$(SRCROOT)/../OpenEmu\"", "\"$(PROJECT_DIR)/core/lib/\"", "\"$(PROJECT_DIR)/core/\"", + "\"$(PROJECT_DIR)/libsnes\"", ); INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Bundles"; @@ -709,11 +2702,18 @@ "\"$(SRCROOT)\"", "\"$(SRCROOT)/libsnescore\"", "\"$(SRCROOT)/libsnescore/libs\"", + "\"$(SRCROOT)/libsnes\"", ); MACOSX_DEPLOYMENT_TARGET = 10.6; - OTHER_CFLAGS = "-DPROFILE_PERFORMANCE"; + OTHER_CFLAGS = ( + "-fomit-frame-pointer", + "-I.", + "-Isnes", + "-fPIC", + "-DPROFILE_PERFORMANCE", + ); PRODUCT_NAME = BSNES; - USER_HEADER_SEARCH_PATHS = "\"$(PROJECT_DIR)/core/\""; + USER_HEADER_SEARCH_PATHS = "\"$(PROJECT_DIR)/core/\" \"$(PROJECT_DIR)/libsnes/\""; WRAPPER_EXTENSION = oecoreplugin; }; name = Debug; @@ -730,6 +2730,7 @@ "\"$(SRCROOT)/../OpenEmu\"", "\"$(PROJECT_DIR)/core/lib/\"", "\"$(PROJECT_DIR)/core/\"", + "\"$(PROJECT_DIR)/libsnes\"", ); INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Bundles"; @@ -739,11 +2740,18 @@ "\"$(SRCROOT)\"", "\"$(SRCROOT)/libsnescore\"", "\"$(SRCROOT)/libsnescore/libs\"", + "\"$(SRCROOT)/libsnes\"", ); MACOSX_DEPLOYMENT_TARGET = 10.6; - OTHER_CFLAGS = "-DPROFILE_PERFORMANCE"; + OTHER_CFLAGS = ( + "-fomit-frame-pointer", + "-I.", + "-Isnes", + "-fPIC", + "-DPROFILE_PERFORMANCE", + ); PRODUCT_NAME = BSNES; - USER_HEADER_SEARCH_PATHS = "\"$(PROJECT_DIR)/core/\""; + USER_HEADER_SEARCH_PATHS = "\"$(PROJECT_DIR)/core/\" \"$(PROJECT_DIR)/libsnes/\""; WRAPPER_EXTENSION = oecoreplugin; }; name = Release; @@ -751,8 +2759,9 @@ 1DEB913F08733D840010E9CD /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; + ALWAYS_SEARCH_USER_PATHS = YES; ARCHS = "$(ARCHS_STANDARD_64_BIT)"; + CLANG_CXX_LANGUAGE_STANDARD = "compiler-default"; CLANG_ENABLE_OBJC_EXPERIMENTAL = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; GCC_C_LANGUAGE_STANDARD = c99; @@ -772,8 +2781,9 @@ 1DEB914008733D840010E9CD /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; + ALWAYS_SEARCH_USER_PATHS = YES; ARCHS = "$(ARCHS_STANDARD_64_BIT)"; + CLANG_CXX_LANGUAGE_STANDARD = "compiler-default"; CLANG_ENABLE_OBJC_EXPERIMENTAL = YES; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; diff --git a/libsnes/Makefile b/libsnes/Makefile new file mode 100644 index 0000000..fe88aec --- /dev/null +++ b/libsnes/Makefile @@ -0,0 +1,83 @@ +include nall/Makefile +snes := snes +gameboy := gameboy +profile := performance +link := + +ifneq ($(platform),win) + fpic = -fPIC +endif + +ifeq ($(platform),win) + CC = gcc + CXX = g++ +endif + +ifeq ($(DEBUG),1) + extraflags = -O0 -g -I. -I$(snes) $(fpic) +else + extraflags = -O3 -fomit-frame-pointer -I. -I$(snes) $(fpic) +endif + +ifeq ($(PROFILING),gen) + extraflags += -fprofile-generate --coverage + link += -fprofile-generate --coverage +endif + +ifeq ($(PROFILING),use) + extraflags += -fprofile-use + link += -fprofile-use +endif + +# implicit rules +compile = \ + $(strip \ + $(if $(filter %.c,$<), \ + $(CC) $(CFLAGS) $(extraflags) $1 -c $< -o $@, \ + $(if $(filter %.cpp,$<), \ + $(CXX) $(CXXFLAGS) $(extraflags) $1 -c $< -o $@, \ + $(if $(filter %.s,$<), \ + $(AS) $(ASFLAGS) -o $@ $<, \ + ) \ + ) \ + ) \ + ) + +all: library; + +include gameboy/Makefile + + +set-static: +ifneq ($(platform),win) + $(eval fpic := ) +endif + +static: set-static static-library; + +install: library-install; + +uninstall: library-uninstall; + +%.o: $<; $(call compile) +include $(snes)/Makefile + +clean: + -@$(call delete,obj/*.o) + -@$(call delete,obj/*.a) + -@$(call delete,obj/*.so) + -@$(call delete,obj/*.dylib) + -@$(call delete,obj/*.dll) + -@$(call delete,out/*.a) + -@$(call delete,out/*.so) + -@$(call delete,*.res) + -@$(call delete,*.pgd) + -@$(call delete,*.pgc) + -@$(call delete,*.ilk) + -@$(call delete,*.pdb) + -@$(call delete,*.manifest) + +archive-all: + tar -cjf libsnes.tar.bz2 libco nall obj out snes Makefile cc.bat clean.bat sync.sh + +help:; diff --git a/libsnes/README b/libsnes/README new file mode 100644 index 0000000..d71aee6 --- /dev/null +++ b/libsnes/README @@ -0,0 +1,7 @@ +This repo aims to support building libsnes with C++98 compilers (with some GNU extensions), and is stripped down to only build libsnes. It will also aim to support building for various console targets, such as PlayStation 3. + +libsnes/bsnes author: byuu + +Porters: +Ver Greeneyes +Themaister diff --git a/libsnes/gameboy/Makefile b/libsnes/gameboy/Makefile new file mode 100755 index 0000000..895f622 --- /dev/null +++ b/libsnes/gameboy/Makefile @@ -0,0 +1,16 @@ +gameboy_objects := gameboy-interface gameboy-system gameboy-scheduler +gameboy_objects += gameboy-memory gameboy-cartridge +gameboy_objects += gameboy-cpu gameboy-apu gameboy-lcd +gameboy_objects += gameboy-cheat gameboy-video +objects += $(gameboy_objects) + +obj/gameboy-interface.o: $(gameboy)/interface/interface_gb.cpp $(call rwildcard,$(gameboy)/interface/) +obj/gameboy-system.o: $(gameboy)/system/system_gb.cpp $(call rwildcard,$(gameboy)/system/) +obj/gameboy-scheduler.o: $(gameboy)/scheduler/scheduler_gb.cpp $(call rwildcard,$(gameboy)/scheduler/) +obj/gameboy-cartridge.o: $(gameboy)/cartridge/cartridge_gb.cpp $(call rwildcard,$(gameboy)/cartridge/) +obj/gameboy-memory.o: $(gameboy)/memory/memory_gb.cpp $(call rwildcard,$(gameboy)/memory/) +obj/gameboy-cpu.o: $(gameboy)/cpu/cpu_gb.cpp $(call rwildcard,$(gameboy)/cpu/) +obj/gameboy-apu.o: $(gameboy)/apu/apu_gb.cpp $(call rwildcard,$(gameboy)/apu/) +obj/gameboy-lcd.o: $(gameboy)/lcd/lcd_gb.cpp $(call rwildcard,$(gameboy)/lcd/) +obj/gameboy-cheat.o: $(gameboy)/cheat/cheat_gb.cpp $(call rwildcard,$(gameboy)/cheat/) +obj/gameboy-video.o: $(gameboy)/video/video_gb.cpp $(call rwildcard,$(gameboy)/video/) diff --git a/libsnes/gameboy/apu/apu.hpp b/libsnes/gameboy/apu/apu.hpp new file mode 100755 index 0000000..7218415 --- /dev/null +++ b/libsnes/gameboy/apu/apu.hpp @@ -0,0 +1,28 @@ +struct APU : Processor, MMIO { + #include "square1/square1.hpp" + #include "square2/square2.hpp" + #include "wave/wave.hpp" + #include "noise/noise.hpp" + #include "master/master.hpp" + + uint8 mmio_data[48]; + uint13 sequencer_base; + uint3 sequencer_step; + + Square1 square1; + Square2 square2; + Wave wave; + Noise noise; + Master master; + + static void Main(); + void main(); + void power(); + + uint8 mmio_read(uint16 addr); + void mmio_write(uint16 addr, uint8 data); + + void serialize(serializer&); +}; + +extern APU apu; diff --git a/libsnes/gameboy/apu/apu_gb.cpp b/libsnes/gameboy/apu/apu_gb.cpp new file mode 100644 index 0000000..a8861ad --- /dev/null +++ b/libsnes/gameboy/apu/apu_gb.cpp @@ -0,0 +1,107 @@ +#include + +#define APU_CPP +namespace GameBoy { + +#include "square1/square1.cpp" +#include "square2/square2.cpp" +#include "wave/wave.cpp" +#include "noise/noise.cpp" +#include "master/master.cpp" +#include "serialization.cpp" +APU apu; + +void APU::Main() { + apu.main(); +} + +void APU::main() { + while(true) { + if(scheduler.sync.i == Scheduler::SynchronizeMode::All) { + scheduler.exit(Scheduler::ExitReason::SynchronizeEvent); + } + + if(sequencer_base == 0) { //512hz + if(sequencer_step == 0 || sequencer_step == 2 || sequencer_step == 4 || sequencer_step == 6) { //256hz + square1.clock_length(); + square2.clock_length(); + wave.clock_length(); + noise.clock_length(); + } + if(sequencer_step == 2 || sequencer_step == 6) { //128hz + square1.clock_sweep(); + } + if(sequencer_step == 7) { //64hz + square1.clock_envelope(); + square2.clock_envelope(); + noise.clock_envelope(); + } + sequencer_step++; + } + sequencer_base++; + + square1.run(); + square2.run(); + wave.run(); + noise.run(); + master.run(); + + interface->audioSample(master.center, master.left, master.right); + + clock += 1 * cpu.frequency; + if(clock >= 0) co_switch(scheduler.active_thread = cpu.thread); + } +} + +void APU::power() { + create(Main, 4 * 1024 * 1024); + for(unsigned n = 0xff10; n <= 0xff3f; n++) bus.mmio[n] = this; + + foreach(n, mmio_data) n = 0x00; + sequencer_base = 0; + sequencer_step = 0; + + square1.power(); + square2.power(); + wave.power(); + noise.power(); + master.power(); +} + +uint8 APU::mmio_read(uint16 addr) { + static const uint8 table[48] = { + 0x80, 0x3f, 0x00, 0xff, 0xbf, //square1 + 0xff, 0x3f, 0x00, 0xff, 0xbf, //square2 + 0x7f, 0xff, 0x9f, 0xff, 0xbf, //wave + 0xff, 0xff, 0x00, 0x00, 0xbf, //noise + 0x00, 0x00, 0x70, //master + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, //unmapped + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //wave pattern + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, //wave pattern + }; + + if(addr == 0xff26) { + uint8 data = master.enable << 7; + if(square1.enable) data |= 0x01; + if(square2.enable) data |= 0x02; + if( wave.enable) data |= 0x04; + if( noise.enable) data |= 0x08; + return data | table[addr - 0xff10]; + } + + if(addr >= 0xff10 && addr <= 0xff3f) return mmio_data[addr - 0xff10] | table[addr - 0xff10]; + return 0xff; +} + +void APU::mmio_write(uint16 addr, uint8 data) { + if(addr >= 0xff10 && addr <= 0xff3f) mmio_data[addr - 0xff10] = data; + + if(addr >= 0xff10 && addr <= 0xff14) return square1.write (addr - 0xff10, data); + if(addr >= 0xff15 && addr <= 0xff19) return square2.write (addr - 0xff15, data); + if(addr >= 0xff1a && addr <= 0xff1e) return wave.write (addr - 0xff1a, data); + if(addr >= 0xff1f && addr <= 0xff23) return noise.write (addr - 0xff1f, data); + if(addr >= 0xff24 && addr <= 0xff26) return master.write (addr - 0xff24, data); + if(addr >= 0xff30 && addr <= 0xff3f) return wave.write_pattern(addr - 0xff30, data); +} + +} diff --git a/libsnes/gameboy/apu/master/master.cpp b/libsnes/gameboy/apu/master/master.cpp new file mode 100755 index 0000000..bd0a247 --- /dev/null +++ b/libsnes/gameboy/apu/master/master.cpp @@ -0,0 +1,119 @@ +#ifdef APU_CPP + +void APU::Master::run() { + if(enable == false) { + center = 0; + left = 0; + right = 0; + return; + } + + signed sample = 0; + sample += apu.square1.output; + sample += apu.square2.output; + sample += apu.wave.output; + sample += apu.noise.output; + center = (sample * 512) - 16384; + + sample = 0; + if(channel1_left_enable) sample += apu.square1.output; + if(channel2_left_enable) sample += apu.square2.output; + if(channel3_left_enable) sample += apu.wave.output; + if(channel4_left_enable) sample += apu.noise.output; + left = (sample * 512) - 16384; + + switch(left_volume) { + case 0: left >>= 3; break; // 12.5% + case 1: left >>= 2; break; // 25.0% + case 2: left = (left >> 2) + (left >> 3); break; // 37.5% + case 3: left >>= 1; break; // 50.0% + case 4: left = (left >> 1) + (left >> 3); break; // 62.5% + case 5: left -= (left >> 2); break; // 75.0% + case 6: left -= (left >> 3); break; // 87.5% + //case 7: break; //100.0% + } + + sample = 0; + if(channel1_right_enable) sample += apu.square1.output; + if(channel2_right_enable) sample += apu.square2.output; + if(channel3_right_enable) sample += apu.wave.output; + if(channel4_right_enable) sample += apu.noise.output; + right = (sample * 512) - 16384; + + switch(right_volume) { + case 0: right >>= 3; break; // 12.5% + case 1: right >>= 2; break; // 25.0% + case 2: right = (right >> 2) + (right >> 3); break; // 37.5% + case 3: right >>= 1; break; // 50.0% + case 4: right = (right >> 1) + (right >> 3); break; // 62.5% + case 5: right -= (right >> 2); break; // 75.0% + case 6: right -= (right >> 3); break; // 87.5% + //case 7: break; //100.0% + } +} + +void APU::Master::write(unsigned r, uint8 data) { + if(r == 0) { //$ff24 NR50 + left_in_enable = data & 0x80; + left_volume = (data >> 4) & 7; + right_in_enable = data & 0x08; + right_volume = (data >> 0) & 7; + } + + if(r == 1) { //$ff25 NR51 + channel4_left_enable = data & 0x80; + channel3_left_enable = data & 0x40; + channel2_left_enable = data & 0x20; + channel1_left_enable = data & 0x10; + channel4_right_enable = data & 0x08; + channel3_right_enable = data & 0x04; + channel2_right_enable = data & 0x02; + channel1_right_enable = data & 0x01; + } + + if(r == 2) { //$ff26 NR52 + enable = data & 0x80; + } +} + +void APU::Master::power() { + left_in_enable = 0; + left_volume = 0; + right_in_enable = 0; + right_volume = 0; + channel4_left_enable = 0; + channel3_left_enable = 0; + channel2_left_enable = 0; + channel1_left_enable = 0; + channel4_right_enable = 0; + channel3_right_enable = 0; + channel2_right_enable = 0; + channel1_right_enable = 0; + enable = 0; + + center = 0; + left = 0; + right = 0; +} + +void APU::Master::serialize(serializer &s) { + s.integer(left_in_enable); + s.integer(left_volume); + s.integer(right_in_enable); + s.integer(right_volume); + s.integer(channel4_left_enable); + s.integer(channel3_left_enable); + s.integer(channel2_left_enable); + s.integer(channel1_left_enable); + s.integer(channel4_right_enable); + s.integer(channel3_right_enable); + s.integer(channel2_right_enable); + s.integer(channel1_right_enable); + s.integer(enable); + + s.integer(center); + s.integer(left); + s.integer(right); +} + +#endif diff --git a/libsnes/gameboy/apu/master/master.hpp b/libsnes/gameboy/apu/master/master.hpp new file mode 100755 index 0000000..31a1e9f --- /dev/null +++ b/libsnes/gameboy/apu/master/master.hpp @@ -0,0 +1,24 @@ +struct Master { + bool left_in_enable; + uint3 left_volume; + bool right_in_enable; + uint3 right_volume; + bool channel4_left_enable; + bool channel3_left_enable; + bool channel2_left_enable; + bool channel1_left_enable; + bool channel4_right_enable; + bool channel3_right_enable; + bool channel2_right_enable; + bool channel1_right_enable; + bool enable; + + int16 center; + int16 left; + int16 right; + + void run(); + void write(unsigned r, uint8 data); + void power(); + void serialize(serializer&); +}; diff --git a/libsnes/gameboy/apu/noise/noise.cpp b/libsnes/gameboy/apu/noise/noise.cpp new file mode 100755 index 0000000..c0def11 --- /dev/null +++ b/libsnes/gameboy/apu/noise/noise.cpp @@ -0,0 +1,108 @@ +#ifdef APU_CPP + +bool APU::Noise::dac_enable() { + return (envelope_volume || envelope_direction); +} + +void APU::Noise::run() { + if(period && --period == 0) { + period = divisor << frequency; + if(frequency < 14) { + bool bit = (lfsr ^ (lfsr >> 1)) & 1; + lfsr = (lfsr >> 1) ^ (bit << (narrow_lfsr ? 6 : 14)); + } + } + + uint4 sample = (lfsr & 1) ? (uint4)0 : volume; + if(enable == false) sample = 0; + + output = sample; +} + +void APU::Noise::clock_length() { + if(counter && length) { + if(--length == 0) enable = false; + } +} + +void APU::Noise::clock_envelope() { + if(enable && envelope_frequency && --envelope_period == 0) { + envelope_period = envelope_frequency; + if(envelope_direction == 0 && volume > 0) volume--; + if(envelope_direction == 1 && volume < 15) volume++; + } +} + +void APU::Noise::write(unsigned r, uint8 data) { + if(r == 1) { //$ff20 NR41 + length = 64 - (data & 0x3f); + } + + if(r == 2) { //$ff21 NR42 + envelope_volume = data >> 4; + envelope_direction = data & 0x08; + envelope_frequency = data & 0x07; + if(dac_enable() == false) enable = false; + } + + if(r == 3) { //$ff22 NR43 + frequency = data >> 4; + narrow_lfsr = data & 0x08; + divisor = (data & 0x07) << 4; + if(divisor == 0) divisor = 8; + period = divisor << frequency; + } + + if(r == 4) { //$ff34 NR44 + bool initialize = data & 0x80; + counter = data & 0x40; + + if(initialize) { + enable = dac_enable(); + lfsr = ~0U; + envelope_period = envelope_frequency; + volume = envelope_volume; + if(length == 0) length = 64; + } + } +} + +void APU::Noise::power() { + enable = 0; + + envelope_volume = 0; + envelope_direction = 0; + envelope_frequency = 0; + frequency = 0; + narrow_lfsr = 0; + divisor = 0; + counter = 0; + + output = 0; + length = 0; + envelope_period = 0; + volume = 0; + period = 0; + lfsr = 0; +} + +void APU::Noise::serialize(serializer &s) { + s.integer(enable); + + s.integer(envelope_volume); + s.integer(envelope_direction); + s.integer(envelope_frequency); + s.integer(frequency); + s.integer(narrow_lfsr); + s.integer(divisor); + s.integer(counter); + + s.integer(output); + s.integer(length); + s.integer(envelope_period); + s.integer(volume); + s.integer(period); + s.integer(lfsr); +} + +#endif diff --git a/libsnes/gameboy/apu/noise/noise.hpp b/libsnes/gameboy/apu/noise/noise.hpp new file mode 100755 index 0000000..f92efbf --- /dev/null +++ b/libsnes/gameboy/apu/noise/noise.hpp @@ -0,0 +1,27 @@ +struct Noise { + bool enable; + + uint4 envelope_volume; + bool envelope_direction; + uint3 envelope_frequency; + uint4 frequency; + bool narrow_lfsr; + unsigned divisor; + bool counter; + + int16 output; + unsigned length; + uint3 envelope_period; + uint4 volume; + unsigned period; + uint15 lfsr; + + bool dac_enable(); + + void run(); + void clock_length(); + void clock_envelope(); + void write(unsigned r, uint8 data); + void power(); + void serialize(serializer&); +}; diff --git a/libsnes/gameboy/apu/serialization.cpp b/libsnes/gameboy/apu/serialization.cpp new file mode 100755 index 0000000..3468634 --- /dev/null +++ b/libsnes/gameboy/apu/serialization.cpp @@ -0,0 +1,17 @@ +#ifdef APU_CPP + +void APU::serialize(serializer &s) { + Processor::serialize(s); + + s.array(mmio_data); + s.integer(sequencer_base); + s.integer(sequencer_step); + + square1.serialize(s); + square2.serialize(s); + wave.serialize(s); + noise.serialize(s); + master.serialize(s); +} + +#endif diff --git a/libsnes/gameboy/apu/square1/square1.cpp b/libsnes/gameboy/apu/square1/square1.cpp new file mode 100755 index 0000000..0a9e013 --- /dev/null +++ b/libsnes/gameboy/apu/square1/square1.cpp @@ -0,0 +1,160 @@ +#ifdef APU_CPP + +bool APU::Square1::dac_enable() { + return (envelope_volume || envelope_direction); +} + +void APU::Square1::run() { + if(period && --period == 0) { + period = 4 * (2048 - frequency); + phase++; + switch(duty) { + case 0: duty_output = (phase == 6); break; //______-_ + case 1: duty_output = (phase >= 6); break; //______-- + case 2: duty_output = (phase >= 4); break; //____---- + case 3: duty_output = (phase <= 5); break; //------__ + } + } + + uint4 sample = (duty_output ? volume : (uint4)0); + if(enable == false) sample = 0; + + output = sample; +} + +void APU::Square1::sweep(bool update) { + if(sweep_enable == false) return; + + sweep_negate = sweep_direction; + unsigned delta = frequency_shadow >> sweep_shift; + signed freq = frequency_shadow + (sweep_negate ? -delta : delta); + + if(freq > 2047) { + enable = false; + } else if(sweep_shift && update) { + frequency_shadow = freq; + frequency = freq & 2047; + period = 4 * (2048 - frequency); + } +} + +void APU::Square1::clock_length() { + if(counter && length) { + if(--length == 0) enable = false; + } +} + +void APU::Square1::clock_sweep() { + if(enable && sweep_frequency && --sweep_period == 0) { + sweep_period = sweep_frequency; + sweep(1); + sweep(0); + } +} + +void APU::Square1::clock_envelope() { + if(enable && envelope_frequency && --envelope_period == 0) { + envelope_period = envelope_frequency; + if(envelope_direction == 0 && volume > 0) volume--; + if(envelope_direction == 1 && volume < 15) volume++; + } +} + +void APU::Square1::write(unsigned r, uint8 data) { + if(r == 0) { //$ff10 NR10 + if(sweep_negate && sweep_direction && !(data & 0x08)) enable = false; + sweep_frequency = (data >> 4) & 7; + sweep_direction = data & 0x08; + sweep_shift = data & 0x07; + } + + if(r == 1) { //$ff11 NR11 + duty = data >> 6; + length = 64 - (data & 0x3f); + } + + if(r == 2) { //$ff12 NR12 + envelope_volume = data >> 4; + envelope_direction = data & 0x08; + envelope_frequency = data & 0x07; + if(dac_enable() == false) enable = false; + } + + if(r == 3) { //$ff13 NR13 + frequency = (frequency & 0x0700) | data; + } + + if(r == 4) { //$ff14 NR14 + bool initialize = data & 0x80; + counter = data & 0x40; + frequency = ((data & 7) << 8) | (frequency & 0x00ff); + + if(initialize) { + enable = dac_enable(); + envelope_period = envelope_frequency; + volume = envelope_volume; + frequency_shadow = frequency; + sweep_period = sweep_frequency; + sweep_enable = sweep_period || sweep_shift; + sweep_negate = false; + if(sweep_shift) sweep(0); + if(length == 0) length = 64; + } + } + + period = 4 * (2048 - frequency); +} + +void APU::Square1::power() { + enable = 0; + + sweep_frequency = 0; + sweep_direction = 0; + sweep_shift = 0; + sweep_negate = 0; + duty = 0; + length = 0; + envelope_volume = 0; + envelope_direction = 0; + envelope_frequency = 0; + frequency = 0; + counter = 0; + + output = 0; + duty_output = 0; + phase = 0; + period = 0; + envelope_period = 0; + sweep_period = 0; + frequency_shadow = 0; + sweep_enable = 0; + volume = 0; +} + +void APU::Square1::serialize(serializer &s) { + s.integer(enable); + + s.integer(sweep_frequency); + s.integer(sweep_direction); + s.integer(sweep_shift); + s.integer(sweep_negate); + s.integer(duty); + s.integer(length); + s.integer(envelope_volume); + s.integer(envelope_direction); + s.integer(envelope_frequency); + s.integer(frequency); + s.integer(counter); + + s.integer(output); + s.integer(duty_output); + s.integer(phase); + s.integer(period); + s.integer(envelope_period); + s.integer(sweep_period); + s.integer(frequency_shadow); + s.integer(sweep_enable); + s.integer(volume); +} + +#endif diff --git a/libsnes/gameboy/apu/square1/square1.hpp b/libsnes/gameboy/apu/square1/square1.hpp new file mode 100755 index 0000000..2b45676 --- /dev/null +++ b/libsnes/gameboy/apu/square1/square1.hpp @@ -0,0 +1,36 @@ +struct Square1 { + bool enable; + + uint3 sweep_frequency; + bool sweep_direction; + uint3 sweep_shift; + bool sweep_negate; + uint2 duty; + unsigned length; + uint4 envelope_volume; + bool envelope_direction; + uint3 envelope_frequency; + uint11 frequency; + bool counter; + + int16 output; + bool duty_output; + uint3 phase; + unsigned period; + uint3 envelope_period; + uint3 sweep_period; + signed frequency_shadow; + bool sweep_enable; + uint4 volume; + + bool dac_enable(); + + void run(); + void sweep(bool update); + void clock_length(); + void clock_sweep(); + void clock_envelope(); + void write(unsigned r, uint8 data); + void power(); + void serialize(serializer&); +}; diff --git a/libsnes/gameboy/apu/square2/square2.cpp b/libsnes/gameboy/apu/square2/square2.cpp new file mode 100755 index 0000000..09e5067 --- /dev/null +++ b/libsnes/gameboy/apu/square2/square2.cpp @@ -0,0 +1,110 @@ +#ifdef APU_CPP + +bool APU::Square2::dac_enable() { + return (envelope_volume || envelope_direction); +} + +void APU::Square2::run() { + if(period && --period == 0) { + period = 4 * (2048 - frequency); + phase++; + switch(duty) { + case 0: duty_output = (phase == 6); break; //______-_ + case 1: duty_output = (phase >= 6); break; //______-- + case 2: duty_output = (phase >= 4); break; //____---- + case 3: duty_output = (phase <= 5); break; //------__ + } + } + + uint4 sample = (duty_output ? volume : (uint4)0); + if(enable == false) sample = 0; + + output = sample; +} + +void APU::Square2::clock_length() { + if(counter && length) { + if(--length == 0) enable = false; + } +} + +void APU::Square2::clock_envelope() { + if(enable && envelope_frequency && --envelope_period == 0) { + envelope_period = envelope_frequency; + if(envelope_direction == 0 && volume > 0) volume--; + if(envelope_direction == 1 && volume < 15) volume++; + } +} + +void APU::Square2::write(unsigned r, uint8 data) { + if(r == 1) { //$ff16 NR21 + duty = data >> 6; + length = 64 - (data & 0x3f); + } + + if(r == 2) { //$ff17 NR22 + envelope_volume = data >> 4; + envelope_direction = data & 0x08; + envelope_frequency = data & 0x07; + if(dac_enable() == false) enable = false; + } + + if(r == 3) { //$ff18 NR23 + frequency = (frequency & 0x0700) | data; + } + + if(r == 4) { //$ff19 NR24 + bool initialize = data & 0x80; + counter = data & 0x40; + frequency = ((data & 7) << 8) | (frequency & 0x00ff); + + if(initialize) { + enable = dac_enable(); + envelope_period = envelope_frequency; + volume = envelope_volume; + if(length == 0) length = 64; + } + } + + period = 4 * (2048 - frequency); +} + +void APU::Square2::power() { + enable = 0; + + duty = 0; + length = 0; + envelope_volume = 0; + envelope_direction = 0; + envelope_frequency = 0; + frequency = 0; + counter = 0; + + output = 0; + duty_output = 0; + phase = 0; + period = 0; + envelope_period = 0; + volume = 0; +} + +void APU::Square2::serialize(serializer &s) { + s.integer(enable); + + s.integer(duty); + s.integer(length); + s.integer(envelope_volume); + s.integer(envelope_direction); + s.integer(envelope_frequency); + s.integer(frequency); + s.integer(counter); + + s.integer(output); + s.integer(duty_output); + s.integer(phase); + s.integer(period); + s.integer(envelope_period); + s.integer(volume); +} + +#endif diff --git a/libsnes/gameboy/apu/square2/square2.hpp b/libsnes/gameboy/apu/square2/square2.hpp new file mode 100755 index 0000000..0a47334 --- /dev/null +++ b/libsnes/gameboy/apu/square2/square2.hpp @@ -0,0 +1,27 @@ +struct Square2 { + bool enable; + + uint2 duty; + unsigned length; + uint4 envelope_volume; + bool envelope_direction; + uint3 envelope_frequency; + uint11 frequency; + bool counter; + + int16 output; + bool duty_output; + uint3 phase; + unsigned period; + uint3 envelope_period; + uint4 volume; + + bool dac_enable(); + + void run(); + void clock_length(); + void clock_envelope(); + void write(unsigned r, uint8 data); + void power(); + void serialize(serializer&); +}; diff --git a/libsnes/gameboy/apu/wave/wave.cpp b/libsnes/gameboy/apu/wave/wave.cpp new file mode 100755 index 0000000..3db04ee --- /dev/null +++ b/libsnes/gameboy/apu/wave/wave.cpp @@ -0,0 +1,99 @@ +#ifdef APU_CPP + +void APU::Wave::run() { + if(period && --period == 0) { + period = 2 * (2048 - frequency); + pattern_sample = pattern[++pattern_offset]; + } + + uint4 sample = pattern_sample >> volume_shift; + if(enable == false) sample = 0; + + output = sample; +} + +void APU::Wave::clock_length() { + if(counter && length) { + if(--length == 0) enable = false; + } +} + +void APU::Wave::write(unsigned r, uint8 data) { + if(r == 0) { //$ff1a NR30 + dac_enable = data & 0x80; + if(dac_enable == false) enable = false; + } + + if(r == 1) { //$ff1b NR31 + length = 256 - data; + } + + if(r == 2) { //$ff1c NR32 + switch((data >> 5) & 3) { + case 0: volume_shift = 4; break; // 0% + case 1: volume_shift = 0; break; //100% + case 2: volume_shift = 1; break; // 50% + case 3: volume_shift = 2; break; // 25% + } + } + + if(r == 3) { //$ff1d NR33 + frequency = (frequency & 0x0700) | data; + } + + if(r == 4) { //$ff1e NR34 + bool initialize = data & 0x80; + counter = data & 0x40; + frequency = ((data & 7) << 8) | (frequency & 0x00ff); + + if(initialize) { + enable = dac_enable; + pattern_offset = 0; + if(length == 0) length = 256; + } + } + + period = 2 * (2048 - frequency); +} + +void APU::Wave::write_pattern(unsigned p, uint8 data) { + p <<= 1; + pattern[p + 0] = (data >> 4) & 15; + pattern[p + 1] = (data >> 0) & 15; +} + +void APU::Wave::power() { + enable = 0; + + dac_enable = 0; + volume_shift = 0; + frequency = 0; + counter = 0; + + random_lfsr r; + foreach(n, pattern) n = r() & 15; + + output = 0; + length = 0; + period = 0; + pattern_offset = 0; + pattern_sample = 0; +} + +void APU::Wave::serialize(serializer &s) { + s.integer(enable); + + s.integer(dac_enable); + s.integer(volume_shift); + s.integer(frequency); + s.integer(counter); + s.array(pattern); + + s.integer(output); + s.integer(length); + s.integer(period); + s.integer(pattern_offset); + s.integer(pattern_sample); +} + +#endif diff --git a/libsnes/gameboy/apu/wave/wave.hpp b/libsnes/gameboy/apu/wave/wave.hpp new file mode 100755 index 0000000..7f8d65d --- /dev/null +++ b/libsnes/gameboy/apu/wave/wave.hpp @@ -0,0 +1,22 @@ +struct Wave { + bool enable; + + bool dac_enable; + unsigned volume_shift; + uint11 frequency; + bool counter; + uint8 pattern[32]; + + int16 output; + unsigned length; + unsigned period; + uint5 pattern_offset; + uint4 pattern_sample; + + void run(); + void clock_length(); + void write(unsigned r, uint8 data); + void write_pattern(unsigned p, uint8 data); + void power(); + void serialize(serializer&); +}; diff --git a/libsnes/gameboy/cartridge/cartridge.hpp b/libsnes/gameboy/cartridge/cartridge.hpp new file mode 100755 index 0000000..7b4b6ae --- /dev/null +++ b/libsnes/gameboy/cartridge/cartridge.hpp @@ -0,0 +1,68 @@ +struct Cartridge : MMIO, property { + #include "mbc0/mbc0.hpp" + #include "mbc1/mbc1.hpp" + #include "mbc2/mbc2.hpp" + #include "mbc3/mbc3.hpp" + #include "mbc5/mbc5.hpp" + #include "mmm01/mmm01.hpp" + #include "huc1/huc1.hpp" + #include "huc3/huc3.hpp" + + struct Mapper { + enum e { + MBC0, + MBC1, + MBC2, + MBC3, + MBC5, + MMM01, + HuC1, + HuC3, + Unknown, + } i; + }; + + struct Information { + string xml; + + Mapper mapper; + bool ram; + bool battery; + bool rtc; + bool rumble; + + unsigned romsize; + unsigned ramsize; + } info; + + readonly loaded; + readonly sha256; + + uint8_t *romdata; + unsigned romsize; + + uint8_t *ramdata; + unsigned ramsize; + + MMIO *mapper; + bool bootrom_enable; + + void load(System::Revision::e revision, const string &markup, const uint8_t *data, unsigned size); + void unload(); + + uint8 rom_read(unsigned addr); + void rom_write(unsigned addr, uint8 data); + uint8 ram_read(unsigned addr); + void ram_write(unsigned addr, uint8 data); + + uint8 mmio_read(uint16 addr); + void mmio_write(uint16 addr, uint8 data); + + void power(); + + void serialize(serializer&); + Cartridge(); + ~Cartridge(); +}; + +extern Cartridge cartridge; diff --git a/libsnes/gameboy/cartridge/cartridge_gb.cpp b/libsnes/gameboy/cartridge/cartridge_gb.cpp new file mode 100644 index 0000000..d6d6d9c --- /dev/null +++ b/libsnes/gameboy/cartridge/cartridge_gb.cpp @@ -0,0 +1,153 @@ +#include + +#include + +#define CARTRIDGE_CPP +namespace GameBoy { + +#include "mbc0/mbc0.cpp" +#include "mbc1/mbc1.cpp" +#include "mbc2/mbc2.cpp" +#include "mbc3/mbc3.cpp" +#include "mbc5/mbc5.cpp" +#include "mmm01/mmm01.cpp" +#include "huc1/huc1.cpp" +#include "huc3/huc3.cpp" +#include "serialization.cpp" +Cartridge cartridge; + +void Cartridge::load(System::Revision::e revision, const string &markup, const uint8_t *data, unsigned size) { + if(size == 0) size = 32768; + romdata = allocate(romsize = size, 0xff); + if(data) memcpy(romdata, data, size); + + info.mapper.i = Mapper::Unknown; + info.ram = false; + info.battery = false; + info.rtc = false; + info.rumble = false; + + info.romsize = 0; + info.ramsize = 0; + + XML::Document document(markup); + + const string &mapperid = document["cartridge"]["mapper"].data; + if(mapperid == "none" ) info.mapper.i = Mapper::MBC0; + if(mapperid == "MBC1" ) info.mapper.i = Mapper::MBC1; + if(mapperid == "MBC2" ) info.mapper.i = Mapper::MBC2; + if(mapperid == "MBC3" ) info.mapper.i = Mapper::MBC3; + if(mapperid == "MBC5" ) info.mapper.i = Mapper::MBC5; + if(mapperid == "MMM01") info.mapper.i = Mapper::MMM01; + if(mapperid == "HuC1" ) info.mapper.i = Mapper::HuC1; + if(mapperid == "HuC3" ) info.mapper.i = Mapper::HuC3; + + info.rtc = document["cartridge"]["rtc"].data == "true"; + info.rumble = document["cartridge"]["rumble"].data == "true"; + + info.romsize = hex(document["cartridge"]["rom"]["size"].data); + info.ramsize = hex(document["cartridge"]["ram"]["size"].data); + info.battery = document["cartridge"]["ram"]["battery"].data == "true"; + + switch(info.mapper.i) { default: + case Mapper::MBC0: mapper = &mbc0; break; + case Mapper::MBC1: mapper = &mbc1; break; + case Mapper::MBC2: mapper = &mbc2; break; + case Mapper::MBC3: mapper = &mbc3; break; + case Mapper::MBC5: mapper = &mbc5; break; + case Mapper::MMM01: mapper = &mmm01; break; + case Mapper::HuC1: mapper = &huc1; break; + case Mapper::HuC3: mapper = &huc3; break; + } + + ramdata = new uint8_t[ramsize = info.ramsize](); + system.load(revision); + + loaded = true; + sha256 = nall::sha256(romdata, romsize); +} + +void Cartridge::unload() { + if(loaded == false) return; + + if(romdata) { delete[] romdata; romdata = 0; } + if(ramdata) { delete[] ramdata; ramdata = 0; } + loaded = false; +} + +uint8 Cartridge::rom_read(unsigned addr) { + if(addr >= romsize) addr %= romsize; + return romdata[addr]; +} + +void Cartridge::rom_write(unsigned addr, uint8 data) { + if(addr >= romsize) addr %= romsize; + romdata[addr] = data; +} + +uint8 Cartridge::ram_read(unsigned addr) { + if(ramsize == 0) return 0x00; + if(addr >= ramsize) addr %= ramsize; + return ramdata[addr]; +} + +void Cartridge::ram_write(unsigned addr, uint8 data) { + if(ramsize == 0) return; + if(addr >= ramsize) addr %= ramsize; + ramdata[addr] = data; +} + +uint8 Cartridge::mmio_read(uint16 addr) { + if(addr == 0xff50) return 0x00; + + if(bootrom_enable) { + const uint8 *data = 0; + switch(system.revision.i) { default: + case System::Revision::GameBoy: data = System::BootROM::dmg; break; + case System::Revision::SuperGameBoy: data = System::BootROM::sgb; break; + case System::Revision::GameBoyColor: data = System::BootROM::cgb; break; + } + if(addr <= 0x00ff) return data[addr]; + if(addr >= 0x0200 && addr <= 0x08ff && system.cgb()) return data[addr - 256]; + } + + return mapper->mmio_read(addr); +} + +void Cartridge::mmio_write(uint16 addr, uint8 data) { + if(bootrom_enable && addr == 0xff50) { + bootrom_enable = false; + return; + } + + mapper->mmio_write(addr, data); +} + +void Cartridge::power() { + bootrom_enable = true; + + mbc0.power(); + mbc1.power(); + mbc2.power(); + mbc3.power(); + mbc5.power(); + mmm01.power(); + huc1.power(); + huc3.power(); + + for(unsigned n = 0x0000; n <= 0x7fff; n++) bus.mmio[n] = this; + for(unsigned n = 0xa000; n <= 0xbfff; n++) bus.mmio[n] = this; + bus.mmio[0xff50] = this; +} + +Cartridge::Cartridge() { + loaded = false; + romdata = 0; + ramdata = 0; +} + +Cartridge::~Cartridge() { + unload(); +} + +} diff --git a/libsnes/gameboy/cartridge/huc1/huc1.cpp b/libsnes/gameboy/cartridge/huc1/huc1.cpp new file mode 100755 index 0000000..b8fb110 --- /dev/null +++ b/libsnes/gameboy/cartridge/huc1/huc1.cpp @@ -0,0 +1,54 @@ +#ifdef CARTRIDGE_CPP + +uint8 Cartridge::HuC1::mmio_read(uint16 addr) { + if((addr & 0xc000) == 0x0000) { //$0000-3fff + return cartridge.rom_read(addr); + } + + if((addr & 0xc000) == 0x4000) { //$4000-7fff + return cartridge.rom_read((rom_select << 14) | (addr & 0x3fff)); + } + + if((addr & 0xe000) == 0xa000) { //$a000-bfff + return cartridge.ram_read((ram_select << 13) | (addr & 0x1fff)); + } + + return 0x00; +} + +void Cartridge::HuC1::mmio_write(uint16 addr, uint8 data) { + if((addr & 0xe000) == 0x0000) { //$0000-1fff + ram_writable = (data & 0x0f) == 0x0a; + return; + } + + if((addr & 0xe000) == 0x2000) { //$2000-3fff + rom_select = data; + if(rom_select == 0) rom_select = 1; + return; + } + + if((addr & 0xe000) == 0x4000) { //$4000-5fff + ram_select = data; + return; + } + + if((addr & 0xe000) == 0x6000) { //$6000-7fff + model = data & 0x01; + return; + } + + if((addr & 0xe000) == 0xa000) { //$a000-bfff + if(ram_writable == false) return; + return cartridge.ram_write((ram_select << 13) | (addr & 0x1fff), data); + } +} + +void Cartridge::HuC1::power() { + ram_writable = false; + rom_select = 0x01; + ram_select = 0x00; + model = 0; +} + +#endif diff --git a/libsnes/gameboy/cartridge/huc1/huc1.hpp b/libsnes/gameboy/cartridge/huc1/huc1.hpp new file mode 100755 index 0000000..f0f60d8 --- /dev/null +++ b/libsnes/gameboy/cartridge/huc1/huc1.hpp @@ -0,0 +1,10 @@ +struct HuC1 : MMIO { + bool ram_writable; //$0000-1fff + uint8 rom_select; //$2000-3fff + uint8 ram_select; //$4000-5fff + bool model; //$6000-7fff + + uint8 mmio_read(uint16 addr); + void mmio_write(uint16 addr, uint8 data); + void power(); +} huc1; diff --git a/libsnes/gameboy/cartridge/huc3/huc3.cpp b/libsnes/gameboy/cartridge/huc3/huc3.cpp new file mode 100755 index 0000000..c2bdcda --- /dev/null +++ b/libsnes/gameboy/cartridge/huc3/huc3.cpp @@ -0,0 +1,53 @@ +#ifdef CARTRIDGE_CPP + +uint8 Cartridge::HuC3::mmio_read(uint16 addr) { + if((addr & 0xc000) == 0x0000) { //$0000-3fff + return cartridge.rom_read(addr); + } + + if((addr & 0xc000) == 0x4000) { //$4000-7fff + return cartridge.rom_read((rom_select << 14) | (addr & 0x3fff)); + } + + if((addr & 0xe000) == 0xa000) { //$a000-bfff + if(ram_enable) return cartridge.ram_read((ram_select << 13) | (addr & 0x1fff)); + return 0x00; + } + + return 0x00; +} + +void Cartridge::HuC3::mmio_write(uint16 addr, uint8 data) { + if((addr & 0xe000) == 0x0000) { //$0000-1fff + ram_enable = (data & 0x0f) == 0x0a; + return; + } + + if((addr & 0xe000) == 0x2000) { //$2000-3fff + rom_select = data; + return; + } + + if((addr & 0xe000) == 0x4000) { //$4000-5fff + ram_select = data; + return; + } + + if((addr & 0xe000) == 0x6000) { //$6000-7fff + //unknown purpose + return; + } + + if((addr & 0xe000) == 0xa000) { //$a000-bfff + if(ram_enable) cartridge.ram_write((ram_select << 13) | (addr & 0x1fff), data); + return; + } +} + +void Cartridge::HuC3::power() { + ram_enable = false; + rom_select = 0x01; + ram_select = 0x00; +} + +#endif diff --git a/libsnes/gameboy/cartridge/huc3/huc3.hpp b/libsnes/gameboy/cartridge/huc3/huc3.hpp new file mode 100755 index 0000000..61d4aa6 --- /dev/null +++ b/libsnes/gameboy/cartridge/huc3/huc3.hpp @@ -0,0 +1,9 @@ +struct HuC3 : MMIO { + bool ram_enable; //$0000-1fff + uint8 rom_select; //$2000-3fff + uint8 ram_select; //$4000-5fff + + uint8 mmio_read(uint16 addr); + void mmio_write(uint16 addr, uint8 data); + void power(); +} huc3; diff --git a/libsnes/gameboy/cartridge/mbc0/mbc0.cpp b/libsnes/gameboy/cartridge/mbc0/mbc0.cpp new file mode 100755 index 0000000..2312f57 --- /dev/null +++ b/libsnes/gameboy/cartridge/mbc0/mbc0.cpp @@ -0,0 +1,25 @@ +#ifdef CARTRIDGE_CPP + +uint8 Cartridge::MBC0::mmio_read(uint16 addr) { + if((addr & 0x8000) == 0x0000) { //$0000-7fff + return cartridge.rom_read(addr); + } + + if((addr & 0xe000) == 0xa000) { //$a000-bfff + return cartridge.ram_read(addr & 0x1fff); + } + + return 0x00; +} + +void Cartridge::MBC0::mmio_write(uint16 addr, uint8 data) { + if((addr & 0xe000) == 0xa000) { //$a000-bfff + cartridge.ram_write(addr & 0x1fff, data); + return; + } +} + +void Cartridge::MBC0::power() { +} + +#endif diff --git a/libsnes/gameboy/cartridge/mbc0/mbc0.hpp b/libsnes/gameboy/cartridge/mbc0/mbc0.hpp new file mode 100755 index 0000000..da7390e --- /dev/null +++ b/libsnes/gameboy/cartridge/mbc0/mbc0.hpp @@ -0,0 +1,5 @@ +struct MBC0 : MMIO { + uint8 mmio_read(uint16 addr); + void mmio_write(uint16 addr, uint8 data); + void power(); +} mbc0; diff --git a/libsnes/gameboy/cartridge/mbc1/mbc1.cpp b/libsnes/gameboy/cartridge/mbc1/mbc1.cpp new file mode 100755 index 0000000..7e15c13 --- /dev/null +++ b/libsnes/gameboy/cartridge/mbc1/mbc1.cpp @@ -0,0 +1,70 @@ +#ifdef CARTRIDGE_CPP + +uint8 Cartridge::MBC1::mmio_read(uint16 addr) { + if((addr & 0xc000) == 0x0000) { //$0000-3fff + return cartridge.rom_read(addr); + } + + if((addr & 0xc000) == 0x4000) { //$4000-7fff + if(mode_select == 0) { + return cartridge.rom_read((ram_select << 19) | (rom_select << 14) | (addr & 0x3fff)); + } else { + return cartridge.rom_read((rom_select << 14) | (addr & 0x3fff)); + } + } + + if((addr & 0xe000) == 0xa000) { //$a000-bfff + if(ram_enable) { + if(mode_select == 0) { + return cartridge.ram_read(addr & 0x1fff); + } else { + return cartridge.ram_read((ram_select << 13) | (addr & 0x1fff)); + } + } + return 0x00; + } + + return 0x00; +} + +void Cartridge::MBC1::mmio_write(uint16 addr, uint8 data) { + if((addr & 0xe000) == 0x0000) { //$0000-1fff + ram_enable = (data & 0x0f) == 0x0a; + return; + } + + if((addr & 0xe000) == 0x2000) { //$2000-3fff + rom_select = (data & 0x1f) + ((data & 0x1f) == 0); + return; + } + + if((addr & 0xe000) == 0x4000) { //$4000-5fff + ram_select = data & 0x03; + return; + } + + if((addr & 0xe000) == 0x6000) { //$6000-7fff + mode_select = data & 0x01; + return; + } + + if((addr & 0xe000) == 0xa000) { //$a000-bfff + if(ram_enable) { + if(mode_select == 0) { + cartridge.ram_write(addr & 0x1fff, data); + } else { + cartridge.ram_write((ram_select << 13) | (addr & 0x1fff), data); + } + } + return; + } +} + +void Cartridge::MBC1::power() { + ram_enable = false; + rom_select = 0x01; + ram_select = 0x00; + mode_select = 0; +} + +#endif diff --git a/libsnes/gameboy/cartridge/mbc1/mbc1.hpp b/libsnes/gameboy/cartridge/mbc1/mbc1.hpp new file mode 100755 index 0000000..19f8b75 --- /dev/null +++ b/libsnes/gameboy/cartridge/mbc1/mbc1.hpp @@ -0,0 +1,10 @@ +struct MBC1 : MMIO { + bool ram_enable; //$0000-1fff + uint8 rom_select; //$2000-3fff + uint8 ram_select; //$4000-5fff + bool mode_select; //$6000-7fff + + uint8 mmio_read(uint16 addr); + void mmio_write(uint16 addr, uint8 data); + void power(); +} mbc1; diff --git a/libsnes/gameboy/cartridge/mbc2/mbc2.cpp b/libsnes/gameboy/cartridge/mbc2/mbc2.cpp new file mode 100755 index 0000000..0e0825f --- /dev/null +++ b/libsnes/gameboy/cartridge/mbc2/mbc2.cpp @@ -0,0 +1,42 @@ +#ifdef CARTRIDGE_CPP + +uint8 Cartridge::MBC2::mmio_read(uint16 addr) { + if((addr & 0xc000) == 0x0000) { //$0000-3fff + return cartridge.rom_read(addr); + } + + if((addr & 0xc000) == 0x4000) { //$4000-7fff + return cartridge.rom_read((rom_select << 14) | (addr & 0x3fff)); + } + + if((addr & 0xee00) == 0xa000) { //$a000-a1ff + if(ram_enable) return cartridge.ram_read(addr & 0x1ff); + return 0x00; + } + + return 0x00; +} + +void Cartridge::MBC2::mmio_write(uint16 addr, uint8 data) { + if((addr & 0xe000) == 0x0000) { //$0000-1fff + if(!(addr & 0x0100)) ram_enable = (data & 0x0f) == 0x0a; + return; + } + + if((addr & 0xe000) == 0x2000) { //$2000-3fff + if( (addr & 0x0100)) rom_select = (data & 0x0f) + ((data & 0x0f) == 0); + return; + } + + if((addr & 0xee00) == 0xa000) { //$a000-a1ff + if(ram_enable) cartridge.ram_write(addr & 0x1ff, data & 0x0f); + return; + } +} + +void Cartridge::MBC2::power() { + ram_enable = false; + rom_select = 0x01; +} + +#endif diff --git a/libsnes/gameboy/cartridge/mbc2/mbc2.hpp b/libsnes/gameboy/cartridge/mbc2/mbc2.hpp new file mode 100755 index 0000000..c22a652 --- /dev/null +++ b/libsnes/gameboy/cartridge/mbc2/mbc2.hpp @@ -0,0 +1,8 @@ +struct MBC2 : MMIO { + bool ram_enable; //$0000-1fff + uint8 rom_select; //$2000-3fff + + uint8 mmio_read(uint16 addr); + void mmio_write(uint16 addr, uint8 data); + void power(); +} mbc2; diff --git a/libsnes/gameboy/cartridge/mbc3/mbc3.cpp b/libsnes/gameboy/cartridge/mbc3/mbc3.cpp new file mode 100755 index 0000000..0d1d61a --- /dev/null +++ b/libsnes/gameboy/cartridge/mbc3/mbc3.cpp @@ -0,0 +1,120 @@ +#ifdef CARTRIDGE_CPP + +void Cartridge::MBC3::second() { + if(rtc_halt == false) { + if(++rtc_second >= 60) { + rtc_second = 0; + if(++rtc_minute >= 60) { + rtc_minute = 0; + if(++rtc_hour >= 24) { + rtc_hour = 0; + if(++rtc_day >= 512) { + rtc_day = 0; + rtc_day_carry = true; + } + } + } + } + } +} + +uint8 Cartridge::MBC3::mmio_read(uint16 addr) { + if((addr & 0xc000) == 0x0000) { //$0000-3fff + return cartridge.rom_read(addr); + } + + if((addr & 0xc000) == 0x4000) { //$4000-7fff + return cartridge.rom_read((rom_select << 14) | (addr & 0x3fff)); + } + + if((addr & 0xe000) == 0xa000) { //$a000-bfff + if(ram_enable) { + if(ram_select <= 0x03) { + return cartridge.ram_read((ram_select << 13) | (addr & 0x1fff)); + } + if(ram_select == 0x08) return rtc_latch_second; + if(ram_select == 0x09) return rtc_latch_minute; + if(ram_select == 0x0a) return rtc_latch_hour; + if(ram_select == 0x0b) return rtc_latch_day; + if(ram_select == 0x0c) return (rtc_latch_day_carry << 7) | (rtc_latch_day >> 8); + } + return 0x00; + } + + return 0x00; +} + +void Cartridge::MBC3::mmio_write(uint16 addr, uint8 data) { + if((addr & 0xe000) == 0x0000) { //$0000-1fff + ram_enable = (data & 0x0f) == 0x0a; + return; + } + + if((addr & 0xe000) == 0x2000) { //$2000-3fff + rom_select = (data & 0x7f) + ((data & 0x7f) == 0); + return; + } + + if((addr & 0xe000) == 0x4000) { //$4000-5fff + ram_select = data; + return; + } + + if((addr & 0xe000) == 0x6000) { //$6000-7fff + if(rtc_latch == 0 && data == 1) { + rtc_latch_second = rtc_second; + rtc_latch_minute = rtc_minute; + rtc_latch_hour = rtc_hour; + rtc_latch_day = rtc_day; + rtc_latch_day_carry = rtc_day_carry; + } + rtc_latch = data; + return; + } + + if((addr & 0xe000) == 0xa000) { //$a000-bfff + if(ram_enable) { + if(ram_select <= 0x03) { + cartridge.ram_write((ram_select << 13) | (addr & 0x1fff), data); + } else if(ram_select == 0x08) { + if(data >= 60) data = 0; + rtc_second = data; + } else if(ram_select == 0x09) { + if(data >= 60) data = 0; + rtc_minute = data; + } else if(ram_select == 0x0a) { + if(data >= 24) data = 0; + rtc_hour = data; + } else if(ram_select == 0x0b) { + rtc_day = (rtc_day & 0x0100) | data; + } else if(ram_select == 0x0c) { + rtc_day = ((data & 1) << 8) | (rtc_day & 0xff); + rtc_halt = data & 0x40; + rtc_day_carry = data & 0x80; + } + } + return; + } +} + +void Cartridge::MBC3::power() { + ram_enable = false; + rom_select = 0x01; + ram_select = 0x00; + rtc_latch = 0; + + rtc_halt = true; + rtc_second = 0; + rtc_minute = 0; + rtc_hour = 0; + rtc_day = 0; + rtc_day_carry = false; + + rtc_latch_second = 0; + rtc_latch_minute = 0; + rtc_latch_hour = 0; + rtc_latch_day = 0; + rtc_latch_day_carry = false; +} + +#endif diff --git a/libsnes/gameboy/cartridge/mbc3/mbc3.hpp b/libsnes/gameboy/cartridge/mbc3/mbc3.hpp new file mode 100755 index 0000000..fbd7775 --- /dev/null +++ b/libsnes/gameboy/cartridge/mbc3/mbc3.hpp @@ -0,0 +1,24 @@ +struct MBC3 : MMIO { + bool ram_enable; //$0000-1fff + uint8 rom_select; //$2000-3fff + uint8 ram_select; //$4000-5fff + bool rtc_latch; //$6000-7fff + + bool rtc_halt; + unsigned rtc_second; + unsigned rtc_minute; + unsigned rtc_hour; + unsigned rtc_day; + bool rtc_day_carry; + + unsigned rtc_latch_second; + unsigned rtc_latch_minute; + unsigned rtc_latch_hour; + unsigned rtc_latch_day; + unsigned rtc_latch_day_carry; + + void second(); + uint8 mmio_read(uint16 addr); + void mmio_write(uint16 addr, uint8 data); + void power(); +} mbc3; diff --git a/libsnes/gameboy/cartridge/mbc5/mbc5.cpp b/libsnes/gameboy/cartridge/mbc5/mbc5.cpp new file mode 100755 index 0000000..52f3223 --- /dev/null +++ b/libsnes/gameboy/cartridge/mbc5/mbc5.cpp @@ -0,0 +1,53 @@ +#ifdef CARTRIDGE_CPP + +uint8 Cartridge::MBC5::mmio_read(uint16 addr) { + if((addr & 0xc000) == 0x0000) { //$0000-3fff + return cartridge.rom_read(addr); + } + + if((addr & 0xc000) == 0x4000) { //$4000-7fff + return cartridge.rom_read((rom_select << 14) | (addr & 0x3fff)); + } + + if((addr & 0xe000) == 0xa000) { //$a000-bfff + if(ram_enable) return cartridge.ram_read((ram_select << 13) | (addr & 0x1fff)); + return 0x00; + } + + return 0x00; +} + +void Cartridge::MBC5::mmio_write(uint16 addr, uint8 data) { + if((addr & 0xe000) == 0x0000) { //$0000-1fff + ram_enable = (data & 0x0f) == 0x0a; + return; + } + + if((addr & 0xf000) == 0x2000) { //$2000-2fff + rom_select = (rom_select & 0x0100) | data; + return; + } + + if((addr & 0xf000) == 0x3000) { //$3000-3fff + rom_select = ((data & 1) << 8) | (rom_select & 0x00ff); + return; + } + + if((addr & 0xe000) == 0x4000) { //$4000-5fff + ram_select = data & 0x0f; + return; + } + + if((addr & 0xe000) == 0xa000) { //$a000-bfff + if(ram_enable) cartridge.ram_write((ram_select << 13) | (addr & 0x1fff), data); + return; + } +} + +void Cartridge::MBC5::power() { + ram_enable = false; + rom_select = 0x001; + ram_select = 0x00; +} + +#endif diff --git a/libsnes/gameboy/cartridge/mbc5/mbc5.hpp b/libsnes/gameboy/cartridge/mbc5/mbc5.hpp new file mode 100755 index 0000000..0ec3abb --- /dev/null +++ b/libsnes/gameboy/cartridge/mbc5/mbc5.hpp @@ -0,0 +1,9 @@ +struct MBC5 : MMIO { + bool ram_enable; //$0000-1fff + uint16 rom_select; //$2000-2fff + $3000-3fff + uint8 ram_select; //$4000-5fff + + uint8 mmio_read(uint16 addr); + void mmio_write(uint16 addr, uint8 data); + void power(); +} mbc5; diff --git a/libsnes/gameboy/cartridge/mmm01/mmm01.cpp b/libsnes/gameboy/cartridge/mmm01/mmm01.cpp new file mode 100755 index 0000000..b690b8c --- /dev/null +++ b/libsnes/gameboy/cartridge/mmm01/mmm01.cpp @@ -0,0 +1,65 @@ +#ifdef CARTRIDGE_CPP + +uint8 Cartridge::MMM01::mmio_read(uint16 addr) { + if((addr & 0x8000) == 0x0000) { //$0000-7fff + if(rom_mode == 0) return cartridge.rom_read(addr); + } + + if((addr & 0xc000) == 0x0000) { //$0000-3fff + return cartridge.rom_read(0x8000 + (rom_base << 14) + (addr & 0x3fff)); + } + + if((addr & 0xc000) == 0x4000) { //$4000-7fff + return cartridge.rom_read(0x8000 + (rom_base << 14) + (rom_select << 14) + (addr & 0x3fff)); + } + + if((addr & 0xe000) == 0xa000) { //$a000-bfff + if(ram_enable) return cartridge.ram_read((ram_select << 13) + (addr & 0x1fff)); + return 0x00; + } + + return 0x00; +} + +void Cartridge::MMM01::mmio_write(uint16 addr, uint8 data) { + if((addr & 0xe000) == 0x0000) { //$0000-1fff + if(rom_mode == 0) { + rom_mode = 1; + } else { + ram_enable = (data & 0x0f) == 0x0a; + } + } + + if((addr & 0xe000) == 0x2000) { //$2000-3fff + if(rom_mode == 0) { + rom_base = data & 0x3f; + } else { + rom_select = data; + } + } + + if((addr & 0xe000) == 0x4000) { //$4000-5fff + if(rom_mode == 1) { + ram_select = data; + } + } + + if((addr & 0xe000) == 0x6000) { //$6000-7fff + //unknown purpose + } + + if((addr & 0xe000) == 0xa000) { //$a000-bfff + if(ram_enable) cartridge.ram_write((ram_select << 13) + (addr & 0x1fff), data); + } +} + +void Cartridge::MMM01::power() { + rom_mode = 0; + rom_base = 0; + + ram_enable = false; + rom_select = 0x01; + ram_select = 0x00; +} + +#endif diff --git a/libsnes/gameboy/cartridge/mmm01/mmm01.hpp b/libsnes/gameboy/cartridge/mmm01/mmm01.hpp new file mode 100755 index 0000000..3474b06 --- /dev/null +++ b/libsnes/gameboy/cartridge/mmm01/mmm01.hpp @@ -0,0 +1,12 @@ +struct MMM01 : MMIO { + bool rom_mode; + uint8 rom_base; + + bool ram_enable; + uint8 rom_select; + uint8 ram_select; + + uint8 mmio_read(uint16 addr); + void mmio_write(uint16 addr, uint8 data); + void power(); +} mmm01; diff --git a/libsnes/gameboy/cartridge/serialization.cpp b/libsnes/gameboy/cartridge/serialization.cpp new file mode 100755 index 0000000..2f54262 --- /dev/null +++ b/libsnes/gameboy/cartridge/serialization.cpp @@ -0,0 +1,54 @@ +#ifdef CARTRIDGE_CPP + +void Cartridge::serialize(serializer &s) { + if(info.battery) s.array(ramdata, ramsize); + s.integer(bootrom_enable); + + s.integer(mbc1.ram_enable); + s.integer(mbc1.rom_select); + s.integer(mbc1.ram_select); + s.integer(mbc1.mode_select); + + s.integer(mbc2.ram_enable); + s.integer(mbc2.rom_select); + + s.integer(mbc3.ram_enable); + s.integer(mbc3.rom_select); + s.integer(mbc3.ram_select); + s.integer(mbc3.rtc_latch); + + s.integer(mbc3.rtc_halt); + s.integer(mbc3.rtc_second); + s.integer(mbc3.rtc_minute); + s.integer(mbc3.rtc_hour); + s.integer(mbc3.rtc_day); + s.integer(mbc3.rtc_day_carry); + + s.integer(mbc3.rtc_latch_second); + s.integer(mbc3.rtc_latch_minute); + s.integer(mbc3.rtc_latch_hour); + s.integer(mbc3.rtc_latch_day); + s.integer(mbc3.rtc_latch_day_carry); + + s.integer(mbc5.ram_enable); + s.integer(mbc5.rom_select); + s.integer(mbc5.ram_select); + + s.integer(mmm01.rom_mode); + s.integer(mmm01.rom_base); + + s.integer(mmm01.ram_enable); + s.integer(mmm01.rom_select); + s.integer(mmm01.ram_select); + + s.integer(huc1.ram_writable); + s.integer(huc1.rom_select); + s.integer(huc1.ram_select); + s.integer(huc1.model); + + s.integer(huc3.ram_enable); + s.integer(huc3.rom_select); + s.integer(huc3.ram_select); +} + +#endif diff --git a/libsnes/gameboy/cheat/cheat.hpp b/libsnes/gameboy/cheat/cheat.hpp new file mode 100644 index 0000000..4446a52 --- /dev/null +++ b/libsnes/gameboy/cheat/cheat.hpp @@ -0,0 +1,14 @@ +struct CheatCode { + unsigned addr; + unsigned data; + unsigned comp; +}; + +struct Cheat : public linear_vector { + static bool decode(const string &code, unsigned &addr, unsigned &data, unsigned &comp); + + void synchronize(); + bool override[65536]; +}; + +extern Cheat cheat; diff --git a/libsnes/gameboy/cheat/cheat_gb.cpp b/libsnes/gameboy/cheat/cheat_gb.cpp new file mode 100644 index 0000000..371f1d5 --- /dev/null +++ b/libsnes/gameboy/cheat/cheat_gb.cpp @@ -0,0 +1,91 @@ +#include + +namespace GameBoy { + +Cheat cheat; + +bool Cheat::decode(const string &code_, unsigned &addr, unsigned &data, unsigned &comp) { + static bool initialize = false; + static uint8 mapProActionReplay[256], mapGameGenie[256]; + + if(initialize == false) { + initialize = true; + + foreach(n, mapProActionReplay) n = ~0; + mapProActionReplay['0'] = 0; mapProActionReplay['1'] = 1; mapProActionReplay['2'] = 2; mapProActionReplay['3'] = 3; + mapProActionReplay['4'] = 4; mapProActionReplay['5'] = 5; mapProActionReplay['6'] = 6; mapProActionReplay['7'] = 7; + mapProActionReplay['8'] = 8; mapProActionReplay['9'] = 9; mapProActionReplay['A'] = 10; mapProActionReplay['B'] = 11; + mapProActionReplay['C'] = 12; mapProActionReplay['D'] = 13; mapProActionReplay['E'] = 14; mapProActionReplay['F'] = 15; + + foreach(n, mapGameGenie) n = ~0; + mapGameGenie['0'] = 0; mapGameGenie['1'] = 1; mapGameGenie['2'] = 2; mapGameGenie['3'] = 3; + mapGameGenie['4'] = 4; mapGameGenie['5'] = 5; mapGameGenie['6'] = 6; mapGameGenie['7'] = 7; + mapGameGenie['8'] = 8; mapGameGenie['9'] = 9; mapGameGenie['A'] = 10; mapGameGenie['B'] = 11; + mapGameGenie['C'] = 12; mapGameGenie['D'] = 13; mapGameGenie['E'] = 14; mapGameGenie['F'] = 15; + } + + string code = code_; + code.upper(); + unsigned length = code.length(), bits = 0; + + if(code.wildcard("????:??")) { + code = string( substr(code, 0, 4), substr(code, 5, 2) ); + for(unsigned n = 0; n < 6; n++) if(mapProActionReplay[code[n]] > 15) return false; + bits = hex(code); + addr = (bits >> 8) & 0xffff; + data = (bits >> 0) & 0xff; + comp = ~0; + return true; + } + + if(code.wildcard("????:??:??")) { + code = string( substr(code, 0, 4), substr(code, 5, 2), substr(code, 8, 2) ); + for(unsigned n = 0; n < 8; n++) if(mapProActionReplay[code[n]] > 15) return false; + bits = hex(code); + addr = (bits >> 16) & 0xffff; + data = (bits >> 8) & 0xff; + comp = (bits >> 0) & 0xff; + return true; + } + + if(code.wildcard("???" "-" "???")) { + code = string( substr(code, 0, 3), substr(code, 4, 3) ); + for(unsigned n = 0; n < 6; n++) if(mapGameGenie[code[n]] > 15) return false; + for(unsigned n = 0; n < 6; n++) bits |= mapGameGenie[code[n]] << (20 - n * 4); + + addr = (bits >> 0) & 0xffff; + data = (bits >> 16) & 0xff; + comp = ~0; + + addr = (((addr >> 4) | (addr << 12)) & 0xffff) ^ 0xf000; + + return true; + } + + if(code.wildcard("???" "-" "???" "-" "???")) { + code = string( substr(code, 0, 3), substr(code, 4, 3), substr(code, 8, 1), substr(code, 10, 1) ); + for(unsigned n = 0; n < 8; n++) if(mapGameGenie[code[n]] > 15) return false; + for(unsigned n = 0; n < 8; n++) bits |= mapGameGenie[code[n]] << (28 - n * 4); + + addr = (bits >> 8) & 0xffff; + data = (bits >> 24) & 0xff; + comp = (bits >> 0) & 0xff; + + addr = (((addr >> 4) | (addr << 12)) & 0xffff) ^ 0xf000; + comp = (((comp >> 2) | (comp << 6)) & 0xff) ^ 0xba; + + return true; + } + + return false; +} + +void Cheat::synchronize() { + foreach(n, override) n = false; + + for(unsigned n = 0; n < size(); n++) { + override[operator[](n).addr] = true; + } +} + +} diff --git a/libsnes/gameboy/cpu/core/core.cpp b/libsnes/gameboy/cpu/core/core.cpp new file mode 100755 index 0000000..11899b3 --- /dev/null +++ b/libsnes/gameboy/cpu/core/core.cpp @@ -0,0 +1,681 @@ +#ifdef CPU_CPP + +#include "table.cpp" + +void CPU::op_xx() { +} + +void CPU::op_cb() { + uint8 opcode = op_read(r[PC]++); + (this->*opcode_table_cb[opcode])(); +} + +//8-bit load commands + +template void CPU::op_ld_r_r() { + r[x] = r[y]; +} + +template void CPU::op_ld_r_n() { + r[x] = op_read(r[PC]++); +} + +template void CPU::op_ld_r_hl() { + r[x] = op_read(r[HL]); +} + +template void CPU::op_ld_hl_r() { + op_write(r[HL], r[x]); +} + +void CPU::op_ld_hl_n() { + op_write(r[HL], op_read(r[PC]++)); +} + +template void CPU::op_ld_a_rr() { + r[A] = op_read(r[x]); +} + +void CPU::op_ld_a_nn() { + uint8 lo = op_read(r[PC]++); + uint8 hi = op_read(r[PC]++); + r[A] = op_read((hi << 8) | (lo << 0)); +} + +template void CPU::op_ld_rr_a() { + op_write(r[x], r[A]); +} + +void CPU::op_ld_nn_a() { + uint8 lo = op_read(r[PC]++); + uint8 hi = op_read(r[PC]++); + op_write((hi << 8) | (lo << 0), r[A]); +} + +void CPU::op_ld_a_ffn() { + r[A] = op_read(0xff00 + op_read(r[PC]++)); +} + +void CPU::op_ld_ffn_a() { + op_write(0xff00 + op_read(r[PC]++), r[A]); +} + +void CPU::op_ld_a_ffc() { + r[A] = op_read(0xff00 + r[C]); +} + +void CPU::op_ld_ffc_a() { + op_write(0xff00 + r[C], r[A]); +} + +void CPU::op_ldi_hl_a() { + op_write(r[HL], r[A]); + r[HL]++; +} + +void CPU::op_ldi_a_hl() { + r[A] = op_read(r[HL]); + r[HL]++; +} + +void CPU::op_ldd_hl_a() { + op_write(r[HL], r[A]); + r[HL]--; +} + +void CPU::op_ldd_a_hl() { + r[A] = op_read(r[HL]); + r[HL]--; +} + +//16-bit load commands + +template void CPU::op_ld_rr_nn() { + r[x] = op_read(r[PC]++) << 0; + r[x] |= op_read(r[PC]++) << 8; +} + +void CPU::op_ld_nn_sp() { + uint16 addr = op_read(r[PC]++) << 0; + addr |= op_read(r[PC]++) << 8; + op_write(addr + 0, r[SP] >> 0); + op_write(addr + 1, r[SP] >> 8); +} + +void CPU::op_ld_sp_hl() { + r[SP] = r[HL]; + op_io(); +} + +template void CPU::op_push_rr() { + op_write(--r[SP], r[x] >> 8); + op_write(--r[SP], r[x] >> 0); + op_io(); +} + +template void CPU::op_pop_rr() { + r[x] = op_read(r[SP]++) << 0; + r[x] |= op_read(r[SP]++) << 8; +} + +//8-bit arithmetic commands + +void CPU::opi_add_a(uint8 x) { + uint16 rh = r[A] + x; + uint16 rl = (r[A] & 0x0f) + (x & 0x0f); + r[A] = rh; + r.f.z = (uint8)rh == 0; + r.f.n = 0; + r.f.h = rl > 0x0f; + r.f.c = rh > 0xff; +} + +template void CPU::op_add_a_r() { opi_add_a(r[x]); } +void CPU::op_add_a_n() { opi_add_a(op_read(r[PC]++)); } +void CPU::op_add_a_hl() { opi_add_a(op_read(r[HL])); } + +void CPU::opi_adc_a(uint8 x) { + uint16 rh = r[A] + x + r.f.c; + uint16 rl = (r[A] & 0x0f) + (x & 0x0f) + r.f.c; + r[A] = rh; + r.f.z = (uint8)rh == 0; + r.f.n = 0; + r.f.h = rl > 0x0f; + r.f.c = rh > 0xff; +} + +template void CPU::op_adc_a_r() { opi_adc_a(r[x]); } +void CPU::op_adc_a_n() { opi_adc_a(op_read(r[PC]++)); } +void CPU::op_adc_a_hl() { opi_adc_a(op_read(r[HL])); } + +void CPU::opi_sub_a(uint8 x) { + uint16 rh = r[A] - x; + uint16 rl = (r[A] & 0x0f) - (x & 0x0f); + r[A] = rh; + r.f.z = (uint8)rh == 0; + r.f.n = 1; + r.f.h = rl > 0x0f; + r.f.c = rh > 0xff; +} + +template void CPU::op_sub_a_r() { opi_sub_a(r[x]); } +void CPU::op_sub_a_n() { opi_sub_a(op_read(r[PC]++)); } +void CPU::op_sub_a_hl() { opi_sub_a(op_read(r[HL])); } + +void CPU::opi_sbc_a(uint8 x) { + uint16 rh = r[A] - x - r.f.c; + uint16 rl = (r[A] & 0x0f) - (x & 0x0f) - r.f.c; + r[A] = rh; + r.f.z = (uint8)rh == 0; + r.f.n = 1; + r.f.h = rl > 0x0f; + r.f.c = rh > 0xff; +} + +template void CPU::op_sbc_a_r() { opi_sbc_a(r[x]); } +void CPU::op_sbc_a_n() { opi_sbc_a(op_read(r[PC]++)); } +void CPU::op_sbc_a_hl() { opi_sbc_a(op_read(r[HL])); } + +void CPU::opi_and_a(uint8 x) { + r[A] &= x; + r.f.z = r[A] == 0; + r.f.n = 0; + r.f.h = 1; + r.f.c = 0; +} + +template void CPU::op_and_a_r() { opi_and_a(r[x]); } +void CPU::op_and_a_n() { opi_and_a(op_read(r[PC]++)); } +void CPU::op_and_a_hl() { opi_and_a(op_read(r[HL])); } + +void CPU::opi_xor_a(uint8 x) { + r[A] ^= x; + r.f.z = r[A] == 0; + r.f.n = 0; + r.f.h = 0; + r.f.c = 0; +} + +template void CPU::op_xor_a_r() { opi_xor_a(r[x]); } +void CPU::op_xor_a_n() { opi_xor_a(op_read(r[PC]++)); } +void CPU::op_xor_a_hl() { opi_xor_a(op_read(r[HL])); } + +void CPU::opi_or_a(uint8 x) { + r[A] |= x; + r.f.z = r[A] == 0; + r.f.n = 0; + r.f.h = 0; + r.f.c = 0; +} + +template void CPU::op_or_a_r() { opi_or_a(r[x]); } +void CPU::op_or_a_n() { opi_or_a(op_read(r[PC]++)); } +void CPU::op_or_a_hl() { opi_or_a(op_read(r[HL])); } + +void CPU::opi_cp_a(uint8 x) { + uint16 rh = r[A] - x; + uint16 rl = (r[A] & 0x0f) - (x & 0x0f); + r.f.z = (uint8)rh == 0; + r.f.n = 1; + r.f.h = rl > 0x0f; + r.f.c = rh > 0xff; +} + +template void CPU::op_cp_a_r() { opi_cp_a(r[x]); } +void CPU::op_cp_a_n() { opi_cp_a(op_read(r[PC]++)); } +void CPU::op_cp_a_hl() { opi_cp_a(op_read(r[HL])); } + +template void CPU::op_inc_r() { + r[x]++; + r.f.z = r[x] == 0; + r.f.n = 0; + r.f.h = (r[x] & 0x0f) == 0x00; +} + +void CPU::op_inc_hl() { + uint8 n = op_read(r[HL]); + op_write(r[HL], ++n); + r.f.z = n == 0; + r.f.n = 0; + r.f.h = (n & 0x0f) == 0x00; +} + +template void CPU::op_dec_r() { + r[x]--; + r.f.z = r[x] == 0; + r.f.n = 1; + r.f.h = (r[x] & 0x0f) == 0x0f; +} + +void CPU::op_dec_hl() { + uint8 n = op_read(r[HL]); + op_write(r[HL], --n); + r.f.z = n == 0; + r.f.n = 1; + r.f.h = (n & 0x0f) == 0x0f; +} + +void CPU::op_daa() { + uint16 a = r[A]; + if(r.f.n == 0) { + if(r.f.h || (a & 0x0f) > 0x09) a += 0x06; + if(r.f.c || (a ) > 0x9f) a += 0x60; + } else { + if(r.f.h) { + a -= 0x06; + if(r.f.c == 0) a &= 0xff; + } + if(r.f.c) a -= 0x60; + } + r[A] = a; + r.f.z = r[A] == 0; + r.f.h = 0; + r.f.c |= a & 0x100; +} + +void CPU::op_cpl() { + r[A] ^= 0xff; + r.f.n = 1; + r.f.h = 1; +} + +//16-bit arithmetic commands + +template void CPU::op_add_hl_rr() { + op_io(); + uint32 rb = (r[HL] + r[x]); + uint32 rn = (r[HL] & 0xfff) + (r[x] & 0xfff); + r[HL] = rb; + r.f.n = 0; + r.f.h = rn > 0x0fff; + r.f.c = rb > 0xffff; +} + +template void CPU::op_inc_rr() { + op_io(); + r[x]++; +} + +template void CPU::op_dec_rr() { + op_io(); + r[x]--; +} + +void CPU::op_add_sp_n() { + op_io(); + op_io(); + signed n = (int8)op_read(r[PC]++); + r.f.z = 0; + r.f.n = 0; + r.f.h = ((r[SP] & 0x0f) + (n & 0x0f)) > 0x0f; + r.f.c = ((r[SP] & 0xff) + (n & 0xff)) > 0xff; + r[SP] += n; +} + +void CPU::op_ld_hl_sp_n() { + op_io(); + signed n = (int8)op_read(r[PC]++); + r.f.z = 0; + r.f.n = 0; + r.f.h = ((r[SP] & 0x0f) + (n & 0x0f)) > 0x0f; + r.f.c = ((r[SP] & 0xff) + (n & 0xff)) > 0xff; + r[HL] = r[SP] + n; +} + +//rotate/shift commands + +void CPU::op_rlca() { + r[A] = (r[A] << 1) | (r[A] >> 7); + r.f.z = 0; + r.f.n = 0; + r.f.h = 0; + r.f.c = r[A] & 0x01; +} + +void CPU::op_rla() { + bool c = r[A] & 0x80; + r[A] = (r[A] << 1) | (r.f.c << 0); + r.f.z = 0; + r.f.n = 0; + r.f.h = 0; + r.f.c = c; +} + +void CPU::op_rrca() { + r[A] = (r[A] >> 1) | (r[A] << 7); + r.f.z = 0; + r.f.n = 0; + r.f.h = 0; + r.f.c = r[A] & 0x80; +} + +void CPU::op_rra() { + bool c = r[A] & 0x01; + r[A] = (r[A] >> 1) | (r.f.c << 7); + r.f.z = 0; + r.f.n = 0; + r.f.h = 0; + r.f.c = c; +} + +template void CPU::op_rlc_r() { + r[x] = (r[x] << 1) | (r[x] >> 7); + r.f.z = r[x] == 0; + r.f.n = 0; + r.f.h = 0; + r.f.c = r[x] & 0x01; +} + +void CPU::op_rlc_hl() { + uint8 n = op_read(r[HL]); + n = (n << 1) | (n >> 7); + op_write(r[HL], n); + r.f.z = n == 0; + r.f.n = 0; + r.f.h = 0; + r.f.c = n & 0x01; +} + +template void CPU::op_rl_r() { + bool c = r[x] & 0x80; + r[x] = (r[x] << 1) | (r.f.c << 0); + r.f.z = r[x] == 0; + r.f.n = 0; + r.f.h = 0; + r.f.c = c; +} + +void CPU::op_rl_hl() { + uint8 n = op_read(r[HL]); + bool c = n & 0x80; + n = (n << 1) | (r.f.c << 0); + op_write(r[HL], n); + r.f.z = n == 0; + r.f.n = 0; + r.f.h = 0; + r.f.c = c; +} + +template void CPU::op_rrc_r() { + r[x] = (r[x] >> 1) | (r[x] << 7); + r.f.z = r[x] == 0; + r.f.n = 0; + r.f.h = 0; + r.f.c = r[x] & 0x80; +} + +void CPU::op_rrc_hl() { + uint8 n = op_read(r[HL]); + n = (n >> 1) | (n << 7); + op_write(r[HL], n); + r.f.z = n == 0; + r.f.n = 0; + r.f.h = 0; + r.f.c = n & 0x80; +} + +template void CPU::op_rr_r() { + bool c = r[x] & 0x01; + r[x] = (r[x] >> 1) | (r.f.c << 7); + r.f.z = r[x] == 0; + r.f.n = 0; + r.f.h = 0; + r.f.c = c; +} + +void CPU::op_rr_hl() { + uint8 n = op_read(r[HL]); + bool c = n & 0x01; + n = (n >> 1) | (r.f.c << 7); + op_write(r[HL], n); + r.f.z = n == 0; + r.f.n = 0; + r.f.h = 0; + r.f.c = c; +} + +template void CPU::op_sla_r() { + bool c = r[x] & 0x80; + r[x] <<= 1; + r.f.z = r[x] == 0; + r.f.n = 0; + r.f.h = 0; + r.f.c = c; +} + +void CPU::op_sla_hl() { + uint8 n = op_read(r[HL]); + bool c = n & 0x80; + n <<= 1; + op_write(r[HL], n); + r.f.z = n == 0; + r.f.n = 0; + r.f.h = 0; + r.f.c = c; +} + +template void CPU::op_swap_r() { + r[x] = (r[x] << 4) | (r[x] >> 4); + r.f.z = r[x] == 0; + r.f.n = 0; + r.f.h = 0; + r.f.c = 0; +} + +void CPU::op_swap_hl() { + uint8 n = op_read(r[HL]); + n = (n << 4) | (n >> 4); + op_write(r[HL], n); + r.f.z = n == 0; + r.f.n = 0; + r.f.h = 0; + r.f.c = 0; +} + +template void CPU::op_sra_r() { + bool c = r[x] & 0x01; + r[x] = (int8)r[x] >> 1; + r.f.z = r[x] == 0; + r.f.n = 0; + r.f.h = 0; + r.f.c = c; +} + +void CPU::op_sra_hl() { + uint8 n = op_read(r[HL]); + bool c = n & 0x01; + n = (int8)n >> 1; + op_write(r[HL], n); + r.f.z = n == 0; + r.f.n = 0; + r.f.h = 0; + r.f.c = c; +} + +template void CPU::op_srl_r() { + bool c = r[x] & 0x01; + r[x] >>= 1; + r.f.z = r[x] == 0; + r.f.n = 0; + r.f.h = 0; + r.f.c = c; +} + +void CPU::op_srl_hl() { + uint8 n = op_read(r[HL]); + bool c = n & 0x01; + n >>= 1; + op_write(r[HL], n); + r.f.z = n == 0; + r.f.n = 0; + r.f.h = 0; + r.f.c = c; +} + +//single-bit commands + +template void CPU::op_bit_n_r() { + r.f.z = (r[x] & (1 << b)) == 0; + r.f.n = 0; + r.f.h = 1; +} + +template void CPU::op_bit_n_hl() { + uint8 n = op_read(r[HL]); + r.f.z = (n & (1 << b)) == 0; + r.f.n = 0; + r.f.h = 1; +} + +template void CPU::op_set_n_r() { + r[x] |= 1 << b; +} + +template void CPU::op_set_n_hl() { + uint8 n = op_read(r[HL]); + n |= 1 << b; + op_write(r[HL], n); +} + +template void CPU::op_res_n_r() { + r[x] &= ~(1 << b); +} + +template void CPU::op_res_n_hl() { + uint8 n = op_read(r[HL]); + n &= ~(1 << b); + op_write(r[HL], n); +} + +//control commands + +void CPU::op_ccf() { + r.f.n = 0; + r.f.h = 0; + r.f.c = !r.f.c; +} + +void CPU::op_scf() { + r.f.n = 0; + r.f.h = 0; + r.f.c = 1; +} + +void CPU::op_nop() { +} + +void CPU::op_halt() { + status.halt = true; + while(status.halt == true) op_io(); +} + +void CPU::op_stop() { + if(status.speed_switch) { + status.speed_switch = 0; + status.speed_double ^= 1; + frequency = 4 * 1024 * 1024; + if(status.speed_double) frequency *= 2; + return; + } + status.stop = true; + while(status.stop == true) op_io(); +} + +void CPU::op_di() { + status.ime = 0; +} + +void CPU::op_ei() { + status.ei = true; +//status.ime = 1; +} + +//jump commands + +void CPU::op_jp_nn() { + uint8 lo = op_read(r[PC]++); + uint8 hi = op_read(r[PC]++); + r[PC] = (hi << 8) | (lo << 0); + op_io(); +} + +void CPU::op_jp_hl() { + r[PC] = r[HL]; +} + +template void CPU::op_jp_f_nn() { + uint8 lo = op_read(r[PC]++); + uint8 hi = op_read(r[PC]++); + if(r.f[x] == y) { + r[PC] = (hi << 8) | (lo << 0); + op_io(); + } +} + +void CPU::op_jr_n() { + int8 n = op_read(r[PC]++); + r[PC] += n; + op_io(); +} + +template void CPU::op_jr_f_n() { + int8 n = op_read(r[PC]++); + if(r.f[x] == y) { + r[PC] += n; + op_io(); + } +} + +void CPU::op_call_nn() { + uint8 lo = op_read(r[PC]++); + uint8 hi = op_read(r[PC]++); + op_write(--r[SP], r[PC] >> 8); + op_write(--r[SP], r[PC] >> 0); + r[PC] = (hi << 8) | (lo << 0); + op_io(); +} + +template void CPU::op_call_f_nn() { + uint8 lo = op_read(r[PC]++); + uint8 hi = op_read(r[PC]++); + if(r.f[x] == y) { + op_write(--r[SP], r[PC] >> 8); + op_write(--r[SP], r[PC] >> 0); + r[PC] = (hi << 8) | (lo << 0); + op_io(); + } +} + +void CPU::op_ret() { + uint8 lo = op_read(r[SP]++); + uint8 hi = op_read(r[SP]++); + r[PC] = (hi << 8) | (lo << 0); + op_io(); +} + +template void CPU::op_ret_f() { + op_io(); + if(r.f[x] == y) { + uint8 lo = op_read(r[SP]++); + uint8 hi = op_read(r[SP]++); + r[PC] = (hi << 8) | (lo << 0); + op_io(); + } +} + +void CPU::op_reti() { + uint8 lo = op_read(r[SP]++); + uint8 hi = op_read(r[SP]++); + r[PC] = (hi << 8) | (lo << 0); + op_io(); + status.ime = 1; +} + +template void CPU::op_rst_n() { + op_write(--r[SP], r[PC] >> 8); + op_write(--r[SP], r[PC] >> 0); + r[PC] = n; + op_io(); +} + +#endif diff --git a/libsnes/gameboy/cpu/core/core.hpp b/libsnes/gameboy/cpu/core/core.hpp new file mode 100755 index 0000000..85ebeb1 --- /dev/null +++ b/libsnes/gameboy/cpu/core/core.hpp @@ -0,0 +1,145 @@ +#include "registers.hpp" +void (CPU::*opcode_table[256])(); +void (CPU::*opcode_table_cb[256])(); +void initialize_opcode_table(); + +void op_xx(); +void op_cb(); + +//8-bit load commands +template void op_ld_r_r(); +template void op_ld_r_n(); +template void op_ld_r_hl(); +template void op_ld_hl_r(); +void op_ld_hl_n(); +template void op_ld_a_rr(); +void op_ld_a_nn(); +template void op_ld_rr_a(); +void op_ld_nn_a(); +void op_ld_a_ffn(); +void op_ld_ffn_a(); +void op_ld_a_ffc(); +void op_ld_ffc_a(); +void op_ldi_hl_a(); +void op_ldi_a_hl(); +void op_ldd_hl_a(); +void op_ldd_a_hl(); + +//16-bit load commands +template void op_ld_rr_nn(); +void op_ld_nn_sp(); +void op_ld_sp_hl(); +template void op_push_rr(); +template void op_pop_rr(); + +//8-bit arithmetic commands +void opi_add_a(uint8 x); +template void op_add_a_r(); +void op_add_a_n(); +void op_add_a_hl(); + +void opi_adc_a(uint8 x); +template void op_adc_a_r(); +void op_adc_a_n(); +void op_adc_a_hl(); + +void opi_sub_a(uint8 x); +template void op_sub_a_r(); +void op_sub_a_n(); +void op_sub_a_hl(); + +void opi_sbc_a(uint8 x); +template void op_sbc_a_r(); +void op_sbc_a_n(); +void op_sbc_a_hl(); + +void opi_and_a(uint8 x); +template void op_and_a_r(); +void op_and_a_n(); +void op_and_a_hl(); + +void opi_xor_a(uint8 x); +template void op_xor_a_r(); +void op_xor_a_n(); +void op_xor_a_hl(); + +void opi_or_a(uint8 x); +template void op_or_a_r(); +void op_or_a_n(); +void op_or_a_hl(); + +void opi_cp_a(uint8 x); +template void op_cp_a_r(); +void op_cp_a_n(); +void op_cp_a_hl(); + +template void op_inc_r(); +void op_inc_hl(); +template void op_dec_r(); +void op_dec_hl(); +void op_daa(); +void op_cpl(); + +//16-bit arithmetic commands +template void op_add_hl_rr(); +template void op_inc_rr(); +template void op_dec_rr(); +void op_add_sp_n(); +void op_ld_hl_sp_n(); + +//rotate/shift commands +void op_rlca(); +void op_rla(); +void op_rrca(); +void op_rra(); +template void op_rlc_r(); +void op_rlc_hl(); +template void op_rl_r(); +void op_rl_hl(); +template void op_rrc_r(); +void op_rrc_hl(); +template void op_rr_r(); +void op_rr_hl(); +template void op_sla_r(); +void op_sla_hl(); +template void op_swap_r(); +void op_swap_hl(); +template void op_sra_r(); +void op_sra_hl(); +template void op_srl_r(); +void op_srl_hl(); + +//single-bit commands +template void op_bit_n_r(); +template void op_bit_n_hl(); +template void op_set_n_r(); +template void op_set_n_hl(); +template void op_res_n_r(); +template void op_res_n_hl(); + +//control commands +void op_ccf(); +void op_scf(); +void op_nop(); +void op_halt(); +void op_stop(); +void op_di(); +void op_ei(); + +//jump commands +void op_jp_nn(); +void op_jp_hl(); +template void op_jp_f_nn(); +void op_jr_n(); +template void op_jr_f_n(); +void op_call_nn(); +template void op_call_f_nn(); +void op_ret(); +template void op_ret_f(); +void op_reti(); +template void op_rst_n(); + +//disassembler.cpp +string disassemble(uint16 pc); +string disassemble_opcode(uint16 pc); +string disassemble_opcode_cb(uint16 pc); diff --git a/libsnes/gameboy/cpu/core/registers.hpp b/libsnes/gameboy/cpu/core/registers.hpp new file mode 100755 index 0000000..fbbcbd8 --- /dev/null +++ b/libsnes/gameboy/cpu/core/registers.hpp @@ -0,0 +1,101 @@ +enum { + A, F, AF, + B, C, BC, + D, E, DE, + H, L, HL, + SP, PC, +}; + +enum { + ZF, NF, HF, CF, +}; + +//register base class +//the idea here is to have all registers derive from a single base class. +//this allows construction of opcodes that can take any register as input or output, +//despite the fact that behind-the-scenes, special handling is done for eg: F, AF, HL, etc. +//registers can also be chained together: eg af = 0x0000 writes both a and f. +struct Register { + virtual operator unsigned() const = 0; + virtual unsigned operator=(unsigned x) = 0; + Register& operator=(const Register &x) { operator=((unsigned)x); return *this; } + + unsigned operator++(int) { unsigned r = *this; operator=(*this + 1); return r; } + unsigned operator--(int) { unsigned r = *this; operator=(*this - 1); return r; } + unsigned operator++() { return operator=(*this + 1); } + unsigned operator--() { return operator=(*this - 1); } + + unsigned operator |=(unsigned x) { return operator=(*this | x); } + unsigned operator ^=(unsigned x) { return operator=(*this ^ x); } + unsigned operator &=(unsigned x) { return operator=(*this & x); } + + unsigned operator<<=(unsigned x) { return operator=(*this << x); } + unsigned operator>>=(unsigned x) { return operator=(*this >> x); } + + unsigned operator +=(unsigned x) { return operator=(*this + x); } + unsigned operator -=(unsigned x) { return operator=(*this - x); } + unsigned operator *=(unsigned x) { return operator=(*this * x); } + unsigned operator /=(unsigned x) { return operator=(*this / x); } + unsigned operator %=(unsigned x) { return operator=(*this % x); } +}; + +struct Register8 : Register { + uint8 data; + operator unsigned() const { return data; } + unsigned operator=(unsigned x) { return data = x; } +}; + +struct RegisterF : Register { + bool z, n, h, c; + operator unsigned() const { return (z << 7) | (n << 6) | (h << 5) | (c << 4); } + unsigned operator=(unsigned x) { z = x & 0x80; n = x & 0x40; h = x & 0x20; c = x & 0x10; return *this; } + bool& operator[](unsigned r) { + static bool* table[] = { &z, &n, &h, &c }; + return *table[r]; + } +}; + +struct Register16 : Register { + uint16 data; + operator unsigned() const { return data; } + unsigned operator=(unsigned x) { return data = x; } +}; + +struct RegisterAF : Register { + Register8 &hi; + RegisterF &lo; + operator unsigned() const { return (hi << 8) | (lo << 0); } + unsigned operator=(unsigned x) { hi = x >> 8; lo = x >> 0; return *this; } + RegisterAF(Register8 &hi, RegisterF &lo) : hi(hi), lo(lo) {} +}; + +struct RegisterW : Register { + Register8 &hi, &lo; + operator unsigned() const { return (hi << 8) | (lo << 0); } + unsigned operator=(unsigned x) { hi = x >> 8; lo = x >> 0; return *this; } + RegisterW(Register8 &hi, Register8 &lo) : hi(hi), lo(lo) {} +}; + +struct Registers { + Register8 a; + RegisterF f; + RegisterAF af; + Register8 b; + Register8 c; + RegisterW bc; + Register8 d; + Register8 e; + RegisterW de; + Register8 h; + Register8 l; + RegisterW hl; + Register16 sp; + Register16 pc; + + Register& operator[](unsigned r) { + static Register* table[] = { &a, &f, &af, &b, &c, &bc, &d, &e, &de, &h, &l, &hl, &sp, &pc }; + return *table[r]; + } + + Registers() : af(a, f), bc(b, c), de(d, e), hl(h, l) {} +} r; diff --git a/libsnes/gameboy/cpu/core/table.cpp b/libsnes/gameboy/cpu/core/table.cpp new file mode 100755 index 0000000..fa2f111 --- /dev/null +++ b/libsnes/gameboy/cpu/core/table.cpp @@ -0,0 +1,519 @@ +#ifdef CPU_CPP + +void CPU::initialize_opcode_table() { + opcode_table[0x00] = &CPU::op_nop; + opcode_table[0x01] = &CPU::op_ld_rr_nn; + opcode_table[0x02] = &CPU::op_ld_rr_a; + opcode_table[0x03] = &CPU::op_inc_rr; + opcode_table[0x04] = &CPU::op_inc_r; + opcode_table[0x05] = &CPU::op_dec_r; + opcode_table[0x06] = &CPU::op_ld_r_n; + opcode_table[0x07] = &CPU::op_rlca; + opcode_table[0x08] = &CPU::op_ld_nn_sp; + opcode_table[0x09] = &CPU::op_add_hl_rr; + opcode_table[0x0a] = &CPU::op_ld_a_rr; + opcode_table[0x0b] = &CPU::op_dec_rr; + opcode_table[0x0c] = &CPU::op_inc_r; + opcode_table[0x0d] = &CPU::op_dec_r; + opcode_table[0x0e] = &CPU::op_ld_r_n; + opcode_table[0x0f] = &CPU::op_rrca; + opcode_table[0x10] = &CPU::op_stop; + opcode_table[0x11] = &CPU::op_ld_rr_nn; + opcode_table[0x12] = &CPU::op_ld_rr_a; + opcode_table[0x13] = &CPU::op_inc_rr; + opcode_table[0x14] = &CPU::op_inc_r; + opcode_table[0x15] = &CPU::op_dec_r; + opcode_table[0x16] = &CPU::op_ld_r_n; + opcode_table[0x17] = &CPU::op_rla; + opcode_table[0x18] = &CPU::op_jr_n; + opcode_table[0x19] = &CPU::op_add_hl_rr; + opcode_table[0x1a] = &CPU::op_ld_a_rr; + opcode_table[0x1b] = &CPU::op_dec_rr; + opcode_table[0x1c] = &CPU::op_inc_r; + opcode_table[0x1d] = &CPU::op_dec_r; + opcode_table[0x1e] = &CPU::op_ld_r_n; + opcode_table[0x1f] = &CPU::op_rra; + opcode_table[0x20] = &CPU::op_jr_f_n; + opcode_table[0x21] = &CPU::op_ld_rr_nn; + opcode_table[0x22] = &CPU::op_ldi_hl_a; + opcode_table[0x23] = &CPU::op_inc_rr; + opcode_table[0x24] = &CPU::op_inc_r; + opcode_table[0x25] = &CPU::op_dec_r; + opcode_table[0x26] = &CPU::op_ld_r_n; + opcode_table[0x27] = &CPU::op_daa; + opcode_table[0x28] = &CPU::op_jr_f_n; + opcode_table[0x29] = &CPU::op_add_hl_rr; + opcode_table[0x2a] = &CPU::op_ldi_a_hl; + opcode_table[0x2b] = &CPU::op_dec_rr; + opcode_table[0x2c] = &CPU::op_inc_r; + opcode_table[0x2d] = &CPU::op_dec_r; + opcode_table[0x2e] = &CPU::op_ld_r_n; + opcode_table[0x2f] = &CPU::op_cpl; + opcode_table[0x30] = &CPU::op_jr_f_n; + opcode_table[0x31] = &CPU::op_ld_rr_nn; + opcode_table[0x32] = &CPU::op_ldd_hl_a; + opcode_table[0x33] = &CPU::op_inc_rr; + opcode_table[0x34] = &CPU::op_inc_hl; + opcode_table[0x35] = &CPU::op_dec_hl; + opcode_table[0x36] = &CPU::op_ld_hl_n; + opcode_table[0x37] = &CPU::op_scf; + opcode_table[0x38] = &CPU::op_jr_f_n; + opcode_table[0x39] = &CPU::op_add_hl_rr; + opcode_table[0x3a] = &CPU::op_ldd_a_hl; + opcode_table[0x3b] = &CPU::op_dec_rr; + opcode_table[0x3c] = &CPU::op_inc_r; + opcode_table[0x3d] = &CPU::op_dec_r; + opcode_table[0x3e] = &CPU::op_ld_r_n; + opcode_table[0x3f] = &CPU::op_ccf; + opcode_table[0x40] = &CPU::op_ld_r_r; + opcode_table[0x41] = &CPU::op_ld_r_r; + opcode_table[0x42] = &CPU::op_ld_r_r; + opcode_table[0x43] = &CPU::op_ld_r_r; + opcode_table[0x44] = &CPU::op_ld_r_r; + opcode_table[0x45] = &CPU::op_ld_r_r; + opcode_table[0x46] = &CPU::op_ld_r_hl; + opcode_table[0x47] = &CPU::op_ld_r_r; + opcode_table[0x48] = &CPU::op_ld_r_r; + opcode_table[0x49] = &CPU::op_ld_r_r; + opcode_table[0x4a] = &CPU::op_ld_r_r; + opcode_table[0x4b] = &CPU::op_ld_r_r; + opcode_table[0x4c] = &CPU::op_ld_r_r; + opcode_table[0x4d] = &CPU::op_ld_r_r; + opcode_table[0x4e] = &CPU::op_ld_r_hl; + opcode_table[0x4f] = &CPU::op_ld_r_r; + opcode_table[0x50] = &CPU::op_ld_r_r; + opcode_table[0x51] = &CPU::op_ld_r_r; + opcode_table[0x52] = &CPU::op_ld_r_r; + opcode_table[0x53] = &CPU::op_ld_r_r; + opcode_table[0x54] = &CPU::op_ld_r_r; + opcode_table[0x55] = &CPU::op_ld_r_r; + opcode_table[0x56] = &CPU::op_ld_r_hl; + opcode_table[0x57] = &CPU::op_ld_r_r; + opcode_table[0x58] = &CPU::op_ld_r_r; + opcode_table[0x59] = &CPU::op_ld_r_r; + opcode_table[0x5a] = &CPU::op_ld_r_r; + opcode_table[0x5b] = &CPU::op_ld_r_r; + opcode_table[0x5c] = &CPU::op_ld_r_r; + opcode_table[0x5d] = &CPU::op_ld_r_r; + opcode_table[0x5e] = &CPU::op_ld_r_hl; + opcode_table[0x5f] = &CPU::op_ld_r_r; + opcode_table[0x60] = &CPU::op_ld_r_r; + opcode_table[0x61] = &CPU::op_ld_r_r; + opcode_table[0x62] = &CPU::op_ld_r_r; + opcode_table[0x63] = &CPU::op_ld_r_r; + opcode_table[0x64] = &CPU::op_ld_r_r; + opcode_table[0x65] = &CPU::op_ld_r_r; + opcode_table[0x66] = &CPU::op_ld_r_hl; + opcode_table[0x67] = &CPU::op_ld_r_r; + opcode_table[0x68] = &CPU::op_ld_r_r; + opcode_table[0x69] = &CPU::op_ld_r_r; + opcode_table[0x6a] = &CPU::op_ld_r_r; + opcode_table[0x6b] = &CPU::op_ld_r_r; + opcode_table[0x6c] = &CPU::op_ld_r_r; + opcode_table[0x6d] = &CPU::op_ld_r_r; + opcode_table[0x6e] = &CPU::op_ld_r_hl; + opcode_table[0x6f] = &CPU::op_ld_r_r; + opcode_table[0x70] = &CPU::op_ld_hl_r; + opcode_table[0x71] = &CPU::op_ld_hl_r; + opcode_table[0x72] = &CPU::op_ld_hl_r; + opcode_table[0x73] = &CPU::op_ld_hl_r; + opcode_table[0x74] = &CPU::op_ld_hl_r; + opcode_table[0x75] = &CPU::op_ld_hl_r; + opcode_table[0x76] = &CPU::op_halt; + opcode_table[0x77] = &CPU::op_ld_hl_r; + opcode_table[0x78] = &CPU::op_ld_r_r; + opcode_table[0x79] = &CPU::op_ld_r_r; + opcode_table[0x7a] = &CPU::op_ld_r_r; + opcode_table[0x7b] = &CPU::op_ld_r_r; + opcode_table[0x7c] = &CPU::op_ld_r_r; + opcode_table[0x7d] = &CPU::op_ld_r_r; + opcode_table[0x7e] = &CPU::op_ld_r_hl; + opcode_table[0x7f] = &CPU::op_ld_r_r; + opcode_table[0x80] = &CPU::op_add_a_r; + opcode_table[0x81] = &CPU::op_add_a_r; + opcode_table[0x82] = &CPU::op_add_a_r; + opcode_table[0x83] = &CPU::op_add_a_r; + opcode_table[0x84] = &CPU::op_add_a_r; + opcode_table[0x85] = &CPU::op_add_a_r; + opcode_table[0x86] = &CPU::op_add_a_hl; + opcode_table[0x87] = &CPU::op_add_a_r; + opcode_table[0x88] = &CPU::op_adc_a_r; + opcode_table[0x89] = &CPU::op_adc_a_r; + opcode_table[0x8a] = &CPU::op_adc_a_r; + opcode_table[0x8b] = &CPU::op_adc_a_r; + opcode_table[0x8c] = &CPU::op_adc_a_r; + opcode_table[0x8d] = &CPU::op_adc_a_r; + opcode_table[0x8e] = &CPU::op_adc_a_hl; + opcode_table[0x8f] = &CPU::op_adc_a_r; + opcode_table[0x90] = &CPU::op_sub_a_r; + opcode_table[0x91] = &CPU::op_sub_a_r; + opcode_table[0x92] = &CPU::op_sub_a_r; + opcode_table[0x93] = &CPU::op_sub_a_r; + opcode_table[0x94] = &CPU::op_sub_a_r; + opcode_table[0x95] = &CPU::op_sub_a_r; + opcode_table[0x96] = &CPU::op_sub_a_hl; + opcode_table[0x97] = &CPU::op_sub_a_r; + opcode_table[0x98] = &CPU::op_sbc_a_r; + opcode_table[0x99] = &CPU::op_sbc_a_r; + opcode_table[0x9a] = &CPU::op_sbc_a_r; + opcode_table[0x9b] = &CPU::op_sbc_a_r; + opcode_table[0x9c] = &CPU::op_sbc_a_r; + opcode_table[0x9d] = &CPU::op_sbc_a_r; + opcode_table[0x9e] = &CPU::op_sbc_a_hl; + opcode_table[0x9f] = &CPU::op_sbc_a_r; + opcode_table[0xa0] = &CPU::op_and_a_r; + opcode_table[0xa1] = &CPU::op_and_a_r; + opcode_table[0xa2] = &CPU::op_and_a_r; + opcode_table[0xa3] = &CPU::op_and_a_r; + opcode_table[0xa4] = &CPU::op_and_a_r; + opcode_table[0xa5] = &CPU::op_and_a_r; + opcode_table[0xa6] = &CPU::op_and_a_hl; + opcode_table[0xa7] = &CPU::op_and_a_r; + opcode_table[0xa8] = &CPU::op_xor_a_r; + opcode_table[0xa9] = &CPU::op_xor_a_r; + opcode_table[0xaa] = &CPU::op_xor_a_r; + opcode_table[0xab] = &CPU::op_xor_a_r; + opcode_table[0xac] = &CPU::op_xor_a_r; + opcode_table[0xad] = &CPU::op_xor_a_r; + opcode_table[0xae] = &CPU::op_xor_a_hl; + opcode_table[0xaf] = &CPU::op_xor_a_r; + opcode_table[0xb0] = &CPU::op_or_a_r; + opcode_table[0xb1] = &CPU::op_or_a_r; + opcode_table[0xb2] = &CPU::op_or_a_r; + opcode_table[0xb3] = &CPU::op_or_a_r; + opcode_table[0xb4] = &CPU::op_or_a_r; + opcode_table[0xb5] = &CPU::op_or_a_r; + opcode_table[0xb6] = &CPU::op_or_a_hl; + opcode_table[0xb7] = &CPU::op_or_a_r; + opcode_table[0xb8] = &CPU::op_cp_a_r; + opcode_table[0xb9] = &CPU::op_cp_a_r; + opcode_table[0xba] = &CPU::op_cp_a_r; + opcode_table[0xbb] = &CPU::op_cp_a_r; + opcode_table[0xbc] = &CPU::op_cp_a_r; + opcode_table[0xbd] = &CPU::op_cp_a_r; + opcode_table[0xbe] = &CPU::op_cp_a_hl; + opcode_table[0xbf] = &CPU::op_cp_a_r; + opcode_table[0xc0] = &CPU::op_ret_f; + opcode_table[0xc1] = &CPU::op_pop_rr; + opcode_table[0xc2] = &CPU::op_jp_f_nn; + opcode_table[0xc3] = &CPU::op_jp_nn; + opcode_table[0xc4] = &CPU::op_call_f_nn; + opcode_table[0xc5] = &CPU::op_push_rr; + opcode_table[0xc6] = &CPU::op_add_a_n; + opcode_table[0xc7] = &CPU::op_rst_n<0x00>; + opcode_table[0xc8] = &CPU::op_ret_f; + opcode_table[0xc9] = &CPU::op_ret; + opcode_table[0xca] = &CPU::op_jp_f_nn; + opcode_table[0xcb] = &CPU::op_cb; + opcode_table[0xcc] = &CPU::op_call_f_nn; + opcode_table[0xcd] = &CPU::op_call_nn; + opcode_table[0xce] = &CPU::op_adc_a_n; + opcode_table[0xcf] = &CPU::op_rst_n<0x08>; + opcode_table[0xd0] = &CPU::op_ret_f; + opcode_table[0xd1] = &CPU::op_pop_rr; + opcode_table[0xd2] = &CPU::op_jp_f_nn; + opcode_table[0xd3] = &CPU::op_xx; + opcode_table[0xd4] = &CPU::op_call_f_nn; + opcode_table[0xd5] = &CPU::op_push_rr; + opcode_table[0xd6] = &CPU::op_sub_a_n; + opcode_table[0xd7] = &CPU::op_rst_n<0x10>; + opcode_table[0xd8] = &CPU::op_ret_f; + opcode_table[0xd9] = &CPU::op_reti; + opcode_table[0xda] = &CPU::op_jp_f_nn; + opcode_table[0xdb] = &CPU::op_xx; + opcode_table[0xdc] = &CPU::op_call_f_nn; + opcode_table[0xdd] = &CPU::op_xx; + opcode_table[0xde] = &CPU::op_sbc_a_n; + opcode_table[0xdf] = &CPU::op_rst_n<0x18>; + opcode_table[0xe0] = &CPU::op_ld_ffn_a; + opcode_table[0xe1] = &CPU::op_pop_rr; + opcode_table[0xe2] = &CPU::op_ld_ffc_a; + opcode_table[0xe3] = &CPU::op_xx; + opcode_table[0xe4] = &CPU::op_xx; + opcode_table[0xe5] = &CPU::op_push_rr; + opcode_table[0xe6] = &CPU::op_and_a_n; + opcode_table[0xe7] = &CPU::op_rst_n<0x20>; + opcode_table[0xe8] = &CPU::op_add_sp_n; + opcode_table[0xe9] = &CPU::op_jp_hl; + opcode_table[0xea] = &CPU::op_ld_nn_a; + opcode_table[0xeb] = &CPU::op_xx; + opcode_table[0xec] = &CPU::op_xx; + opcode_table[0xed] = &CPU::op_xx; + opcode_table[0xee] = &CPU::op_xor_a_n; + opcode_table[0xef] = &CPU::op_rst_n<0x28>; + opcode_table[0xf0] = &CPU::op_ld_a_ffn; + opcode_table[0xf1] = &CPU::op_pop_rr; + opcode_table[0xf2] = &CPU::op_ld_a_ffc; + opcode_table[0xf3] = &CPU::op_di; + opcode_table[0xf4] = &CPU::op_xx; + opcode_table[0xf5] = &CPU::op_push_rr; + opcode_table[0xf6] = &CPU::op_or_a_n; + opcode_table[0xf7] = &CPU::op_rst_n<0x30>; + opcode_table[0xf8] = &CPU::op_ld_hl_sp_n; + opcode_table[0xf9] = &CPU::op_ld_sp_hl; + opcode_table[0xfa] = &CPU::op_ld_a_nn; + opcode_table[0xfb] = &CPU::op_ei; + opcode_table[0xfc] = &CPU::op_xx; + opcode_table[0xfd] = &CPU::op_xx; + opcode_table[0xfe] = &CPU::op_cp_a_n; + opcode_table[0xff] = &CPU::op_rst_n<0x38>; + + opcode_table_cb[0x00] = &CPU::op_rlc_r; + opcode_table_cb[0x01] = &CPU::op_rlc_r; + opcode_table_cb[0x02] = &CPU::op_rlc_r; + opcode_table_cb[0x03] = &CPU::op_rlc_r; + opcode_table_cb[0x04] = &CPU::op_rlc_r; + opcode_table_cb[0x05] = &CPU::op_rlc_r; + opcode_table_cb[0x06] = &CPU::op_rlc_hl; + opcode_table_cb[0x07] = &CPU::op_rlc_r; + opcode_table_cb[0x08] = &CPU::op_rrc_r; + opcode_table_cb[0x09] = &CPU::op_rrc_r; + opcode_table_cb[0x0a] = &CPU::op_rrc_r; + opcode_table_cb[0x0b] = &CPU::op_rrc_r; + opcode_table_cb[0x0c] = &CPU::op_rrc_r; + opcode_table_cb[0x0d] = &CPU::op_rrc_r; + opcode_table_cb[0x0e] = &CPU::op_rrc_hl; + opcode_table_cb[0x0f] = &CPU::op_rrc_r; + opcode_table_cb[0x10] = &CPU::op_rl_r; + opcode_table_cb[0x11] = &CPU::op_rl_r; + opcode_table_cb[0x12] = &CPU::op_rl_r; + opcode_table_cb[0x13] = &CPU::op_rl_r; + opcode_table_cb[0x14] = &CPU::op_rl_r; + opcode_table_cb[0x15] = &CPU::op_rl_r; + opcode_table_cb[0x16] = &CPU::op_rl_hl; + opcode_table_cb[0x17] = &CPU::op_rl_r; + opcode_table_cb[0x18] = &CPU::op_rr_r; + opcode_table_cb[0x19] = &CPU::op_rr_r; + opcode_table_cb[0x1a] = &CPU::op_rr_r; + opcode_table_cb[0x1b] = &CPU::op_rr_r; + opcode_table_cb[0x1c] = &CPU::op_rr_r; + opcode_table_cb[0x1d] = &CPU::op_rr_r; + opcode_table_cb[0x1e] = &CPU::op_rr_hl; + opcode_table_cb[0x1f] = &CPU::op_rr_r; + opcode_table_cb[0x20] = &CPU::op_sla_r; + opcode_table_cb[0x21] = &CPU::op_sla_r; + opcode_table_cb[0x22] = &CPU::op_sla_r; + opcode_table_cb[0x23] = &CPU::op_sla_r; + opcode_table_cb[0x24] = &CPU::op_sla_r; + opcode_table_cb[0x25] = &CPU::op_sla_r; + opcode_table_cb[0x26] = &CPU::op_sla_hl; + opcode_table_cb[0x27] = &CPU::op_sla_r; + opcode_table_cb[0x28] = &CPU::op_sra_r; + opcode_table_cb[0x29] = &CPU::op_sra_r; + opcode_table_cb[0x2a] = &CPU::op_sra_r; + opcode_table_cb[0x2b] = &CPU::op_sra_r; + opcode_table_cb[0x2c] = &CPU::op_sra_r; + opcode_table_cb[0x2d] = &CPU::op_sra_r; + opcode_table_cb[0x2e] = &CPU::op_sra_hl; + opcode_table_cb[0x2f] = &CPU::op_sra_r; + opcode_table_cb[0x30] = &CPU::op_swap_r; + opcode_table_cb[0x31] = &CPU::op_swap_r; + opcode_table_cb[0x32] = &CPU::op_swap_r; + opcode_table_cb[0x33] = &CPU::op_swap_r; + opcode_table_cb[0x34] = &CPU::op_swap_r; + opcode_table_cb[0x35] = &CPU::op_swap_r; + opcode_table_cb[0x36] = &CPU::op_swap_hl; + opcode_table_cb[0x37] = &CPU::op_swap_r; + opcode_table_cb[0x38] = &CPU::op_srl_r; + opcode_table_cb[0x39] = &CPU::op_srl_r; + opcode_table_cb[0x3a] = &CPU::op_srl_r; + opcode_table_cb[0x3b] = &CPU::op_srl_r; + opcode_table_cb[0x3c] = &CPU::op_srl_r; + opcode_table_cb[0x3d] = &CPU::op_srl_r; + opcode_table_cb[0x3e] = &CPU::op_srl_hl; + opcode_table_cb[0x3f] = &CPU::op_srl_r; + opcode_table_cb[0x40] = &CPU::op_bit_n_r<0, B>; + opcode_table_cb[0x41] = &CPU::op_bit_n_r<0, C>; + opcode_table_cb[0x42] = &CPU::op_bit_n_r<0, D>; + opcode_table_cb[0x43] = &CPU::op_bit_n_r<0, E>; + opcode_table_cb[0x44] = &CPU::op_bit_n_r<0, H>; + opcode_table_cb[0x45] = &CPU::op_bit_n_r<0, L>; + opcode_table_cb[0x46] = &CPU::op_bit_n_hl<0>; + opcode_table_cb[0x47] = &CPU::op_bit_n_r<0, A>; + opcode_table_cb[0x48] = &CPU::op_bit_n_r<1, B>; + opcode_table_cb[0x49] = &CPU::op_bit_n_r<1, C>; + opcode_table_cb[0x4a] = &CPU::op_bit_n_r<1, D>; + opcode_table_cb[0x4b] = &CPU::op_bit_n_r<1, E>; + opcode_table_cb[0x4c] = &CPU::op_bit_n_r<1, H>; + opcode_table_cb[0x4d] = &CPU::op_bit_n_r<1, L>; + opcode_table_cb[0x4e] = &CPU::op_bit_n_hl<1>; + opcode_table_cb[0x4f] = &CPU::op_bit_n_r<1, A>; + opcode_table_cb[0x50] = &CPU::op_bit_n_r<2, B>; + opcode_table_cb[0x51] = &CPU::op_bit_n_r<2, C>; + opcode_table_cb[0x52] = &CPU::op_bit_n_r<2, D>; + opcode_table_cb[0x53] = &CPU::op_bit_n_r<2, E>; + opcode_table_cb[0x54] = &CPU::op_bit_n_r<2, H>; + opcode_table_cb[0x55] = &CPU::op_bit_n_r<2, L>; + opcode_table_cb[0x56] = &CPU::op_bit_n_hl<2>; + opcode_table_cb[0x57] = &CPU::op_bit_n_r<2, A>; + opcode_table_cb[0x58] = &CPU::op_bit_n_r<3, B>; + opcode_table_cb[0x59] = &CPU::op_bit_n_r<3, C>; + opcode_table_cb[0x5a] = &CPU::op_bit_n_r<3, D>; + opcode_table_cb[0x5b] = &CPU::op_bit_n_r<3, E>; + opcode_table_cb[0x5c] = &CPU::op_bit_n_r<3, H>; + opcode_table_cb[0x5d] = &CPU::op_bit_n_r<3, L>; + opcode_table_cb[0x5e] = &CPU::op_bit_n_hl<3>; + opcode_table_cb[0x5f] = &CPU::op_bit_n_r<3, A>; + opcode_table_cb[0x60] = &CPU::op_bit_n_r<4, B>; + opcode_table_cb[0x61] = &CPU::op_bit_n_r<4, C>; + opcode_table_cb[0x62] = &CPU::op_bit_n_r<4, D>; + opcode_table_cb[0x63] = &CPU::op_bit_n_r<4, E>; + opcode_table_cb[0x64] = &CPU::op_bit_n_r<4, H>; + opcode_table_cb[0x65] = &CPU::op_bit_n_r<4, L>; + opcode_table_cb[0x66] = &CPU::op_bit_n_hl<4>; + opcode_table_cb[0x67] = &CPU::op_bit_n_r<4, A>; + opcode_table_cb[0x68] = &CPU::op_bit_n_r<5, B>; + opcode_table_cb[0x69] = &CPU::op_bit_n_r<5, C>; + opcode_table_cb[0x6a] = &CPU::op_bit_n_r<5, D>; + opcode_table_cb[0x6b] = &CPU::op_bit_n_r<5, E>; + opcode_table_cb[0x6c] = &CPU::op_bit_n_r<5, H>; + opcode_table_cb[0x6d] = &CPU::op_bit_n_r<5, L>; + opcode_table_cb[0x6e] = &CPU::op_bit_n_hl<5>; + opcode_table_cb[0x6f] = &CPU::op_bit_n_r<5, A>; + opcode_table_cb[0x70] = &CPU::op_bit_n_r<6, B>; + opcode_table_cb[0x71] = &CPU::op_bit_n_r<6, C>; + opcode_table_cb[0x72] = &CPU::op_bit_n_r<6, D>; + opcode_table_cb[0x73] = &CPU::op_bit_n_r<6, E>; + opcode_table_cb[0x74] = &CPU::op_bit_n_r<6, H>; + opcode_table_cb[0x75] = &CPU::op_bit_n_r<6, L>; + opcode_table_cb[0x76] = &CPU::op_bit_n_hl<6>; + opcode_table_cb[0x77] = &CPU::op_bit_n_r<6, A>; + opcode_table_cb[0x78] = &CPU::op_bit_n_r<7, B>; + opcode_table_cb[0x79] = &CPU::op_bit_n_r<7, C>; + opcode_table_cb[0x7a] = &CPU::op_bit_n_r<7, D>; + opcode_table_cb[0x7b] = &CPU::op_bit_n_r<7, E>; + opcode_table_cb[0x7c] = &CPU::op_bit_n_r<7, H>; + opcode_table_cb[0x7d] = &CPU::op_bit_n_r<7, L>; + opcode_table_cb[0x7e] = &CPU::op_bit_n_hl<7>; + opcode_table_cb[0x7f] = &CPU::op_bit_n_r<7, A>; + opcode_table_cb[0x80] = &CPU::op_res_n_r<0, B>; + opcode_table_cb[0x81] = &CPU::op_res_n_r<0, C>; + opcode_table_cb[0x82] = &CPU::op_res_n_r<0, D>; + opcode_table_cb[0x83] = &CPU::op_res_n_r<0, E>; + opcode_table_cb[0x84] = &CPU::op_res_n_r<0, H>; + opcode_table_cb[0x85] = &CPU::op_res_n_r<0, L>; + opcode_table_cb[0x86] = &CPU::op_res_n_hl<0>; + opcode_table_cb[0x87] = &CPU::op_res_n_r<0, A>; + opcode_table_cb[0x88] = &CPU::op_res_n_r<1, B>; + opcode_table_cb[0x89] = &CPU::op_res_n_r<1, C>; + opcode_table_cb[0x8a] = &CPU::op_res_n_r<1, D>; + opcode_table_cb[0x8b] = &CPU::op_res_n_r<1, E>; + opcode_table_cb[0x8c] = &CPU::op_res_n_r<1, H>; + opcode_table_cb[0x8d] = &CPU::op_res_n_r<1, L>; + opcode_table_cb[0x8e] = &CPU::op_res_n_hl<1>; + opcode_table_cb[0x8f] = &CPU::op_res_n_r<1, A>; + opcode_table_cb[0x90] = &CPU::op_res_n_r<2, B>; + opcode_table_cb[0x91] = &CPU::op_res_n_r<2, C>; + opcode_table_cb[0x92] = &CPU::op_res_n_r<2, D>; + opcode_table_cb[0x93] = &CPU::op_res_n_r<2, E>; + opcode_table_cb[0x94] = &CPU::op_res_n_r<2, H>; + opcode_table_cb[0x95] = &CPU::op_res_n_r<2, L>; + opcode_table_cb[0x96] = &CPU::op_res_n_hl<2>; + opcode_table_cb[0x97] = &CPU::op_res_n_r<2, A>; + opcode_table_cb[0x98] = &CPU::op_res_n_r<3, B>; + opcode_table_cb[0x99] = &CPU::op_res_n_r<3, C>; + opcode_table_cb[0x9a] = &CPU::op_res_n_r<3, D>; + opcode_table_cb[0x9b] = &CPU::op_res_n_r<3, E>; + opcode_table_cb[0x9c] = &CPU::op_res_n_r<3, H>; + opcode_table_cb[0x9d] = &CPU::op_res_n_r<3, L>; + opcode_table_cb[0x9e] = &CPU::op_res_n_hl<3>; + opcode_table_cb[0x9f] = &CPU::op_res_n_r<3, A>; + opcode_table_cb[0xa0] = &CPU::op_res_n_r<4, B>; + opcode_table_cb[0xa1] = &CPU::op_res_n_r<4, C>; + opcode_table_cb[0xa2] = &CPU::op_res_n_r<4, D>; + opcode_table_cb[0xa3] = &CPU::op_res_n_r<4, E>; + opcode_table_cb[0xa4] = &CPU::op_res_n_r<4, H>; + opcode_table_cb[0xa5] = &CPU::op_res_n_r<4, L>; + opcode_table_cb[0xa6] = &CPU::op_res_n_hl<4>; + opcode_table_cb[0xa7] = &CPU::op_res_n_r<4, A>; + opcode_table_cb[0xa8] = &CPU::op_res_n_r<5, B>; + opcode_table_cb[0xa9] = &CPU::op_res_n_r<5, C>; + opcode_table_cb[0xaa] = &CPU::op_res_n_r<5, D>; + opcode_table_cb[0xab] = &CPU::op_res_n_r<5, E>; + opcode_table_cb[0xac] = &CPU::op_res_n_r<5, H>; + opcode_table_cb[0xad] = &CPU::op_res_n_r<5, L>; + opcode_table_cb[0xae] = &CPU::op_res_n_hl<5>; + opcode_table_cb[0xaf] = &CPU::op_res_n_r<5, A>; + opcode_table_cb[0xb0] = &CPU::op_res_n_r<6, B>; + opcode_table_cb[0xb1] = &CPU::op_res_n_r<6, C>; + opcode_table_cb[0xb2] = &CPU::op_res_n_r<6, D>; + opcode_table_cb[0xb3] = &CPU::op_res_n_r<6, E>; + opcode_table_cb[0xb4] = &CPU::op_res_n_r<6, H>; + opcode_table_cb[0xb5] = &CPU::op_res_n_r<6, L>; + opcode_table_cb[0xb6] = &CPU::op_res_n_hl<6>; + opcode_table_cb[0xb7] = &CPU::op_res_n_r<6, A>; + opcode_table_cb[0xb8] = &CPU::op_res_n_r<7, B>; + opcode_table_cb[0xb9] = &CPU::op_res_n_r<7, C>; + opcode_table_cb[0xba] = &CPU::op_res_n_r<7, D>; + opcode_table_cb[0xbb] = &CPU::op_res_n_r<7, E>; + opcode_table_cb[0xbc] = &CPU::op_res_n_r<7, H>; + opcode_table_cb[0xbd] = &CPU::op_res_n_r<7, L>; + opcode_table_cb[0xbe] = &CPU::op_res_n_hl<7>; + opcode_table_cb[0xbf] = &CPU::op_res_n_r<7, A>; + opcode_table_cb[0xc0] = &CPU::op_set_n_r<0, B>; + opcode_table_cb[0xc1] = &CPU::op_set_n_r<0, C>; + opcode_table_cb[0xc2] = &CPU::op_set_n_r<0, D>; + opcode_table_cb[0xc3] = &CPU::op_set_n_r<0, E>; + opcode_table_cb[0xc4] = &CPU::op_set_n_r<0, H>; + opcode_table_cb[0xc5] = &CPU::op_set_n_r<0, L>; + opcode_table_cb[0xc6] = &CPU::op_set_n_hl<0>; + opcode_table_cb[0xc7] = &CPU::op_set_n_r<0, A>; + opcode_table_cb[0xc8] = &CPU::op_set_n_r<1, B>; + opcode_table_cb[0xc9] = &CPU::op_set_n_r<1, C>; + opcode_table_cb[0xca] = &CPU::op_set_n_r<1, D>; + opcode_table_cb[0xcb] = &CPU::op_set_n_r<1, E>; + opcode_table_cb[0xcc] = &CPU::op_set_n_r<1, H>; + opcode_table_cb[0xcd] = &CPU::op_set_n_r<1, L>; + opcode_table_cb[0xce] = &CPU::op_set_n_hl<1>; + opcode_table_cb[0xcf] = &CPU::op_set_n_r<1, A>; + opcode_table_cb[0xd0] = &CPU::op_set_n_r<2, B>; + opcode_table_cb[0xd1] = &CPU::op_set_n_r<2, C>; + opcode_table_cb[0xd2] = &CPU::op_set_n_r<2, D>; + opcode_table_cb[0xd3] = &CPU::op_set_n_r<2, E>; + opcode_table_cb[0xd4] = &CPU::op_set_n_r<2, H>; + opcode_table_cb[0xd5] = &CPU::op_set_n_r<2, L>; + opcode_table_cb[0xd6] = &CPU::op_set_n_hl<2>; + opcode_table_cb[0xd7] = &CPU::op_set_n_r<2, A>; + opcode_table_cb[0xd8] = &CPU::op_set_n_r<3, B>; + opcode_table_cb[0xd9] = &CPU::op_set_n_r<3, C>; + opcode_table_cb[0xda] = &CPU::op_set_n_r<3, D>; + opcode_table_cb[0xdb] = &CPU::op_set_n_r<3, E>; + opcode_table_cb[0xdc] = &CPU::op_set_n_r<3, H>; + opcode_table_cb[0xdd] = &CPU::op_set_n_r<3, L>; + opcode_table_cb[0xde] = &CPU::op_set_n_hl<3>; + opcode_table_cb[0xdf] = &CPU::op_set_n_r<3, A>; + opcode_table_cb[0xe0] = &CPU::op_set_n_r<4, B>; + opcode_table_cb[0xe1] = &CPU::op_set_n_r<4, C>; + opcode_table_cb[0xe2] = &CPU::op_set_n_r<4, D>; + opcode_table_cb[0xe3] = &CPU::op_set_n_r<4, E>; + opcode_table_cb[0xe4] = &CPU::op_set_n_r<4, H>; + opcode_table_cb[0xe5] = &CPU::op_set_n_r<4, L>; + opcode_table_cb[0xe6] = &CPU::op_set_n_hl<4>; + opcode_table_cb[0xe7] = &CPU::op_set_n_r<4, A>; + opcode_table_cb[0xe8] = &CPU::op_set_n_r<5, B>; + opcode_table_cb[0xe9] = &CPU::op_set_n_r<5, C>; + opcode_table_cb[0xea] = &CPU::op_set_n_r<5, D>; + opcode_table_cb[0xeb] = &CPU::op_set_n_r<5, E>; + opcode_table_cb[0xec] = &CPU::op_set_n_r<5, H>; + opcode_table_cb[0xed] = &CPU::op_set_n_r<5, L>; + opcode_table_cb[0xee] = &CPU::op_set_n_hl<5>; + opcode_table_cb[0xef] = &CPU::op_set_n_r<5, A>; + opcode_table_cb[0xf0] = &CPU::op_set_n_r<6, B>; + opcode_table_cb[0xf1] = &CPU::op_set_n_r<6, C>; + opcode_table_cb[0xf2] = &CPU::op_set_n_r<6, D>; + opcode_table_cb[0xf3] = &CPU::op_set_n_r<6, E>; + opcode_table_cb[0xf4] = &CPU::op_set_n_r<6, H>; + opcode_table_cb[0xf5] = &CPU::op_set_n_r<6, L>; + opcode_table_cb[0xf6] = &CPU::op_set_n_hl<6>; + opcode_table_cb[0xf7] = &CPU::op_set_n_r<6, A>; + opcode_table_cb[0xf8] = &CPU::op_set_n_r<7, B>; + opcode_table_cb[0xf9] = &CPU::op_set_n_r<7, C>; + opcode_table_cb[0xfa] = &CPU::op_set_n_r<7, D>; + opcode_table_cb[0xfb] = &CPU::op_set_n_r<7, E>; + opcode_table_cb[0xfc] = &CPU::op_set_n_r<7, H>; + opcode_table_cb[0xfd] = &CPU::op_set_n_r<7, L>; + opcode_table_cb[0xfe] = &CPU::op_set_n_hl<7>; + opcode_table_cb[0xff] = &CPU::op_set_n_r<7, A>; +} + +#endif diff --git a/libsnes/gameboy/cpu/cpu.hpp b/libsnes/gameboy/cpu/cpu.hpp new file mode 100755 index 0000000..edbab41 --- /dev/null +++ b/libsnes/gameboy/cpu/cpu.hpp @@ -0,0 +1,107 @@ +struct CPU : Processor, MMIO { + #include "core/core.hpp" + #include "mmio/mmio.hpp" + #include "timing/timing.hpp" + + bool trace; + + struct Interrupt { + enum e { + Vblank, + Stat, + Timer, + Serial, + Joypad, + } i; + }; + + struct Status { + unsigned clock; + bool halt; + bool stop; + bool ei; + bool ime; + + //$ff00 JOYP + bool p15; + bool p14; + uint8 joyp; + uint8 mlt_req; + + //$ff01 SB + uint8 serial_data; + unsigned serial_bits; + + //$ff02 SC + bool serial_transfer; + bool serial_clock; + + //$ff04 DIV + uint8 div; + + //$ff05 TIMA + uint8 tima; + + //$ff06 TMA + uint8 tma; + + //$ff07 TAC + bool timer_enable; + unsigned timer_clock; + + //$ff0f IF + bool interrupt_request_joypad; + bool interrupt_request_serial; + bool interrupt_request_timer; + bool interrupt_request_stat; + bool interrupt_request_vblank; + + //$ff4d KEY1 + bool speed_double; + bool speed_switch; + + //$ff51,$ff52 HDMA1,HDMA2 + uint16 dma_source; + + //$ff53,$ff54 HDMA3,HDMA4 + uint16 dma_target; + + //$ff55 HDMA5 + bool dma_mode; + uint16 dma_length; + + //$ff6c ??? + uint8 ff6c; + + //$ff70 SVBK + uint3 wram_bank; + + //$ff72-$ff75 ??? + uint8 ff72; + uint8 ff73; + uint8 ff74; + uint8 ff75; + + //$ffff IE + bool interrupt_enable_joypad; + bool interrupt_enable_serial; + bool interrupt_enable_timer; + bool interrupt_enable_stat; + bool interrupt_enable_vblank; + } status; + + uint8 wram[32768]; //GB=8192, GBC=32768 + uint8 hram[128]; + + static void Main(); + void main(); + void interrupt_raise(Interrupt::e id); + void interrupt_test(); + void interrupt_exec(uint16 pc); + void power(); + + void serialize(serializer&); + CPU(); +}; + +extern CPU cpu; diff --git a/libsnes/gameboy/cpu/cpu_gb.cpp b/libsnes/gameboy/cpu/cpu_gb.cpp new file mode 100644 index 0000000..0d5a3c3 --- /dev/null +++ b/libsnes/gameboy/cpu/cpu_gb.cpp @@ -0,0 +1,202 @@ +#include + +#define CPU_CPP +namespace GameBoy { + +#include "core/core.cpp" +#include "mmio/mmio.cpp" +#include "timing/timing.cpp" +#include "serialization.cpp" +CPU cpu; + +void CPU::Main() { + cpu.main(); +} + +void CPU::main() { + while(true) { + if(scheduler.sync.i == Scheduler::SynchronizeMode::CPU) { + scheduler.sync.i = Scheduler::SynchronizeMode::All; + scheduler.exit(Scheduler::ExitReason::SynchronizeEvent); + } + + //if(trace) print(disassemble(r[PC]), "\n"); + interrupt_test(); + uint8 opcode = op_read(r[PC]++); + (this->*opcode_table[opcode])(); + } +} + +void CPU::interrupt_raise(CPU::Interrupt::e id) { + if(id == Interrupt::Vblank) { + status.interrupt_request_vblank = 1; + if(status.interrupt_enable_vblank) status.halt = false; + } + + if(id == Interrupt::Stat) { + status.interrupt_request_stat = 1; + if(status.interrupt_enable_stat) status.halt = false; + } + + if(id == Interrupt::Timer) { + status.interrupt_request_timer = 1; + if(status.interrupt_enable_timer) status.halt = false; + } + + if(id == Interrupt::Serial) { + status.interrupt_request_serial = 1; + if(status.interrupt_enable_serial) status.halt = false; + } + + if(id == Interrupt::Joypad) { + status.interrupt_request_joypad = 1; + if(status.interrupt_enable_joypad) status.halt = status.stop = false; + } +} + +void CPU::interrupt_test() { + if(status.ime) { + if(status.interrupt_request_vblank && status.interrupt_enable_vblank) { + status.interrupt_request_vblank = 0; + return interrupt_exec(0x0040); + } + + if(status.interrupt_request_stat && status.interrupt_enable_stat) { + status.interrupt_request_stat = 0; + return interrupt_exec(0x0048); + } + + if(status.interrupt_request_timer && status.interrupt_enable_timer) { + status.interrupt_request_timer = 0; + return interrupt_exec(0x0050); + } + + if(status.interrupt_request_serial && status.interrupt_enable_serial) { + status.interrupt_request_serial = 0; + return interrupt_exec(0x0058); + } + + if(status.interrupt_request_joypad && status.interrupt_enable_joypad) { + status.interrupt_request_joypad = 0; + return interrupt_exec(0x0060); + } + } +} + +void CPU::interrupt_exec(uint16 pc) { + status.ime = 0; + op_write(--r[SP], r[PC] >> 8); + op_write(--r[SP], r[PC] >> 0); + r[PC] = pc; + op_io(); + op_io(); + op_io(); +} + +void CPU::power() { + create(Main, 4 * 1024 * 1024); + + for(unsigned n = 0xc000; n <= 0xdfff; n++) bus.mmio[n] = this; //WRAM + for(unsigned n = 0xe000; n <= 0xfdff; n++) bus.mmio[n] = this; //WRAM (mirror) + for(unsigned n = 0xff80; n <= 0xfffe; n++) bus.mmio[n] = this; //HRAM + + bus.mmio[0xff00] = this; //JOYP + bus.mmio[0xff01] = this; //SB + bus.mmio[0xff02] = this; //SC + bus.mmio[0xff04] = this; //DIV + bus.mmio[0xff05] = this; //TIMA + bus.mmio[0xff06] = this; //TMA + bus.mmio[0xff07] = this; //TAC + bus.mmio[0xff0f] = this; //IF + bus.mmio[0xff46] = this; //DMA + bus.mmio[0xffff] = this; //IE + + if(system.cgb()) { + bus.mmio[0xff4d] = this; //KEY1 + bus.mmio[0xff51] = this; //HDMA1 + bus.mmio[0xff52] = this; //HDMA2 + bus.mmio[0xff53] = this; //HDMA3 + bus.mmio[0xff54] = this; //HDMA4 + bus.mmio[0xff55] = this; //HDMA5 + bus.mmio[0xff56] = this; //RP + bus.mmio[0xff6c] = this; //??? + bus.mmio[0xff70] = this; //SVBK + bus.mmio[0xff72] = this; //??? + bus.mmio[0xff73] = this; //??? + bus.mmio[0xff74] = this; //??? + bus.mmio[0xff75] = this; //??? + bus.mmio[0xff76] = this; //??? + bus.mmio[0xff77] = this; //??? + } + + foreach(n, wram) n = 0x00; + foreach(n, hram) n = 0x00; + + r[PC] = 0x0000; + r[SP] = 0x0000; + r[AF] = 0x0000; + r[BC] = 0x0000; + r[DE] = 0x0000; + r[HL] = 0x0000; + + status.clock = 0; + status.halt = false; + status.stop = false; + status.ei = false; + status.ime = 0; + + status.p15 = 0; + status.p14 = 0; + status.joyp = 0; + status.mlt_req = 0; + + status.serial_data = 0; + status.serial_bits = 0; + + status.serial_transfer = 0; + status.serial_clock = 0; + + status.div = 0; + + status.tima = 0; + + status.tma = 0; + + status.timer_enable = 0; + status.timer_clock = 0; + + status.interrupt_request_joypad = 0; + status.interrupt_request_serial = 0; + status.interrupt_request_timer = 0; + status.interrupt_request_stat = 0; + status.interrupt_request_vblank = 0; + + status.speed_double = 0; + status.speed_switch = 0; + + status.dma_source = 0; + status.dma_target = 0; + + status.dma_mode = 0; + status.dma_length = 0; + + status.ff6c = 0; + status.ff72 = 0; + status.ff73 = 0; + status.ff74 = 0; + status.ff75 = 0; + + status.wram_bank = 1; + + status.interrupt_enable_joypad = 0; + status.interrupt_enable_serial = 0; + status.interrupt_enable_timer = 0; + status.interrupt_enable_stat = 0; + status.interrupt_enable_vblank = 0; +} + +CPU::CPU() : trace(false) { + initialize_opcode_table(); +} + +} diff --git a/libsnes/gameboy/cpu/mmio/mmio.cpp b/libsnes/gameboy/cpu/mmio/mmio.cpp new file mode 100755 index 0000000..61e7f3d --- /dev/null +++ b/libsnes/gameboy/cpu/mmio/mmio.cpp @@ -0,0 +1,271 @@ +#ifdef CPU_CPP + +unsigned CPU::wram_addr(uint16 addr) const { + addr &= 0x1fff; + if(addr < 0x1000) return addr; + unsigned bank = status.wram_bank + (status.wram_bank == 0); + return (bank * 0x1000) + (addr & 0x0fff); +} + +void CPU::mmio_joyp_poll() { + unsigned button = 0, dpad = 0; + + button |= interface->inputPoll((unsigned)Input::Start) << 3; + button |= interface->inputPoll((unsigned)Input::Select) << 2; + button |= interface->inputPoll((unsigned)Input::B) << 1; + button |= interface->inputPoll((unsigned)Input::A) << 0; + + dpad |= interface->inputPoll((unsigned)Input::Down) << 3; + dpad |= interface->inputPoll((unsigned)Input::Up) << 2; + dpad |= interface->inputPoll((unsigned)Input::Left) << 1; + dpad |= interface->inputPoll((unsigned)Input::Right) << 0; + + status.joyp = 0x0f; + if(status.p15 == 1 && status.p14 == 1) status.joyp -= status.mlt_req; + if(status.p15 == 0) status.joyp &= button ^ 0x0f; + if(status.p14 == 0) status.joyp &= dpad ^ 0x0f; + if(status.joyp != 0x0f) interrupt_raise(Interrupt::Joypad); +} + +uint8 CPU::mmio_read(uint16 addr) { + if(addr >= 0xc000 && addr <= 0xfdff) return wram[wram_addr(addr)]; + if(addr >= 0xff80 && addr <= 0xfffe) return hram[addr & 0x7f]; + + if(addr == 0xff00) { //JOYP + return (status.p15 << 5) + | (status.p14 << 4) + | (status.joyp << 0); + } + + if(addr == 0xff01) { //SB + return 0xff; + } + + if(addr == 0xff02) { //SC + return (status.serial_transfer << 7) + | (status.serial_clock << 0); + } + + if(addr == 0xff04) { //DIV + return status.div; + } + + if(addr == 0xff05) { //TIMA + return status.tima; + } + + if(addr == 0xff06) { //TMA + return status.tma; + } + + if(addr == 0xff07) { //TAC + return (status.timer_enable << 2) + | (status.timer_clock << 0); + } + + if(addr == 0xff0f) { //IF + return (status.interrupt_request_joypad << 4) + | (status.interrupt_request_serial << 3) + | (status.interrupt_request_timer << 2) + | (status.interrupt_request_stat << 1) + | (status.interrupt_request_vblank << 0); + } + + if(addr == 0xff4d) { //KEY1 + return (status.speed_double << 7); + } + + if(addr == 0xff55) { //HDMA5 + return (status.dma_length / 16) - 1; + } + + if(addr == 0xff56) { //RP + return 0x02; + } + + if(addr == 0xff6c) { //??? + return 0xfe | status.ff6c; + } + + if(addr == 0xff70) { //SVBK + return status.wram_bank; + } + + if(addr == 0xff72) { //??? + return status.ff72; + } + + if(addr == 0xff73) { //??? + return status.ff73; + } + + if(addr == 0xff74) { //??? + return status.ff74; + } + + if(addr == 0xff75) { //??? + return 0x8f | status.ff75; + } + + if(addr == 0xff76) { //??? + return 0x00; + } + + if(addr == 0xff77) { //??? + return 0x00; + } + + if(addr == 0xffff) { //IE + return (status.interrupt_enable_joypad << 4) + | (status.interrupt_enable_serial << 3) + | (status.interrupt_enable_timer << 2) + | (status.interrupt_enable_stat << 1) + | (status.interrupt_enable_vblank << 0); + } + + return 0x00; +} + +void CPU::mmio_write(uint16 addr, uint8 data) { + if(addr >= 0xc000 && addr <= 0xfdff) { wram[wram_addr(addr)] = data; return; } + if(addr >= 0xff80 && addr <= 0xfffe) { hram[addr & 0x7f] = data; return; } + + if(addr == 0xff00) { //JOYP + status.p15 = data & 0x20; + status.p14 = data & 0x10; + interface->joypWrite(status.p15, status.p14); + mmio_joyp_poll(); + return; + } + + if(addr == 0xff01) { //SB + status.serial_data = data; + return; + } + + if(addr == 0xff02) { //SC + status.serial_transfer = data & 0x80; + status.serial_clock = data & 0x01; + if(status.serial_transfer) status.serial_bits = 8; + return; + } + + if(addr == 0xff04) { //DIV + status.div = 0; + return; + } + + if(addr == 0xff05) { //TIMA + status.tima = data; + return; + } + + if(addr == 0xff06) { //TMA + status.tma = data; + return; + } + + if(addr == 0xff07) { //TAC + status.timer_enable = data & 0x04; + status.timer_clock = data & 0x03; + return; + } + + if(addr == 0xff0f) { //IF + status.interrupt_request_joypad = data & 0x10; + status.interrupt_request_serial = data & 0x08; + status.interrupt_request_timer = data & 0x04; + status.interrupt_request_stat = data & 0x02; + status.interrupt_request_vblank = data & 0x01; + return; + } + + if(addr == 0xff46) { //DMA + for(unsigned n = 0x00; n <= 0x9f; n++) { + bus.write(0xfe00 + n, bus.read((data << 8) + n)); + add_clocks(4); + } + return; + } + + if(addr == 0xff4d) { //KEY1 + status.speed_switch = data & 0x01; + return; + } + + if(addr == 0xff51) { //HDMA1 + status.dma_source = (status.dma_source & 0x00ff) | (data << 8); + return; + } + + if(addr == 0xff52) { //HDMA2 + status.dma_source = (status.dma_source & 0xff00) | (data << 0); + return; + } + + if(addr == 0xff53) { //HDMA3 + status.dma_target = (status.dma_target & 0x00ff) | (data << 8); + return; + } + + if(addr == 0xff54) { //HDMA4 + status.dma_target = (status.dma_target & 0xff00) | (data << 0); + return; + } + + if(addr == 0xff55) { //HDMA5 + status.dma_mode = data & 0x80; + status.dma_length = ((data & 0x7f) + 1) * 16; + + if(status.dma_mode == 0) do { + bus.write(status.dma_target++, bus.read(status.dma_source++)); + add_clocks(4 << status.speed_double); + } while(--status.dma_length); + return; + } + + if(addr == 0xff56) { //RP + return; + } + + if(addr == 0xff6c) { //??? + status.ff6c = data & 0x01; + return; + } + + if(addr == 0xff72) { //??? + status.ff72 = data; + return; + } + + if(addr == 0xff73) { //??? + status.ff73 = data; + return; + } + + if(addr == 0xff74) { //??? + status.ff74 = data; + return; + } + + if(addr == 0xff75) { //??? + status.ff75 = data & 0x70; + return; + } + + if(addr == 0xff70) { //SVBK + status.wram_bank = data & 0x07; + return; + } + + if(addr == 0xffff) { //IE + status.interrupt_enable_joypad = data & 0x10; + status.interrupt_enable_serial = data & 0x08; + status.interrupt_enable_timer = data & 0x04; + status.interrupt_enable_stat = data & 0x02; + status.interrupt_enable_vblank = data & 0x01; + return; + } +} + +#endif diff --git a/libsnes/gameboy/cpu/mmio/mmio.hpp b/libsnes/gameboy/cpu/mmio/mmio.hpp new file mode 100755 index 0000000..1440899 --- /dev/null +++ b/libsnes/gameboy/cpu/mmio/mmio.hpp @@ -0,0 +1,4 @@ +unsigned wram_addr(uint16 addr) const; +void mmio_joyp_poll(); +uint8 mmio_read(uint16 addr); +void mmio_write(uint16 addr, uint8 data); diff --git a/libsnes/gameboy/cpu/serialization.cpp b/libsnes/gameboy/cpu/serialization.cpp new file mode 100755 index 0000000..5a2b8d5 --- /dev/null +++ b/libsnes/gameboy/cpu/serialization.cpp @@ -0,0 +1,76 @@ +#ifdef CPU_CPP + +void CPU::serialize(serializer &s) { + Processor::serialize(s); + + s.array(wram); + s.array(hram); + + s.integer(r.a.data); + s.integer(r.f.z); + s.integer(r.f.n); + s.integer(r.f.h); + s.integer(r.f.c); + s.integer(r.b.data); + s.integer(r.c.data); + s.integer(r.d.data); + s.integer(r.e.data); + s.integer(r.h.data); + s.integer(r.l.data); + s.integer(r.sp.data); + s.integer(r.pc.data); + + s.integer(status.clock); + s.integer(status.halt); + s.integer(status.stop); + s.integer(status.ei); + s.integer(status.ime); + + s.integer(status.p15); + s.integer(status.p14); + s.integer(status.joyp); + s.integer(status.mlt_req); + + s.integer(status.serial_data); + s.integer(status.serial_bits); + + s.integer(status.serial_transfer); + s.integer(status.serial_clock); + + s.integer(status.div); + s.integer(status.tima); + s.integer(status.tma); + s.integer(status.timer_enable); + s.integer(status.timer_clock); + + s.integer(status.interrupt_request_joypad); + s.integer(status.interrupt_request_serial); + s.integer(status.interrupt_request_timer); + s.integer(status.interrupt_request_stat); + s.integer(status.interrupt_request_vblank); + + s.integer(status.speed_double); + s.integer(status.speed_switch); + + s.integer(status.dma_source); + s.integer(status.dma_target); + s.integer(status.dma_mode); + s.integer(status.dma_length); + + s.integer(status.ff6c); + + s.integer(status.wram_bank); + + s.integer(status.ff72); + s.integer(status.ff73); + s.integer(status.ff74); + s.integer(status.ff75); + + s.integer(status.interrupt_enable_joypad); + s.integer(status.interrupt_enable_serial); + s.integer(status.interrupt_enable_timer); + s.integer(status.interrupt_enable_stat); + s.integer(status.interrupt_enable_vblank); +} + +#endif diff --git a/libsnes/gameboy/cpu/timing/opcode.cpp b/libsnes/gameboy/cpu/timing/opcode.cpp new file mode 100755 index 0000000..5774670 --- /dev/null +++ b/libsnes/gameboy/cpu/timing/opcode.cpp @@ -0,0 +1,28 @@ +#ifdef CPU_CPP + +void CPU::op_io() { + cycle_edge(); + add_clocks(4); +} + +uint8 CPU::op_read(uint16 addr) { + cycle_edge(); + uint8 r = bus.read(addr); + add_clocks(4); + return r; +} + +void CPU::op_write(uint16 addr, uint8 data) { + cycle_edge(); + bus.write(addr, data); + add_clocks(4); +} + +void CPU::cycle_edge() { + if(status.ei) { + status.ei = false; + status.ime = 1; + } +} + +#endif diff --git a/libsnes/gameboy/cpu/timing/timing.cpp b/libsnes/gameboy/cpu/timing/timing.cpp new file mode 100755 index 0000000..cc5e3ab --- /dev/null +++ b/libsnes/gameboy/cpu/timing/timing.cpp @@ -0,0 +1,90 @@ +//70224 clocks/frame +// 456 clocks/scanline +// 154 scanlines/frame + +#ifdef CPU_CPP + +#include "opcode.cpp" + +void CPU::add_clocks(unsigned clocks) { + system.clocks_executed += clocks; + if(system.sgb()) scheduler.exit(Scheduler::ExitReason::StepEvent); + + status.clock += clocks; + if(status.clock >= 4 * 1024 * 1024) { + status.clock -= 4 * 1024 * 1024; + cartridge.mbc3.second(); + } + + //4MHz / N(hz) - 1 = mask + if((status.clock & 15) == 0) timer_262144hz(); + if((status.clock & 63) == 0) timer_65536hz(); + if((status.clock & 255) == 0) timer_16384hz(); + if((status.clock & 511) == 0) timer_8192hz(); + if((status.clock & 1023) == 0) timer_4096hz(); + + lcd.clock -= clocks * lcd.frequency; + if(lcd.clock <= 0) co_switch(scheduler.active_thread = lcd.thread); + + apu.clock -= clocks * apu.frequency; + if(apu.clock <= 0) co_switch(scheduler.active_thread = apu.thread); +} + +void CPU::timer_262144hz() { + if(status.timer_enable && status.timer_clock == 1) { + if(++status.tima == 0) { + status.tima = status.tma; + interrupt_raise(Interrupt::Timer); + } + } +} + +void CPU::timer_65536hz() { + if(status.timer_enable && status.timer_clock == 2) { + if(++status.tima == 0) { + status.tima = status.tma; + interrupt_raise(Interrupt::Timer); + } + } +} + +void CPU::timer_16384hz() { + if(status.timer_enable && status.timer_clock == 3) { + if(++status.tima == 0) { + status.tima = status.tma; + interrupt_raise(Interrupt::Timer); + } + } + + status.div++; +} + +void CPU::timer_8192hz() { + if(status.serial_transfer && status.serial_clock) { + if(--status.serial_bits == 0) { + status.serial_transfer = 0; + interrupt_raise(Interrupt::Serial); + } + } +} + +void CPU::timer_4096hz() { + if(status.timer_enable && status.timer_clock == 0) { + if(++status.tima == 0) { + status.tima = status.tma; + interrupt_raise(Interrupt::Timer); + } + } +} + +void CPU::hblank() { + if(status.dma_mode == 1 && status.dma_length) { + for(unsigned n = 0; n < 16; n++) { + bus.write(status.dma_target++, bus.read(status.dma_source++)); + add_clocks(4); + } + status.dma_length -= 16; + } +} + +#endif diff --git a/libsnes/gameboy/cpu/timing/timing.hpp b/libsnes/gameboy/cpu/timing/timing.hpp new file mode 100755 index 0000000..6c16c47 --- /dev/null +++ b/libsnes/gameboy/cpu/timing/timing.hpp @@ -0,0 +1,13 @@ +void add_clocks(unsigned clocks); +void timer_262144hz(); +void timer_65536hz(); +void timer_16384hz(); +void timer_8192hz(); +void timer_4096hz(); +void hblank(); + +//opcode.cpp +void op_io(); +uint8 op_read(uint16 addr); +void op_write(uint16 addr, uint8 data); +void cycle_edge(); diff --git a/libsnes/gameboy/gameboy.hpp b/libsnes/gameboy/gameboy.hpp new file mode 100755 index 0000000..6497063 --- /dev/null +++ b/libsnes/gameboy/gameboy.hpp @@ -0,0 +1,107 @@ +#ifndef GAMEBOY_HPP +#define GAMEBOY_HPP + +namespace GameBoy { + namespace Info { + static const char Name[] = "bgameboy"; + static const unsigned SerializerVersion = 3; + } +} + +/* + bgameboy - Game Boy, Super Game Boy, and Game Boy Color emulator + author: byuu + license: GPLv3 + project started: 2010-12-27 +*/ + +#include + +#include +#include +#include +#include +#include +#include +#include +using namespace nall; + +namespace GameBoy { + 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 uint_t< 1> uint1; + typedef uint_t< 2> uint2; + typedef uint_t< 3> uint3; + typedef uint_t< 4> uint4; + typedef uint_t< 5> uint5; + typedef uint_t< 6> uint6; + typedef uint_t< 7> uint7; + + typedef uint_t< 9> uint9; + typedef uint_t<10> uint10; + typedef uint_t<11> uint11; + typedef uint_t<12> uint12; + typedef uint_t<13> uint13; + typedef uint_t<14> uint14; + typedef uint_t<15> uint15; + + typedef uint_t<17> uint17; + typedef uint_t<18> uint18; + typedef uint_t<19> uint19; + typedef uint_t<20> uint20; + typedef uint_t<21> uint21; + typedef uint_t<22> uint22; + typedef uint_t<23> uint23; + typedef uint_t<24> uint24; + typedef uint_t<25> uint25; + typedef uint_t<26> uint26; + typedef uint_t<27> uint27; + typedef uint_t<28> uint28; + typedef uint_t<29> uint29; + typedef uint_t<30> uint30; + typedef uint_t<31> uint31; + + struct Processor { + cothread_t thread; + unsigned frequency; + int64 clock; + + inline void create(void (*entrypoint)(), unsigned frequency) { + if(thread) co_delete(thread); + thread = co_create(65536 * sizeof(void*), entrypoint); + this->frequency = frequency; + clock = 0; + } + + inline void serialize(serializer &s) { + s.integer(frequency); + s.integer(clock); + } + + inline Processor() : thread(0) {} + + inline ~Processor() { + if (thread) co_delete(thread); + } + }; + + #include + #include + #include + #include + #include + #include + #include + #include + #include +}; + +#endif diff --git a/libsnes/gameboy/interface/interface.hpp b/libsnes/gameboy/interface/interface.hpp new file mode 100755 index 0000000..918e606 --- /dev/null +++ b/libsnes/gameboy/interface/interface.hpp @@ -0,0 +1,12 @@ +struct Interface { + virtual void lcdScanline(); + virtual void joypWrite(bool p15, bool p14); + + virtual void videoRefresh(const uint16_t *data); + virtual void audioSample(int16_t center, int16_t left, int16_t right); + virtual bool inputPoll(unsigned id); + + virtual void message(const string &text); +}; + +extern Interface *interface; diff --git a/libsnes/gameboy/interface/interface_gb.cpp b/libsnes/gameboy/interface/interface_gb.cpp new file mode 100644 index 0000000..c480eec --- /dev/null +++ b/libsnes/gameboy/interface/interface_gb.cpp @@ -0,0 +1,27 @@ +#include + +namespace GameBoy { + +Interface *interface = 0; + +void Interface::lcdScanline() { +} + +void Interface::joypWrite(bool p15, bool p14) { +} + +void Interface::videoRefresh(const uint16_t *data) { +} + +void Interface::audioSample(int16_t center, int16_t left, int16_t right) { +} + +bool Interface::inputPoll(unsigned id) { + return false; +} + +void Interface::message(const string &text) { + print(text, "\n"); +} + +} diff --git a/libsnes/gameboy/lcd/cgb.cpp b/libsnes/gameboy/lcd/cgb.cpp new file mode 100644 index 0000000..31892ca --- /dev/null +++ b/libsnes/gameboy/lcd/cgb.cpp @@ -0,0 +1,185 @@ +#ifdef LCD_CPP + +void LCD::cgb_render() { + for(unsigned n = 0; n < 160; n++) { + line[n] = 0x7fff; + origin[n] = Origin::None; + } + + if(status.display_enable) { + cgb_render_bg(); + if(status.window_display_enable) cgb_render_window(); + if(status.ob_enable) cgb_render_ob(); + } + + uint16 *output = screen + status.ly * 160; + for(unsigned n = 0; n < 160; n++) output[n] = line[n]; + interface->lcdScanline(); +} + +//Attributes: +//0x80: 0 = OAM priority, 1 = BG priority +//0x40: vertical flip +//0x20: horizontal flip +//0x08: VRAM bank# +//0x07: palette# +void LCD::cgb_read_tile(bool select, unsigned x, unsigned y, unsigned &tile, unsigned &attr, unsigned &data) { + unsigned tmaddr = 0x1800 + (select << 10); + tmaddr += (((y >> 3) << 5) + (x >> 3)) & 0x03ff; + + tile = vram[0x0000 + tmaddr]; + attr = vram[0x2000 + tmaddr]; + + unsigned tdaddr = attr & 0x08 ? 0x2000 : 0x0000; + if(status.bg_tiledata_select == 0) { + tdaddr += 0x1000 + ((int8)tile << 4); + } else { + tdaddr += 0x0000 + (tile << 4); + } + + y &= 7; + if(attr & 0x40) y ^= 7; + tdaddr += y << 1; + + data = vram[tdaddr++] << 0; + data |= vram[tdaddr++] << 8; + if(attr & 0x20) data = hflip(data); +} + +void LCD::cgb_render_bg() { + unsigned iy = (status.ly + status.scy) & 255; + unsigned ix = status.scx, tx = ix & 7; + + unsigned tile, attr, data; + cgb_read_tile(status.bg_tilemap_select, ix, iy, tile, attr, data); + + for(unsigned ox = 0; ox < 160; ox++) { + unsigned index = ((data & (0x0080 >> tx)) ? 1 : 0) + | ((data & (0x8000 >> tx)) ? 2 : 0); + unsigned palette_index = ((attr & 0x07) << 3) + (index << 1); + unsigned palette = 0; + palette |= bgpd[palette_index++] << 0; + palette |= bgpd[palette_index++] << 8; + palette &= 0x7fff; + + line[ox] = palette; + origin[ox] = (attr & 0x80 ? Origin::BGP : Origin::BG); + + ix = (ix + 1) & 255; + tx = (tx + 1) & 7; + if(tx == 0) cgb_read_tile(status.bg_tilemap_select, ix, iy, tile, attr, data); + } +} + +void LCD::cgb_render_window() { + if(status.ly - status.wy >= 144u) return; + if(status.wx >= 167u) return; + unsigned iy = status.wyc++; + unsigned ix = (7 - status.wx) & 255, tx = ix & 7; + + unsigned tile, attr, data; + cgb_read_tile(status.window_tilemap_select, ix, iy, tile, attr, data); + + for(unsigned ox = 0; ox < 160; ox++) { + unsigned index = ((data & (0x0080 >> tx)) ? 1 : 0) + | ((data & (0x8000 >> tx)) ? 2 : 0); + unsigned palette_index = ((attr & 0x07) << 3) + (index << 1); + unsigned palette = 0; + palette |= bgpd[palette_index++] << 0; + palette |= bgpd[palette_index++] << 8; + palette &= 0x7fff; + + if(ox - (status.wx - 7) < 160u) { + line[ox] = palette; + origin[ox] = (attr & 0x80 ? Origin::BGP : Origin::BG); + } + + ix = (ix + 1) & 255; + tx = (tx + 1) & 7; + if(tx == 0) cgb_read_tile(status.window_tilemap_select, ix, iy, tile, attr, data); + } +} + +//Attributes: +//0x80: 0 = OBJ above BG, 1 = BG above OBJ +//0x40: vertical flip +//0x20: horizontal flip +//0x08: VRAM bank# +//0x07: palette# +void LCD::cgb_render_ob() { + const unsigned Height = (status.ob_size == 0 ? 8 : 16); + unsigned sprite[10], sprites = 0; + + //find first ten sprites on this scanline + for(unsigned s = 0; s < 40; s++) { + unsigned sy = oam[(s << 2) + 0] - 16; + unsigned sx = oam[(s << 2) + 1] - 8; + + sy = status.ly - sy; + if(sy >= Height) continue; + + sprite[sprites++] = s; + if(sprites == 10) break; + } + + //sort by X-coordinate, when equal, lower address comes first + for(unsigned x = 0; x < sprites; x++) { + for(unsigned y = x + 1; y < sprites; y++) { + signed sx = oam[(sprite[x] << 2) + 1] - 8; + signed sy = oam[(sprite[y] << 2) + 1] - 8; + if(sy < sx) { + sprite[x] ^= sprite[y]; + sprite[y] ^= sprite[x]; + sprite[x] ^= sprite[y]; + } + } + } + + //render backwards, so that first sprite has highest priority + for(signed s = sprites - 1; s >= 0; s--) { + unsigned n = sprite[s] << 2; + unsigned sy = oam[n + 0] - 16; + unsigned sx = oam[n + 1] - 8; + unsigned tile = oam[n + 2] & ~status.ob_size; + unsigned attr = oam[n + 3]; + + sy = status.ly - sy; + if(sy >= Height) continue; + if(attr & 0x40) sy ^= (Height - 1); + + unsigned tdaddr = (attr & 0x08 ? 0x2000 : 0x0000) + (tile << 4) + (sy << 1), data = 0; + data |= vram[tdaddr++] << 0; + data |= vram[tdaddr++] << 8; + if(attr & 0x20) data = hflip(data); + + for(unsigned tx = 0; tx < 8; tx++) { + unsigned index = ((data & (0x0080 >> tx)) ? 1 : 0) + | ((data & (0x8000 >> tx)) ? 2 : 0); + if(index == 0) continue; + + unsigned palette_index = ((attr & 0x07) << 3) + (index << 1); + unsigned palette = 0; + palette |= obpd[palette_index++] << 0; + palette |= obpd[palette_index++] << 8; + palette &= 0x7fff; + + unsigned ox = sx + tx; + + if(ox < 160) { + //When LCDC.D0 (BG enable) is off, OB is always rendered above BG+Window + if(status.bg_enable) { + if(origin[ox] == Origin::BGP) continue; + if(attr & 0x80) { + if(origin[ox] == Origin::BG || origin[ox] == Origin::BGP) { + if(line[ox] > 0) continue; + } + } + } + line[ox] = palette; + origin[ox] = Origin::OB; + } + } + } +} + +#endif diff --git a/libsnes/gameboy/lcd/dmg.cpp b/libsnes/gameboy/lcd/dmg.cpp new file mode 100644 index 0000000..dae518a --- /dev/null +++ b/libsnes/gameboy/lcd/dmg.cpp @@ -0,0 +1,145 @@ +#ifdef LCD_CPP + +void LCD::dmg_render() { + for(unsigned n = 0; n < 160; n++) { + line[n] = 0x00; + origin[n] = Origin::None; + } + + if(status.display_enable) { + if(status.bg_enable) dmg_render_bg(); + if(status.window_display_enable) dmg_render_window(); + if(status.ob_enable) dmg_render_ob(); + } + + uint16 *output = screen + status.ly * 160; + for(unsigned n = 0; n < 160; n++) output[n] = line[n]; + interface->lcdScanline(); +} + +uint16 LCD::dmg_read_tile(bool select, unsigned x, unsigned y) { + unsigned tmaddr = 0x1800 + (select << 10), tdaddr; + tmaddr += (((y >> 3) << 5) + (x >> 3)) & 0x03ff; + if(status.bg_tiledata_select == 0) { + tdaddr = 0x1000 + ((int8)vram[tmaddr] << 4); + } else { + tdaddr = 0x0000 + (vram[tmaddr] << 4); + } + tdaddr += (y & 7) << 1; + return (vram[tdaddr + 0] << 0) | (vram[tdaddr + 1] << 8); +} + +void LCD::dmg_render_bg() { + unsigned iy = (status.ly + status.scy) & 255; + unsigned ix = status.scx, tx = ix & 7; + unsigned data = dmg_read_tile(status.bg_tilemap_select, ix, iy); + + for(unsigned ox = 0; ox < 160; ox++) { + uint8 palette = ((data & (0x0080 >> tx)) ? 1 : 0) + | ((data & (0x8000 >> tx)) ? 2 : 0); + + line[ox] = bgp[palette]; + origin[ox] = Origin::BG; + + ix = (ix + 1) & 255; + tx = (tx + 1) & 7; + + if(tx == 0) data = dmg_read_tile(status.bg_tilemap_select, ix, iy); + } +} + +void LCD::dmg_render_window() { + if(status.ly - status.wy >= 144u) return; + if(status.wx >= 167u) return; + unsigned iy = status.wyc++; + unsigned ix = (7 - status.wx) & 255, tx = ix & 7; + unsigned data = dmg_read_tile(status.window_tilemap_select, ix, iy); + + for(unsigned ox = 0; ox < 160; ox++) { + uint8 palette = ((data & (0x0080 >> tx)) ? 1 : 0) + | ((data & (0x8000 >> tx)) ? 2 : 0); + if(ox - (status.wx - 7) < 160u) { + line[ox] = bgp[palette]; + origin[ox] = Origin::BG; + } + + ix = (ix + 1) & 255; + tx = (tx + 1) & 7; + + if(tx == 0) data = dmg_read_tile(status.window_tilemap_select, ix, iy); + } +} + +//Attributes: +//0x80: 0 = OBJ above BG, 1 = BG above OBJ +//0x40: vertical flip +//0x20: horizontal flip +//0x10: palette# +void LCD::dmg_render_ob() { + const unsigned Height = (status.ob_size == 0 ? 8 : 16); + unsigned sprite[10], sprites = 0; + + //find first ten sprites on this scanline + for(unsigned s = 0; s < 40; s++) { + unsigned sy = oam[(s << 2) + 0] - 16; + unsigned sx = oam[(s << 2) + 1] - 8; + + sy = status.ly - sy; + if(sy >= Height) continue; + + sprite[sprites++] = s; + if(sprites == 10) break; + } + + //sort by X-coordinate, when equal, lower address comes first + for(unsigned x = 0; x < sprites; x++) { + for(unsigned y = x + 1; y < sprites; y++) { + signed sx = oam[(sprite[x] << 2) + 1] - 8; + signed sy = oam[(sprite[y] << 2) + 1] - 8; + if(sy < sx) { + sprite[x] ^= sprite[y]; + sprite[y] ^= sprite[x]; + sprite[x] ^= sprite[y]; + } + } + } + + //render backwards, so that first sprite has highest priority + for(signed s = sprites - 1; s >= 0; s--) { + unsigned n = sprite[s] << 2; + unsigned sy = oam[n + 0] - 16; + unsigned sx = oam[n + 1] - 8; + unsigned tile = oam[n + 2] & ~status.ob_size; + unsigned attr = oam[n + 3]; + + sy = status.ly - sy; + if(sy >= Height) continue; + if(attr & 0x40) sy ^= (Height - 1); + + unsigned tdaddr = (tile << 4) + (sy << 1), data = 0; + data |= vram[tdaddr++] << 0; + data |= vram[tdaddr++] << 8; + if(attr & 0x20) data = hflip(data); + + for(unsigned tx = 0; tx < 8; tx++) { + uint8 palette = ((data & (0x0080 >> tx)) ? 1 : 0) + | ((data & (0x8000 >> tx)) ? 2 : 0); + if(palette == 0) continue; + + palette = obp[(bool)(attr & 0x10)][palette]; + unsigned ox = sx + tx; + + if(ox < 160) { + if(attr & 0x80) { + if(origin[ox] == Origin::BG) { + if(line[ox] > 0) continue; + } + } + line[ox] = palette; + origin[ox] = Origin::OB; + } + } + } +} + +#endif diff --git a/libsnes/gameboy/lcd/lcd.hpp b/libsnes/gameboy/lcd/lcd.hpp new file mode 100755 index 0000000..e95af19 --- /dev/null +++ b/libsnes/gameboy/lcd/lcd.hpp @@ -0,0 +1,94 @@ +struct LCD : Processor, MMIO { + #include "mmio/mmio.hpp" + + struct Status { + unsigned lx; + unsigned wyc; + + //$ff40 LCDC + bool display_enable; + bool window_tilemap_select; + bool window_display_enable; + bool bg_tiledata_select; + bool bg_tilemap_select; + bool ob_size; + bool ob_enable; + bool bg_enable; + + //$ff41 STAT + bool interrupt_lyc; + bool interrupt_oam; + bool interrupt_vblank; + bool interrupt_hblank; + + //$ff42 SCY + uint8 scy; + + //$ff43 SCX + uint8 scx; + + //$ff44 LY + uint8 ly; + + //$ff45 LYC + uint8 lyc; + + //$ff4a WY + uint8 wy; + + //$ff4b WX + uint8 wx; + + //$ff4f VBK + bool vram_bank; + + //$ff68 BGPI + bool bgpi_increment; + uint6 bgpi; + + //$ff6a OBPI + bool obpi_increment; + uint8 obpi; + } status; + + uint16 screen[160 * 144]; + uint16 line[160]; + struct Origin { enum { None, BG, BGP, OB }; }; + uint8 origin[160]; + + uint8 vram[16384]; //GB = 8192, GBC = 16384 + uint8 oam[160]; + uint8 bgp[4]; + uint8 obp[2][4]; + uint8 bgpd[64]; + uint8 obpd[64]; + + static void Main(); + void main(); + void add_clocks(unsigned clocks); + void scanline(); + void frame(); + + unsigned hflip(unsigned data) const; + + //dmg.cpp + void dmg_render(); + uint16 dmg_read_tile(bool select, unsigned x, unsigned y); + void dmg_render_bg(); + void dmg_render_window(); + void dmg_render_ob(); + + //cgb.cpp + void cgb_render(); + void cgb_read_tile(bool select, unsigned x, unsigned y, unsigned &tile, unsigned &attr, unsigned &data); + void cgb_render_bg(); + void cgb_render_window(); + void cgb_render_ob(); + + void power(); + + void serialize(serializer&); + LCD(); +}; + +extern LCD lcd; diff --git a/libsnes/gameboy/lcd/lcd_gb.cpp b/libsnes/gameboy/lcd/lcd_gb.cpp new file mode 100644 index 0000000..8518421 --- /dev/null +++ b/libsnes/gameboy/lcd/lcd_gb.cpp @@ -0,0 +1,158 @@ +#include + +//LY = 0-153 +//Raster = 0-143 +//Vblank = 144-153 + +//LX = 0-455 + +#define LCD_CPP +namespace GameBoy { + +#include "dmg.cpp" +#include "cgb.cpp" +#include "mmio/mmio.cpp" +#include "serialization.cpp" +LCD lcd; + +void LCD::Main() { + lcd.main(); +} + +void LCD::main() { + while(true) { + if(scheduler.sync.i == Scheduler::SynchronizeMode::All) { + scheduler.exit(Scheduler::ExitReason::SynchronizeEvent); + } + + add_clocks(4); + status.lx += 4; + if(status.lx >= 456) scanline(); + + if(status.display_enable && status.lx == 0) { + if(status.interrupt_oam) cpu.interrupt_raise(CPU::Interrupt::Stat); + } + + if(status.display_enable && status.lx == 252) { + if(status.interrupt_hblank) cpu.interrupt_raise(CPU::Interrupt::Stat); + cpu.hblank(); + } + } +} + +void LCD::add_clocks(unsigned clocks) { + clock += clocks * cpu.frequency; + if(clock >= 0 && scheduler.sync.i != Scheduler::SynchronizeMode::All) { + co_switch(scheduler.active_thread = cpu.thread); + } +} + +void LCD::scanline() { + status.lx -= 456; + if(++status.ly == 154) frame(); + + if(status.display_enable && status.interrupt_lyc == true) { + if(status.ly == status.lyc) cpu.interrupt_raise(CPU::Interrupt::Stat); + } + + if(status.ly < 144) { + system.cgb() == false ? dmg_render() : cgb_render(); + } + + if(status.display_enable && status.ly == 144) { + cpu.interrupt_raise(CPU::Interrupt::Vblank); + if(status.interrupt_vblank) cpu.interrupt_raise(CPU::Interrupt::Stat); + } +} + +void LCD::frame() { + interface->videoRefresh(screen); + cpu.mmio_joyp_poll(); + + status.ly = 0; + status.wyc = 0; + scheduler.exit(Scheduler::ExitReason::FrameEvent); +} + +unsigned LCD::hflip(unsigned data) const { + return ((data & 0x8080) >> 7) | ((data & 0x4040) >> 5) + | ((data & 0x2020) >> 3) | ((data & 0x1010) >> 1) + | ((data & 0x0808) << 1) | ((data & 0x0404) << 3) + | ((data & 0x0202) << 5) | ((data & 0x0101) << 7); +} + +void LCD::power() { + create(Main, 4 * 1024 * 1024); + + for(unsigned n = 0x8000; n <= 0x9fff; n++) bus.mmio[n] = this; //VRAM + for(unsigned n = 0xfe00; n <= 0xfe9f; n++) bus.mmio[n] = this; //OAM + + bus.mmio[0xff40] = this; //LCDC + bus.mmio[0xff41] = this; //STAT + bus.mmio[0xff42] = this; //SCY + bus.mmio[0xff43] = this; //SCX + bus.mmio[0xff44] = this; //LY + bus.mmio[0xff45] = this; //LYC + bus.mmio[0xff47] = this; //BGP + bus.mmio[0xff48] = this; //OBP0 + bus.mmio[0xff49] = this; //OBP1 + bus.mmio[0xff4a] = this; //WY + bus.mmio[0xff4b] = this; //WX + + if(system.cgb()) { + bus.mmio[0xff4f] = this; //VBK + bus.mmio[0xff68] = this; //BGPI + bus.mmio[0xff69] = this; //BGPD + bus.mmio[0xff6a] = this; //OBPI + bus.mmio[0xff6b] = this; //OBPD + } + + foreach(n, screen) n = 0x0000; + foreach(n, line) n = 0x0000; + foreach(n, origin) n = Origin::None; + + foreach(n, vram) n = 0x00; + foreach(n, oam) n = 0x00; + foreach(n, bgp) n = 0x00; + foreach(n, obp[0]) n = 0x00; + foreach(n, obp[1]) n = 0x00; + foreach(n, bgpd) n = 0x0000; + foreach(n, obpd) n = 0x0000; + + status.lx = 0; + status.wyc = 0; + + status.display_enable = 0; + status.window_tilemap_select = 0; + status.window_display_enable = 0; + status.bg_tiledata_select = 0; + status.bg_tilemap_select = 0; + status.ob_size = 0; + status.ob_enable = 0; + status.bg_enable = 0; + + status.interrupt_lyc = 0; + status.interrupt_oam = 0; + status.interrupt_vblank = 0; + status.interrupt_hblank = 0; + + status.scy = 0; + status.scx = 0; + status.ly = 0; + status.lyc = 0; + status.wy = 0; + status.wx = 0; + + status.vram_bank = 0; + + status.bgpi_increment = 0; + status.bgpi = 0; + + status.obpi_increment = 0; + status.obpi = 0; +} + +LCD::LCD() { +} + +} diff --git a/libsnes/gameboy/lcd/mmio/mmio.cpp b/libsnes/gameboy/lcd/mmio/mmio.cpp new file mode 100755 index 0000000..5b20fe7 --- /dev/null +++ b/libsnes/gameboy/lcd/mmio/mmio.cpp @@ -0,0 +1,203 @@ +#ifdef LCD_CPP + +unsigned LCD::vram_addr(uint16 addr) const { + return (status.vram_bank * 0x2000) + (addr & 0x1fff); +} + +uint8 LCD::mmio_read(uint16 addr) { + if(addr >= 0x8000 && addr <= 0x9fff) return vram[vram_addr(addr)]; + if(addr >= 0xfe00 && addr <= 0xfe9f) return oam[addr & 0xff]; + + if(addr == 0xff40) { //LCDC + return (status.display_enable << 7) + | (status.window_tilemap_select << 6) + | (status.window_display_enable << 5) + | (status.bg_tiledata_select << 4) + | (status.bg_tilemap_select << 3) + | (status.ob_size << 2) + | (status.ob_enable << 1) + | (status.bg_enable << 0); + } + + if(addr == 0xff41) { //STAT + unsigned mode; + if(status.ly >= 144) mode = 1; //Vblank + else if(status.lx < 80) mode = 2; //OAM + else if(status.lx < 252) mode = 3; //LCD + else mode = 0; //Hblank + + return (status.interrupt_lyc << 6) + | (status.interrupt_oam << 5) + | (status.interrupt_vblank << 4) + | (status.interrupt_hblank << 3) + | ((status.ly == status.lyc) << 2) + | (mode << 0); + } + + if(addr == 0xff42) { //SCY + return status.scy; + } + + if(addr == 0xff43) { //SCX + return status.scx; + } + + if(addr == 0xff44) { //LY + return status.ly; + } + + if(addr == 0xff45) { //LYC + return status.lyc; + } + + if(addr == 0xff47) { //BGP + return (bgp[3] << 6) + | (bgp[2] << 4) + | (bgp[1] << 2) + | (bgp[0] << 0); + } + + if(addr == 0xff48) { //OBP0 + return (obp[0][3] << 6) + | (obp[0][2] << 4) + | (obp[0][1] << 2) + | (obp[0][0] << 0); + } + + if(addr == 0xff49) { //OBP1 + return (obp[1][3] << 6) + | (obp[1][2] << 4) + | (obp[1][1] << 2) + | (obp[1][0] << 0); + } + + if(addr == 0xff4a) { //WY + return status.wy; + } + + if(addr == 0xff4b) { //WX + return status.wx; + } + + if(addr == 0xff69) { //BGPD + return bgpd[status.bgpi]; + } + + if(addr == 0xff6b) { //OBPD + return obpd[status.obpi]; + } + + return 0x00; +} + +void LCD::mmio_write(uint16 addr, uint8 data) { + if(addr >= 0x8000 && addr <= 0x9fff) { vram[vram_addr(addr)] = data; return; } + if(addr >= 0xfe00 && addr <= 0xfe9f) { oam[addr & 0xff] = data; return; } + + if(addr == 0xff40) { //LCDC + if(status.display_enable == false && (data & 0x80)) { + status.lx = 0; //unverified behavior; fixes Super Mario Land 2 - Tree Zone + } + + status.display_enable = data & 0x80; + status.window_tilemap_select = data & 0x40; + status.window_display_enable = data & 0x20; + status.bg_tiledata_select = data & 0x10; + status.bg_tilemap_select = data & 0x08; + status.ob_size = data & 0x04; + status.ob_enable = data & 0x02; + status.bg_enable = data & 0x01; + return; + } + + if(addr == 0xff41) { //STAT + status.interrupt_lyc = data & 0x40; + status.interrupt_oam = data & 0x20; + status.interrupt_vblank = data & 0x10; + status.interrupt_hblank = data & 0x08; + return; + } + + if(addr == 0xff42) { //SCY + status.scy = data; + return; + } + + if(addr == 0xff43) { //SCX + status.scx = data; + return; + } + + if(addr == 0xff44) { //LY + status.ly = 0; + return; + } + + if(addr == 0xff45) { //LYC + status.lyc = data; + return; + } + + if(addr == 0xff47) { //BGP + bgp[3] = (data >> 6) & 3; + bgp[2] = (data >> 4) & 3; + bgp[1] = (data >> 2) & 3; + bgp[0] = (data >> 0) & 3; + return; + } + + if(addr == 0xff48) { //OBP0 + obp[0][3] = (data >> 6) & 3; + obp[0][2] = (data >> 4) & 3; + obp[0][1] = (data >> 2) & 3; + obp[0][0] = (data >> 0) & 3; + return; + } + + if(addr == 0xff49) { //OBP1 + obp[1][3] = (data >> 6) & 3; + obp[1][2] = (data >> 4) & 3; + obp[1][1] = (data >> 2) & 3; + obp[1][0] = (data >> 0) & 3; + return; + } + + if(addr == 0xff4a) { //WY + status.wy = data; + return; + } + + if(addr == 0xff4b) { //WX + status.wx = data; + return; + } + + if(addr == 0xff4f) { //VBK + status.vram_bank = data & 1; + return; + } + + if(addr == 0xff68) { //BGPI + status.bgpi_increment = data & 0x80; + status.bgpi = data & 0x3f; + return; + } + + if(addr == 0xff69) { //BGPD + bgpd[status.bgpi] = data; + if(status.bgpi_increment) status.bgpi++; + return; + } + + if(addr == 0xff6a) { //OBPI + status.obpi_increment = data & 0x80; + status.obpi = data & 0x3f; + } + + if(addr == 0xff6b) { //OBPD + obpd[status.obpi] = data; + if(status.obpi_increment) status.obpi++; + } +} + +#endif diff --git a/libsnes/gameboy/lcd/mmio/mmio.hpp b/libsnes/gameboy/lcd/mmio/mmio.hpp new file mode 100755 index 0000000..d02d755 --- /dev/null +++ b/libsnes/gameboy/lcd/mmio/mmio.hpp @@ -0,0 +1,3 @@ +unsigned vram_addr(uint16 addr) const; +uint8 mmio_read(uint16 addr); +void mmio_write(uint16 addr, uint8 data); diff --git a/libsnes/gameboy/lcd/serialization.cpp b/libsnes/gameboy/lcd/serialization.cpp new file mode 100755 index 0000000..df4752a --- /dev/null +++ b/libsnes/gameboy/lcd/serialization.cpp @@ -0,0 +1,53 @@ +#ifdef LCD_CPP + +void LCD::serialize(serializer &s) { + Processor::serialize(s); + + s.array(screen); + s.array(line); + s.array(origin); + + s.array(vram); + s.array(oam); + s.array(bgp); + s.array(obp[0]); + s.array(obp[1]); + s.array(bgpd); + s.array(obpd); + + s.integer(status.lx); + s.integer(status.wyc); + + s.integer(status.display_enable); + s.integer(status.window_tilemap_select); + s.integer(status.window_display_enable); + s.integer(status.bg_tiledata_select); + s.integer(status.bg_tilemap_select); + s.integer(status.ob_size); + s.integer(status.ob_enable); + s.integer(status.bg_enable); + + s.integer(status.interrupt_lyc); + s.integer(status.interrupt_oam); + s.integer(status.interrupt_vblank); + s.integer(status.interrupt_hblank); + + s.integer(status.scy); + s.integer(status.scx); + + s.integer(status.ly); + s.integer(status.lyc); + + s.integer(status.wy); + s.integer(status.wx); + + s.integer(status.vram_bank); + + s.integer(status.bgpi_increment); + s.integer(status.bgpi); + + s.integer(status.obpi_increment); + s.integer(status.obpi); +} + +#endif diff --git a/libsnes/gameboy/memory/memory.hpp b/libsnes/gameboy/memory/memory.hpp new file mode 100755 index 0000000..3dfb8df --- /dev/null +++ b/libsnes/gameboy/memory/memory.hpp @@ -0,0 +1,32 @@ +struct Memory { + uint8_t *data; + unsigned size; + + uint8_t& operator[](unsigned addr); + void allocate(unsigned size); + void copy(const uint8_t *data, unsigned size); + void free(); + Memory(); + ~Memory(); +}; + +struct MMIO { + virtual uint8 mmio_read(uint16 addr) = 0; + virtual void mmio_write(uint16 addr, uint8 data) = 0; +}; + +struct Unmapped : MMIO { + uint8 mmio_read(uint16) { return 0x00; } + void mmio_write(uint16, uint8) {} +}; + +struct Bus { + MMIO *mmio[65536]; + uint8 read(uint16 addr); + void write(uint16 addr, uint8 data); + + void power(); +}; + +extern Unmapped unmapped; +extern Bus bus; diff --git a/libsnes/gameboy/memory/memory_gb.cpp b/libsnes/gameboy/memory/memory_gb.cpp new file mode 100644 index 0000000..ad28184 --- /dev/null +++ b/libsnes/gameboy/memory/memory_gb.cpp @@ -0,0 +1,69 @@ +#include + +#define MEMORY_CPP +namespace GameBoy { + +Unmapped unmapped; +Bus bus; + +uint8_t& Memory::operator[](unsigned addr) { + return data[addr]; +} + +void Memory::allocate(unsigned size_) { + free(); + size = size_; + data = new uint8_t[size](); +} + +void Memory::copy(const uint8_t *data_, unsigned size_) { + free(); + size = size_; + data = new uint8_t[size]; + memcpy(data, data_, size); +} + +void Memory::free() { + if(data) { + delete[] data; + data = 0; + } +} + +Memory::Memory() { + data = 0; + size = 0; +} + +Memory::~Memory() { + free(); +} + +// + +uint8 Bus::read(uint16 addr) { + uint8 data = mmio[addr]->mmio_read(addr); + + if(cheat.override[addr]) { + for(unsigned n = 0; n < cheat.size(); n++) { + if(cheat[n].addr == addr) { + if(cheat[n].comp > 255 || cheat[n].comp == data) { + data = cheat[n].data; + break; + } + } + } + } + + return data; +} + +void Bus::write(uint16 addr, uint8 data) { + mmio[addr]->mmio_write(addr, data); +} + +void Bus::power() { + for(unsigned n = 0x0000; n <= 0xffff; n++) mmio[n] = &unmapped; +} + +} diff --git a/libsnes/gameboy/scheduler/scheduler.hpp b/libsnes/gameboy/scheduler/scheduler.hpp new file mode 100755 index 0000000..e0b8769 --- /dev/null +++ b/libsnes/gameboy/scheduler/scheduler.hpp @@ -0,0 +1,27 @@ +struct Scheduler : property { + struct SynchronizeMode { + enum e { + None, CPU, All + } i; + } sync; + + struct ExitReason { + enum e { + UnknownEvent, StepEvent, FrameEvent, SynchronizeEvent + } i; + }; + + ExitReason exit_reason; + + cothread_t host_thread; + cothread_t active_thread; + + void enter(); + void exit(ExitReason::e); + void swapto(Processor&); + + void init(); + Scheduler(); +}; + +extern Scheduler scheduler; diff --git a/libsnes/gameboy/scheduler/scheduler_gb.cpp b/libsnes/gameboy/scheduler/scheduler_gb.cpp new file mode 100644 index 0000000..88611a8 --- /dev/null +++ b/libsnes/gameboy/scheduler/scheduler_gb.cpp @@ -0,0 +1,35 @@ +#include + +#define SCHEDULER_CPP +namespace GameBoy { + +Scheduler scheduler; + +void Scheduler::enter() { + host_thread = co_active(); + co_switch(active_thread); +} + +void Scheduler::exit(ExitReason::e reason) { + exit_reason.i = reason; + active_thread = co_active(); + co_switch(host_thread); +} + +void Scheduler::swapto(Processor &p) { + active_thread = p.thread; + co_switch(active_thread); +} + +void Scheduler::init() { + host_thread = co_active(); + active_thread = cpu.thread; +} + +Scheduler::Scheduler() { + exit_reason.i = ExitReason::UnknownEvent; + host_thread = 0; + active_thread = 0; +} + +} diff --git a/libsnes/gameboy/system/bootrom-cgb.cpp b/libsnes/gameboy/system/bootrom-cgb.cpp new file mode 100644 index 0000000..5d69bd0 --- /dev/null +++ b/libsnes/gameboy/system/bootrom-cgb.cpp @@ -0,0 +1,135 @@ +#ifdef SYSTEM_CPP + +//SHA256 = 4bf5021be357ce523a59ac5f4efff5d6371ae50112a6db0adf4a75916ad760a9 +const uint8_t System::BootROM::cgb[2048] = { + 0x31,0xfe,0xff,0x3e,0x02,0xc3,0x7c,0x00,0xd3,0x00,0x98,0xa0,0x12,0xd3,0x00,0x80, + 0x00,0x40,0x1e,0x53,0xd0,0x00,0x1f,0x42,0x1c,0x00,0x14,0x2a,0x4d,0x19,0x8c,0x7e, + 0x00,0x7c,0x31,0x6e,0x4a,0x45,0x52,0x4a,0x00,0x00,0xff,0x53,0x1f,0x7c,0xff,0x03, + 0x1f,0x00,0xff,0x1f,0xa7,0x00,0xef,0x1b,0x1f,0x00,0xef,0x1b,0x00,0x7c,0x00,0x00, + 0xff,0x03,0xce,0xed,0x66,0x66,0xcc,0x0d,0x00,0x0b,0x03,0x73,0x00,0x83,0x00,0x0c, + 0x00,0x0d,0x00,0x08,0x11,0x1f,0x88,0x89,0x00,0x0e,0xdc,0xcc,0x6e,0xe6,0xdd,0xdd, + 0xd9,0x99,0xbb,0xbb,0x67,0x63,0x6e,0x0e,0xec,0xcc,0xdd,0xdc,0x99,0x9f,0xbb,0xb9, + 0x33,0x3e,0x3c,0x42,0xb9,0xa5,0xb9,0xa5,0x42,0x3c,0x58,0x43,0xe0,0x70,0x3e,0xfc, + 0xe0,0x47,0xcd,0x75,0x02,0xcd,0x00,0x02,0x26,0xd0,0xcd,0x03,0x02,0x21,0x00,0xfe, + 0x0e,0xa0,0xaf,0x22,0x0d,0x20,0xfc,0x11,0x04,0x01,0x21,0x10,0x80,0x4c,0x1a,0xe2, + 0x0c,0xcd,0xc6,0x03,0xcd,0xc7,0x03,0x13,0x7b,0xfe,0x34,0x20,0xf1,0x11,0x72,0x00, + 0x06,0x08,0x1a,0x13,0x22,0x23,0x05,0x20,0xf9,0xcd,0xf0,0x03,0x3e,0x01,0xe0,0x4f, + 0x3e,0x91,0xe0,0x40,0x21,0xb2,0x98,0x06,0x4e,0x0e,0x44,0xcd,0x91,0x02,0xaf,0xe0, + 0x4f,0x0e,0x80,0x21,0x42,0x00,0x06,0x18,0xf2,0x0c,0xbe,0x20,0xfe,0x23,0x05,0x20, + 0xf7,0x21,0x34,0x01,0x06,0x19,0x78,0x86,0x2c,0x05,0x20,0xfb,0x86,0x20,0xfe,0xcd, + 0x1c,0x03,0x18,0x02,0x00,0x00,0xcd,0xd0,0x05,0xaf,0xe0,0x70,0x3e,0x11,0xe0,0x50, + 0x21,0x00,0x80,0xaf,0x22,0xcb,0x6c,0x28,0xfb,0xc9,0x2a,0x12,0x13,0x0d,0x20,0xfa, + 0xc9,0xe5,0x21,0x0f,0xff,0xcb,0x86,0xcb,0x46,0x28,0xfc,0xe1,0xc9,0x11,0x00,0xff, + 0x21,0x03,0xd0,0x0e,0x0f,0x3e,0x30,0x12,0x3e,0x20,0x12,0x1a,0x2f,0xa1,0xcb,0x37, + 0x47,0x3e,0x10,0x12,0x1a,0x2f,0xa1,0xb0,0x4f,0x7e,0xa9,0xe6,0xf0,0x47,0x2a,0xa9, + 0xa1,0xb0,0x32,0x47,0x79,0x77,0x3e,0x30,0x12,0xc9,0x3e,0x80,0xe0,0x68,0xe0,0x6a, + 0x0e,0x6b,0x2a,0xe2,0x05,0x20,0xfb,0x4a,0x09,0x43,0x0e,0x69,0x2a,0xe2,0x05,0x20, + 0xfb,0xc9,0xc5,0xd5,0xe5,0x21,0x00,0xd8,0x06,0x01,0x16,0x3f,0x1e,0x40,0xcd,0x4a, + 0x02,0xe1,0xd1,0xc1,0xc9,0x3e,0x80,0xe0,0x26,0xe0,0x11,0x3e,0xf3,0xe0,0x12,0xe0, + 0x25,0x3e,0x77,0xe0,0x24,0x21,0x30,0xff,0xaf,0x0e,0x10,0x22,0x2f,0x0d,0x20,0xfb, + 0xc9,0xcd,0x11,0x02,0xcd,0x62,0x02,0x79,0xfe,0x38,0x20,0x14,0xe5,0xaf,0xe0,0x4f, + 0x21,0xa7,0x99,0x3e,0x38,0x22,0x3c,0xfe,0x3f,0x20,0xfa,0x3e,0x01,0xe0,0x4f,0xe1, + 0xc5,0xe5,0x21,0x43,0x01,0xcb,0x7e,0xcc,0x89,0x05,0xe1,0xc1,0xcd,0x11,0x02,0x79, + 0xd6,0x30,0xd2,0x06,0x03,0x79,0xfe,0x01,0xca,0x06,0x03,0x7d,0xfe,0xd1,0x28,0x21, + 0xc5,0x06,0x03,0x0e,0x01,0x16,0x03,0x7e,0xe6,0xf8,0xb1,0x22,0x15,0x20,0xf8,0x0c, + 0x79,0xfe,0x06,0x20,0xf0,0x11,0x11,0x00,0x19,0x05,0x20,0xe7,0x11,0xa1,0xff,0x19, + 0xc1,0x04,0x78,0x1e,0x83,0xfe,0x62,0x28,0x06,0x1e,0xc1,0xfe,0x64,0x20,0x07,0x7b, + 0xe0,0x13,0x3e,0x87,0xe0,0x14,0xfa,0x02,0xd0,0xfe,0x00,0x28,0x0a,0x3d,0xea,0x02, + 0xd0,0x79,0xfe,0x01,0xca,0x91,0x02,0x0d,0xc2,0x91,0x02,0xc9,0x0e,0x26,0xcd,0x4a, + 0x03,0xcd,0x11,0x02,0xcd,0x62,0x02,0x0d,0x20,0xf4,0xcd,0x11,0x02,0x3e,0x01,0xe0, + 0x4f,0xcd,0x3e,0x03,0xcd,0x41,0x03,0xaf,0xe0,0x4f,0xcd,0x3e,0x03,0xc9,0x21,0x08, + 0x00,0x11,0x51,0xff,0x0e,0x05,0xcd,0x0a,0x02,0xc9,0xc5,0xd5,0xe5,0x21,0x40,0xd8, + 0x0e,0x20,0x7e,0xe6,0x1f,0xfe,0x1f,0x28,0x01,0x3c,0x57,0x2a,0x07,0x07,0x07,0xe6, + 0x07,0x47,0x3a,0x07,0x07,0x07,0xe6,0x18,0xb0,0xfe,0x1f,0x28,0x01,0x3c,0x0f,0x0f, + 0x0f,0x47,0xe6,0xe0,0xb2,0x22,0x78,0xe6,0x03,0x5f,0x7e,0x0f,0x0f,0xe6,0x1f,0xfe, + 0x1f,0x28,0x01,0x3c,0x07,0x07,0xb3,0x22,0x0d,0x20,0xc7,0xe1,0xd1,0xc1,0xc9,0x0e, + 0x00,0x1a,0xe6,0xf0,0xcb,0x49,0x28,0x02,0xcb,0x37,0x47,0x23,0x7e,0xb0,0x22,0x1a, + 0xe6,0x0f,0xcb,0x49,0x20,0x02,0xcb,0x37,0x47,0x23,0x7e,0xb0,0x22,0x13,0xcb,0x41, + 0x28,0x0d,0xd5,0x11,0xf8,0xff,0xcb,0x49,0x28,0x03,0x11,0x08,0x00,0x19,0xd1,0x0c, + 0x79,0xfe,0x18,0x20,0xcc,0xc9,0x47,0xd5,0x16,0x04,0x58,0xcb,0x10,0x17,0xcb,0x13, + 0x17,0x15,0x20,0xf6,0xd1,0x22,0x23,0x22,0x23,0xc9,0x3e,0x19,0xea,0x10,0x99,0x21, + 0x2f,0x99,0x0e,0x0c,0x3d,0x28,0x08,0x32,0x0d,0x20,0xf9,0x2e,0x0f,0x18,0xf3,0xc9, + 0x3e,0x01,0xe0,0x4f,0xcd,0x00,0x02,0x11,0x07,0x06,0x21,0x80,0x80,0x0e,0xc0,0x1a, + 0x22,0x23,0x22,0x23,0x13,0x0d,0x20,0xf7,0x11,0x04,0x01,0xcd,0x8f,0x03,0x01,0xa8, + 0xff,0x09,0xcd,0x8f,0x03,0x01,0xf8,0xff,0x09,0x11,0x72,0x00,0x0e,0x08,0x23,0x1a, + 0x22,0x13,0x0d,0x20,0xf9,0x21,0xc2,0x98,0x06,0x08,0x3e,0x08,0x0e,0x10,0x22,0x0d, + 0x20,0xfc,0x11,0x10,0x00,0x19,0x05,0x20,0xf3,0xaf,0xe0,0x4f,0x21,0xc2,0x98,0x3e, + 0x08,0x22,0x3c,0xfe,0x18,0x20,0x02,0x2e,0xe2,0xfe,0x28,0x20,0x03,0x21,0x02,0x99, + 0xfe,0x38,0x20,0xed,0x21,0xd8,0x08,0x11,0x40,0xd8,0x06,0x08,0x3e,0xff,0x12,0x13, + 0x12,0x13,0x0e,0x02,0xcd,0x0a,0x02,0x3e,0x00,0x12,0x13,0x12,0x13,0x13,0x13,0x05, + 0x20,0xea,0xcd,0x62,0x02,0x21,0x4b,0x01,0x7e,0xfe,0x33,0x20,0x0b,0x2e,0x44,0x1e, + 0x30,0x2a,0xbb,0x20,0x49,0x1c,0x18,0x04,0x2e,0x4b,0x1e,0x01,0x2a,0xbb,0x20,0x3e, + 0x2e,0x34,0x01,0x10,0x00,0x2a,0x80,0x47,0x0d,0x20,0xfa,0xea,0x00,0xd0,0x21,0xc7, + 0x06,0x0e,0x00,0x2a,0xb8,0x28,0x08,0x0c,0x79,0xfe,0x4f,0x20,0xf6,0x18,0x1f,0x79, + 0xd6,0x41,0x38,0x1c,0x21,0x16,0x07,0x16,0x00,0x5f,0x19,0xfa,0x37,0x01,0x57,0x7e, + 0xba,0x28,0x0d,0x11,0x0e,0x00,0x19,0x79,0x83,0x4f,0xd6,0x5e,0x38,0xed,0x0e,0x00, + 0x21,0x33,0x07,0x06,0x00,0x09,0x7e,0xe6,0x1f,0xea,0x08,0xd0,0x7e,0xe6,0xe0,0x07, + 0x07,0x07,0xea,0x0b,0xd0,0xcd,0xe9,0x04,0xc9,0x11,0x91,0x07,0x21,0x00,0xd9,0xfa, + 0x0b,0xd0,0x47,0x0e,0x1e,0xcb,0x40,0x20,0x02,0x13,0x13,0x1a,0x22,0x20,0x02,0x1b, + 0x1b,0xcb,0x48,0x20,0x02,0x13,0x13,0x1a,0x22,0x13,0x13,0x20,0x02,0x1b,0x1b,0xcb, + 0x50,0x28,0x05,0x1b,0x2b,0x1a,0x22,0x13,0x1a,0x22,0x13,0x0d,0x20,0xd7,0x21,0x00, + 0xd9,0x11,0x00,0xda,0xcd,0x64,0x05,0xc9,0x21,0x12,0x00,0xfa,0x05,0xd0,0x07,0x07, + 0x06,0x00,0x4f,0x09,0x11,0x40,0xd8,0x06,0x08,0xe5,0x0e,0x02,0xcd,0x0a,0x02,0x13, + 0x13,0x13,0x13,0x13,0x13,0xe1,0x05,0x20,0xf0,0x11,0x42,0xd8,0x0e,0x02,0xcd,0x0a, + 0x02,0x11,0x4a,0xd8,0x0e,0x02,0xcd,0x0a,0x02,0x2b,0x2b,0x11,0x44,0xd8,0x0e,0x02, + 0xcd,0x0a,0x02,0xc9,0x0e,0x60,0x2a,0xe5,0xc5,0x21,0xe8,0x07,0x06,0x00,0x4f,0x09, + 0x0e,0x08,0xcd,0x0a,0x02,0xc1,0xe1,0x0d,0x20,0xec,0xc9,0xfa,0x08,0xd0,0x11,0x18, + 0x00,0x3c,0x3d,0x28,0x03,0x19,0x20,0xfa,0xc9,0xcd,0x1d,0x02,0x78,0xe6,0xff,0x28, + 0x0f,0x21,0xe4,0x08,0x06,0x00,0x2a,0xb9,0x28,0x08,0x04,0x78,0xfe,0x0c,0x20,0xf6, + 0x18,0x2d,0x78,0xea,0x05,0xd0,0x3e,0x1e,0xea,0x02,0xd0,0x11,0x0b,0x00,0x19,0x56, + 0x7a,0xe6,0x1f,0x5f,0x21,0x08,0xd0,0x3a,0x22,0x7b,0x77,0x7a,0xe6,0xe0,0x07,0x07, + 0x07,0x5f,0x21,0x0b,0xd0,0x3a,0x22,0x7b,0x77,0xcd,0xe9,0x04,0xcd,0x28,0x05,0xc9, + 0xcd,0x11,0x02,0xfa,0x43,0x01,0xcb,0x7f,0x28,0x04,0xe0,0x4c,0x18,0x28,0x3e,0x04, + 0xe0,0x4c,0x3e,0x01,0xe0,0x6c,0x21,0x00,0xda,0xcd,0x7b,0x05,0x06,0x10,0x16,0x00, + 0x1e,0x08,0xcd,0x4a,0x02,0x21,0x7a,0x00,0xfa,0x00,0xd0,0x47,0x0e,0x02,0x2a,0xb8, + 0xcc,0xda,0x03,0x0d,0x20,0xf8,0xc9,0x01,0x0f,0x3f,0x7e,0xff,0xff,0xc0,0x00,0xc0, + 0xf0,0xf1,0x03,0x7c,0xfc,0xfe,0xfe,0x03,0x07,0x07,0x0f,0xe0,0xe0,0xf0,0xf0,0x1e, + 0x3e,0x7e,0xfe,0x0f,0x0f,0x1f,0x1f,0xff,0xff,0x00,0x00,0x01,0x01,0x01,0x03,0xff, + 0xff,0xe1,0xe0,0xc0,0xf0,0xf9,0xfb,0x1f,0x7f,0xf8,0xe0,0xf3,0xfd,0x3e,0x1e,0xe0, + 0xf0,0xf9,0x7f,0x3e,0x7c,0xf8,0xe0,0xf8,0xf0,0xf0,0xf8,0x00,0x00,0x7f,0x7f,0x07, + 0x0f,0x9f,0xbf,0x9e,0x1f,0xff,0xff,0x0f,0x1e,0x3e,0x3c,0xf1,0xfb,0x7f,0x7f,0xfe, + 0xde,0xdf,0x9f,0x1f,0x3f,0x3e,0x3c,0xf8,0xf8,0x00,0x00,0x03,0x03,0x07,0x07,0xff, + 0xff,0xc1,0xc0,0xf3,0xe7,0xf7,0xf3,0xc0,0xc0,0xc0,0xc0,0x1f,0x1f,0x1e,0x3e,0x3f, + 0x1f,0x3e,0x3e,0x80,0x00,0x00,0x00,0x7c,0x1f,0x07,0x00,0x0f,0xff,0xfe,0x00,0x7c, + 0xf8,0xf0,0x00,0x1f,0x0f,0x0f,0x00,0x7c,0xf8,0xf8,0x00,0x3f,0x3e,0x1c,0x00,0x0f, + 0x0f,0x0f,0x00,0x7c,0xff,0xff,0x00,0x00,0xf8,0xf8,0x00,0x07,0x0f,0x0f,0x00,0x81, + 0xff,0xff,0x00,0xf3,0xe1,0x80,0x00,0xe0,0xff,0x7f,0x00,0xfc,0xf0,0xc0,0x00,0x3e, + 0x7c,0x7c,0x00,0x00,0x00,0x00,0x00,0x00,0x88,0x16,0x36,0xd1,0xdb,0xf2,0x3c,0x8c, + 0x92,0x3d,0x5c,0x58,0xc9,0x3e,0x70,0x1d,0x59,0x69,0x19,0x35,0xa8,0x14,0xaa,0x75, + 0x95,0x99,0x34,0x6f,0x15,0xff,0x97,0x4b,0x90,0x17,0x10,0x39,0xf7,0xf6,0xa2,0x49, + 0x4e,0x43,0x68,0xe0,0x8b,0xf0,0xce,0x0c,0x29,0xe8,0xb7,0x86,0x9a,0x52,0x01,0x9d, + 0x71,0x9c,0xbd,0x5d,0x6d,0x67,0x3f,0x6b,0xb3,0x46,0x28,0xa5,0xc6,0xd3,0x27,0x61, + 0x18,0x66,0x6a,0xbf,0x0d,0xf4,0x42,0x45,0x46,0x41,0x41,0x52,0x42,0x45,0x4b,0x45, + 0x4b,0x20,0x52,0x2d,0x55,0x52,0x41,0x52,0x20,0x49,0x4e,0x41,0x49,0x4c,0x49,0x43, + 0x45,0x20,0x52,0x7c,0x08,0x12,0xa3,0xa2,0x07,0x87,0x4b,0x20,0x12,0x65,0xa8,0x16, + 0xa9,0x86,0xb1,0x68,0xa0,0x87,0x66,0x12,0xa1,0x30,0x3c,0x12,0x85,0x12,0x64,0x1b, + 0x07,0x06,0x6f,0x6e,0x6e,0xae,0xaf,0x6f,0xb2,0xaf,0xb2,0xa8,0xab,0x6f,0xaf,0x86, + 0xae,0xa2,0xa2,0x12,0xaf,0x13,0x12,0xa1,0x6e,0xaf,0xaf,0xad,0x06,0x4c,0x6e,0xaf, + 0xaf,0x12,0x7c,0xac,0xa8,0x6a,0x6e,0x13,0xa0,0x2d,0xa8,0x2b,0xac,0x64,0xac,0x6d, + 0x87,0xbc,0x60,0xb4,0x13,0x72,0x7c,0xb5,0xae,0xae,0x7c,0x7c,0x65,0xa2,0x6c,0x64, + 0x85,0x80,0xb0,0x40,0x88,0x20,0x68,0xde,0x00,0x70,0xde,0x20,0x78,0x20,0x20,0x38, + 0x20,0xb0,0x90,0x20,0xb0,0xa0,0xe0,0xb0,0xc0,0x98,0xb6,0x48,0x80,0xe0,0x50,0x1e, + 0x1e,0x58,0x20,0xb8,0xe0,0x88,0xb0,0x10,0x20,0x00,0x10,0x20,0xe0,0x18,0xe0,0x18, + 0x00,0x18,0xe0,0x20,0xa8,0xe0,0x20,0x18,0xe0,0x00,0x20,0x18,0xd8,0xc8,0x18,0xe0, + 0x00,0xe0,0x40,0x28,0x28,0x28,0x18,0xe0,0x60,0x20,0x18,0xe0,0x00,0x00,0x08,0xe0, + 0x18,0x30,0xd0,0xd0,0xd0,0x20,0xe0,0xe8,0xff,0x7f,0xbf,0x32,0xd0,0x00,0x00,0x00, + 0x9f,0x63,0x79,0x42,0xb0,0x15,0xcb,0x04,0xff,0x7f,0x31,0x6e,0x4a,0x45,0x00,0x00, + 0xff,0x7f,0xef,0x1b,0x00,0x02,0x00,0x00,0xff,0x7f,0x1f,0x42,0xf2,0x1c,0x00,0x00, + 0xff,0x7f,0x94,0x52,0x4a,0x29,0x00,0x00,0xff,0x7f,0xff,0x03,0x2f,0x01,0x00,0x00, + 0xff,0x7f,0xef,0x03,0xd6,0x01,0x00,0x00,0xff,0x7f,0xb5,0x42,0xc8,0x3d,0x00,0x00, + 0x74,0x7e,0xff,0x03,0x80,0x01,0x00,0x00,0xff,0x67,0xac,0x77,0x13,0x1a,0x6b,0x2d, + 0xd6,0x7e,0xff,0x4b,0x75,0x21,0x00,0x00,0xff,0x53,0x5f,0x4a,0x52,0x7e,0x00,0x00, + 0xff,0x4f,0xd2,0x7e,0x4c,0x3a,0xe0,0x1c,0xed,0x03,0xff,0x7f,0x5f,0x25,0x00,0x00, + 0x6a,0x03,0x1f,0x02,0xff,0x03,0xff,0x7f,0xff,0x7f,0xdf,0x01,0x12,0x01,0x00,0x00, + 0x1f,0x23,0x5f,0x03,0xf2,0x00,0x09,0x00,0xff,0x7f,0xea,0x03,0x1f,0x01,0x00,0x00, + 0x9f,0x29,0x1a,0x00,0x0c,0x00,0x00,0x00,0xff,0x7f,0x7f,0x02,0x1f,0x00,0x00,0x00, + 0xff,0x7f,0xe0,0x03,0x06,0x02,0x20,0x01,0xff,0x7f,0xeb,0x7e,0x1f,0x00,0x00,0x7c, + 0xff,0x7f,0xff,0x3f,0x00,0x7e,0x1f,0x00,0xff,0x7f,0xff,0x03,0x1f,0x00,0x00,0x00, + 0xff,0x03,0x1f,0x00,0x0c,0x00,0x00,0x00,0xff,0x7f,0x3f,0x03,0x93,0x01,0x00,0x00, + 0x00,0x00,0x00,0x42,0x7f,0x03,0xff,0x7f,0xff,0x7f,0x8c,0x7e,0x00,0x7c,0x00,0x00, + 0xff,0x7f,0xef,0x1b,0x80,0x61,0x00,0x00,0xff,0x7f,0x00,0x7c,0xe0,0x03,0x1f,0x7c, + 0x1f,0x00,0xff,0x03,0x40,0x41,0x42,0x20,0x21,0x22,0x80,0x81,0x82,0x10,0x11,0x12, + 0x12,0xb0,0x79,0xb8,0xad,0x16,0x17,0x07,0xba,0x05,0x7c,0x13,0x00,0x00,0x00,0x00, +}; + +#endif diff --git a/libsnes/gameboy/system/bootrom-dmg.cpp b/libsnes/gameboy/system/bootrom-dmg.cpp new file mode 100755 index 0000000..54f0274 --- /dev/null +++ b/libsnes/gameboy/system/bootrom-dmg.cpp @@ -0,0 +1,23 @@ +#ifdef SYSTEM_CPP + +//SHA256 = cf053eccb4ccafff9e67339d4e78e98dce7d1ed59be819d2a1ba2232c6fce1c7 +const uint8_t System::BootROM::dmg[256] = { + 0x31,0xfe,0xff,0xaf,0x21,0xff,0x9f,0x32,0xcb,0x7c,0x20,0xfb,0x21,0x26,0xff,0x0e, + 0x11,0x3e,0x80,0x32,0xe2,0x0c,0x3e,0xf3,0xe2,0x32,0x3e,0x77,0x77,0x3e,0xfc,0xe0, + 0x47,0x11,0x04,0x01,0x21,0x10,0x80,0x1a,0xcd,0x95,0x00,0xcd,0x96,0x00,0x13,0x7b, + 0xfe,0x34,0x20,0xf3,0x11,0xd8,0x00,0x06,0x08,0x1a,0x13,0x22,0x23,0x05,0x20,0xf9, + 0x3e,0x19,0xea,0x10,0x99,0x21,0x2f,0x99,0x0e,0x0c,0x3d,0x28,0x08,0x32,0x0d,0x20, + 0xf9,0x2e,0x0f,0x18,0xf3,0x67,0x3e,0x64,0x57,0xe0,0x42,0x3e,0x91,0xe0,0x40,0x04, + 0x1e,0x02,0x0e,0x0c,0xf0,0x44,0xfe,0x90,0x20,0xfa,0x0d,0x20,0xf7,0x1d,0x20,0xf2, + 0x0e,0x13,0x24,0x7c,0x1e,0x83,0xfe,0x62,0x28,0x06,0x1e,0xc1,0xfe,0x64,0x20,0x06, + 0x7b,0xe2,0x0c,0x3e,0x87,0xe2,0xf0,0x42,0x90,0xe0,0x42,0x15,0x20,0xd2,0x05,0x20, + 0x4f,0x16,0x20,0x18,0xcb,0x4f,0x06,0x04,0xc5,0xcb,0x11,0x17,0xc1,0xcb,0x11,0x17, + 0x05,0x20,0xf5,0x22,0x23,0x22,0x23,0xc9,0xce,0xed,0x66,0x66,0xcc,0x0d,0x00,0x0b, + 0x03,0x73,0x00,0x83,0x00,0x0c,0x00,0x0d,0x00,0x08,0x11,0x1f,0x88,0x89,0x00,0x0e, + 0xdc,0xcc,0x6e,0xe6,0xdd,0xdd,0xd9,0x99,0xbb,0xbb,0x67,0x63,0x6e,0x0e,0xec,0xcc, + 0xdd,0xdc,0x99,0x9f,0xbb,0xb9,0x33,0x3e,0x3c,0x42,0xb9,0xa5,0xb9,0xa5,0x42,0x3c, + 0x21,0x04,0x01,0x11,0xa8,0x00,0x1a,0x13,0xbe,0x20,0xfe,0x23,0x7d,0xfe,0x34,0x20, + 0xf5,0x06,0x19,0x78,0x86,0x23,0x05,0x20,0xfb,0x86,0x20,0xfe,0x3e,0x01,0xe0,0x50, +}; + +#endif diff --git a/libsnes/gameboy/system/bootrom-sgb.cpp b/libsnes/gameboy/system/bootrom-sgb.cpp new file mode 100755 index 0000000..71c0952 --- /dev/null +++ b/libsnes/gameboy/system/bootrom-sgb.cpp @@ -0,0 +1,23 @@ +#ifdef SYSTEM_CPP + +//SHA256 = 0e4ddff32fc9d1eeaae812a157dd246459b00c9e14f2f61751f661f32361e360 +const uint8_t System::BootROM::sgb[256] = { + 0x31,0xfe,0xff,0x3e,0x30,0xe0,0x00,0xaf,0x21,0xff,0x9f,0x32,0xcb,0x7c,0x20,0xfb, + 0x21,0x26,0xff,0x0e,0x11,0x3e,0x80,0x32,0xe2,0x0c,0x3e,0xf3,0xe2,0x32,0x3e,0x77, + 0x77,0x3e,0xfc,0xe0,0x47,0x21,0x5f,0xc0,0x0e,0x08,0xaf,0x32,0x0d,0x20,0xfc,0x11, + 0x4f,0x01,0x3e,0xfb,0x0e,0x06,0xf5,0x06,0x00,0x1a,0x1b,0x32,0x80,0x47,0x0d,0x20, + 0xf8,0x32,0xf1,0x32,0x0e,0x0e,0xd6,0x02,0xfe,0xef,0x20,0xea,0x11,0x04,0x01,0x21, + 0x10,0x80,0x1a,0xcd,0xd3,0x00,0xcd,0xd4,0x00,0x13,0x7b,0xfe,0x34,0x20,0xf3,0x11, + 0xe6,0x00,0x06,0x08,0x1a,0x13,0x22,0x23,0x05,0x20,0xf9,0x3e,0x19,0xea,0x10,0x99, + 0x21,0x2f,0x99,0x0e,0x0c,0x3d,0x28,0x08,0x32,0x0d,0x20,0xf9,0x2e,0x0f,0x18,0xf3, + 0x3e,0x91,0xe0,0x40,0x21,0x00,0xc0,0x0e,0x00,0x3e,0x00,0xe2,0x3e,0x30,0xe2,0x06, + 0x10,0x1e,0x08,0x2a,0x57,0xcb,0x42,0x3e,0x10,0x20,0x02,0x3e,0x20,0xe2,0x3e,0x30, + 0xe2,0xcb,0x1a,0x1d,0x20,0xef,0x05,0x20,0xe8,0x3e,0x20,0xe2,0x3e,0x30,0xe2,0xcd, + 0xc2,0x00,0x7d,0xfe,0x60,0x20,0xd2,0x0e,0x13,0x3e,0xc1,0xe2,0x0c,0x3e,0x07,0xe2, + 0x18,0x3a,0x16,0x04,0xf0,0x44,0xfe,0x90,0x20,0xfa,0x1e,0x00,0x1d,0x20,0xfd,0x15, + 0x20,0xf2,0xc9,0x4f,0x06,0x04,0xc5,0xcb,0x11,0x17,0xc1,0xcb,0x11,0x17,0x05,0x20, + 0xf5,0x22,0x23,0x22,0x23,0xc9,0x3c,0x42,0xb9,0xa5,0xb9,0xa5,0x42,0x3c,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3e,0x01,0xe0,0x50, +}; + +#endif diff --git a/libsnes/gameboy/system/serialization.cpp b/libsnes/gameboy/system/serialization.cpp new file mode 100755 index 0000000..c7ca0f8 --- /dev/null +++ b/libsnes/gameboy/system/serialization.cpp @@ -0,0 +1,64 @@ +#ifdef SYSTEM_CPP + +serializer System::serialize() { + serializer s(serialize_size); + + unsigned signature = 0x31545342, version = Info::SerializerVersion, crc32 = 0; + char description[512]; + memset(&description, 0, sizeof description); + + s.integer(signature); + s.integer(version); + s.integer(crc32); + s.array(description); + + serialize_all(s); + return s; +} + +bool System::unserialize(serializer &s) { + unsigned signature, version, crc32; + char description[512]; + + s.integer(signature); + s.integer(version); + s.integer(crc32); + s.array(description); + + if(signature != 0x31545342) return false; + if(version != Info::SerializerVersion) return false; +//if(crc32 != 0) return false; + + power(); + serialize_all(s); + return true; +} + +void System::serialize(serializer &s) { + s.integer(clocks_executed); +} + +void System::serialize_all(serializer &s) { + cartridge.serialize(s); + system.serialize(s); + cpu.serialize(s); + apu.serialize(s); + lcd.serialize(s); +} + +void System::serialize_init() { + serializer s; + + unsigned signature = 0, version = 0, crc32 = 0; + char description[512]; + + s.integer(signature); + s.integer(version); + s.integer(crc32); + s.array(description); + + serialize_all(s); + serialize_size = s.size(); +} + +#endif diff --git a/libsnes/gameboy/system/system.hpp b/libsnes/gameboy/system/system.hpp new file mode 100755 index 0000000..5ba6b98 --- /dev/null +++ b/libsnes/gameboy/system/system.hpp @@ -0,0 +1,51 @@ +class Interface; + +struct Input { + enum e { + Up, Down, Left, Right, B, A, Select, Start, + } i; +}; + +struct System : property { + struct Revision { + enum e { + GameBoy, + SuperGameBoy, + GameBoyColor, + } i; + }; + Revision revision; + inline bool dmg() const { return revision.i == Revision::GameBoy; } + inline bool sgb() const { return revision.i == Revision::SuperGameBoy; } + inline bool cgb() const { return revision.i == Revision::GameBoyColor; } + + struct BootROM { + static const uint8 dmg[ 256]; + static const uint8 sgb[ 256]; + static const uint8 cgb[2048]; + } bootROM; + + void run(); + void runtosave(); + void runthreadtosave(); + + void init(); + void load(Revision::e); + void power(); + + unsigned clocks_executed; + + //serialization.cpp + unsigned serialize_size; + + serializer serialize(); + bool unserialize(serializer&); + + void serialize(serializer&); + void serialize_all(serializer&); + void serialize_init(); +}; + +#include + +extern System system; diff --git a/libsnes/gameboy/system/system_gb.cpp b/libsnes/gameboy/system/system_gb.cpp new file mode 100644 index 0000000..308284f --- /dev/null +++ b/libsnes/gameboy/system/system_gb.cpp @@ -0,0 +1,57 @@ +#include + +#define SYSTEM_CPP +namespace GameBoy { + +#include "bootrom-dmg.cpp" +#include "bootrom-sgb.cpp" +#include "bootrom-cgb.cpp" +#include "serialization.cpp" +System system; + +void System::run() { + scheduler.sync.i = Scheduler::SynchronizeMode::None; + + scheduler.enter(); + if(scheduler.exit_reason.i == Scheduler::ExitReason::FrameEvent) { + } +} + +void System::runtosave() { + scheduler.sync.i = Scheduler::SynchronizeMode::CPU; + runthreadtosave(); + + scheduler.active_thread = lcd.thread; + runthreadtosave(); +} + +void System::runthreadtosave() { + while(true) { + scheduler.enter(); + if(scheduler.exit_reason.i == Scheduler::ExitReason::SynchronizeEvent) break; + if(scheduler.exit_reason.i == Scheduler::ExitReason::FrameEvent) { + } + } +} + +void System::init() { + assert(interface != 0); +} + +void System::load(Revision::e revision) { + this->revision.i = revision; + serialize_init(); +} + +void System::power() { + bus.power(); + cartridge.power(); + cpu.power(); + apu.power(); + lcd.power(); + scheduler.init(); + + clocks_executed = 0; +} + +} diff --git a/libsnes/gameboy/video/video.hpp b/libsnes/gameboy/video/video.hpp new file mode 100644 index 0000000..d2dceec --- /dev/null +++ b/libsnes/gameboy/video/video.hpp @@ -0,0 +1,17 @@ +struct Video { + struct Format { enum e { RGB30, RGB24, RGB16, RGB15 } i; }; + unsigned *palette; + + unsigned palette_dmg(unsigned color) const; + unsigned palette_sgb(unsigned color) const; + unsigned palette_cgb(unsigned color) const; + + void generate(Format format); + Video(); + ~Video(); + +private: + static const double monochrome[4][3]; +}; + +extern Video video; diff --git a/libsnes/gameboy/video/video_gb.cpp b/libsnes/gameboy/video/video_gb.cpp new file mode 100644 index 0000000..38726d8 --- /dev/null +++ b/libsnes/gameboy/video/video_gb.cpp @@ -0,0 +1,82 @@ +#include + +#define VIDEO_CPP +namespace GameBoy { + +Video video; + +unsigned Video::palette_dmg(unsigned color) const { + unsigned R = monochrome[color][0] * 1023.0; + unsigned G = monochrome[color][1] * 1023.0; + unsigned B = monochrome[color][2] * 1023.0; + + return (R << 20) + (G << 10) + (B << 0); +} + +unsigned Video::palette_sgb(unsigned color) const { + unsigned R = (3 - color) * 341; + unsigned G = (3 - color) * 341; + unsigned B = (3 - color) * 341; + + return (R << 20) + (G << 10) + (B << 0); +} + +unsigned Video::palette_cgb(unsigned color) const { + unsigned r = (color >> 0) & 31; + unsigned g = (color >> 5) & 31; + unsigned b = (color >> 10) & 31; + + unsigned R = (r * 26 + g * 4 + b * 2); + unsigned G = ( g * 24 + b * 8); + unsigned B = (r * 6 + g * 4 + b * 22); + + R = min(960, R); + G = min(960, G); + B = min(960, B); + + return (R << 20) + (G << 10) + (B << 0); +} + +void Video::generate(Format format) { + if(system.dmg()) for(unsigned n = 0; n < 4; n++) palette[n] = palette_dmg(n); + if(system.sgb()) for(unsigned n = 0; n < 4; n++) palette[n] = palette_sgb(n); + if(system.cgb()) for(unsigned n = 0; n < (1 << 15); n++) palette[n] = palette_cgb(n); + + if(format.i == Format::RGB24) { + for(unsigned n = 0; n < (1 << 15); n++) { + unsigned color = palette[n]; + palette[n] = ((color >> 6) & 0xff0000) + ((color >> 4) & 0x00ff00) + ((color >> 2) & 0x0000ff); + } + } + + if(format.i == Format::RGB16) { + for(unsigned n = 0; n < (1 << 15); n++) { + unsigned color = palette[n]; + palette[n] = ((color >> 14) & 0xf800) + ((color >> 9) & 0x07e0) + ((color >> 5) & 0x001f); + } + } + + if(format.i == Format::RGB15) { + for(unsigned n = 0; n < (1 << 15); n++) { + unsigned color = palette[n]; + palette[n] = ((color >> 15) & 0x7c00) + ((color >> 10) & 0x03e0) + ((color >> 5) & 0x001f); + } + } +} + +Video::Video() { + palette = new unsigned[1 << 15]; +} + +Video::~Video() { + delete[] palette; +} + +const double Video::monochrome[4][3] = { + { 0.605, 0.734, 0.059 }, + { 0.543, 0.672, 0.059 }, + { 0.188, 0.383, 0.188 }, + { 0.059, 0.219, 0.059 }, +}; + +} diff --git a/libsnes/libco/amd64.c b/libsnes/libco/amd64.c new file mode 100644 index 0000000..17ff2a9 --- /dev/null +++ b/libsnes/libco/amd64.c @@ -0,0 +1,142 @@ +/* + libco.amd64 (2009-10-12) + author: byuu + license: public domain +*/ + +#define LIBCO_C +#include "libco.h" +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// ASM co_swap. +void co_swap(cothread_t new_active, cothread_t old_active); + +static thread_local long long co_active_buffer[64]; +static thread_local cothread_t co_active_handle = 0; + +#ifdef _WIN32 +asm ( + ".text\n" + ".globl co_swap\n" + "co_swap:\n" + + "movq %rsp, (%rdx) # Save stack pointer and pop old back\n" + "movq (%rcx), %rsp\n" + + "popq %rax # Pop saved PC to %rax\n" + + "movq %rbp, 0x8(%rdx) # Save non-volatile registers\n" + "movq %rsi, 0x10(%rdx)\n" + "movq %rdi, 0x18(%rdx)\n" + "movq %rbx, 0x20(%rdx)\n" + "movq %r12, 0x28(%rdx)\n" + "movq %r13, 0x30(%rdx)\n" + "movq %r14, 0x38(%rdx)\n" + "movq %r15, 0x40(%rdx)\n" + + "addq $0x80, %rdx\n" + "andq $0xfffffffffffffff0, %rdx # Align buffer to 16 bytes (SSE).\n" + "movaps %xmm6, (%rdx) # Also save non-volatile vector registers.\n" + "movaps %xmm7, 0x10(%rdx)\n" + "movaps %xmm8, 0x20(%rdx)\n" + "movaps %xmm9, 0x30(%rdx)\n" + "movaps %xmm10, 0x40(%rdx)\n" + "movaps %xmm11, 0x50(%rdx)\n" + "movaps %xmm12, 0x60(%rdx)\n" + "movaps %xmm13, 0x70(%rdx)\n" + "movaps %xmm14, 0x80(%rdx)\n" + "movaps %xmm15, 0x90(%rdx)\n" + + "movq 0x8(%rcx), %rbp # Pop back our saved registers and vector registers.\n" + "movq 0x10(%rcx), %rsi\n" + "movq 0x18(%rcx), %rdi\n" + "movq 0x20(%rcx), %rbx\n" + "movq 0x28(%rcx), %r12\n" + "movq 0x30(%rcx), %r13\n" + "movq 0x38(%rcx), %r14\n" + "movq 0x40(%rcx), %r15\n" + + "addq $0x80, %rcx\n" + "andq $0xfffffffffffffff0, %rcx\n" + "movaps %xmm6, (%rcx)\n" + "movaps %xmm7, 0x10(%rcx)\n" + "movaps %xmm8, 0x20(%rcx)\n" + "movaps %xmm9, 0x30(%rcx)\n" + "movaps %xmm10, 0x40(%rcx)\n" + "movaps %xmm11, 0x50(%rcx)\n" + "movaps %xmm12, 0x60(%rcx)\n" + "movaps %xmm13, 0x70(%rcx)\n" + "movaps %xmm14, 0x80(%rcx)\n" + "movaps %xmm15, 0x90(%rcx)\n" + "jmpq *%rax # Jump back to old saved PC.\n" +); +#else +asm ( + ".text\n" + ".globl co_swap\n" + ".globl _co_swap\n" + "_co_swap:\n" + "co_swap:\n" + "movq %rsp, (%rsi) # Save stack pointer, and pop the old one back.\n" + "movq (%rdi), %rsp\n" + "popq %rax\n" + + "movq %rbp, 0x8(%rsi) # Save our non-volatile registers to buffer.\n" + "movq %rbx, 0x10(%rsi)\n" + "movq %r12, 0x18(%rsi)\n" + "movq %r13, 0x20(%rsi)\n" + "movq %r14, 0x28(%rsi)\n" + "movq %r15, 0x30(%rsi)\n" + + "movq 0x8(%rdi), %rbp # Pop back our saved registers.\n" + "movq 0x10(%rdi), %rbx\n" + "movq 0x18(%rdi), %r12\n" + "movq 0x20(%rdi), %r13\n" + "movq 0x28(%rdi), %r14\n" + "movq 0x30(%rdi), %r15\n" + "jmpq *%rax # Jump back to saved PC.\n" +); +#endif + +static void crash() { + assert(0); /* called only if cothread_t entrypoint returns */ +} + +cothread_t co_active() { + if(!co_active_handle) co_active_handle = &co_active_buffer; + return co_active_handle; +} + +cothread_t co_create(unsigned int size, void (*entrypoint)(void)) { + cothread_t handle; + if(!co_active_handle) co_active_handle = &co_active_buffer; + size += 512; /* allocate additional space for storage */ + size &= ~15; /* align stack to 16-byte boundary */ + + if((handle = (cothread_t)malloc(size))) { + long long *p = (long long*)((char*)handle + size); /* seek to top of stack */ + *--p = (long long)crash; /* crash if entrypoint returns */ + *--p = (long long)entrypoint; /* start of function */ + *(long long*)handle = (long long)p; /* stack pointer */ + } + + return handle; +} + +void co_delete(cothread_t handle) { + free(handle); +} + +void co_switch(cothread_t handle) { + register cothread_t co_previous_handle = co_active_handle; + co_swap(co_active_handle = handle, co_previous_handle); +} + +#ifdef __cplusplus +} +#endif diff --git a/libsnes/libco/amd64_msvc.c b/libsnes/libco/amd64_msvc.c new file mode 100644 index 0000000..167819d --- /dev/null +++ b/libsnes/libco/amd64_msvc.c @@ -0,0 +1,84 @@ +/* + libco.amd64 (2009-10-12) + author: byuu + license: public domain +*/ + +#define LIBCO_C +#include "libco.h" +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +static thread_local long long co_active_buffer[64]; +static thread_local cothread_t co_active_handle = 0; +static void (*co_swap)(cothread_t, cothread_t) = 0; + +//ABI: Win64 +static unsigned char co_swap_function[] = { + 0x48, 0x89, 0x22, 0x48, 0x8B, 0x21, 0x58, 0x48, 0x89, 0x6A, 0x08, 0x48, 0x89, 0x72, 0x10, 0x48, + 0x89, 0x7A, 0x18, 0x48, 0x89, 0x5A, 0x20, 0x4C, 0x89, 0x62, 0x28, 0x4C, 0x89, 0x6A, 0x30, 0x4C, + 0x89, 0x72, 0x38, 0x4C, 0x89, 0x7A, 0x40, 0x48, 0x81, 0xC2, 0x80, 0x00, 0x00, 0x00, 0x48, 0x83, + 0xE2, 0xF0, 0x0F, 0x29, 0x32, 0x0F, 0x29, 0x7A, 0x10, 0x44, 0x0F, 0x29, 0x42, 0x20, 0x44, 0x0F, + 0x29, 0x4A, 0x30, 0x44, 0x0F, 0x29, 0x52, 0x40, 0x44, 0x0F, 0x29, 0x5A, 0x50, 0x44, 0x0F, 0x29, + 0x62, 0x60, 0x44, 0x0F, 0x29, 0x6A, 0x70, 0x44, 0x0F, 0x29, 0xB2, 0x80, 0x00, 0x00, 0x00, 0x44, + 0x0F, 0x29, 0xBA, 0x90, 0x00, 0x00, 0x00, 0x48, 0x8B, 0x69, 0x08, 0x48, 0x8B, 0x71, 0x10, 0x48, + 0x8B, 0x79, 0x18, 0x48, 0x8B, 0x59, 0x20, 0x4C, 0x8B, 0x61, 0x28, 0x4C, 0x8B, 0x69, 0x30, 0x4C, + 0x8B, 0x71, 0x38, 0x4C, 0x8B, 0x79, 0x40, 0x48, 0x81, 0xC1, 0x80, 0x00, 0x00, 0x00, 0x48, 0x83, + 0xE1, 0xF0, 0x0F, 0x29, 0x31, 0x0F, 0x29, 0x79, 0x10, 0x44, 0x0F, 0x29, 0x41, 0x20, 0x44, 0x0F, + 0x29, 0x49, 0x30, 0x44, 0x0F, 0x29, 0x51, 0x40, 0x44, 0x0F, 0x29, 0x59, 0x50, 0x44, 0x0F, 0x29, + 0x61, 0x60, 0x44, 0x0F, 0x29, 0x69, 0x70, 0x44, 0x0F, 0x29, 0xB1, 0x80, 0x00, 0x00, 0x00, 0x44, + 0x0F, 0x29, 0xB9, 0x90, 0x00, 0x00, 0x00, 0xFF, 0xE0, +}; + +#include + +void co_init() { + DWORD old_privileges; + VirtualProtect(co_swap_function, sizeof co_swap_function, PAGE_EXECUTE_READWRITE, &old_privileges); +} + +static void crash() { + assert(0); /* called only if cothread_t entrypoint returns */ +} + +cothread_t co_active() { + if(!co_active_handle) co_active_handle = &co_active_buffer; + return co_active_handle; +} + +cothread_t co_create(unsigned int size, void (*entrypoint)(void)) { + cothread_t handle; + if(!co_swap) { + co_init(); + co_swap = (void (*)(cothread_t, cothread_t))co_swap_function; + } + if(!co_active_handle) co_active_handle = &co_active_buffer; + size += 512; /* allocate additional space for storage */ + size &= ~15; /* align stack to 16-byte boundary */ + + if(handle = (cothread_t)malloc(size)) { + long long *p = (long long*)((char*)handle + size); /* seek to top of stack */ + *--p = (long long)crash; /* crash if entrypoint returns */ + *--p = (long long)entrypoint; /* start of function */ + *(long long*)handle = (long long)p; /* stack pointer */ + } + + return handle; +} + +void co_delete(cothread_t handle) { + free(handle); +} + +void co_switch(cothread_t handle) { + register cothread_t co_previous_handle = co_active_handle; + co_swap(co_active_handle = handle, co_previous_handle); +} + +#ifdef __cplusplus +} +#endif diff --git a/libsnes/libco/fiber.c b/libsnes/libco/fiber.c new file mode 100644 index 0000000..02ef5bc --- /dev/null +++ b/libsnes/libco/fiber.c @@ -0,0 +1,51 @@ +/* + libco.win (2008-01-28) + authors: Nach, byuu + license: public domain +*/ + +#define LIBCO_C +#include "libco.h" +#define WINVER 0x0400 +#define _WIN32_WINNT 0x0400 +#define WIN32_LEAN_AND_MEAN +#include + +#ifdef __cplusplus +extern "C" { +#endif + +static thread_local cothread_t co_active_ = 0; + +static void __stdcall co_thunk(void *coentry) { + ((void (*)(void))coentry)(); +} + +cothread_t co_active() { + if(!co_active_) { + ConvertThreadToFiber(0); + co_active_ = GetCurrentFiber(); + } + return co_active_; +} + +cothread_t co_create(unsigned int heapsize, void (*coentry)(void)) { + if(!co_active_) { + ConvertThreadToFiber(0); + co_active_ = GetCurrentFiber(); + } + return (cothread_t)CreateFiber(heapsize, co_thunk, (void*)coentry); +} + +void co_delete(cothread_t cothread) { + DeleteFiber(cothread); +} + +void co_switch(cothread_t cothread) { + co_active_ = cothread; + SwitchToFiber(cothread); +} + +#ifdef __cplusplus +} +#endif diff --git a/libsnes/libco/libco.c b/libsnes/libco/libco.c new file mode 100644 index 0000000..c22153f --- /dev/null +++ b/libsnes/libco/libco.c @@ -0,0 +1,28 @@ +/* + libco + auto-selection module + license: public domain +*/ + +#if defined(__GNUC__) && defined(__i386__) + #include "x86.c" +#elif defined(__GNUC__) && defined(__amd64__) + #include "amd64.c" +#elif defined(_MSC_VER) && defined(_WIN64) + #include "amd64_msvc.c" +#elif defined(_MSC_VER) && defined(_WIN32) + #include "x86_msvc.c" +#elif defined(__GNUC__) && (defined(__powerpc__) || defined(__POWERPC__)) // Seems to run fine on PPC G5 OSX even when no __ELF__ is defined. + #include "ppc.c" +#elif defined(__GNUC__) + #include "sjlj.c" +#elif defined(_MSC_VER) && defined(_M_IX86) + #include "x86.c" +#elif defined(_MSC_VER) && defined(_M_AMD64) + #include "amd64.c" +#elif defined(_MSC_VER) + #include "fiber.c" +#else + #error "libco: unsupported processor, compiler or operating system" +#endif + diff --git a/libsnes/libco/libco.h b/libsnes/libco/libco.h new file mode 100644 index 0000000..deb954f --- /dev/null +++ b/libsnes/libco/libco.h @@ -0,0 +1,34 @@ +/* + libco + version: 0.16 (2010-12-24) + license: public domain +*/ + +#ifndef LIBCO_H +#define LIBCO_H + +#ifdef LIBCO_C + #ifdef LIBCO_MP + #define thread_local __thread + #else + #define thread_local + #endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void* cothread_t; + +cothread_t co_active(); +cothread_t co_create(unsigned int, void (*)(void)); +void co_delete(cothread_t); +void co_switch(cothread_t); + +#ifdef __cplusplus +} +#endif + +/* ifndef LIBCO_H */ +#endif diff --git a/libsnes/libco/ppc.c b/libsnes/libco/ppc.c new file mode 100644 index 0000000..a6028fd --- /dev/null +++ b/libsnes/libco/ppc.c @@ -0,0 +1,407 @@ +/* + libco.ppc (2010-10-17) + author: blargg + license: public domain +*/ + +/* PowerPC 32/64 using embedded or external asm, with optional +floating-point and AltiVec save/restore */ + +#define LIBCO_C +#include "libco.h" +#include +#include +#include + +#define LIBCO_MPROTECT (__unix__ && !LIBCO_PPC_ASM) + +#if LIBCO_MPROTECT + #include + #include +#endif + +/* State format (offsets in 32-bit words) + ++0 Pointer to swap code + Rest of function descriptor for entry function ++8 PC ++10 SP + Special regs + GPRs + FPRs + VRs + stack +*/ + +enum { state_size = 1024 }; +enum { above_stack = 2048 }; +enum { stack_align = 256 }; + +static thread_local cothread_t co_active_handle = 0; + +/**** Determine environment ****/ + +#define LIBCO_PPC64 (_ARCH_PPC64 || __PPC64__ || __ppc64__ || __powerpc64__) + +/* Whether function calls are indirect through a descriptor, +or are directly to function */ +#ifndef LIBCO_PPCDESC + #if !_CALL_SYSV && (_CALL_AIX || _CALL_AIXDESC || LIBCO_PPC64) + #define LIBCO_PPCDESC 1 + #endif +#endif + +#ifdef LIBCO_PPC_ASM + + #ifdef __cplusplus + extern "C" + #endif + + /* Swap code is in ppc.S */ + void co_swap_asm( cothread_t, cothread_t ); + #define CO_SWAP_ASM( x, y ) co_swap_asm( x, y ) + +#else + +/* Swap code is here in array. Please leave dieassembly comments, +as they make it easy to see what it does, and reorder instructions +if one wants to see whether that improves performance. */ +static const uint32_t libco_ppc_code [] = { +#if LIBCO_PPC64 + 0x7d000026, /* mfcr r8 */ + 0xf8240028, /* std r1,40(r4) */ + 0x7d2802a6, /* mflr r9 */ + 0xf9c40048, /* std r14,72(r4) */ + 0xf9e40050, /* std r15,80(r4) */ + 0xfa040058, /* std r16,88(r4) */ + 0xfa240060, /* std r17,96(r4) */ + 0xfa440068, /* std r18,104(r4) */ + 0xfa640070, /* std r19,112(r4) */ + 0xfa840078, /* std r20,120(r4) */ + 0xfaa40080, /* std r21,128(r4) */ + 0xfac40088, /* std r22,136(r4) */ + 0xfae40090, /* std r23,144(r4) */ + 0xfb040098, /* std r24,152(r4) */ + 0xfb2400a0, /* std r25,160(r4) */ + 0xfb4400a8, /* std r26,168(r4) */ + 0xfb6400b0, /* std r27,176(r4) */ + 0xfb8400b8, /* std r28,184(r4) */ + 0xfba400c0, /* std r29,192(r4) */ + 0xfbc400c8, /* std r30,200(r4) */ + 0xfbe400d0, /* std r31,208(r4) */ + 0xf9240020, /* std r9,32(r4) */ + 0xe8e30020, /* ld r7,32(r3) */ + 0xe8230028, /* ld r1,40(r3) */ + 0x48000009, /* bl 1 */ + 0x7fe00008, /* trap */ + 0x91040030,/*1:stw r8,48(r4) */ + 0x80c30030, /* lwz r6,48(r3) */ + 0x7ce903a6, /* mtctr r7 */ + 0xe9c30048, /* ld r14,72(r3) */ + 0xe9e30050, /* ld r15,80(r3) */ + 0xea030058, /* ld r16,88(r3) */ + 0xea230060, /* ld r17,96(r3) */ + 0xea430068, /* ld r18,104(r3) */ + 0xea630070, /* ld r19,112(r3) */ + 0xea830078, /* ld r20,120(r3) */ + 0xeaa30080, /* ld r21,128(r3) */ + 0xeac30088, /* ld r22,136(r3) */ + 0xeae30090, /* ld r23,144(r3) */ + 0xeb030098, /* ld r24,152(r3) */ + 0xeb2300a0, /* ld r25,160(r3) */ + 0xeb4300a8, /* ld r26,168(r3) */ + 0xeb6300b0, /* ld r27,176(r3) */ + 0xeb8300b8, /* ld r28,184(r3) */ + 0xeba300c0, /* ld r29,192(r3) */ + 0xebc300c8, /* ld r30,200(r3) */ + 0xebe300d0, /* ld r31,208(r3) */ + 0x7ccff120, /* mtcr r6 */ +#else + 0x7d000026, /* mfcr r8 */ + 0x90240028, /* stw r1,40(r4) */ + 0x7d2802a6, /* mflr r9 */ + 0x91a4003c, /* stw r13,60(r4) */ + 0x91c40040, /* stw r14,64(r4) */ + 0x91e40044, /* stw r15,68(r4) */ + 0x92040048, /* stw r16,72(r4) */ + 0x9224004c, /* stw r17,76(r4) */ + 0x92440050, /* stw r18,80(r4) */ + 0x92640054, /* stw r19,84(r4) */ + 0x92840058, /* stw r20,88(r4) */ + 0x92a4005c, /* stw r21,92(r4) */ + 0x92c40060, /* stw r22,96(r4) */ + 0x92e40064, /* stw r23,100(r4) */ + 0x93040068, /* stw r24,104(r4) */ + 0x9324006c, /* stw r25,108(r4) */ + 0x93440070, /* stw r26,112(r4) */ + 0x93640074, /* stw r27,116(r4) */ + 0x93840078, /* stw r28,120(r4) */ + 0x93a4007c, /* stw r29,124(r4) */ + 0x93c40080, /* stw r30,128(r4) */ + 0x93e40084, /* stw r31,132(r4) */ + 0x91240020, /* stw r9,32(r4) */ + 0x80e30020, /* lwz r7,32(r3) */ + 0x80230028, /* lwz r1,40(r3) */ + 0x48000009, /* bl 1 */ + 0x7fe00008, /* trap */ + 0x91040030,/*1:stw r8,48(r4) */ + 0x80c30030, /* lwz r6,48(r3) */ + 0x7ce903a6, /* mtctr r7 */ + 0x81a3003c, /* lwz r13,60(r3) */ + 0x81c30040, /* lwz r14,64(r3) */ + 0x81e30044, /* lwz r15,68(r3) */ + 0x82030048, /* lwz r16,72(r3) */ + 0x8223004c, /* lwz r17,76(r3) */ + 0x82430050, /* lwz r18,80(r3) */ + 0x82630054, /* lwz r19,84(r3) */ + 0x82830058, /* lwz r20,88(r3) */ + 0x82a3005c, /* lwz r21,92(r3) */ + 0x82c30060, /* lwz r22,96(r3) */ + 0x82e30064, /* lwz r23,100(r3) */ + 0x83030068, /* lwz r24,104(r3) */ + 0x8323006c, /* lwz r25,108(r3) */ + 0x83430070, /* lwz r26,112(r3) */ + 0x83630074, /* lwz r27,116(r3) */ + 0x83830078, /* lwz r28,120(r3) */ + 0x83a3007c, /* lwz r29,124(r3) */ + 0x83c30080, /* lwz r30,128(r3) */ + 0x83e30084, /* lwz r31,132(r3) */ + 0x7ccff120, /* mtcr r6 */ +#endif + +#ifndef LIBCO_PPC_NOFP + 0xd9c400e0, /* stfd f14,224(r4) */ + 0xd9e400e8, /* stfd f15,232(r4) */ + 0xda0400f0, /* stfd f16,240(r4) */ + 0xda2400f8, /* stfd f17,248(r4) */ + 0xda440100, /* stfd f18,256(r4) */ + 0xda640108, /* stfd f19,264(r4) */ + 0xda840110, /* stfd f20,272(r4) */ + 0xdaa40118, /* stfd f21,280(r4) */ + 0xdac40120, /* stfd f22,288(r4) */ + 0xdae40128, /* stfd f23,296(r4) */ + 0xdb040130, /* stfd f24,304(r4) */ + 0xdb240138, /* stfd f25,312(r4) */ + 0xdb440140, /* stfd f26,320(r4) */ + 0xdb640148, /* stfd f27,328(r4) */ + 0xdb840150, /* stfd f28,336(r4) */ + 0xdba40158, /* stfd f29,344(r4) */ + 0xdbc40160, /* stfd f30,352(r4) */ + 0xdbe40168, /* stfd f31,360(r4) */ + 0xc9c300e0, /* lfd f14,224(r3) */ + 0xc9e300e8, /* lfd f15,232(r3) */ + 0xca0300f0, /* lfd f16,240(r3) */ + 0xca2300f8, /* lfd f17,248(r3) */ + 0xca430100, /* lfd f18,256(r3) */ + 0xca630108, /* lfd f19,264(r3) */ + 0xca830110, /* lfd f20,272(r3) */ + 0xcaa30118, /* lfd f21,280(r3) */ + 0xcac30120, /* lfd f22,288(r3) */ + 0xcae30128, /* lfd f23,296(r3) */ + 0xcb030130, /* lfd f24,304(r3) */ + 0xcb230138, /* lfd f25,312(r3) */ + 0xcb430140, /* lfd f26,320(r3) */ + 0xcb630148, /* lfd f27,328(r3) */ + 0xcb830150, /* lfd f28,336(r3) */ + 0xcba30158, /* lfd f29,344(r3) */ + 0xcbc30160, /* lfd f30,352(r3) */ + 0xcbe30168, /* lfd f31,360(r3) */ +#endif + +#ifdef __ALTIVEC__ + 0x7ca042a6, /* mfvrsave r5 */ + 0x39040180, /* addi r8,r4,384 */ + 0x39240190, /* addi r9,r4,400 */ + 0x70a00fff, /* andi. r0,r5,4095 */ + 0x90a40034, /* stw r5,52(r4) */ + 0x4182005c, /* beq- 2 */ + 0x7e8041ce, /* stvx v20,r0,r8 */ + 0x39080020, /* addi r8,r8,32 */ + 0x7ea049ce, /* stvx v21,r0,r9 */ + 0x39290020, /* addi r9,r9,32 */ + 0x7ec041ce, /* stvx v22,r0,r8 */ + 0x39080020, /* addi r8,r8,32 */ + 0x7ee049ce, /* stvx v23,r0,r9 */ + 0x39290020, /* addi r9,r9,32 */ + 0x7f0041ce, /* stvx v24,r0,r8 */ + 0x39080020, /* addi r8,r8,32 */ + 0x7f2049ce, /* stvx v25,r0,r9 */ + 0x39290020, /* addi r9,r9,32 */ + 0x7f4041ce, /* stvx v26,r0,r8 */ + 0x39080020, /* addi r8,r8,32 */ + 0x7f6049ce, /* stvx v27,r0,r9 */ + 0x39290020, /* addi r9,r9,32 */ + 0x7f8041ce, /* stvx v28,r0,r8 */ + 0x39080020, /* addi r8,r8,32 */ + 0x7fa049ce, /* stvx v29,r0,r9 */ + 0x39290020, /* addi r9,r9,32 */ + 0x7fc041ce, /* stvx v30,r0,r8 */ + 0x7fe049ce, /* stvx v31,r0,r9 */ + 0x80a30034,/*2:lwz r5,52(r3) */ + 0x39030180, /* addi r8,r3,384 */ + 0x39230190, /* addi r9,r3,400 */ + 0x70a00fff, /* andi. r0,r5,4095 */ + 0x7ca043a6, /* mtvrsave r5 */ + 0x4d820420, /* beqctr */ + 0x7e8040ce, /* lvx v20,r0,r8 */ + 0x39080020, /* addi r8,r8,32 */ + 0x7ea048ce, /* lvx v21,r0,r9 */ + 0x39290020, /* addi r9,r9,32 */ + 0x7ec040ce, /* lvx v22,r0,r8 */ + 0x39080020, /* addi r8,r8,32 */ + 0x7ee048ce, /* lvx v23,r0,r9 */ + 0x39290020, /* addi r9,r9,32 */ + 0x7f0040ce, /* lvx v24,r0,r8 */ + 0x39080020, /* addi r8,r8,32 */ + 0x7f2048ce, /* lvx v25,r0,r9 */ + 0x39290020, /* addi r9,r9,32 */ + 0x7f4040ce, /* lvx v26,r0,r8 */ + 0x39080020, /* addi r8,r8,32 */ + 0x7f6048ce, /* lvx v27,r0,r9 */ + 0x39290020, /* addi r9,r9,32 */ + 0x7f8040ce, /* lvx v28,r0,r8 */ + 0x39080020, /* addi r8,r8,32 */ + 0x7fa048ce, /* lvx v29,r0,r9 */ + 0x39290020, /* addi r9,r9,32 */ + 0x7fc040ce, /* lvx v30,r0,r8 */ + 0x7fe048ce, /* lvx v31,r0,r9 */ +#endif + + 0x4e800420, /* bctr */ +}; + + #if LIBCO_PPCDESC + /* Function call goes through indirect descriptor */ + #define CO_SWAP_ASM( x, y ) \ + ((void (*)( cothread_t, cothread_t )) (uintptr_t) x)( x, y ) + #else + /* Function call goes directly to code */ + #define CO_SWAP_ASM( x, y ) \ + ((void (*)( cothread_t, cothread_t )) (uintptr_t) libco_ppc_code)( x, y ) + #endif + +#endif + +static uint32_t* co_create_( unsigned size, uintptr_t entry ) +{ + uint32_t* t = (uint32_t*) malloc( size ); + + (void) entry; + + #if LIBCO_PPCDESC + if ( t ) + { + /* Copy entry's descriptor */ + memcpy( t, (void*) entry, sizeof (void*) * 3 ); + + /* Set function pointer to swap routine */ + #ifdef LIBCO_PPC_ASM + *(const void**) t = *(void**) &co_swap_asm; + #else + *(const void**) t = libco_ppc_code; + #endif + } + #endif + + return t; +} + +cothread_t co_create( unsigned int size, void (*entry_)( void ) ) +{ + uintptr_t entry = (uintptr_t) entry_; + uint32_t* t = NULL; + + /* Be sure main thread was successfully allocated */ + if ( co_active() ) + { + size += state_size + above_stack + stack_align; + t = co_create_( size, entry ); + } + + if ( t ) + { + uintptr_t sp; + int shift; + + /* Save current registers into new thread, so that any special ones will + have proper values when thread is begun */ + CO_SWAP_ASM( t, t ); + + #if LIBCO_PPCDESC + /* Get real address */ + entry = (uintptr_t) *(void**) entry; + #endif + + /* Put stack near end of block, and align */ + sp = (uintptr_t) t + size - above_stack; + sp -= sp % stack_align; + + /* On PPC32, we save and restore GPRs as 32 bits. For PPC64, we + save and restore them as 64 bits, regardless of the size the ABI + uses. So, we manually write pointers at the proper size. We always + save and restore at the same address, and since PPC is big-endian, + we must put the low byte first on PPC32. */ + + /* If uintptr_t is 32 bits, >>32 is undefined behavior, so we do two shifts + and don't have to care how many bits uintptr_t is. */ + #if LIBCO_PPC64 + shift = 16; + #else + shift = 0; + #endif + + /* Set up so entry will be called on next swap */ + t [8] = (uint32_t) (entry >> shift >> shift); + t [9] = (uint32_t) entry; + + t [10] = (uint32_t) (sp >> shift >> shift); + t [11] = (uint32_t) sp; + } + + return t; +} + +void co_delete( cothread_t t ) +{ + free( t ); +} + +static void co_init_( void ) +{ + #if LIBCO_MPROTECT + /* TODO: pre- and post-pad PPC code so that this doesn't make other + data executable and writable */ + long page_size = sysconf( _SC_PAGESIZE ); + if ( page_size > 0 ) + { + uintptr_t align = page_size; + uintptr_t begin = (uintptr_t) libco_ppc_code; + uintptr_t end = begin + sizeof libco_ppc_code; + + /* Align beginning and end */ + end += align - 1; + end -= end % align; + begin -= begin % align; + + mprotect( (void*) begin, end - begin, PROT_READ | PROT_WRITE | PROT_EXEC ); + } + #endif + + co_active_handle = co_create_( state_size, (uintptr_t) &co_switch ); +} + +cothread_t co_active() +{ + if ( !co_active_handle ) + co_init_(); + + return co_active_handle; +} + +void co_switch( cothread_t t ) +{ + cothread_t old = co_active_handle; + co_active_handle = t; + + CO_SWAP_ASM( t, old ); +} diff --git a/libsnes/libco/sjlj.c b/libsnes/libco/sjlj.c new file mode 100644 index 0000000..8b72b61 --- /dev/null +++ b/libsnes/libco/sjlj.c @@ -0,0 +1,102 @@ +/* + libco.sjlj (2008-01-28) + author: Nach + license: public domain +*/ + +/* + * Note this was designed for UNIX systems. Based on ideas expressed in a paper + * by Ralf Engelschall. + * For SJLJ on other systems, one would want to rewrite springboard() and + * co_create() and hack the jmb_buf stack pointer. + */ + +#define LIBCO_C +#include "libco.h" +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + sigjmp_buf context; + void (*coentry)(void); + void *stack; +} cothread_struct; + +static thread_local cothread_struct co_primary; +static thread_local cothread_struct *creating, *co_running = 0; + +static void springboard(int ignored) { + if(sigsetjmp(creating->context, 0)) { + co_running->coentry(); + } +} + +cothread_t co_active() { + if(!co_running) co_running = &co_primary; + return (cothread_t)co_running; +} + +cothread_t co_create(unsigned int size, void (*coentry)(void)) { + if(!co_running) co_running = &co_primary; + + cothread_struct *thread = (cothread_struct*)malloc(sizeof(cothread_struct)); + if(thread) { + struct sigaction handler; + struct sigaction old_handler; + + stack_t stack; + stack_t old_stack; + + thread->coentry = thread->stack = 0; + + stack.ss_flags = 0; + stack.ss_size = size; + thread->stack = stack.ss_sp = malloc(size); + if(stack.ss_sp && !sigaltstack(&stack, &old_stack)) { + handler.sa_handler = springboard; + handler.sa_flags = SA_ONSTACK; + sigemptyset(&handler.sa_mask); + creating = thread; + + if(!sigaction(SIGUSR1, &handler, &old_handler)) { + if(!raise(SIGUSR1)) { + thread->coentry = coentry; + } + sigaltstack(&old_stack, 0); + sigaction(SIGUSR1, &old_handler, 0); + } + } + + if(thread->coentry != coentry) { + co_delete(thread); + thread = 0; + } + } + + return (cothread_t)thread; +} + +void co_delete(cothread_t cothread) { + if(cothread) { + if(((cothread_struct*)cothread)->stack) { + free(((cothread_struct*)cothread)->stack); + } + free(cothread); + } +} + +void co_switch(cothread_t cothread) { + if(!sigsetjmp(co_running->context, 0)) { + co_running = (cothread_struct*)cothread; + siglongjmp(co_running->context, 1); + } +} + +#ifdef __cplusplus +} +#endif diff --git a/libsnes/libco/ucontext.c b/libsnes/libco/ucontext.c new file mode 100644 index 0000000..17472f6 --- /dev/null +++ b/libsnes/libco/ucontext.c @@ -0,0 +1,67 @@ +/* + libco.ucontext (2008-01-28) + author: Nach + license: public domain +*/ + +/* + * WARNING: the overhead of POSIX ucontext is very high, + * assembly versions of libco or libco_sjlj should be much faster + * + * This library only exists for two reasons: + * 1 - as an initial test for the viability of a ucontext implementation + * 2 - to demonstrate the power and speed of libco over existing implementations, + * such as pth (which defaults to wrapping ucontext on unix targets) + * + * Use this library only as a *last resort* + */ + +#define LIBCO_C +#include "libco.h" +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +static thread_local ucontext_t co_primary; +static thread_local ucontext_t *co_running = 0; + +cothread_t co_active() { + if(!co_running) co_running = &co_primary; + return (cothread_t)co_running; +} + +cothread_t co_create(unsigned int heapsize, void (*coentry)(void)) { + if(!co_running) co_running = &co_primary; + ucontext_t *thread = (ucontext_t*)malloc(sizeof(ucontext_t)); + if(thread) { + if((!getcontext(thread) && !(thread->uc_stack.ss_sp = 0)) && (thread->uc_stack.ss_sp = malloc(heapsize))) { + thread->uc_link = co_running; + thread->uc_stack.ss_size = heapsize; + makecontext(thread, coentry, 0); + } else { + co_delete((cothread_t)thread); + thread = 0; + } + } + return (cothread_t)thread; +} + +void co_delete(cothread_t cothread) { + if(cothread) { + if(((ucontext_t*)cothread)->uc_stack.ss_sp) { free(((ucontext_t*)cothread)->uc_stack.ss_sp); } + free(cothread); + } +} + +void co_switch(cothread_t cothread) { + ucontext_t *old_thread = co_running; + co_running = (ucontext_t*)cothread; + swapcontext(old_thread, co_running); +} + +#ifdef __cplusplus +} +#endif diff --git a/libsnes/libco/x86.c b/libsnes/libco/x86.c new file mode 100644 index 0000000..e9de521 --- /dev/null +++ b/libsnes/libco/x86.c @@ -0,0 +1,99 @@ +/* + libco.x86 (2009-10-12) + author: byuu + license: public domain +*/ + +#define LIBCO_C +#include "libco.h" +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(_MSC_VER) + #define fastcall __fastcall +#elif defined(__GNUC__) + #define fastcall __attribute__((fastcall)) +#else + #error "libco: please define fastcall macro" +#endif + +static thread_local long co_active_buffer[64]; +static thread_local cothread_t co_active_handle = 0; + +// ASM co_swap +void fastcall co_swap(cothread_t new_thread, cothread_t old_thread); + +#ifdef _WIN32 +#define ASM_CO_SWAP_DEF \ + ".globl @co_swap@8\n" \ + "@co_swap@8:\n" +#else +#define ASM_CO_SWAP_DEF \ + ".globl co_swap\n" \ + ".globl _co_swap\n" \ + "_co_swap:\n" \ + "co_swap:\n" +#endif + +// ABI: fastcall +asm ( + ".text\n" + ASM_CO_SWAP_DEF + "movl %esp, (%edx) # Save stack pointer, and restore it again.\n" + "movl (%ecx), %esp\n" + "popl %eax\n" + + "movl %ebp, 0x4(%edx) # Save non-volatile registers to buffer.\n" + "movl %esi, 0x8(%edx)\n" + "movl %edi, 0xc(%edx)\n" + "movl %ebx, 0x10(%edx)\n" + + "movl 0x4(%ecx), %ebp # Restore non-volatile registers from buffer.\n" + "movl 0x8(%ecx), %esi\n" + "movl 0xc(%ecx), %edi\n" + "movl 0x10(%ecx), %ebx\n" + + "jmp *%eax # Jump back to saved PC.\n" +); + +static void crash() { + assert(0); /* called only if cothread_t entrypoint returns */ +} + +cothread_t co_active() { + if(!co_active_handle) co_active_handle = &co_active_buffer; + return co_active_handle; +} + +cothread_t co_create(unsigned int size, void (*entrypoint)(void)) { + cothread_t handle; + if(!co_active_handle) co_active_handle = &co_active_buffer; + size += 256; /* allocate additional space for storage */ + size &= ~15; /* align stack to 16-byte boundary */ + + if(handle = (cothread_t)malloc(size)) { + long *p = (long*)((char*)handle + size); /* seek to top of stack */ + *--p = (long)crash; /* crash if entrypoint returns */ + *--p = (long)entrypoint; /* start of function */ + *(long*)handle = (long)p; /* stack pointer */ + } + + return handle; +} + +void co_delete(cothread_t handle) { + free(handle); +} + +void co_switch(cothread_t handle) { + register cothread_t co_previous_handle = co_active_handle; + co_swap(co_active_handle = handle, co_previous_handle); +} + +#ifdef __cplusplus +} +#endif diff --git a/libsnes/libco/x86_msvc.c b/libsnes/libco/x86_msvc.c new file mode 100644 index 0000000..728d83e --- /dev/null +++ b/libsnes/libco/x86_msvc.c @@ -0,0 +1,73 @@ +/* + libco.x86 (2009-10-12) + author: byuu + license: public domain +*/ + +#define LIBCO_C +#include "libco.h" +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +static thread_local long co_active_buffer[64]; +static thread_local cothread_t co_active_handle = 0; +static void (__fastcall *co_swap)(cothread_t, cothread_t) = 0; + +//ABI: fastcall +static unsigned char co_swap_function[] = { + 0x89, 0x22, 0x8B, 0x21, 0x58, 0x89, 0x6A, 0x04, 0x89, 0x72, 0x08, 0x89, 0x7A, 0x0C, 0x89, 0x5A, + 0x10, 0x8B, 0x69, 0x04, 0x8B, 0x71, 0x08, 0x8B, 0x79, 0x0C, 0x8B, 0x59, 0x10, 0xFF, 0xE0, +}; + +#include + +void co_init() { + DWORD old_privileges; + VirtualProtect(co_swap_function, sizeof co_swap_function, PAGE_EXECUTE_READWRITE, &old_privileges); +} + +static void crash() { + assert(0); /* called only if cothread_t entrypoint returns */ +} + +cothread_t co_active() { + if(!co_active_handle) co_active_handle = &co_active_buffer; + return co_active_handle; +} + +cothread_t co_create(unsigned int size, void (*entrypoint)(void)) { + cothread_t handle; + if(!co_swap) { + co_init(); + co_swap = (void (__fastcall*)(cothread_t, cothread_t))co_swap_function; + } + if(!co_active_handle) co_active_handle = &co_active_buffer; + size += 256; /* allocate additional space for storage */ + size &= ~15; /* align stack to 16-byte boundary */ + + if(handle = (cothread_t)malloc(size)) { + long *p = (long*)((char*)handle + size); /* seek to top of stack */ + *--p = (long)crash; /* crash if entrypoint returns */ + *--p = (long)entrypoint; /* start of function */ + *(long*)handle = (long)p; /* stack pointer */ + } + + return handle; +} + +void co_delete(cothread_t handle) { + free(handle); +} + +void co_switch(cothread_t handle) { + register cothread_t co_previous_handle = co_active_handle; + co_swap(co_active_handle = handle, co_previous_handle); +} + +#ifdef __cplusplus +} +#endif diff --git a/libsnes/link.T b/libsnes/link.T new file mode 100644 index 0000000..432292c --- /dev/null +++ b/libsnes/link.T @@ -0,0 +1,4 @@ +{ + global: snes_*; + local: *; +}; diff --git a/libsnes/msvc/libsnes-compat/libsnes-compat.vcxproj b/libsnes/msvc/libsnes-compat/libsnes-compat.vcxproj new file mode 100644 index 0000000..2993083 --- /dev/null +++ b/libsnes/msvc/libsnes-compat/libsnes-compat.vcxproj @@ -0,0 +1,193 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {A5AC8D29-9F37-4F0A-93A7-C428400ED705} + Win32Proj + libsnescompat + + + + DynamicLibrary + true + Unicode + + + DynamicLibrary + true + Unicode + + + DynamicLibrary + false + true + Unicode + + + DynamicLibrary + false + true + Unicode + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + + + TurnOffAllWarnings + Disabled + WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBSNESCOMPAT_EXPORTS;%(PreprocessorDefinitions) + ..\.. + /D PROFILE_COMPATIBILITY %(AdditionalOptions) + + + Windows + true + + + + + + + TurnOffAllWarnings + Disabled + WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBSNESCOMPAT_EXPORTS;%(PreprocessorDefinitions) + ..\.. + /D PROFILE_COMPATIBILITY %(AdditionalOptions) + + + Windows + true + + + + + TurnOffAllWarnings + + + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBSNESCOMPAT_EXPORTS;%(PreprocessorDefinitions) + ..\.. + Speed + true + /D PROFILE_COMPATIBILITY %(AdditionalOptions) + + + Windows + true + true + true + + + + + TurnOffAllWarnings + + + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBSNESCOMPAT_EXPORTS;%(PreprocessorDefinitions) + ..\.. + Speed + true + /D PROFILE_COMPATIBILITY %(AdditionalOptions) + + + Windows + true + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/libsnes/msvc/libsnes-compat/libsnes-compat.vcxproj.filters b/libsnes/msvc/libsnes-compat/libsnes-compat.vcxproj.filters new file mode 100644 index 0000000..6ddd592 --- /dev/null +++ b/libsnes/msvc/libsnes-compat/libsnes-compat.vcxproj.filters @@ -0,0 +1,136 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/libsnes/msvc/libsnes-performance/libsnes-performance.vcxproj b/libsnes/msvc/libsnes-performance/libsnes-performance.vcxproj new file mode 100644 index 0000000..0e18e39 --- /dev/null +++ b/libsnes/msvc/libsnes-performance/libsnes-performance.vcxproj @@ -0,0 +1,195 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {DD87D448-CA73-4ED4-894D-DB6275BA5E44} + Win32Proj + libsnesperformance + + + + DynamicLibrary + true + Unicode + + + DynamicLibrary + true + Unicode + + + DynamicLibrary + false + true + Unicode + + + DynamicLibrary + false + true + Unicode + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + + + TurnOffAllWarnings + Disabled + WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBSNESPERFORMANCE_EXPORTS;%(PreprocessorDefinitions) + ..\.. + /D PROFILE_PERFORMANCE %(AdditionalOptions) + + + Windows + true + + + + + + + TurnOffAllWarnings + Disabled + WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBSNESPERFORMANCE_EXPORTS;%(PreprocessorDefinitions) + ..\.. + /D PROFILE_PERFORMANCE %(AdditionalOptions) + + + Windows + true + + + + + TurnOffAllWarnings + + + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBSNESPERFORMANCE_EXPORTS;%(PreprocessorDefinitions) + ..\.. + Speed + true + /D PROFILE_PERFORMANCE %(AdditionalOptions) + true + + + Windows + true + true + true + + + + + TurnOffAllWarnings + + + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBSNESPERFORMANCE_EXPORTS;%(PreprocessorDefinitions) + ..\.. + Speed + true + /D PROFILE_PERFORMANCE %(AdditionalOptions) + true + + + Windows + true + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/libsnes/msvc/libsnes-performance/libsnes-performance.vcxproj.filters b/libsnes/msvc/libsnes-performance/libsnes-performance.vcxproj.filters new file mode 100644 index 0000000..d7c2ed8 --- /dev/null +++ b/libsnes/msvc/libsnes-performance/libsnes-performance.vcxproj.filters @@ -0,0 +1,136 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/libsnes/msvc/libsnes.sln b/libsnes/msvc/libsnes.sln new file mode 100644 index 0000000..022955d --- /dev/null +++ b/libsnes/msvc/libsnes.sln @@ -0,0 +1,46 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual C++ Express 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libsnes-accuracy", "libsnes\libsnes.vcxproj", "{E7E2E52D-A3A4-4118-B74D-892315EB4719}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libsnes-performance", "libsnes-performance\libsnes-performance.vcxproj", "{DD87D448-CA73-4ED4-894D-DB6275BA5E44}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libsnes-compat", "libsnes-compat\libsnes-compat.vcxproj", "{A5AC8D29-9F37-4F0A-93A7-C428400ED705}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {E7E2E52D-A3A4-4118-B74D-892315EB4719}.Debug|Win32.ActiveCfg = Debug|Win32 + {E7E2E52D-A3A4-4118-B74D-892315EB4719}.Debug|Win32.Build.0 = Debug|Win32 + {E7E2E52D-A3A4-4118-B74D-892315EB4719}.Debug|x64.ActiveCfg = Debug|x64 + {E7E2E52D-A3A4-4118-B74D-892315EB4719}.Debug|x64.Build.0 = Debug|x64 + {E7E2E52D-A3A4-4118-B74D-892315EB4719}.Release|Win32.ActiveCfg = Release|Win32 + {E7E2E52D-A3A4-4118-B74D-892315EB4719}.Release|Win32.Build.0 = Release|Win32 + {E7E2E52D-A3A4-4118-B74D-892315EB4719}.Release|x64.ActiveCfg = Release|x64 + {E7E2E52D-A3A4-4118-B74D-892315EB4719}.Release|x64.Build.0 = Release|x64 + {DD87D448-CA73-4ED4-894D-DB6275BA5E44}.Debug|Win32.ActiveCfg = Debug|Win32 + {DD87D448-CA73-4ED4-894D-DB6275BA5E44}.Debug|Win32.Build.0 = Debug|Win32 + {DD87D448-CA73-4ED4-894D-DB6275BA5E44}.Debug|x64.ActiveCfg = Debug|x64 + {DD87D448-CA73-4ED4-894D-DB6275BA5E44}.Debug|x64.Build.0 = Debug|x64 + {DD87D448-CA73-4ED4-894D-DB6275BA5E44}.Release|Win32.ActiveCfg = Release|Win32 + {DD87D448-CA73-4ED4-894D-DB6275BA5E44}.Release|Win32.Build.0 = Release|Win32 + {DD87D448-CA73-4ED4-894D-DB6275BA5E44}.Release|x64.ActiveCfg = Release|x64 + {DD87D448-CA73-4ED4-894D-DB6275BA5E44}.Release|x64.Build.0 = Release|x64 + {A5AC8D29-9F37-4F0A-93A7-C428400ED705}.Debug|Win32.ActiveCfg = Debug|Win32 + {A5AC8D29-9F37-4F0A-93A7-C428400ED705}.Debug|Win32.Build.0 = Debug|Win32 + {A5AC8D29-9F37-4F0A-93A7-C428400ED705}.Debug|x64.ActiveCfg = Debug|x64 + {A5AC8D29-9F37-4F0A-93A7-C428400ED705}.Debug|x64.Build.0 = Debug|x64 + {A5AC8D29-9F37-4F0A-93A7-C428400ED705}.Release|Win32.ActiveCfg = Release|Win32 + {A5AC8D29-9F37-4F0A-93A7-C428400ED705}.Release|Win32.Build.0 = Release|Win32 + {A5AC8D29-9F37-4F0A-93A7-C428400ED705}.Release|x64.ActiveCfg = Release|x64 + {A5AC8D29-9F37-4F0A-93A7-C428400ED705}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/libsnes/msvc/libsnes/libsnes.vcxproj b/libsnes/msvc/libsnes/libsnes.vcxproj new file mode 100644 index 0000000..4aa8cd6 --- /dev/null +++ b/libsnes/msvc/libsnes/libsnes.vcxproj @@ -0,0 +1,196 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {E7E2E52D-A3A4-4118-B74D-892315EB4719} + Win32Proj + libsnes + libsnes-accuracy + + + + DynamicLibrary + true + Unicode + + + DynamicLibrary + true + Unicode + + + DynamicLibrary + false + true + Unicode + + + DynamicLibrary + false + true + Unicode + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + + + TurnOffAllWarnings + Disabled + WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBSNES_EXPORTS;%(PreprocessorDefinitions) + /D PROFILE_ACCURACY %(AdditionalOptions) + ..\..;%(AdditionalIncludeDirectories) + + + Windows + true + + + + + + + TurnOffAllWarnings + Disabled + WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBSNES_EXPORTS;%(PreprocessorDefinitions) + /D PROFILE_ACCURACY %(AdditionalOptions) + ..\..;%(AdditionalIncludeDirectories) + + + Windows + true + + + + + TurnOffAllWarnings + + + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBSNES_EXPORTS;%(PreprocessorDefinitions) + ..\..;%(AdditionalIncludeDirectories) + /D PROFILE_ACCURACY %(AdditionalOptions) + Speed + true + true + + + Windows + true + true + true + + + + + TurnOffAllWarnings + + + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBSNES_EXPORTS;%(PreprocessorDefinitions) + ..\..;%(AdditionalIncludeDirectories) + /D PROFILE_ACCURACY %(AdditionalOptions) + Speed + true + true + + + Windows + true + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/libsnes/msvc/libsnes/libsnes.vcxproj.filters b/libsnes/msvc/libsnes/libsnes.vcxproj.filters new file mode 100644 index 0000000..3041fc6 --- /dev/null +++ b/libsnes/msvc/libsnes/libsnes.vcxproj.filters @@ -0,0 +1,136 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/libsnes/nall/C++98.hpp b/libsnes/nall/C++98.hpp new file mode 100644 index 0000000..605294a --- /dev/null +++ b/libsnes/nall/C++98.hpp @@ -0,0 +1,45 @@ +#ifndef NALL_Cpp98_HPP +#define NALL_Cpp98_HPP + +#include + +namespace std { + template struct integral_constant + { static const T value = v; + typedef T value_type; + typedef integral_constant type; + }; + typedef integral_constant true_type; + typedef integral_constant false_type; + template struct is_array : public false_type { }; + template struct is_array : public true_type { }; + template struct is_array : public true_type { }; + template struct remove_extent { typedef T type; }; + template struct remove_extent { typedef T type; }; + template struct remove_extent { typedef T type; }; + template struct add_const { typedef T const type; }; + template struct remove_reference; + template struct is_same : public false_type { }; + template struct is_same : public true_type { }; + template class initializer_list{ + public: + typedef T value_type; + typedef T const & reference; + typedef T const & const_reference; + typedef size_t size_type; + typedef T const * iterator; + typedef T const * const_iterator; + private: + iterator _M_array; + size_type _M_len; + initializer_list(const_iterator __a, size_type __l) : _M_array(__a), _M_len(__l) { } + public: + initializer_list() : _M_array(NULL), _M_len(0) { } + size_type size () const { return _M_len; } + const_iterator begin() const { return _M_array; } + const_iterator end () const { return begin() + size(); } + }; +} + +#endif + diff --git a/libsnes/nall/Makefile b/libsnes/nall/Makefile new file mode 100644 index 0000000..921ac08 --- /dev/null +++ b/libsnes/nall/Makefile @@ -0,0 +1,108 @@ +# Makefile +# author: byuu +# license: public domain + +[A-Z] = A B C D E F G H I J K L M N O P Q R S T U V W X Y Z +[a-z] = a b c d e f g h i j k l m n o p q r s t u v w x y z +[0-9] = 0 1 2 3 4 5 6 7 8 9 +[markup] = ` ~ ! @ \# $$ % ^ & * ( ) - _ = + [ { ] } \ | ; : ' " , < . > / ? +[all] = $([A-Z]) $([a-z]) $([0-9]) $([markup]) +[space] := +[space] += + +##### +# platform detection +##### + +ifeq ($(platform),) + uname := $(shell uname -a) + ifeq ($(uname),) + platform := win + delete = del $(subst /,\,$1) + else ifneq ($(findstring CYGWIN,$(uname)),) + platform := win + delete = del $(subst /,\,$1) + else ifneq ($(findstring MINGW32,$(uname)),) + platform := win + delete = rm -f $1 + else ifneq ($(findstring Darwin,$(uname)),) + platform := osx + delete = rm -f $1 + else ifneq ($(findstring BSD,$(uname)),) + platform := xbsd + delete = rm -f $1 + else + platform := x + delete = rm -f $1 + endif +endif + +ifeq ($(prefix),) + prefix := /usr/local +endif + +##### +# function rwildcard(directory, pattern) +##### +rwildcard = \ + $(strip \ + $(filter $(if $2,$2,%), \ + $(foreach f, \ + $(wildcard $1*), \ + $(eval t = $(call rwildcard,$f/)) \ + $(if $t,$t,$f) \ + ) \ + ) \ + ) + +##### +# function strtr(source, from, to) +##### +strtr = \ + $(eval __temp := $1) \ + $(strip \ + $(foreach c, \ + $(join $(addsuffix :,$2),$3), \ + $(eval __temp := \ + $(subst $(word 1,$(subst :, ,$c)),$(word 2,$(subst :, ,$c)),$(__temp)) \ + ) \ + ) \ + $(__temp) \ + ) + +##### +# function strupper(source) +##### +strupper = $(call strtr,$1,$([a-z]),$([A-Z])) + +##### +# function strlower(source) +##### +strlower = $(call strtr,$1,$([A-Z]),$([a-z])) + +##### +# function strlen(source) +##### +strlen = \ + $(eval __temp := $(subst $([space]),_,$1)) \ + $(words \ + $(strip \ + $(foreach c, \ + $([all]), \ + $(eval __temp := \ + $(subst $c,$c ,$(__temp)) \ + ) \ + ) \ + $(__temp) \ + ) \ + ) + +##### +# function streq(source) +##### +streq = $(if $(filter-out xx,x$(subst $1,,$2)$(subst $2,,$1)x),,1) + +##### +# function strne(source) +##### +strne = $(if $(filter-out xx,x$(subst $1,,$2)$(subst $2,,$1)x),1,) diff --git a/libsnes/nall/algorithm.hpp b/libsnes/nall/algorithm.hpp new file mode 100644 index 0000000..037f0bb --- /dev/null +++ b/libsnes/nall/algorithm.hpp @@ -0,0 +1,17 @@ +#ifndef NALL_ALGORITHM_HPP +#define NALL_ALGORITHM_HPP + +#undef min +#undef max + +namespace nall { + template T min(const T &t, const U &u) { + return t < u ? t : u; + } + + template T max(const T &t, const U &u) { + return t > u ? t : u; + } +} + +#endif diff --git a/libsnes/nall/array.hpp b/libsnes/nall/array.hpp new file mode 100644 index 0000000..9c00ad1 --- /dev/null +++ b/libsnes/nall/array.hpp @@ -0,0 +1,125 @@ +#ifndef NALL_ARRAY_HPP +#define NALL_ARRAY_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace nall { + //dynamic vector array + //neither constructor nor destructor is ever invoked; + //thus, this should only be used for POD objects. + template class array { + protected: + T *pool; + unsigned poolsize, buffersize; + + public: + unsigned size() const { return buffersize; } + unsigned capacity() const { return poolsize; } + + void reset() { + if(pool) free(pool); + pool = 0; + poolsize = 0; + buffersize = 0; + } + + void reserve(unsigned newsize) { + if(newsize == poolsize) return; + + pool = (T*)realloc(pool, newsize * sizeof(T)); + poolsize = newsize; + buffersize = min(buffersize, newsize); + } + + void resize(unsigned newsize) { + if(newsize > poolsize) reserve(bit::round(newsize)); //round reserve size up to power of 2 + buffersize = newsize; + } + + T* get(unsigned minsize = 0) { + if(minsize > buffersize) resize(minsize); + if(minsize > buffersize) throw "array[] out of bounds"; + return pool; + } + + void append(const T data) { + operator[](buffersize) = data; + } + + template void insert(unsigned index, const U list) { + unsigned listsize = container_size(list); + resize(buffersize + listsize); + memmove(pool + index + listsize, pool + index, (buffersize - index) * sizeof(T)); + foreach(item, list) pool[index++] = item; + } + + void insert(unsigned index, const T item) { + insert(index, array(item)); + } + + void remove(unsigned index, unsigned count = 1) { + for(unsigned i = index; count + i < buffersize; i++) { + pool[i] = pool[count + i]; + } + if(count + index >= buffersize) resize(index); //every element >= index was removed + else resize(buffersize - count); + } + + optional find(const T data) { + for(unsigned i = 0; i < size(); i++) if(pool[i] == data) return optional(true, i); + return optional(false, 0); + } + + void clear() { + memset(pool, 0, buffersize * sizeof(T)); + } + + array() : pool(0), poolsize(0), buffersize(0) { + } + + array(std::initializer_list list) : pool(0), poolsize(0), buffersize(0) { + for(const T *p = list.begin(); p != list.end(); ++p) append(*p); + } + + ~array() { + reset(); + } + + //copy + array& operator=(const array &source) { + if(pool) free(pool); + buffersize = source.buffersize; + poolsize = source.poolsize; + pool = (T*)std::malloc(sizeof(T) * poolsize); //allocate entire pool size, + std::memcpy(pool, source.pool, sizeof(T) * buffersize); //... but only copy used pool objects + return *this; + } + + array(const array &source) : pool(0), poolsize(0), buffersize(0) { + operator=(source); + } + + //index + inline T& operator[](unsigned index) { + if(index >= buffersize) resize(index + 1); + if(index >= buffersize) throw "array[] out of bounds"; + return pool[index]; + } + + inline const T& operator[](unsigned index) const { + if(index >= buffersize) throw "array[] out of bounds"; + return pool[index]; + } + }; + + template struct has_size< array > { enum { value = true }; }; +} + +#endif diff --git a/libsnes/nall/base64.hpp b/libsnes/nall/base64.hpp new file mode 100644 index 0000000..e41c87b --- /dev/null +++ b/libsnes/nall/base64.hpp @@ -0,0 +1,90 @@ +#ifndef NALL_BASE64_HPP +#define NALL_BASE64_HPP + +#include +#include + +namespace nall { + class base64 { + public: + static bool encode(char *&output, const uint8_t* input, unsigned inlength) { + output = new char[inlength * 8 / 6 + 6](); + + unsigned i = 0, o = 0; + while(i < inlength) { + switch(i % 3) { + case 0: { + output[o++] = enc(input[i] >> 2); + output[o] = enc((input[i] & 3) << 4); + } break; + + case 1: { + uint8_t prev = dec(output[o]); + output[o++] = enc(prev + (input[i] >> 4)); + output[o] = enc((input[i] & 15) << 2); + } break; + + case 2: { + uint8_t prev = dec(output[o]); + output[o++] = enc(prev + (input[i] >> 6)); + output[o++] = enc(input[i] & 63); + } break; + } + + i++; + } + + return true; + } + + static bool decode(uint8_t *&output, unsigned &outlength, const char *input) { + unsigned inlength = strlen(input), infix = 0; + output = new uint8_t[inlength](); + + unsigned i = 0, o = 0; + while(i < inlength) { + uint8_t x = dec(input[i]); + + switch(i++ & 3) { + case 0: { + output[o] = x << 2; + } break; + + case 1: { + output[o++] |= x >> 4; + output[o] = (x & 15) << 4; + } break; + + case 2: { + output[o++] |= x >> 2; + output[o] = (x & 3) << 6; + } break; + + case 3: { + output[o++] |= x; + } break; + } + } + + outlength = o; + return true; + } + + private: + static char enc(uint8_t n) { + static char lookup_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; + return lookup_table[n & 63]; + } + + static uint8_t dec(char n) { + if(n >= 'A' && n <= 'Z') return n - 'A'; + if(n >= 'a' && n <= 'z') return n - 'a' + 26; + if(n >= '0' && n <= '9') return n - '0' + 52; + if(n == '-') return 62; + if(n == '_') return 63; + return 0; + } + }; +} + +#endif diff --git a/libsnes/nall/bit.hpp b/libsnes/nall/bit.hpp new file mode 100644 index 0000000..ca6ea29 --- /dev/null +++ b/libsnes/nall/bit.hpp @@ -0,0 +1,51 @@ +#ifndef NALL_BIT_HPP +#define NALL_BIT_HPP + +namespace nall { + template inline unsigned uclamp(const unsigned x) { + enum { y = (1U << (bits - 1)) + ((1U << (bits - 1)) - 1) }; + return y + ((x - y) & -(x < y)); //min(x, y); + } + + template inline unsigned uclip(const unsigned x) { + enum { m = (1U << (bits - 1)) + ((1U << (bits - 1)) - 1) }; + return (x & m); + } + + template inline signed sclamp(const signed x) { + enum { b = 1U << (bits - 1), m = (1U << (bits - 1)) - 1 }; + return (x > m) ? m : (x < -b) ? -b : x; + } + + template inline signed sclip(const signed x) { + enum { b = 1U << (bits - 1), m = (1U << bits) - 1 }; + return ((x & m) ^ b) - b; + } + + namespace bit { + //lowest(0b1110) == 0b0010 + template inline T lowest(const T x) { + return x & -x; + } + + //clear_lowest(0b1110) == 0b1100 + template inline T clear_lowest(const T x) { + return x & (x - 1); + } + + //set_lowest(0b0101) == 0b0111 + template inline T set_lowest(const T x) { + return x | (x + 1); + } + + //round up to next highest single bit: + //round(15) == 16, round(16) == 16, round(17) == 32 + inline unsigned round(unsigned x) { + if((x & (x - 1)) == 0) return x; + while(x & (x - 1)) x &= x - 1; + return x << 1; + } + } +} + +#endif diff --git a/libsnes/nall/concept.hpp b/libsnes/nall/concept.hpp new file mode 100644 index 0000000..e82e919 --- /dev/null +++ b/libsnes/nall/concept.hpp @@ -0,0 +1,20 @@ +#ifndef NALL_CONCEPT_HPP +#define NALL_CONCEPT_HPP + +#include +#include + +namespace nall { + template struct has_count { enum { value = false }; }; + template struct has_length { enum { value = false }; }; + template struct has_size { enum { value = false }; }; + template unsigned container_size(const T& object, typename mp_enable_if< has_count >::type = 0) { return object.count(); } + template unsigned container_size(const T& object, typename mp_enable_if< has_length >::type = 0) { return object.length(); } + template unsigned container_size(const T& object, typename mp_enable_if< has_size >::type = 0) { return object.size(); } + + template unsigned container_size(const T& object, typename mp_enable_if< std::is_array >::type = 0) { + return sizeof(T) / sizeof(typename std::remove_extent::type); + } +} + +#endif diff --git a/libsnes/nall/config.hpp b/libsnes/nall/config.hpp new file mode 100644 index 0000000..ebace0c --- /dev/null +++ b/libsnes/nall/config.hpp @@ -0,0 +1,123 @@ +#ifndef NALL_CONFIG_HPP +#define NALL_CONFIG_HPP + +#include +#include +#include + +namespace nall { + namespace configuration_traits { + template struct is_boolean { enum { value = false }; }; + template<> struct is_boolean { enum { value = true }; }; + + template struct is_signed { enum { value = false }; }; + template<> struct is_signed { enum { value = true }; }; + + template struct is_unsigned { enum { value = false }; }; + template<> struct is_unsigned { enum { value = true }; }; + + template struct is_double { enum { value = false }; }; + template<> struct is_double { enum { value = true }; }; + + template struct is_string { enum { value = false }; }; + template<> struct is_string { enum { value = true }; }; + } + + class configuration { + public: + enum type_t { boolean_t, signed_t, unsigned_t, double_t, string_t, unknown_t }; + struct item_t { + uintptr_t data; + string name; + string desc; + type_t type; + + string get() const { + switch(type) { + case boolean_t: return string() << *(bool*)data; + case signed_t: return string() << *(signed*)data; + case unsigned_t: return string() << *(unsigned*)data; + case double_t: return string() << *(double*)data; + case string_t: return string() << "\"" << *(string*)data << "\""; + } + return "???"; + } + + void set(string s) { + switch(type) { + case boolean_t: *(bool*)data = (s == "true"); break; + case signed_t: *(signed*)data = integer(s); break; + case unsigned_t: *(unsigned*)data = decimal(s); break; + case double_t: *(double*)data = fp(s); break; + case string_t: s.trim("\""); *(string*)data = s; break; + } + } + }; + linear_vector list; + + template + void attach(T &data, const char *name, const char *desc = "") { + unsigned n = list.size(); + list[n].data = (uintptr_t)&data; + list[n].name = name; + list[n].desc = desc; + + if(configuration_traits::is_boolean::value) list[n].type = boolean_t; + else if(configuration_traits::is_signed::value) list[n].type = signed_t; + else if(configuration_traits::is_unsigned::value) list[n].type = unsigned_t; + else if(configuration_traits::is_double::value) list[n].type = double_t; + else if(configuration_traits::is_string::value) list[n].type = string_t; + else list[n].type = unknown_t; + } + + virtual bool load(const char *filename) { + string data; + if(data.readfile(filename) == true) { + data.replace("\r", ""); + lstring line; + line.split("\n", data); + + for(unsigned i = 0; i < line.size(); i++) { + if(optional position = qstrpos(line[i], "#")) line[i][position()] = 0; + if(!qstrpos(line[i], " = ")) continue; + + lstring part; + part.qsplit(" = ", line[i]); + part[0].trim(); + part[1].trim(); + + for(unsigned n = 0; n < list.size(); n++) { + if(part[0] == list[n].name) { + list[n].set(part[1]); + break; + } + } + } + + return true; + } else { + return false; + } + } + + virtual bool save(const char *filename) const { + file fp; + if(fp.open(filename, file::mode_write)) { + for(unsigned i = 0; i < list.size(); i++) { + string output; + output << list[i].name << " = " << list[i].get(); + if(list[i].desc != "") output << " # " << list[i].desc; + output << "\r\n"; + fp.print(output); + } + + fp.close(); + return true; + } else { + return false; + } + } + }; +} + +#endif diff --git a/libsnes/nall/crc32.hpp b/libsnes/nall/crc32.hpp new file mode 100644 index 0000000..ad36fbf --- /dev/null +++ b/libsnes/nall/crc32.hpp @@ -0,0 +1,66 @@ +#ifndef NALL_CRC32_HPP +#define NALL_CRC32_HPP + +#include + +namespace nall { + const uint32_t crc32_table[256] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d + }; + + inline uint32_t crc32_adjust(uint32_t crc32, uint8_t input) { + return ((crc32 >> 8) & 0x00ffffff) ^ crc32_table[(crc32 ^ input) & 0xff]; + } + + inline uint32_t crc32_calculate(const uint8_t *data, unsigned length) { + uint32_t crc32 = ~0; + for(unsigned i = 0; i < length; i++) { + crc32 = crc32_adjust(crc32, data[i]); + } + return ~crc32; + } +} + +#endif diff --git a/libsnes/nall/detect.hpp b/libsnes/nall/detect.hpp new file mode 100644 index 0000000..1fd63af --- /dev/null +++ b/libsnes/nall/detect.hpp @@ -0,0 +1,30 @@ +#ifndef NALL_DETECT_HPP +#define NALL_DETECT_HPP + +/* Compiler detection */ + +#if defined(__GNUC__) + #define COMPILER_GCC +#elif defined(_MSC_VER) + #define COMPILER_VISUALC +#endif + +/* Platform detection */ + +#if defined(_WIN32) + #define PLATFORM_WIN +#elif defined(__APPLE__) + #define PLATFORM_OSX +#elif defined(linux) || defined(__sun__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__FreeBSD_kernel__) || defined(__GNU__) + #define PLATFORM_X +#endif + +/* Endian detection */ + +#if defined(__i386__) || defined(__amd64__) || defined(_M_IX86) || defined(_M_AMD64) + #define ARCH_LSB +#elif defined(__powerpc__) || defined(_M_PPC) || defined(__BIG_ENDIAN__) + #define ARCH_MSB +#endif + +#endif diff --git a/libsnes/nall/dictionary.hpp b/libsnes/nall/dictionary.hpp new file mode 100644 index 0000000..695c7b5 --- /dev/null +++ b/libsnes/nall/dictionary.hpp @@ -0,0 +1,72 @@ +#ifndef NALL_DICTIONARY_HPP +#define NALL_DICTIONARY_HPP + +#include +#include +#include + +namespace nall { + class dictionary { + public: + string operator[](const char *input) { + for(unsigned i = 0; i < index_input.size(); i++) { + if(index_input[i] == input) return index_output[i]; + } + + //no match, use input; remove input identifier, if one exists + if(strbegin(input, "{{")) { + if(optional pos = strpos(input, "}}")) { + string temp = substr(input, pos() + 2); + return temp; + } + } + + return input; + } + + bool import(const char *filename) { + string data; + if(data.readfile(filename) == false) return false; + data.ltrim_once("\xef\xbb\xbf"); //remove UTF-8 marker, if it exists + data.replace("\r", ""); + + lstring line; + line.split("\n", data); + for(unsigned i = 0; i < line.size(); i++) { + lstring part; + //format: "Input" = "Output" + part.qsplit("=", line[i]); + if(part.size() != 2) continue; + + //remove whitespace + part[0].trim(); + part[1].trim(); + + //remove quotes + part[0].trim_once("\""); + part[1].trim_once("\""); + + unsigned n = index_input.size(); + index_input[n] = part[0]; + index_output[n] = part[1]; + } + + return true; + } + + void reset() { + index_input.reset(); + index_output.reset(); + } + + ~dictionary() { + reset(); + } + + protected: + lstring index_input; + lstring index_output; + }; +} + +#endif diff --git a/libsnes/nall/directory.hpp b/libsnes/nall/directory.hpp new file mode 100644 index 0000000..85e9aca --- /dev/null +++ b/libsnes/nall/directory.hpp @@ -0,0 +1,129 @@ +#ifndef NALL_DIRECTORY_HPP +#define NALL_DIRECTORY_HPP + +#include +#include +#include + +#if defined(_WIN32) + #include +#else + #include + #include + #include +#endif + +namespace nall { + +struct directory { + static lstring folders(const char *pathname); + static lstring files(const char *pathname); + static lstring contents(const char *pathname); +}; + +#if defined(_WIN32) + inline lstring directory::folders(const char *pathname) { + lstring list; + string path = pathname; + path.transform("/", "\\"); + if(!strend(path, "\\")) path.append("\\"); + path.append("*"); + HANDLE handle; + WIN32_FIND_DATA data; + handle = FindFirstFile(utf16_t(path), &data); + if(handle != INVALID_HANDLE_VALUE) { + if(wcscmp(data.cFileName, L".") && wcscmp(data.cFileName, L"..")) { + if(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + list.append(string(utf8_t(data.cFileName), "/")); + } + } + while(FindNextFile(handle, &data) != false) { + if(wcscmp(data.cFileName, L".") && wcscmp(data.cFileName, L"..")) { + if(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + list.append(string(utf8_t(data.cFileName), "/")); + } + } + } + FindClose(handle); + } + sort(&list[0], list.size()); + return list; + } + + inline lstring directory::files(const char *pathname) { + lstring list; + string path = pathname; + path.transform("/", "\\"); + if(!strend(path, "\\")) path.append("\\"); + path.append("*"); + HANDLE handle; + WIN32_FIND_DATA data; + handle = FindFirstFile(utf16_t(path), &data); + if(handle != INVALID_HANDLE_VALUE) { + if((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) { + list.append(utf8_t(data.cFileName)); + } + while(FindNextFile(handle, &data) != false) { + if((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) { + list.append(utf8_t(data.cFileName)); + } + } + FindClose(handle); + } + sort(&list[0], list.size()); + return list; + } + + inline lstring directory::contents(const char *pathname) { + lstring folders = directory::folders(pathname); + lstring files = directory::files(pathname); + foreach(file, files) folders.append(file); + return folders; + } +#else + inline lstring directory::folders(const char *pathname) { + lstring list; + DIR *dp; + struct dirent *ep; + dp = opendir(pathname); + if(dp) { + while(ep = readdir(dp)) { + if(!strcmp(ep->d_name, ".")) continue; + if(!strcmp(ep->d_name, "..")) continue; + if(ep->d_type & DT_DIR) list.append(string(ep->d_name, "/")); + } + closedir(dp); + } + sort(&list[0], list.size()); + return list; + + } + + inline lstring directory::files(const char *pathname) { + lstring list; + DIR *dp; + struct dirent *ep; + dp = opendir(pathname); + if(dp) { + while(ep = readdir(dp)) { + if(!strcmp(ep->d_name, ".")) continue; + if(!strcmp(ep->d_name, "..")) continue; + if((ep->d_type & DT_DIR) == 0) list.append(ep->d_name); + } + closedir(dp); + } + sort(&list[0], list.size()); + return list; + } + + inline lstring directory::contents(const char *pathname) { + lstring folders = directory::folders(pathname); + lstring files = directory::files(pathname); + foreach(file, files) folders.append(file); + return folders; + } +#endif + +} + +#endif diff --git a/libsnes/nall/dl.hpp b/libsnes/nall/dl.hpp new file mode 100644 index 0000000..2d0cb9f --- /dev/null +++ b/libsnes/nall/dl.hpp @@ -0,0 +1,115 @@ +#ifndef NALL_DL_HPP +#define NALL_DL_HPP + +//dynamic linking support + +#include +#include +#include +#include + +#if defined(PLATFORM_X) || defined(PLATFORM_OSX) + #include +#elif defined(PLATFORM_WIN) + #include + #include +#endif + +namespace nall { + struct library { + bool opened() const { return handle; } + bool open(const char*, const char* = ""); + bool open_absolute(const char*); + void* sym(const char*); + void close(); + + library() : handle(0) {} + ~library() { close(); } + + library& operator=(const library&){ handle = 0; return *this; } + library(const library&): handle(0){} + + private: + uintptr_t handle; + }; + + #if defined(PLATFORM_X) + inline bool library::open(const char *name, const char *path) { + if(handle) close(); + handle = (uintptr_t)dlopen(string(path, *path && !strend(path, "/") ? "/" : "", "lib", name, ".so"), RTLD_LAZY); + if(!handle) handle = (uintptr_t)dlopen(string("/usr/local/lib/lib", name, ".so"), RTLD_LAZY); + return handle; + } + + inline bool library::open_absolute(const char *name) { + if(handle) close(); + handle = (uintptr_t)dlopen(name, RTLD_LAZY); + return handle; + } + + inline void* library::sym(const char *name) { + if(!handle) return 0; + return dlsym((void*)handle, name); + } + + inline void library::close() { + if(!handle) return; + dlclose((void*)handle); + handle = 0; + } + #elif defined(PLATFORM_OSX) + inline bool library::open(const char *name, const char *path) { + if(handle) close(); + handle = (uintptr_t)dlopen(string(path, *path && !strend(path, "/") ? "/" : "", "lib", name, ".dylib"), RTLD_LAZY); + if(!handle) handle = (uintptr_t)dlopen(string("/usr/local/lib/lib", name, ".dylib"), RTLD_LAZY); + return handle; + } + + inline bool library::open_absolute(const char *name) { + if(handle) close(); + handle = (uintptr_t)dlopen(name, RTLD_LAZY); + return handle; + } + + inline void* library::sym(const char *name) { + if(!handle) return 0; + return dlsym((void*)handle, name); + } + + inline void library::close() { + if(!handle) return; + dlclose((void*)handle); + handle = 0; + } + #elif defined(PLATFORM_WIN) + inline bool library::open(const char *name, const char *path) { + if(handle) close(); + string filepath(path, *path && !strend(path, "/") && !strend(path, "\\") ? "\\" : "", name, ".dll"); + handle = (uintptr_t)LoadLibraryW(utf16_t(filepath)); + return handle; + } + + inline bool library::open_absolute(const char *name) { + if(handle) close(); + handle = (uintptr_t)LoadLibraryW(utf16_t(name)); + return handle; + } + + inline void* library::sym(const char *name) { + if(!handle) return 0; + return (void*)GetProcAddress((HMODULE)handle, name); + } + + inline void library::close() { + if(!handle) return; + FreeLibrary((HMODULE)handle); + handle = 0; + } + #else + inline bool library::open(const char*, const char*) { return false; } + inline void* library::sym(const char*) { return 0; } + inline void library::close() {} + #endif +}; + +#endif diff --git a/libsnes/nall/dsp.hpp b/libsnes/nall/dsp.hpp new file mode 100755 index 0000000..a2400ec --- /dev/null +++ b/libsnes/nall/dsp.hpp @@ -0,0 +1,13 @@ +#ifndef NALL_DSP_HPP +#define NALL_DSP_HPP + +#include +#ifdef __SSE__ + #include +#endif + +#define NALL_DSP_INTERNAL_HPP +#include +#undef NALL_DSP_INTERNAL_HPP + +#endif diff --git a/libsnes/nall/dsp/buffer.hpp b/libsnes/nall/dsp/buffer.hpp new file mode 100755 index 0000000..4386d0e --- /dev/null +++ b/libsnes/nall/dsp/buffer.hpp @@ -0,0 +1,51 @@ +#ifdef NALL_DSP_INTERNAL_HPP + +struct Buffer { + double **sample; + uint16_t rdoffset; + uint16_t wroffset; + unsigned channels; + + void setChannels(unsigned channels) { + for(unsigned c = 0; c < this->channels; c++) { + if(sample[c]) delete[] sample[c]; + } + if(sample) delete[] sample; + + this->channels = channels; + if(channels == 0) return; + + sample = new double*[channels]; + for(unsigned c = 0; c < channels; c++) { + sample[c] = new double[65536](); + } + } + + inline double& read(unsigned channel, signed offset = 0) { + return sample[channel][(uint16_t)(rdoffset + offset)]; + } + + inline double& write(unsigned channel, signed offset = 0) { + return sample[channel][(uint16_t)(wroffset + offset)]; + } + + inline void clear() { + for(unsigned c = 0; c < channels; c++) { + for(unsigned n = 0; n < 65536; n++) { + sample[c][n] = 0; + } + } + rdoffset = 0; + wroffset = 0; + } + + Buffer() { + channels = 0; + } + + ~Buffer() { + setChannels(0); + } +}; + +#endif diff --git a/libsnes/nall/dsp/core.hpp b/libsnes/nall/dsp/core.hpp new file mode 100755 index 0000000..6ae407d --- /dev/null +++ b/libsnes/nall/dsp/core.hpp @@ -0,0 +1,170 @@ +#ifdef NALL_DSP_INTERNAL_HPP + +#include +#include +#include + +namespace nall { + +//precision: can be float, double or long double +#define real float + +struct DSP; + +struct Resampler { + DSP &dsp; + real frequency; + + virtual void setFrequency() = 0; + virtual void clear() = 0; + virtual void sample() = 0; + Resampler(DSP &dsp) : dsp(dsp) {} +}; + +struct DSP { + struct ResampleEngine { + enum e { + Nearest, + Linear, + Cosine, + Cubic, + Hermite, + Average, + Sinc, + } i; + }; + + inline void setChannels(unsigned channels); + inline void setPrecision(unsigned precision); + inline void setFrequency(real frequency); //inputFrequency + inline void setVolume(real volume); + inline void setBalance(real balance); + + inline void setResampler(ResampleEngine::e resamplingEngine); + inline void setResamplerFrequency(real frequency); //outputFrequency + + inline void sample(signed channel[]); + inline bool pending(); + inline void read(signed channel[]); + + inline void clear(); + inline DSP(); + inline ~DSP(); + +protected: + friend class ResampleNearest; + friend class ResampleLinear; + friend class ResampleCosine; + friend class ResampleCubic; + friend class ResampleAverage; + friend class ResampleHermite; + friend class ResampleSinc; + + struct Settings { + unsigned channels; + unsigned precision; + real frequency; + real volume; + real balance; + + //internal + real intensity; + real intensityInverse; + } settings; + + Resampler *resampler; + inline void write(real channel[]); + + #include "buffer.hpp" + Buffer buffer; + Buffer output; + + inline void adjustVolume(); + inline void adjustBalance(); + inline signed clamp(const unsigned bits, const signed x); +}; + +#include "resample/nearest.hpp" +#include "resample/linear.hpp" +#include "resample/cosine.hpp" +#include "resample/cubic.hpp" +#include "resample/hermite.hpp" +#include "resample/average.hpp" +#include "resample/sinc.hpp" +#include "settings.hpp" + +void DSP::sample(signed channel[]) { + for(unsigned c = 0; c < settings.channels; c++) { + buffer.write(c) = (real)channel[c] * settings.intensityInverse; + } + buffer.wroffset++; + resampler->sample(); +} + +bool DSP::pending() { + return output.rdoffset != output.wroffset; +} + +void DSP::read(signed channel[]) { + adjustVolume(); + adjustBalance(); + + for(unsigned c = 0; c < settings.channels; c++) { + channel[c] = clamp(settings.precision, output.read(c) * settings.intensity); + } + output.rdoffset++; +} + +void DSP::write(real channel[]) { + for(unsigned c = 0; c < settings.channels; c++) { + output.write(c) = channel[c]; + } + output.wroffset++; +} + +void DSP::adjustVolume() { + for(unsigned c = 0; c < settings.channels; c++) { + output.read(c) *= settings.volume; + } +} + +void DSP::adjustBalance() { + if(settings.channels != 2) return; //TODO: support > 2 channels + if(settings.balance < 0.0) output.read(1) *= 1.0 + settings.balance; + if(settings.balance > 0.0) output.read(0) *= 1.0 - settings.balance; +} + +signed DSP::clamp(const unsigned bits, const signed x) { + const signed b = 1U << (bits - 1); + const signed m = (1U << (bits - 1)) - 1; + return (x > m) ? m : (x < -b) ? -b : x; +} + +void DSP::clear() { + buffer.clear(); + output.clear(); + resampler->clear(); +} + +DSP::DSP() { + setResampler(ResampleEngine::Hermite); + setResamplerFrequency(44100.0); + + setChannels(2); + setPrecision(16); + setFrequency(44100.0); + setVolume(1.0); + setBalance(0.0); + + clear(); +} + +DSP::~DSP() { + if(resampler) delete resampler; +} + +#undef real + +} + +#endif diff --git a/libsnes/nall/dsp/resample/average.hpp b/libsnes/nall/dsp/resample/average.hpp new file mode 100755 index 0000000..867b13b --- /dev/null +++ b/libsnes/nall/dsp/resample/average.hpp @@ -0,0 +1,72 @@ +#ifdef NALL_DSP_INTERNAL_HPP + +struct ResampleAverage : Resampler { + inline void setFrequency(); + inline void clear(); + inline void sample(); + inline void sampleLinear(); + ResampleAverage(DSP &dsp) : Resampler(dsp) {} + + real fraction; + real step; +}; + +void ResampleAverage::setFrequency() { + fraction = 0.0; + step = dsp.settings.frequency / frequency; +} + +void ResampleAverage::clear() { + fraction = 0.0; +} + +void ResampleAverage::sample() { + //can only average if input frequency >= output frequency + if(step < 1.0) return sampleLinear(); + + fraction += 1.0; + + real scalar = 1.0; + if(fraction > step) scalar = 1.0 - (fraction - step); + + for(unsigned c = 0; c < dsp.settings.channels; c++) { + dsp.output.write(c) += dsp.buffer.read(c) * scalar; + } + + if(fraction >= step) { + for(unsigned c = 0; c < dsp.settings.channels; c++) { + dsp.output.write(c) /= step; + } + dsp.output.wroffset++; + + fraction -= step; + for(unsigned c = 0; c < dsp.settings.channels; c++) { + dsp.output.write(c) = dsp.buffer.read(c) * fraction; + } + } + + dsp.buffer.rdoffset++; +} + +void ResampleAverage::sampleLinear() { + while(fraction <= 1.0) { + real channel[dsp.settings.channels]; + + for(unsigned n = 0; n < dsp.settings.channels; n++) { + real a = dsp.buffer.read(n, -1); + real b = dsp.buffer.read(n, -0); + + real mu = fraction; + + channel[n] = a * (1.0 - mu) + b * mu; + } + + dsp.write(channel); + fraction += step; + } + + dsp.buffer.rdoffset++; + fraction -= 1.0; +} + +#endif diff --git a/libsnes/nall/dsp/resample/cosine.hpp b/libsnes/nall/dsp/resample/cosine.hpp new file mode 100755 index 0000000..3363d5f --- /dev/null +++ b/libsnes/nall/dsp/resample/cosine.hpp @@ -0,0 +1,44 @@ +#ifdef NALL_DSP_INTERNAL_HPP + +struct ResampleCosine : Resampler { + inline void setFrequency(); + inline void clear(); + inline void sample(); + ResampleCosine(DSP &dsp) : Resampler(dsp) {} + + real fraction; + real step; +}; + +void ResampleCosine::setFrequency() { + fraction = 0.0; + step = dsp.settings.frequency / frequency; +} + +void ResampleCosine::clear() { + fraction = 0.0; +} + +void ResampleCosine::sample() { + while(fraction <= 1.0) { + real channel[dsp.settings.channels]; + + for(unsigned n = 0; n < dsp.settings.channels; n++) { + real a = dsp.buffer.read(n, -1); + real b = dsp.buffer.read(n, -0); + + real mu = fraction; + mu = (1.0 - cos(mu * 3.14159265)) / 2.0; + + channel[n] = a * (1.0 - mu) + b * mu; + } + + dsp.write(channel); + fraction += step; + } + + dsp.buffer.rdoffset++; + fraction -= 1.0; +} + +#endif diff --git a/libsnes/nall/dsp/resample/cubic.hpp b/libsnes/nall/dsp/resample/cubic.hpp new file mode 100755 index 0000000..bc4cc95 --- /dev/null +++ b/libsnes/nall/dsp/resample/cubic.hpp @@ -0,0 +1,50 @@ +#ifdef NALL_DSP_INTERNAL_HPP + +struct ResampleCubic : Resampler { + inline void setFrequency(); + inline void clear(); + inline void sample(); + ResampleCubic(DSP &dsp) : Resampler(dsp) {} + + real fraction; + real step; +}; + +void ResampleCubic::setFrequency() { + fraction = 0.0; + step = dsp.settings.frequency / frequency; +} + +void ResampleCubic::clear() { + fraction = 0.0; +} + +void ResampleCubic::sample() { + while(fraction <= 1.0) { + real channel[dsp.settings.channels]; + + for(unsigned n = 0; n < dsp.settings.channels; n++) { + real a = dsp.buffer.read(n, -3); + real b = dsp.buffer.read(n, -2); + real c = dsp.buffer.read(n, -1); + real d = dsp.buffer.read(n, -0); + + real mu = fraction; + + real A = d - c - a + b; + real B = a - b - A; + real C = c - a; + real D = b; + + channel[n] = A * (mu * 3) + B * (mu * 2) + C * mu + D; + } + + dsp.write(channel); + fraction += step; + } + + dsp.buffer.rdoffset++; + fraction -= 1.0; +} + +#endif diff --git a/libsnes/nall/dsp/resample/hermite.hpp b/libsnes/nall/dsp/resample/hermite.hpp new file mode 100755 index 0000000..0cc9ba0 --- /dev/null +++ b/libsnes/nall/dsp/resample/hermite.hpp @@ -0,0 +1,62 @@ +#ifdef NALL_DSP_INTERNAL_HPP + +struct ResampleHermite : Resampler { + inline void setFrequency(); + inline void clear(); + inline void sample(); + ResampleHermite(DSP &dsp) : Resampler(dsp) {} + + real fraction; + real step; +}; + +void ResampleHermite::setFrequency() { + fraction = 0.0; + step = dsp.settings.frequency / frequency; +} + +void ResampleHermite::clear() { + fraction = 0.0; +} + +void ResampleHermite::sample() { + while(fraction <= 1.0) { + real channel[dsp.settings.channels]; + + for(unsigned n = 0; n < dsp.settings.channels; n++) { + real a = dsp.buffer.read(n, -3); + real b = dsp.buffer.read(n, -2); + real c = dsp.buffer.read(n, -1); + real d = dsp.buffer.read(n, -0); + + const real tension = 0.0; //-1 = low, 0 = normal, +1 = high + const real bias = 0.0; //-1 = left, 0 = even, +1 = right + + real mu1, mu2, mu3, m0, m1, a0, a1, a2, a3; + + mu1 = fraction; + mu2 = mu1 * mu1; + mu3 = mu2 * mu1; + + m0 = (b - a) * (1.0 + bias) * (1.0 - tension) / 2.0; + m0 += (c - b) * (1.0 - bias) * (1.0 - tension) / 2.0; + m1 = (c - b) * (1.0 + bias) * (1.0 - tension) / 2.0; + m1 += (d - c) * (1.0 - bias) * (1.0 - tension) / 2.0; + + a0 = +2 * mu3 - 3 * mu2 + 1; + a1 = mu3 - 2 * mu2 + mu1; + a2 = mu3 - mu2; + a3 = -2 * mu3 + 3 * mu2; + + channel[n] = (a0 * b) + (a1 * m0) + (a2 * m1) + (a3 * c); + } + + dsp.write(channel); + fraction += step; + } + + dsp.buffer.rdoffset++; + fraction -= 1.0; +} + +#endif diff --git a/libsnes/nall/dsp/resample/lib/sinc.hpp b/libsnes/nall/dsp/resample/lib/sinc.hpp new file mode 100755 index 0000000..3e95367 --- /dev/null +++ b/libsnes/nall/dsp/resample/lib/sinc.hpp @@ -0,0 +1,600 @@ +// If these types are changed to anything other than "float", you should comment out the SSE detection directives below +// so that the SSE code is not used. + +typedef float resample_coeff_t; // note: sizeof(resample_coeff_t) must be == to a power of 2, and not larger than 16 +typedef float resample_samp_t; + + +// ...but don't comment this single RESAMPLE_SSEREGPARM define out when disabling SSE. +#define RESAMPLE_SSEREGPARM + +#if defined(__SSE__) + #define SINCRESAMPLE_USE_SSE 1 + #ifndef __x86_64__ + #undef RESAMPLE_SSEREGPARM + #define RESAMPLE_SSEREGPARM __attribute__((sseregparm)) + #endif +#else + // TODO: altivec here +#endif + +namespace ResampleUtility +{ + inline void kaiser_window(double* io, int count, double beta); + inline void gen_sinc(double* out, int size, double cutoff, double kaiser); + inline void gen_sinc_os(double* out, int size, double cutoff, double kaiser); + inline void normalize(double* io, int size, double gain = 1.0); + + inline void* make_aligned(void* ptr, unsigned boundary); // boundary must be a power of 2 +} + +class SincResampleHR +{ + private: + + inline void Init(unsigned ratio_arg, double desired_bandwidth, double beta, double d); + + inline void write(resample_samp_t sample) RESAMPLE_SSEREGPARM; + inline resample_samp_t read(void) RESAMPLE_SSEREGPARM; + inline bool output_avail(void); + + private: + + inline resample_samp_t mac(const resample_samp_t *wave, const resample_coeff_t *coeff, unsigned count); + + unsigned ratio; + unsigned num_convolutions; + + resample_coeff_t *coeffs; + std::vector coeffs_mem; + + // second half of ringbuffer should be copy of first half. + resample_samp_t *rb; + std::vector rb_mem; + + signed rb_readpos; + signed rb_writepos; + signed rb_in; + signed rb_eff_size; + + friend class SincResample; +}; + +class SincResample +{ + public: + + enum + { + QUALITY_LOW = 0, + QUALITY_MEDIUM = 2, + QUALITY_HIGH = 4 + }; + + inline SincResample(double input_rate, double output_rate, double desired_bandwidth, unsigned quality = QUALITY_HIGH); + + inline void write(resample_samp_t sample) RESAMPLE_SSEREGPARM; + inline resample_samp_t read(void) RESAMPLE_SSEREGPARM; + inline bool output_avail(void); + + private: + + inline void Init(double input_rate, double output_rate, double desired_bandwidth, double beta, double d, unsigned pn_nume, unsigned phases_min); + + inline resample_samp_t mac(const resample_samp_t *wave, const resample_coeff_t *coeffs_a, const resample_coeff_t *coeffs_b, const double ffract, unsigned count) RESAMPLE_SSEREGPARM; + + unsigned num_convolutions; + unsigned num_phases; + + unsigned step_int; + double step_fract; + + double input_pos_fract; + + + std::vector coeffs; // Pointers into coeff_mem. + std::vector coeff_mem; + + + std::vector rb; // second half should be copy of first half. + signed rb_readpos; + signed rb_writepos; + signed rb_in; + + bool hr_used; + SincResampleHR hr; +}; + + +// +// Code: +// +//#include "resample.hpp" + +#if 0 +namespace bit +{ + inline unsigned round(unsigned x) { + if((x & (x - 1)) == 0) return x; + while(x & (x - 1)) x &= x - 1; + return x << 1; + } +} +#endif + +void SincResampleHR::Init(unsigned ratio_arg, double desired_bandwidth, double beta, double d) +{ + const unsigned align_boundary = 16; + std::vector coeffs_tmp; + double cutoff; // 1.0 = f/2 + + ratio = ratio_arg; + + //num_convolutions = ((unsigned)ceil(d / ((1.0 - desired_bandwidth) / ratio)) + 1) &~ 1; // round up to be even + num_convolutions = ((unsigned)ceil(d / ((1.0 - desired_bandwidth) / ratio)) | 1); + + cutoff = (1.0 / ratio) - (d / num_convolutions); + +//printf("%d %d %.20f\n", ratio, num_convolutions, cutoff); + assert(num_convolutions > ratio); + + + // Generate windowed sinc of POWER + coeffs_tmp.resize(num_convolutions); + //ResampleUtility::gen_sinc(&coeffs_tmp[0], num_convolutions, cutoff, beta); + ResampleUtility::gen_sinc_os(&coeffs_tmp[0], num_convolutions, cutoff, beta); + ResampleUtility::normalize(&coeffs_tmp[0], num_convolutions); + + // Copy from coeffs_tmp to coeffs~ + // We multiply many coefficients at a time in the mac loop, so make sure the last few that don't really + // exist are allocated, zero'd mem. + + coeffs_mem.resize(((num_convolutions + 7) &~ 7) * sizeof(resample_coeff_t) + (align_boundary - 1)); + coeffs = (resample_coeff_t *)ResampleUtility::make_aligned(&coeffs_mem[0], align_boundary); + + + for(unsigned i = 0; i < num_convolutions; i++) + coeffs[i] = coeffs_tmp[i]; + + rb_eff_size = nall::bit::round(num_convolutions * 2) >> 1; + rb_readpos = 0; + rb_writepos = 0; + rb_in = 0; + + rb_mem.resize(rb_eff_size * 2 * sizeof(resample_samp_t) + (align_boundary - 1)); + rb = (resample_samp_t *)ResampleUtility::make_aligned(&rb_mem[0], align_boundary); +} + + +inline bool SincResampleHR::output_avail(void) +{ + return(rb_in >= (signed)num_convolutions); +} + +inline void SincResampleHR::write(resample_samp_t sample) +{ + assert(!output_avail()); + + rb[rb_writepos] = sample; + rb[rb_writepos + rb_eff_size] = sample; + rb_writepos = (rb_writepos + 1) & (rb_eff_size - 1); + rb_in++; +} + +resample_samp_t SincResampleHR::mac(const resample_samp_t *wave, const resample_coeff_t *coeff, unsigned count) +{ +#if SINCRESAMPLE_USE_SSE + __m128 accum_veca[2] = { _mm_set1_ps(0), _mm_set1_ps(0) }; + + resample_samp_t accum; + + for(unsigned c = 0; c < count; c += 8) + { + for(unsigned i = 0; i < 2; i++) + { + __m128 co[2]; + __m128 w[2]; + + co[i] = _mm_load_ps(&coeff[c + i * 4]); + w[i] = _mm_load_ps(&wave[c + i * 4]); + + w[i] = _mm_mul_ps(w[i], co[i]); + + accum_veca[i] = _mm_add_ps(w[i], accum_veca[i]); + } + } + + __m128 accum_vec = _mm_add_ps(accum_veca[0], accum_veca[1]); //_mm_add_ps(_mm_add_ps(accum_veca[0], accum_veca[1]), _mm_add_ps(accum_veca[2], accum_veca[3])); + + accum_vec = _mm_add_ps(accum_vec, _mm_shuffle_ps(accum_vec, accum_vec, (3 << 0) | (2 << 2) | (1 << 4) | (0 << 6))); + accum_vec = _mm_add_ps(accum_vec, _mm_shuffle_ps(accum_vec, accum_vec, (1 << 0) | (0 << 2) | (1 << 4) | (0 << 6))); + + _mm_store_ss(&accum, accum_vec); + + return accum; +#else + resample_samp_t accum[4] = { 0, 0, 0, 0 }; + + for(unsigned c = 0; c < count; c+= 4) + { + accum[0] += wave[c + 0] * coeff[c + 0]; + accum[1] += wave[c + 1] * coeff[c + 1]; + accum[2] += wave[c + 2] * coeff[c + 2]; + accum[3] += wave[c + 3] * coeff[c + 3]; + } + + return (accum[0] + accum[1]) + (accum[2] + accum[3]); // don't mess with parentheses(assuming compiler doesn't already, which it may... + +#endif +} + + +resample_samp_t SincResampleHR::read(void) +{ + assert(output_avail()); + resample_samp_t ret; + + ret = mac(&rb[rb_readpos], &coeffs[0], num_convolutions); + + rb_readpos = (rb_readpos + ratio) & (rb_eff_size - 1); + rb_in -= ratio; + + return ret; +} + + +SincResample::SincResample(double input_rate, double output_rate, double desired_bandwidth, unsigned quality) +{ + const struct + { + double beta; + double d; + unsigned pn_nume; + unsigned phases_min; + } qtab[5] = + { + { 5.658, 3.62, 4096, 4 }, + { 6.764, 4.32, 8192, 4 }, + { 7.865, 5.0, 16384, 8 }, + { 8.960, 5.7, 32768, 16 }, + { 10.056, 6.4, 65536, 32 } + }; + + // Sanity checks + assert(ceil(input_rate) > 0); + assert(ceil(output_rate) > 0); + assert(ceil(input_rate / output_rate) <= 1024); + assert(ceil(output_rate / input_rate) <= 1024); + + // The simplistic number-of-phases calculation code doesn't work well enough for when desired_bandwidth is close to 1.0 and when + // upsampling. + assert(desired_bandwidth >= 0.25 && desired_bandwidth < 0.96); + assert(quality >= 0 && quality <= 4); + + hr_used = false; + +#if 1 + // Round down to the nearest multiple of 4(so wave buffer remains aligned) + // It also adjusts the effective intermediate sampling rate up slightly, so that the upper frequencies below f/2 + // aren't overly attenuated so much. In the future, we might want to do an FFT or something to choose the intermediate rate more accurately + // to virtually eliminate over-attenuation. + unsigned ioratio_rd = (unsigned)floor(input_rate / (output_rate * (1.0 + (1.0 - desired_bandwidth) / 2) )) & ~3; + + if(ioratio_rd >= 8) + { + hr.Init(ioratio_rd, desired_bandwidth, qtab[quality].beta, qtab[quality].d); //10.056, 6.4); + hr_used = true; + + input_rate /= ioratio_rd; + } +#endif + + Init(input_rate, output_rate, desired_bandwidth, qtab[quality].beta, qtab[quality].d, qtab[quality].pn_nume, qtab[quality].phases_min); +} + +void SincResample::Init(double input_rate, double output_rate, double desired_bandwidth, double beta, double d, unsigned pn_nume, unsigned phases_min) +{ + const unsigned max_mult_atatime = 8; // multiply "granularity". must be power of 2. + const unsigned max_mult_minus1 = (max_mult_atatime - 1); + const unsigned conv_alignment_bytes = 16; // must be power of 2 + const double input_to_output_ratio = input_rate / output_rate; + const double output_to_input_ratio = output_rate / input_rate; + double cutoff; // 1.0 = input_rate / 2 + std::vector coeff_init_buffer; + + // Round up num_convolutions to be even. + if(output_rate > input_rate) + num_convolutions = ((unsigned)ceil(d / (1.0 - desired_bandwidth)) + 1) & ~1; + else + num_convolutions = ((unsigned)ceil(d / (output_to_input_ratio * (1.0 - desired_bandwidth))) + 1) & ~1; + + if(output_rate > input_rate) // Upsampling + cutoff = desired_bandwidth; + else // Downsampling + cutoff = output_to_input_ratio * desired_bandwidth; + + // Round up to be even. + num_phases = (std::max(pn_nume / num_convolutions, phases_min) + 1) &~1; + + // Adjust cutoff to account for the multiple phases. + cutoff = cutoff / num_phases; + + assert((num_convolutions & 1) == 0); + assert((num_phases & 1) == 0); + +// fprintf(stderr, "num_convolutions=%u, num_phases=%u, total expected coeff byte size=%lu\n", num_convolutions, num_phases, +// (long)((num_phases + 2) * ((num_convolutions + max_mult_minus1) & ~max_mult_minus1) * sizeof(float) + conv_alignment_bytes)); + + coeff_init_buffer.resize(num_phases * num_convolutions); + + coeffs.resize(num_phases + 1 + 1); + + coeff_mem.resize((num_phases + 1 + 1) * ((num_convolutions + max_mult_minus1) &~ max_mult_minus1) * sizeof(resample_coeff_t) + conv_alignment_bytes); + + // Assign aligned pointers into coeff_mem + { + resample_coeff_t *base_ptr = (resample_coeff_t *)ResampleUtility::make_aligned(&coeff_mem[0], conv_alignment_bytes); + + for(unsigned phase = 0; phase < (num_phases + 1 + 1); phase++) + { + coeffs[phase] = base_ptr + (((num_convolutions + max_mult_minus1) & ~max_mult_minus1) * phase); + } + } + + ResampleUtility::gen_sinc(&coeff_init_buffer[0], num_phases * num_convolutions, cutoff, beta); + ResampleUtility::normalize(&coeff_init_buffer[0], num_phases * num_convolutions, num_phases); + + // Reorder coefficients to allow for more efficient convolution. + for(int phase = -1; phase < ((int)num_phases + 1); phase++) + { + for(int conv = 0; conv < (int)num_convolutions; conv++) + { + double coeff; + + if(phase == -1 && conv == 0) + coeff = 0; + else if(phase == (int)num_phases && conv == ((int)num_convolutions - 1)) + coeff = 0; + else + coeff = coeff_init_buffer[conv * num_phases + phase]; + + coeffs[phase + 1][conv] = coeff; + } + } + + // Free a bit of mem + coeff_init_buffer.resize(0); + + step_int = floor(input_to_output_ratio); + step_fract = input_to_output_ratio - step_int; + + input_pos_fract = 0; + + // Do NOT use rb.size() later in the code, since it'll include the padding. + // We should only need one "max_mult_minus1" here, not two, since it won't matter if it over-reads(due to doing "max_mult_atatime" multiplications at a time + // rather than just 1, in which case this over-read wouldn't happen), from the first half into the duplicated half, + // since those corresponding coefficients will be zero anyway; this is just to handle the case of reading off the end of the duplicated half to + // prevent illegal memory accesses. + rb.resize(num_convolutions * 2 + max_mult_minus1); + + rb_readpos = 0; + rb_writepos = 0; + rb_in = 0; +} + +resample_samp_t SincResample::mac(const resample_samp_t *wave, const resample_coeff_t *coeffs_a, const resample_coeff_t *coeffs_b, const double ffract, unsigned count) +{ + resample_samp_t accum = 0; +#if SINCRESAMPLE_USE_SSE + __m128 accum_vec_a[2] = { _mm_set1_ps(0), _mm_set1_ps(0) }; + __m128 accum_vec_b[2] = { _mm_set1_ps(0), _mm_set1_ps(0) }; + + for(unsigned c = 0; c < count; c += 8) //8) //4) + { + __m128 coeff_a[2]; + __m128 coeff_b[2]; + __m128 w[2]; + __m128 result_a[2], result_b[2]; + + for(unsigned i = 0; i < 2; i++) + { + coeff_a[i] = _mm_load_ps(&coeffs_a[c + (i * 4)]); + coeff_b[i] = _mm_load_ps(&coeffs_b[c + (i * 4)]); + w[i] = _mm_loadu_ps(&wave[c + (i * 4)]); + + result_a[i] = _mm_mul_ps(coeff_a[i], w[i]); + result_b[i] = _mm_mul_ps(coeff_b[i], w[i]); + + accum_vec_a[i] = _mm_add_ps(result_a[i], accum_vec_a[i]); + accum_vec_b[i] = _mm_add_ps(result_b[i], accum_vec_b[i]); + } + } + + __m128 accum_vec, av_a, av_b; + __m128 mult_a_vec = _mm_set1_ps(1.0 - ffract); + __m128 mult_b_vec = _mm_set1_ps(ffract); + + av_a = _mm_mul_ps(mult_a_vec, /*accum_vec_a[0]);*/ _mm_add_ps(accum_vec_a[0], accum_vec_a[1])); + av_b = _mm_mul_ps(mult_b_vec, /*accum_vec_b[0]);*/ _mm_add_ps(accum_vec_b[0], accum_vec_b[1])); + + accum_vec = _mm_add_ps(av_a, av_b); + + accum_vec = _mm_add_ps(accum_vec, _mm_shuffle_ps(accum_vec, accum_vec, (3 << 0) | (2 << 2) | (1 << 4) | (0 << 6))); + accum_vec = _mm_add_ps(accum_vec, _mm_shuffle_ps(accum_vec, accum_vec, (1 << 0) | (0 << 2) | (1 << 4) | (0 << 6))); + + _mm_store_ss(&accum, accum_vec); +#else + resample_coeff_t mult_a = 1.0 - ffract; + resample_coeff_t mult_b = ffract; + + for(unsigned c = 0; c < count; c += 4) + { + accum += wave[c + 0] * (coeffs_a[c + 0] * mult_a + coeffs_b[c + 0] * mult_b); + accum += wave[c + 1] * (coeffs_a[c + 1] * mult_a + coeffs_b[c + 1] * mult_b); + accum += wave[c + 2] * (coeffs_a[c + 2] * mult_a + coeffs_b[c + 2] * mult_b); + accum += wave[c + 3] * (coeffs_a[c + 3] * mult_a + coeffs_b[c + 3] * mult_b); + } +#endif + + return accum; +} + +inline bool SincResample::output_avail(void) +{ + return(rb_in >= (int)num_convolutions); +} + +resample_samp_t SincResample::read(void) +{ + assert(output_avail()); + double phase = input_pos_fract * num_phases - 0.5; + signed phase_int = (signed)floor(phase); + double phase_fract = phase - phase_int; + unsigned phase_a = num_phases - 1 - phase_int; + unsigned phase_b = phase_a - 1; + resample_samp_t ret; + + ret = mac(&rb[rb_readpos], &coeffs[phase_a + 1][0], &coeffs[phase_b + 1][0], phase_fract, num_convolutions); + + unsigned int_increment = step_int; + + input_pos_fract += step_fract; + int_increment += floor(input_pos_fract); + input_pos_fract -= floor(input_pos_fract); + + rb_readpos = (rb_readpos + int_increment) % num_convolutions; + rb_in -= int_increment; + + return ret; +} + +inline void SincResample::write(resample_samp_t sample) +{ + assert(!output_avail()); + + if(hr_used) + { + hr.write(sample); + + if(hr.output_avail()) + { + sample = hr.read(); + } + else + { + return; + } + } + + rb[rb_writepos + 0 * num_convolutions] = sample; + rb[rb_writepos + 1 * num_convolutions] = sample; + rb_writepos = (rb_writepos + 1) % num_convolutions; + rb_in++; +} + +void ResampleUtility::kaiser_window( double* io, int count, double beta) +{ + int const accuracy = 24; //16; //12; + + double* end = io + count; + + double beta2 = beta * beta * (double) -0.25; + double to_fract = beta2 / ((double) count * count); + double i = 0; + double rescale = 0; // Doesn't need an initializer, to shut up gcc + + for ( ; io < end; ++io, i += 1 ) + { + double x = i * i * to_fract - beta2; + double u = x; + double k = x + 1; + + double n = 2; + do + { + u *= x / (n * n); + n += 1; + k += u; + } + while ( k <= u * (1 << accuracy) ); + + if ( !i ) + rescale = 1 / k; // otherwise values get large + + *io *= k * rescale; + } +} + +void ResampleUtility::gen_sinc(double* out, int size, double cutoff, double kaiser) +{ + assert( size % 2 == 0 ); // size must be even + + int const half_size = size / 2; + double* const mid = &out [half_size]; + + // Generate right half of sinc + for ( int i = 0; i < half_size; i++ ) + { + double angle = (i * 2 + 1) * (M_PI / 2); + mid [i] = sin( angle * cutoff ) / angle; + } + + kaiser_window( mid, half_size, kaiser ); + + // Mirror for left half + for ( int i = 0; i < half_size; i++ ) + out [i] = mid [half_size - 1 - i]; +} + +void ResampleUtility::gen_sinc_os(double* out, int size, double cutoff, double kaiser) +{ + assert( size % 2 == 1); // size must be odd + + for(int i = 0; i < size; i++) + { + if(i == (size / 2)) + out[i] = 2 * M_PI * (cutoff / 2); //0.078478; //1.0; //sin(2 * M_PI * (cutoff / 2) * (i - size / 2)) / (i - (size / 2)); + else + out[i] = sin(2 * M_PI * (cutoff / 2) * (i - size / 2)) / (i - (size / 2)); + +// out[i] *= 0.3635819 - 0.4891775 * cos(2 * M_PI * i / (size - 1)) + 0.1365995 * cos(4 * M_PI * i / (size - 1)) - 0.0106411 * cos(6 * M_PI * i / (size - 1)); +//0.42 - 0.5 * cos(2 * M_PI * i / (size - 1)) + 0.08 * cos(4 * M_PI * i / (size - 1)); + +// printf("%d %f\n", i, out[i]); + } + + kaiser_window(&out[size / 2], size / 2 + 1, kaiser); + + // Mirror for left half + for ( int i = 0; i < size / 2; i++ ) + out [i] = out [size - 1 - i]; + +} + +void ResampleUtility::normalize(double* io, int size, double gain) +{ + double sum = 0; + for ( int i = 0; i < size; i++ ) + sum += io [i]; + + double scale = gain / sum; + for ( int i = 0; i < size; i++ ) + io [i] *= scale; +} + +void* ResampleUtility::make_aligned(void* ptr, unsigned boundary) +{ + unsigned char* null_ptr = (unsigned char *)NULL; + unsigned char* uc_ptr = (unsigned char *)ptr; + + uc_ptr += (boundary - ((uc_ptr - null_ptr) & (boundary - 1))) & (boundary - 1); + + //while((uc_ptr - null_ptr) & (boundary - 1)) + // uc_ptr++; + + //printf("%16llx %16llx\n", (unsigned long long)ptr, (unsigned long long)uc_ptr); + + assert((uc_ptr - (unsigned char *)ptr) < boundary && (uc_ptr >= (unsigned char *)ptr)); + + return uc_ptr; +} diff --git a/libsnes/nall/dsp/resample/linear.hpp b/libsnes/nall/dsp/resample/linear.hpp new file mode 100755 index 0000000..3c2dc9e --- /dev/null +++ b/libsnes/nall/dsp/resample/linear.hpp @@ -0,0 +1,43 @@ +#ifdef NALL_DSP_INTERNAL_HPP + +struct ResampleLinear : Resampler { + inline void setFrequency(); + inline void clear(); + inline void sample(); + ResampleLinear(DSP &dsp) : Resampler(dsp) {} + + real fraction; + real step; +}; + +void ResampleLinear::setFrequency() { + fraction = 0.0; + step = dsp.settings.frequency / frequency; +} + +void ResampleLinear::clear() { + fraction = 0.0; +} + +void ResampleLinear::sample() { + while(fraction <= 1.0) { + real channel[dsp.settings.channels]; + + for(unsigned n = 0; n < dsp.settings.channels; n++) { + real a = dsp.buffer.read(n, -1); + real b = dsp.buffer.read(n, -0); + + real mu = fraction; + + channel[n] = a * (1.0 - mu) + b * mu; + } + + dsp.write(channel); + fraction += step; + } + + dsp.buffer.rdoffset++; + fraction -= 1.0; +} + +#endif diff --git a/libsnes/nall/dsp/resample/nearest.hpp b/libsnes/nall/dsp/resample/nearest.hpp new file mode 100755 index 0000000..14b401e --- /dev/null +++ b/libsnes/nall/dsp/resample/nearest.hpp @@ -0,0 +1,43 @@ +#ifdef NALL_DSP_INTERNAL_HPP + +struct ResampleNearest : Resampler { + inline void setFrequency(); + inline void clear(); + inline void sample(); + ResampleNearest(DSP &dsp) : Resampler(dsp) {} + + real fraction; + real step; +}; + +void ResampleNearest::setFrequency() { + fraction = 0.0; + step = dsp.settings.frequency / frequency; +} + +void ResampleNearest::clear() { + fraction = 0.0; +} + +void ResampleNearest::sample() { + while(fraction <= 1.0) { + real channel[dsp.settings.channels]; + + for(unsigned n = 0; n < dsp.settings.channels; n++) { + real a = dsp.buffer.read(n, -1); + real b = dsp.buffer.read(n, -0); + + real mu = fraction; + + channel[n] = mu < 0.5 ? a : b; + } + + dsp.write(channel); + fraction += step; + } + + dsp.buffer.rdoffset++; + fraction -= 1.0; +} + +#endif diff --git a/libsnes/nall/dsp/resample/sinc.hpp b/libsnes/nall/dsp/resample/sinc.hpp new file mode 100755 index 0000000..a77a1ee --- /dev/null +++ b/libsnes/nall/dsp/resample/sinc.hpp @@ -0,0 +1,54 @@ +#ifdef NALL_DSP_INTERNAL_HPP + +#include "lib/sinc.hpp" + +struct ResampleSinc : Resampler { + inline void setFrequency(); + inline void clear(); + inline void sample(); + inline ResampleSinc(DSP &dsp); + +private: + inline void remakeSinc(); + SincResample *sinc_resampler[8]; +}; + +void ResampleSinc::setFrequency() { + remakeSinc(); +} + +void ResampleSinc::clear() { + remakeSinc(); +} + +void ResampleSinc::sample() { + for(unsigned c = 0; c < dsp.settings.channels; c++) { + sinc_resampler[c]->write(dsp.buffer.read(c)); + } + + if(sinc_resampler[0]->output_avail()) { + do { + for(unsigned c = 0; c < dsp.settings.channels; c++) { + dsp.output.write(c) = sinc_resampler[c]->read(); + } + dsp.output.wroffset++; + } while(sinc_resampler[0]->output_avail()); + } + + dsp.buffer.rdoffset++; +} + +ResampleSinc::ResampleSinc(DSP &dsp) : Resampler(dsp) { + for(unsigned n = 0; n < 8; n++) sinc_resampler[n] = 0; +} + +void ResampleSinc::remakeSinc() { + assert(dsp.settings.channels < 8); + + for(unsigned c = 0; c < dsp.settings.channels; c++) { + if(sinc_resampler[c]) delete sinc_resampler[c]; + sinc_resampler[c] = new SincResample(dsp.settings.frequency, frequency, 0.85, SincResample::QUALITY_HIGH); + } +} + +#endif diff --git a/libsnes/nall/dsp/settings.hpp b/libsnes/nall/dsp/settings.hpp new file mode 100755 index 0000000..33ababd --- /dev/null +++ b/libsnes/nall/dsp/settings.hpp @@ -0,0 +1,50 @@ +#ifdef NALL_DSP_INTERNAL_HPP + +void DSP::setChannels(unsigned channels) { + assert(channels > 0); + buffer.setChannels(channels); + output.setChannels(channels); + settings.channels = channels; +} + +void DSP::setPrecision(unsigned precision) { + settings.precision = precision; + settings.intensity = 1 << (settings.precision - 1); + settings.intensityInverse = 1.0 / settings.intensity; +} + +void DSP::setFrequency(real frequency) { + settings.frequency = frequency; + resampler->setFrequency(); +} + +void DSP::setVolume(real volume) { + settings.volume = volume; +} + +void DSP::setBalance(real balance) { + settings.balance = balance; +} + +void DSP::setResampler(ResampleEngine::e engine) { + if(resampler) delete resampler; + + switch(engine) { + case ResampleEngine::Nearest: resampler = new ResampleNearest(*this); return; + case ResampleEngine::Linear: resampler = new ResampleLinear (*this); return; + case ResampleEngine::Cosine: resampler = new ResampleCosine (*this); return; + case ResampleEngine::Cubic: resampler = new ResampleCubic (*this); return; + case ResampleEngine::Hermite: resampler = new ResampleHermite(*this); return; + case ResampleEngine::Average: resampler = new ResampleAverage(*this); return; + case ResampleEngine::Sinc: resampler = new ResampleSinc (*this); return; + } + + throw; +} + +void DSP::setResamplerFrequency(real frequency) { + resampler->frequency = frequency; + resampler->setFrequency(); +} + +#endif diff --git a/libsnes/nall/endian.hpp b/libsnes/nall/endian.hpp new file mode 100644 index 0000000..40d1563 --- /dev/null +++ b/libsnes/nall/endian.hpp @@ -0,0 +1,38 @@ +#ifndef NALL_ENDIAN_HPP +#define NALL_ENDIAN_HPP + +#if !defined(ARCH_MSB) + //little-endian: uint8_t[] { 0x01, 0x02, 0x03, 0x04 } == 0x04030201 + #define order_lsb2(a,b) a,b + #define order_lsb3(a,b,c) a,b,c + #define order_lsb4(a,b,c,d) a,b,c,d + #define order_lsb5(a,b,c,d,e) a,b,c,d,e + #define order_lsb6(a,b,c,d,e,f) a,b,c,d,e,f + #define order_lsb7(a,b,c,d,e,f,g) a,b,c,d,e,f,g + #define order_lsb8(a,b,c,d,e,f,g,h) a,b,c,d,e,f,g,h + #define order_msb2(a,b) b,a + #define order_msb3(a,b,c) c,b,a + #define order_msb4(a,b,c,d) d,c,b,a + #define order_msb5(a,b,c,d,e) e,d,c,b,a + #define order_msb6(a,b,c,d,e,f) f,e,d,c,b,a + #define order_msb7(a,b,c,d,e,f,g) g,f,e,d,c,b,a + #define order_msb8(a,b,c,d,e,f,g,h) h,g,f,e,d,c,b,a +#else + //big-endian: uint8_t[] { 0x01, 0x02, 0x03, 0x04 } == 0x01020304 + #define order_lsb2(a,b) b,a + #define order_lsb3(a,b,c) c,b,a + #define order_lsb4(a,b,c,d) d,c,b,a + #define order_lsb5(a,b,c,d,e) e,d,c,b,a + #define order_lsb6(a,b,c,d,e,f) f,e,d,c,b,a + #define order_lsb7(a,b,c,d,e,f,g) g,f,e,d,c,b,a + #define order_lsb8(a,b,c,d,e,f,g,h) h,g,f,e,d,c,b,a + #define order_msb2(a,b) a,b + #define order_msb3(a,b,c) a,b,c + #define order_msb4(a,b,c,d) a,b,c,d + #define order_msb5(a,b,c,d,e) a,b,c,d,e + #define order_msb6(a,b,c,d,e,f) a,b,c,d,e,f + #define order_msb7(a,b,c,d,e,f,g) a,b,c,d,e,f,g + #define order_msb8(a,b,c,d,e,f,g,h) a,b,c,d,e,f,g,h +#endif + +#endif diff --git a/libsnes/nall/file.hpp b/libsnes/nall/file.hpp new file mode 100644 index 0000000..ace1176 --- /dev/null +++ b/libsnes/nall/file.hpp @@ -0,0 +1,345 @@ +#ifndef NALL_FILE_HPP +#define NALL_FILE_HPP + +#include +#include + +#if !defined(_WIN32) + #include +#else + #include +#endif + +#include +#include +#include +#include + +namespace nall { + inline FILE* fopen_utf8(const char *utf8_filename, const char *mode) { + #if !defined(_WIN32) + return fopen(utf8_filename, mode); + #else + return _wfopen(utf16_t(utf8_filename), utf16_t(mode)); + #endif + } + + class file { + public: + enum FileMode { mode_read, mode_write, mode_readwrite, mode_writeread }; + enum SeekMode { seek_absolute, seek_relative }; + + uint8_t read() { + if(!fp) return 0xff; //file not open + if(file_mode == mode_write) return 0xff; //reads not permitted + if(file_offset >= file_size) return 0xff; //cannot read past end of file + buffer_sync(); + return buffer[(file_offset++) & buffer_mask]; + } + + uintmax_t readl(unsigned length = 1) { + uintmax_t data = 0; + for(int i = 0; i < length; i++) { + data |= (uintmax_t)read() << (i << 3); + } + return data; + } + + uintmax_t readm(unsigned length = 1) { + uintmax_t data = 0; + while(length--) { + data <<= 8; + data |= read(); + } + return data; + } + + void read(uint8_t *buffer, unsigned length) { + while(length--) *buffer++ = read(); + } + + void write(uint8_t data) { + if(!fp) return; //file not open + if(file_mode == mode_read) return; //writes not permitted + buffer_sync(); + buffer[(file_offset++) & buffer_mask] = data; + buffer_dirty = true; + if(file_offset > file_size) file_size = file_offset; + } + + void writel(uintmax_t data, unsigned length = 1) { + while(length--) { + write(data); + data >>= 8; + } + } + + void writem(uintmax_t data, unsigned length = 1) { + for(int i = length - 1; i >= 0; i--) { + write(data >> (i << 3)); + } + } + + void write(const uint8_t *buffer, unsigned length) { + while(length--) write(*buffer++); + } + + void print_intern(string & data){ + char const * p = data; + while(*p) write(*p++); + } + + template + void print(T1 v1){ + string data(v1); + print_intern(data); + } + template + void print(T1 v1, T2 v2){ + string data(v1, v2); + print_intern(data); + } + template + void print(T1 v1, T2 v2, T3 v3){ + string data(v1, v2, v3); + print_intern(data); + } + template + void print(T1 v1, T2 v2, T3 v3, T4 v4){ + string data(v1, v2, v3, v4); + print_intern(data); + } + template + void print(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5){ + string data(v1, v2, v3, v4, v5); + print_intern(data); + } + template + void print(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6){ + string data(v1, v2, v3, v4, v5, v6); + print_intern(data); + } + template + void print(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7){ + string data(v1, v2, v3, v4, v5, v6, v7); + print_intern(data); + } + template + void print(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8){ + string data(v1, v2, v3, v4, v5, v6, v7, v8); + print_intern(data); + } + + void flush() { + buffer_flush(); + fflush(fp); + } + + void seek(int offset, SeekMode mode = seek_absolute) { + if(!fp) return; //file not open + buffer_flush(); + + uintmax_t req_offset = file_offset; + switch(mode) { + case seek_absolute: req_offset = offset; break; + case seek_relative: req_offset += offset; break; + } + + if(req_offset > file_size) { + if(file_mode == mode_read) { //cannot seek past end of file + req_offset = file_size; + } else { //pad file to requested location + file_offset = file_size; + while(file_size < req_offset) write(0x00); + } + } + + file_offset = req_offset; + } + + int offset() { + if(!fp) return -1; //file not open + return file_offset; + } + + int size() { + if(!fp) return -1; //file not open + return file_size; + } + + bool truncate(unsigned size) { + if(!fp) return false; //file not open + #if !defined(_WIN32) + return ftruncate(fileno(fp), size) == 0; + #else + return _chsize(fileno(fp), size) == 0; + #endif + } + + bool end() { + if(!fp) return true; //file not open + return file_offset >= file_size; + } + + static bool exists(const char *fn) { + #if !defined(_WIN32) + FILE *fp = fopen(fn, "rb"); + #else + FILE *fp = _wfopen(utf16_t(fn), L"rb"); + #endif + if(fp) { + fclose(fp); + return true; + } + return false; + } + + static unsigned size(const char *fn) { + #if !defined(_WIN32) + FILE *fp = fopen(fn, "rb"); + #else + FILE *fp = _wfopen(utf16_t(fn), L"rb"); + #endif + unsigned filesize = 0; + if(fp) { + fseek(fp, 0, SEEK_END); + filesize = ftell(fp); + fclose(fp); + } + return filesize; + } + + bool open() { + return fp; + } + + bool open(const char *fn, FileMode mode) { + if(fp) return false; + + switch(file_mode = mode) { + #if !defined(_WIN32) + case mode_read: fp = fopen(fn, "rb"); break; + case mode_write: fp = fopen(fn, "wb+"); break; //need read permission for buffering + case mode_readwrite: fp = fopen(fn, "rb+"); break; + case mode_writeread: fp = fopen(fn, "wb+"); break; + #else + case mode_read: fp = _wfopen(utf16_t(fn), L"rb"); break; + case mode_write: fp = _wfopen(utf16_t(fn), L"wb+"); break; + case mode_readwrite: fp = _wfopen(utf16_t(fn), L"rb+"); break; + case mode_writeread: fp = _wfopen(utf16_t(fn), L"wb+"); break; + #endif + } + if(!fp) return false; + buffer_offset = -1; //invalidate buffer + file_offset = 0; + fseek(fp, 0, SEEK_END); + file_size = ftell(fp); + fseek(fp, 0, SEEK_SET); + return true; + } + + void close() { + if(!fp) return; + buffer_flush(); + fclose(fp); + fp = 0; + } + + file() { + memset(buffer, 0, sizeof buffer); + buffer_offset = -1; + buffer_dirty = false; + fp = 0; + file_offset = 0; + file_size = 0; + file_mode = mode_read; + } + + ~file() { + close(); + } + + file& operator=(const file&){ + memset(buffer, 0, sizeof buffer); + buffer_offset = -1; + buffer_dirty = false; + fp = 0; + file_offset = 0; + file_size = 0; + file_mode = mode_read; + return *this; + } + file(const file&){ + memset(buffer, 0, sizeof buffer); + buffer_offset = -1; + buffer_dirty = false; + fp = 0; + file_offset = 0; + file_size = 0; + file_mode = mode_read; + } + + private: + enum { buffer_size = 1 << 12, buffer_mask = buffer_size - 1 }; + char buffer[buffer_size]; + int buffer_offset; + bool buffer_dirty; + FILE *fp; + unsigned file_offset; + unsigned file_size; + FileMode file_mode; + + void buffer_sync() { + if(!fp) return; //file not open + if(buffer_offset != (file_offset & ~buffer_mask)) { + buffer_flush(); + buffer_offset = file_offset & ~buffer_mask; + fseek(fp, buffer_offset, SEEK_SET); + unsigned length = (buffer_offset + buffer_size) <= file_size ? buffer_size : (file_size & buffer_mask); + if(length) unsigned unused = fread(buffer, 1, length, fp); + } + } + + void buffer_flush() { + if(!fp) return; //file not open + if(file_mode == mode_read) return; //buffer cannot be written to + if(buffer_offset < 0) return; //buffer unused + if(buffer_dirty == false) return; //buffer unmodified since read + fseek(fp, buffer_offset, SEEK_SET); + unsigned length = (buffer_offset + buffer_size) <= file_size ? buffer_size : (file_size & buffer_mask); + if(length) unsigned unused = fwrite(buffer, 1, length, fp); + buffer_offset = -1; //invalidate buffer + buffer_dirty = false; + } + }; +} + +#endif diff --git a/libsnes/nall/filemap.hpp b/libsnes/nall/filemap.hpp new file mode 100644 index 0000000..5e8cc05 --- /dev/null +++ b/libsnes/nall/filemap.hpp @@ -0,0 +1,200 @@ +#ifndef NALL_FILEMAP_HPP +#define NALL_FILEMAP_HPP + +#include +#include + +#include +#include +#if defined(_WIN32) + #include +#else + #include + #include + #include + #include + #include +#endif + +namespace nall { + class filemap { + public: + enum class mode : unsigned { read, write, readwrite, writeread }; + + bool open() const { return p_open(); } + bool open(const char *filename, mode mode_) { return p_open(filename, mode_); } + void close() { return p_close(); } + unsigned size() const { return p_size; } + uint8_t* data() { return p_handle; } + const uint8_t* data() const { return p_handle; } + filemap() : p_size(0), p_handle(0) { p_ctor(); } + filemap(const char *filename, mode mode_) : p_size(0), p_handle(0) { p_ctor(); p_open(filename, mode_); } + ~filemap() { p_dtor(); } + + private: + unsigned p_size; + uint8_t *p_handle; + + #if defined(_WIN32) + //============= + //MapViewOfFile + //============= + + HANDLE p_filehandle, p_maphandle; + + bool p_open() const { + return p_handle; + } + + bool p_open(const char *filename, mode mode_) { + int desired_access, creation_disposition, flprotect, map_access; + + switch(mode_) { + default: return false; + case mode::read: + desired_access = GENERIC_READ; + creation_disposition = OPEN_EXISTING; + flprotect = PAGE_READONLY; + map_access = FILE_MAP_READ; + break; + case mode::write: + //write access requires read access + desired_access = GENERIC_WRITE; + creation_disposition = CREATE_ALWAYS; + flprotect = PAGE_READWRITE; + map_access = FILE_MAP_ALL_ACCESS; + break; + case mode::readwrite: + desired_access = GENERIC_READ | GENERIC_WRITE; + creation_disposition = OPEN_EXISTING; + flprotect = PAGE_READWRITE; + map_access = FILE_MAP_ALL_ACCESS; + break; + case mode::writeread: + desired_access = GENERIC_READ | GENERIC_WRITE; + creation_disposition = CREATE_NEW; + flprotect = PAGE_READWRITE; + map_access = FILE_MAP_ALL_ACCESS; + break; + } + + p_filehandle = CreateFileW(utf16_t(filename), desired_access, FILE_SHARE_READ, NULL, + creation_disposition, FILE_ATTRIBUTE_NORMAL, NULL); + if(p_filehandle == INVALID_HANDLE_VALUE) return false; + + p_size = GetFileSize(p_filehandle, NULL); + + p_maphandle = CreateFileMapping(p_filehandle, NULL, flprotect, 0, p_size, NULL); + if(p_maphandle == INVALID_HANDLE_VALUE) { + CloseHandle(p_filehandle); + p_filehandle = INVALID_HANDLE_VALUE; + return false; + } + + p_handle = (uint8_t*)MapViewOfFile(p_maphandle, map_access, 0, 0, p_size); + return p_handle; + } + + void p_close() { + if(p_handle) { + UnmapViewOfFile(p_handle); + p_handle = 0; + } + + if(p_maphandle != INVALID_HANDLE_VALUE) { + CloseHandle(p_maphandle); + p_maphandle = INVALID_HANDLE_VALUE; + } + + if(p_filehandle != INVALID_HANDLE_VALUE) { + CloseHandle(p_filehandle); + p_filehandle = INVALID_HANDLE_VALUE; + } + } + + void p_ctor() { + p_filehandle = INVALID_HANDLE_VALUE; + p_maphandle = INVALID_HANDLE_VALUE; + } + + void p_dtor() { + close(); + } + + #else + //==== + //mmap + //==== + + int p_fd; + + bool p_open() const { + return p_handle; + } + + bool p_open(const char *filename, mode mode_) { + int open_flags, mmap_flags; + + switch(mode_) { + default: return false; + case mode::read: + open_flags = O_RDONLY; + mmap_flags = PROT_READ; + break; + case mode::write: + open_flags = O_RDWR | O_CREAT; //mmap() requires read access + mmap_flags = PROT_WRITE; + break; + case mode::readwrite: + open_flags = O_RDWR; + mmap_flags = PROT_READ | PROT_WRITE; + break; + case mode::writeread: + open_flags = O_RDWR | O_CREAT; + mmap_flags = PROT_READ | PROT_WRITE; + break; + } + + p_fd = ::open(filename, open_flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); + if(p_fd < 0) return false; + + struct stat p_stat; + fstat(p_fd, &p_stat); + p_size = p_stat.st_size; + + p_handle = (uint8_t*)mmap(0, p_size, mmap_flags, MAP_SHARED, p_fd, 0); + if(p_handle == MAP_FAILED) { + p_handle = 0; + ::close(p_fd); + p_fd = -1; + return false; + } + + return p_handle; + } + + void p_close() { + if(p_handle) { + munmap(p_handle, p_size); + p_handle = 0; + } + + if(p_fd >= 0) { + ::close(p_fd); + p_fd = -1; + } + } + + void p_ctor() { + p_fd = -1; + } + + void p_dtor() { + p_close(); + } + + #endif + }; +} + +#endif diff --git a/libsnes/nall/foreach.hpp b/libsnes/nall/foreach.hpp new file mode 100644 index 0000000..c0a6b1d --- /dev/null +++ b/libsnes/nall/foreach.hpp @@ -0,0 +1,18 @@ +#ifndef NALL_FOREACH_HPP +#define NALL_FOREACH_HPP + +#include + +#undef foreach + +#ifdef _MSC_VER +#define foreach(iter, object) \ + for(unsigned foreach_counter = 0, foreach_limit = container_size(object), foreach_once = 0, foreach_broken = 0; foreach_counter < foreach_limit && foreach_broken == 0; foreach_counter++, foreach_once = 0) \ + for(auto &iter = object[foreach_counter]; foreach_once == 0 && (foreach_broken = 1); foreach_once++, foreach_broken = 0) +#else +#define foreach(iter, object) \ + for(unsigned foreach_counter = 0, foreach_limit = container_size(object), foreach_once = 0, foreach_broken = 0; foreach_counter < foreach_limit && foreach_broken == 0; foreach_counter++, foreach_once = 0) \ + for(typeof(object[0u]) &iter = object[foreach_counter]; foreach_once == 0 && (foreach_broken = 1); foreach_once++, foreach_broken = 0) +#endif + +#endif diff --git a/libsnes/nall/function.hpp b/libsnes/nall/function.hpp new file mode 100644 index 0000000..58fe349 --- /dev/null +++ b/libsnes/nall/function.hpp @@ -0,0 +1,190 @@ +#ifndef NALL_FUNCTION_HPP +#define NALL_FUNCTION_HPP + +#include + +//prologue + +#define TN typename + +namespace nall { + template class function; +} + +//parameters = 0 + +#define cat(n) n +#define TL typename R +#define PL +#define CL + +#include "function.hpp" + +//parameters = 1 + +#define cat(n) , n +#define TL TN R, TN P1 +#define PL P1 p1 +#define CL p1 + +#include "function.hpp" + +//parameters = 2 + +#define cat(n) , n +#define TL TN R, TN P1, TN P2 +#define PL P1 p1, P2 p2 +#define CL p1, p2 + +#include "function.hpp" + +//parameters = 3 + +#define cat(n) , n +#define TL TN R, TN P1, TN P2, TN P3 +#define PL P1 p1, P2 p2, P3 p3 +#define CL p1, p2, p3 + +#include "function.hpp" + +//parameters = 4 + +#define cat(n) , n +#define TL TN R, TN P1, TN P2, TN P3, TN P4 +#define PL P1 p1, P2 p2, P3 p3, P4 p4 +#define CL p1, p2, p3, p4 + +#include "function.hpp" + +//parameters = 5 + +#define cat(n) , n +#define TL TN R, TN P1, TN P2, TN P3, TN P4, TN P5 +#define PL P1 p1, P2 p2, P3 p3, P4 p4, P5 p5 +#define CL p1, p2, p3, p4, p5 + +#include "function.hpp" + +//parameters = 6 + +#define cat(n) , n +#define TL TN R, TN P1, TN P2, TN P3, TN P4, TN P5, TN P6 +#define PL P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6 +#define CL p1, p2, p3, p4, p5, p6 + +#include "function.hpp" + +//parameters = 7 + +#define cat(n) , n +#define TL TN R, TN P1, TN P2, TN P3, TN P4, TN P5, TN P6, TN P7 +#define PL P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7 +#define CL p1, p2, p3, p4, p5, p6, p7 + +#include "function.hpp" + +//parameters = 8 + +#define cat(n) , n +#define TL TN R, TN P1, TN P2, TN P3, TN P4, TN P5, TN P6, TN P7, TN P8 +#define PL P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8 +#define CL p1, p2, p3, p4, p5, p6, p7, p8 + +#include "function.hpp" + +//epilogue + +#undef TN +#define NALL_FUNCTION_T + +#elif !defined(NALL_FUNCTION_T) + +//function implementation template class + +namespace nall { + template + class function { + private: + struct base1 { virtual void func1(PL) {} }; + struct base2 { virtual void func2(PL) {} }; + struct derived : base1, virtual base2 {}; + + struct data_t { + R (*fn_call)(const data_t& cat(PL)); + union { + R (*fn_global)(PL); + struct { + R (derived::*fn_member)(PL); + void *object; + }; + }; + } data; + + static R fn_call_global(const data_t &d cat(PL)) { + return d.fn_global(CL); + } + + template + static R fn_call_member(const data_t &d cat(PL)) { + return (((C*)d.object)->*((R (C::*&)(PL))d.fn_member))(CL); + } + + public: + R operator()(PL) const { return data.fn_call(data cat(CL)); } + operator bool() const { return data.fn_call; } + + function() { data.fn_call = 0; } + + function(void *fn) { + data.fn_call = fn ? &fn_call_global : 0; + data.fn_global = (R (*)(PL))fn; + } + + function(R (*fn)(PL)) { + data.fn_call = &fn_call_global; + data.fn_global = fn; + } + + template + function(R (C::*fn)(PL), C *obj) { + data.fn_call = &fn_call_member; + (R (C::*&)(PL))data.fn_member = fn; + assert(sizeof data.fn_member >= sizeof fn); + data.object = obj; + } + + template + function(R (C::*fn)(PL) const, C *obj) { + data.fn_call = &fn_call_member; + (R (C::*&)(PL))data.fn_member = (R (C::*&)(PL))fn; + assert(sizeof data.fn_member >= sizeof fn); + data.object = obj; + } + + function& operator=(void *fn) { return operator=(function(fn)); } + function& operator=(const function &source) { memcpy(&data, &source.data, sizeof(data_t)); return *this; } + function(const function &source) { memcpy(&data, &source.data, sizeof(data_t)); } + }; + + template + function bind(R (*fn)(PL)) { + return function(fn); + } + + template + function bind(R (C::*fn)(PL), C *obj) { + return function(fn, obj); + } + + template + function bind(R (C::*fn)(PL) const, C *obj) { + return function(fn, obj); + } +} + +#undef cat +#undef TL +#undef PL +#undef CL + +#endif diff --git a/libsnes/nall/gameboy/cartridge.hpp b/libsnes/nall/gameboy/cartridge.hpp new file mode 100644 index 0000000..059ddab --- /dev/null +++ b/libsnes/nall/gameboy/cartridge.hpp @@ -0,0 +1,115 @@ +#ifndef NALL_GAMEBOY_CARTRIDGE_HPP +#define NALL_GAMEBOY_CARTRIDGE_HPP + +namespace nall { + +class GameBoyCartridge { +public: + string markup; + inline GameBoyCartridge(uint8_t *data, unsigned size); + +//private: + struct Information { + string mapper; + bool ram; + bool battery; + bool rtc; + bool rumble; + + unsigned romsize; + unsigned ramsize; + } info; +}; + +GameBoyCartridge::GameBoyCartridge(uint8_t *romdata, unsigned romsize) { + markup = ""; + if(romsize < 0x4000) return; + + info.mapper = "unknown"; + info.ram = false; + info.battery = false; + info.rtc = false; + info.rumble = false; + + info.romsize = 0; + info.ramsize = 0; + + unsigned base = romsize - 0x8000; + if(romdata[base + 0x0104] == 0xce && romdata[base + 0x0105] == 0xed + && romdata[base + 0x0106] == 0x66 && romdata[base + 0x0107] == 0x66 + && romdata[base + 0x0108] == 0xcc && romdata[base + 0x0109] == 0x0d + && romdata[base + 0x0147] >= 0x0b && romdata[base + 0x0147] <= 0x0d + ) { + //MMM01 stores header at bottom of image + //flip this around for consistency with all other mappers + uint8_t header[0x8000]; + memcpy(header, romdata + base, 0x8000); + memmove(romdata + 0x8000, romdata, romsize - 0x8000); + memcpy(romdata, header, 0x8000); + } + + switch(romdata[0x0147]) { + case 0x00: info.mapper = "none"; break; + case 0x01: info.mapper = "MBC1"; break; + case 0x02: info.mapper = "MBC1"; info.ram = true; break; + case 0x03: info.mapper = "MBC1"; info.ram = true; info.battery = true; break; + case 0x05: info.mapper = "MBC2"; info.ram = true; break; + case 0x06: info.mapper = "MBC2"; info.ram = true; info.battery = true; break; + case 0x08: info.mapper = "none"; info.ram = true; break; + case 0x09: info.mapper = "MBC0"; info.ram = true; info.battery = true; break; + case 0x0b: info.mapper = "MMM01"; break; + case 0x0c: info.mapper = "MMM01"; info.ram = true; break; + case 0x0d: info.mapper = "MMM01"; info.ram = true; info.battery = true; break; + case 0x0f: info.mapper = "MBC3"; info.rtc = true; info.battery = true; break; + case 0x10: info.mapper = "MBC3"; info.rtc = true; info.ram = true; info.battery = true; break; + case 0x11: info.mapper = "MBC3"; break; + case 0x12: info.mapper = "MBC3"; info.ram = true; break; + case 0x13: info.mapper = "MBC3"; info.ram = true; info.battery = true; break; + case 0x19: info.mapper = "MBC5"; break; + case 0x1a: info.mapper = "MBC5"; info.ram = true; break; + case 0x1b: info.mapper = "MBC5"; info.ram = true; info.battery = true; break; + case 0x1c: info.mapper = "MBC5"; info.rumble = true; break; + case 0x1d: info.mapper = "MBC5"; info.rumble = true; info.ram = true; break; + case 0x1e: info.mapper = "MBC5"; info.rumble = true; info.ram = true; info.battery = true; break; + case 0xfc: break; //Pocket Camera + case 0xfd: break; //Bandai TAMA5 + case 0xfe: info.mapper = "HuC3"; break; + case 0xff: info.mapper = "HuC1"; info.ram = true; info.battery = true; break; + } + + switch(romdata[0x0148]) { default: + case 0x00: info.romsize = 2 * 16 * 1024; break; + case 0x01: info.romsize = 4 * 16 * 1024; break; + case 0x02: info.romsize = 8 * 16 * 1024; break; + case 0x03: info.romsize = 16 * 16 * 1024; break; + case 0x04: info.romsize = 32 * 16 * 1024; break; + case 0x05: info.romsize = 64 * 16 * 1024; break; + case 0x06: info.romsize = 128 * 16 * 1024; break; + case 0x07: info.romsize = 256 * 16 * 1024; break; + case 0x52: info.romsize = 72 * 16 * 1024; break; + case 0x53: info.romsize = 80 * 16 * 1024; break; + case 0x54: info.romsize = 96 * 16 * 1024; break; + } + + switch(romdata[0x0149]) { default: + case 0x00: info.ramsize = 0 * 1024; break; + case 0x01: info.ramsize = 2 * 1024; break; + case 0x02: info.ramsize = 8 * 1024; break; + case 0x03: info.ramsize = 32 * 1024; break; + } + + if(info.mapper == "MBC2") info.ramsize = 512; //512 x 4-bit + + markup.append(string( + string("\n"), + string("\n"), + string(" \n"))); + if(info.ramsize > 0) markup.append( + " \n"); + markup.append( + "\n"); +} + +} + +#endif diff --git a/libsnes/nall/input.hpp b/libsnes/nall/input.hpp new file mode 100644 index 0000000..f821a89 --- /dev/null +++ b/libsnes/nall/input.hpp @@ -0,0 +1,386 @@ +#ifndef NALL_INPUT_HPP +#define NALL_INPUT_HPP + +#include +#include +#include + +#include +#include + +namespace nall { + +struct Keyboard; +Keyboard& keyboard(unsigned = 0); + +static const char KeyboardScancodeName[][64] = { + "Escape", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", + "PrintScreen", "ScrollLock", "Pause", "Tilde", + "Num1", "Num2", "Num3", "Num4", "Num5", "Num6", "Num7", "Num8", "Num9", "Num0", + "Dash", "Equal", "Backspace", + "Insert", "Delete", "Home", "End", "PageUp", "PageDown", + "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", + "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", + "LeftBracket", "RightBracket", "Backslash", "Semicolon", "Apostrophe", "Comma", "Period", "Slash", + "Keypad1", "Keypad2", "Keypad3", "Keypad4", "Keypad5", "Keypad6", "Keypad7", "Keypad8", "Keypad9", "Keypad0", + "Point", "Enter", "Add", "Subtract", "Multiply", "Divide", + "NumLock", "CapsLock", + "Up", "Down", "Left", "Right", + "Tab", "Return", "Spacebar", "Menu", + "Shift", "Control", "Alt", "Super", +}; + +struct Keyboard { + const unsigned ID; + enum { Base = 1 }; + enum { Count = 8, Size = 128 }; + + enum Scancode { + Escape, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, + PrintScreen, ScrollLock, Pause, Tilde, + Num1, Num2, Num3, Num4, Num5, Num6, Num7, Num8, Num9, Num0, + Dash, Equal, Backspace, + Insert, Delete, Home, End, PageUp, PageDown, + A, B, C, D, E, F, G, H, I, J, K, L, M, + N, O, P, Q, R, S, T, U, V, W, X, Y, Z, + LeftBracket, RightBracket, Backslash, Semicolon, Apostrophe, Comma, Period, Slash, + Keypad1, Keypad2, Keypad3, Keypad4, Keypad5, Keypad6, Keypad7, Keypad8, Keypad9, Keypad0, + Point, Enter, Add, Subtract, Multiply, Divide, + NumLock, CapsLock, + Up, Down, Left, Right, + Tab, Return, Spacebar, Menu, + Shift, Control, Alt, Super, + Limit, + }; + + static signed numberDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(keyboard(i).belongsTo(scancode)) return i; + } + return -1; + } + + static signed keyDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(keyboard(i).isKey(scancode)) return scancode - keyboard(i).key(Escape); + } + return -1; + } + + static signed modifierDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(keyboard(i).isModifier(scancode)) return scancode - keyboard(i).key(Shift); + } + return -1; + } + + static bool isAnyKey(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(keyboard(i).isKey(scancode)) return true; + } + return false; + } + + static bool isAnyModifier(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(keyboard(i).isModifier(scancode)) return true; + } + return false; + } + + static uint16_t decode(const char *name) { + string s(name); + if(!strbegin(name, "KB")) return 0; + s.ltrim("KB"); + unsigned id = decimal(s); + optional pos = strpos(s, "::"); + if(!pos) return 0; + s = substr(s, pos() + 2); + for(unsigned i = 0; i < Limit; i++) { + if(s == KeyboardScancodeName[i]) return Base + Size * id + i; + } + return 0; + } + + string encode(uint16_t code) const { + unsigned index = 0; + for(unsigned i = 0; i < Count; i++) { + if(code >= Base + Size * i && code < Base + Size * (i + 1)) { + index = code - (Base + Size * i); + break; + } + } + return string() << "KB" << ID << "::" << KeyboardScancodeName[index]; + } + + uint16_t operator[](Scancode code) const { return Base + ID * Size + code; } + uint16_t key(unsigned id) const { return Base + Size * ID + id; } + bool isKey(unsigned id) const { return id >= key(Escape) && id <= key(Menu); } + bool isModifier(unsigned id) const { return id >= key(Shift) && id <= key(Super); } + bool belongsTo(uint16_t scancode) const { return isKey(scancode) || isModifier(scancode); } + + Keyboard(unsigned ID_) : ID(ID_) {} +}; + +inline Keyboard& keyboard(unsigned id) { + static Keyboard kb0(0), kb1(1), kb2(2), kb3(3), kb4(4), kb5(5), kb6(6), kb7(7); + switch(id) { default: + case 0: return kb0; case 1: return kb1; case 2: return kb2; case 3: return kb3; + case 4: return kb4; case 5: return kb5; case 6: return kb6; case 7: return kb7; + } +} + +static const char MouseScancodeName[][64] = { + "Xaxis", "Yaxis", "Zaxis", + "Button0", "Button1", "Button2", "Button3", "Button4", "Button5", "Button6", "Button7", +}; + +struct Mouse; +Mouse& mouse(unsigned = 0); + +struct Mouse { + const unsigned ID; + enum { Base = Keyboard::Base + Keyboard::Size * Keyboard::Count }; + enum { Count = 8, Size = 16 }; + enum { Axes = 3, Buttons = 8 }; + + enum Scancode { + Xaxis, Yaxis, Zaxis, + Button0, Button1, Button2, Button3, Button4, Button5, Button6, Button7, + Limit, + }; + + static signed numberDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(mouse(i).belongsTo(scancode)) return i; + } + return -1; + } + + static signed axisDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(mouse(i).isAxis(scancode)) return scancode - mouse(i).axis(0); + } + return -1; + } + + static signed buttonDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(mouse(i).isButton(scancode)) return scancode - mouse(i).button(0); + } + return -1; + } + + static bool isAnyAxis(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(mouse(i).isAxis(scancode)) return true; + } + return false; + } + + static bool isAnyButton(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(mouse(i).isButton(scancode)) return true; + } + return false; + } + + static uint16_t decode(const char *name) { + string s(name); + if(!strbegin(name, "MS")) return 0; + s.ltrim("MS"); + unsigned id = decimal(s); + optional pos = strpos(s, "::"); + if(!pos) return 0; + s = substr(s, pos() + 2); + for(unsigned i = 0; i < Limit; i++) { + if(s == MouseScancodeName[i]) return Base + Size * id + i; + } + return 0; + } + + string encode(uint16_t code) const { + unsigned index = 0; + for(unsigned i = 0; i < Count; i++) { + if(code >= Base + Size * i && code < Base + Size * (i + 1)) { + index = code - (Base + Size * i); + break; + } + } + return string() << "MS" << ID << "::" << MouseScancodeName[index]; + } + + uint16_t operator[](Scancode code) const { return Base + ID * Size + code; } + uint16_t axis(unsigned id) const { return Base + Size * ID + Xaxis + id; } + uint16_t button(unsigned id) const { return Base + Size * ID + Button0 + id; } + bool isAxis(unsigned id) const { return id >= axis(0) && id <= axis(2); } + bool isButton(unsigned id) const { return id >= button(0) && id <= button(7); } + bool belongsTo(uint16_t scancode) const { return isAxis(scancode) || isButton(scancode); } + + Mouse(unsigned ID_) : ID(ID_) {} +}; + +inline Mouse& mouse(unsigned id) { + static Mouse ms0(0), ms1(1), ms2(2), ms3(3), ms4(4), ms5(5), ms6(6), ms7(7); + switch(id) { default: + case 0: return ms0; case 1: return ms1; case 2: return ms2; case 3: return ms3; + case 4: return ms4; case 5: return ms5; case 6: return ms6; case 7: return ms7; + } +} + +static const char JoypadScancodeName[][64] = { + "Hat0", "Hat1", "Hat2", "Hat3", "Hat4", "Hat5", "Hat6", "Hat7", + "Axis0", "Axis1", "Axis2", "Axis3", "Axis4", "Axis5", "Axis6", "Axis7", + "Axis8", "Axis9", "Axis10", "Axis11", "Axis12", "Axis13", "Axis14", "Axis15", + "Button0", "Button1", "Button2", "Button3", "Button4", "Button5", "Button6", "Button7", + "Button8", "Button9", "Button10", "Button11", "Button12", "Button13", "Button14", "Button15", + "Button16", "Button17", "Button18", "Button19", "Button20", "Button21", "Button22", "Button23", + "Button24", "Button25", "Button26", "Button27", "Button28", "Button29", "Button30", "Button31", +}; + +struct Joypad; +Joypad& joypad(unsigned = 0); + +struct Joypad { + const unsigned ID; + enum { Base = Mouse::Base + Mouse::Size * Mouse::Count }; + enum { Count = 8, Size = 64 }; + enum { Hats = 8, Axes = 16, Buttons = 32 }; + + enum Scancode { + Hat0, Hat1, Hat2, Hat3, Hat4, Hat5, Hat6, Hat7, + Axis0, Axis1, Axis2, Axis3, Axis4, Axis5, Axis6, Axis7, + Axis8, Axis9, Axis10, Axis11, Axis12, Axis13, Axis14, Axis15, + Button0, Button1, Button2, Button3, Button4, Button5, Button6, Button7, + Button8, Button9, Button10, Button11, Button12, Button13, Button14, Button15, + Button16, Button17, Button18, Button19, Button20, Button21, Button22, Button23, + Button24, Button25, Button26, Button27, Button28, Button29, Button30, Button31, + Limit, + }; + + enum Hat { HatCenter = 0, HatUp = 1, HatRight = 2, HatDown = 4, HatLeft = 8 }; + + static signed numberDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(joypad(i).belongsTo(scancode)) return i; + } + return -1; + } + + static signed hatDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(joypad(i).isHat(scancode)) return scancode - joypad(i).hat(0); + } + return -1; + } + + static signed axisDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(joypad(i).isAxis(scancode)) return scancode - joypad(i).axis(0); + } + return -1; + } + + static signed buttonDecode(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(joypad(i).isButton(scancode)) return scancode - joypad(i).button(0); + } + return -1; + } + + static bool isAnyHat(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(joypad(i).isHat(scancode)) return true; + } + return false; + } + + static bool isAnyAxis(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(joypad(i).isAxis(scancode)) return true; + } + return false; + } + + static bool isAnyButton(uint16_t scancode) { + for(unsigned i = 0; i < Count; i++) { + if(joypad(i).isButton(scancode)) return true; + } + return false; + } + + static uint16_t decode(const char *name) { + string s(name); + if(!strbegin(name, "JP")) return 0; + s.ltrim("JP"); + unsigned id = decimal(s); + optional pos = strpos(s, "::"); + if(!pos) return 0; + s = substr(s, pos() + 2); + for(unsigned i = 0; i < Limit; i++) { + if(s == JoypadScancodeName[i]) return Base + Size * id + i; + } + return 0; + } + + string encode(uint16_t code) const { + unsigned index = 0; + for(unsigned i = 0; i < Count; i++) { + if(code >= Base + Size * i && code < Base + Size * (i + 1)) { + index = code - (Base + Size * i); + } + } + return string() << "JP" << ID << "::" << JoypadScancodeName[index]; + } + + uint16_t operator[](Scancode code) const { return Base + ID * Size + code; } + uint16_t hat(unsigned id) const { return Base + Size * ID + Hat0 + id; } + uint16_t axis(unsigned id) const { return Base + Size * ID + Axis0 + id; } + uint16_t button(unsigned id) const { return Base + Size * ID + Button0 + id; } + bool isHat(unsigned id) const { return id >= hat(0) && id <= hat(7); } + bool isAxis(unsigned id) const { return id >= axis(0) && id <= axis(15); } + bool isButton(unsigned id) const { return id >= button(0) && id <= button(31); } + bool belongsTo(uint16_t scancode) const { return isHat(scancode) || isAxis(scancode) || isButton(scancode); } + + Joypad(unsigned ID_) : ID(ID_) {} +}; + +inline Joypad& joypad(unsigned id) { + static Joypad jp0(0), jp1(1), jp2(2), jp3(3), jp4(4), jp5(5), jp6(6), jp7(7); + switch(id) { default: + case 0: return jp0; case 1: return jp1; case 2: return jp2; case 3: return jp3; + case 4: return jp4; case 5: return jp5; case 6: return jp6; case 7: return jp7; + } +} + +struct Scancode { + enum { None = 0, Limit = Joypad::Base + Joypad::Size * Joypad::Count }; + + static uint16_t decode(const char *name) { + uint16_t code; + code = Keyboard::decode(name); + if(code) return code; + code = Mouse::decode(name); + if(code) return code; + code = Joypad::decode(name); + if(code) return code; + return None; + } + + static string encode(uint16_t code) { + for(unsigned i = 0; i < Keyboard::Count; i++) { + if(keyboard(i).belongsTo(code)) return keyboard(i).encode(code); + } + for(unsigned i = 0; i < Mouse::Count; i++) { + if(mouse(i).belongsTo(code)) return mouse(i).encode(code); + } + for(unsigned i = 0; i < Joypad::Count; i++) { + if(joypad(i).belongsTo(code)) return joypad(i).encode(code); + } + return "None"; + } +}; + +} + +#endif diff --git a/libsnes/nall/libmalloc.c b/libsnes/nall/libmalloc.c new file mode 100644 index 0000000..2df1b73 --- /dev/null +++ b/libsnes/nall/libmalloc.c @@ -0,0 +1,71 @@ +// C99, build as shared library and LD_PRELOAD ;) + +#include +#include +#include +#include + +#define RTLD_NEXT ((void *) -1l) + +static void *(*real_malloc)(size_t); +static void (*real_free)(void *); + +static long malloc_count = 0; +static long free_count = 0; + +static bool block = false; + +static bool lib_block = true; + +void libmalloc_start(void) +{ + lib_block = false; +} + +void libmalloc_stop(void) +{ + lib_block = true; +} + +void *malloc(size_t size) +{ + if (!real_malloc) + real_malloc = dlsym(RTLD_NEXT, "malloc"); + + void *res = real_malloc(size); + if (block || lib_block) + return res; + + if (size && res) + malloc_count++; + + block = true; + printf("malloc =>\t%p (size = %u)\n", res, (unsigned)size); + printf("Delta = %ld\n", malloc_count - free_count); + block = false; + + return res; +} + +void free(void *ptr) +{ + if (!real_free) + real_free = dlsym(RTLD_NEXT, "free"); + + if (block || lib_block) + { + real_free(ptr); + return; + } + + if (ptr) + free_count++; + + block = true; + printf("free =>\t%p\n", ptr); + printf("Delta = %ld\n", malloc_count - free_count); + + real_free(ptr); + block = false; +} + diff --git a/libsnes/nall/lzss.hpp b/libsnes/nall/lzss.hpp new file mode 100644 index 0000000..202bc81 --- /dev/null +++ b/libsnes/nall/lzss.hpp @@ -0,0 +1,81 @@ +#ifndef NALL_LZSS_HPP +#define NALL_LZSS_HPP + +#include +#include +#include + +namespace nall { + class lzss { + public: + static bool encode(uint8_t *&output, unsigned &outlength, const uint8_t *input, unsigned inlength) { + output = new(zeromemory) uint8_t[inlength * 9 / 8 + 9]; + + unsigned i = 0, o = 0; + while(i < inlength) { + unsigned flagoffset = o++; + uint8_t flag = 0x00; + + for(unsigned b = 0; b < 8 && i < inlength; b++) { + unsigned longest = 0, pointer; + for(unsigned index = 1; index < 4096; index++) { + unsigned count = 0; + while(true) { + if(count >= 15 + 3) break; //verify pattern match is not longer than max length + if(i + count >= inlength) break; //verify pattern match does not read past end of input + if(i + count < index) break; //verify read is not before start of input + if(input[i + count] != input[i + count - index]) break; //verify pattern still matches + count++; + } + + if(count > longest) { + longest = count; + pointer = index; + } + } + + if(longest < 3) output[o++] = input[i++]; + else { + flag |= 1 << b; + uint16_t x = ((longest - 3) << 12) + pointer; + output[o++] = x; + output[o++] = x >> 8; + i += longest; + } + } + + output[flagoffset] = flag; + } + + outlength = o; + return true; + } + + static bool decode(uint8_t *&output, const uint8_t *input, unsigned length) { + output = new(zeromemory) uint8_t[length]; + + unsigned i = 0, o = 0; + while(o < length) { + uint8_t flag = input[i++]; + + for(unsigned b = 0; b < 8 && o < length; b++) { + if(!(flag & (1 << b))) output[o++] = input[i++]; + else { + uint16_t offset = input[i++]; + offset += input[i++] << 8; + uint16_t lookuplength = (offset >> 12) + 3; + offset &= 4095; + for(unsigned index = 0; index < lookuplength && o + index < length; index++) { + output[o + index] = output[o + index - offset]; + } + o += lookuplength; + } + } + } + + return true; + } + }; +} + +#endif diff --git a/libsnes/nall/memory_debug.hpp b/libsnes/nall/memory_debug.hpp new file mode 100644 index 0000000..3318b7a --- /dev/null +++ b/libsnes/nall/memory_debug.hpp @@ -0,0 +1,10 @@ +#ifndef NALL_MEMORY_DEBUG_HPP +#define NALL_MEMORY_DEBUG_HPP + +extern "C" { + void libmalloc_start(); + void libmalloc_stop(); +} + +#endif + diff --git a/libsnes/nall/moduloarray.hpp b/libsnes/nall/moduloarray.hpp new file mode 100644 index 0000000..be549ae --- /dev/null +++ b/libsnes/nall/moduloarray.hpp @@ -0,0 +1,40 @@ +#ifndef NALL_MODULO_HPP +#define NALL_MODULO_HPP + +#include + +namespace nall { + template class modulo_array { + public: + inline T operator[](int index) const { + return buffer[size + index]; + } + + inline T read(int index) const { + return buffer[size + index]; + } + + inline void write(unsigned index, const T value) { + buffer[index] = + buffer[index + size] = + buffer[index + size + size] = value; + } + + void serialize(serializer &s) { + s.array(buffer, size * 3); + } + + modulo_array() { + buffer = new T[size * 3](); + } + + ~modulo_array() { + delete[] buffer; + } + + private: + T *buffer; + }; +} + +#endif diff --git a/libsnes/nall/platform.hpp b/libsnes/nall/platform.hpp new file mode 100644 index 0000000..6eef0e0 --- /dev/null +++ b/libsnes/nall/platform.hpp @@ -0,0 +1,124 @@ +#ifndef NALL_PLATFORM_HPP +#define NALL_PLATFORM_HPP + +#include + +//========================= +//standard platform headers +//========================= + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(_WIN32) +#if defined(_WIN64) || defined(_MSC_VER) // Odd quirk + #define interface struct +#endif + #include + #include + #include + #undef interface +#else + #include + #include +#endif + +//================== +//warning supression +//================== + +//Visual C++ +#if defined(_MSC_VER) + //disable libc "deprecation" warnings + #pragma warning(disable:4996) +#endif + +//================ +//POSIX compliance +//================ + +#if defined(_MSC_VER) + #define PATH_MAX _MAX_PATH + #define va_copy(dest, src) ((dest) = (src)) +#endif + +#if defined(_WIN32) + #define getcwd _getcwd + #define ftruncate _chsize + #define putenv _putenv + #define mkdir(n, m) _wmkdir(nall::utf16_t(n)) + #define rmdir _rmdir + #define vsnprintf _vsnprintf + #define usleep(n) Sleep(n / 1000) +#endif + +//================ +//inline expansion +//================ + +#if defined(__GNUC__) + #define noinline __attribute__((noinline)) + #define inline inline + #define alwaysinline inline __attribute__((always_inline)) +#elif defined(_MSC_VER) + #define noinline __declspec(noinline) + #define inline inline + #define alwaysinline inline __forceinline +#else + #define noinline + #define inline inline + #define alwaysinline inline +#endif + +//========================= +//file system functionality +//========================= + +#if 0 // Shouldn't be needed here. +#if defined(_WIN32) + inline char* realpath(const char *filename, char *resolvedname) { + wchar_t fn[_MAX_PATH] = L""; + _wfullpath(fn, nall::utf16_t(filename), _MAX_PATH); + strcpy(resolvedname, nall::utf8_t(fn)); + return resolvedname; + } + + inline char* userpath(char *path) { + wchar_t fp[_MAX_PATH] = L""; + SHGetFolderPathW(0, CSIDL_APPDATA | CSIDL_FLAG_CREATE, 0, 0, fp); + strcpy(path, nall::utf8_t(fp)); + return path; + } + + inline char* getcwd(char *path) { + wchar_t fp[_MAX_PATH] = L""; + _wgetcwd(fp, _MAX_PATH); + strcpy(path, nall::utf8_t(fp)); + return path; + } +#else + //realpath() already exists + + inline char* userpath(char *path) { + *path = 0; + struct passwd *userinfo = getpwuid(getuid()); + if(userinfo) strcpy(path, userinfo->pw_dir); + return path; + } + + inline char *getcwd(char *path) { + return getcwd(path, PATH_MAX); + } +#endif +#endif + +#endif + diff --git a/libsnes/nall/priorityqueue.hpp b/libsnes/nall/priorityqueue.hpp new file mode 100644 index 0000000..17e5810 --- /dev/null +++ b/libsnes/nall/priorityqueue.hpp @@ -0,0 +1,120 @@ +#ifndef NALL_PRIORITYQUEUE_HPP +#define NALL_PRIORITYQUEUE_HPP + +#include +#include +#include +#include + +namespace nall { + template void priority_queue_nocallback(type_t) {} + + //priority queue implementation using binary min-heap array; + //does not require normalize() function. + //O(1) find (tick) + //O(log n) insert (enqueue) + //O(log n) remove (dequeue) + template class priority_queue { + public: + inline void tick(unsigned ticks) { + basecounter += ticks; + while(heapsize && gte(basecounter, heap[0].counter)) callback(dequeue()); + } + + //counter is relative to current time (eg enqueue(64, ...) fires in 64 ticks); + //counter cannot exceed std::numeric_limits::max() >> 1. + void enqueue(unsigned counter, type_t event) { + unsigned child = heapsize++; + counter += basecounter; + + while(child) { + unsigned parent = (child - 1) >> 1; + if(gte(counter, heap[parent].counter)) break; + + heap[child].counter = heap[parent].counter; + heap[child].event = heap[parent].event; + child = parent; + } + + heap[child].counter = counter; + heap[child].event = event; + } + + type_t dequeue() { + type_t event(heap[0].event); + unsigned parent = 0; + unsigned counter = heap[--heapsize].counter; + + while(true) { + unsigned child = (parent << 1) + 1; + if(child >= heapsize) break; + if(child + 1 < heapsize && gte(heap[child].counter, heap[child + 1].counter)) child++; + if(gte(heap[child].counter, counter)) break; + + heap[parent].counter = heap[child].counter; + heap[parent].event = heap[child].event; + parent = child; + } + + heap[parent].counter = counter; + heap[parent].event = heap[heapsize].event; + return event; + } + + void reset() { + basecounter = 0; + heapsize = 0; + } + + void serialize(serializer &s) { + s.integer(basecounter); + s.integer(heapsize); + for(unsigned n = 0; n < heapcapacity; n++) { + s.integer(heap[n].counter); + s.integer(heap[n].event); + } + } + + priority_queue(unsigned size, function callback_ = &priority_queue_nocallback) + : callback(callback_) { + heap = new heap_t[size]; + heapcapacity = size; + reset(); + } + + ~priority_queue() { + delete[] heap; + } + + priority_queue& operator=(const priority_queue& s){ + delete [] heap; + callback = s.callback; + heap = new heap_t[s.size]; + heapcapacity = s.size; + reset(); + } + + priority_queue(const priority_queue& s) : callback(s.callback) { + heap = new heap_t[s.size]; + heapcapacity = s.size; + reset(); + } + + private: + function callback; + unsigned basecounter; + unsigned heapsize; + unsigned heapcapacity; + struct heap_t { + unsigned counter; + type_t event; + } *heap; + + //return true if x is greater than or equal to y + inline bool gte(unsigned x, unsigned y) { + return x - y < (std::numeric_limits::max() >> 1); + } + }; +} + +#endif diff --git a/libsnes/nall/property.hpp b/libsnes/nall/property.hpp new file mode 100644 index 0000000..67d5e65 --- /dev/null +++ b/libsnes/nall/property.hpp @@ -0,0 +1,91 @@ +#ifndef NALL_PROPERTY_HPP +#define NALL_PROPERTY_HPP + +//nall::property implements ownership semantics into container classes +//example: property::readonly implies that only owner has full +//access to type; and all other code has readonly access. +// +//this code relies on extended friend semantics from C++0x to work, as it +//declares a friend class via a template paramter. it also exploits a bug in +//G++ 4.x to work even in C++98 mode. +// +//if compiling elsewhere, simply remove the friend class and private semantics + +//property can be used either of two ways: +//struct foo { +// property::readonly x; +// property::readwrite y; +//}; +//-or- +//struct foo : property { +// readonly x; +// readwrite y; +//}; + +//return types are const T& (byref) instead fo T (byval) to avoid major speed +//penalties for objects with expensive copy constructors + +//operator-> provides access to underlying object type: +//readonly foo; +//foo->bar(); +//... will call Object::bar(); + +//operator='s reference is constant so as to avoid leaking a reference handle +//that could bypass access restrictions + +//both constant and non-constant operators are provided, though it may be +//necessary to cast first, for instance: +//struct foo : property { readonly bar; } object; +//int main() { int value = const_cast(object); } + +//writeonly is useful for objects that have non-const reads, but const writes. +//however, to avoid leaking handles, the interface is very restricted. the only +//way to write is via operator=, which requires conversion via eg copy +//constructor. example: +//struct foo { +// foo(bool value) { ... } +//}; +//writeonly bar; +//bar = true; + +namespace nall { + template struct property { + template struct traits { typedef T type; }; + + template struct readonly { + const T* operator->() const { return &value; } + const T& operator()() const { return value; } + operator const T&() const { return value; } + //private: + T* operator->() { return &value; } + operator T&() { return value; } + const T& operator=(const T& value_) { return value = value_; } + T value; + // friend class traits::type; + }; + + template struct writeonly { + void operator=(const T& value_) { value = value_; } + //private: + const T* operator->() const { return &value; } + const T& operator()() const { return value; } + operator const T&() const { return value; } + T* operator->() { return &value; } + operator T&() { return value; } + T value; + // friend class traits::type; + }; + + template struct readwrite { + const T* operator->() const { return &value; } + const T& operator()() const { return value; } + operator const T&() const { return value; } + T* operator->() { return &value; } + operator T&() { return value; } + const T& operator=(const T& value_) { return value = value_; } + T value; + }; + }; +} + +#endif diff --git a/libsnes/nall/public_cast.hpp b/libsnes/nall/public_cast.hpp new file mode 100644 index 0000000..87cdddd --- /dev/null +++ b/libsnes/nall/public_cast.hpp @@ -0,0 +1,34 @@ +#ifndef NALL_PUBLIC_CAST_HPP +#define NALL_PUBLIC_CAST_HPP + +// Themaister: This is C++0x, but it not used for obvious reasons :) + +//this is a proof-of-concept-*only* C++ access-privilege elevation exploit. +//this code is 100% legal C++, per C++98 section 14.7.2 paragraph 8: +//"access checking rules do not apply to names in explicit instantiations." +//usage example: + +//struct N { typedef void (Class::*)(); }; +//template class public_cast; +//(class.*public_cast::value); + +//Class::Reference may be public, protected or private +//Class::Reference may be a function, object or variable + +namespace nall { + template struct public_cast; + + template struct public_cast { + static typename T::type value; + }; + + template typename T::type public_cast::value; + + template struct public_cast { + static typename T::type value; + }; + + template typename T::type public_cast::value = public_cast::value = P; +} + +#endif diff --git a/libsnes/nall/random.hpp b/libsnes/nall/random.hpp new file mode 100644 index 0000000..409c456 --- /dev/null +++ b/libsnes/nall/random.hpp @@ -0,0 +1,28 @@ +#ifndef NALL_RANDOM_HPP +#define NALL_RANDOM_HPP + +namespace nall { + //pseudo-random number generator + inline unsigned prng() { + static unsigned n = 0; + return n = (n >> 1) ^ (((n & 1) - 1) & 0xedb88320); + } + + struct random_lfsr { + inline void seed(unsigned seed__) { + seed_ = seed__; + } + + inline unsigned operator()() { + return seed_ = (seed_ >> 1) ^ (((seed_ & 1) - 1) & 0xedb88320); + } + + random_lfsr() : seed_(0) { + } + + private: + unsigned seed_; + }; +} + +#endif diff --git a/libsnes/nall/reference_array.hpp b/libsnes/nall/reference_array.hpp new file mode 100644 index 0000000..20834ef --- /dev/null +++ b/libsnes/nall/reference_array.hpp @@ -0,0 +1,105 @@ +#ifndef NALL_REFERENCE_ARRAY_HPP +#define NALL_REFERENCE_ARRAY_HPP + +// Themaister: This is C++0x, but doesn't seem to be used anyways. + +#include +#include +#include + +namespace nall { + template struct reference_array { + protected: + typedef typename std::remove_reference::type *Tptr; + Tptr *pool; + unsigned poolsize, buffersize; + + public: + unsigned size() const { return buffersize; } + unsigned capacity() const { return poolsize; } + + void reset() { + if(pool) free(pool); + pool = 0; + poolsize = 0; + buffersize = 0; + } + + void reserve(unsigned newsize) { + if(newsize == poolsize) return; + + pool = (Tptr*)realloc(pool, newsize * sizeof(T)); + poolsize = newsize; + buffersize = min(buffersize, newsize); + } + + void resize(unsigned newsize) { + if(newsize > poolsize) reserve(bit::round(newsize)); + buffersize = newsize; + } + + void append(const T data) { + unsigned index = buffersize++; + if(index >= poolsize) resize(index + 1); + pool[index] = &data; + } + + template reference_array(Args&... args) : pool(0), poolsize(0), buffersize(0) { + construct(args...); + } + + ~reference_array() { + reset(); + } + + reference_array& operator=(const reference_array &source) { + if(pool) free(pool); + buffersize = source.buffersize; + poolsize = source.poolsize; + pool = (Tptr*)malloc(sizeof(T) * poolsize); + memcpy(pool, source.pool, sizeof(T) * buffersize); + return *this; + } + + reference_array& operator=(const reference_array &&source) { + if(pool) free(pool); + pool = source.pool; + poolsize = source.poolsize; + buffersize = source.buffersize; + source.pool = 0; + source.reset(); + return *this; + } + + inline T operator[](unsigned index) { + if(index >= buffersize) throw "reference_array[] out of bounds"; + return *pool[index]; + } + + inline const T operator[](unsigned index) const { + if(index >= buffersize) throw "reference_array[] out of bounds"; + return *pool[index]; + } + + private: + void construct() { + } + + void construct(const reference_array &source) { + operator=(source); + } + + void construct(const reference_array &&source) { + operator=(std::move(source)); + } + + template void construct(T data, Args&... args) { + append(data); + construct(args...); + } + }; + + template struct has_size> { enum { value = true }; }; +} + +#endif diff --git a/libsnes/nall/serial.hpp b/libsnes/nall/serial.hpp new file mode 100644 index 0000000..9ac8451 --- /dev/null +++ b/libsnes/nall/serial.hpp @@ -0,0 +1,85 @@ +#ifndef NALL_SERIAL_HPP +#define NALL_SERIAL_HPP + +#include +#include +#include +#include + +#include + +namespace nall { + class serial { + public: + //-1 on error, otherwise return bytes read + int read(uint8_t *data, unsigned length) { + if(port_open == false) return -1; + return ::read(port, (void*)data, length); + } + + //-1 on error, otherwise return bytes written + int write(const uint8_t *data, unsigned length) { + if(port_open == false) return -1; + return ::write(port, (void*)data, length); + } + + bool open(const char *portname, unsigned rate, bool flowcontrol) { + close(); + + port = ::open(portname, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK); + if(port == -1) return false; + + if(ioctl(port, TIOCEXCL) == -1) { close(); return false; } + if(fcntl(port, F_SETFL, 0) == -1) { close(); return false; } + if(tcgetattr(port, &original_attr) == -1) { close(); return false; } + + termios attr = original_attr; + cfmakeraw(&attr); + cfsetspeed(&attr, rate); + + attr.c_lflag &=~ (ECHO | ECHONL | ISIG | ICANON | IEXTEN); + attr.c_iflag &=~ (BRKINT | PARMRK | INPCK | ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXOFF | IXANY); + attr.c_iflag |= (IGNBRK | IGNPAR); + attr.c_oflag &=~ (OPOST); + attr.c_cflag &=~ (CSIZE | CSTOPB | PARENB | CLOCAL); + attr.c_cflag |= (CS8 | CREAD); + if(flowcontrol == false) { + attr.c_cflag &= ~CRTSCTS; + } else { + attr.c_cflag |= CRTSCTS; + } + attr.c_cc[VTIME] = attr.c_cc[VMIN] = 0; + + if(tcsetattr(port, TCSANOW, &attr) == -1) { close(); return false; } + return port_open = true; + } + + void close() { + if(port != -1) { + tcdrain(port); + if(port_open == true) { + tcsetattr(port, TCSANOW, &original_attr); + port_open = false; + } + ::close(port); + port = -1; + } + } + + serial() { + port = -1; + port_open = false; + } + + ~serial() { + close(); + } + + private: + int port; + bool port_open; + termios original_attr; + }; +} + +#endif diff --git a/libsnes/nall/serializer.hpp b/libsnes/nall/serializer.hpp new file mode 100644 index 0000000..10a492c --- /dev/null +++ b/libsnes/nall/serializer.hpp @@ -0,0 +1,127 @@ +#ifndef NALL_SERIALIZER_HPP +#define NALL_SERIALIZER_HPP + +#include +#include +#include + +namespace nall { + //serializer: a class designed to save and restore the state of classes. + // + //benefits: + //- data() will be portable in size (it is not necessary to specify type sizes.) + //- data() will be portable in endianness (always stored internally as little-endian.) + //- one serialize function can both save and restore class states. + // + //caveats: + //- only plain-old-data can be stored. complex classes must provide serialize(serializer&); + //- floating-point usage is not portable across platforms + + class serializer { + public: + enum mode_t { Load, Save, Size }; + + mode_t mode() const { + return imode; + } + + const uint8_t* data() const { + return idata; + } + + unsigned size() const { + return isize; + } + + unsigned capacity() const { + return icapacity; + } + + template void floatingpoint(T &value) { + enum { size = sizeof(T) }; + //this is rather dangerous, and not cross-platform safe; + //but there is no standardized way to export FP-values + uint8_t *p = (uint8_t*)&value; + if(imode == Save) { + for(unsigned n = 0; n < size; n++) idata[isize++] = p[n]; + } else if(imode == Load) { + for(unsigned n = 0; n < size; n++) p[n] = idata[isize++]; + } else { + isize += size; + } + } + + template void integer(T &value) { + enum { size = std::is_same::value ? 1 : sizeof(T) }; + if(imode == Save) { + for(unsigned n = 0; n < size; n++) idata[isize++] = value >> (n << 3); + } else if(imode == Load) { + value = 0; + for(unsigned n = 0; n < size; n++) value |= static_cast(idata[isize++]) << (n << 3); + } else if(imode == Size) { + isize += size; + } + } + + template void array(T &array) { + enum { size = sizeof(T) / sizeof(typename std::remove_extent::type) }; + for(unsigned n = 0; n < size; n++) integer(array[n]); + } + + template void array(T array, unsigned size) { + for(unsigned n = 0; n < size; n++) integer(array[n]); + } + + //copy + serializer& operator=(const serializer &s) { + if(idata) delete[] idata; + + imode = s.imode; + idata = new uint8_t[s.icapacity]; + isize = s.isize; + icapacity = s.icapacity; + + memcpy(idata, s.idata, s.icapacity); + return *this; + } + + serializer(const serializer &s) : idata(0) { + operator=(s); + } + + //construction + serializer() { + imode = Size; + idata = 0; + isize = 0; + } + + serializer(unsigned capacity) { + imode = Save; + idata = new uint8_t[capacity](); + isize = 0; + icapacity = capacity; + } + + serializer(const uint8_t *data, unsigned capacity) { + imode = Load; + idata = new uint8_t[capacity]; + isize = 0; + icapacity = capacity; + memcpy(idata, data, capacity); + } + + ~serializer() { + if(idata) delete[] idata; + } + + private: + mode_t imode; + uint8_t *idata; + unsigned isize; + unsigned icapacity; + }; + +}; + +#endif diff --git a/libsnes/nall/sha256.hpp b/libsnes/nall/sha256.hpp new file mode 100644 index 0000000..c63367a --- /dev/null +++ b/libsnes/nall/sha256.hpp @@ -0,0 +1,145 @@ +#ifndef NALL_SHA256_HPP +#define NALL_SHA256_HPP + +//author: vladitx + +#include + +namespace nall { + #define PTR(t, a) ((t*)(a)) + + #define SWAP32(x) ((uint32_t)( \ + (((uint32_t)(x) & 0x000000ff) << 24) | \ + (((uint32_t)(x) & 0x0000ff00) << 8) | \ + (((uint32_t)(x) & 0x00ff0000) >> 8) | \ + (((uint32_t)(x) & 0xff000000) >> 24) \ + )) + + #define ST32(a, d) *PTR(uint32_t, a) = (d) + #define ST32BE(a, d) ST32(a, SWAP32(d)) + + #define LD32(a) *PTR(uint32_t, a) + #define LD32BE(a) SWAP32(LD32(a)) + + #define LSL32(x, n) ((uint32_t)(x) << (n)) + #define LSR32(x, n) ((uint32_t)(x) >> (n)) + #define ROR32(x, n) (LSR32(x, n) | LSL32(x, 32 - (n))) + + //first 32 bits of the fractional parts of the square roots of the first 8 primes 2..19 + static const uint32_t T_H[8] = { + 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19, + }; + + //first 32 bits of the fractional parts of the cube roots of the first 64 primes 2..311 + static const uint32_t T_K[64] = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, + }; + + struct sha256_ctx { + uint8_t in[64]; + unsigned inlen; + + uint32_t w[64]; + uint32_t h[8]; + uint64_t len; + }; + + inline void sha256_init(sha256_ctx *p) { + memset(p, 0, sizeof(sha256_ctx)); + memcpy(p->h, T_H, sizeof(T_H)); + } + + static void sha256_block(sha256_ctx *p) { + unsigned i; + uint32_t s0, s1; + uint32_t a, b, c, d, e, f, g, h; + uint32_t t1, t2, maj, ch; + + for(i = 0; i < 16; i++) p->w[i] = LD32BE(p->in + i * 4); + + for(i = 16; i < 64; i++) { + s0 = ROR32(p->w[i - 15], 7) ^ ROR32(p->w[i - 15], 18) ^ LSR32(p->w[i - 15], 3); + s1 = ROR32(p->w[i - 2], 17) ^ ROR32(p->w[i - 2], 19) ^ LSR32(p->w[i - 2], 10); + p->w[i] = p->w[i - 16] + s0 + p->w[i - 7] + s1; + } + + a = p->h[0]; b = p->h[1]; c = p->h[2]; d = p->h[3]; + e = p->h[4]; f = p->h[5]; g = p->h[6]; h = p->h[7]; + + for(i = 0; i < 64; i++) { + s0 = ROR32(a, 2) ^ ROR32(a, 13) ^ ROR32(a, 22); + maj = (a & b) ^ (a & c) ^ (b & c); + t2 = s0 + maj; + s1 = ROR32(e, 6) ^ ROR32(e, 11) ^ ROR32(e, 25); + ch = (e & f) ^ (~e & g); + t1 = h + s1 + ch + T_K[i] + p->w[i]; + + h = g; g = f; f = e; e = d + t1; + d = c; c = b; b = a; a = t1 + t2; + } + + p->h[0] += a; p->h[1] += b; p->h[2] += c; p->h[3] += d; + p->h[4] += e; p->h[5] += f; p->h[6] += g; p->h[7] += h; + + //next block + p->inlen = 0; + } + + inline void sha256_chunk(sha256_ctx *p, const uint8_t *s, unsigned len) { + unsigned l; + p->len += len; + + while(len) { + l = 64 - p->inlen; + l = (len < l) ? len : l; + + memcpy(p->in + p->inlen, s, l); + s += l; + p->inlen += l; + len -= l; + + if(p->inlen == 64) sha256_block(p); + } + } + + inline void sha256_final(sha256_ctx *p) { + uint64_t len; + p->in[p->inlen++] = 0x80; + + if(p->inlen > 56) { + memset(p->in + p->inlen, 0, 64 - p->inlen); + sha256_block(p); + } + + memset(p->in + p->inlen, 0, 56 - p->inlen); + + len = p->len << 3; + ST32BE(p->in + 56, len >> 32); + ST32BE(p->in + 60, len); + sha256_block(p); + } + + inline void sha256_hash(sha256_ctx *p, uint8_t *s) { + uint32_t *t = (uint32_t*)s; + for(unsigned i = 0; i < 8; i++) ST32BE(t++, p->h[i]); + } + + #undef PTR + #undef SWAP32 + #undef ST32 + #undef ST32BE + #undef LD32 + #undef LD32BE + #undef LSL32 + #undef LSR32 + #undef ROR32 +} + +#endif diff --git a/libsnes/nall/snes/cartridge.hpp b/libsnes/nall/snes/cartridge.hpp new file mode 100644 index 0000000..6bf4a82 --- /dev/null +++ b/libsnes/nall/snes/cartridge.hpp @@ -0,0 +1,885 @@ +#ifndef NALL_SNES_CARTRIDGE_HPP +#define NALL_SNES_CARTRIDGE_HPP + +namespace nall { + +class SnesCartridge { +public: + string markup; + inline SnesCartridge(const uint8_t *data, unsigned size); + +//private: + inline void read_header(const uint8_t *data, unsigned size); + inline unsigned find_header(const uint8_t *data, unsigned size); + inline unsigned score_header(const uint8_t *data, unsigned size, unsigned addr); + inline unsigned gameboy_ram_size(const uint8_t *data, unsigned size); + inline bool gameboy_has_rtc(const uint8_t *data, unsigned size); + + enum HeaderField { + 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 Mode { + ModeNormal, + ModeBsxSlotted, + ModeBsx, + ModeSufamiTurbo, + ModeSuperGameBoy, + }; + + enum Type { + TypeNormal, + TypeBsxSlotted, + TypeBsxBios, + TypeBsx, + TypeSufamiTurboBios, + TypeSufamiTurbo, + TypeSuperGameBoy1Bios, + TypeSuperGameBoy2Bios, + TypeGameBoy, + TypeUnknown, + }; + + enum Region { + NTSC, + PAL, + }; + + enum MemoryMapper { + LoROM, + HiROM, + ExLoROM, + ExHiROM, + SuperFXROM, + SA1ROM, + SPC7110ROM, + BSCLoROM, + BSCHiROM, + BSXROM, + STROM, + }; + + enum DSP1MemoryMapper { + DSP1Unmapped, + DSP1LoROM1MB, + DSP1LoROM2MB, + DSP1HiROM, + }; + + bool loaded; //is a base cartridge inserted? + unsigned crc32; //crc32 of all cartridges (base+slot(s)) + unsigned rom_size; + unsigned ram_size; + + Mode mode; + Type type; + Region region; + MemoryMapper mapper; + DSP1MemoryMapper dsp1_mapper; + + bool has_bsx_slot; + bool has_superfx; + bool has_sa1; + bool has_srtc; + bool has_sdd1; + bool has_spc7110; + bool has_spc7110rtc; + bool has_cx4; + bool has_dsp1; + bool has_dsp2; + bool has_dsp3; + bool has_dsp4; + bool has_obc1; + bool has_st010; + bool has_st011; + bool has_st018; +}; + +SnesCartridge::SnesCartridge(const uint8_t *data, unsigned size) { + read_header(data, size); + + string xml; + markup = "\n"; + + if(type == TypeBsx) { + markup.append("\n"); + return; + } + + if(type == TypeSufamiTurbo) { + markup.append("\n"); + return; + } + + if(type == TypeGameBoy) { + markup.append(" 0) { + markup.append(" \n"); + } + markup.append("\n"); + return; + } + + const char *range = (rom_size > 0x200000) || (ram_size > 32 * 1024) ? "0000-7fff" : "0000-ffff"; + markup.append("\n"); + + if(type == TypeSuperGameBoy1Bios || type == TypeSuperGameBoy2Bios) markup.append( + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + ); + + else if(has_cx4) markup.append( + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + ); + + else if(has_spc7110) { + markup.append( + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + ); + if(has_spc7110rtc) markup.append( + " \n" + " \n" + " \n" + " \n" + ); + markup.append( + " \n" + ); + } + + else if(mapper == LoROM) { + markup.append( + " \n" + " \n" + " \n" + " \n" + ); + if(ram_size > 0) markup.append(string( + string(" \n"), + string(" \n" + " \n"), + string(" \n"), + string(" \n"), + " \n" + )); + } + + else if(mapper == HiROM) { + markup.append( + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + ); + if(ram_size > 0) markup.append( + " \n" + " \n" + " \n" + " \n" + " \n" + ); + } + + else if(mapper == ExLoROM) { + markup.append( + " \n" + " \n" + " \n" + " \n" + " \n" + ); + if(ram_size > 0) markup.append( + " \n" + " \n" + " \n" + " \n" + " \n" + ); + } + + else if(mapper == ExHiROM) { + markup.append( + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + ); + if(ram_size > 0) markup.append( + " \n" + " \n" + " \n" + " \n" + " \n" + ); + } + + else if(mapper == SuperFXROM) markup.append( + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + ); + + else if(mapper == SA1ROM) markup.append( + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + ); + + else if(mapper == BSCLoROM) markup.append( + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + ); + + else if(mapper == BSCHiROM) markup.append( + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + ); + + else if(mapper == BSXROM) markup.append( + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + ); + + else if(mapper == STROM) markup.append( + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + ); + + if(has_srtc) markup.append( + " \n" + " \n" + " \n" + " \n" + ); + + if(has_sdd1) markup.append( + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + ); + + if(has_obc1) markup.append( + " \n" + " \n" + " \n" + " \n" + ); + + if(has_dsp1) { + markup.append(" \n"); + if(dsp1_mapper == DSP1LoROM1MB) markup.append( + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + ); + if(dsp1_mapper == DSP1LoROM2MB) markup.append( + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + ); + if(dsp1_mapper == DSP1HiROM) markup.append( + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + ); + markup.append(" \n"); + } + + if(has_dsp2) markup.append( + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + ); + + if(has_dsp3) markup.append( + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + ); + + if(has_dsp4) markup.append( + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + ); + + if(has_st010) markup.append( + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + ); + + if(has_st011) markup.append( + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + ); + + if(has_st018) markup.append( + " \n" + " \n" + " \n" + " \n" + ); + + markup.append("\n"); +} + +void SnesCartridge::read_header(const uint8_t *data, unsigned size) { + type = TypeUnknown; + mapper = LoROM; + dsp1_mapper = DSP1Unmapped; + region = NTSC; + rom_size = size; + ram_size = 0; + + has_bsx_slot = false; + has_superfx = false; + has_sa1 = false; + has_srtc = false; + has_sdd1 = false; + has_spc7110 = false; + has_spc7110rtc = false; + has_cx4 = false; + has_dsp1 = false; + has_dsp2 = false; + has_dsp3 = false; + has_dsp4 = false; + has_obc1 = false; + has_st010 = false; + has_st011 = false; + has_st018 = false; + + //===================== + //detect Game Boy carts + //===================== + + if(size >= 0x0140) { + if(data[0x0104] == 0xce && data[0x0105] == 0xed && data[0x0106] == 0x66 && data[0x0107] == 0x66 + && data[0x0108] == 0xcc && data[0x0109] == 0x0d && data[0x010a] == 0x00 && data[0x010b] == 0x0b) { + type = TypeGameBoy; + return; + } + } + + if(size < 32768) { + type = TypeUnknown; + return; + } + + const unsigned index = find_header(data, size); + const uint8_t mapperid = data[index + Mapper]; + const uint8_t rom_type = data[index + RomType]; + const uint8_t rom_size = data[index + RomSize]; + const uint8_t company = data[index + Company]; + const uint8_t regionid = data[index + CartRegion] & 0x7f; + + ram_size = 1024 << (data[index + RamSize] & 7); + if(ram_size == 1024) ram_size = 0; //no RAM present + + //0, 1, 13 = NTSC; 2 - 12 = PAL + region = (regionid <= 1 || regionid >= 13) ? NTSC : PAL; + + //======================= + //detect BS-X flash carts + //======================= + + if(data[index + 0x13] == 0x00 || data[index + 0x13] == 0xff) { + if(data[index + 0x14] == 0x00) { + const uint8_t n15 = data[index + 0x15]; + if(n15 == 0x00 || n15 == 0x80 || n15 == 0x84 || n15 == 0x9c || n15 == 0xbc || n15 == 0xfc) { + if(data[index + 0x1a] == 0x33 || data[index + 0x1a] == 0xff) { + type = TypeBsx; + mapper = BSXROM; + region = NTSC; //BS-X only released in Japan + return; + } + } + } + } + + //========================= + //detect Sufami Turbo carts + //========================= + + if(!memcmp(data, "BANDAI SFC-ADX", 14)) { + if(!memcmp(data + 16, "SFC-ADX BACKUP", 14)) { + type = TypeSufamiTurboBios; + } else { + type = TypeSufamiTurbo; + } + mapper = STROM; + region = NTSC; //Sufami Turbo only released in Japan + return; //RAM size handled outside this routine + } + + //========================== + //detect Super Game Boy BIOS + //========================== + + if(!memcmp(data + index, "Super GAMEBOY2", 14)) { + type = TypeSuperGameBoy2Bios; + return; + } + + if(!memcmp(data + index, "Super GAMEBOY", 13)) { + type = TypeSuperGameBoy1Bios; + return; + } + + //===================== + //detect standard carts + //===================== + + //detect presence of BS-X flash cartridge connector (reads extended header information) + if(data[index - 14] == 'Z') { + if(data[index - 11] == 'J') { + uint8_t n13 = data[index - 13]; + if((n13 >= 'A' && n13 <= 'Z') || (n13 >= '0' && n13 <= '9')) { + if(company == 0x33 || (data[index - 10] == 0x00 && data[index - 4] == 0x00)) { + has_bsx_slot = true; + } + } + } + } + + if(has_bsx_slot) { + if(!memcmp(data + index, "Satellaview BS-X ", 21)) { + //BS-X base cart + type = TypeBsxBios; + mapper = BSXROM; + region = NTSC; //BS-X only released in Japan + return; //RAM size handled internally by load_cart_bsx() -> BSXCart class + } else { + type = TypeBsxSlotted; + mapper = (index == 0x7fc0 ? BSCLoROM : BSCHiROM); + region = NTSC; //BS-X slotted cartridges only released in Japan + } + } else { + //standard cart + type = TypeNormal; + + if(index == 0x7fc0 && size >= 0x401000) { + mapper = ExLoROM; + } else if(index == 0x7fc0 && mapperid == 0x32) { + mapper = ExLoROM; + } else if(index == 0x7fc0) { + mapper = LoROM; + } else if(index == 0xffc0) { + mapper = HiROM; + } else { //index == 0x40ffc0 + mapper = ExHiROM; + } + } + + if(mapperid == 0x20 && (rom_type == 0x13 || rom_type == 0x14 || rom_type == 0x15 || rom_type == 0x1a)) { + has_superfx = true; + mapper = SuperFXROM; + ram_size = 1024 << (data[index - 3] & 7); + if(ram_size == 1024) ram_size = 0; + } + + if(mapperid == 0x23 && (rom_type == 0x32 || rom_type == 0x34 || rom_type == 0x35)) { + has_sa1 = true; + mapper = SA1ROM; + } + + if(mapperid == 0x35 && rom_type == 0x55) { + has_srtc = true; + } + + if(mapperid == 0x32 && (rom_type == 0x43 || rom_type == 0x45)) { + has_sdd1 = true; + } + + if(mapperid == 0x3a && (rom_type == 0xf5 || rom_type == 0xf9)) { + has_spc7110 = true; + has_spc7110rtc = (rom_type == 0xf9); + mapper = SPC7110ROM; + } + + if(mapperid == 0x20 && rom_type == 0xf3) { + has_cx4 = true; + } + + if((mapperid == 0x20 || mapperid == 0x21) && rom_type == 0x03) { + has_dsp1 = true; + } + + if(mapperid == 0x30 && rom_type == 0x05 && company != 0xb2) { + has_dsp1 = true; + } + + if(mapperid == 0x31 && (rom_type == 0x03 || rom_type == 0x05)) { + has_dsp1 = true; + } + + if(has_dsp1 == true) { + if((mapperid & 0x2f) == 0x20 && size <= 0x100000) { + dsp1_mapper = DSP1LoROM1MB; + } else if((mapperid & 0x2f) == 0x20) { + dsp1_mapper = DSP1LoROM2MB; + } else if((mapperid & 0x2f) == 0x21) { + dsp1_mapper = DSP1HiROM; + } + } + + if(mapperid == 0x20 && rom_type == 0x05) { + has_dsp2 = true; + } + + if(mapperid == 0x30 && rom_type == 0x05 && company == 0xb2) { + has_dsp3 = true; + } + + if(mapperid == 0x30 && rom_type == 0x03) { + has_dsp4 = true; + } + + if(mapperid == 0x30 && rom_type == 0x25) { + has_obc1 = true; + } + + if(mapperid == 0x30 && rom_type == 0xf6 && rom_size >= 10) { + has_st010 = true; + } + + if(mapperid == 0x30 && rom_type == 0xf6 && rom_size < 10) { + has_st011 = true; + } + + if(mapperid == 0x30 && rom_type == 0xf5) { + has_st018 = true; + } +} + +unsigned SnesCartridge::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_lo >= score_hi && score_lo >= score_ex) { + return 0x007fc0; + } else if(score_hi >= score_ex) { + return 0x00ffc0; + } else { + return 0x40ffc0; + } +} + +unsigned SnesCartridge::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? + int score = 0; + + uint16_t resetvector = data[addr + ResetVector] | (data[addr + ResetVector + 1] << 8); + uint16_t checksum = data[addr + Checksum ] | (data[addr + Checksum + 1] << 8); + uint16_t complement = data[addr + Complement ] | (data[addr + Complement + 1] << 8); + + uint8_t resetop = data[(addr & ~0x7fff) | (resetvector & 0x7fff)]; //first opcode executed upon reset + uint8_t 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. + if(resetvector < 0x8000) return 0; + + //some images duplicate the header in multiple locations, and others have completely + //invalid header information that cannot be relied upon. + //below code will analyze the first opcode executed at the specified reset vector to + //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 + ) 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 + ) 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 + ) 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 + ) 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 + 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(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; +} + +unsigned SnesCartridge::gameboy_ram_size(const uint8_t *data, unsigned size) { + if(size < 512) return 0; + switch(data[0x0149]) { + case 0x00: return 0 * 1024; + case 0x01: return 8 * 1024; + case 0x02: return 8 * 1024; + case 0x03: return 32 * 1024; + case 0x04: return 128 * 1024; + case 0x05: return 128 * 1024; + default: return 128 * 1024; + } +} + +bool SnesCartridge::gameboy_has_rtc(const uint8_t *data, unsigned size) { + if(size < 512) return false; + if(data[0x0147] == 0x0f ||data[0x0147] == 0x10) return true; + return false; +} + +} + +#endif diff --git a/libsnes/nall/snes/cpu.hpp b/libsnes/nall/snes/cpu.hpp new file mode 100644 index 0000000..28f5ddb --- /dev/null +++ b/libsnes/nall/snes/cpu.hpp @@ -0,0 +1,458 @@ +#ifndef NALL_SNES_CPU_HPP +#define NALL_SNES_CPU_HPP + +namespace nall { + +struct SNESCPU { + enum : unsigned { + Implied, // + Constant, //#$00 + AccumConstant, //#$00 + IndexConstant, //#$00 + Direct, //$00 + DirectX, //$00,x + DirectY, //$00,y + IDirect, //($00) + IDirectX, //($00,x) + IDirectY, //($00),y + ILDirect, //[$00] + ILDirectY, //[$00],y + Address, //$0000 + AddressX, //$0000,x + AddressY, //$0000,y + IAddressX, //($0000,x) + ILAddress, //[$0000] + PAddress, //PBR:$0000 + PIAddress, //PBR:($0000) + Long, //$000000 + LongX, //$000000,x + Stack, //$00,s + IStackY, //($00,s),y + BlockMove, //$00,$00 + RelativeShort, //+/- $00 + RelativeLong, //+/- $0000 + }; + + struct OpcodeInfo { + char name[4]; + unsigned mode; + }; + + static const OpcodeInfo opcodeInfo[256]; + + static unsigned getOpcodeLength(bool accum, bool index, uint8_t opcode); + static string disassemble(unsigned pc, bool accum, bool index, uint8_t opcode, uint8_t pl, uint8_t ph, uint8_t pb); +}; + +const SNESCPU::OpcodeInfo SNESCPU::opcodeInfo[256] = { + //0x00 - 0x0f + { "brk", Constant }, + { "ora", IDirectX }, + { "cop", Constant }, + { "ora", Stack }, + + { "tsb", Direct }, + { "ora", Direct }, + { "asl", Direct }, + { "ora", ILDirect }, + + { "php", Implied }, + { "ora", AccumConstant }, + { "asl", Implied }, + { "phd", Implied }, + + { "tsb", Address }, + { "ora", Address }, + { "asl", Address }, + { "ora", Long }, + + //0x10 - 0x1f + { "bpl", RelativeShort }, + { "ora", IDirectY }, + { "ora", IDirect }, + { "ora", IStackY }, + + { "trb", Direct }, + { "ora", DirectX }, + { "asl", DirectX }, + { "ora", ILDirectY }, + + { "clc", Implied }, + { "ora", AddressY }, + { "inc", Implied }, + { "tcs", Implied }, + + { "trb", Address }, + { "ora", AddressX }, + { "asl", AddressX }, + { "ora", LongX }, + + //0x20 - 0x2f + { "jsr", Address }, + { "and", IDirectX }, + { "jsl", Long }, + { "and", Stack }, + + { "bit", Direct }, + { "and", Direct }, + { "rol", Direct }, + { "and", ILDirect }, + + { "plp", Implied }, + { "and", AccumConstant }, + { "rol", Implied }, + { "pld", Implied }, + + { "bit", Address }, + { "and", Address }, + { "rol", Address }, + { "and", Long }, + + //0x30 - 0x3f + { "bmi", RelativeShort }, + { "and", IDirectY }, + { "and", IDirect }, + { "and", IStackY }, + + { "bit", DirectX }, + { "and", DirectX }, + { "rol", DirectX }, + { "and", ILDirectY }, + + { "sec", Implied }, + { "and", AddressY }, + { "dec", Implied }, + { "tsc", Implied }, + + { "bit", AddressX }, + { "and", AddressX }, + { "rol", AddressX }, + { "and", LongX }, + + //0x40 - 0x4f + { "rti", Implied }, + { "eor", IDirectX }, + { "wdm", Constant }, + { "eor", Stack }, + + { "mvp", BlockMove }, + { "eor", Direct }, + { "lsr", Direct }, + { "eor", ILDirect }, + + { "pha", Implied }, + { "eor", AccumConstant }, + { "lsr", Implied }, + { "phk", Implied }, + + { "jmp", PAddress }, + { "eor", Address }, + { "lsr", Address }, + { "eor", Long }, + + //0x50 - 0x5f + { "bvc", RelativeShort }, + { "eor", IDirectY }, + { "eor", IDirect }, + { "eor", IStackY }, + + { "mvn", BlockMove }, + { "eor", DirectX }, + { "lsr", DirectX }, + { "eor", ILDirectY }, + + { "cli", Implied }, + { "eor", AddressY }, + { "phy", Implied }, + { "tcd", Implied }, + + { "jml", Long }, + { "eor", AddressX }, + { "lsr", AddressX }, + { "eor", LongX }, + + //0x60 - 0x6f + { "rts", Implied }, + { "adc", IDirectX }, + { "per", Address }, + { "adc", Stack }, + + { "stz", Direct }, + { "adc", Direct }, + { "ror", Direct }, + { "adc", ILDirect }, + + { "pla", Implied }, + { "adc", AccumConstant }, + { "ror", Implied }, + { "rtl", Implied }, + + { "jmp", PIAddress }, + { "adc", Address }, + { "ror", Address }, + { "adc", Long }, + + //0x70 - 0x7f + { "bvs", RelativeShort }, + { "adc", IDirectY }, + { "adc", IDirect }, + { "adc", IStackY }, + + { "stz", DirectX }, + { "adc", DirectX }, + { "ror", DirectX }, + { "adc", ILDirectY }, + + { "sei", Implied }, + { "adc", AddressY }, + { "ply", Implied }, + { "tdc", Implied }, + + { "jmp", IAddressX }, + { "adc", AddressX }, + { "ror", AddressX }, + { "adc", LongX }, + + //0x80 - 0x8f + { "bra", RelativeShort }, + { "sta", IDirectX }, + { "brl", RelativeLong }, + { "sta", Stack }, + + { "sty", Direct }, + { "sta", Direct }, + { "stx", Direct }, + { "sta", ILDirect }, + + { "dey", Implied }, + { "bit", AccumConstant }, + { "txa", Implied }, + { "phb", Implied }, + + { "sty", Address }, + { "sta", Address }, + { "stx", Address }, + { "sta", Long }, + + //0x90 - 0x9f + { "bcc", RelativeShort }, + { "sta", IDirectY }, + { "sta", IDirect }, + { "sta", IStackY }, + + { "sty", DirectX }, + { "sta", DirectX }, + { "stx", DirectY }, + { "sta", ILDirectY }, + + { "tya", Implied }, + { "sta", AddressY }, + { "txs", Implied }, + { "txy", Implied }, + + { "stz", Address }, + { "sta", AddressX }, + { "stz", AddressX }, + { "sta", LongX }, + + //0xa0 - 0xaf + { "ldy", IndexConstant }, + { "lda", IDirectX }, + { "ldx", IndexConstant }, + { "lda", Stack }, + + { "ldy", Direct }, + { "lda", Direct }, + { "ldx", Direct }, + { "lda", ILDirect }, + + { "tay", Implied }, + { "lda", AccumConstant }, + { "tax", Implied }, + { "plb", Implied }, + + { "ldy", Address }, + { "lda", Address }, + { "ldx", Address }, + { "lda", Long }, + + //0xb0 - 0xbf + { "bcs", RelativeShort }, + { "lda", IDirectY }, + { "lda", IDirect }, + { "lda", IStackY }, + + { "ldy", DirectX }, + { "lda", DirectX }, + { "ldx", DirectY }, + { "lda", ILDirectY }, + + { "clv", Implied }, + { "lda", AddressY }, + { "tsx", Implied }, + { "tyx", Implied }, + + { "ldy", AddressX }, + { "lda", AddressX }, + { "ldx", AddressY }, + { "lda", LongX }, + + //0xc0 - 0xcf + { "cpy", IndexConstant }, + { "cmp", IDirectX }, + { "rep", Constant }, + { "cmp", Stack }, + + { "cpy", Direct }, + { "cmp", Direct }, + { "dec", Direct }, + { "cmp", ILDirect }, + + { "iny", Implied }, + { "cmp", AccumConstant }, + { "dex", Implied }, + { "wai", Implied }, + + { "cpy", Address }, + { "cmp", Address }, + { "dec", Address }, + { "cmp", Long }, + + //0xd0 - 0xdf + { "bne", RelativeShort }, + { "cmp", IDirectY }, + { "cmp", IDirect }, + { "cmp", IStackY }, + + { "pei", IDirect }, + { "cmp", DirectX }, + { "dec", DirectX }, + { "cmp", ILDirectY }, + + { "cld", Implied }, + { "cmp", AddressY }, + { "phx", Implied }, + { "stp", Implied }, + + { "jmp", ILAddress }, + { "cmp", AddressX }, + { "dec", AddressX }, + { "cmp", LongX }, + + //0xe0 - 0xef + { "cpx", IndexConstant }, + { "sbc", IDirectX }, + { "sep", Constant }, + { "sbc", Stack }, + + { "cpx", Direct }, + { "sbc", Direct }, + { "inc", Direct }, + { "sbc", ILDirect }, + + { "inx", Implied }, + { "sbc", AccumConstant }, + { "nop", Implied }, + { "xba", Implied }, + + { "cpx", Address }, + { "sbc", Address }, + { "inc", Address }, + { "sbc", Long }, + + //0xf0 - 0xff + { "beq", RelativeShort }, + { "sbc", IDirectY }, + { "sbc", IDirect }, + { "sbc", IStackY }, + + { "pea", Address }, + { "sbc", DirectX }, + { "inc", DirectX }, + { "sbc", ILDirectY }, + + { "sed", Implied }, + { "sbc", AddressY }, + { "plx", Implied }, + { "xce", Implied }, + + { "jsr", IAddressX }, + { "sbc", AddressX }, + { "inc", AddressX }, + { "sbc", LongX }, +}; + +inline unsigned SNESCPU::getOpcodeLength(bool accum, bool index, uint8_t opcode) { + switch(opcodeInfo[opcode].mode) { default: + case Implied: return 1; + case Constant: return 2; + case AccumConstant: return 3 - accum; + case IndexConstant: return 3 - index; + case Direct: return 2; + case DirectX: return 2; + case DirectY: return 2; + case IDirect: return 2; + case IDirectX: return 2; + case IDirectY: return 2; + case ILDirect: return 2; + case ILDirectY: return 2; + case Address: return 3; + case AddressX: return 3; + case AddressY: return 3; + case IAddressX: return 3; + case ILAddress: return 3; + case PAddress: return 3; + case PIAddress: return 3; + case Long: return 4; + case LongX: return 4; + case Stack: return 2; + case IStackY: return 2; + case BlockMove: return 3; + case RelativeShort: return 2; + case RelativeLong: return 3; + } +} + +inline string SNESCPU::disassemble(unsigned pc, bool accum, bool index, uint8_t opcode, uint8_t pl, uint8_t ph, uint8_t pb) { + string name = opcodeInfo[opcode].name; + unsigned mode = opcodeInfo[opcode].mode; + + if(mode == Implied) return name; + if(mode == Constant) return { name, " #$", hex<2>(pl) }; + if(mode == AccumConstant) return { name, " #$", accum ? "" : hex<2>(ph), hex<2>(pl) }; + if(mode == IndexConstant) return { name, " #$", index ? "" : hex<2>(ph), hex<2>(pl) }; + if(mode == Direct) return { name, " $", hex<2>(pl) }; + if(mode == DirectX) return { name, " $", hex<2>(pl), ",x" }; + if(mode == DirectY) return { name, " $", hex<2>(pl), ",y" }; + if(mode == IDirect) return { name, " ($", hex<2>(pl), ")" }; + if(mode == IDirectX) return { name, " ($", hex<2>(pl), ",x)" }; + if(mode == IDirectY) return { name, " ($", hex<2>(pl), "),y" }; + if(mode == ILDirect) return { name, " [$", hex<2>(pl), "]" }; + if(mode == ILDirectY) return { name, " [$", hex<2>(pl), "],y" }; + if(mode == Address) return { name, " $", hex<2>(ph), hex<2>(pl) }; + if(mode == AddressX) return { name, " $", hex<2>(ph), hex<2>(pl), ",x" }; + if(mode == AddressY) return { name, " $", hex<2>(ph), hex<2>(pl), ",y" }; + if(mode == IAddressX) return { name, " ($", hex<2>(ph), hex<2>(pl), ",x)" }; + if(mode == ILAddress) return { name, " [$", hex<2>(ph), hex<2>(pl), "]" }; + if(mode == PAddress) return { name, " $", hex<2>(ph), hex<2>(pl) }; + if(mode == PIAddress) return { name, " ($", hex<2>(ph), hex<2>(pl), ")" }; + if(mode == Long) return { name, " $", hex<2>(pb), hex<2>(ph), hex<2>(pl) }; + if(mode == LongX) return { name, " $", hex<2>(pb), hex<2>(ph), hex<2>(pl), ",x" }; + if(mode == Stack) return { name, " $", hex<2>(pl), ",s" }; + if(mode == IStackY) return { name, " ($", hex<2>(pl), ",s),y" }; + if(mode == BlockMove) return { name, " $", hex<2>(ph), ",$", hex<2>(pl) }; + if(mode == RelativeShort) { + unsigned addr = (pc + 2) + (int8_t)(pl << 0); + return { name, " $", hex<4>(addr) }; + } + if(mode == RelativeLong) { + unsigned addr = (pc + 3) + (int16_t)((ph << 8) + (pl << 0)); + return { name, " $", hex<4>(addr) }; + } + + return ""; +} + +} + +#endif diff --git a/libsnes/nall/snes/smp.hpp b/libsnes/nall/snes/smp.hpp new file mode 100644 index 0000000..7a1ac47 --- /dev/null +++ b/libsnes/nall/snes/smp.hpp @@ -0,0 +1,639 @@ +#ifndef NALL_SNES_SMP_HPP +#define NALL_SNES_SMP_HPP + +namespace nall { + +struct SNESSMP { + enum : unsigned { + Implied, // + TVector, //0 + Direct, //$00 + DirectRelative, //$00,+/-$00 + ADirect, //a,$00 + AAbsolute, //a,$0000 + AIX, //a,(x) + AIDirectX, //a,($00+x) + AConstant, //a,#$00 + DirectDirect, //$00,$00 + CAbsoluteBit, //c,$0000:0 + Absolute, //$0000 + P, //p + AbsoluteA, //$0000,a + Relative, //+/-$00 + ADirectX, //a,$00+x + AAbsoluteX, //a,$0000+x + AAbsoluteY, //a,$0000+y + AIDirectY, //a,($00)+y + DirectConstant, //$00,#$00 + IXIY, //(x),(y) + DirectX, //$00+x + A, //a + X, //x + XAbsolute, //x,$0000 + IAbsoluteX, //($0000+x) + CNAbsoluteBit, //c,!$0000:0 + XDirect, //x,$00 + PVector, //$ff00 + YaDirect, //ya,$00 + XA, //x,a + YAbsolute, //y,$0000 + Y, //y + AX, //a,x + YDirect, //y,$00 + YConstant, //y,#$00 + XSp, //x,sp + YaX, //ya,x + IXPA, //(x)+,a + SpX, //sp,x + AIXP, //a,(x)+ + DirectA, //$00,a + IXA, //(x),a + IDirectXA, //($00+x),a + XConstant, //x,#$00 + AbsoluteX, //$0000,x + AbsoluteBitC, //$0000:0,c + DirectY, //$00,y + AbsoluteY, //$0000,y + Ya, //ya + DirectXA, //$00+x,a + AbsoluteXA, //$0000+x,a + AbsoluteYA, //$0000+y,a + IDirectYA, //($00)+y,a + DirectYX, //$00+y,x + DirectYa, //$00,ya + DirectXY, //$00+x,y + AY, //a,y + DirectXRelative, //$00+x,+/-$00 + XDirectY, //x,$00+y + YDirectX, //y,$00+x + YA, //y,a + YRelative, //y,+/-$00 + }; + + struct OpcodeInfo { + char name[6]; + unsigned mode; + }; + + static const OpcodeInfo opcodeInfo[256]; + + static unsigned getOpcodeLength(uint8_t opcode); + static string disassemble(uint16_t pc, uint8_t opcode, uint8_t pl, uint8_t ph); + static string disassemble(uint16_t pc, bool p, uint8_t opcode, uint8_t pl, uint8_t ph); +}; + +const SNESSMP::OpcodeInfo SNESSMP::opcodeInfo[256] = { + //0x00 - 0x0f + { "nop ", Implied }, + { "tcall", TVector }, + { "set0 ", Direct }, + { "bbs0 ", DirectRelative }, + + { "or ", ADirect }, + { "or ", AAbsolute }, + { "or ", AIX }, + { "or ", AIDirectX }, + + { "or ", AConstant }, + { "or ", DirectDirect }, + { "or1 ", CAbsoluteBit }, + { "asl ", Direct }, + + { "asl ", Absolute }, + { "push ", P }, + { "tset ", AbsoluteA }, + { "brk ", Implied }, + + //0x10 - 0x1f + { "bpl ", Relative }, + { "tcall", TVector }, + { "clr0 ", Direct }, + { "bbc0 ", DirectRelative }, + + { "or ", ADirectX }, + { "or ", AAbsoluteX }, + { "or ", AAbsoluteY }, + { "or ", AIDirectY }, + + { "or ", DirectConstant }, + { "or ", IXIY }, + { "decw ", Direct }, + { "asl ", DirectX }, + + { "asl ", A }, + { "dec ", X }, + { "cmp ", XAbsolute }, + { "jmp ", IAbsoluteX }, + + //0x20 - 0x2f + { "clrp ", Implied }, + { "tcall", TVector }, + { "set1 ", Direct }, + { "bbs1 ", DirectRelative }, + + { "and ", ADirect }, + { "and ", AAbsolute }, + { "and ", AIX }, + { "and ", AIDirectX }, + + { "and ", AConstant }, + { "and ", DirectDirect }, + { "or1 ", CNAbsoluteBit }, + { "rol ", Direct }, + + { "rol ", Absolute }, + { "push ", A }, + { "cbne ", DirectRelative }, + { "bra ", Relative }, + + //0x30 - 0x3f + { "bmi ", Relative }, + { "tcall", TVector }, + { "clr1 ", Direct }, + { "bbc1 ", DirectRelative }, + + { "and ", ADirectX }, + { "and ", AAbsoluteX }, + { "and ", AAbsoluteY }, + { "and ", AIDirectY }, + + { "and ", DirectConstant }, + { "and ", IXIY }, + { "incw ", Direct }, + { "rol ", DirectX }, + + { "rol ", A }, + { "inc ", X }, + { "cmp ", XDirect }, + { "call ", Absolute }, + + //0x40 - 0x4f + { "setp ", Implied }, + { "tcall", TVector }, + { "set2 ", Direct }, + { "bbs2 ", DirectRelative }, + + { "eor ", ADirect }, + { "eor ", AAbsolute }, + { "eor ", AIX }, + { "eor ", AIDirectX }, + + { "eor ", AConstant }, + { "eor ", DirectDirect }, + { "and1 ", CAbsoluteBit }, + { "lsr ", Direct }, + + { "lsr ", Absolute }, + { "push ", X }, + { "tclr ", AbsoluteA }, + { "pcall", PVector }, + + //0x50 - 0x5f + { "bvc ", Relative }, + { "tcall", TVector }, + { "clr2 ", Direct }, + { "bbc2 ", DirectRelative }, + + { "eor ", ADirectX }, + { "eor ", AAbsoluteX }, + { "eor ", AAbsoluteY }, + { "eor ", AIDirectY }, + + { "eor ", DirectConstant }, + { "eor ", IXIY }, + { "cmpw ", YaDirect }, + { "lsr ", DirectX }, + + { "lsr ", A }, + { "mov ", XA }, + { "cmp ", YAbsolute }, + { "jmp ", Absolute }, + + //0x60 - 0x6f + { "clrc ", Implied }, + { "tcall", TVector }, + { "set3 ", Direct }, + { "bbs3 ", DirectRelative }, + + { "cmp ", ADirect }, + { "cmp ", AAbsolute }, + { "cmp ", AIX }, + { "cmp ", AIDirectX }, + + { "cmp ", AConstant }, + { "cmp ", DirectDirect }, + { "and1 ", CNAbsoluteBit }, + { "ror ", Direct }, + + { "ror ", Absolute }, + { "push ", Y }, + { "dbnz ", DirectRelative }, + { "ret ", Implied }, + + //0x70 - 0x7f + { "bvs ", Relative }, + { "tcall", TVector }, + { "clr3 ", Direct }, + { "bbc3 ", DirectRelative }, + + { "cmp ", ADirectX }, + { "cmp ", AAbsoluteX }, + { "cmp ", AAbsoluteY }, + { "cmp ", AIDirectY }, + + { "cmp ", DirectConstant }, + { "cmp ", IXIY }, + { "addw ", YaDirect }, + { "ror ", DirectX }, + + { "ror ", A }, + { "mov ", AX }, + { "cmp ", YDirect }, + { "reti ", Implied }, + + //0x80 - 0x8f + { "setc ", Implied }, + { "tcall", TVector }, + { "set4 ", Direct }, + { "bbs4 ", DirectRelative }, + + { "adc ", ADirect }, + { "adc ", AAbsolute }, + { "adc ", AIX }, + { "adc ", AIDirectX }, + + { "adc ", AConstant }, + { "adc ", DirectDirect }, + { "eor1 ", CAbsoluteBit }, + { "dec ", Direct }, + + { "dec ", Absolute }, + { "mov ", YConstant }, + { "pop ", P }, + { "mov ", DirectConstant }, + + //0x90 - 0x9f + { "bcc ", Relative }, + { "tcall", TVector }, + { "clr4 ", Direct }, + { "bbc4 ", DirectRelative }, + + { "adc ", ADirectX }, + { "adc ", AAbsoluteX }, + { "adc ", AAbsoluteY }, + { "adc ", AIDirectY }, + + { "adc ", DirectRelative }, + { "adc ", IXIY }, + { "subw ", YaDirect }, + { "dec ", DirectX }, + + { "dec ", A }, + { "mov ", XSp }, + { "div ", YaX }, + { "xcn ", A }, + + //0xa0 - 0xaf + { "ei ", Implied }, + { "tcall", TVector }, + { "set5 ", Direct }, + { "bbs5 ", DirectRelative }, + + { "sbc ", ADirect }, + { "sbc ", AAbsolute }, + { "sbc ", AIX }, + { "sbc ", AIDirectX }, + + { "sbc ", AConstant }, + { "sbc ", DirectDirect }, + { "mov1 ", CAbsoluteBit }, + { "inc ", Direct }, + + { "inc ", Absolute }, + { "cmp ", YConstant }, + { "pop ", A }, + { "mov ", IXPA }, + + //0xb0 - 0xbf + { "bcs ", Relative }, + { "tcall", TVector }, + { "clr5 ", Direct }, + { "bbc5 ", DirectRelative }, + + { "sbc ", ADirectX }, + { "sbc ", AAbsoluteX }, + { "sbc ", AAbsoluteY }, + { "sbc ", AIDirectY }, + + { "sbc ", DirectConstant }, + { "sbc ", IXIY }, + { "movw ", YaDirect }, + { "inc ", DirectX }, + + { "inc ", A }, + { "mov ", SpX }, + { "das ", A }, + { "mov ", AIXP }, + + //0xc0 - 0xcf + { "di ", Implied }, + { "tcall", TVector }, + { "set6 ", Direct }, + { "bbs6 ", DirectRelative }, + + { "mov ", DirectA }, + { "mov ", AbsoluteA }, + { "mov ", IXA }, + { "mov ", IDirectXA }, + + { "cmp ", XConstant }, + { "mov ", AbsoluteX }, + { "mov1 ", AbsoluteBitC }, + { "mov ", DirectY }, + + { "mov ", AbsoluteY }, + { "mov ", XConstant }, + { "pop ", X }, + { "mul ", Ya }, + + //0xd0 - 0xdf + { "bne ", Relative }, + { "tcall", TVector }, + { "clr6 ", Relative }, + { "bbc6 ", DirectRelative }, + + { "mov ", DirectXA }, + { "mov ", AbsoluteXA }, + { "mov ", AbsoluteYA }, + { "mov ", IDirectYA }, + + { "mov ", DirectX }, + { "mov ", DirectYX }, + { "movw ", DirectYa }, + { "mov ", DirectXY }, + + { "dec ", Y }, + { "mov ", AY }, + { "cbne ", DirectXRelative }, + { "daa ", A }, + + //0xe0 - 0xef + { "clrv ", Implied }, + { "tcall", TVector }, + { "set7 ", Direct }, + { "bbs7 ", DirectRelative }, + + { "mov ", ADirect }, + { "mov ", AAbsolute }, + { "mov ", AIX }, + { "mov ", AIDirectX }, + + { "mov ", AConstant }, + { "mov ", XAbsolute }, + { "not1 ", CAbsoluteBit }, + { "mov ", YDirect }, + + { "mov ", YAbsolute }, + { "notc ", Implied }, + { "pop ", Y }, + { "sleep", Implied }, + + //0xf0 - 0xff + { "beq ", Relative }, + { "tcall", TVector }, + { "clr7 ", Direct }, + { "bbc7 ", DirectRelative }, + + { "mov ", ADirectX }, + { "mov ", AAbsoluteX }, + { "mov ", AAbsoluteY }, + { "mov ", AIDirectY }, + + { "mov ", XDirect }, + { "mov ", XDirectY }, + { "mov ", DirectDirect }, + { "mov ", YDirectX }, + + { "inc ", Y }, + { "mov ", YA }, + { "dbz ", YRelative }, + { "stop ", Implied }, +}; + +inline unsigned SNESSMP::getOpcodeLength(uint8_t opcode) { + switch(opcodeInfo[opcode].mode) { default: + case Implied: return 1; // + case TVector: return 1; //0 + case Direct: return 2; //$00 + case DirectRelative: return 3; //$00,+/-$00 + case ADirect: return 2; //a,$00 + case AAbsolute: return 3; //a,$0000 + case AIX: return 1; //a,(x) + case AIDirectX: return 2; //a,($00+x) + case AConstant: return 2; //a,#$00 + case DirectDirect: return 3; //$00,$00 + case CAbsoluteBit: return 3; //c,$0000:0 + case Absolute: return 3; //$0000 + case P: return 1; //p + case AbsoluteA: return 3; //$0000,a + case Relative: return 2; //+/-$00 + case ADirectX: return 2; //a,$00+x + case AAbsoluteX: return 3; //a,$0000+x + case AAbsoluteY: return 3; //a,$0000+y + case AIDirectY: return 2; //a,($00)+y + case DirectConstant: return 3; //$00,#$00 + case IXIY: return 1; //(x),(y) + case DirectX: return 2; //$00+x + case A: return 1; //a + case X: return 1; //x + case XAbsolute: return 3; //x,$0000 + case IAbsoluteX: return 3; //($0000+x) + case CNAbsoluteBit: return 3; //c,!$0000:0 + case XDirect: return 2; //x,$00 + case PVector: return 2; //$ff00 + case YaDirect: return 2; //ya,$00 + case XA: return 1; //x,a + case YAbsolute: return 3; //y,$0000 + case Y: return 1; //y + case AX: return 1; //a,x + case YDirect: return 2; //y,$00 + case YConstant: return 2; //y,#$00 + case XSp: return 1; //x,sp + case YaX: return 1; //ya,x + case IXPA: return 1; //(x)+,a + case SpX: return 1; //sp,x + case AIXP: return 1; //a,(x)+ + case DirectA: return 2; //$00,a + case IXA: return 1; //(x),a + case IDirectXA: return 2; //($00+x),a + case XConstant: return 2; //x,#$00 + case AbsoluteX: return 3; //$0000,x + case AbsoluteBitC: return 3; //$0000:0,c + case DirectY: return 2; //$00,y + case AbsoluteY: return 3; //$0000,y + case Ya: return 1; //ya + case DirectXA: return 2; //$00+x,a + case AbsoluteXA: return 3; //$0000+x,a + case AbsoluteYA: return 3; //$0000+y,a + case IDirectYA: return 2; //($00)+y,a + case DirectYX: return 2; //$00+y,x + case DirectYa: return 2; //$00,ya + case DirectXY: return 2; //$00+x,y + case AY: return 1; //a,y + case DirectXRelative: return 3; //$00+x,+/-$00 + case XDirectY: return 2; //x,$00+y + case YDirectX: return 2; //y,$00+x + case YA: return 1; //y,a + case YRelative: return 2; //y,+/-$00 + } +} + +inline string SNESSMP::disassemble(uint16_t pc, uint8_t opcode, uint8_t pl, uint8_t ph) { + string name = opcodeInfo[opcode].name; + unsigned mode = opcodeInfo[opcode].mode; + unsigned pa = (ph << 8) + pl; + + if(mode == Implied) return name; + if(mode == TVector) return { name, " ", opcode >> 4 }; + if(mode == Direct) return { name, " $", hex<2>(pl) }; + if(mode == DirectRelative) return { name, " $", hex<2>(pl), ",$", hex<4>(pc + 3 + (int8_t)ph) }; + if(mode == ADirect) return { name, " a,$", hex<2>(pl) }; + if(mode == AAbsolute) return { name, " a,$", hex<4>(pa) }; + if(mode == AIX) return { name, "a,(x)" }; + if(mode == AIDirectX) return { name, " a,($", hex<2>(pl), "+x)" }; + if(mode == AConstant) return { name, " a,#$", hex<2>(pl) }; + if(mode == DirectDirect) return { name, " $", hex<2>(ph), ",$", hex<2>(pl) }; + if(mode == CAbsoluteBit) return { name, " c,$", hex<4>(pa & 0x1fff), ":", pa >> 13 }; + if(mode == Absolute) return { name, " $", hex<4>(pa) }; + if(mode == P) return { name, " p" }; + if(mode == AbsoluteA) return { name, " $", hex<4>(pa), ",a" }; + if(mode == Relative) return { name, " $", hex<4>(pc + 2 + (int8_t)pl) }; + if(mode == ADirectX) return { name, " a,$", hex<2>(pl), "+x" }; + if(mode == AAbsoluteX) return { name, " a,$", hex<4>(pa), "+x" }; + if(mode == AAbsoluteY) return { name, " a,$", hex<4>(pa), "+y" }; + if(mode == AIDirectY) return { name, " a,($", hex<2>(pl), ")+y" }; + if(mode == DirectConstant) return { name, " $", hex<2>(ph), ",#$", hex<2>(pl) }; + if(mode == IXIY) return { name, " (x),(y)" }; + if(mode == DirectX) return { name, " $", hex<2>(pl), "+x" }; + if(mode == A) return { name, " a" }; + if(mode == X) return { name, " x" }; + if(mode == XAbsolute) return { name, " x,$", hex<4>(pa) }; + if(mode == IAbsoluteX) return { name, " ($", hex<4>(pa), "+x)" }; + if(mode == CNAbsoluteBit) return { name, " c,!$", hex<4>(pa & 0x1fff), ":", pa >> 13 }; + if(mode == XDirect) return { name, " x,$", hex<2>(pl) }; + if(mode == PVector) return { name, " $ff", hex<2>(pl) }; + if(mode == YaDirect) return { name, " ya,$", hex<2>(pl) }; + if(mode == XA) return { name, " x,a" }; + if(mode == YAbsolute) return { name, " y,$", hex<4>(pa) }; + if(mode == Y) return { name, " y" }; + if(mode == AX) return { name, " a,x" }; + if(mode == YDirect) return { name, " y,$", hex<2>(pl) }; + if(mode == YConstant) return { name, " y,#$", hex<2>(pl) }; + if(mode == XSp) return { name, " x,sp" }; + if(mode == YaX) return { name, " ya,x" }; + if(mode == IXPA) return { name, " (x)+,a" }; + if(mode == SpX) return { name, " sp,x" }; + if(mode == AIXP) return { name, " a,(x)+" }; + if(mode == DirectA) return { name, " $", hex<2>(pl), ",a" }; + if(mode == IXA) return { name, " (x),a" }; + if(mode == IDirectXA) return { name, " ($", hex<2>(pl), "+x),a" }; + if(mode == XConstant) return { name, " x,#$", hex<2>(pl) }; + if(mode == AbsoluteX) return { name, " $", hex<4>(pa), ",x" }; + if(mode == AbsoluteBitC) return { name, " $", hex<4>(pa & 0x1fff), ":", pa >> 13, ",c" }; + if(mode == DirectY) return { name, " $", hex<2>(pl), ",y" }; + if(mode == AbsoluteY) return { name, " $", hex<4>(pa), ",y" }; + if(mode == Ya) return { name, " ya" }; + if(mode == DirectXA) return { name, " $", hex<2>(pl), "+x,a" }; + if(mode == AbsoluteXA) return { name, " $", hex<4>(pa), "+x,a" }; + if(mode == AbsoluteYA) return { name, " $", hex<4>(pa), "+y,a" }; + if(mode == IDirectYA) return { name, " ($", hex<2>(pl), ")+y,a" }; + if(mode == DirectYX) return { name, " $", hex<2>(pl), "+y,x" }; + if(mode == DirectYa) return { name, " $", hex<2>(pl), ",ya" }; + if(mode == DirectXY) return { name, " $", hex<2>(pl), "+x,y" }; + if(mode == AY) return { name, " a,y" }; + if(mode == DirectXRelative) return { name, " $", hex<2>(pl), ",$", hex<4>(pc + 3 + (int8_t)ph) }; + if(mode == XDirectY) return { name, " x,$", hex<2>(pl), "+y" }; + if(mode == YDirectX) return { name, " y,$", hex<2>(pl), "+x" }; + if(mode == YA) return { name, " y,a" }; + if(mode == YRelative) return { name, " y,$", hex<4>(pc + 2 + (int8_t)pl) }; + + return ""; +} + +inline string SNESSMP::disassemble(uint16_t pc, bool p, uint8_t opcode, uint8_t pl, uint8_t ph) { + string name = opcodeInfo[opcode].name; + unsigned mode = opcodeInfo[opcode].mode; + unsigned pdl = (p << 8) + pl; + unsigned pdh = (p << 8) + ph; + unsigned pa = (ph << 8) + pl; + + if(mode == Implied) return name; + if(mode == TVector) return { name, " ", opcode >> 4 }; + if(mode == Direct) return { name, " $", hex<3>(pdl) }; + if(mode == DirectRelative) return { name, " $", hex<3>(pdl), ",$", hex<4>(pc + 3 + (int8_t)ph) }; + if(mode == ADirect) return { name, " a,$", hex<3>(pdl) }; + if(mode == AAbsolute) return { name, " a,$", hex<4>(pa) }; + if(mode == AIX) return { name, "a,(x)" }; + if(mode == AIDirectX) return { name, " a,($", hex<3>(pdl), "+x)" }; + if(mode == AConstant) return { name, " a,#$", hex<2>(pl) }; + if(mode == DirectDirect) return { name, " $", hex<3>(pdh), ",$", hex<3>(pdl) }; + if(mode == CAbsoluteBit) return { name, " c,$", hex<4>(pa & 0x1fff), ":", pa >> 13 }; + if(mode == Absolute) return { name, " $", hex<4>(pa) }; + if(mode == P) return { name, " p" }; + if(mode == AbsoluteA) return { name, " $", hex<4>(pa), ",a" }; + if(mode == Relative) return { name, " $", hex<4>(pc + 2 + (int8_t)pl) }; + if(mode == ADirectX) return { name, " a,$", hex<3>(pdl), "+x" }; + if(mode == AAbsoluteX) return { name, " a,$", hex<4>(pa), "+x" }; + if(mode == AAbsoluteY) return { name, " a,$", hex<4>(pa), "+y" }; + if(mode == AIDirectY) return { name, " a,($", hex<3>(pdl), ")+y" }; + if(mode == DirectConstant) return { name, " $", hex<3>(pdh), ",#$", hex<2>(pl) }; + if(mode == IXIY) return { name, " (x),(y)" }; + if(mode == DirectX) return { name, " $", hex<3>(pdl), "+x" }; + if(mode == A) return { name, " a" }; + if(mode == X) return { name, " x" }; + if(mode == XAbsolute) return { name, " x,$", hex<4>(pa) }; + if(mode == IAbsoluteX) return { name, " ($", hex<4>(pa), "+x)" }; + if(mode == CNAbsoluteBit) return { name, " c,!$", hex<4>(pa & 0x1fff), ":", pa >> 13 }; + if(mode == XDirect) return { name, " x,$", hex<3>(pdl) }; + if(mode == PVector) return { name, " $ff", hex<2>(pl) }; + if(mode == YaDirect) return { name, " ya,$", hex<3>(pdl) }; + if(mode == XA) return { name, " x,a" }; + if(mode == YAbsolute) return { name, " y,$", hex<4>(pa) }; + if(mode == Y) return { name, " y" }; + if(mode == AX) return { name, " a,x" }; + if(mode == YDirect) return { name, " y,$", hex<3>(pdl) }; + if(mode == YConstant) return { name, " y,#$", hex<2>(pl) }; + if(mode == XSp) return { name, " x,sp" }; + if(mode == YaX) return { name, " ya,x" }; + if(mode == IXPA) return { name, " (x)+,a" }; + if(mode == SpX) return { name, " sp,x" }; + if(mode == AIXP) return { name, " a,(x)+" }; + if(mode == DirectA) return { name, " $", hex<3>(pdl), ",a" }; + if(mode == IXA) return { name, " (x),a" }; + if(mode == IDirectXA) return { name, " ($", hex<3>(pdl), "+x),a" }; + if(mode == XConstant) return { name, " x,#$", hex<2>(pl) }; + if(mode == AbsoluteX) return { name, " $", hex<4>(pa), ",x" }; + if(mode == AbsoluteBitC) return { name, " $", hex<4>(pa & 0x1fff), ":", pa >> 13, ",c" }; + if(mode == DirectY) return { name, " $", hex<3>(pdl), ",y" }; + if(mode == AbsoluteY) return { name, " $", hex<4>(pa), ",y" }; + if(mode == Ya) return { name, " ya" }; + if(mode == DirectXA) return { name, " $", hex<3>(pdl), "+x,a" }; + if(mode == AbsoluteXA) return { name, " $", hex<4>(pa), "+x,a" }; + if(mode == AbsoluteYA) return { name, " $", hex<4>(pa), "+y,a" }; + if(mode == IDirectYA) return { name, " ($", hex<3>(pdl), ")+y,a" }; + if(mode == DirectYX) return { name, " $", hex<3>(pdl), "+y,x" }; + if(mode == DirectYa) return { name, " $", hex<3>(pdl), ",ya" }; + if(mode == DirectXY) return { name, " $", hex<3>(pdl), "+x,y" }; + if(mode == AY) return { name, " a,y" }; + if(mode == DirectXRelative) return { name, " $", hex<3>(pdl), ",$", hex<4>(pc + 3 + (int8_t)ph) }; + if(mode == XDirectY) return { name, " x,$", hex<3>(pdl), "+y" }; + if(mode == YDirectX) return { name, " y,$", hex<3>(pdl), "+x" }; + if(mode == YA) return { name, " y,a" }; + if(mode == YRelative) return { name, " y,$", hex<4>(pc + 2 + (int8_t)pl) }; + + return ""; +} + +} + +#endif diff --git a/libsnes/nall/sort.hpp b/libsnes/nall/sort.hpp new file mode 100644 index 0000000..23c317a --- /dev/null +++ b/libsnes/nall/sort.hpp @@ -0,0 +1,62 @@ +#ifndef NALL_SORT_HPP +#define NALL_SORT_HPP + +#include + +//class: merge sort +//average: O(n log n) +//worst: O(n log n) +//memory: O(n) +//stack: O(log n) +//stable?: yes + +//notes: +//there are two primary reasons for choosing merge sort +//over the (usually) faster quick sort*: +//1: it is a stable sort. +//2: it lacks O(n^2) worst-case overhead. +//(* which is also O(n log n) in the average case.) + +namespace nall { + template + void sort(T list[], unsigned length) { + if(length <= 1) return; //nothing to sort + + //use insertion sort to quickly sort smaller blocks + if(length < 64) { + for(unsigned i = 0; i < length; i++) { + unsigned min = i; + for(unsigned j = i + 1; j < length; j++) { + if(list[j] < list[min]) min = j; + } + if(min != i) swap(list[i], list[min]); + } + return; + } + + //split list in half and recursively sort both + unsigned middle = length / 2; + sort(list, middle); + sort(list + middle, length - middle); + + //left and right are sorted here; perform merge sort + T *buffer = new T[length]; + unsigned offset = 0; + unsigned left = 0; + unsigned right = middle; + while(left < middle && right < length) { + if(list[left] < list[right]) { + buffer[offset++] = list[left++]; + } else { + buffer[offset++] = list[right++]; + } + } + while(left < middle) buffer[offset++] = list[left++]; + while(right < length) buffer[offset++] = list[right++]; + + for(unsigned i = 0; i < length; i++) list[i] = buffer[i]; + delete[] buffer; + } +} + +#endif diff --git a/libsnes/nall/static.hpp b/libsnes/nall/static.hpp new file mode 100644 index 0000000..4acb9fd --- /dev/null +++ b/libsnes/nall/static.hpp @@ -0,0 +1,20 @@ +#ifndef NALL_STATIC_HPP +#define NALL_STATIC_HPP + +namespace nall { + template struct static_if { typedef T type; }; + template struct static_if { typedef F type; }; + template struct mp_static_if { typedef typename static_if::type type; }; + + template struct static_and { enum { value = false }; }; + template<> struct static_and { enum { value = true }; }; + template struct mp_static_and { enum { value = static_and::value }; }; + + template struct static_or { enum { value = false }; }; + template<> struct static_or { enum { value = true }; }; + template<> struct static_or { enum { value = true }; }; + template<> struct static_or { enum { value = true }; }; + template struct mp_static_or { enum { value = static_or::value }; }; +} + +#endif diff --git a/libsnes/nall/stdint.hpp b/libsnes/nall/stdint.hpp new file mode 100644 index 0000000..e3dca98 --- /dev/null +++ b/libsnes/nall/stdint.hpp @@ -0,0 +1,32 @@ +#ifndef NALL_STDINT_HPP +#define NALL_STDINT_HPP + +#include + +#if defined(_MSC_VER) + typedef signed char int8_t; + typedef signed short int16_t; + typedef signed int int32_t; + typedef signed long long int64_t; + typedef int64_t intmax_t; + #if defined(_WIN64) + typedef int64_t intptr_t; + #else + typedef int32_t intptr_t; + #endif + + typedef unsigned char uint8_t; + typedef unsigned short uint16_t; + typedef unsigned int uint32_t; + typedef unsigned long long uint64_t; + typedef uint64_t uintmax_t; + #if defined(_WIN64) + typedef uint64_t uintptr_t; + #else + typedef uint32_t uintptr_t; + #endif +#else + #include +#endif + +#endif diff --git a/libsnes/nall/string.hpp b/libsnes/nall/string.hpp new file mode 100644 index 0000000..f3c9c48 --- /dev/null +++ b/libsnes/nall/string.hpp @@ -0,0 +1,35 @@ +#ifndef NALL_STRING_HPP +#define NALL_STRING_HPP + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace nall { + template<> struct has_length { enum { value = true }; }; + template<> struct has_size { enum { value = true }; }; +} + +#endif diff --git a/libsnes/nall/string/base.hpp b/libsnes/nall/string/base.hpp new file mode 100644 index 0000000..687fbce --- /dev/null +++ b/libsnes/nall/string/base.hpp @@ -0,0 +1,267 @@ +#ifndef NALL_STRING_BASE_HPP +#define NALL_STRING_BASE_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace nall { + struct string; + struct cstring; + + struct cstring { + inline operator const char*() const; + inline unsigned length() const; + inline bool operator==(const char*) const; + inline bool operator!=(const char*) const; + inline optional position(const char *key) const; + inline optional iposition(const char *key) const; + inline cstring& operator=(const char *data); + inline cstring(const char *data); + inline cstring(); + + protected: + const char *data; + }; + + struct string { + inline void reserve(unsigned); + inline unsigned length() const; + inline bool empty() const; + + inline string& assign(const char*); + inline string& append(const char*); + inline string& append(bool); + inline string& append(signed int value); + inline string& append(unsigned int value); + inline string& append(double value); + + inline char* begin() { return &data[0]; } + inline char* end() { return &data[length()]; } + inline const char* begin() const { return &data[0]; } + inline const char* end() const { return &data[length()]; } + + // <_______< + template + inline string& append(const T1 &t1, const T2 &t2) { append(t1), append(t2); return *this; } + + template + inline string& append(const T1 &t1, const T2 &t2, const T3 &t3) { append(t1), append(t2, t3); return *this; } + + template + inline string& append(const T1 &t1, const T2 &t2, const T3 &t3, const T4 &t4) { append(t1); append(t2, t3, t4); return *this; } + + template + inline string& append(const T1 &t1, const T2 &t2, const T3 &t3, const T4 &t4, const T5 &t5) { append(t1); append(t2, t3, t4, t5); return *this; } + + inline bool beginswith(const char*) const; + + template inline string& operator= (const T& value); + template inline string& operator<<(const T& value); + + inline operator const char*() const; + inline char* operator()(); + inline char& operator[](int); + + inline bool operator==(const char*) const; + inline bool operator!=(const char*) const; + inline bool operator< (const char*) const; + inline bool operator<=(const char*) const; + inline bool operator> (const char*) const; + inline bool operator>=(const char*) const; + + inline string& operator=(const string&); + + inline string(); + + template + inline string(T1 const &); + + template + inline string(T1 const &, T2 const &); + + template + inline string(T1 const &, T2 const &, T3 const &); + + template + inline string(T1 const &, T2 const &, T3 const &, T4 const &); + + template + inline string(T1 const &, T2 const &, T3 const &, T4 const &, T5 const &); + + template + inline string(T1 const &, T2 const &, T3 const &, T4 const &, T5 const &, T6 const &); + + template + inline string(T1 const &, T2 const &, T3 const &, T4 const &, T5 const &, T6 const &, T7 const &); + + template + inline string(T1 const &, T2 const &, T3 const &, T4 const &, T5 const &, T6 const &, T7 const &, T8 const &); + + inline string(string const &); + + inline ~string(); + + inline bool readfile(const string&); + inline string& replace (const char*, const char*); + inline string& qreplace(const char*, const char*); + + inline string& lower(); + inline string& upper(); + inline string& transform(const char *before, const char *after); + inline string& ltrim(const char *key = " "); + inline string& rtrim(const char *key = " "); + inline string& trim (const char *key = " "); + inline string& ltrim_once(const char *key = " "); + inline string& rtrim_once(const char *key = " "); + inline string& trim_once (const char *key = " "); + + inline bool wildcard(const char*) const; + inline bool iwildcard(const char*) const; + + protected: + char *data; + unsigned size; + + #if defined(QSTRING_H) + public: + inline operator QString() const; + #endif + }; + + template inline string to_string(const T& t) + { + return t; + } + + struct lstring : public linear_vector { + template inline lstring& operator<<(const T& value); + + inline optional find(const char*); + inline void split (const char*, const char*, unsigned = 0); + inline void qsplit(const char*, const char*, unsigned = 0); + + lstring(); + + lstring(const string &); + lstring(const string &, const string &); + lstring(const string &, const string &, const string &); + lstring(const string &, const string &, const string &, const string &); + lstring(const string &, const string &, const string &, const string &, const string &); + lstring(const string &, const string &, const string &, const string &, const string &, const string &); + lstring(const string &, const string &, const string &, const string &, const string &, const string &, const string &); + lstring(const string &, const string &, const string &, const string &, const string &, const string &, const string &, const string &); + + inline bool operator==(const lstring&) const; + inline bool operator!=(const lstring&) const; + + lstring(const lstring & str); + lstring(std::initializer_list); + }; + + //compare.hpp + inline char chrlower(char c); + inline char chrupper(char c); + inline int stricmp(const char *dest, const char *src); + inline bool strbegin (const char *str, const char *key); + inline bool stribegin(const char *str, const char *key); + inline bool strend (const char *str, const char *key); + inline bool striend(const char *str, const char *key); + inline bool wildcard(const char *str, const char *pattern); + inline bool iwildcard(const char *str, const char *pattern); + + //convert.hpp + inline char* strlower(char *str); + inline char* strupper(char *str); + inline char* strtr(char *dest, const char *before, const char *after); + inline uintmax_t hex (const char *str); + inline intmax_t integer (const char *str); + inline uintmax_t decimal (const char *str); + inline uintmax_t binary (const char *str); + inline double fp (const char *str); + + //match.hpp + inline bool match(const char *pattern, const char *str); + + //math.hpp + inline bool strint (const char *str, int &result); + inline bool strmath(const char *str, int &result); + + //strl.hpp + inline unsigned strlcpy(char *dest, const char *src, unsigned length); + inline unsigned strlcat(char *dest, const char *src, unsigned length); + + //strpos.hpp + inline optional strpos(const char *str, const char *key); + inline optional istrpos(const char *str, const char *key); + inline optional qstrpos(const char *str, const char *key); + inline optional iqstrpos(const char *str, const char *key); + template inline optional ustrpos(const char *str, const char *key); + inline optional ustrpos(const char *str, const char *key) { return ustrpos(str, key); } + + //trim.hpp + inline char* ltrim(char *str, const char *key = " "); + inline char* rtrim(char *str, const char *key = " "); + inline char* trim (char *str, const char *key = " "); + inline char* ltrim_once(char *str, const char *key = " "); + inline char* rtrim_once(char *str, const char *key = " "); + inline char* trim_once (char *str, const char *key = " "); + + //utility.hpp + inline unsigned strlcpy(string &dest, const char *src, unsigned length); + inline unsigned strlcat(string &dest, const char *src, unsigned length); + inline string substr(const char *src, unsigned start = 0, unsigned length = 0); + inline string& strtr(string &dest, const char *before, const char *after); + template inline bool quoteskip(T *&p); + template inline bool chrequal(char x, char y); + + inline string integer(intmax_t value); + template inline string linteger(intmax_t value); + template inline string rinteger(intmax_t value); + inline string decimal(uintmax_t value); + template inline string ldecimal(uintmax_t value); + template inline string rdecimal(uintmax_t value); + template inline string hex(uintmax_t value); + template inline string binary(uintmax_t value); + inline unsigned fp(char *str, double value); + inline string fp(double value); + + inline string linteger(intmax_t value) { return linteger<0>(value); } + inline string rinteger(intmax_t value) { return rinteger<0>(value); } + inline string ldecimal(uintmax_t value) { return ldecimal<0>(value); } + inline string rdecimal(uintmax_t value) { return rdecimal<0>(value); } + inline string hex(uintmax_t value) { return hex<0>(value); } + inline string binary(uintmax_t value) { return binary<0>(value); } + + //variadic.hpp + template + inline void print(T1 const &); + + template + inline void print(T1 const &, T2 const &); + + template + inline void print(T1 const &, T2 const &, T3 const &); + + template + inline void print(T1 const &, T2 const &, T3 const &, T4 const &); + + template + inline void print(T1 const &, T2 const &, T3 const &, T4 const &, T5 const &); + + template + inline void print(T1 const &, T2 const &, T3 const &, T4 const &, T5 const &, T6 const &); + + template + inline void print(T1 const &, T2 const &, T3 const &, T4 const &, T5 const &, T6 const &, T7 const &); + + template + inline void print(T1 const &, T2 const &, T3 const &, T4 const &, T5 const &, T6 const &, T7 const &, T8 const &); +}; + +#endif diff --git a/libsnes/nall/string/bml.hpp b/libsnes/nall/string/bml.hpp new file mode 100755 index 0000000..774c8e2 --- /dev/null +++ b/libsnes/nall/string/bml.hpp @@ -0,0 +1,160 @@ +#ifndef NALL_STRING_INTERNAL_HPP +#define NALL_STRING_INTERNAL_HPP + +//BML v1.0 parser +//revision 0.05 +// + +namespace nall { +namespace BML { + +inline static string indent(const char *s, unsigned depth) { + array output; + do { + for(unsigned n = 0; n < depth; n++) output.append('\t'); + do output.append(*s); while(*s && *s++ != '\n'); + } while(*s); + return output.get(); +} + +struct Node { + cstring name; + cstring value; + +private: + linear_vector children; + + inline bool valid(char p) const { //A-Za-z0-9-. + return p - 'A' < 26u | p - 'a' < 26u | p - '0' < 10u | p - '-' < 2u; + } + + inline unsigned parseDepth(char *&p) { + while(*p == '\n' || *p == '#') { + while(*p != '\n') *p++ = 0; + *p++ = 0; //'\n' + } + unsigned depth = 0; + while(p[depth] == '\t') depth++; + return depth; + } + + inline void parseName(char *&p) { + if(valid(*p) == false) throw "Missing node name"; + name = p; + while(valid(*p)) p++; + } + + inline void parseValue(char *&p) { + char terminal = *p == ':' ? '\n' : ' '; //':' or '=' + *p++ = 0; + value = p; + while(*p && *p != terminal && *p != '\n') p++; + } + + inline void parseBlock(char *&p, unsigned depth) { + value = p; + char *w = p; + while(parseDepth(p) > depth) { + p += depth + 1; + while(*p && *p != '\n') *w++ = *p++; + if(*p && *p != '\n') throw "Multi-line value missing line feed"; + *w++ = *p; + } + *(w - 1) = 0; //'\n' + } + + inline void parseLine(char *&p) { + unsigned depth = parseDepth(p); + while(*p == '\t') p++; + + parseName(p); + bool multiLine = *p == '~'; + if(multiLine) *p++ = 0; + else if(*p == ':' || *p == '=') parseValue(p); + if(*p && *p != ' ' && *p != '\n') throw "Invalid character encountered"; + + while(*p == ' ') { + *p++ = 0; + Node node; + node.parseName(p); + if(*p == ':' || *p == '=') node.parseValue(p); + if(*p && *p != ' ' && *p != '\n') throw "Invalid character after node"; + if(*p == '\n') *p++ = 0; + children.append(node); + } + + if(multiLine) return parseBlock(p, depth); + + while(parseDepth(p) > depth) { + Node node; + node.parseLine(p); + children.append(node); + } + } + + inline void parse(char *&p) { + while(*p) { + Node node; + node.parseLine(p); + children.append(node); + } + } + +public: + inline Node& operator[](const char *name) { + foreach (node, children) { + if(node.name == name) return node; + } + static Node node; + node.name = 0; + return node; + } + + inline Node& operator[](unsigned i) { + return children[i]; + } + + inline bool exists() const { return name; } + unsigned size() const { return children.size(); } + Node* begin() { return children.begin(); } + Node* end() { return children.end(); } + const Node* begin() const { return children.begin(); } + const Node* end() const { return children.end(); } + inline Node() : name(""), value("") {} + friend class Document; +}; + +struct Document : Node { + cstring error; + + inline bool load(const char *document) { + if(document == 0) return false; + this->document = strdup(document); + char *p = this->document; + try { + this->error = 0; + parse(p); + } catch(const char *error) { + this->error = error; + free(this->document); + this->document = 0; + children.reset(); + return false; + } + return true; + } + + inline Document(const char *document = "") : document(0), error(0) { if(*document) load(document); } + inline ~Document() { if(document) free(document); } + +private: + char *document; +}; + +} + +template<> struct has_size { enum { value = true }; }; + +} + +#endif diff --git a/libsnes/nall/string/bsv.hpp b/libsnes/nall/string/bsv.hpp new file mode 100644 index 0000000..d4b919e --- /dev/null +++ b/libsnes/nall/string/bsv.hpp @@ -0,0 +1,75 @@ +#ifndef NALL_STRING_BSV_HPP +#define NALL_STRING_BSV_HPP + +//BSV parser +//version 0.01 + +namespace nall { + +inline string bsv_decode(const char *input) { + string output; + unsigned offset = 0; + while(*input) { + //illegal characters + if(*input == '}' ) return ""; + if(*input == '\r') return ""; + if(*input == '\n') return ""; + + //normal characters + if(*input != '{') { output[offset++] = *input++; continue; } + + //entities + if(strbegin(input, "{lf}")) { output[offset++] = '\n'; input += 4; continue; } + if(strbegin(input, "{lb}")) { output[offset++] = '{'; input += 4; continue; } + if(strbegin(input, "{rb}")) { output[offset++] = '}'; input += 4; continue; } + + //illegal entities + return ""; + } + output[offset] = 0; + return output; +} + +inline string bsv_encode(const char *input) { + string output; + unsigned offset = 0; + while(*input) { + //illegal characters + if(*input == '\r') return ""; + + if(*input == '\n') { + output[offset++] = '{'; + output[offset++] = 'l'; + output[offset++] = 'f'; + output[offset++] = '}'; + input++; + continue; + } + + if(*input == '{') { + output[offset++] = '{'; + output[offset++] = 'l'; + output[offset++] = 'b'; + output[offset++] = '}'; + input++; + continue; + } + + if(*input == '}') { + output[offset++] = '{'; + output[offset++] = 'r'; + output[offset++] = 'b'; + output[offset++] = '}'; + input++; + continue; + } + + output[offset++] = *input++; + } + output[offset] = 0; + return output; +} + +} + +#endif diff --git a/libsnes/nall/string/cast.hpp b/libsnes/nall/string/cast.hpp new file mode 100644 index 0000000..54dd00c --- /dev/null +++ b/libsnes/nall/string/cast.hpp @@ -0,0 +1,31 @@ +#ifndef NALL_STRING_CAST_HPP +#define NALL_STRING_CAST_HPP + +namespace nall { + +//this is needed, as C++0x does not support explicit template specialization inside classes +template<> inline string to_string (const bool& v) { return v ? "true" : "false"; } +template<> inline string to_string (const signed int& v) { return integer(v); } +template<> inline string to_string (const unsigned int& v) { return decimal(v); } +template<> inline string to_string (const double& v) { return fp(v); } +template<> inline string to_string (char * const & v) { return v; } +template<> inline string to_string (const string& v) { return v; } + + +template string& string::operator= (const T& value) { return assign(to_string(value)); } +template string& string::operator<<(const T& value) { return append(to_string(value)); } + +template lstring& lstring::operator<<(const T& value) { + operator[](size()).assign(to_string(value)); + return *this; +} + +#if defined(QSTRING_H) +template<> inline string to_string(QString v) { return v.toUtf8().constData(); } +template<> inline string to_string(const QString &v) { return v.toUtf8().constData(); } +string::operator QString() const { return QString::fromUtf8(*this); } +#endif + +} + +#endif diff --git a/libsnes/nall/string/compare.hpp b/libsnes/nall/string/compare.hpp new file mode 100644 index 0000000..3122f62 --- /dev/null +++ b/libsnes/nall/string/compare.hpp @@ -0,0 +1,112 @@ +#ifndef NALL_STRING_COMPARE_HPP +#define NALL_STRING_COMPARE_HPP + +namespace nall { + +inline char chrlower(char c) { + return (c >= 'A' && c <= 'Z') ? c + ('a' - 'A') : c; +} + +inline char chrupper(char c) { + return (c >= 'a' && c <= 'z') ? c - ('a' - 'A') : c; +} + +inline int stricmp(const char *dest, const char *src) { + while(*dest) { + if(chrlower(*dest) != chrlower(*src)) break; + dest++; + src++; + } + + return (int)chrlower(*dest) - (int)chrlower(*src); +} + +inline bool strbegin(const char *str, const char *key) { + int i, ssl = strlen(str), ksl = strlen(key); + + if(ksl > ssl) return false; + return (!memcmp(str, key, ksl)); +} + +inline bool stribegin(const char *str, const char *key) { + int ssl = strlen(str), ksl = strlen(key); + + if(ksl > ssl) return false; + for(int i = 0; i < ksl; i++) { + if(str[i] >= 'A' && str[i] <= 'Z') { + if(str[i] != key[i] && str[i]+0x20 != key[i])return false; + } else if(str[i] >= 'a' && str[i] <= 'z') { + if(str[i] != key[i] && str[i]-0x20 != key[i])return false; + } else { + if(str[i] != key[i])return false; + } + } + return true; +} + +inline bool strend(const char *str, const char *key) { + int ssl = strlen(str), ksl = strlen(key); + + if(ksl > ssl) return false; + return (!memcmp(str + ssl - ksl, key, ksl)); +} + +inline bool striend(const char *str, const char *key) { + int ssl = strlen(str), ksl = strlen(key); + + if(ksl > ssl) return false; + for(int i = ssl - ksl, z = 0; i < ssl; i++, z++) { + if(str[i] >= 'A' && str[i] <= 'Z') { + if(str[i] != key[z] && str[i]+0x20 != key[z])return false; + } else if(str[i] >= 'a' && str[i] <= 'z') { + if(str[i] != key[z] && str[i]-0x20 != key[z])return false; + } else { + if(str[i] != key[z])return false; + } + } + return true; +} + +bool wildcard(const char *s, const char *p) { + const char *cp = 0, *mp = 0; + while(*s && *p != '*') { + if(*p != '?' && *s != *p) return false; + p++, s++; + } + while(*s) { + if(*p == '*') { + if(!*++p) return true; + mp = p, cp = s + 1; + } else if(*p == '?' || *p == *s) { + p++, s++; + } else { + p = mp, s = cp++; + } + } + while(*p == '*') p++; + return !*p; +} + +bool iwildcard(const char *s, const char *p) { + const char *cp = 0, *mp = 0; + while(*s && *p != '*') { + if(*p != '?' && chrlower(*s) != chrlower(*p)) return false; + p++, s++; + } + while(*s) { + if(*p == '*') { + if(!*++p) return true; + mp = p, cp = s + 1; + } else if(*p == '?' || chrlower(*p) == chrlower(*s)) { + p++, s++; + } else { + p = mp, s = cp++; + } + } + while(*p == '*') p++; + return !*p; +} + +} + +#endif diff --git a/libsnes/nall/string/convert.hpp b/libsnes/nall/string/convert.hpp new file mode 100644 index 0000000..826aecf --- /dev/null +++ b/libsnes/nall/string/convert.hpp @@ -0,0 +1,129 @@ +#ifndef NALL_STRING_CONVERT_HPP +#define NALL_STRING_CONVERT_HPP + +namespace nall { + +inline char* strlower(char *str) { + if(!str) return 0; + int i = 0; + while(str[i]) { + str[i] = chrlower(str[i]); + i++; + } + return str; +} + +inline char* strupper(char *str) { + if(!str) return 0; + int i = 0; + while(str[i]) { + str[i] = chrupper(str[i]); + i++; + } + return str; +} + +inline char* strtr(char *dest, const char *before, const char *after) { + if(!dest || !before || !after) return dest; + int sl = strlen(dest), bsl = strlen(before), asl = strlen(after); + + if(bsl != asl || bsl == 0) return dest; //patterns must be the same length for 1:1 replace + for(unsigned i = 0; i < sl; i++) { + for(unsigned l = 0; l < bsl; l++) { + if(dest[i] == before[l]) { + dest[i] = after[l]; + break; + } + } + } + + return dest; +} + +inline string& string::lower() { nall::strlower(data); return *this; } +inline string& string::upper() { nall::strupper(data); return *this; } +inline string& string::transform(const char *before, const char *after) { nall::strtr(data, before, after); return *this; } + +inline uintmax_t hex(const char *str) { + if(!str) return 0; + uintmax_t result = 0; + + //skip hex identifiers 0x and $, if present + if(*str == '0' && (*(str + 1) == 'X' || *(str + 1) == 'x')) str += 2; + else if(*str == '$') str++; + + while(*str) { + uint8_t x = *str++; + if(x >= '0' && x <= '9') x -= '0'; + else if(x >= 'A' && x <= 'F') x -= 'A' - 10; + else if(x >= 'a' && x <= 'f') x -= 'a' - 10; + else break; //stop at first invalid character + result = result * 16 + x; + } + + return result; +} + +inline intmax_t integer(const char *str) { + if(!str) return 0; + intmax_t result = 0; + bool negate = false; + + //check for sign + if(*str == '+') { + negate = false; + str++; + } else if(*str == '-') { + negate = true; + str++; + } + + while(*str) { + uint8_t x = *str++; + if(x >= '0' && x <= '9') x -= '0'; + else break; //stop at first invalid character + result = result * 10 + x; + } + + return !negate ? result : -result; +} + +inline uintmax_t decimal(const char *str) { + if(!str) return 0; + uintmax_t result = 0; + + while(*str) { + uint8_t x = *str++; + if(x >= '0' && x <= '9') x -= '0'; + else break; //stop at first invalid character + result = result * 10 + x; + } + + return result; +} + +inline uintmax_t binary(const char *str) { + if(!str) return 0; + uintmax_t result = 0; + + //skip bin identifiers 0b and %, if present + if(*str == '0' && (*(str + 1) == 'B' || *(str + 1) == 'b')) str += 2; + else if(*str == '%') str++; + + while(*str) { + uint8_t x = *str++; + if(x == '0' || x == '1') x -= '0'; + else break; //stop at first invalid character + result = result * 2 + x; + } + + return result; +} + +inline double fp(const char *str) { + return strtod(str, NULL); +} + +} + +#endif diff --git a/libsnes/nall/string/core.hpp b/libsnes/nall/string/core.hpp new file mode 100644 index 0000000..50cb151 --- /dev/null +++ b/libsnes/nall/string/core.hpp @@ -0,0 +1,335 @@ +#ifndef NALL_STRING_CORE_HPP +#define NALL_STRING_CORE_HPP + +namespace nall { + +void string::reserve(unsigned size_) { + if(size_ > size) { + size = size_; + data = (char*)realloc(data, size + 1); + data[size] = 0; + } +} + +unsigned string::length() const { + return strlen(data); +} + +bool string::empty() const { + return !*data; +} + +string& string::assign(const char *s) { + unsigned length = strlen(s); + reserve(length); + strcpy(data, s); + return *this; +} + +string& string::append(const char *s) { + unsigned length = strlen(data) + strlen(s); + reserve(length); + strcat(data, s); + return *this; +} + +string& string::append(bool value) { append(value ? "true" : "false"); return *this; } +string& string::append(signed int value) { append(integer(value)); return *this; } +string& string::append(unsigned int value) { append(decimal(value)); return *this; } +string& string::append(double value) { append(fp(value)); return *this; } + +bool string::beginswith(const char *str) const { return strstr(data, str) == data; } + +string::operator const char*() const { + return data; +} + +char* string::operator()() { + return data; +} + +char& string::operator[](int index) { + reserve(index); + return data[index]; +} + +bool string::operator==(const char *str) const { return strcmp(data, str) == 0; } +bool string::operator!=(const char *str) const { return strcmp(data, str) != 0; } +bool string::operator< (const char *str) const { return strcmp(data, str) < 0; } +bool string::operator<=(const char *str) const { return strcmp(data, str) <= 0; } +bool string::operator> (const char *str) const { return strcmp(data, str) > 0; } +bool string::operator>=(const char *str) const { return strcmp(data, str) >= 0; } + +string& string::operator=(const string &value) { + assign(value); + return *this; +} + +static void istring(string & output){} + +template +static void istring(string & output, T1 const & v1) +{ output.append(v1); istring(output); } + +template +static void istring(string & output, T1 const & v1, + T2 const & v2) +{ output.append(v1); istring(output, v2); } + +template +static void istring(string & output, T1 const & v1, + T2 const & v2, + T3 const & v3) +{ output.append(v1); istring(output, v2, v3); } + +template +static void istring(string & output, T1 const & v1, + T2 const & v2, + T3 const & v3, + T4 const & v4) +{ output.append(v1); istring(output, v2, v3, v4); } + +template +static void istring(string & output, T1 const & v1, + T2 const & v2, + T3 const & v3, + T4 const & v4, + T5 const & v5) +{ output.append(v1); istring(output, v2, v3, v4, v5); } + +template +static void istring(string & output, T1 const & v1, + T2 const & v2, + T3 const & v3, + T4 const & v4, + T5 const & v5, + T6 const & v6) +{ output.append(v1); istring(output, v2, v3, v4, v5, v6); } + +template +static void istring(string & output, T1 const & v1, + T2 const & v2, + T3 const & v3, + T4 const & v4, + T5 const & v5, + T6 const & v6, + T7 const & v7) +{ output.append(v1); istring(output, v2, v3, v4, v5, v6, v7); } + +template +static void istring(string & output, T1 const & v1, + T2 const & v2, + T3 const & v3, + T4 const & v4, + T5 const & v5, + T6 const & v6, + T7 const & v7, + T8 const & v8) +{ output.append(v1); istring(output, v2, v3, v4, v5, v6, v7, v8); } + +string::string(){ + size = 64; + data = (char*)malloc(size + 1); + *data = 0; +} + +template +string::string(T1 const & v1) : size(64) +{ data = (char*)malloc(size + 1); *data = 0; istring(*this, v1); } + +template +string::string(T1 const & v1, + T2 const & v2) : size(64) +{ data = (char*)malloc(size + 1); *data = 0; istring(*this, v1, v2); } + +template +string::string(T1 const & v1, + T2 const & v2, + T3 const & v3) : size(64) +{ data = (char*)malloc(size + 1); *data = 0; istring(*this, v1, v2, v3); } + +template +string::string(T1 const & v1, + T2 const & v2, + T3 const & v3, + T4 const & v4) : size(64) +{ data = (char*)malloc(size + 1); *data = 0; istring(*this, v1, v2, v3, v4); } + +template +string::string(T1 const & v1, + T2 const & v2, + T3 const & v3, + T4 const & v4, + T5 const & v5) : size(64) +{ data = (char*)malloc(size + 1); *data = 0; istring(*this, v1, v2, v3, v4, v5); } + +template +string::string(T1 const & v1, + T2 const & v2, + T3 const & v3, + T4 const & v4, + T5 const & v5, + T6 const & v6) : size(64) +{ data = (char*)malloc(size + 1); *data = 0; istring(*this, v1, v2, v3, v4, v5, v6); } + +template +string::string(T1 const & v1, + T2 const & v2, + T3 const & v3, + T4 const & v4, + T5 const & v5, + T6 const & v6, + T7 const & v7) : size(64) +{ data = (char*)malloc(size + 1); *data = 0; istring(*this, v1, v2, v3, v4, v5, v6, v7); } + +template +string::string(T1 const & v1, + T2 const & v2, + T3 const & v3, + T4 const & v4, + T5 const & v5, + T6 const & v6, + T7 const & v7, + T8 const & v8) : size(64) +{ data = (char*)malloc(size + 1); *data = 0; istring(*this, v1, v2, v3, v4, v5, v6, v7, v8); } + +string::string(const string &value) { + size = strlen(value); + data = strdup(value); +} + +string::~string() { + if(data) free(data); +} + +bool string::readfile(const string &filename) { + assign(""); + + #if !defined(_WIN32) + FILE *fp = fopen(filename, "rb"); + #else + FILE *fp = _wfopen(utf16_t(filename), L"rb"); + #endif + if(!fp) return false; + + fseek(fp, 0, SEEK_END); + unsigned size = ftell(fp); + rewind(fp); + char *fdata = new char[size + 1]; + unsigned unused = fread(fdata, 1, size, fp); + fclose(fp); + fdata[size] = 0; + assign(fdata); + delete[] fdata; + + return true; +} + +optional lstring::find(const char *key) { + for(unsigned i = 0; i < size(); i++) { + if(operator[](i) == key) return optional(true, i); + } + return optional(false, 0); +} + +inline lstring::lstring() { +} + +inline lstring::lstring(const lstring & str) { + for(int i = 0; i < str.size(); i++) operator<<(str[i]); +} + +inline lstring::lstring(const string & v1){ + operator<<(v1); +} +inline lstring::lstring(const string & v1, const string & v2){ + operator<<(v1); operator<<(v2); +} +inline lstring::lstring(const string & v1, const string & v2, const string & v3){ + operator<<(v1); operator<<(v2); operator<<(v3); +} +inline lstring::lstring(const string & v1, const string & v2, const string & v3, const string & v4){ + operator<<(v1); operator<<(v2); operator<<(v3); operator<<(v4); +} +inline lstring::lstring(const string & v1, const string & v2, const string & v3, const string & v4, const string & v5){ + operator<<(v1); operator<<(v2); operator<<(v3); operator<<(v4); + operator<<(v5); +} +inline lstring::lstring(const string & v1, const string & v2, const string & v3, const string & v4, const string & v5, const string & v6){ + operator<<(v1); operator<<(v2); operator<<(v3); operator<<(v4); + operator<<(v5); operator<<(v6); +} +inline lstring::lstring(const string & v1, const string & v2, const string & v3, const string & v4, const string & v5, const string & v6, const string & v7){ + operator<<(v1); operator<<(v2); operator<<(v3); operator<<(v4); + operator<<(v5); operator<<(v6); operator<<(v7); +} +inline lstring::lstring(const string & v1, const string & v2, const string & v3, const string & v4, const string & v5, const string & v6, const string & v7, const string & v8){ + operator<<(v1); operator<<(v2); operator<<(v3); operator<<(v4); + operator<<(v5); operator<<(v6); operator<<(v7); operator<<(v8); +} + +inline lstring::lstring(std::initializer_list list) { + for(const string *s = list.begin(); s != list.end(); ++s) { + operator<<(*s); + } +} + +} + +#endif diff --git a/libsnes/nall/string/cstring.hpp b/libsnes/nall/string/cstring.hpp new file mode 100755 index 0000000..13b508f --- /dev/null +++ b/libsnes/nall/string/cstring.hpp @@ -0,0 +1,21 @@ +#ifdef NALL_STRING_INTERNAL_HPP + +//const string: +//bind a const char* pointer to an object that has various testing functionality; +//yet lacks the memory allocation and modification functionality of the string class + +namespace nall { + +cstring::operator const char*() const { return data; } +unsigned cstring::length() const { return strlen(data); } +bool cstring::operator==(const char *s) const { return !strcmp(data, s); } +bool cstring::operator!=(const char *s) const { return strcmp(data, s); } +optional cstring::position (const char *key) const { return strpos(data, key); } +optional cstring::iposition(const char *key) const { return istrpos(data, key); } +cstring& cstring::operator=(const char *data) { this->data = data; return *this; } +cstring::cstring(const char *data) : data(data) {} +cstring::cstring() : data("") {} + +} + +#endif diff --git a/libsnes/nall/string/filename.hpp b/libsnes/nall/string/filename.hpp new file mode 100644 index 0000000..f375076 --- /dev/null +++ b/libsnes/nall/string/filename.hpp @@ -0,0 +1,61 @@ +#ifndef NALL_FILENAME_HPP +#define NALL_FILENAME_HPP + +namespace nall { + +// "foo/bar.c" -> "foo/", "bar.c" -> "./" +inline string dir(char const *name) { + string result = name; + for(signed i = strlen(result); i >= 0; i--) { + if(result[i] == '/' || result[i] == '\\') { + result[i + 1] = 0; + break; + } + if(i == 0) result = "./"; + } + return result; +} + +// "foo/bar.c" -> "bar.c" +inline string notdir(char const *name) { + for(signed i = strlen(name); i >= 0; i--) { + if(name[i] == '/' || name[i] == '\\') { + name += i + 1; + break; + } + } + string result = name; + return result; +} + +// "foo/bar.c" -> "foo/bar" +inline string basename(char const *name) { + string result = name; + for(signed i = strlen(result); i >= 0; i--) { + if(result[i] == '/' || result[i] == '\\') { + //file has no extension + break; + } + if(result[i] == '.') { + result[i] = 0; + break; + } + } + return result; +} + +// "foo/bar.c" -> "c" +inline string extension(char const *name) { + for(signed i = strlen(name); i >= 0; i--) { + if(name[i] == '.') { + name += i + 1; + break; + } + } + string result = name; + return result; +} + +} + +#endif diff --git a/libsnes/nall/string/match.hpp b/libsnes/nall/string/match.hpp new file mode 100644 index 0000000..8822d24 --- /dev/null +++ b/libsnes/nall/string/match.hpp @@ -0,0 +1,76 @@ +#ifndef NALL_STRING_MATCH_HPP +#define NALL_STRING_MATCH_HPP + +namespace nall { + +inline bool match(const char *p, const char *s) { + const char *p_ = 0, *s_ = 0; + + for(;;) { + if(!*s) { + while(*p == '*') p++; + return !*p; + } + + //wildcard match + if(*p == '*') { + p_ = p++, s_ = s; + continue; + } + + //any match + if(*p == '?') { + p++, s++; + continue; + } + + //ranged match + if(*p == '{') { + #define pattern(name_, rule_) \ + if(strbegin(p, name_)) { \ + if(rule_) { \ + p += sizeof(name_) - 1, s++; \ + continue; \ + } \ + goto failure; \ + } + + pattern("{alpha}", (*s >= 'A' && *s <= 'Z') || (*s >= 'a' && *s <= 'z')) + pattern("{alphanumeric}", (*s >= 'A' && *s <= 'Z') || (*s >= 'a' && *s <= 'z') || (*s >= '0' && *s <= '9')) + pattern("{binary}", (*s == '0' || *s == '1')) + pattern("{hex}", (*s >= '0' && *s <= '9') || (*s >= 'A' && *s <= 'F') || (*s >= 'a' && *s <= 'f')) + pattern("{lowercase}", (*s >= 'a' && *s <= 'z')) + pattern("{numeric}", (*s >= '0' && *s <= '9')) + pattern("{uppercase}", (*s >= 'A' && *s <= 'Z')) + pattern("{whitespace}", (*s == ' ' || *s == '\t')) + + #undef pattern + goto failure; + } + + //reserved character match + if(*p == '\\') { + p++; + //fallthrough + } + + //literal match + if(*p == *s) { + p++; s++; + continue; + } + + //attempt wildcard rematch + failure: + if(p_) { + p = p_, s = s_ + 1; + continue; + } + + return false; + } +} + +} + +#endif diff --git a/libsnes/nall/string/math.hpp b/libsnes/nall/string/math.hpp new file mode 100644 index 0000000..496315f --- /dev/null +++ b/libsnes/nall/string/math.hpp @@ -0,0 +1,164 @@ +#ifndef NALL_STRING_MATH_HPP +#define NALL_STRING_MATH_HPP + +namespace nall { + +inline int eval_integer(const char *&s) { + if(!*s) throw "unrecognized_integer"; + int value = 0, x = *s, y = *(s + 1); + + //hexadecimal + if(x == '0' && (y == 'X' || y == 'x')) { + s += 2; + while(true) { + if(*s >= '0' && *s <= '9') { value = value * 16 + (*s++ - '0'); continue; } + if(*s >= 'A' && *s <= 'F') { value = value * 16 + (*s++ - 'A' + 10); continue; } + if(*s >= 'a' && *s <= 'f') { value = value * 16 + (*s++ - 'a' + 10); continue; } + return value; + } + } + + //binary + if(x == '0' && (y == 'B' || y == 'b')) { + s += 2; + while(true) { + if(*s == '0' || *s == '1') { value = value * 2 + (*s++ - '0'); continue; } + return value; + } + } + + //octal (or decimal '0') + if(x == '0') { + s += 1; + while(true) { + if(*s >= '0' && *s <= '7') { value = value * 8 + (*s++ - '0'); continue; } + return value; + } + } + + //decimal + if(x >= '0' && x <= '9') { + while(true) { + if(*s >= '0' && *s <= '9') { value = value * 10 + (*s++ - '0'); continue; } + return value; + } + } + + //char + if(x == '\'' && y != '\'') { + s += 1; + while(true) { + value = value * 256 + *s++; + if(*s == '\'') { s += 1; return value; } + if(!*s) throw "mismatched_char"; + } + } + + throw "unrecognized_integer"; +} + +inline int eval(const char *&s, int depth = 0) { + while(*s == ' ' || *s == '\t') s++; //trim whitespace + if(!*s) throw "unrecognized_token"; + int value = 0, x = *s, y = *(s + 1); + + if(*s == '(') { + value = eval(++s, 1); + if(*s++ != ')') throw "mismatched_group"; + } + + else if(x == '!') value = !eval(++s, 13); + else if(x == '~') value = ~eval(++s, 13); + else if(x == '+') value = +eval(++s, 13); + else if(x == '-') value = -eval(++s, 13); + + else if((x >= '0' && x <= '9') || x == '\'') value = eval_integer(s); + + else throw "unrecognized_token"; + + while(true) { + while(*s == ' ' || *s == '\t') s++; //trim whitespace + if(!*s) break; + x = *s, y = *(s + 1); + + if(depth >= 13) break; + if(x == '*') { value *= eval(++s, 13); continue; } + if(x == '/') { value /= eval(++s, 13); continue; } + if(x == '%') { value %= eval(++s, 13); continue; } + + if(depth >= 12) break; + if(x == '+') { value += eval(++s, 12); continue; } + if(x == '-') { value -= eval(++s, 12); continue; } + + if(depth >= 11) break; + if(x == '<' && y == '<') { value <<= eval(++++s, 11); continue; } + if(x == '>' && y == '>') { value >>= eval(++++s, 11); continue; } + + if(depth >= 10) break; + if(x == '<' && y == '=') { value = value <= eval(++++s, 10); continue; } + if(x == '>' && y == '=') { value = value >= eval(++++s, 10); continue; } + if(x == '<') { value = value < eval(++s, 10); continue; } + if(x == '>') { value = value > eval(++s, 10); continue; } + + if(depth >= 9) break; + if(x == '=' && y == '=') { value = value == eval(++++s, 9); continue; } + if(x == '!' && y == '=') { value = value != eval(++++s, 9); continue; } + + if(depth >= 8) break; + if(x == '&' && y != '&') { value = value & eval(++s, 8); continue; } + + if(depth >= 7) break; + if(x == '^' && y != '^') { value = value ^ eval(++s, 7); continue; } + + if(depth >= 6) break; + if(x == '|' && y != '|') { value = value | eval(++s, 6); continue; } + + if(depth >= 5) break; + if(x == '&' && y == '&') { value = eval(++++s, 5) && value; continue; } + + if(depth >= 4) break; + if(x == '^' && y == '^') { value = (!eval(++++s, 4) != !value); continue; } + + if(depth >= 3) break; + if(x == '|' && y == '|') { value = eval(++++s, 3) || value; continue; } + + if(x == '?') { + int lhs = eval(++s, 2); + if(*s != ':') throw "mismatched_ternary"; + int rhs = eval(++s, 2); + value = value ? lhs : rhs; + continue; + } + if(depth >= 2) break; + + if(depth > 0 && x == ')') break; + + throw "unrecognized_token"; + } + + return value; +} + +inline bool strint(const char *s, int &result) { + try { + result = eval_integer(s); + return true; + } catch(const char*) { + result = 0; + return false; + } +} + +inline bool strmath(const char *s, int &result) { + try { + result = eval(s); + return true; + } catch(const char*) { + result = 0; + return false; + } +} + +} + +#endif diff --git a/libsnes/nall/string/replace.hpp b/libsnes/nall/string/replace.hpp new file mode 100644 index 0000000..db405a9 --- /dev/null +++ b/libsnes/nall/string/replace.hpp @@ -0,0 +1,103 @@ +#ifndef NALL_STRING_REPLACE_HPP +#define NALL_STRING_REPLACE_HPP + +namespace nall { + +string& string::replace(const char *key, const char *token) { + int i, z, ksl = strlen(key), tsl = strlen(token), ssl = length(); + unsigned int replace_count = 0, size = ssl; + char *buffer; + + if(ksl <= ssl) { + if(tsl > ksl) { //the new string may be longer than the old string... + for(i = 0; i <= ssl - ksl;) { //so let's find out how big of a string we'll need... + if(!memcmp(data + i, key, ksl)) { + replace_count++; + i += ksl; + } else i++; + } + size = ssl + ((tsl - ksl) * replace_count); + reserve(size); + } + + buffer = new char[size + 1]; + for(i = z = 0; i < ssl;) { + if(i <= ssl - ksl) { + if(!memcmp(data + i, key, ksl)) { + memcpy(buffer + z, token, tsl); + z += tsl; + i += ksl; + } else buffer[z++] = data[i++]; + } else buffer[z++] = data[i++]; + } + buffer[z] = 0; + + assign(buffer); + delete[] buffer; + } + + return *this; +} + +string& string::qreplace(const char *key, const char *token) { + int i, l, z, ksl = strlen(key), tsl = strlen(token), ssl = length(); + unsigned int replace_count = 0, size = ssl; + uint8_t x; + char *buffer; + + if(ksl <= ssl) { + if(tsl > ksl) { + for(i = 0; i <= ssl - ksl;) { + x = data[i]; + if(x == '\"' || x == '\'') { + l = i; + i++; + while(data[i++] != x) { + if(i == ssl) { + i = l; + break; + } + } + } + if(!memcmp(data + i, key, ksl)) { + replace_count++; + i += ksl; + } else i++; + } + size = ssl + ((tsl - ksl) * replace_count); + reserve(size); + } + + buffer = new char[size + 1]; + for(i = z = 0; i < ssl;) { + x = data[i]; + if(x == '\"' || x == '\'') { + l = i++; + while(data[i] != x && i < ssl)i++; + if(i >= ssl)i = l; + else { + memcpy(buffer + z, data + l, i - l); + z += i - l; + } + } + if(i <= ssl - ksl) { + if(!memcmp(data + i, key, ksl)) { + memcpy(buffer + z, token, tsl); + z += tsl; + i += ksl; + replace_count++; + } else buffer[z++] = data[i++]; + } else buffer[z++] = data[i++]; + } + buffer[z] = 0; + + assign(buffer); + delete[] buffer; + } + + return *this; +} + +}; + +#endif diff --git a/libsnes/nall/string/split.hpp b/libsnes/nall/string/split.hpp new file mode 100644 index 0000000..bb77dfc --- /dev/null +++ b/libsnes/nall/string/split.hpp @@ -0,0 +1,56 @@ +#ifndef NALL_STRING_SPLIT_HPP +#define NALL_STRING_SPLIT_HPP + +namespace nall { + +void lstring::split(const char *key, const char *src, unsigned limit) { + reset(); + + int ssl = strlen(src), ksl = strlen(key); + int lp = 0, split_count = 0; + + for(int i = 0; i <= ssl - ksl;) { + if(!memcmp(src + i, key, ksl)) { + strlcpy(operator[](split_count++), src + lp, i - lp + 1); + i += ksl; + lp = i; + if(!--limit) break; + } else i++; + } + + operator[](split_count++) = src + lp; +} + +void lstring::qsplit(const char *key, const char *src, unsigned limit) { + reset(); + + int ssl = strlen(src), ksl = strlen(key); + int lp = 0, split_count = 0; + + for(int i = 0; i <= ssl - ksl;) { + uint8_t x = src[i]; + + if(x == '\"' || x == '\'') { + int z = i++; //skip opening quote + while(i < ssl && src[i] != x) i++; + if(i >= ssl) i = z; //failed match, rewind i + else { + i++; //skip closing quote + continue; //restart in case next char is also a quote + } + } + + if(!memcmp(src + i, key, ksl)) { + strlcpy(operator[](split_count++), src + lp, i - lp + 1); + i += ksl; + lp = i; + if(!--limit) break; + } else i++; + } + + operator[](split_count++) = src + lp; +} + +}; + +#endif diff --git a/libsnes/nall/string/strl.hpp b/libsnes/nall/string/strl.hpp new file mode 100644 index 0000000..1c90c1c --- /dev/null +++ b/libsnes/nall/string/strl.hpp @@ -0,0 +1,52 @@ +#ifndef NALL_STRING_STRL_HPP +#define NALL_STRING_STRL_HPP + +namespace nall { + +//strlcpy, strlcat based on OpenBSD implementation by Todd C. Miller + +//return = strlen(src) +inline unsigned strlcpy(char *dest, const char *src, unsigned length) { + char *d = dest; + const char *s = src; + unsigned n = length; + + if(n) { + while(--n && (*d++ = *s++)); //copy as many bytes as possible, or until null terminator reached + } + + if(!n) { + if(length) *d = 0; + while(*s++); //traverse rest of s, so that s - src == strlen(src) + } + + return (s - src - 1); //return length of copied string, sans null terminator +} + +//return = strlen(src) + min(length, strlen(dest)) +inline unsigned strlcat(char *dest, const char *src, unsigned length) { + char *d = dest; + const char *s = src; + unsigned n = length; + + while(n-- && *d) d++; //find end of dest + unsigned dlength = d - dest; + n = length - dlength; //subtract length of dest from maximum string length + + if(!n) return dlength + strlen(s); + + while(*s) { + if(n != 1) { + *d++ = *s; + n--; + } + s++; + } + *d = 0; + + return dlength + (s - src); //return length of resulting string, sans null terminator +} + +} + +#endif diff --git a/libsnes/nall/string/strpos.hpp b/libsnes/nall/string/strpos.hpp new file mode 100644 index 0000000..e698c40 --- /dev/null +++ b/libsnes/nall/string/strpos.hpp @@ -0,0 +1,34 @@ +#ifndef NALL_STRING_STRPOS_HPP +#define NALL_STRING_STRPOS_HPP + +//usage example: +//if(auto pos = strpos(str, key)) print(pos(), "\n"); +//prints position of key within str, only if it is found + +namespace nall { + +template +optional ustrpos(const char *str, const char *key) { + const char *base = str; + + while(*str) { + if(quoteskip(str)) continue; + for(unsigned n = 0;; n++) { + if(key[n] == 0) return optional(true, (unsigned)(str - base)); + if(str[n] == 0) return optional(false, 0); + if(!chrequal(str[n], key[n])) break; + } + str++; + } + + return optional(false, 0); +} + +optional strpos(const char *str, const char *key) { return ustrpos(str, key); } +optional istrpos(const char *str, const char *key) { return ustrpos(str, key); } +optional qstrpos(const char *str, const char *key) { return ustrpos(str, key); } +optional iqstrpos(const char *str, const char *key) { return ustrpos(str, key); } + +} + +#endif diff --git a/libsnes/nall/string/trim.hpp b/libsnes/nall/string/trim.hpp new file mode 100644 index 0000000..f048626 --- /dev/null +++ b/libsnes/nall/string/trim.hpp @@ -0,0 +1,61 @@ +#ifndef NALL_STRING_TRIM_HPP +#define NALL_STRING_TRIM_HPP + +namespace nall { + +inline char* ltrim(char *str, const char *key) { + if(!key || !*key) return str; + while(strbegin(str, key)) { + char *dest = str, *src = str + strlen(key); + while(true) { + *dest = *src++; + if(!*dest) break; + dest++; + } + } + return str; +} + +inline char* rtrim(char *str, const char *key) { + if(!key || !*key) return str; + while(strend(str, key)) str[strlen(str) - strlen(key)] = 0; + return str; +} + +inline char* trim(char *str, const char *key) { + return ltrim(rtrim(str, key), key); +} + +inline char* ltrim_once(char *str, const char *key) { + if(!key || !*key) return str; + if(strbegin(str, key)) { + char *dest = str, *src = str + strlen(key); + while(true) { + *dest = *src++; + if(!*dest) break; + dest++; + } + } + return str; +} + +inline char* rtrim_once(char *str, const char *key) { + if(!key || !*key) return str; + if(strend(str, key)) str[strlen(str) - strlen(key)] = 0; + return str; +} + +inline char* trim_once(char *str, const char *key) { + return ltrim_once(rtrim_once(str, key), key); +} + +string& string::ltrim(const char *key) { nall::ltrim(data, key); return *this; } +string& string::rtrim(const char *key) { nall::rtrim(data, key); return *this; } +string& string::trim (const char *key) { nall::trim (data, key); return *this; } +string& string::ltrim_once(const char *key) { nall::ltrim_once(data, key); return *this; } +string& string::rtrim_once(const char *key) { nall::rtrim_once(data, key); return *this; } +string& string::trim_once (const char *key) { nall::trim_once (data, key); return *this; } + +} + +#endif diff --git a/libsnes/nall/string/utility.hpp b/libsnes/nall/string/utility.hpp new file mode 100644 index 0000000..ef69b1d --- /dev/null +++ b/libsnes/nall/string/utility.hpp @@ -0,0 +1,287 @@ +#ifndef NALL_STRING_UTILITY_HPP +#define NALL_STRING_UTILITY_HPP + +#include + +namespace nall { + +inline unsigned strlcpy(string &dest, const char *src, unsigned length) { + dest.reserve(length); + return strlcpy(dest(), src, length); +} + +inline unsigned strlcat(string &dest, const char *src, unsigned length) { + dest.reserve(length); + return strlcat(dest(), src, length); +} + +inline string substr(const char *src, unsigned start, unsigned length) { + string dest; + if(length == 0) { + //copy entire string + dest = src + start; + } else { + //copy partial string + strlcpy(dest, src + start, length + 1); + } + return dest; +} + +/* arithmetic <> string */ + +inline string integer(intmax_t value) { + bool negative = value < 0; + if(negative) value = abs(value); + + char buffer[64]; + unsigned size = 0; + + do { + unsigned n = value % 10; + buffer[size++] = '0' + n; + value /= 10; + } while(value); + buffer[size++] = negative ? '-' : '+'; + buffer[size] = 0; + + char result[64]; + memset(result, '0', size); + result[size] = 0; + + for(signed x = size - 1, y = 0; x >= 0 && y < size; x--, y++) { + result[x] = buffer[y]; + } + + return (const char*)result; +} + +template inline string linteger(intmax_t value) { + bool negative = value < 0; + if(negative) value = abs(value); + + char buffer[64]; + unsigned size = 0; + + do { + unsigned n = value % 10; + buffer[size++] = '0' + n; + value /= 10; + } while(value); + buffer[size++] = negative ? '-' : '+'; + buffer[size] = 0; + + unsigned length = (length_ == 0 ? size : length_); + char result[64]; + memset(result, ' ', length); + result[length] = 0; + + for(signed x = 0, y = size - 1; x < length && y >= 0; x++, y--) { + result[x] = buffer[y]; + } + + return (const char*)result; +} + +template inline string rinteger(intmax_t value) { + bool negative = value < 0; + if(negative) value = abs(value); + + char buffer[64]; + unsigned size = 0; + + do { + unsigned n = value % 10; + buffer[size++] = '0' + n; + value /= 10; + } while(value); + buffer[size++] = negative ? '-' : '+'; + buffer[size] = 0; + + unsigned length = (length_ == 0 ? size : length_); + char result[64]; + memset(result, ' ', length); + result[length] = 0; + + for(signed x = length - 1, y = 0; x >= 0 && y < size; x--, y++) { + result[x] = buffer[y]; + } + + return (const char*)result; +} + +inline string decimal(uintmax_t value) { + char buffer[64]; + unsigned size = 0; + + do { + unsigned n = value % 10; + buffer[size++] = '0' + n; + value /= 10; + } while(value); + buffer[size] = 0; + + char result[64]; + memset(result, '0', size); + result[size] = 0; + + for(signed x = size - 1, y = 0; x >= 0 && y < size; x--, y++) { + result[x] = buffer[y]; + } + + return (const char*)result; +} + +template inline string ldecimal(uintmax_t value) { + char buffer[64]; + unsigned size = 0; + + do { + unsigned n = value % 10; + buffer[size++] = '0' + n; + value /= 10; + } while(value); + buffer[size] = 0; + + unsigned length = (length_ == 0 ? size : length_); + char result[64]; + memset(result, ' ', length); + result[length] = 0; + + for(signed x = 0, y = size - 1; x < length && y >= 0; x++, y--) { + result[x] = buffer[y]; + } + + return (const char*)result; +} + +template inline string rdecimal(uintmax_t value) { + char buffer[64]; + unsigned size = 0; + + do { + unsigned n = value % 10; + buffer[size++] = '0' + n; + value /= 10; + } while(value); + buffer[size] = 0; + + unsigned length = (length_ == 0 ? size : length_); + char result[64]; + memset(result, ' ', length); + result[length] = 0; + + for(signed x = length - 1, y = 0; x >= 0 && y < size; x--, y++) { + result[x] = buffer[y]; + } + + return (const char*)result; +} + +template inline string hex(uintmax_t value) { + char buffer[64]; + unsigned size = 0; + + do { + unsigned n = value & 15; + buffer[size++] = n < 10 ? '0' + n : 'a' + n - 10; + value >>= 4; + } while(value); + + unsigned length = (length_ == 0 ? size : length_); + char result[64]; + memset(result, '0', length); + result[length] = 0; + + for(signed x = length - 1, y = 0; x >= 0 && y < size; x--, y++) { + result[x] = buffer[y]; + } + + return (const char*)result; +} + +template inline string binary(uintmax_t value) { + char buffer[256]; + unsigned size = 0; + + do { + unsigned n = value & 1; + buffer[size++] = '0' + n; + value >>= 1; + } while(value); + + unsigned length = (length_ == 0 ? size : length_); + char result[64]; + memset(result, '0', length); + result[length] = 0; + + for(signed x = length - 1, y = 0; x >= 0 && y < size; x--, y++) { + result[x] = buffer[y]; + } + + return (const char*)result; +} + +//using sprintf is certainly not the most ideal method to convert +//a double to a string ... but attempting to parse a double by +//hand, digit-by-digit, results in subtle rounding errors. +inline unsigned fp(char *str, double value) { + char buffer[256]; + sprintf(buffer, "%f", value); + + //remove excess 0's in fraction (2.500000 -> 2.5) + for(char *p = buffer; *p; p++) { + if(*p == '.') { + char *p = buffer + strlen(buffer) - 1; + while(*p == '0') { + if(*(p - 1) != '.') *p = 0; //... but not for eg 1.0 -> 1. + p--; + } + break; + } + } + + unsigned length = strlen(buffer); + if(str) strcpy(str, buffer); + return length + 1; +} + +inline string fp(double value) { + string temp; + temp.reserve(fp(0, value)); + fp(temp(), value); + return temp; +} + +inline string sha256(const uint8_t *data, unsigned size) { + sha256_ctx sha; + uint8_t hash[32]; + sha256_init(&sha); + sha256_chunk(&sha, data, size); + sha256_final(&sha); + sha256_hash(&sha, hash); + string result; + foreach(byte, hash) result.append(hex<2>(byte)); + return result; +} + +template +inline bool chrequal(char x, char y) { + if(Insensitive) return chrlower(x) == chrlower(y); + return x == y; +} + +template +inline bool quoteskip(T *&p) { + if(Quoted == false) return false; + if(*p != '\'' && *p != '\"') return false; + + while(*p == '\'' || *p == '\"') { + char x = *p++; + while(*p && *p++ != x); + } + return true; +} + +} + +#endif diff --git a/libsnes/nall/string/variadic.hpp b/libsnes/nall/string/variadic.hpp new file mode 100644 index 0000000..2eeaaf3 --- /dev/null +++ b/libsnes/nall/string/variadic.hpp @@ -0,0 +1,96 @@ +#ifndef NALL_STRING_VARIADIC_HPP +#define NALL_STRING_VARIADIC_HPP + +namespace nall { + +template +inline void print(T1 const & v1) +{ printf("%s", (const char*)string(v1)); } + +template +inline void print(T1 const & v1, + T2 const & v2) +{ printf("%s", (const char*)string(v1, v2)); } + +template +inline void print(T1 const & v1, + T2 const & v2, + T3 const & v3) +{ printf("%s", (const char*)string(v1, v2, v3)); } + +template +inline void print(T1 const & v1, + T2 const & v2, + T3 const & v3, + T4 const & v4) +{ printf("%s", (const char*)string(v1, v2, v3, v4)); } + +template +inline void print(T1 const & v1, + T2 const & v2, + T3 const & v3, + T4 const & v4, + T5 const & v5) +{ printf("%s", (const char*)string(v1, v2, v3, v4, v5)); } + +template +inline void print(T1 const & v1, + T2 const & v2, + T3 const & v3, + T4 const & v4, + T5 const & v5, + T6 const & v6) +{ printf("%s", (const char*)string(v1, v2, v3, v4, v5, v6)); } + +template +inline void print(T1 const & v1, + T2 const & v2, + T3 const & v3, + T4 const & v4, + T5 const & v5, + T6 const & v6, + T7 const & v7) +{ printf("%s", (const char*)string(v1, v2, v3, v4, v5, v6, v7)); } + +template +inline void print(T1 const & v1, + T2 const & v2, + T3 const & v3, + T4 const & v4, + T5 const & v5, + T6 const & v6, + T7 const & v7, + T8 const & v8) +{ printf("%s", (const char*)string(v1, v2, v3, v4, v5, v6, v7, v8)); } + +} + +#endif diff --git a/libsnes/nall/string/wrapper.hpp b/libsnes/nall/string/wrapper.hpp new file mode 100644 index 0000000..4ba0811 --- /dev/null +++ b/libsnes/nall/string/wrapper.hpp @@ -0,0 +1,12 @@ +#ifndef NALL_STRING_WRAPPER_HPP +#define NALL_STRING_WRAPPER_HPP + +namespace nall { + +bool string::wildcard(const char *str) const { return nall::wildcard(data, str); } +bool string::iwildcard(const char *str) const { return nall::iwildcard(data, str); } + +} + +#endif + diff --git a/libsnes/nall/string/xml.hpp b/libsnes/nall/string/xml.hpp new file mode 100644 index 0000000..39ef6b9 --- /dev/null +++ b/libsnes/nall/string/xml.hpp @@ -0,0 +1,255 @@ +//XML v1.0 subset parser +//revision 0.01 + +namespace nall { +namespace XML { + +struct Node { + string name; + string data; + bool attribute; + array children; + + inline bool exists() const { + return !name.empty(); + } + + inline bool isName(char c) const { + if(c >= 'A' && c <= 'Z') return true; + if(c >= 'a' && c <= 'z') return true; + if(c >= '0' && c <= '9') return true; + if(c == '.' || c == '_') return true; + if(c == '?') return true; + return false; + } + + inline bool isWhitespace(char c) const { + if(c == ' ' || c == '\t') return true; + if(c == '\r' || c == '\n') return true; + return false; + } + + //copy part of string from source document into target string; decode markup while copying + inline void copy(string &target, const char *source, unsigned length) { + target.reserve(length + 1); + + #if defined(NALL_XML_LITERAL) + memcpy(target(), source, length); + target[length] = 0; + return; + #endif + + char *output = target(); + while(length) { + if(*source == '&') { + if(!memcmp(source, "<", 4)) { *output++ = '<'; source += 4; length -= 4; continue; } + if(!memcmp(source, ">", 4)) { *output++ = '>'; source += 4; length -= 4; continue; } + if(!memcmp(source, "&", 5)) { *output++ = '&'; source += 5; length -= 5; continue; } + if(!memcmp(source, "'", 6)) { *output++ = '\''; source += 6; length -= 6; continue; } + if(!memcmp(source, """, 6)) { *output++ = '\"'; source += 6; length -= 6; continue; } + } + + if(attribute == false && source[0] == '<' && source[1] == '!') { + //comment + if(!memcmp(source, "", 3)) source++, length--; + source += 3, length -= 3; + continue; + } + + //CDATA + if(!memcmp(source, "", 3)) *output++ = *source++, length--; + source += 3, length -= 3; + continue; + } + } + + *output++ = *source++, length--; + } + *output = 0; + } + + inline bool parseExpression(const char *&p) { + if(*(p + 1) != '!') return false; + + //comment + if(!memcmp(p, "", 3)) p++; + if(!*p) throw "unclosed comment"; + p += 3; + return true; + } + + //CDATA + if(!memcmp(p, "", 3)) p++; + if(!*p) throw "unclosed CDATA"; + p += 3; + return true; + } + + //DOCTYPE + if(!memcmp(p, "') counter--; + } while(counter); + return true; + } + + return false; + } + + //returns true if tag closes itself (); false if not () + inline bool parseHead(const char *&p) { + //parse name + const char *nameStart = ++p; //skip '<' + while(isName(*p)) p++; + const char *nameEnd = p; + copy(name, nameStart, nameEnd - nameStart); + if(name.empty()) throw "missing element name"; + + //parse attributes + while(*p) { + while(isWhitespace(*p)) p++; + if(!*p) throw "unclosed attribute"; + if(*p == '?' || *p == '/' || *p == '>') break; + + //parse attribute name + Node *attribute = new Node; + children.append(attribute); + attribute->attribute = true; + + const char *nameStart = p; + while(isName(*p)) p++; + const char *nameEnd = p; + copy(attribute->name, nameStart, nameEnd - nameStart); + if(attribute->name.empty()) throw "missing attribute name"; + + //parse attribute data + if(*p++ != '=') throw "missing attribute value"; + char terminal = *p++; + if(terminal != '\'' && terminal != '\"') throw "attribute value not quoted"; + const char *dataStart = p; + while(*p && *p != terminal) p++; + if(!*p) throw "missing attribute data terminal"; + const char *dataEnd = p++; //skip closing terminal + + copy(attribute->data, dataStart, dataEnd - dataStart); + } + + //parse closure + if(*p == '?' && *(p + 1) == '>') { p += 2; return true; } + if(*p == '/' && *(p + 1) == '>') { p += 2; return true; } + if(*p == '>') { p += 1; return false; } + throw "invalid element tag"; + } + + //parse element and all of its child elements + inline void parseElement(const char *&p) { + Node *node = new Node; + children.append(node); + if(node->parseHead(p) == true) return; + node->parse(p); + } + + //return true if matches this node's name + inline bool parseClosureElement(const char *&p) { + if(p[0] != '<' || p[1] != '/') return false; + p += 2; + const char *nameStart = p; + while(*p && *p != '>') p++; + if(*p != '>') throw "unclosed closure element"; + const char *nameEnd = p++; + if(memcmp(name, nameStart, nameEnd - nameStart)) throw "closure element name mismatch"; + return true; + } + + //parse contents of an element + inline void parse(const char *&p) { + const char *dataStart = p, *dataEnd = p; + + while(*p) { + while(*p && *p != '<') p++; + if(!*p) break; + dataEnd = p; + if(parseClosureElement(p) == true) break; + if(parseExpression(p) == true) continue; + parseElement(p); + } + + copy(data, dataStart, dataEnd - dataStart); + } + + inline void reset() { + foreach(child, children) delete child; + children.reset(); + } + + struct iterator { + inline bool operator!=(const iterator &source) const { return index != source.index; } + inline Node& operator*() { return *node.children[index]; } + inline iterator& operator++() { index++; return *this; } + inline iterator(const Node &node, unsigned index) : node(node), index(index) {} + private: + const Node &node; + unsigned index; + }; + + inline iterator begin() { return iterator(*this, 0); } + inline iterator end() { return iterator(*this, children.size()); } + inline const iterator begin() const { return iterator(*this, 0); } + inline const iterator end() const { return iterator(*this, children.size()); } + unsigned size() const { return children.size(); } + + inline Node& operator[](unsigned i) { + return *children[i]; + } + + inline Node& operator[](const char *name) { + foreach(node, children) { + if(node->name == name) return *node; + } + static Node node; + return node; + } + + inline Node() : attribute(false) {} + inline ~Node() { reset(); } + + private: + Node(const Node&); + Node& operator=(const Node&); +}; + +struct Document : Node { + string error; + + inline bool load(const char *document) { + if(document == 0) return false; + reset(); + try { + parse(document); + } catch(const char *error) { + reset(); + this->error = error; + return false; + } + return true; + } + + inline Document() {} + inline Document(const char *document) { load(document); } +}; + +} + +template<> struct has_size { enum { value = true }; }; + +} diff --git a/libsnes/nall/ups.hpp b/libsnes/nall/ups.hpp new file mode 100644 index 0000000..f255ecb --- /dev/null +++ b/libsnes/nall/ups.hpp @@ -0,0 +1,190 @@ +#ifndef NALL_UPS_HPP +#define NALL_UPS_HPP + +#include + +#include +#include +#include +#include + +namespace nall { + class ups { + public: + enum result { + ok, + patch_unreadable, + patch_unwritable, + patch_invalid, + input_invalid, + output_invalid, + patch_crc32_invalid, + input_crc32_invalid, + output_crc32_invalid, + }; + + ups::result create(const char *patch_fn, const uint8_t *x_data, unsigned x_size, const uint8_t *y_data, unsigned y_size) { + if(!fp.open(patch_fn, file::mode_write)) return patch_unwritable; + + crc32 = ~0; + uint32_t x_crc32 = crc32_calculate(x_data, x_size); + uint32_t y_crc32 = crc32_calculate(y_data, y_size); + + //header + write('U'); + write('P'); + write('S'); + write('1'); + encptr(x_size); + encptr(y_size); + + //body + unsigned max_size = max(x_size, y_size); + unsigned relative = 0; + for(unsigned i = 0; i < max_size;) { + uint8_t x = i < x_size ? x_data[i] : 0x00; + uint8_t y = i < y_size ? y_data[i] : 0x00; + + if(x == y) { + i++; + continue; + } + + encptr(i++ - relative); + write(x ^ y); + + while(true) { + if(i >= max_size) { + write(0x00); + break; + } + + x = i < x_size ? x_data[i] : 0x00; + y = i < y_size ? y_data[i] : 0x00; + i++; + write(x ^ y); + if(x == y) break; + } + + relative = i; + } + + //footer + for(unsigned i = 0; i < 4; i++) write(x_crc32 >> (i << 3)); + for(unsigned i = 0; i < 4; i++) write(y_crc32 >> (i << 3)); + uint32_t p_crc32 = ~crc32; + for(unsigned i = 0; i < 4; i++) write(p_crc32 >> (i << 3)); + + fp.close(); + return ok; + } + + ups::result apply(const uint8_t *p_data, unsigned p_size, const uint8_t *x_data, unsigned x_size, uint8_t *&y_data, unsigned &y_size) { + if(p_size < 18) return patch_invalid; + p_buffer = p_data; + + crc32 = ~0; + + //header + if(read() != 'U') return patch_invalid; + if(read() != 'P') return patch_invalid; + if(read() != 'S') return patch_invalid; + if(read() != '1') return patch_invalid; + + unsigned px_size = decptr(); + unsigned py_size = decptr(); + + //mirror + if(x_size != px_size && x_size != py_size) return input_invalid; + y_size = (x_size == px_size) ? py_size : px_size; + y_data = new uint8_t[y_size](); + + for(unsigned i = 0; i < x_size && i < y_size; i++) y_data[i] = x_data[i]; + for(unsigned i = x_size; i < y_size; i++) y_data[i] = 0x00; + + //body + unsigned relative = 0; + while(p_buffer < p_data + p_size - 12) { + relative += decptr(); + + while(true) { + uint8_t x = read(); + if(x && relative < y_size) { + uint8_t y = relative < x_size ? x_data[relative] : 0x00; + y_data[relative] = x ^ y; + } + relative++; + if(!x) break; + } + } + + //footer + unsigned px_crc32 = 0, py_crc32 = 0, pp_crc32 = 0; + for(unsigned i = 0; i < 4; i++) px_crc32 |= read() << (i << 3); + for(unsigned i = 0; i < 4; i++) py_crc32 |= read() << (i << 3); + uint32_t p_crc32 = ~crc32; + for(unsigned i = 0; i < 4; i++) pp_crc32 |= read() << (i << 3); + + uint32_t x_crc32 = crc32_calculate(x_data, x_size); + uint32_t y_crc32 = crc32_calculate(y_data, y_size); + + if(px_size != py_size) { + if(x_size == px_size && x_crc32 != px_crc32) return input_crc32_invalid; + if(x_size == py_size && x_crc32 != py_crc32) return input_crc32_invalid; + if(y_size == px_size && y_crc32 != px_crc32) return output_crc32_invalid; + if(y_size == py_size && y_crc32 != py_crc32) return output_crc32_invalid; + } else { + if(x_crc32 != px_crc32 && x_crc32 != py_crc32) return input_crc32_invalid; + if(y_crc32 != px_crc32 && y_crc32 != py_crc32) return output_crc32_invalid; + if(x_crc32 == y_crc32 && px_crc32 != py_crc32) return output_crc32_invalid; + if(x_crc32 != y_crc32 && px_crc32 == py_crc32) return output_crc32_invalid; + } + + if(p_crc32 != pp_crc32) return patch_crc32_invalid; + return ok; + } + + private: + file fp; + uint32_t crc32; + const uint8_t *p_buffer; + + uint8_t read() { + uint8_t n = *p_buffer++; + crc32 = crc32_adjust(crc32, n); + return n; + } + + void write(uint8_t n) { + fp.write(n); + crc32 = crc32_adjust(crc32, n); + } + + void encptr(uint64_t offset) { + while(true) { + uint64_t x = offset & 0x7f; + offset >>= 7; + if(offset == 0) { + write(0x80 | x); + break; + } + write(x); + offset--; + } + } + + uint64_t decptr() { + uint64_t offset = 0, shift = 1; + while(true) { + uint8_t x = read(); + offset += (x & 0x7f) * shift; + if(x & 0x80) break; + shift <<= 7; + offset += shift; + } + return offset; + } + }; +} + +#endif diff --git a/libsnes/nall/utf8.hpp b/libsnes/nall/utf8.hpp new file mode 100644 index 0000000..db65610 --- /dev/null +++ b/libsnes/nall/utf8.hpp @@ -0,0 +1,96 @@ +#ifndef NALL_UTF8_HPP +#define NALL_UTF8_HPP + +//UTF-8 <> UTF-16 conversion +//used only for Win32; Linux, etc use UTF-8 internally + +#if defined(_WIN32) + +#undef UNICODE +#undef _WIN32_WINNT +#undef NOMINMAX +#define UNICODE +#define _WIN32_WINNT 0x0501 +#define NOMINMAX +#include +#include +#undef interface + +namespace nall { + //UTF-8 to UTF-16 + class utf16_t { + public: + operator wchar_t*() { + return buffer; + } + + operator const wchar_t*() const { + return buffer; + } + + utf16_t(const char *s = "") { + if(!s) s = ""; + unsigned length = MultiByteToWideChar(CP_UTF8, 0, s, -1, 0, 0); + buffer = new wchar_t[length + 1](); + MultiByteToWideChar(CP_UTF8, 0, s, -1, buffer, length); + } + + ~utf16_t() { + delete[] buffer; + } + + private: + wchar_t *buffer; + }; + + //UTF-16 to UTF-8 + class utf8_t { + public: + operator char*() { + return buffer; + } + + operator const char*() const { + return buffer; + } + + utf8_t(const wchar_t *s = L"") { + if(!s) s = L""; + unsigned length = WideCharToMultiByte(CP_UTF8, 0, s, -1, 0, 0, (const char*)0, (BOOL*)0); + buffer = new char[length + 1](); + WideCharToMultiByte(CP_UTF8, 0, s, -1, buffer, length, (const char*)0, (BOOL*)0); + } + + ~utf8_t() { + delete[] buffer; + } + + utf8_t(const utf8_t & s){ + unsigned length = _mbslen((unsigned char const *)s.buffer); + buffer = new char[length + 1](); + memcpy(buffer, s.buffer, length + 1); + } + + utf8_t& operator=(const utf8_t & s){ + delete [] buffer; + unsigned length = _mbslen((unsigned char const *)s.buffer); + buffer = new char[length + 1](); + memcpy(buffer, s.buffer, length + 1); + } + private: + char *buffer; + }; + + inline void utf8_args(int &argc, char **&argv) { + wchar_t **wargv = CommandLineToArgvW(GetCommandLineW(), &argc); + argv = new char*[argc]; + for(unsigned i = 0; i < argc; i++) { + argv[i] = new char[_MAX_PATH]; + strcpy(argv[i], nall::utf8_t(wargv[i])); + } + } +} + +#endif //if defined(_WIN32) + +#endif diff --git a/libsnes/nall/utility.hpp b/libsnes/nall/utility.hpp new file mode 100644 index 0000000..2be740e --- /dev/null +++ b/libsnes/nall/utility.hpp @@ -0,0 +1,38 @@ +#ifndef NALL_UTILITY_HPP +#define NALL_UTILITY_HPP + +#include + +namespace nall { + template struct enable_if { typedef T type; }; + template struct enable_if {}; + template struct mp_enable_if : enable_if {}; + + template inline void swap(T & x, T & y) { + T tmp = x; + x = y; + y = tmp; + } + + template struct base_from_member { + T value; + base_from_member(T value_) : value(value_) {} + }; + + template class optional { + bool valid; + T value; + public: + inline operator bool() const { return valid; } + inline const T& operator()() const { if(!valid) throw; return value; } + inline optional(bool valid, const T &value) : valid(valid), value(value) {} + }; + + template inline T* allocate(unsigned size, const T &value) { + T *array = new T[size]; + for(unsigned i = 0; i < size; i++) array[i] = value; + return array; + } +} + +#endif diff --git a/libsnes/nall/varint.hpp b/libsnes/nall/varint.hpp new file mode 100644 index 0000000..a5f1636 --- /dev/null +++ b/libsnes/nall/varint.hpp @@ -0,0 +1,117 @@ +#ifndef NALL_VARINT_HPP +#define NALL_VARINT_HPP + +#include +#include + +namespace nall { + template class uint_t { + private: + unsigned data; + + public: + inline operator unsigned() const { return data; } + inline unsigned operator ++(int) { unsigned r = data; data = uclip(data + 1); return r; } + inline unsigned operator --(int) { unsigned r = data; data = uclip(data - 1); return r; } + inline unsigned operator ++() { return data = uclip(data + 1); } + inline unsigned operator --() { return data = uclip(data - 1); } + inline unsigned operator =(const unsigned i) { return data = uclip(i); } + inline unsigned operator |=(const unsigned i) { return data = uclip(data | i); } + inline unsigned operator ^=(const unsigned i) { return data = uclip(data ^ i); } + inline unsigned operator &=(const unsigned i) { return data = uclip(data & i); } + inline unsigned operator<<=(const unsigned i) { return data = uclip(data << i); } + inline unsigned operator>>=(const unsigned i) { return data = uclip(data >> i); } + inline unsigned operator +=(const unsigned i) { return data = uclip(data + i); } + inline unsigned operator -=(const unsigned i) { return data = uclip(data - i); } + inline unsigned operator *=(const unsigned i) { return data = uclip(data * i); } + inline unsigned operator /=(const unsigned i) { return data = uclip(data / i); } + inline unsigned operator %=(const unsigned i) { return data = uclip(data % i); } + + inline uint_t() : data(0) {} + inline uint_t(const unsigned i) : data(uclip(i)) {} + }; + + template class int_t { + private: + signed data; + + public: + inline operator signed() const { return data; } + inline signed operator ++(int) { signed r = data; data = sclip(data + 1); return r; } + inline signed operator --(int) { signed r = data; data = sclip(data - 1); return r; } + inline signed operator ++() { return data = sclip(data + 1); } + inline signed operator --() { return data = sclip(data - 1); } + inline signed operator =(const signed i) { return data = sclip(i); } + inline signed operator |=(const signed i) { return data = sclip(data | i); } + inline signed operator ^=(const signed i) { return data = sclip(data ^ i); } + inline signed operator &=(const signed i) { return data = sclip(data & i); } + inline signed operator<<=(const signed i) { return data = sclip(data << i); } + inline signed operator>>=(const signed i) { return data = sclip(data >> i); } + inline signed operator +=(const signed i) { return data = sclip(data + i); } + inline signed operator -=(const signed i) { return data = sclip(data - i); } + inline signed operator *=(const signed i) { return data = sclip(data * i); } + inline signed operator /=(const signed i) { return data = sclip(data / i); } + inline signed operator %=(const signed i) { return data = sclip(data % i); } + + inline int_t() : data(0) {} + inline int_t(const signed i) : data(sclip(i)) {} + }; + + class varuint_t { + private: + unsigned data; + unsigned mask; + + public: + inline operator unsigned() const { return data; } + inline unsigned operator ++(int) { unsigned r = data; data = (data + 1) & mask; return r; } + inline unsigned operator --(int) { unsigned r = data; data = (data - 1) & mask; return r; } + inline unsigned operator ++() { return data = (data + 1) & mask; } + inline unsigned operator --() { return data = (data - 1) & mask; } + inline unsigned operator =(const unsigned i) { return data = (i) & mask; } + inline unsigned operator |=(const unsigned i) { return data = (data | i) & mask; } + inline unsigned operator ^=(const unsigned i) { return data = (data ^ i) & mask; } + inline unsigned operator &=(const unsigned i) { return data = (data & i) & mask; } + inline unsigned operator<<=(const unsigned i) { return data = (data << i) & mask; } + inline unsigned operator>>=(const unsigned i) { return data = (data >> i) & mask; } + inline unsigned operator +=(const unsigned i) { return data = (data + i) & mask; } + inline unsigned operator -=(const unsigned i) { return data = (data - i) & mask; } + inline unsigned operator *=(const unsigned i) { return data = (data * i) & mask; } + inline unsigned operator /=(const unsigned i) { return data = (data / i) & mask; } + inline unsigned operator %=(const unsigned i) { return data = (data % i) & mask; } + + inline void bits(unsigned bits) { mask = (1U << (bits - 1)) + ((1U << (bits - 1)) - 1); data &= mask; } + inline varuint_t() : data(0), mask(~0U) {} + inline varuint_t(const unsigned i) : data(i), mask(~0U) {} + }; + + class varuintmax_t { + private: + uintmax_t data; + uintmax_t mask; + + public: + inline operator uintmax_t() const { return data; } + inline uintmax_t operator ++(int) { uintmax_t r = data; data = (data + 1) & mask; return r; } + inline uintmax_t operator --(int) { uintmax_t r = data; data = (data - 1) & mask; return r; } + inline uintmax_t operator ++() { return data = (data + 1) & mask; } + inline uintmax_t operator --() { return data = (data - 1) & mask; } + inline uintmax_t operator =(const uintmax_t i) { return data = (i) & mask; } + inline uintmax_t operator |=(const uintmax_t i) { return data = (data | i) & mask; } + inline uintmax_t operator ^=(const uintmax_t i) { return data = (data ^ i) & mask; } + inline uintmax_t operator &=(const uintmax_t i) { return data = (data & i) & mask; } + inline uintmax_t operator<<=(const uintmax_t i) { return data = (data << i) & mask; } + inline uintmax_t operator>>=(const uintmax_t i) { return data = (data >> i) & mask; } + inline uintmax_t operator +=(const uintmax_t i) { return data = (data + i) & mask; } + inline uintmax_t operator -=(const uintmax_t i) { return data = (data - i) & mask; } + inline uintmax_t operator *=(const uintmax_t i) { return data = (data * i) & mask; } + inline uintmax_t operator /=(const uintmax_t i) { return data = (data / i) & mask; } + inline uintmax_t operator %=(const uintmax_t i) { return data = (data % i) & mask; } + + inline void bits(unsigned bits) { mask = (1ULL << (bits - 1)) + ((1ULL << (bits - 1)) - 1); data &= mask; } + inline varuintmax_t() : data(0), mask(~0ULL) {} + inline varuintmax_t(const uintmax_t i) : data(i), mask(~0ULL) {} + }; +} + +#endif diff --git a/libsnes/nall/vector.hpp b/libsnes/nall/vector.hpp new file mode 100644 index 0000000..264c66a --- /dev/null +++ b/libsnes/nall/vector.hpp @@ -0,0 +1,271 @@ +#ifndef NALL_VECTOR_HPP +#define NALL_VECTOR_HPP + +#include +#include +#include +#include +#include +#include +#include + +namespace nall { + //linear_vector + //memory: O(capacity * 2) + // + //linear_vector uses placement new + manual destructor calls to create a + //contiguous block of memory for all objects. accessing individual elements + //is fast, though resizing the array incurs significant overhead. + //reserve() overhead is reduced from quadratic time to amortized constant time + //by resizing twice as much as requested. + // + //if objects hold memory address references to themselves (introspection), a + //valid copy constructor will be needed to keep pointers valid. + + template class linear_vector { + protected: + T *pool; + unsigned poolsize, objectsize; + + public: + unsigned size() const { return objectsize; } + unsigned capacity() const { return poolsize; } + + void reset() { + if(pool) { + for(unsigned i = 0; i < objectsize; i++) pool[i].~T(); + free(pool); + } + pool = 0; + poolsize = 0; + objectsize = 0; + } + + void reserve(unsigned newsize) { + newsize = bit::round(newsize); //round to nearest power of two (for amortized growth) + + T *poolcopy = (T*)calloc(newsize, sizeof(T)); + for(unsigned i = 0; i < min(objectsize, newsize); i++) new(poolcopy + i) T(pool[i]); + for(unsigned i = 0; i < objectsize; i++) pool[i].~T(); + free(pool); + pool = poolcopy; + poolsize = newsize; + objectsize = min(objectsize, newsize); + } + + void resize(unsigned newsize) { + if(newsize > poolsize) reserve(newsize); + + if(newsize < objectsize) { + //vector is shrinking; destroy excess objects + for(unsigned i = newsize; i < objectsize; i++) pool[i].~T(); + } else if(newsize > objectsize) { + //vector is expanding; allocate new objects + for(unsigned i = objectsize; i < newsize; i++) new(pool + i) T; + } + + objectsize = newsize; + } + + void append(const T data) { + if(objectsize + 1 > poolsize) reserve(objectsize + 1); + new(pool + objectsize++) T(data); + } + + template void insert(unsigned index, const U list) { + linear_vector merged; + for(unsigned i = 0; i < index; i++) merged.append(pool[i]); + foreach(item, list) merged.append(item); + for(unsigned i = index; i < objectsize; i++) merged.append(pool[i]); + operator=(merged); + } + + void insert(unsigned index, const T item) { + insert(index, linear_vector(item)); + } + + void remove(unsigned index, unsigned count = 1) { + for(unsigned i = index; count + i < objectsize; i++) { + pool[i] = pool[count + i]; + } + if(count + index >= objectsize) resize(index); //every element >= index was removed + else resize(objectsize - count); + } + + inline T& operator[](unsigned index) { + if(index >= objectsize) resize(index + 1); + return pool[index]; + } + + inline const T& operator[](unsigned index) const { + if(index >= objectsize) throw "vector[] out of bounds"; + return pool[index]; + } + + //copy + inline linear_vector& operator=(const linear_vector &source) { + reset(); + reserve(source.capacity()); + resize(source.size()); + for(unsigned i = 0; i < source.size(); i++) operator[](i) = source.operator[](i); + return *this; + } + + linear_vector(const linear_vector &source) : pool(0), poolsize(0), objectsize(0) { + operator=(source); + } + + //construction + linear_vector() : pool(0), poolsize(0), objectsize(0) { + } + + linear_vector(std::initializer_list list) : pool(0), poolsize(0), objectsize(0) { + for(const T *p = list.begin(); p != list.end(); ++p) append(*p); + } + + ~linear_vector() { + reset(); + } + + //iteration + T* begin() { return &pool[0]; } + T* end() { return &pool[objectsize]; } + const T* begin() const { return &pool[0]; } + const T* end() const { return &pool[objectsize]; } + }; + + //pointer_vector + //memory: O(1) + // + //pointer_vector keeps an array of pointers to each vector object. this adds + //significant overhead to individual accesses, but allows for optimal memory + //utilization. + // + //by guaranteeing that the base memory address of each objects never changes, + //this avoids the need for an object to have a valid copy constructor. + + template class pointer_vector { + protected: + T **pool; + unsigned poolsize, objectsize; + + public: + unsigned size() const { return objectsize; } + unsigned capacity() const { return poolsize; } + + void reset() { + if(pool) { + for(unsigned i = 0; i < objectsize; i++) { if(pool[i]) delete pool[i]; } + free(pool); + } + pool = 0; + poolsize = 0; + objectsize = 0; + } + + void reserve(unsigned newsize) { + newsize = bit::round(newsize); //round to nearest power of two (for amortized growth) + + for(unsigned i = newsize; i < objectsize; i++) { + if(pool[i]) { delete pool[i]; pool[i] = 0; } + } + + pool = (T**)realloc(pool, newsize * sizeof(T*)); + for(unsigned i = poolsize; i < newsize; i++) pool[i] = 0; + poolsize = newsize; + objectsize = min(objectsize, newsize); + } + + void resize(unsigned newsize) { + if(newsize > poolsize) reserve(newsize); + + for(unsigned i = newsize; i < objectsize; i++) { + if(pool[i]) { delete pool[i]; pool[i] = 0; } + } + + objectsize = newsize; + } + + void append(const T data) { + if(objectsize + 1 > poolsize) reserve(objectsize + 1); + pool[objectsize++] = new T(data); + } + + template void insert(unsigned index, const U list) { + pointer_vector merged; + for(unsigned i = 0; i < index; i++) merged.append(*pool[i]); + foreach(item, list) merged.append(item); + for(unsigned i = index; i < objectsize; i++) merged.append(*pool[i]); + operator=(merged); + } + + void insert(unsigned index, const T item) { + insert(index, pointer_vector(item)); + } + + void remove(unsigned index, unsigned count = 1) { + for(unsigned i = index; count + i < objectsize; i++) { + *pool[i] = *pool[count + i]; + } + if(count + index >= objectsize) resize(index); //every element >= index was removed + else resize(objectsize - count); + } + + inline T& operator[](unsigned index) { + if(index >= objectsize) resize(index + 1); + if(!pool[index]) pool[index] = new T; + return *pool[index]; + } + + inline const T& operator[](unsigned index) const { + if(index >= objectsize || !pool[index]) throw "vector[] out of bounds"; + return *pool[index]; + } + + //copy + inline pointer_vector& operator=(const pointer_vector &source) { + reset(); + reserve(source.capacity()); + resize(source.size()); + for(unsigned i = 0; i < source.size(); i++) operator[](i) = source.operator[](i); + return *this; + } + + pointer_vector(const pointer_vector &source) : pool(0), poolsize(0), objectsize(0) { + operator=(source); + } + + //construction + pointer_vector() : pool(0), poolsize(0), objectsize(0) { + } + + pointer_vector(std::initializer_list list) : pool(0), poolsize(0), objectsize(0) { + for(const T *p = list.begin(); p != list.end(); ++p) append(*p); + } + + ~pointer_vector() { + reset(); + } + + //iteration + struct iterator { + bool operator!=(const iterator &source) const { return index != source.index; } + T& operator*() { return vector.operator[](index); } + iterator& operator++() { index++; return *this; } + iterator(const pointer_vector &vector, unsigned index) : vector(vector), index(index) {} + private: + const pointer_vector &vector; + unsigned index; + }; + + iterator begin() { return iterator(*this, 0); } + iterator end() { return iterator(*this, objectsize); } + const iterator begin() const { return iterator(*this, 0); } + const iterator end() const { return iterator(*this, objectsize); } + }; + + template struct has_size< linear_vector > { enum { value = true }; }; + template struct has_size< pointer_vector > { enum { value = true }; }; +} + +#endif diff --git a/libsnes/snes/Makefile b/libsnes/snes/Makefile new file mode 100644 index 0000000..ea509a6 --- /dev/null +++ b/libsnes/snes/Makefile @@ -0,0 +1,108 @@ +snes_objects := libco +snes_objects += snes-interface snes-system snes-controller +snes_objects += snes-cartridge snes-cheat +snes_objects += snes-memory snes-cpucore snes-smpcore +snes_objects += snes-cpu snes-smp snes-dsp snes-ppu +snes_objects += snes-nss snes-icd2 snes-superfx snes-sa1 snes-necdsp snes-hitachidsp +snes_objects += snes-bsx snes-srtc snes-sdd1 snes-spc7110 snes-sufamiturbo +snes_objects += snes-obc1 snes-st0018 +snes_objects += snes-msu1 snes-link +snes_objects += $(gameboy_objects) +objects += $(snes_objects) + +ifeq ($(profile),accuracy) + extraflags += -DPROFILE_ACCURACY + snescpu := $(snes)/cpu + snessmp := $(snes)/smp + snesdsp := $(snes)/dsp + snesppu := $(snes)/ppu +else ifeq ($(profile),compatibility) + extraflags += -DPROFILE_COMPATIBILITY + snescpu := $(snes)/cpu + snessmp := $(snes)/smp + snesdsp := $(snes)/alt/dsp + snesppu := $(snes)/alt/ppu-compatibility +else ifeq ($(profile),performance) + extraflags += -DPROFILE_PERFORMANCE + snescpu := $(snes)/alt/cpu + snessmp := $(snes)/alt/smp + snesdsp := $(snes)/alt/dsp + snesppu := $(snes)/alt/ppu-performance +endif + +obj/libsnes.o: $(snes)/libsnes/libsnes.cpp $(snes)/libsnes/* + +obj/libco.o : libco/libco.c libco/* + +obj/snes-interface.o : $(snes)/interface/interface.cpp $(call rwildcard,$(snes)/interface) +obj/snes-system.o : $(snes)/system/system.cpp $(call rwildcard,$(snes)/system/) +obj/snes-controller.o: $(snes)/controller/controller.cpp $(call rwildcard,$(snes)/controller/) +obj/snes-memory.o : $(snes)/memory/memory.cpp $(call rwildcard,$(snes)/memory/) +obj/snes-cpucore.o : $(snes)/cpu/core/core.cpp $(call rwildcard,$(snes)/cpu/core/) +obj/snes-smpcore.o : $(snes)/smp/core/smp_core.cpp $(call rwildcard,$(snes)/smp/core/) +obj/snes-cpu.o : $(snescpu)/cpu.cpp $(call rwildcard,$(snescpu)/) +obj/snes-smp.o : $(snessmp)/smp.cpp $(call rwildcard,$(snessmp)/) +obj/snes-dsp.o : $(snesdsp)/dsp.cpp $(call rwildcard,$(snesdsp)/) +obj/snes-ppu.o : $(snesppu)/ppu.cpp $(call rwildcard,$(snesppu)/) +obj/snes-cartridge.o : $(snes)/cartridge/cartridge.cpp $(snes)/cartridge/* +obj/snes-cheat.o : $(snes)/cheat/cheat.cpp $(snes)/cheat/* + +obj/snes-nss.o : $(snes)/chip/nss/nss.cpp $(call rwildcard,$(snes)/chip/nss/) +obj/snes-icd2.o : $(snes)/chip/icd2/icd2.cpp $(call rwildcard,$(snes)/chip/icd2/) +obj/snes-superfx.o : $(snes)/chip/superfx/superfx.cpp $(call rwildcard,$(snes)/chip/superfx/) +obj/snes-sa1.o : $(snes)/chip/sa1/sa1.cpp $(call rwildcard,$(snes)/chip/sa1/) +obj/snes-necdsp.o : $(snes)/chip/necdsp/necdsp.cpp $(call rwildcard,$(snes)/chip/necdsp/) +obj/snes-hitachidsp.o : $(snes)/chip/hitachidsp/hitachidsp.cpp $(call rwildcard,$(snes)/chip/hitachidsp/) +obj/snes-bsx.o : $(snes)/chip/bsx/bsx.cpp $(snes)/chip/bsx/* +obj/snes-srtc.o : $(snes)/chip/srtc/srtc.cpp $(snes)/chip/srtc/* +obj/snes-sdd1.o : $(snes)/chip/sdd1/sdd1.cpp $(snes)/chip/sdd1/* +obj/snes-spc7110.o : $(snes)/chip/spc7110/spc7110.cpp $(snes)/chip/spc7110/* +obj/snes-obc1.o : $(snes)/chip/obc1/obc1.cpp $(snes)/chip/obc1/* +obj/snes-st0018.o : $(snes)/chip/st0018/st0018.cpp $(snes)/chip/st0018/* +obj/snes-msu1.o : $(snes)/chip/msu1/msu1.cpp $(snes)/chip/msu1/* +obj/snes-link.o : $(snes)/chip/link/link.cpp $(snes)/chip/link/* +obj/snes-sufamiturbo.o: $(snes)/chip/sufamiturbo/sufamiturbo.cpp $(snes)/chip/sufamiturbo/* + +########### +# library # +########### + +snes_objects := $(patsubst %,obj/%.o,$(snes_objects)) + +static-library: obj/libsnes.o $(snes_objects) + ar rcs out/libsnes.a obj/libsnes.o $(snes_objects) + +library: obj/libsnes.o $(snes_objects) +ifeq ($(platform),x) + ar rcs out/libsnes.a $(snes_objects) obj/libsnes.o + $(CXX) -o out/libsnes.so -fPIC -shared -Wl,-soname,libsnes.so.1 obj/libsnes.o $(snes_objects) -ldl -Wl,-version-script=link.T -Wl,-no-undefined +else ifeq ($(platform),xbsd) + ar rcs out/libsnes.a $(snes_objects) obj/libsnes.o + $(CXX) -o out/libsnes.so -fPIC -shared -Wl,-soname,libsnes.so.1 obj/libsnes.o $(snes_objects) -lc -Wl,-version-script=link.T -Wl,-no-undefined +else ifeq ($(platform),osx) + ar rcs out/libsnes.a $(snes_objects) obj/libsnes.o + $(CXX) -o out/libsnes.dylib -install_name $(prefix)/lib/libsnes.dylib -dynamiclib obj/libsnes.o $(snes_objects) $(link) +else ifeq ($(platform),win) + $(CXX) -o out/snes.dll -static-libgcc -static-libstdc++ -shared -Wl,--out-implib,libsnes.a obj/libsnes.o $(snes_objects) $(link) -Wl,-version-script=link.T -Wl,-no-undefined +endif + +library-install: +ifeq ($(platform),x) + install -D -m 755 out/libsnes.a $(DESTDIR)$(prefix)/lib/libsnes.a + install -D -m 755 out/libsnes.so $(DESTDIR)$(prefix)/lib/libsnes.so + install -D -m 644 snes/libsnes/libsnes.hpp $(DESTDIR)$(prefix)/include/libsnes.hpp + ldconfig -n $(DESTDIR)$(prefix)/lib +else ifeq ($(platform),osx) + cp out/libsnes.dylib $(DESTDIR)$(prefix)/lib/libsnes.dylib + cp snes/libsnes/libsnes.hpp $(DESTDIR)$(prefix)/include/libsnes.hpp +endif + +library-uninstall: +ifeq ($(platform),x) + rm $(DESTDIR)$(prefix)/lib/libsnes.a + rm $(DESTDIR)$(prefix)/lib/libsnes.so +else ifeq ($(platform),osx) + rm $(DESTDIR)$(prefix)/lib/libsnes.dylib + rm $(DESTDIR)$(prefix)/include/libsnes.hpp +endif + diff --git a/libsnes/snes/alt/cpu/cpu.cpp b/libsnes/snes/alt/cpu/cpu.cpp new file mode 100644 index 0000000..aeb7beb --- /dev/null +++ b/libsnes/snes/alt/cpu/cpu.cpp @@ -0,0 +1,194 @@ +#include + +#define CPU_CPP +namespace SNES { + +#if defined(DEBUGGER) + #include "debugger/debugger.cpp" + CPUDebugger cpu; +#else + CPU cpu; +#endif + +#include "serialization.cpp" +#include "dma.cpp" +#include "memory.cpp" +#include "mmio.cpp" +#include "timing.cpp" + +void CPU::step(unsigned clocks) { + smp.clock -= clocks * (uint64)smp.frequency; + ppu.clock -= clocks; + for(unsigned i = 0; i < coprocessors.size(); i++) { + Processor &chip = *coprocessors[i]; + chip.clock -= clocks * (uint64)chip.frequency; + } + input.port1->clock -= clocks * (uint64)input.port1->frequency; + input.port2->clock -= clocks * (uint64)input.port2->frequency; + synchronize_controllers(); +} + +void CPU::synchronize_smp() { + if(SMP::Threaded == true) { + if(smp.clock < 0) co_switch(smp.thread); + } else { + while(smp.clock < 0) smp.enter(); + } +} + +void CPU::synchronize_ppu() { + if(PPU::Threaded == true) { + if(ppu.clock < 0) co_switch(ppu.thread); + } else { + while(ppu.clock < 0) ppu.enter(); + } +} + +void CPU::synchronize_coprocessors() { + for(unsigned i = 0; i < coprocessors.size(); i++) { + Processor &chip = *coprocessors[i]; + if(chip.clock < 0) co_switch(chip.thread); + } +} + +void CPU::synchronize_controllers() { + if(input.port1->clock < 0) co_switch(input.port1->thread); + if(input.port2->clock < 0) co_switch(input.port2->thread); +} + +void CPU::Enter() { cpu.enter(); } + +void CPU::enter() { + while(true) { + if(scheduler.sync.i == Scheduler::SynchronizeMode::CPU) { + scheduler.sync.i = Scheduler::SynchronizeMode::All; + scheduler.exit(Scheduler::ExitReason::SynchronizeEvent); + } + + if(status.nmi_pending) { + status.nmi_pending = false; + regs.vector = (regs.e == false ? 0xffea : 0xfffa); + op_irq(); + } + + if(status.irq_pending) { + status.irq_pending = false; + regs.vector = (regs.e == false ? 0xffee : 0xfffe); + op_irq(); + } + + op_step(); + } +} + +alwaysinline void CPU::op_step() { + (this->*opcode_table[op_readpc()])(); +} + +static uint8 cpu_wram_reader(unsigned addr) { + return cpu.wram[addr]; +} + +static void cpu_wram_writer(unsigned addr, uint8 data) { + cpu.wram[addr] = data; +} + +void CPU::enable() { + function read(&CPU::mmio_read, (CPU*)&cpu); + function write(&CPU::mmio_write, (CPU*)&cpu); + + bus.map(Bus::MapMode::Direct, 0x00, 0x3f, 0x2140, 0x2183, read, write); + bus.map(Bus::MapMode::Direct, 0x80, 0xbf, 0x2140, 0x2183, read, write); + + bus.map(Bus::MapMode::Direct, 0x00, 0x3f, 0x4016, 0x4017, read, write); + bus.map(Bus::MapMode::Direct, 0x80, 0xbf, 0x4016, 0x4017, read, write); + + bus.map(Bus::MapMode::Direct, 0x00, 0x3f, 0x4200, 0x421f, read, write); + bus.map(Bus::MapMode::Direct, 0x80, 0xbf, 0x4200, 0x421f, read, write); + + bus.map(Bus::MapMode::Direct, 0x00, 0x3f, 0x4300, 0x437f, read, write); + bus.map(Bus::MapMode::Direct, 0x80, 0xbf, 0x4300, 0x437f, read, write); + + read = function(cpu_wram_reader); + write = function(cpu_wram_writer); + + bus.map(Bus::MapMode::Linear, 0x00, 0x3f, 0x0000, 0x1fff, read, write, 0x000000, 0x002000); + bus.map(Bus::MapMode::Linear, 0x80, 0xbf, 0x0000, 0x1fff, read, write, 0x000000, 0x002000); + bus.map(Bus::MapMode::Linear, 0x7e, 0x7f, 0x0000, 0xffff, read, write); +} + +void CPU::power() { + regs.a = 0x0000; + regs.x = 0x0000; + regs.y = 0x0000; + regs.s = 0x01ff; + + reset(); +} + +void CPU::reset() { + create(Enter, system.cpu_frequency); + coprocessors.reset(); + PPUcounter::reset(); + + regs.pc = 0x000000; + regs.x.h = 0x00; + regs.y.h = 0x00; + regs.s.h = 0x01; + regs.d = 0x0000; + regs.db = 0x00; + regs.p = 0x34; + regs.e = 1; + regs.mdr = 0x00; + regs.wai = false; + update_table(); + + regs.pc.l = bus.read(0xfffc); + regs.pc.h = bus.read(0xfffd); + regs.pc.b = 0x00; + + status.nmi_valid = false; + status.nmi_line = false; + status.nmi_transition = false; + status.nmi_pending = false; + + status.irq_valid = false; + status.irq_line = false; + status.irq_transition = false; + status.irq_pending = false; + + status.irq_lock = false; + status.hdma_pending = false; + + status.wram_addr = 0x000000; + + status.joypad_strobe_latch = 0; + + status.nmi_enabled = false; + status.virq_enabled = false; + status.hirq_enabled = false; + status.auto_joypad_poll_enabled = false; + + status.pio = 0xff; + + status.htime = 0x0000; + status.vtime = 0x0000; + + status.rom_speed = 8; + + status.joy1l = status.joy1h = 0x00; + status.joy2l = status.joy2h = 0x00; + status.joy3l = status.joy3h = 0x00; + status.joy4l = status.joy4h = 0x00; + + dma_reset(); +} + +CPU::CPU() : queue(512, bind( &CPU::queue_event, this )) { + PPUcounter::scanline = bind( &CPU::scanline, this ); +} + +CPU::~CPU() { +} + +} diff --git a/libsnes/snes/alt/cpu/cpu.hpp b/libsnes/snes/alt/cpu/cpu.hpp new file mode 100644 index 0000000..91f0dee --- /dev/null +++ b/libsnes/snes/alt/cpu/cpu.hpp @@ -0,0 +1,157 @@ +class CPU : public Processor, public CPUcore, public PPUcounter { +public: + uint8 wram[128 * 1024]; + + enum{ Threaded = true }; + array coprocessors; + alwaysinline void step(unsigned clocks); + alwaysinline void synchronize_smp(); + void synchronize_ppu(); + void synchronize_coprocessors(); + void synchronize_controllers(); + + uint8 pio(); + bool joylatch(); + bool interrupt_pending(); + uint8 port_read(uint8 port); + void port_write(uint8 port, uint8 data); + uint8 mmio_read(unsigned addr); + void mmio_write(unsigned addr, uint8 data); + + void op_io(); + debugvirtual uint8 op_read(unsigned addr); + debugvirtual void op_write(unsigned addr, uint8 data); + + void enter(); + void enable(); + void power(); + void reset(); + + void serialize(serializer&); + CPU(); + ~CPU(); + +private: + //cpu + static void Enter(); + debugvirtual void op_step(); + + //timing + struct QueueEvent { + enum{ + DramRefresh, + HdmaRun, + }; + }; + nall::priority_queue queue; + void queue_event(unsigned id); + void last_cycle(); + void add_clocks(unsigned clocks); + void scanline(); + void run_auto_joypad_poll(); + + //memory + unsigned speed(unsigned addr) const; + + //dma + bool dma_transfer_valid(uint8 bbus, unsigned abus); + bool dma_addr_valid(unsigned abus); + uint8 dma_read(unsigned abus); + void dma_write(bool valid, unsigned addr, uint8 data); + void dma_transfer(bool direction, uint8 bbus, unsigned abus); + uint8 dma_bbus(unsigned i, unsigned index); + unsigned dma_addr(unsigned i); + unsigned hdma_addr(unsigned i); + unsigned hdma_iaddr(unsigned i); + void dma_run(); + bool hdma_active_after(unsigned i); + void hdma_update(unsigned i); + void hdma_run(); + void hdma_init(); + void dma_reset(); + + //registers + uint8 port_data[4]; + + struct Channel { + bool dma_enabled; + bool hdma_enabled; + + bool direction; + bool indirect; + bool unused; + bool reverse_transfer; + bool fixed_transfer; + uint8 transfer_mode; + + uint8 dest_addr; + uint16 source_addr; + uint8 source_bank; + + union { + uint16 transfer_size; + uint16 indirect_addr; + }; + + uint8 indirect_bank; + uint16 hdma_addr; + uint8 line_counter; + uint8 unknown; + + bool hdma_completed; + bool hdma_do_transfer; + } channel[8]; + + struct Status { + bool nmi_valid; + bool nmi_line; + bool nmi_transition; + bool nmi_pending; + + bool irq_valid; + bool irq_line; + bool irq_transition; + bool irq_pending; + + bool irq_lock; + bool hdma_pending; + + unsigned wram_addr; + + bool joypad_strobe_latch; + + bool nmi_enabled; + bool virq_enabled; + bool hirq_enabled; + bool auto_joypad_poll_enabled; + + uint8 pio; + + uint8 wrmpya; + uint8 wrmpyb; + uint16 wrdiva; + uint8 wrdivb; + + uint16 htime; + uint16 vtime; + + unsigned rom_speed; + + uint16 rddiv; + uint16 rdmpy; + + uint8 joy1l, joy1h; + uint8 joy2l, joy2h; + uint8 joy3l, joy3h; + uint8 joy4l, joy4h; + } status; + + friend class CPUDebugger; +}; + +#if defined(DEBUGGER) + #include "debugger/debugger.hpp" + extern CPUDebugger cpu; +#else + extern CPU cpu; +#endif diff --git a/libsnes/snes/alt/cpu/debugger/debugger.cpp b/libsnes/snes/alt/cpu/debugger/debugger.cpp new file mode 100644 index 0000000..59423a5 --- /dev/null +++ b/libsnes/snes/alt/cpu/debugger/debugger.cpp @@ -0,0 +1,156 @@ +#ifdef CPU_CPP + +void CPUDebugger::op_step() { + bool break_event = false; + + usage[regs.pc] &= ~(UsageFlagM | UsageFlagX); + usage[regs.pc] |= UsageExec | (regs.p.m << 1) | (regs.p.x << 0); + opcode_pc = regs.pc; + + opcode_edge = true; + debugger.breakpoint_test(Debugger::Breakpoint::Source::CPUBus, Debugger::Breakpoint::Mode::Exec, regs.pc, 0x00); + if(step_event && step_event()) { + debugger.break_event = Debugger::BreakEvent::CPUStep; + scheduler.exit(Scheduler::ExitReason::DebuggerEvent); + } + opcode_edge = false; + + CPU::op_step(); + synchronize_smp(); +} + +uint8 CPUDebugger::op_read(uint32 addr) { + uint8 data = CPU::op_read(addr); + usage[addr] |= UsageRead; + debugger.breakpoint_test(Debugger::Breakpoint::Source::CPUBus, Debugger::Breakpoint::Mode::Read, addr, data); + return data; +} + +void CPUDebugger::op_write(uint32 addr, uint8 data) { + CPU::op_write(addr, data); + usage[addr] |= UsageWrite; + usage[addr] &= ~UsageExec; + debugger.breakpoint_test(Debugger::Breakpoint::Source::CPUBus, Debugger::Breakpoint::Mode::Write, addr, data); +} + +CPUDebugger::CPUDebugger() { + usage = new uint8[1 << 24](); + opcode_pc = 0x8000; + opcode_edge = false; +} + +CPUDebugger::~CPUDebugger() { + delete[] usage; +} + +bool CPUDebugger::property(unsigned id, string &name, string &value) { + unsigned n = 0; + + #define item(name_, value_) \ + if(id == n++) { \ + name = name_; \ + value = value_; \ + return true; \ + } + + //internal + item("S-CPU MDR", string("0x", hex<2>(regs.mdr))); + + //$2181-2183 + item("$2181-$2183", ""); + item("WRAM Address", string("0x", hex<6>(status.wram_addr))); + + //$4016 + item("$4016", ""); + item("Joypad Strobe Latch", status.joypad_strobe_latch); + + //$4200 + item("$4200", ""); + item("NMI Enable", status.nmi_enabled); + item("H-IRQ Enable", status.hirq_enabled); + item("V-IRQ Enable", status.virq_enabled); + item("Auto Joypad Poll", status.auto_joypad_poll_enabled); + + //$4201 + item("$4201", ""); + item("PIO", string("0x", hex<2>(status.pio))); + + //$4202 + item("$4202", ""); + item("Multiplicand", string("0x", hex<2>(status.wrmpya))); + + //$4203 + item("$4203", ""); + item("Multiplier", string("0x", hex<2>(status.wrmpyb))); + + //$4204-$4205 + item("$4204-$4205", ""); + item("Dividend", string("0x", hex<4>(status.wrdiva))); + + //$4206 + item("$4206", ""); + item("Divisor", string("0x", hex<2>(status.wrdivb))); + + //$4207-$4208 + item("$4207-$4208", ""); + item("H-Time", string("0x", hex<4>(status.htime))); + + //$4209-$420a + item("$4209-$420a", ""); + item("V-Time", string("0x", hex<4>(status.vtime))); + + //$420b + unsigned dma_enable = 0; + for(unsigned n = 0; n < 8; n++) dma_enable |= channel[n].dma_enabled << n; + + item("$420b", ""); + item("DMA Enable", string("0x", hex<2>(dma_enable))); + + //$420c + unsigned hdma_enable = 0; + for(unsigned n = 0; n < 8; n++) hdma_enable |= channel[n].hdma_enabled << n; + + item("$420c", ""); + item("HDMA Enable", string("0x", hex<2>(hdma_enable))); + + //$420d + item("$420d", ""); + item("FastROM Enable", status.rom_speed == 6); + + for(unsigned i = 0; i < 8; i++) { + item(string("DMA Channel ", i), ""); + + //$43x0 + item("Direction", channel[i].direction); + item("Indirect", channel[i].indirect); + item("Reverse Transfer", channel[i].reverse_transfer); + item("Fixed Transfer", channel[i].fixed_transfer); + item("Transfer Mode", (unsigned)channel[i].transfer_mode); + + //$43x1 + item("B-Bus Address", string("0x", hex<4>(channel[i].dest_addr))); + + //$43x2-$43x3 + item("A-Bus Address", string("0x", hex<4>(channel[i].source_addr))); + + //$43x4 + item("A-Bus Bank", string("0x", hex<2>(channel[i].source_bank))); + + //$43x5-$43x6 + item("Transfer Size / Indirect Address", string("0x", hex<4>(channel[i].transfer_size))); + + //$43x7 + item("Indirect Bank", string("0x", hex<2>(channel[i].indirect_bank))); + + //$43x8-$43x9 + item("Table Address", string("0x", hex<4>(channel[i].hdma_addr))); + + //$43xa + item("Line Counter", string("0x", hex<2>(channel[i].line_counter))); + } + + #undef item + return false; +} + +#endif diff --git a/libsnes/snes/alt/cpu/debugger/debugger.hpp b/libsnes/snes/alt/cpu/debugger/debugger.hpp new file mode 100644 index 0000000..31d6044 --- /dev/null +++ b/libsnes/snes/alt/cpu/debugger/debugger.hpp @@ -0,0 +1,24 @@ +class CPUDebugger : public CPU, public ChipDebugger { +public: + bool property(unsigned id, string &name, string &value); + + function step_event; + + enum Usage { + UsageRead = 0x80, + UsageWrite = 0x40, + UsageExec = 0x20, + UsageFlagM = 0x02, + UsageFlagX = 0x01, + }; + uint8 *usage; + uint32 opcode_pc; + bool opcode_edge; + + void op_step(); + uint8 op_read(uint32 addr); + void op_write(uint32 addr, uint8 data); + + CPUDebugger(); + ~CPUDebugger(); +}; diff --git a/libsnes/snes/alt/cpu/dma.cpp b/libsnes/snes/alt/cpu/dma.cpp new file mode 100644 index 0000000..addb6db --- /dev/null +++ b/libsnes/snes/alt/cpu/dma.cpp @@ -0,0 +1,204 @@ +#ifdef CPU_CPP + +bool CPU::dma_transfer_valid(uint8 bbus, unsigned abus) { + //transfers from WRAM to WRAM are invalid; chip only has one address bus + if(bbus == 0x80 && ((abus & 0xfe0000) == 0x7e0000 || (abus & 0x40e000) == 0x0000)) return false; + return true; +} + +bool CPU::dma_addr_valid(unsigned abus) { + //A-bus access to 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] + return true; +} + +uint8 CPU::dma_read(unsigned abus) { + if(dma_addr_valid(abus) == false) return 0x00; + return bus.read(abus); +} + +void CPU::dma_write(bool valid, unsigned addr, uint8 data) { + if(valid) bus.write(addr, data); +} + +void CPU::dma_transfer(bool direction, uint8 bbus, unsigned abus) { + if(direction == 0) { + uint8 data = dma_read(abus); + add_clocks(8); + dma_write(dma_transfer_valid(bbus, abus), 0x2100 | bbus, data); + } else { + uint8 data = dma_transfer_valid(bbus, abus) ? bus.read(0x2100 | bbus) : 0x00; + add_clocks(8); + dma_write(dma_addr_valid(abus), abus, data); + } +} + +uint8 CPU::dma_bbus(unsigned i, unsigned index) { + switch(channel[i].transfer_mode) { default: + case 0: return (channel[i].dest_addr); //0 + case 1: return (channel[i].dest_addr + (index & 1)); //0,1 + case 2: return (channel[i].dest_addr); //0,0 + case 3: return (channel[i].dest_addr + ((index >> 1) & 1)); //0,0,1,1 + case 4: return (channel[i].dest_addr + (index & 3)); //0,1,2,3 + case 5: return (channel[i].dest_addr + (index & 1)); //0,1,0,1 + case 6: return (channel[i].dest_addr); //0,0 [2] + case 7: return (channel[i].dest_addr + ((index >> 1) & 1)); //0,0,1,1 [3] + } +} + +unsigned CPU::dma_addr(unsigned i) { + unsigned result = (channel[i].source_bank << 16) | (channel[i].source_addr); + + if(channel[i].fixed_transfer == false) { + if(channel[i].reverse_transfer == false) { + channel[i].source_addr++; + } else { + channel[i].source_addr--; + } + } + + return result; +} + +unsigned CPU::hdma_addr(unsigned i) { + return (channel[i].source_bank << 16) | (channel[i].hdma_addr++); +} + +unsigned CPU::hdma_iaddr(unsigned i) { + return (channel[i].indirect_bank << 16) | (channel[i].indirect_addr++); +} + +void CPU::dma_run() { + add_clocks(16); + + for(unsigned i = 0; i < 8; i++) { + if(channel[i].dma_enabled == false) continue; + add_clocks(8); + + unsigned index = 0; + do { + dma_transfer(channel[i].direction, dma_bbus(i, index++), dma_addr(i)); + } while(channel[i].dma_enabled && --channel[i].transfer_size); + + channel[i].dma_enabled = false; + } + + status.irq_lock = true; +} + +bool CPU::hdma_active_after(unsigned i) { + for(unsigned n = i + 1; i < 8; i++) { + if(channel[i].hdma_enabled && !channel[i].hdma_completed) return true; + } + return false; +} + +void CPU::hdma_update(unsigned i) { + if((channel[i].line_counter & 0x7f) == 0) { + channel[i].line_counter = dma_read(hdma_addr(i)); + channel[i].hdma_completed = (channel[i].line_counter == 0); + channel[i].hdma_do_transfer = !channel[i].hdma_completed; + add_clocks(8); + + if(channel[i].indirect) { + channel[i].indirect_addr = dma_read(hdma_addr(i)) << 8; + add_clocks(8); + + //emulating this glitch causes a slight slowdown; only enable if needed + //if(!channel[i].hdma_completed || hdma_active_after(i)) { + channel[i].indirect_addr >>= 8; + channel[i].indirect_addr |= dma_read(hdma_addr(i)) << 8; + add_clocks(8); + //} + } + } +} + +void CPU::hdma_run() { + unsigned channels = 0; + for(unsigned i = 0; i < 8; i++) { + if(channel[i].hdma_enabled) channels++; + } + if(channels == 0) return; + + add_clocks(16); + for(unsigned i = 0; i < 8; i++) { + if(channel[i].hdma_enabled == false || channel[i].hdma_completed == true) continue; + channel[i].dma_enabled = false; + + if(channel[i].hdma_do_transfer) { + static const unsigned transfer_length[] = { 1, 2, 2, 4, 4, 4, 2, 4 }; + unsigned length = transfer_length[channel[i].transfer_mode]; + for(unsigned index = 0; index < length; index++) { + unsigned addr = channel[i].indirect == false ? hdma_addr(i) : hdma_iaddr(i); + dma_transfer(channel[i].direction, dma_bbus(i, index), addr); + } + } + } + + for(unsigned i = 0; i < 8; i++) { + if(channel[i].hdma_enabled == false || channel[i].hdma_completed == true) continue; + + channel[i].line_counter--; + channel[i].hdma_do_transfer = channel[i].line_counter & 0x80; + hdma_update(i); + } + + status.irq_lock = true; +} + +void CPU::hdma_init() { + unsigned channels = 0; + for(unsigned i = 0; i < 8; i++) { + channel[i].hdma_completed = false; + channel[i].hdma_do_transfer = false; + if(channel[i].hdma_enabled) channels++; + } + if(channels == 0) return; + + add_clocks(16); + for(unsigned i = 0; i < 8; i++) { + if(!channel[i].hdma_enabled) continue; + channel[i].dma_enabled = false; + + channel[i].hdma_addr = channel[i].source_addr; + channel[i].line_counter = 0; + hdma_update(i); + } + + status.irq_lock = true; +} + +void CPU::dma_reset() { + for(unsigned i = 0; i < 8; i++) { + channel[i].dma_enabled = false; + channel[i].hdma_enabled = false; + + channel[i].direction = 1; + channel[i].indirect = true; + channel[i].unused = true; + channel[i].reverse_transfer = true; + channel[i].fixed_transfer = true; + channel[i].transfer_mode = 0x07; + + channel[i].dest_addr = 0xff; + channel[i].source_addr = 0xffff; + channel[i].source_bank = 0xff; + + channel[i].transfer_size = 0xffff; + channel[i].indirect_addr = 0xffff; + + channel[i].indirect_bank = 0xff; + channel[i].hdma_addr = 0xff; + channel[i].line_counter = 0xff; + channel[i].unknown = 0xff; + + channel[i].hdma_completed = false; + channel[i].hdma_do_transfer = false; + } +} + +#endif diff --git a/libsnes/snes/alt/cpu/memory.cpp b/libsnes/snes/alt/cpu/memory.cpp new file mode 100644 index 0000000..fed9870 --- /dev/null +++ b/libsnes/snes/alt/cpu/memory.cpp @@ -0,0 +1,48 @@ +#ifdef CPU_CPP + +uint8 CPU::pio() { + return status.pio; +} + +bool CPU::joylatch() { + return status.joypad_strobe_latch; +} + +bool CPU::interrupt_pending() { + return false; +} + +uint8 CPU::port_read(uint8 port) { + return port_data[port & 3]; +} + +void CPU::port_write(uint8 port, uint8 data) { + port_data[port & 3] = data; +} + +void CPU::op_io() { + add_clocks(6); +} + +uint8 CPU::op_read(unsigned addr) { + regs.mdr = bus.read(addr); + add_clocks(speed(addr)); + return regs.mdr; +} + +void CPU::op_write(unsigned addr, uint8 data) { + add_clocks(speed(addr)); + bus.write(addr, regs.mdr = data); +} + +unsigned CPU::speed(unsigned addr) const { + if(addr & 0x408000) { + if(addr & 0x800000) return status.rom_speed; + return 8; + } + if((addr + 0x6000) & 0x4000) return 8; + if((addr - 0x4000) & 0x7e00) return 6; + return 12; +} + +#endif diff --git a/libsnes/snes/alt/cpu/mmio.cpp b/libsnes/snes/alt/cpu/mmio.cpp new file mode 100644 index 0000000..09bd7bc --- /dev/null +++ b/libsnes/snes/alt/cpu/mmio.cpp @@ -0,0 +1,303 @@ +#ifdef CPU_CPP + +uint8 CPU::mmio_read(unsigned addr) { + if((addr & 0xffc0) == 0x2140) { + synchronize_smp(); + return smp.port_read(addr & 3); + } + + switch(addr & 0xffff) { + case 0x2180: { + uint8 result = bus.read(0x7e0000 | status.wram_addr); + status.wram_addr = (status.wram_addr + 1) & 0x01ffff; + return result; + } + + case 0x4016: { + uint8 result = regs.mdr & 0xfc; + result |= input.port1->data() & 3; + return result; + } + + case 0x4017: { + uint8 result = (regs.mdr & 0xe0) | 0x1c; + result |= input.port2->data() & 3; + return result; + } + + case 0x4210: { + uint8 result = (regs.mdr & 0x70); + result |= status.nmi_line << 7; + result |= 0x02; //CPU revision + status.nmi_line = false; + return result; + } + + case 0x4211: { + uint8 result = (regs.mdr & 0x7f); + result |= status.irq_line << 7; + status.irq_line = false; + return result; + } + + case 0x4212: { + uint8 result = (regs.mdr & 0x3e); + unsigned vbstart = ppu.overscan() == false ? 225 : 240; + + if(vcounter() >= vbstart && vcounter() <= vbstart + 2) result |= 0x01; + if(hcounter() <= 2 || hcounter() >= 1096) result |= 0x40; + if(vcounter() >= vbstart) result |= 0x80; + + return result; + } + + case 0x4213: return status.pio; + + case 0x4214: return status.rddiv >> 0; + case 0x4215: return status.rddiv >> 8; + case 0x4216: return status.rdmpy >> 0; + case 0x4217: return status.rdmpy >> 8; + + case 0x4218: return status.joy1l; + case 0x4219: return status.joy1h; + case 0x421a: return status.joy2l; + case 0x421b: return status.joy2h; + case 0x421c: return status.joy3l; + case 0x421d: return status.joy3h; + case 0x421e: return status.joy4l; + case 0x421f: return status.joy4h; + } + + if((addr & 0xff80) == 0x4300) { + unsigned i = (addr >> 4) & 7; + switch(addr & 0xff8f) { + case 0x4300: { + return (channel[i].direction << 7) + | (channel[i].indirect << 6) + | (channel[i].unused << 5) + | (channel[i].reverse_transfer << 4) + | (channel[i].fixed_transfer << 3) + | (channel[i].transfer_mode << 0); + } + + case 0x4301: return channel[i].dest_addr; + case 0x4302: return channel[i].source_addr >> 0; + case 0x4303: return channel[i].source_addr >> 8; + case 0x4304: return channel[i].source_bank; + case 0x4305: return channel[i].transfer_size >> 0; + case 0x4306: return channel[i].transfer_size >> 8; + case 0x4307: return channel[i].indirect_bank; + case 0x4308: return channel[i].hdma_addr >> 0; + case 0x4309: return channel[i].hdma_addr >> 8; + case 0x430a: return channel[i].line_counter; + case 0x430b: case 0x430f: return channel[i].unknown; + } + } + + return regs.mdr; +} + +void CPU::mmio_write(unsigned addr, uint8 data) { + if((addr & 0xffc0) == 0x2140) { + synchronize_smp(); + port_write(addr & 3, data); + return; + } + + switch(addr & 0xffff) { + case 0x2180: { + bus.write(0x7e0000 | status.wram_addr, data); + status.wram_addr = (status.wram_addr + 1) & 0x01ffff; + return; + } + + case 0x2181: { + status.wram_addr = (status.wram_addr & 0x01ff00) | (data << 0); + return; + } + + case 0x2182: { + status.wram_addr = (status.wram_addr & 0x0100ff) | (data << 8); + return; + } + + case 0x2183: { + status.wram_addr = (status.wram_addr & 0x00ffff) | ((data & 1) << 16); + return; + } + + case 0x4016: { + input.port1->latch(data & 1); + input.port2->latch(data & 1); + return; + } + + case 0x4200: { + bool nmi_enabled = status.nmi_enabled; + bool virq_enabled = status.virq_enabled; + bool hirq_enabled = status.hirq_enabled; + + status.nmi_enabled = data & 0x80; + status.virq_enabled = data & 0x20; + status.hirq_enabled = data & 0x10; + status.auto_joypad_poll_enabled = data & 0x01; + + if(!nmi_enabled && status.nmi_enabled && status.nmi_line) { + status.nmi_transition = true; + } + + if(status.virq_enabled && !status.hirq_enabled && status.irq_line) { + status.irq_transition = true; + } + + if(!status.virq_enabled && !status.hirq_enabled) { + status.irq_line = false; + status.irq_transition = false; + } + + status.irq_lock = true; + return; + } + + case 0x4201: { + if((status.pio & 0x80) && !(data & 0x80)) ppu.latch_counters(); + status.pio = data; + } + + case 0x4202: { + status.wrmpya = data; + return; + } + + case 0x4203: { + status.wrmpyb = data; + status.rdmpy = status.wrmpya * status.wrmpyb; + return; + } + + case 0x4204: { + status.wrdiva = (status.wrdiva & 0xff00) | (data << 0); + return; + } + + case 0x4205: { + status.wrdiva = (data << 8) | (status.wrdiva & 0x00ff); + return; + } + + case 0x4206: { + status.wrdivb = data; + status.rddiv = status.wrdivb ? status.wrdiva / status.wrdivb : 0xffff; + status.rdmpy = status.wrdivb ? status.wrdiva % status.wrdivb : status.wrdiva; + return; + } + + case 0x4207: { + status.htime = (status.htime & 0x0100) | (data << 0); + return; + } + + case 0x4208: { + status.htime = ((data & 1) << 8) | (status.htime & 0x00ff); + return; + } + + case 0x4209: { + status.vtime = (status.vtime & 0x0100) | (data << 0); + return; + } + + case 0x420a: { + status.vtime = ((data & 1) << 8) | (status.vtime & 0x00ff); + return; + } + + case 0x420b: { + for(unsigned i = 0; i < 8; i++) channel[i].dma_enabled = data & (1 << i); + if(data) dma_run(); + return; + } + + case 0x420c: { + for(unsigned i = 0; i < 8; i++) channel[i].hdma_enabled = data & (1 << i); + return; + } + + case 0x420d: { + status.rom_speed = data & 1 ? 6 : 8; + return; + } + } + + if((addr & 0xff80) == 0x4300) { + unsigned i = (addr >> 4) & 7; + switch(addr & 0xff8f) { + case 0x4300: { + channel[i].direction = data & 0x80; + channel[i].indirect = data & 0x40; + channel[i].unused = data & 0x20; + channel[i].reverse_transfer = data & 0x10; + channel[i].fixed_transfer = data & 0x08; + channel[i].transfer_mode = data & 0x07; + return; + } + + case 0x4301: { + channel[i].dest_addr = data; + return; + } + + case 0x4302: { + channel[i].source_addr = (channel[i].source_addr & 0xff00) | (data << 0); + return; + } + + case 0x4303: { + channel[i].source_addr = (data << 8) | (channel[i].source_addr & 0x00ff); + return; + } + + case 0x4304: { + channel[i].source_bank = data; + return; + } + + case 0x4305: { + channel[i].transfer_size = (channel[i].transfer_size & 0xff00) | (data << 0); + return; + } + + case 0x4306: { + channel[i].transfer_size = (data << 8) | (channel[i].transfer_size & 0x00ff); + return; + } + + case 0x4307: { + channel[i].indirect_bank = data; + return; + } + + case 0x4308: { + channel[i].hdma_addr = (channel[i].hdma_addr & 0xff00) | (data << 0); + return; + } + + case 0x4309: { + channel[i].hdma_addr = (data << 8) | (channel[i].hdma_addr & 0x00ff); + return; + } + + case 0x430a: { + channel[i].line_counter = data; + return; + } + + case 0x430b: case 0x430f: { + channel[i].unknown = data; + return; + } + } + } +} + +#endif diff --git a/libsnes/snes/alt/cpu/serialization.cpp b/libsnes/snes/alt/cpu/serialization.cpp new file mode 100644 index 0000000..2f7a3c9 --- /dev/null +++ b/libsnes/snes/alt/cpu/serialization.cpp @@ -0,0 +1,86 @@ +#ifdef CPU_CPP + +void CPU::serialize(serializer &s) { + Processor::serialize(s); + CPUcore::core_serialize(s); + PPUcounter::serialize(s); + + s.array(wram); + + queue.serialize(s); + s.array(port_data); + + for(unsigned i = 0; i < 8; i++) { + s.integer(channel[i].dma_enabled); + s.integer(channel[i].hdma_enabled); + + s.integer(channel[i].direction); + s.integer(channel[i].indirect); + s.integer(channel[i].unused); + s.integer(channel[i].reverse_transfer); + s.integer(channel[i].fixed_transfer); + s.integer(channel[i].transfer_mode); + + s.integer(channel[i].dest_addr); + s.integer(channel[i].source_addr); + s.integer(channel[i].source_bank); + + s.integer(channel[i].transfer_size); + + s.integer(channel[i].indirect_bank); + s.integer(channel[i].hdma_addr); + s.integer(channel[i].line_counter); + s.integer(channel[i].unknown); + + s.integer(channel[i].hdma_completed); + s.integer(channel[i].hdma_do_transfer); + } + + s.integer(status.nmi_valid); + s.integer(status.nmi_line); + s.integer(status.nmi_transition); + s.integer(status.nmi_pending); + + s.integer(status.irq_valid); + s.integer(status.irq_line); + s.integer(status.irq_transition); + s.integer(status.irq_pending); + + s.integer(status.irq_lock); + s.integer(status.hdma_pending); + + s.integer(status.wram_addr); + + s.integer(status.joypad_strobe_latch); + + s.integer(status.nmi_enabled); + s.integer(status.virq_enabled); + s.integer(status.hirq_enabled); + s.integer(status.auto_joypad_poll_enabled); + + s.integer(status.pio); + + s.integer(status.wrmpya); + s.integer(status.wrmpyb); + s.integer(status.wrdiva); + s.integer(status.wrdivb); + + s.integer(status.htime); + s.integer(status.vtime); + + s.integer(status.rom_speed); + + s.integer(status.rddiv); + s.integer(status.rdmpy); + + s.integer(status.joy1l); + s.integer(status.joy1h); + s.integer(status.joy2l); + s.integer(status.joy2h); + s.integer(status.joy3l); + s.integer(status.joy3h); + s.integer(status.joy4l); + s.integer(status.joy4h); +} + +#endif diff --git a/libsnes/snes/alt/cpu/timing.cpp b/libsnes/snes/alt/cpu/timing.cpp new file mode 100644 index 0000000..3eea123 --- /dev/null +++ b/libsnes/snes/alt/cpu/timing.cpp @@ -0,0 +1,119 @@ +#ifdef CPU_CPP + +void CPU::queue_event(unsigned id) { + switch(id) { + case QueueEvent::DramRefresh: return add_clocks(40); + case QueueEvent::HdmaRun: return hdma_run(); + } +} + +void CPU::last_cycle() { + if(status.irq_lock) { + status.irq_lock = false; + return; + } + + if(status.nmi_transition) { + regs.wai = false; + status.nmi_transition = false; + status.nmi_pending = true; + } + + if(status.irq_transition || regs.irq) { + regs.wai = false; + status.irq_transition = false; + status.irq_pending = !regs.p.i; + } +} + +void CPU::add_clocks(unsigned clocks) { + if(status.hirq_enabled) { + if(status.virq_enabled) { + unsigned cpu_time = vcounter() * 1364 + hcounter(); + unsigned irq_time = status.vtime * 1364 + status.htime * 4; + unsigned framelines = (system.region.i == System::Region::NTSC ? 262 : 312) + field(); + if(cpu_time > irq_time) irq_time += framelines * 1364; + bool irq_valid = status.irq_valid; + status.irq_valid = cpu_time <= irq_time && cpu_time + clocks > irq_time; + if(!irq_valid && status.irq_valid) status.irq_line = true; + } else { + unsigned irq_time = status.htime * 4; + if(hcounter() > irq_time) irq_time += 1364; + bool irq_valid = status.irq_valid; + status.irq_valid = hcounter() <= irq_time && hcounter() + clocks > irq_time; + if(!irq_valid && status.irq_valid) status.irq_line = true; + } + if(status.irq_line) status.irq_transition = true; + } else if(status.virq_enabled) { + bool irq_valid = status.irq_valid; + status.irq_valid = vcounter() == status.vtime; + if(!irq_valid && status.irq_valid) status.irq_line = true; + if(status.irq_line) status.irq_transition = true; + } else { + status.irq_valid = false; + } + + tick(clocks); + queue.tick(clocks); + step(clocks); +} + +void CPU::scanline() { + synchronize_smp(); + synchronize_ppu(); + synchronize_coprocessors(); + system.scanline(); + + if(vcounter() == 0) hdma_init(); + + queue.enqueue(534, QueueEvent::DramRefresh); + + if(vcounter() <= (ppu.overscan() == false ? 224 : 239)) { + queue.enqueue(1104 + 8, QueueEvent::HdmaRun); + } + + bool nmi_valid = status.nmi_valid; + status.nmi_valid = vcounter() >= (ppu.overscan() == false ? 225 : 240); + if(!nmi_valid && status.nmi_valid) { + status.nmi_line = true; + if(status.nmi_enabled) status.nmi_transition = true; + } else if(nmi_valid && !status.nmi_valid) { + status.nmi_line = false; + } + + if(status.auto_joypad_poll_enabled && vcounter() == (ppu.overscan() == false ? 227 : 242)) { + run_auto_joypad_poll(); + } +} + +void CPU::run_auto_joypad_poll() { + input.port1->latch(1); + input.port2->latch(1); + input.port1->latch(0); + input.port2->latch(0); + + uint16 joy1 = 0, joy2 = 0, joy3 = 0, joy4 = 0; + for(unsigned i = 0; i < 16; i++) { + uint8 port0 = input.port1->data(); + uint8 port1 = input.port2->data(); + + joy1 |= (port0 & 1) ? (0x8000 >> i) : 0; + joy2 |= (port1 & 1) ? (0x8000 >> i) : 0; + joy3 |= (port0 & 2) ? (0x8000 >> i) : 0; + joy4 |= (port1 & 2) ? (0x8000 >> i) : 0; + } + + status.joy1l = joy1; + status.joy1h = joy1 >> 8; + + status.joy2l = joy2; + status.joy2h = joy2 >> 8; + + status.joy3l = joy3; + status.joy3h = joy3 >> 8; + + status.joy4l = joy4; + status.joy4h = joy4 >> 8; +} + +#endif diff --git a/libsnes/snes/alt/dsp/SPC_DSP.cpp b/libsnes/snes/alt/dsp/SPC_DSP.cpp new file mode 100644 index 0000000..b012140 --- /dev/null +++ b/libsnes/snes/alt/dsp/SPC_DSP.cpp @@ -0,0 +1,1027 @@ +// snes_spc 0.9.0. http://www.slack.net/~ant/ + +#include "SPC_DSP.h" + +#include "blargg_endian.h" +#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 */ + +#include "blargg_source.h" + +#ifdef BLARGG_ENABLE_OPTIMIZER + #include BLARGG_ENABLE_OPTIMIZER +#endif + +#if INT_MAX < 0x7FFFFFFF + #error "Requires that int type have at least 32 bits" +#endif + +// TODO: add to blargg_endian.h +#define GET_LE16SA( addr ) ((BOOST::int16_t) GET_LE16( addr )) +#define GET_LE16A( addr ) GET_LE16( addr ) +#define SET_LE16A( addr, data ) SET_LE16( addr, data ) + +static BOOST::uint8_t const initial_regs [SPC_DSP::register_count] = +{ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + +// 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 +}; + +// if ( io < -32768 ) io = -32768; +// if ( io > 32767 ) io = 32767; +#define CLAMP16( io )\ +{\ + if ( (int16_t) io != io )\ + io = (io >> 31) ^ 0x7FFF;\ +} + +// Access global DSP register +#define REG(n) m.regs [r_##n] + +// Access voice DSP register +#define VREG(r,n) r [v_##n] + +#define WRITE_SAMPLES( l, r, out ) \ +{\ + out [0] = l;\ + out [1] = r;\ + out += 2;\ + if ( out >= m.out_end )\ + {\ + check( out == m.out_end );\ + check( m.out_end != &m.extra [extra_size] || \ + (m.extra <= m.out_begin && m.extra < &m.extra [extra_size]) );\ + out = m.extra;\ + m.out_end = &m.extra [extra_size];\ + }\ +}\ + +void SPC_DSP::set_output( sample_t* out, int size ) +{ + require( (size & 1) == 0 ); // must be even + if ( !out ) + { + out = m.extra; + size = extra_size; + } + m.out_begin = out; + m.out = out; + m.out_end = out + size; +} + +// Volume registers and efb are signed! Easy to forget int8_t 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, +}; + +inline int SPC_DSP::interpolate( voice_t 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 + +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 void SPC_DSP::init_counter() +{ + m.counter = 0; +} + +inline void SPC_DSP::run_counters() +{ + if ( --m.counter < 0 ) + m.counter = simple_counter_range - 1; +} + +inline unsigned SPC_DSP::read_counter( int rate ) +{ + return ((unsigned) m.counter + counter_offsets [rate]) % counter_rates [rate]; +} + + +//// Envelope + +inline void SPC_DSP::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 + { + int mode; + env_data = VREG(v->regs,gain); + 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 + +inline void SPC_DSP::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; + + // Write to next four samples in circular buffer + int* pos = &v->buf [v->buf_pos]; + int* end; + if ( (v->buf_pos += 4) >= brr_buf_size ) + v->buf_pos = 0; + + // Decode four samples + for ( end = pos + 4; pos < end; pos++, nybbles <<= 4 ) + { + // Extract nybble and sign-extend + int s = (int16_t) nybbles >> 12; + + // Shift sample based on header + int const shift = header >> 4; + s = (s << shift) >> 1; + if ( shift >= 0xD ) // handle invalid range + s = (s >> 25) << 11; // same as: s = (s < 0 ? -0x800 : 0) + + // 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 void SPC_DSP::misc_##n() + +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 ) void SPC_DSP::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 = GET_LE16A( entry ); + + 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 + m.kon_check = true; + } + + // 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 = (uint8_t) (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 SPC_DSP::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 + 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_t) endx_buf; +} +inline VOICE_CLOCK( V6 ) +{ + (void) v; // avoid compiler warning about unused v + m.outx_buf = (uint8_t) (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 ) (&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 void SPC_DSP::echo_##n() + +inline void SPC_DSP::echo_read( int ch ) +{ + int s = GET_LE16SA( ECHO_PTR( ch ) ); + // 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 [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; +} +inline int SPC_DSP::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 l = m.t_main_out [0]; + int r = 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 ) + { + l = 0; + r = 0; + } + + // Output sample to DAC + #ifdef SPC_DSP_OUT_HOOK + SPC_DSP_OUT_HOOK( l, r ); + #else + sample_t* out = m.out; + WRITE_SAMPLES( l, r, out ); + m.out = out; + #endif +} +ECHO_CLOCK( 28 ) +{ + m.t_echo_enabled = REG(flg); +} +inline void SPC_DSP::echo_write( int ch ) +{ + if ( !(m.t_echo_enabled & 0x20) ) + SET_LE16A( ECHO_PTR( ch ), m.t_echo_out [ch] ); + 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 + +// 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) */ + +// Voice 0 1 2 3 4 5 6 7 +#define GEN_DSP_TIMING \ +PHASE( 0) V(V5,0)V(V2,1)\ +PHASE( 1) V(V6,0)V(V3,1)\ +PHASE( 2) V(V7_V4_V1,0)\ +PHASE( 3) V(V8_V5_V2,0)\ +PHASE( 4) V(V9_V6_V3,0)\ +PHASE( 5) V(V7_V4_V1,1)\ +PHASE( 6) V(V8_V5_V2,1)\ +PHASE( 7) V(V9_V6_V3,1)\ +PHASE( 8) V(V7_V4_V1,2)\ +PHASE( 9) V(V8_V5_V2,2)\ +PHASE(10) V(V9_V6_V3,2)\ +PHASE(11) V(V7_V4_V1,3)\ +PHASE(12) V(V8_V5_V2,3)\ +PHASE(13) V(V9_V6_V3,3)\ +PHASE(14) V(V7_V4_V1,4)\ +PHASE(15) V(V8_V5_V2,4)\ +PHASE(16) V(V9_V6_V3,4)\ +PHASE(17) V(V1,0) V(V7,5)V(V4,6)\ +PHASE(18) V(V8_V5_V2,5)\ +PHASE(19) V(V9_V6_V3,5)\ +PHASE(20) V(V1,1) V(V7,6)V(V4,7)\ +PHASE(21) V(V8,6)V(V5,7) V(V2,0) /* t_brr_next_addr order dependency */\ +PHASE(22) V(V3a,0) V(V9,6)V(V6,7) echo_22();\ +PHASE(23) V(V7,7) echo_23();\ +PHASE(24) V(V8,7) echo_24();\ +PHASE(25) V(V3b,0) V(V9,7) echo_25();\ +PHASE(26) echo_26();\ +PHASE(27) misc_27(); echo_27();\ +PHASE(28) misc_28(); echo_28();\ +PHASE(29) misc_29(); echo_29();\ +PHASE(30) misc_30();V(V3c,0) echo_30();\ +PHASE(31) V(V4,0) V(V1,2)\ + +#if !SPC_DSP_CUSTOM_RUN + +void SPC_DSP::run( int clocks_remain ) +{ + require( 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: + GEN_DSP_TIMING + #undef PHASE + + if ( --clocks_remain ) + goto loop; + } +} + +#endif + + +//// Setup + +void SPC_DSP::init( void* ram_64k ) +{ + m.ram = (uint8_t*) ram_64k; + mute_voices( 0 ); + disable_surround( false ); + set_output( 0, 0 ); + reset(); + + #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 ); + + blargg_verify_byte_order(); + #endif +} + +void SPC_DSP::soft_reset_common() +{ + require( m.ram ); // init() must have been called already + + m.noise = 0x4000; + m.echo_hist_pos = m.echo_hist; + m.every_other_sample = 1; + m.echo_offset = 0; + m.phase = 0; + + init_counter(); +} + +void SPC_DSP::soft_reset() +{ + REG(flg) = 0xE0; + soft_reset_common(); +} + +void SPC_DSP::load( uint8_t const regs [register_count] ) +{ + memcpy( m.regs, regs, sizeof m.regs ); + memset( &m.regs [register_count], 0, offsetof (state_t,ram) - register_count ); + + // 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); + m.t_esa = REG(esa); + + soft_reset_common(); +} + +void SPC_DSP::reset() { load( initial_regs ); } + + +//// State save/load + +#if !SPC_NO_COPY_STATE_FUNCS + +void SPC_State_Copier::copy( void* state, size_t size ) +{ + func( buf, state, size ); +} + +int SPC_State_Copier::copy_int( int state, int size ) +{ + BOOST::uint8_t s [2]; + SET_LE16( s, state ); + func( buf, &s, size ); + return GET_LE16( s ); +} + +void SPC_State_Copier::skip( int count ) +{ + if ( count > 0 ) + { + char temp [64]; + memset( temp, 0, sizeof temp ); + do + { + int n = sizeof temp; + if ( n > count ) + n = count; + count -= n; + func( buf, temp, n ); + } + while ( count ); + } +} + +void SPC_State_Copier::extra() +{ + int n = 0; + SPC_State_Copier& copier = *this; + SPC_COPY( uint8_t, n ); + skip( n ); +} + +void SPC_DSP::copy_state( unsigned char** io, copy_func_t copy ) +{ + SPC_State_Copier copier( io, copy ); + + // DSP registers + copier.copy( m.regs, register_count ); + + // Internal state + + // Voices + int i; + for ( i = 0; i < voice_count; i++ ) + { + voice_t* v = &m.voices [i]; + + // BRR buffer + int i; + for ( i = 0; i < brr_buf_size; i++ ) + { + int s = v->buf [i]; + SPC_COPY( int16_t, s ); + v->buf [i] = v->buf [i + brr_buf_size] = s; + } + + SPC_COPY( uint16_t, v->interp_pos ); + SPC_COPY( uint16_t, v->brr_addr ); + SPC_COPY( uint16_t, v->env ); + SPC_COPY( int16_t, v->hidden_env ); + SPC_COPY( uint8_t, v->buf_pos ); + SPC_COPY( uint8_t, v->brr_offset ); + SPC_COPY( uint8_t, v->kon_delay ); + { + int m = v->env_mode; + SPC_COPY( uint8_t, m ); + v->env_mode = (enum env_mode_t) m; + } + SPC_COPY( uint8_t, v->t_envx_out ); + + copier.extra(); + } + + // Echo history + for ( i = 0; i < echo_hist_size; i++ ) + { + int j; + for ( j = 0; j < 2; j++ ) + { + int s = m.echo_hist_pos [i] [j]; + SPC_COPY( int16_t, s ); + m.echo_hist [i] [j] = s; // write back at offset 0 + } + } + m.echo_hist_pos = m.echo_hist; + memcpy( &m.echo_hist [echo_hist_size], m.echo_hist, echo_hist_size * sizeof m.echo_hist [0] ); + + // Misc + SPC_COPY( uint8_t, m.every_other_sample ); + SPC_COPY( uint8_t, m.kon ); + + SPC_COPY( uint16_t, m.noise ); + SPC_COPY( uint16_t, m.counter ); + SPC_COPY( uint16_t, m.echo_offset ); + SPC_COPY( uint16_t, m.echo_length ); + SPC_COPY( uint8_t, m.phase ); + + SPC_COPY( uint8_t, m.new_kon ); + SPC_COPY( uint8_t, m.endx_buf ); + SPC_COPY( uint8_t, m.envx_buf ); + SPC_COPY( uint8_t, m.outx_buf ); + + SPC_COPY( uint8_t, m.t_pmon ); + SPC_COPY( uint8_t, m.t_non ); + SPC_COPY( uint8_t, m.t_eon ); + SPC_COPY( uint8_t, m.t_dir ); + SPC_COPY( uint8_t, m.t_koff ); + + SPC_COPY( uint16_t, m.t_brr_next_addr ); + SPC_COPY( uint8_t, m.t_adsr0 ); + SPC_COPY( uint8_t, m.t_brr_header ); + SPC_COPY( uint8_t, m.t_brr_byte ); + SPC_COPY( uint8_t, m.t_srcn ); + SPC_COPY( uint8_t, m.t_esa ); + SPC_COPY( uint8_t, m.t_echo_enabled ); + + SPC_COPY( int16_t, m.t_main_out [0] ); + SPC_COPY( int16_t, m.t_main_out [1] ); + SPC_COPY( int16_t, m.t_echo_out [0] ); + SPC_COPY( int16_t, m.t_echo_out [1] ); + SPC_COPY( int16_t, m.t_echo_in [0] ); + SPC_COPY( int16_t, m.t_echo_in [1] ); + + SPC_COPY( uint16_t, m.t_dir_addr ); + SPC_COPY( uint16_t, m.t_pitch ); + SPC_COPY( int16_t, m.t_output ); + SPC_COPY( uint16_t, m.t_echo_ptr ); + SPC_COPY( uint8_t, m.t_looped ); + + copier.extra(); +} +#endif diff --git a/libsnes/snes/alt/dsp/SPC_DSP.h b/libsnes/snes/alt/dsp/SPC_DSP.h new file mode 100644 index 0000000..4522ace --- /dev/null +++ b/libsnes/snes/alt/dsp/SPC_DSP.h @@ -0,0 +1,304 @@ +// Highly accurate SNES SPC-700 DSP emulator + +// snes_spc 0.9.0 +#ifndef SPC_DSP_H +#define SPC_DSP_H + +#include "blargg_common.h" + +extern "C" { typedef void (*dsp_copy_func_t)( unsigned char** io, void* state, size_t ); } + +class SPC_DSP { +public: + typedef BOOST::uint8_t uint8_t; + +// Setup + + // Initializes DSP and has it use the 64K RAM provided + void init( void* ram_64k ); + + // Sets destination for output samples. If out is NULL or out_size is 0, + // doesn't generate any. + typedef short sample_t; + void set_output( sample_t* out, int out_size ); + + // Number of samples written to output since it was last set, always + // a multiple of 2. Undefined if more samples were generated than + // output buffer could hold. + int sample_count() const; + +// Emulation + + // Resets DSP to power-on state + void reset(); + + // Emulates pressing reset switch on SNES + void soft_reset(); + + // Reads/writes DSP registers. For accuracy, you must first call run() + // to catch the DSP up to present. + int read ( int addr ) const; + void write( int addr, int data ); + + // Runs DSP for specified number of clocks (~1024000 per second). Every 32 clocks + // a pair of samples is be generated. + void run( int clock_count ); + +// Sound control + + // Mutes voices corresponding to non-zero bits in mask (issues repeated KOFF events). + // Reduces emulation accuracy. + enum { voice_count = 8 }; + void mute_voices( int mask ); + +// State + + // Resets DSP and uses supplied values to initialize registers + enum { register_count = 128 }; + void load( uint8_t const regs [register_count] ); + + // Saves/loads exact emulator state + enum { state_size = 640 }; // maximum space needed when saving + typedef dsp_copy_func_t copy_func_t; + void copy_state( unsigned char** io, copy_func_t ); + + // Returns non-zero if new key-on events occurred since last call + bool check_kon(); + +// DSP register addresses + + // 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 + }; + +public: + enum { extra_size = 16 }; + sample_t* extra() { return m.extra; } + sample_t const* out_pos() const { return m.out; } + void disable_surround( bool ) { } // not supported +public: + BLARGG_DISABLE_NOTHROW + + typedef BOOST::int8_t int8_t; + typedef BOOST::int16_t int16_t; + + enum { echo_hist_size = 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_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 + env_mode_t env_mode; + int env; // current envelope level + int hidden_env; // used by GAIN mode 7, very obscure quirk + uint8_t t_envx_out; + }; +private: + enum { brr_block_size = 9 }; + + struct state_t + { + uint8_t regs [register_count]; + + // Echo history keeps most recent 8 samples (twice the size to simplify wrap handling) + int echo_hist [echo_hist_size * 2] [2]; + int (*echo_hist_pos) [2]; // &echo_hist [0 to 7] + + 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 + int phase; // next clock cycle to run (0-31) + bool kon_check; // set when a new KON occurs + + // Hidden registers also written to when main register is written to + int new_kon; + uint8_t endx_buf; + uint8_t envx_buf; + uint8_t 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; + int t_esa; + int t_echo_enabled; + + // internal state that is recalculated every sample + int t_dir_addr; + int t_pitch; + int t_output; + int t_looped; + int t_echo_ptr; + + // left/right sums + int t_main_out [2]; + int t_echo_out [2]; + int t_echo_in [2]; + + voice_t voices [voice_count]; + + // non-emulation state + uint8_t* ram; // 64K shared RAM between DSP and SMP + int mute_mask; + sample_t* out; + sample_t* out_end; + sample_t* out_begin; + sample_t extra [extra_size]; + }; + state_t m; + + void init_counter(); + void run_counters(); + unsigned read_counter( int rate ); + + int interpolate( voice_t const* v ); + void run_envelope( voice_t* const v ); + void decode_brr( voice_t* v ); + + void misc_27(); + void misc_28(); + void misc_29(); + void misc_30(); + + 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 ); + + void echo_read( int ch ); + int echo_output( int ch ); + void echo_write( int ch ); + void echo_22(); + void echo_23(); + void echo_24(); + void echo_25(); + void echo_26(); + void echo_27(); + void echo_28(); + void echo_29(); + void echo_30(); + + void soft_reset_common(); +}; + +#include + +inline int SPC_DSP::sample_count() const { return m.out - m.out_begin; } + +inline int SPC_DSP::read( int addr ) const +{ + assert( (unsigned) addr < register_count ); + return m.regs [addr]; +} + +inline void SPC_DSP::write( int addr, int data ) +{ + assert( (unsigned) addr < register_count ); + + m.regs [addr] = (uint8_t) data; + switch ( addr & 0x0F ) + { + case v_envx: + m.envx_buf = (uint8_t) data; + break; + + case v_outx: + m.outx_buf = (uint8_t) data; + break; + + case 0x0C: + if ( addr == r_kon ) + m.new_kon = (uint8_t) data; + + if ( addr == r_endx ) // always cleared, regardless of data written + { + m.endx_buf = 0; + m.regs [r_endx] = 0; + } + break; + } +} + +inline void SPC_DSP::mute_voices( int mask ) { m.mute_mask = mask; } + +inline bool SPC_DSP::check_kon() +{ + bool old = m.kon_check; + m.kon_check = 0; + return old; +} + +#if !SPC_NO_COPY_STATE_FUNCS + +class SPC_State_Copier { + SPC_DSP::copy_func_t func; + unsigned char** buf; +public: + SPC_State_Copier( unsigned char** p, SPC_DSP::copy_func_t f ) { func = f; buf = p; } + void copy( void* state, size_t size ); + int copy_int( int state, int size ); + void skip( int count ); + void extra(); +}; + +#define SPC_COPY( type, state )\ +{\ + state = (BOOST::type) copier.copy_int( state, sizeof (BOOST::type) );\ + assert( (BOOST::type) state == state );\ +} + +#endif + +#endif diff --git a/libsnes/snes/alt/dsp/blargg_common.h b/libsnes/snes/alt/dsp/blargg_common.h new file mode 100644 index 0000000..75edff3 --- /dev/null +++ b/libsnes/snes/alt/dsp/blargg_common.h @@ -0,0 +1,186 @@ +// Sets up common environment for Shay Green's libraries. +// To change configuration options, modify blargg_config.h, not this file. + +// snes_spc 0.9.0 +#ifndef BLARGG_COMMON_H +#define BLARGG_COMMON_H + +#include +#include +#include +#include + +#undef BLARGG_COMMON_H +// allow blargg_config.h to #include blargg_common.h +#include "blargg_config.h" +#ifndef BLARGG_COMMON_H +#define BLARGG_COMMON_H + +// BLARGG_RESTRICT: equivalent to restrict, where supported +#if defined (__GNUC__) || _MSC_VER >= 1100 + #define BLARGG_RESTRICT __restrict +#else + #define BLARGG_RESTRICT +#endif + +// STATIC_CAST(T,expr): Used in place of static_cast (expr) +#ifndef STATIC_CAST + #define STATIC_CAST(T,expr) ((T) (expr)) +#endif + +// blargg_err_t (0 on success, otherwise error string) +#ifndef blargg_err_t + typedef const char* blargg_err_t; +#endif + +// blargg_vector - very lightweight vector of POD types (no constructor/destructor) +template +class blargg_vector { + T* begin_; + size_t size_; +public: + blargg_vector() : begin_( 0 ), size_( 0 ) { } + ~blargg_vector() { free( begin_ ); } + size_t size() const { return size_; } + T* begin() const { return begin_; } + T* end() const { return begin_ + size_; } + blargg_err_t resize( size_t n ) + { + // TODO: blargg_common.cpp to hold this as an outline function, ugh + void* p = realloc( begin_, n * sizeof (T) ); + if ( p ) + begin_ = (T*) p; + else if ( n > size_ ) // realloc failure only a problem if expanding + return "Out of memory"; + size_ = n; + return 0; + } + void clear() { void* p = begin_; begin_ = 0; size_ = 0; free( p ); } + T& operator [] ( size_t n ) const + { + assert( n <= size_ ); // <= to allow past-the-end value + return begin_ [n]; + } +}; + +#ifndef BLARGG_DISABLE_NOTHROW + // throw spec mandatory in ISO C++ if operator new can return NULL + #if __cplusplus >= 199711 || defined (__GNUC__) + #define BLARGG_THROWS( spec ) throw spec + #else + #define BLARGG_THROWS( spec ) + #endif + #define BLARGG_DISABLE_NOTHROW \ + void* operator new ( size_t s ) BLARGG_THROWS(()) { return malloc( s ); }\ + void operator delete ( void* p ) { free( p ); } + #define BLARGG_NEW new +#else + #include + #define BLARGG_NEW new (std::nothrow) +#endif + +// BLARGG_4CHAR('a','b','c','d') = 'abcd' (four character integer constant) +#define BLARGG_4CHAR( a, b, c, d ) \ + ((a&0xFF)*0x1000000L + (b&0xFF)*0x10000L + (c&0xFF)*0x100L + (d&0xFF)) + +// BOOST_STATIC_ASSERT( expr ): Generates compile error if expr is 0. +#ifndef BOOST_STATIC_ASSERT + #ifdef _MSC_VER + // MSVC6 (_MSC_VER < 1300) fails for use of __LINE__ when /Zl is specified + #define BOOST_STATIC_ASSERT( expr ) \ + void blargg_failed_( int (*arg) [2 / (int) !!(expr) - 1] ) + #else + // Some other compilers fail when declaring same function multiple times in class, + // so differentiate them by line + #define BOOST_STATIC_ASSERT( expr ) \ + void blargg_failed_( int (*arg) [2 / !!(expr) - 1] [__LINE__] ) + #endif +#endif + +// BLARGG_COMPILER_HAS_BOOL: If 0, provides bool support for old compiler. If 1, +// compiler is assumed to support bool. If undefined, availability is determined. +#ifndef BLARGG_COMPILER_HAS_BOOL + #if defined (__MWERKS__) + #if !__option(bool) + #define BLARGG_COMPILER_HAS_BOOL 0 + #endif + #elif defined (_MSC_VER) + #if _MSC_VER < 1100 + #define BLARGG_COMPILER_HAS_BOOL 0 + #endif + #elif defined (__GNUC__) + // supports bool + #elif __cplusplus < 199711 + #define BLARGG_COMPILER_HAS_BOOL 0 + #endif +#endif +#if defined (BLARGG_COMPILER_HAS_BOOL) && !BLARGG_COMPILER_HAS_BOOL + // If you get errors here, modify your blargg_config.h file + typedef int bool; + const bool true = 1; + const bool false = 0; +#endif + +// blargg_long/blargg_ulong = at least 32 bits, int if it's big enough + +#if INT_MAX < 0x7FFFFFFF || LONG_MAX == 0x7FFFFFFF + typedef long blargg_long; +#else + typedef int blargg_long; +#endif + +#if UINT_MAX < 0xFFFFFFFF || ULONG_MAX == 0xFFFFFFFF + typedef unsigned long blargg_ulong; +#else + typedef unsigned blargg_ulong; +#endif + +// BOOST::int8_t etc. + +// HAVE_STDINT_H: If defined, use for int8_t etc. +#if defined (HAVE_STDINT_H) + #include + #define BOOST + +// HAVE_INTTYPES_H: If defined, use for int8_t etc. +#elif defined (HAVE_INTTYPES_H) + #include + #define BOOST + +#else + struct BOOST + { + #if UCHAR_MAX == 0xFF && SCHAR_MAX == 0x7F + typedef signed char int8_t; + typedef unsigned char uint8_t; + #else + // No suitable 8-bit type available + typedef struct see_blargg_common_h int8_t; + typedef struct see_blargg_common_h uint8_t; + #endif + + #if USHRT_MAX == 0xFFFF + typedef short int16_t; + typedef unsigned short uint16_t; + #else + // No suitable 16-bit type available + typedef struct see_blargg_common_h int16_t; + typedef struct see_blargg_common_h uint16_t; + #endif + + #if ULONG_MAX == 0xFFFFFFFF + typedef long int32_t; + typedef unsigned long uint32_t; + #elif UINT_MAX == 0xFFFFFFFF + typedef int int32_t; + typedef unsigned int uint32_t; + #else + // No suitable 32-bit type available + typedef struct see_blargg_common_h int32_t; + typedef struct see_blargg_common_h uint32_t; + #endif + }; +#endif + +#endif +#endif diff --git a/libsnes/snes/alt/dsp/blargg_config.h b/libsnes/snes/alt/dsp/blargg_config.h new file mode 100644 index 0000000..d85d266 --- /dev/null +++ b/libsnes/snes/alt/dsp/blargg_config.h @@ -0,0 +1,24 @@ +// snes_spc 0.9.0 user configuration file. Don't replace when updating library. + +// snes_spc 0.9.0 +#ifndef BLARGG_CONFIG_H +#define BLARGG_CONFIG_H + +// Uncomment to disable debugging checks +#define NDEBUG 1 + +// Uncomment to enable platform-specific (and possibly non-portable) optimizations +//#define BLARGG_NONPORTABLE 1 + +// Uncomment if automatic byte-order determination doesn't work +//#define BLARGG_BIG_ENDIAN 1 + +// Uncomment if you get errors in the bool section of blargg_common.h +//#define BLARGG_COMPILER_HAS_BOOL 1 + +// Use standard config.h if present +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#endif diff --git a/libsnes/snes/alt/dsp/blargg_endian.h b/libsnes/snes/alt/dsp/blargg_endian.h new file mode 100644 index 0000000..f2daca6 --- /dev/null +++ b/libsnes/snes/alt/dsp/blargg_endian.h @@ -0,0 +1,185 @@ +// CPU Byte Order Utilities + +// snes_spc 0.9.0 +#ifndef BLARGG_ENDIAN +#define BLARGG_ENDIAN + +#include "blargg_common.h" + +// BLARGG_CPU_CISC: Defined if CPU has very few general-purpose registers (< 16) +#if defined (_M_IX86) || defined (_M_IA64) || defined (__i486__) || \ + defined (__x86_64__) || defined (__ia64__) || defined (__i386__) + #define BLARGG_CPU_X86 1 + #define BLARGG_CPU_CISC 1 +#endif + +#if defined (__powerpc__) || defined (__ppc__) || defined (__POWERPC__) || defined (__powerc) + #define BLARGG_CPU_POWERPC 1 + #define BLARGG_CPU_RISC 1 +#endif + +// BLARGG_BIG_ENDIAN, BLARGG_LITTLE_ENDIAN: Determined automatically, otherwise only +// one may be #defined to 1. Only needed if something actually depends on byte order. +#if !defined (BLARGG_BIG_ENDIAN) && !defined (BLARGG_LITTLE_ENDIAN) +#ifdef __GLIBC__ + // GCC handles this for us + #include + #if __BYTE_ORDER == __LITTLE_ENDIAN + #define BLARGG_LITTLE_ENDIAN 1 + #elif __BYTE_ORDER == __BIG_ENDIAN + #define BLARGG_BIG_ENDIAN 1 + #endif +#else + +#if defined (LSB_FIRST) || defined (__LITTLE_ENDIAN__) || BLARGG_CPU_X86 || \ + (defined (LITTLE_ENDIAN) && LITTLE_ENDIAN+0 != 1234) + #define BLARGG_LITTLE_ENDIAN 1 +#endif + +#if defined (MSB_FIRST) || defined (__BIG_ENDIAN__) || defined (WORDS_BIGENDIAN) || \ + defined (__sparc__) || BLARGG_CPU_POWERPC || \ + (defined (BIG_ENDIAN) && BIG_ENDIAN+0 != 4321) + #define BLARGG_BIG_ENDIAN 1 +#elif !defined (__mips__) + // No endian specified; assume little-endian, since it's most common + #define BLARGG_LITTLE_ENDIAN 1 +#endif +#endif +#endif + +#if BLARGG_LITTLE_ENDIAN && BLARGG_BIG_ENDIAN + #undef BLARGG_LITTLE_ENDIAN + #undef BLARGG_BIG_ENDIAN +#endif + +inline void blargg_verify_byte_order() +{ + #ifndef NDEBUG + #if BLARGG_BIG_ENDIAN + volatile int i = 1; + assert( *(volatile char*) &i == 0 ); + #elif BLARGG_LITTLE_ENDIAN + volatile int i = 1; + assert( *(volatile char*) &i != 0 ); + #endif + #endif +} + +inline unsigned get_le16( void const* p ) +{ + return (unsigned) ((unsigned char const*) p) [1] << 8 | + (unsigned) ((unsigned char const*) p) [0]; +} + +inline unsigned get_be16( void const* p ) +{ + return (unsigned) ((unsigned char const*) p) [0] << 8 | + (unsigned) ((unsigned char const*) p) [1]; +} + +inline blargg_ulong get_le32( void const* p ) +{ + return (blargg_ulong) ((unsigned char const*) p) [3] << 24 | + (blargg_ulong) ((unsigned char const*) p) [2] << 16 | + (blargg_ulong) ((unsigned char const*) p) [1] << 8 | + (blargg_ulong) ((unsigned char const*) p) [0]; +} + +inline blargg_ulong get_be32( void const* p ) +{ + return (blargg_ulong) ((unsigned char const*) p) [0] << 24 | + (blargg_ulong) ((unsigned char const*) p) [1] << 16 | + (blargg_ulong) ((unsigned char const*) p) [2] << 8 | + (blargg_ulong) ((unsigned char const*) p) [3]; +} + +inline void set_le16( void* p, unsigned n ) +{ + ((unsigned char*) p) [1] = (unsigned char) (n >> 8); + ((unsigned char*) p) [0] = (unsigned char) n; +} + +inline void set_be16( void* p, unsigned n ) +{ + ((unsigned char*) p) [0] = (unsigned char) (n >> 8); + ((unsigned char*) p) [1] = (unsigned char) n; +} + +inline void set_le32( void* p, blargg_ulong n ) +{ + ((unsigned char*) p) [0] = (unsigned char) n; + ((unsigned char*) p) [1] = (unsigned char) (n >> 8); + ((unsigned char*) p) [2] = (unsigned char) (n >> 16); + ((unsigned char*) p) [3] = (unsigned char) (n >> 24); +} + +inline void set_be32( void* p, blargg_ulong n ) +{ + ((unsigned char*) p) [3] = (unsigned char) n; + ((unsigned char*) p) [2] = (unsigned char) (n >> 8); + ((unsigned char*) p) [1] = (unsigned char) (n >> 16); + ((unsigned char*) p) [0] = (unsigned char) (n >> 24); +} + +#if BLARGG_NONPORTABLE + // Optimized implementation if byte order is known + #if BLARGG_LITTLE_ENDIAN + #define GET_LE16( addr ) (*(BOOST::uint16_t*) (addr)) + #define GET_LE32( addr ) (*(BOOST::uint32_t*) (addr)) + #define SET_LE16( addr, data ) (void) (*(BOOST::uint16_t*) (addr) = (data)) + #define SET_LE32( addr, data ) (void) (*(BOOST::uint32_t*) (addr) = (data)) + #elif BLARGG_BIG_ENDIAN + #define GET_BE16( addr ) (*(BOOST::uint16_t*) (addr)) + #define GET_BE32( addr ) (*(BOOST::uint32_t*) (addr)) + #define SET_BE16( addr, data ) (void) (*(BOOST::uint16_t*) (addr) = (data)) + #define SET_BE32( addr, data ) (void) (*(BOOST::uint32_t*) (addr) = (data)) + + #if BLARGG_CPU_POWERPC + // PowerPC has special byte-reversed instructions + #if defined (__MWERKS__) + #define GET_LE16( addr ) (__lhbrx( addr, 0 )) + #define GET_LE32( addr ) (__lwbrx( addr, 0 )) + #define SET_LE16( addr, in ) (__sthbrx( in, addr, 0 )) + #define SET_LE32( addr, in ) (__stwbrx( in, addr, 0 )) + #elif defined (__GNUC__) + #define GET_LE16( addr ) ({unsigned ppc_lhbrx_; asm( "lhbrx %0,0,%1" : "=r" (ppc_lhbrx_) : "r" (addr), "0" (ppc_lhbrx_) ); ppc_lhbrx_;}) + #define GET_LE32( addr ) ({unsigned ppc_lwbrx_; asm( "lwbrx %0,0,%1" : "=r" (ppc_lwbrx_) : "r" (addr), "0" (ppc_lwbrx_) ); ppc_lwbrx_;}) + #define SET_LE16( addr, in ) ({asm( "sthbrx %0,0,%1" : : "r" (in), "r" (addr) );}) + #define SET_LE32( addr, in ) ({asm( "stwbrx %0,0,%1" : : "r" (in), "r" (addr) );}) + #endif + #endif + #endif +#endif + +#ifndef GET_LE16 + #define GET_LE16( addr ) get_le16( addr ) + #define SET_LE16( addr, data ) set_le16( addr, data ) +#endif + +#ifndef GET_LE32 + #define GET_LE32( addr ) get_le32( addr ) + #define SET_LE32( addr, data ) set_le32( addr, data ) +#endif + +#ifndef GET_BE16 + #define GET_BE16( addr ) get_be16( addr ) + #define SET_BE16( addr, data ) set_be16( addr, data ) +#endif + +#ifndef GET_BE32 + #define GET_BE32( addr ) get_be32( addr ) + #define SET_BE32( addr, data ) set_be32( addr, data ) +#endif + +// auto-selecting versions + +inline void set_le( BOOST::uint16_t* p, unsigned n ) { SET_LE16( p, n ); } +inline void set_le( BOOST::uint32_t* p, blargg_ulong n ) { SET_LE32( p, n ); } +inline void set_be( BOOST::uint16_t* p, unsigned n ) { SET_BE16( p, n ); } +inline void set_be( BOOST::uint32_t* p, blargg_ulong n ) { SET_BE32( p, n ); } +inline unsigned get_le( BOOST::uint16_t* p ) { return GET_LE16( p ); } +inline blargg_ulong get_le( BOOST::uint32_t* p ) { return GET_LE32( p ); } +inline unsigned get_be( BOOST::uint16_t* p ) { return GET_BE16( p ); } +inline blargg_ulong get_be( BOOST::uint32_t* p ) { return GET_BE32( p ); } + +#endif diff --git a/libsnes/snes/alt/dsp/blargg_source.h b/libsnes/snes/alt/dsp/blargg_source.h new file mode 100644 index 0000000..5e45c4f --- /dev/null +++ b/libsnes/snes/alt/dsp/blargg_source.h @@ -0,0 +1,100 @@ +/* Included at the beginning of library source files, after all other #include lines. +Sets up helpful macros and services used in my source code. They don't need +module an annoying module prefix on their names since they are defined after +all other #include lines. */ + +// snes_spc 0.9.0 +#ifndef BLARGG_SOURCE_H +#define BLARGG_SOURCE_H + +// If debugging is enabled, abort program if expr is false. Meant for checking +// internal state and consistency. A failed assertion indicates a bug in the module. +// void assert( bool expr ); +#include + +// If debugging is enabled and expr is false, abort program. Meant for checking +// caller-supplied parameters and operations that are outside the control of the +// module. A failed requirement indicates a bug outside the module. +// void require( bool expr ); +#undef require +#define require( expr ) assert( expr ) + +// Like printf() except output goes to debug log file. Might be defined to do +// nothing (not even evaluate its arguments). +// void dprintf( const char* format, ... ); +static inline void blargg_dprintf_( const char*, ... ) { } +#undef dprintf +#define dprintf (1) ? (void) 0 : blargg_dprintf_ + +// If enabled, evaluate expr and if false, make debug log entry with source file +// and line. Meant for finding situations that should be examined further, but that +// don't indicate a problem. In all cases, execution continues normally. +#undef check +#define check( expr ) ((void) 0) + +// If expr yields error string, return it from current function, otherwise continue. +#undef RETURN_ERR +#define RETURN_ERR( expr ) do { \ + blargg_err_t blargg_return_err_ = (expr); \ + if ( blargg_return_err_ ) return blargg_return_err_; \ + } while ( 0 ) + +// If ptr is 0, return out of memory error string. +#undef CHECK_ALLOC +#define CHECK_ALLOC( ptr ) do { if ( (ptr) == 0 ) return "Out of memory"; } while ( 0 ) + +// Avoid any macros which evaluate their arguments multiple times +#undef min +#undef max + +#define DEF_MIN_MAX( type ) \ + static inline type min( type x, type y ) { if ( x < y ) return x; return y; }\ + static inline type max( type x, type y ) { if ( y < x ) return x; return y; } + +DEF_MIN_MAX( int ) +DEF_MIN_MAX( unsigned ) +DEF_MIN_MAX( long ) +DEF_MIN_MAX( unsigned long ) +DEF_MIN_MAX( float ) +DEF_MIN_MAX( double ) + +#undef DEF_MIN_MAX + +/* +// using const references generates crappy code, and I am currenly only using these +// for built-in types, so they take arguments by value + +// TODO: remove +inline int min( int x, int y ) +template +inline T min( T x, T y ) +{ + if ( x < y ) + return x; + return y; +} + +template +inline T max( T x, T y ) +{ + if ( x < y ) + return y; + return x; +} +*/ + +// TODO: good idea? bad idea? +#undef byte +#define byte byte_ +typedef unsigned char byte; + +// deprecated +#define BLARGG_CHECK_ALLOC CHECK_ALLOC +#define BLARGG_RETURN_ERR RETURN_ERR + +// BLARGG_SOURCE_BEGIN: If defined, #included, allowing redefition of dprintf and check +#ifdef BLARGG_SOURCE_BEGIN + #include BLARGG_SOURCE_BEGIN +#endif + +#endif diff --git a/libsnes/snes/alt/dsp/dsp.cpp b/libsnes/snes/alt/dsp/dsp.cpp new file mode 100644 index 0000000..d7b0293 --- /dev/null +++ b/libsnes/snes/alt/dsp/dsp.cpp @@ -0,0 +1,66 @@ +#include + +#define DSP_CPP +namespace SNES { + +DSP dsp; + +#include "serialization.cpp" +#include "SPC_DSP.cpp" + +void DSP::step(unsigned clocks) { + clock += clocks; +} + +void DSP::synchronize_smp() { + if(SMP::Threaded == true) { + if(clock >= 0 && scheduler.sync.i != Scheduler::SynchronizeMode::All) co_switch(smp.thread); + } else { + while(clock >= 0) smp.enter(); + } +} + +void DSP::enter() { + spc_dsp.run(1); + step(24); + + signed count = spc_dsp.sample_count(); + if(count > 0) { + for(unsigned n = 0; n < count; n += 2) audio.sample(samplebuffer[n + 0], samplebuffer[n + 1]); + spc_dsp.set_output(samplebuffer, 8192); + } +} + +uint8 DSP::read(uint8 addr) { + return spc_dsp.read(addr); +} + +void DSP::write(uint8 addr, uint8 data) { + spc_dsp.write(addr, data); +} + +void DSP::power() { + spc_dsp.init(smp.apuram); + spc_dsp.reset(); + spc_dsp.set_output(samplebuffer, 8192); +} + +void DSP::reset() { + spc_dsp.soft_reset(); + spc_dsp.set_output(samplebuffer, 8192); +} + +void DSP::channel_enable(unsigned channel, bool enable) { + channel_enabled[channel & 7] = enable; + unsigned mask = 0; + for(unsigned i = 0; i < 8; i++) { + if(channel_enabled[i] == false) mask |= 1 << i; + } + spc_dsp.mute_voices(mask); +} + +DSP::DSP() { + for(unsigned i = 0; i < 8; i++) channel_enabled[i] = true; +} + +} diff --git a/libsnes/snes/alt/dsp/dsp.hpp b/libsnes/snes/alt/dsp/dsp.hpp new file mode 100644 index 0000000..6c1f856 --- /dev/null +++ b/libsnes/snes/alt/dsp/dsp.hpp @@ -0,0 +1,28 @@ +#include "SPC_DSP.h" + +class DSP : public Processor, public ChipDebugger { +public: + enum{ Threaded = false }; + alwaysinline void step(unsigned clocks); + alwaysinline void synchronize_smp(); + + uint8 read(uint8 addr); + void write(uint8 addr, uint8 data); + + void enter(); + void power(); + void reset(); + + void channel_enable(unsigned channel, bool enable); + + void serialize(serializer&); + bool property(unsigned id, string &name, string &value) { return false; } + DSP(); + +private: + SPC_DSP spc_dsp; + int16 samplebuffer[8192]; + bool channel_enabled[8]; +}; + +extern DSP dsp; diff --git a/libsnes/snes/alt/dsp/serialization.cpp b/libsnes/snes/alt/dsp/serialization.cpp new file mode 100644 index 0000000..0565a1b --- /dev/null +++ b/libsnes/snes/alt/dsp/serialization.cpp @@ -0,0 +1,31 @@ +#ifdef DSP_CPP + +static void dsp_state_save(unsigned char **out, void *in, size_t size) { + memcpy(*out, in, size); + *out += size; +} + +static void dsp_state_load(unsigned char **in, void *out, size_t size) { + memcpy(out, *in, size); + *in += size; +} + +void DSP::serialize(serializer &s) { + Processor::serialize(s); + s.array(samplebuffer); + + unsigned char state[SPC_DSP::state_size]; + unsigned char *p = state; + memset(&state, 0, SPC_DSP::state_size); + if(s.mode() == serializer::Save) { + spc_dsp.copy_state(&p, dsp_state_save); + s.array(state); + } else if(s.mode() == serializer::Load) { + s.array(state); + spc_dsp.copy_state(&p, dsp_state_load); + } else { + s.array(state); + } +} + +#endif diff --git a/libsnes/snes/alt/ppu-compatibility/debugger/debugger.cpp b/libsnes/snes/alt/ppu-compatibility/debugger/debugger.cpp new file mode 100644 index 0000000..2a294c5 --- /dev/null +++ b/libsnes/snes/alt/ppu-compatibility/debugger/debugger.cpp @@ -0,0 +1,349 @@ +#ifdef PPU_CPP + +uint8 PPUDebugger::vram_mmio_read(uint16 addr) { + uint8 data = PPU::vram_mmio_read(addr); + debugger.breakpoint_test(Debugger::Breakpoint::Source::VRAM, Debugger::Breakpoint::Mode::Read, addr, data); + return data; +} + +void PPUDebugger::vram_mmio_write(uint16 addr, uint8 data) { + PPU::vram_mmio_write(addr, data); + debugger.breakpoint_test(Debugger::Breakpoint::Source::VRAM, Debugger::Breakpoint::Mode::Write, addr, data); +} + +uint8 PPUDebugger::oam_mmio_read(uint16 addr) { + uint8 data = PPU::oam_mmio_read(addr); + debugger.breakpoint_test(Debugger::Breakpoint::Source::OAM, Debugger::Breakpoint::Mode::Read, addr, data); + return data; +} + +void PPUDebugger::oam_mmio_write(uint16 addr, uint8 data) { + PPU::oam_mmio_write(addr, data); + debugger.breakpoint_test(Debugger::Breakpoint::Source::OAM, Debugger::Breakpoint::Mode::Write, addr, data); +} + +uint8 PPUDebugger::cgram_mmio_read(uint16 addr) { + uint8 data = PPU::cgram_mmio_read(addr); + debugger.breakpoint_test(Debugger::Breakpoint::Source::CGRAM, Debugger::Breakpoint::Mode::Read, addr, data); + return data; +} + +void PPUDebugger::cgram_mmio_write(uint16 addr, uint8 data) { + PPU::cgram_mmio_write(addr, data); + debugger.breakpoint_test(Debugger::Breakpoint::Source::CGRAM, Debugger::Breakpoint::Mode::Write, addr, data); +} + +PPUDebugger::PPUDebugger() { +} + +bool PPUDebugger::property(unsigned id, string &name, string &value) { + unsigned n = 0; + + #define item(name_, value_) \ + if(id == n++) { \ + name = name_; \ + value = value_; \ + return true; \ + } + + //internal + item("S-PPU1 MDR", string("0x", hex<2>(regs.ppu1_mdr))); + item("S-PPU2 MDR", string("0x", hex<2>(regs.ppu2_mdr))); + + //$2100 + item("$2100", ""); + item("Display Disable", regs.display_disabled); + item("Display Brightness", (unsigned)regs.display_brightness); + + //$2101 + item("$2101", ""); + item("OAM Base Size", (unsigned)regs.oam_basesize); + item("OAM Name Select", (unsigned)regs.oam_nameselect); + item("OAM Name Base Address", string("0x", hex<4>(regs.oam_tdaddr))); + + //$2102-$2103 + item("$2102-$2103", ""); + item("OAM Base Address", string("0x", hex<4>(regs.oam_baseaddr))); + item("OAM Priority", regs.oam_priority); + + //$2105 + item("$2105", ""); + item("BG1 Tile Size", regs.bg_tilesize[BG1] ? "16x16" : "8x8"); + item("BG2 Tile Size", regs.bg_tilesize[BG2] ? "16x16" : "8x8"); + item("BG3 Tile Size", regs.bg_tilesize[BG3] ? "16x16" : "8x8"); + item("BG4 Tile Size", regs.bg_tilesize[BG4] ? "16x16" : "8x8"); + item("BG3 Priority", regs.bg3_priority); + item("BG Mode", (unsigned)regs.bg_mode); + + //$2106 + item("$2106", ""); + item("Mosaic Size", (unsigned)regs.mosaic_size); + item("BG1 Mosaic Enable", regs.mosaic_enabled[BG1]); + item("BG2 Mosaic Enable", regs.mosaic_enabled[BG2]); + item("BG3 Mosaic Enable", regs.mosaic_enabled[BG3]); + item("BG4 Mosaic Enable", regs.mosaic_enabled[BG4]); + + static char screen_size[4][8] = { "32x32", "32x64", "64x32", "64x64" }; + + //$2107 + item("$2107", ""); + item("BG1 Screen Address", string("0x", hex<4>(regs.bg_scaddr[BG1]))); + item("BG1 Screen Size", screen_size[regs.bg_scsize[BG1]]); + + //$2108 + item("$2108", ""); + item("BG2 Screen Address", string("0x", hex<4>(regs.bg_scaddr[BG2]))); + item("BG2 Screen Size", screen_size[regs.bg_scsize[BG2]]); + + //$2109 + item("$2109", ""); + item("BG3 Screen Address", string("0x", hex<4>(regs.bg_scaddr[BG3]))); + item("BG3 Screen Size", screen_size[regs.bg_scsize[BG3]]); + + //$210a + item("$210a", ""); + item("BG4 Screen Address", string("0x", hex<4>(regs.bg_scaddr[BG4]))); + item("BG4 Screen Size", screen_size[regs.bg_scsize[BG4]]); + + //$210b + item("$210b", ""); + item("BG1 Name Base Address", string("0x", hex<4>(regs.bg_tdaddr[BG1]))); + item("BG2 Name Base Address", string("0x", hex<4>(regs.bg_tdaddr[BG2]))); + + //$210c + item("$210c", ""); + item("BG3 Name Base Address", string("0x", hex<4>(regs.bg_tdaddr[BG3]))); + item("BG4 Name Base Address", string("0x", hex<4>(regs.bg_tdaddr[BG4]))); + + //$210d + item("$210d", ""); + item("Mode 7 Scroll H-offset", (unsigned)(regs.m7_hofs & 0x1fff)); + item("BG1 Scroll H-offset", (unsigned)(regs.bg_hofs[BG1] & 0x03ff)); + + //$210e + item("$210e", ""); + item("Mode 7 Scroll V-offset", (unsigned)(regs.m7_vofs & 0x1fff)); + item("BG1 Scroll V-offset", (unsigned)(regs.bg_vofs[BG1] & 0x03ff)); + + //$210f + item("$210f", ""); + item("BG2 Scroll H-offset", (unsigned)(regs.bg_hofs[BG2] & 0x03ff)); + + //$2110 + item("$2110", ""); + item("BG2 Scroll V-offset", (unsigned)(regs.bg_vofs[BG2] & 0x03ff)); + + //$2111 + item("$2111", ""); + item("BG3 Scroll H-offset", (unsigned)(regs.bg_hofs[BG3] & 0x03ff)); + + //$2112 + item("$2112", ""); + item("BG3 Scroll V-offset", (unsigned)(regs.bg_vofs[BG3] & 0x03ff)); + + //$2113 + item("$2113", ""); + item("BG4 Scroll H-offset", (unsigned)(regs.bg_hofs[BG4] & 0x03ff)); + + //$2114 + item("$2114", ""); + item("BG4 Scroll V-offset", (unsigned)(regs.bg_vofs[BG4] & 0x03ff)); + + //$2115 + item("$2115", ""); + item("VRAM Increment Mode", (unsigned)regs.vram_incmode); + item("VRAM Increment Formation", (unsigned)regs.vram_mapping); + item("VRAM Increment Size", (unsigned)regs.vram_incsize); + + //$2116-$2117 + item("$2116-$2117", ""); + item("VRAM Address", string("0x", hex<4>(regs.vram_addr))); + + //$211a + item("$211a", ""); + item("Mode 7 Repeat", (unsigned)regs.mode7_repeat); + item("Mode 7 V-flip", regs.mode7_vflip); + item("Mode 7 H-flip", regs.mode7_hflip); + + //$211b + item("$211b", ""); + item("Mode 7 A", (unsigned)regs.m7a); + + //$211c + item("$211c", ""); + item("Mode 7 B", (unsigned)regs.m7b); + + //$211d + item("$211d", ""); + item("Mode 7 C", (unsigned)regs.m7c); + + //$211e + item("$211e", ""); + item("Mode 7 D", (unsigned)regs.m7d); + + //$211f + item("$211f", ""); + item("Mode 7 X", (unsigned)regs.m7x); + + //$2120 + item("$2120", ""); + item("Mode 7 Y", (unsigned)regs.m7y); + + //$2121 + item("$2121", ""); + item("CGRAM Address", string("0x", hex<4>(regs.cgram_addr))); + + //$2123 + item("$2123", ""); + item("BG1 Window 1 Enable", regs.window1_enabled[BG1]); + item("BG1 Window 1 Invert", regs.window1_invert [BG1]); + item("BG1 Window 2 Enable", regs.window2_enabled[BG1]); + item("BG1 Window 2 Invert", regs.window2_invert [BG1]); + item("BG2 Window 1 Enable", regs.window1_enabled[BG2]); + item("BG2 Window 1 Invert", regs.window1_invert [BG2]); + item("BG2 Window 2 Enable", regs.window2_enabled[BG2]); + item("BG2 Window 2 Invert", regs.window2_invert [BG2]); + + //$2124 + item("$2124", ""); + item("BG3 Window 1 Enable", regs.window1_enabled[BG3]); + item("BG3 Window 1 Invert", regs.window1_invert [BG3]); + item("BG3 Window 2 Enable", regs.window2_enabled[BG3]); + item("BG3 Window 2 Invert", regs.window2_invert [BG3]); + item("BG4 Window 1 Enable", regs.window1_enabled[BG4]); + item("BG4 Window 1 Invert", regs.window1_invert [BG4]); + item("BG4 Window 2 Enable", regs.window2_enabled[BG4]); + item("BG4 Window 2 Invert", regs.window2_invert [BG4]); + + //$2125 + item("$2125", ""); + item("OAM Window 1 Enable", regs.window1_enabled[OAM]); + item("OAM Window 1 Invert", regs.window1_invert [OAM]); + item("OAM Window 2 Enable", regs.window2_enabled[OAM]); + item("OAM Window 2 Invert", regs.window2_invert [OAM]); + item("Color Window 1 Enable", regs.window1_enabled[COL]); + item("Color Window 1 Invert", regs.window1_invert [COL]); + item("Color Window 2 Enable", regs.window2_enabled[COL]); + item("Color Window 2 Invert", regs.window2_enabled[COL]); + + //$2126 + item("$2126", ""); + item("Window 1 Left", (unsigned)regs.window1_left); + + //$2127 + item("$2127", ""); + item("Window 1 Right", (unsigned)regs.window1_right); + + //$2128 + item("$2128", ""); + item("Window 2 Left", (unsigned)regs.window2_left); + + //$2129 + item("$2129", ""); + item("Window 2 Right", (unsigned)regs.window2_right); + + static char window_mask_mode[4][8] = { "OR", "AND", "XOR", "XNOR" }; + + //$212a + item("$212a", ""); + item("BG1 Window Mask", window_mask_mode[regs.window_mask[BG1]]); + item("BG2 Window Mask", window_mask_mode[regs.window_mask[BG2]]); + item("BG3 Window Mask", window_mask_mode[regs.window_mask[BG3]]); + item("BG4 Window Mask", window_mask_mode[regs.window_mask[BG4]]); + + //$212b + item("$212b", ""); + item("OAM Window Mask", window_mask_mode[regs.window_mask[OAM]]); + item("Color Window Mask", window_mask_mode[regs.window_mask[COL]]); + + //$212c + item("$212c", ""); + item("BG1 Mainscreen Enable", regs.bg_enabled[BG1]); + item("BG2 Mainscreen Enable", regs.bg_enabled[BG2]); + item("BG3 Mainscreen Enable", regs.bg_enabled[BG3]); + item("BG4 Mainscreen Enable", regs.bg_enabled[BG4]); + item("OAM Mainscreen Enable", regs.bg_enabled[OAM]); + + //$212d + item("$212d", ""); + item("BG1 Subscreen Enable", regs.bgsub_enabled[BG1]); + item("BG2 Subscreen Enable", regs.bgsub_enabled[BG2]); + item("BG3 Subscreen Enable", regs.bgsub_enabled[BG3]); + item("BG4 Subscreen Enable", regs.bgsub_enabled[BG4]); + item("OAM Subscreen Enable", regs.bgsub_enabled[OAM]); + + //$212e + item("$212e", ""); + item("BG1 Mainscreen Window Enable", regs.window_enabled[BG1]); + item("BG2 Mainscreen Window Enable", regs.window_enabled[BG2]); + item("BG3 Mainscreen Window Enable", regs.window_enabled[BG3]); + item("BG4 Mainscreen Window Enable", regs.window_enabled[BG4]); + item("OAM Mainscreen Window Enable", regs.window_enabled[OAM]); + + //$212f + item("$212f", ""); + item("BG1 Subscreen Window Enable", regs.sub_window_enabled[BG1]); + item("BG2 Subscreen Window Enable", regs.sub_window_enabled[BG2]); + item("BG3 Subscreen Window Enable", regs.sub_window_enabled[BG3]); + item("BG4 Subscreen Window Enable", regs.sub_window_enabled[BG4]); + item("OAM Subscreen Window Enable", regs.sub_window_enabled[OAM]); + + static char color_window_mask_mode[4][32] = { "Always", "Never", "Inside Window Only", "Outside Window Only" }; + + //$2130 + item("$2130", ""); + item("Color Mainscreen Window Mask", color_window_mask_mode[regs.color_mask]); + item("Color Subscreen Window Mask", color_window_mask_mode[regs.colorsub_mask]); + item("Color Add/Subtract Mode", !regs.addsub_mode ? "Fixed Color" : "Subscreen"); + item("Direct Color", regs.direct_color); + + //$2131 + item("$2131", ""); + item("Color Mode", !regs.color_mode ? "Add" : "Subtract"); + item("Color Halve", regs.color_halve); + item("BG1 Color Enable", regs.color_enabled[BG1]); + item("BG2 Color Enable", regs.color_enabled[BG2]); + item("BG3 Color Enable", regs.color_enabled[BG3]); + item("BG4 Color Enable", regs.color_enabled[BG4]); + item("OAM Color Enable", regs.color_enabled[OAM]); + item("Back Color Enable", regs.color_enabled[BACK]); + + //$2132 + item("$2132", ""); + item("Color Constant - Blue", (unsigned)regs.color_b); + item("Color Constant - Green", (unsigned)regs.color_g); + item("Color Constant - Red", (unsigned)regs.color_r); + + //$2133 + item("$2133", ""); + item("Mode 7 EXTBG", regs.mode7_extbg); + item("Pseudo Hires", regs.pseudo_hires); + item("Overscan", regs.overscan); + item("OAM Interlace", regs.oam_interlace); + item("Interlace", regs.interlace); + + //$213c + item("$213c", ""); + item("H-counter", (unsigned)hcounter()); + + //$213d + item("$213d", ""); + item("V-counter", (unsigned)vcounter()); + + //$213e + item("$213e", ""); + item("Range Over", regs.range_over); + item("Time Over", regs.time_over); + item("S-PPU1 Version", (unsigned)ppu1_version); + + //$213f + item("$213f", ""); + item("Field", cpu.field()); + item("Region", !region ? "NTSC" : "PAL"); + item("S-PPU2 Version", (unsigned)ppu2_version); + + #undef item + return false; +} + +#endif diff --git a/libsnes/snes/alt/ppu-compatibility/debugger/debugger.hpp b/libsnes/snes/alt/ppu-compatibility/debugger/debugger.hpp new file mode 100644 index 0000000..f4ef8f5 --- /dev/null +++ b/libsnes/snes/alt/ppu-compatibility/debugger/debugger.hpp @@ -0,0 +1,15 @@ +class PPUDebugger : public PPU, public ChipDebugger { +public: + bool property(unsigned id, string &name, string &value); + + uint8 vram_mmio_read(uint16 addr); + void vram_mmio_write(uint16 addr, uint8 data); + + uint8 oam_mmio_read(uint16 addr); + void oam_mmio_write(uint16 addr, uint8 data); + + uint8 cgram_mmio_read(uint16 addr); + void cgram_mmio_write(uint16 addr, uint8 data); + + PPUDebugger(); +}; diff --git a/libsnes/snes/alt/ppu-compatibility/memory/memory.cpp b/libsnes/snes/alt/ppu-compatibility/memory/memory.cpp new file mode 100644 index 0000000..84bb259 --- /dev/null +++ b/libsnes/snes/alt/ppu-compatibility/memory/memory.cpp @@ -0,0 +1,157 @@ +#ifdef PPU_CPP + +void PPU::latch_counters() { + regs.hcounter = cpu.hdot(); + regs.vcounter = cpu.vcounter(); + regs.counters_latched = true; +} + +uint16 PPU::get_vram_address() { + uint16 addr = regs.vram_addr; + switch(regs.vram_mapping) { + case 0: break; //direct mapping + case 1: addr = (addr & 0xff00) | ((addr & 0x001f) << 3) | ((addr >> 5) & 7); break; + case 2: addr = (addr & 0xfe00) | ((addr & 0x003f) << 3) | ((addr >> 6) & 7); break; + case 3: addr = (addr & 0xfc00) | ((addr & 0x007f) << 3) | ((addr >> 7) & 7); break; + } + return (addr << 1); +} + +//NOTE: all VRAM writes during active display are invalid. Unlike OAM and CGRAM, they will +//not be written anywhere at all. The below address ranges for where writes are invalid have +//been validated on hardware, as has the edge case where the S-CPU MDR can be written if the +//write occurs during the very last clock cycle of vblank. + +uint8 PPU::vram_mmio_read(uint16 addr) { + uint8 data; + + if(regs.display_disabled == true) { + data = vram[addr]; + } else { + uint16 v = cpu.vcounter(); + uint16 h = cpu.hcounter(); + uint16 ls = ((system.region.i == System::Region::NTSC ? 525 : 625) >> 1) - 1; + if(interlace() && !cpu.field()) ls++; + + if(v == ls && h == 1362) { + data = 0x00; + } else if(v < (!overscan() ? 224 : 239)) { + data = 0x00; + } else if(v == (!overscan() ? 224 : 239)) { + if(h == 1362) { + data = vram[addr]; + } else { + data = 0x00; + } + } else { + data = vram[addr]; + } + } + + return data; +} + +void PPU::vram_mmio_write(uint16 addr, uint8 data) { + if(regs.display_disabled == true) { + vram[addr] = data; + } else { + uint16 v = cpu.vcounter(); + uint16 h = cpu.hcounter(); + if(v == 0) { + if(h <= 4) { + vram[addr] = data; + } else if(h == 6) { + vram[addr] = cpu.regs.mdr; + } else { + //no write + } + } else if(v < (!overscan() ? 225 : 240)) { + //no write + } else if(v == (!overscan() ? 225 : 240)) { + if(h <= 4) { + //no write + } else { + vram[addr] = data; + } + } else { + vram[addr] = data; + } + } +} + +uint8 PPU::oam_mmio_read(uint16 addr) { + addr &= 0x03ff; + if(addr & 0x0200) addr &= 0x021f; + uint8 data; + + if(regs.display_disabled == true) { + data = oam[addr]; + } else { + if(cpu.vcounter() < (!overscan() ? 225 : 240)) { + data = oam[regs.ioamaddr]; + } else { + data = oam[addr]; + } + } + + return data; +} + +void PPU::oam_mmio_write(uint16 addr, uint8 data) { + addr &= 0x03ff; + if(addr & 0x0200) addr &= 0x021f; + + sprite_list_valid = false; + + if(regs.display_disabled == true) { + oam[addr] = data; + update_sprite_list(addr, data); + } else { + if(cpu.vcounter() < (!overscan() ? 225 : 240)) { + oam[regs.ioamaddr] = data; + update_sprite_list(regs.ioamaddr, data); + } else { + oam[addr] = data; + update_sprite_list(addr, data); + } + } +} + +uint8 PPU::cgram_mmio_read(uint16 addr) { + addr &= 0x01ff; + uint8 data; + + if(1 || regs.display_disabled == true) { + data = cgram[addr]; + } else { + uint16 v = cpu.vcounter(); + uint16 h = cpu.hcounter(); + if(v < (!overscan() ? 225 : 240) && h >= 128 && h < 1096) { + data = cgram[regs.icgramaddr] & 0x7f; + } else { + data = cgram[addr]; + } + } + + if(addr & 1) data &= 0x7f; + return data; +} + +void PPU::cgram_mmio_write(uint16 addr, uint8 data) { + addr &= 0x01ff; + if(addr & 1) data &= 0x7f; + + if(1 || regs.display_disabled == true) { + cgram[addr] = data; + } else { + uint16 v = cpu.vcounter(); + uint16 h = cpu.hcounter(); + if(v < (!overscan() ? 225 : 240) && h >= 128 && h < 1096) { + cgram[regs.icgramaddr] = data & 0x7f; + } else { + cgram[addr] = data; + } + } +} + +#endif diff --git a/libsnes/snes/alt/ppu-compatibility/memory/memory.hpp b/libsnes/snes/alt/ppu-compatibility/memory/memory.hpp new file mode 100644 index 0000000..c2979ae --- /dev/null +++ b/libsnes/snes/alt/ppu-compatibility/memory/memory.hpp @@ -0,0 +1,10 @@ +uint16 get_vram_address(); + +debugvirtual uint8 vram_mmio_read(uint16 addr); +debugvirtual void vram_mmio_write(uint16 addr, uint8 data); + +debugvirtual uint8 oam_mmio_read(uint16 addr); +debugvirtual void oam_mmio_write(uint16 addr, uint8 data); + +debugvirtual uint8 cgram_mmio_read(uint16 addr); +debugvirtual void cgram_mmio_write(uint16 addr, uint8 data); diff --git a/libsnes/snes/alt/ppu-compatibility/mmio/mmio.cpp b/libsnes/snes/alt/ppu-compatibility/mmio/mmio.cpp new file mode 100644 index 0000000..aedb67c --- /dev/null +++ b/libsnes/snes/alt/ppu-compatibility/mmio/mmio.cpp @@ -0,0 +1,671 @@ +#ifdef PPU_CPP + +//INIDISP +void PPU::mmio_w2100(uint8 value) { + if(regs.display_disabled == true && cpu.vcounter() == (!overscan() ? 225 : 240)) { + regs.oam_addr = regs.oam_baseaddr << 1; + regs.oam_firstsprite = (regs.oam_priority == false) ? 0 : (regs.oam_addr >> 2) & 127; + } + + regs.display_disabled = !!(value & 0x80); + regs.display_brightness = value & 15; +} + +//OBSEL +void PPU::mmio_w2101(uint8 value) { + regs.oam_basesize = (value >> 5) & 7; + regs.oam_nameselect = (value >> 3) & 3; + regs.oam_tdaddr = (value & 3) << 14; +} + +//OAMADDL +void PPU::mmio_w2102(uint8 data) { + regs.oam_baseaddr = (regs.oam_baseaddr & ~0xff) | (data << 0); + regs.oam_baseaddr &= 0x01ff; + regs.oam_addr = regs.oam_baseaddr << 1; + regs.oam_firstsprite = (regs.oam_priority == false) ? 0 : (regs.oam_addr >> 2) & 127; +} + +//OAMADDH +void PPU::mmio_w2103(uint8 data) { + regs.oam_priority = !!(data & 0x80); + regs.oam_baseaddr = (regs.oam_baseaddr & 0xff) | (data << 8); + regs.oam_baseaddr &= 0x01ff; + regs.oam_addr = regs.oam_baseaddr << 1; + regs.oam_firstsprite = (regs.oam_priority == false) ? 0 : (regs.oam_addr >> 2) & 127; +} + +//OAMDATA +void PPU::mmio_w2104(uint8 data) { + if((regs.oam_addr & 1) == 0) regs.oam_latchdata = data; + + if(regs.oam_addr & 0x0200) { + oam_mmio_write(regs.oam_addr, data); + } else if((regs.oam_addr & 1) == 1) { + oam_mmio_write((regs.oam_addr & ~1) + 0, regs.oam_latchdata); + oam_mmio_write((regs.oam_addr & ~1) + 1, data); + } + + regs.oam_addr++; + regs.oam_addr &= 0x03ff; + regs.oam_firstsprite = (regs.oam_priority == false) ? 0 : (regs.oam_addr >> 2) & 127; +} + +//BGMODE +void PPU::mmio_w2105(uint8 value) { + regs.bg_tilesize[BG4] = !!(value & 0x80); + regs.bg_tilesize[BG3] = !!(value & 0x40); + regs.bg_tilesize[BG2] = !!(value & 0x20); + regs.bg_tilesize[BG1] = !!(value & 0x10); + regs.bg3_priority = !!(value & 0x08); + regs.bg_mode = (value & 7); +} + +//MOSAIC +void PPU::mmio_w2106(uint8 value) { + regs.mosaic_size = (value >> 4) & 15; + regs.mosaic_enabled[BG4] = !!(value & 0x08); + regs.mosaic_enabled[BG3] = !!(value & 0x04); + regs.mosaic_enabled[BG2] = !!(value & 0x02); + regs.mosaic_enabled[BG1] = !!(value & 0x01); +} + +//BG1SC +void PPU::mmio_w2107(uint8 value) { + regs.bg_scaddr[BG1] = (value & 0x7c) << 9; + regs.bg_scsize[BG1] = value & 3; +} + +//BG2SC +void PPU::mmio_w2108(uint8 value) { + regs.bg_scaddr[BG2] = (value & 0x7c) << 9; + regs.bg_scsize[BG2] = value & 3; +} + +//BG3SC +void PPU::mmio_w2109(uint8 value) { + regs.bg_scaddr[BG3] = (value & 0x7c) << 9; + regs.bg_scsize[BG3] = value & 3; +} + +//BG4SC +void PPU::mmio_w210a(uint8 value) { + regs.bg_scaddr[BG4] = (value & 0x7c) << 9; + regs.bg_scsize[BG4] = value & 3; +} + +//BG12NBA +void PPU::mmio_w210b(uint8 value) { + regs.bg_tdaddr[BG1] = (value & 0x07) << 13; + regs.bg_tdaddr[BG2] = (value & 0x70) << 9; +} + +//BG34NBA +void PPU::mmio_w210c(uint8 value) { + regs.bg_tdaddr[BG3] = (value & 0x07) << 13; + regs.bg_tdaddr[BG4] = (value & 0x70) << 9; +} + +//BG1HOFS +void PPU::mmio_w210d(uint8 value) { + regs.m7_hofs = (value << 8) | regs.m7_latch; + regs.m7_latch = value; + + regs.bg_hofs[BG1] = (value << 8) | (regs.bg_ofslatch & ~7) | ((regs.bg_hofs[BG1] >> 8) & 7); + regs.bg_ofslatch = value; +} + +//BG1VOFS +void PPU::mmio_w210e(uint8 value) { + regs.m7_vofs = (value << 8) | regs.m7_latch; + regs.m7_latch = value; + + regs.bg_vofs[BG1] = (value << 8) | (regs.bg_ofslatch); + regs.bg_ofslatch = value; +} + +//BG2HOFS +void PPU::mmio_w210f(uint8 value) { + regs.bg_hofs[BG2] = (value << 8) | (regs.bg_ofslatch & ~7) | ((regs.bg_hofs[BG2] >> 8) & 7); + regs.bg_ofslatch = value; +} + +//BG2VOFS +void PPU::mmio_w2110(uint8 value) { + regs.bg_vofs[BG2] = (value << 8) | (regs.bg_ofslatch); + regs.bg_ofslatch = value; +} + +//BG3HOFS +void PPU::mmio_w2111(uint8 value) { + regs.bg_hofs[BG3] = (value << 8) | (regs.bg_ofslatch & ~7) | ((regs.bg_hofs[BG3] >> 8) & 7); + regs.bg_ofslatch = value; +} + +//BG3VOFS +void PPU::mmio_w2112(uint8 value) { + regs.bg_vofs[BG3] = (value << 8) | (regs.bg_ofslatch); + regs.bg_ofslatch = value; +} + +//BG4HOFS +void PPU::mmio_w2113(uint8 value) { + regs.bg_hofs[BG4] = (value << 8) | (regs.bg_ofslatch & ~7) | ((regs.bg_hofs[BG4] >> 8) & 7); + regs.bg_ofslatch = value; +} + +//BG4VOFS +void PPU::mmio_w2114(uint8 value) { + regs.bg_vofs[BG4] = (value << 8) | (regs.bg_ofslatch); + regs.bg_ofslatch = value; +} + +//VMAIN +void PPU::mmio_w2115(uint8 value) { + regs.vram_incmode = !!(value & 0x80); + regs.vram_mapping = (value >> 2) & 3; + switch(value & 3) { + case 0: regs.vram_incsize = 1; break; + case 1: regs.vram_incsize = 32; break; + case 2: regs.vram_incsize = 128; break; + case 3: regs.vram_incsize = 128; break; + } +} + +//VMADDL +void PPU::mmio_w2116(uint8 value) { + regs.vram_addr = (regs.vram_addr & 0xff00) | value; + uint16 addr = get_vram_address(); + regs.vram_readbuffer = vram_mmio_read(addr + 0); + regs.vram_readbuffer |= vram_mmio_read(addr + 1) << 8; +} + +//VMADDH +void PPU::mmio_w2117(uint8 value) { + regs.vram_addr = (value << 8) | (regs.vram_addr & 0x00ff); + uint16 addr = get_vram_address(); + regs.vram_readbuffer = vram_mmio_read(addr + 0); + regs.vram_readbuffer |= vram_mmio_read(addr + 1) << 8; +} + +//VMDATAL +void PPU::mmio_w2118(uint8 value) { +uint16 addr = get_vram_address(); + vram_mmio_write(addr, value); + bg_tiledata_state[TILE_2BIT][(addr >> 4)] = 1; + bg_tiledata_state[TILE_4BIT][(addr >> 5)] = 1; + bg_tiledata_state[TILE_8BIT][(addr >> 6)] = 1; + + if(regs.vram_incmode == 0) { + regs.vram_addr += regs.vram_incsize; + } +} + +//VMDATAH +void PPU::mmio_w2119(uint8 value) { +uint16 addr = get_vram_address() + 1; + vram_mmio_write(addr, value); + bg_tiledata_state[TILE_2BIT][(addr >> 4)] = 1; + bg_tiledata_state[TILE_4BIT][(addr >> 5)] = 1; + bg_tiledata_state[TILE_8BIT][(addr >> 6)] = 1; + + if(regs.vram_incmode == 1) { + regs.vram_addr += regs.vram_incsize; + } +} + +//M7SEL +void PPU::mmio_w211a(uint8 value) { + regs.mode7_repeat = (value >> 6) & 3; + regs.mode7_vflip = !!(value & 0x02); + regs.mode7_hflip = !!(value & 0x01); +} + +//M7A +void PPU::mmio_w211b(uint8 value) { + regs.m7a = (value << 8) | regs.m7_latch; + regs.m7_latch = value; +} + +//M7B +void PPU::mmio_w211c(uint8 value) { + regs.m7b = (value << 8) | regs.m7_latch; + regs.m7_latch = value; +} + +//M7C +void PPU::mmio_w211d(uint8 value) { + regs.m7c = (value << 8) | regs.m7_latch; + regs.m7_latch = value; +} + +//M7D +void PPU::mmio_w211e(uint8 value) { + regs.m7d = (value << 8) | regs.m7_latch; + regs.m7_latch = value; +} + +//M7X +void PPU::mmio_w211f(uint8 value) { + regs.m7x = (value << 8) | regs.m7_latch; + regs.m7_latch = value; +} + +//M7Y +void PPU::mmio_w2120(uint8 value) { + regs.m7y = (value << 8) | regs.m7_latch; + regs.m7_latch = value; +} + +//CGADD +void PPU::mmio_w2121(uint8 value) { + regs.cgram_addr = value << 1; +} + +//CGDATA +//note: CGRAM palette data format is 15-bits +//(0,bbbbb,ggggg,rrrrr). Highest bit is ignored, +//as evidenced by $213b CGRAM data reads. +// +//anomie indicates writes to CGDATA work the same +//as writes to OAMDATA's low table. need to verify +//this on hardware. +void PPU::mmio_w2122(uint8 value) { + if(!(regs.cgram_addr & 1)) { + regs.cgram_latchdata = value; + } else { + cgram_mmio_write((regs.cgram_addr & 0x01fe), regs.cgram_latchdata); + cgram_mmio_write((regs.cgram_addr & 0x01fe) + 1, value & 0x7f); + } + regs.cgram_addr++; + regs.cgram_addr &= 0x01ff; +} + +//W12SEL +void PPU::mmio_w2123(uint8 value) { + regs.window2_enabled[BG2] = !!(value & 0x80); + regs.window2_invert [BG2] = !!(value & 0x40); + regs.window1_enabled[BG2] = !!(value & 0x20); + regs.window1_invert [BG2] = !!(value & 0x10); + regs.window2_enabled[BG1] = !!(value & 0x08); + regs.window2_invert [BG1] = !!(value & 0x04); + regs.window1_enabled[BG1] = !!(value & 0x02); + regs.window1_invert [BG1] = !!(value & 0x01); +} + +//W34SEL +void PPU::mmio_w2124(uint8 value) { + regs.window2_enabled[BG4] = !!(value & 0x80); + regs.window2_invert [BG4] = !!(value & 0x40); + regs.window1_enabled[BG4] = !!(value & 0x20); + regs.window1_invert [BG4] = !!(value & 0x10); + regs.window2_enabled[BG3] = !!(value & 0x08); + regs.window2_invert [BG3] = !!(value & 0x04); + regs.window1_enabled[BG3] = !!(value & 0x02); + regs.window1_invert [BG3] = !!(value & 0x01); +} + +//WOBJSEL +void PPU::mmio_w2125(uint8 value) { + regs.window2_enabled[COL] = !!(value & 0x80); + regs.window2_invert [COL] = !!(value & 0x40); + regs.window1_enabled[COL] = !!(value & 0x20); + regs.window1_invert [COL] = !!(value & 0x10); + regs.window2_enabled[OAM] = !!(value & 0x08); + regs.window2_invert [OAM] = !!(value & 0x04); + regs.window1_enabled[OAM] = !!(value & 0x02); + regs.window1_invert [OAM] = !!(value & 0x01); +} + +//WH0 +void PPU::mmio_w2126(uint8 value) { + regs.window1_left = value; +} + +//WH1 +void PPU::mmio_w2127(uint8 value) { + regs.window1_right = value; +} + +//WH2 +void PPU::mmio_w2128(uint8 value) { + regs.window2_left = value; +} + +//WH3 +void PPU::mmio_w2129(uint8 value) { + regs.window2_right = value; +} + +//WBGLOG +void PPU::mmio_w212a(uint8 value) { + regs.window_mask[BG4] = (value >> 6) & 3; + regs.window_mask[BG3] = (value >> 4) & 3; + regs.window_mask[BG2] = (value >> 2) & 3; + regs.window_mask[BG1] = (value ) & 3; +} + +//WOBJLOG +void PPU::mmio_w212b(uint8 value) { + regs.window_mask[COL] = (value >> 2) & 3; + regs.window_mask[OAM] = (value ) & 3; +} + +//TM +void PPU::mmio_w212c(uint8 value) { + regs.bg_enabled[OAM] = !!(value & 0x10); + regs.bg_enabled[BG4] = !!(value & 0x08); + regs.bg_enabled[BG3] = !!(value & 0x04); + regs.bg_enabled[BG2] = !!(value & 0x02); + regs.bg_enabled[BG1] = !!(value & 0x01); +} + +//TS +void PPU::mmio_w212d(uint8 value) { + regs.bgsub_enabled[OAM] = !!(value & 0x10); + regs.bgsub_enabled[BG4] = !!(value & 0x08); + regs.bgsub_enabled[BG3] = !!(value & 0x04); + regs.bgsub_enabled[BG2] = !!(value & 0x02); + regs.bgsub_enabled[BG1] = !!(value & 0x01); +} + +//TMW +void PPU::mmio_w212e(uint8 value) { + regs.window_enabled[OAM] = !!(value & 0x10); + regs.window_enabled[BG4] = !!(value & 0x08); + regs.window_enabled[BG3] = !!(value & 0x04); + regs.window_enabled[BG2] = !!(value & 0x02); + regs.window_enabled[BG1] = !!(value & 0x01); +} + +//TSW +void PPU::mmio_w212f(uint8 value) { + regs.sub_window_enabled[OAM] = !!(value & 0x10); + regs.sub_window_enabled[BG4] = !!(value & 0x08); + regs.sub_window_enabled[BG3] = !!(value & 0x04); + regs.sub_window_enabled[BG2] = !!(value & 0x02); + regs.sub_window_enabled[BG1] = !!(value & 0x01); +} + +//CGWSEL +void PPU::mmio_w2130(uint8 value) { + regs.color_mask = (value >> 6) & 3; + regs.colorsub_mask = (value >> 4) & 3; + regs.addsub_mode = !!(value & 0x02); + regs.direct_color = !!(value & 0x01); +} + +//CGADDSUB +void PPU::mmio_w2131(uint8 value) { + regs.color_mode = !!(value & 0x80); + regs.color_halve = !!(value & 0x40); + regs.color_enabled[BACK] = !!(value & 0x20); + regs.color_enabled[OAM] = !!(value & 0x10); + regs.color_enabled[BG4] = !!(value & 0x08); + regs.color_enabled[BG3] = !!(value & 0x04); + regs.color_enabled[BG2] = !!(value & 0x02); + regs.color_enabled[BG1] = !!(value & 0x01); +} + +//COLDATA +void PPU::mmio_w2132(uint8 value) { + if(value & 0x80) regs.color_b = value & 0x1f; + if(value & 0x40) regs.color_g = value & 0x1f; + if(value & 0x20) regs.color_r = value & 0x1f; + + regs.color_rgb = (regs.color_r) + | (regs.color_g << 5) + | (regs.color_b << 10); +} + +//SETINI +void PPU::mmio_w2133(uint8 value) { + regs.mode7_extbg = !!(value & 0x40); + regs.pseudo_hires = !!(value & 0x08); + regs.overscan = !!(value & 0x04); + regs.oam_interlace = !!(value & 0x02); + regs.interlace = !!(value & 0x01); + + display.overscan = regs.overscan; + sprite_list_valid = false; +} + +//MPYL +uint8 PPU::mmio_r2134() { +uint32 r; + r = ((int16)regs.m7a * (int8)(regs.m7b >> 8)); + regs.ppu1_mdr = r; + return regs.ppu1_mdr; +} + +//MPYM +uint8 PPU::mmio_r2135() { +uint32 r; + r = ((int16)regs.m7a * (int8)(regs.m7b >> 8)); + regs.ppu1_mdr = r >> 8; + return regs.ppu1_mdr; +} + +//MPYH +uint8 PPU::mmio_r2136() { +uint32 r; + r = ((int16)regs.m7a * (int8)(regs.m7b >> 8)); + regs.ppu1_mdr = r >> 16; + return regs.ppu1_mdr; +} + +//SLHV +uint8 PPU::mmio_r2137() { + if(cpu.pio() & 0x80) { + latch_counters(); + } + return cpu.regs.mdr; +} + +//OAMDATAREAD +uint8 PPU::mmio_r2138() { + regs.ppu1_mdr = oam_mmio_read(regs.oam_addr); + + regs.oam_addr++; + regs.oam_addr &= 0x03ff; + regs.oam_firstsprite = (regs.oam_priority == false) ? 0 : (regs.oam_addr >> 2) & 127; + + return regs.ppu1_mdr; +} + +//VMDATALREAD +uint8 PPU::mmio_r2139() { +uint16 addr = get_vram_address(); + regs.ppu1_mdr = regs.vram_readbuffer; + if(regs.vram_incmode == 0) { + addr &= 0xfffe; + regs.vram_readbuffer = vram_mmio_read(addr + 0); + regs.vram_readbuffer |= vram_mmio_read(addr + 1) << 8; + regs.vram_addr += regs.vram_incsize; + } + return regs.ppu1_mdr; +} + +//VMDATAHREAD +uint8 PPU::mmio_r213a() { +uint16 addr = get_vram_address() + 1; + regs.ppu1_mdr = regs.vram_readbuffer >> 8; + if(regs.vram_incmode == 1) { + addr &= 0xfffe; + regs.vram_readbuffer = vram_mmio_read(addr + 0); + regs.vram_readbuffer |= vram_mmio_read(addr + 1) << 8; + regs.vram_addr += regs.vram_incsize; + } + return regs.ppu1_mdr; +} + +//CGDATAREAD +//note: CGRAM palette data is 15-bits (0,bbbbb,ggggg,rrrrr) +//therefore, the high byte read from each color does not +//update bit 7 of the PPU2 MDR. +uint8 PPU::mmio_r213b() { + if(!(regs.cgram_addr & 1)) { + regs.ppu2_mdr = cgram_mmio_read(regs.cgram_addr) & 0xff; + } else { + regs.ppu2_mdr &= 0x80; + regs.ppu2_mdr |= cgram_mmio_read(regs.cgram_addr) & 0x7f; + } + regs.cgram_addr++; + regs.cgram_addr &= 0x01ff; + return regs.ppu2_mdr; +} + +//OPHCT +uint8 PPU::mmio_r213c() { + if(!regs.latch_hcounter) { + regs.ppu2_mdr = regs.hcounter & 0xff; + } else { + regs.ppu2_mdr &= 0xfe; + regs.ppu2_mdr |= (regs.hcounter >> 8) & 1; + } + regs.latch_hcounter ^= 1; + return regs.ppu2_mdr; +} + +//OPVCT +uint8 PPU::mmio_r213d() { + if(!regs.latch_vcounter) { + regs.ppu2_mdr = regs.vcounter & 0xff; + } else { + regs.ppu2_mdr &= 0xfe; + regs.ppu2_mdr |= (regs.vcounter >> 8) & 1; + } + regs.latch_vcounter ^= 1; + return regs.ppu2_mdr; +} + +//STAT77 +uint8 PPU::mmio_r213e() { +uint8 r = 0x00; + r |= (regs.time_over) ? 0x80 : 0x00; + r |= (regs.range_over) ? 0x40 : 0x00; + r |= (regs.ppu1_mdr & 0x10); + r |= (ppu1_version & 0x0f); + regs.ppu1_mdr = r; + return regs.ppu1_mdr; +} + +//STAT78 +uint8 PPU::mmio_r213f() { +uint8 r = 0x00; + regs.latch_hcounter = 0; + regs.latch_vcounter = 0; + + r |= cpu.field() << 7; + if(!(cpu.pio() & 0x80)) { + r |= 0x40; + } else if(regs.counters_latched == true) { + r |= 0x40; + regs.counters_latched = false; + } + r |= (regs.ppu2_mdr & 0x20); + r |= (region << 4); //0 = NTSC, 1 = PAL + r |= (ppu2_version & 0x0f); + regs.ppu2_mdr = r; + return regs.ppu2_mdr; +} + +uint8 PPU::mmio_read(unsigned addr) { + cpu.synchronize_ppu(); + + switch(addr & 0xffff) { + case 0x2104: + case 0x2105: + case 0x2106: + case 0x2108: + case 0x2109: + case 0x210a: + case 0x2114: + case 0x2115: + case 0x2116: + case 0x2118: + case 0x2119: + case 0x211a: + case 0x2124: + case 0x2125: + case 0x2126: + case 0x2128: + case 0x2129: + case 0x212a: return regs.ppu1_mdr; + case 0x2134: return mmio_r2134(); //MPYL + case 0x2135: return mmio_r2135(); //MPYM + case 0x2136: return mmio_r2136(); //MPYH + case 0x2137: return mmio_r2137(); //SLHV + case 0x2138: return mmio_r2138(); //OAMDATAREAD + case 0x2139: return mmio_r2139(); //VMDATALREAD + case 0x213a: return mmio_r213a(); //VMDATAHREAD + case 0x213b: return mmio_r213b(); //CGDATAREAD + case 0x213c: return mmio_r213c(); //OPHCT + case 0x213d: return mmio_r213d(); //OPVCT + case 0x213e: return mmio_r213e(); //STAT77 + case 0x213f: return mmio_r213f(); //STAT78 + } + + return cpu.regs.mdr; +} + +void PPU::mmio_write(unsigned addr, uint8 data) { + cpu.synchronize_ppu(); + + switch(addr & 0xffff) { + case 0x2100: return mmio_w2100(data); //INIDISP + case 0x2101: return mmio_w2101(data); //OBSEL + case 0x2102: return mmio_w2102(data); //OAMADDL + case 0x2103: return mmio_w2103(data); //OAMADDH + case 0x2104: return mmio_w2104(data); //OAMDATA + case 0x2105: return mmio_w2105(data); //BGMODE + case 0x2106: return mmio_w2106(data); //MOSAIC + case 0x2107: return mmio_w2107(data); //BG1SC + case 0x2108: return mmio_w2108(data); //BG2SC + case 0x2109: return mmio_w2109(data); //BG3SC + case 0x210a: return mmio_w210a(data); //BG4SC + case 0x210b: return mmio_w210b(data); //BG12NBA + case 0x210c: return mmio_w210c(data); //BG34NBA + case 0x210d: return mmio_w210d(data); //BG1HOFS + case 0x210e: return mmio_w210e(data); //BG1VOFS + case 0x210f: return mmio_w210f(data); //BG2HOFS + case 0x2110: return mmio_w2110(data); //BG2VOFS + case 0x2111: return mmio_w2111(data); //BG3HOFS + case 0x2112: return mmio_w2112(data); //BG3VOFS + case 0x2113: return mmio_w2113(data); //BG4HOFS + case 0x2114: return mmio_w2114(data); //BG4VOFS + case 0x2115: return mmio_w2115(data); //VMAIN + case 0x2116: return mmio_w2116(data); //VMADDL + case 0x2117: return mmio_w2117(data); //VMADDH + case 0x2118: return mmio_w2118(data); //VMDATAL + case 0x2119: return mmio_w2119(data); //VMDATAH + case 0x211a: return mmio_w211a(data); //M7SEL + case 0x211b: return mmio_w211b(data); //M7A + case 0x211c: return mmio_w211c(data); //M7B + case 0x211d: return mmio_w211d(data); //M7C + case 0x211e: return mmio_w211e(data); //M7D + case 0x211f: return mmio_w211f(data); //M7X + case 0x2120: return mmio_w2120(data); //M7Y + case 0x2121: return mmio_w2121(data); //CGADD + case 0x2122: return mmio_w2122(data); //CGDATA + case 0x2123: return mmio_w2123(data); //W12SEL + case 0x2124: return mmio_w2124(data); //W34SEL + case 0x2125: return mmio_w2125(data); //WOBJSEL + case 0x2126: return mmio_w2126(data); //WH0 + case 0x2127: return mmio_w2127(data); //WH1 + case 0x2128: return mmio_w2128(data); //WH2 + case 0x2129: return mmio_w2129(data); //WH3 + case 0x212a: return mmio_w212a(data); //WBGLOG + case 0x212b: return mmio_w212b(data); //WOBJLOG + case 0x212c: return mmio_w212c(data); //TM + case 0x212d: return mmio_w212d(data); //TS + case 0x212e: return mmio_w212e(data); //TMW + case 0x212f: return mmio_w212f(data); //TSW + case 0x2130: return mmio_w2130(data); //CGWSEL + case 0x2131: return mmio_w2131(data); //CGADDSUB + case 0x2132: return mmio_w2132(data); //COLDATA + case 0x2133: return mmio_w2133(data); //SETINI + } +} + +#endif diff --git a/libsnes/snes/alt/ppu-compatibility/mmio/mmio.hpp b/libsnes/snes/alt/ppu-compatibility/mmio/mmio.hpp new file mode 100644 index 0000000..aeb1c3a --- /dev/null +++ b/libsnes/snes/alt/ppu-compatibility/mmio/mmio.hpp @@ -0,0 +1,202 @@ +struct { + //open bus support + uint8 ppu1_mdr, ppu2_mdr; + + //bg line counters + uint16 bg_y[4]; + + //internal state + uint16 ioamaddr; + uint16 icgramaddr; + + //$2100 + bool display_disabled; + uint8 display_brightness; + + //$2101 + uint8 oam_basesize; + uint8 oam_nameselect; + uint16 oam_tdaddr; + + //$2102-$2103 + uint16 oam_baseaddr; + uint16 oam_addr; + bool oam_priority; + uint8 oam_firstsprite; + + //$2104 + uint8 oam_latchdata; + + //$2105 + bool bg_tilesize[4]; + bool bg3_priority; + uint8 bg_mode; + + //$2106 + uint8 mosaic_size; + bool mosaic_enabled[4]; + uint16 mosaic_countdown; + + //$2107-$210a + uint16 bg_scaddr[4]; + uint8 bg_scsize[4]; + + //$210b-$210c + uint16 bg_tdaddr[4]; + + //$210d-$2114 + uint8 bg_ofslatch; + uint16 m7_hofs, m7_vofs; + uint16 bg_hofs[4]; + uint16 bg_vofs[4]; + + //$2115 + bool vram_incmode; + uint8 vram_mapping; + uint8 vram_incsize; + + //$2116-$2117 + uint16 vram_addr; + + //$211a + uint8 mode7_repeat; + bool mode7_vflip; + bool mode7_hflip; + + //$211b-$2120 + uint8 m7_latch; + uint16 m7a, m7b, m7c, m7d, m7x, m7y; + + //$2121 + uint16 cgram_addr; + + //$2122 + uint8 cgram_latchdata; + + //$2123-$2125 + bool window1_enabled[6]; + bool window1_invert [6]; + bool window2_enabled[6]; + bool window2_invert [6]; + + //$2126-$2129 + uint8 window1_left, window1_right; + uint8 window2_left, window2_right; + + //$212a-$212b + uint8 window_mask[6]; + + //$212c-$212d + bool bg_enabled[5], bgsub_enabled[5]; + + //$212e-$212f + bool window_enabled[5], sub_window_enabled[5]; + + //$2130 + uint8 color_mask, colorsub_mask; + bool addsub_mode; + bool direct_color; + + //$2131 + bool color_mode, color_halve; + bool color_enabled[6]; + + //$2132 + uint8 color_r, color_g, color_b; + uint16 color_rgb; + + //$2133 + //overscan and interlace are checked once per frame to + //determine if entire frame should be interlaced/non-interlace + //and overscan adjusted. therefore, the variables act sort of + //like a buffer, but they do still affect internal rendering + bool mode7_extbg; + bool pseudo_hires; + bool overscan; + uint16 scanlines; + bool oam_interlace; + bool interlace; + + //$2137 + uint16 hcounter, vcounter; + bool latch_hcounter, latch_vcounter; + bool counters_latched; + + //$2139-$213a + uint16 vram_readbuffer; + + //$213e + bool time_over, range_over; + uint16 oam_itemcount, oam_tilecount; +} regs; + +void mmio_w2100(uint8 value); //INIDISP +void mmio_w2101(uint8 value); //OBSEL +void mmio_w2102(uint8 value); //OAMADDL +void mmio_w2103(uint8 value); //OAMADDH +void mmio_w2104(uint8 value); //OAMDATA +void mmio_w2105(uint8 value); //BGMODE +void mmio_w2106(uint8 value); //MOSAIC +void mmio_w2107(uint8 value); //BG1SC +void mmio_w2108(uint8 value); //BG2SC +void mmio_w2109(uint8 value); //BG3SC +void mmio_w210a(uint8 value); //BG4SC +void mmio_w210b(uint8 value); //BG12NBA +void mmio_w210c(uint8 value); //BG34NBA +void mmio_w210d(uint8 value); //BG1HOFS +void mmio_w210e(uint8 value); //BG1VOFS +void mmio_w210f(uint8 value); //BG2HOFS +void mmio_w2110(uint8 value); //BG2VOFS +void mmio_w2111(uint8 value); //BG3HOFS +void mmio_w2112(uint8 value); //BG3VOFS +void mmio_w2113(uint8 value); //BG4HOFS +void mmio_w2114(uint8 value); //BG4VOFS +void mmio_w2115(uint8 value); //VMAIN +void mmio_w2116(uint8 value); //VMADDL +void mmio_w2117(uint8 value); //VMADDH +void mmio_w2118(uint8 value); //VMDATAL +void mmio_w2119(uint8 value); //VMDATAH +void mmio_w211a(uint8 value); //M7SEL +void mmio_w211b(uint8 value); //M7A +void mmio_w211c(uint8 value); //M7B +void mmio_w211d(uint8 value); //M7C +void mmio_w211e(uint8 value); //M7D +void mmio_w211f(uint8 value); //M7X +void mmio_w2120(uint8 value); //M7Y +void mmio_w2121(uint8 value); //CGADD +void mmio_w2122(uint8 value); //CGDATA +void mmio_w2123(uint8 value); //W12SEL +void mmio_w2124(uint8 value); //W34SEL +void mmio_w2125(uint8 value); //WOBJSEL +void mmio_w2126(uint8 value); //WH0 +void mmio_w2127(uint8 value); //WH1 +void mmio_w2128(uint8 value); //WH2 +void mmio_w2129(uint8 value); //WH3 +void mmio_w212a(uint8 value); //WBGLOG +void mmio_w212b(uint8 value); //WOBJLOG +void mmio_w212c(uint8 value); //TM +void mmio_w212d(uint8 value); //TS +void mmio_w212e(uint8 value); //TMW +void mmio_w212f(uint8 value); //TSW +void mmio_w2130(uint8 value); //CGWSEL +void mmio_w2131(uint8 value); //CGADDSUB +void mmio_w2132(uint8 value); //COLDATA +void mmio_w2133(uint8 value); //SETINI + +uint8 mmio_r2134(); //MPYL +uint8 mmio_r2135(); //MPYM +uint8 mmio_r2136(); //MPYH +uint8 mmio_r2137(); //SLHV +uint8 mmio_r2138(); //OAMDATAREAD +uint8 mmio_r2139(); //VMDATALREAD +uint8 mmio_r213a(); //VMDATAHREAD +uint8 mmio_r213b(); //CGDATAREAD +uint8 mmio_r213c(); //OPHCT +uint8 mmio_r213d(); //OPVCT +uint8 mmio_r213e(); //STAT77 +uint8 mmio_r213f(); //STAT78 + +uint8 mmio_read(unsigned addr); +void mmio_write(unsigned addr, uint8 data); + +void latch_counters(); diff --git a/libsnes/snes/alt/ppu-compatibility/ppu.cpp b/libsnes/snes/alt/ppu-compatibility/ppu.cpp new file mode 100644 index 0000000..9a6a83d --- /dev/null +++ b/libsnes/snes/alt/ppu-compatibility/ppu.cpp @@ -0,0 +1,434 @@ +#include + +#define PPU_CPP +namespace SNES { + +#if defined(DEBUGGER) + #include "debugger/debugger.cpp" + PPUDebugger ppu; +#else + PPU ppu; +#endif + +#include "memory/memory.cpp" +#include "mmio/mmio.cpp" +#include "render/render.cpp" +#include "serialization.cpp" + +void PPU::step(unsigned clocks) { + clock += clocks; +} + +void PPU::synchronize_cpu() { + if(CPU::Threaded == true) { + if(clock >= 0 && scheduler.sync.i != Scheduler::SynchronizeMode::All) co_switch(cpu.thread); + } else { + while(clock >= 0) cpu.enter(); + } +} + +void PPU::Enter() { ppu.enter(); } + +void PPU::enter() { + while(true) { + if(scheduler.sync.i == Scheduler::SynchronizeMode::All) { + scheduler.exit(Scheduler::ExitReason::SynchronizeEvent); + } + + //H = 0 (initialize) + scanline(); + add_clocks(10); + + //H = 10 (cache mode7 registers + OAM address reset) + cache.m7_hofs = regs.m7_hofs; + cache.m7_vofs = regs.m7_vofs; + cache.m7a = regs.m7a; + cache.m7b = regs.m7b; + cache.m7c = regs.m7c; + cache.m7d = regs.m7d; + cache.m7x = regs.m7x; + cache.m7y = regs.m7y; + if(vcounter() == (!overscan() ? 225 : 240)) { + if(regs.display_disabled == false) { + regs.oam_addr = regs.oam_baseaddr << 1; + regs.oam_firstsprite = (regs.oam_priority == false) ? 0 : (regs.oam_addr >> 2) & 127; + } + } + add_clocks(502); + + //H = 512 (render) + render_scanline(); + add_clocks(640); + + //H = 1152 (cache OBSEL) + if(cache.oam_basesize != regs.oam_basesize) { + cache.oam_basesize = regs.oam_basesize; + sprite_list_valid = false; + } + cache.oam_nameselect = regs.oam_nameselect; + cache.oam_tdaddr = regs.oam_tdaddr; + add_clocks(lineclocks() - 1152); //seek to start of next scanline + + } +} + +void PPU::add_clocks(unsigned clocks) { + tick(clocks); + step(clocks); + synchronize_cpu(); +} + +void PPU::scanline() { + line = vcounter(); + + if(line == 0) { + frame(); + + //RTO flag reset + regs.time_over = false; + regs.range_over = false; + } + + if(line == 1) { + //mosaic reset + for(int bg = BG1; bg <= BG4; bg++) regs.bg_y[bg] = 1; + regs.mosaic_countdown = regs.mosaic_size + 1; + regs.mosaic_countdown--; + } else { + for(int bg = BG1; bg <= BG4; bg++) { + if(!regs.mosaic_enabled[bg] || !regs.mosaic_countdown) regs.bg_y[bg] = line; + } + if(!regs.mosaic_countdown) regs.mosaic_countdown = regs.mosaic_size + 1; + regs.mosaic_countdown--; + } +} + +void PPU::render_scanline() { + if(line >= 1 && line < (!overscan() ? 225 : 240)) { + if(framecounter) return; + render_line_oam_rto(); + render_line(); + } +} + +void PPU::frame() { + system.frame(); + + if(field() == 0) { + display.interlace = regs.interlace; + regs.scanlines = (regs.overscan == false) ? 224 : 239; + } + + framecounter = (frameskip == 0 ? 0 : (framecounter + 1) % frameskip); +} + +void PPU::enable() { + function read(&PPU::mmio_read, (PPU*)&ppu); + function write(&PPU::mmio_write, (PPU*)&ppu); + + bus.map(Bus::MapMode::Direct, 0x00, 0x3f, 0x2100, 0x213f, read, write); + bus.map(Bus::MapMode::Direct, 0x80, 0xbf, 0x2100, 0x213f, read, write); +} + +void PPU::power() { + ppu1_version = config.ppu1.version; + ppu2_version = config.ppu2.version; + + foreach(n, vram) n = 0x00; + foreach(n, oam) n = 0x00; + foreach(n, cgram) n = 0x00; + flush_tiledata_cache(); + + region = (system.region.i == System::Region::NTSC ? 0 : 1); //0 = NTSC, 1 = PAL + + regs.ioamaddr = 0x0000; + regs.icgramaddr = 0x01ff; + + //$2100 + regs.display_disabled = true; + regs.display_brightness = 15; + + //$2101 + regs.oam_basesize = 0; + regs.oam_nameselect = 0; + regs.oam_tdaddr = 0x0000; + + cache.oam_basesize = 0; + cache.oam_nameselect = 0; + cache.oam_tdaddr = 0x0000; + + //$2102-$2103 + regs.oam_baseaddr = 0x0000; + regs.oam_addr = 0x0000; + regs.oam_priority = false; + regs.oam_firstsprite = 0; + + //$2104 + regs.oam_latchdata = 0x00; + + //$2105 + regs.bg_tilesize[BG1] = 0; + regs.bg_tilesize[BG2] = 0; + regs.bg_tilesize[BG3] = 0; + regs.bg_tilesize[BG4] = 0; + regs.bg3_priority = 0; + regs.bg_mode = 0; + + //$2106 + regs.mosaic_size = 0; + regs.mosaic_enabled[BG1] = false; + regs.mosaic_enabled[BG2] = false; + regs.mosaic_enabled[BG3] = false; + regs.mosaic_enabled[BG4] = false; + regs.mosaic_countdown = 0; + + //$2107-$210a + regs.bg_scaddr[BG1] = 0x0000; + regs.bg_scaddr[BG2] = 0x0000; + regs.bg_scaddr[BG3] = 0x0000; + regs.bg_scaddr[BG4] = 0x0000; + regs.bg_scsize[BG1] = SC_32x32; + regs.bg_scsize[BG2] = SC_32x32; + regs.bg_scsize[BG3] = SC_32x32; + regs.bg_scsize[BG4] = SC_32x32; + + //$210b-$210c + regs.bg_tdaddr[BG1] = 0x0000; + regs.bg_tdaddr[BG2] = 0x0000; + regs.bg_tdaddr[BG3] = 0x0000; + regs.bg_tdaddr[BG4] = 0x0000; + + //$210d-$2114 + regs.bg_ofslatch = 0x00; + regs.m7_hofs = regs.m7_vofs = 0x0000; + regs.bg_hofs[BG1] = regs.bg_vofs[BG1] = 0x0000; + regs.bg_hofs[BG2] = regs.bg_vofs[BG2] = 0x0000; + regs.bg_hofs[BG3] = regs.bg_vofs[BG3] = 0x0000; + regs.bg_hofs[BG4] = regs.bg_vofs[BG4] = 0x0000; + + //$2115 + regs.vram_incmode = 1; + regs.vram_mapping = 0; + regs.vram_incsize = 1; + + //$2116-$2117 + regs.vram_addr = 0x0000; + + //$211a + regs.mode7_repeat = 0; + regs.mode7_vflip = false; + regs.mode7_hflip = false; + + //$211b-$2120 + regs.m7_latch = 0x00; + regs.m7a = 0x0000; + regs.m7b = 0x0000; + regs.m7c = 0x0000; + regs.m7d = 0x0000; + regs.m7x = 0x0000; + regs.m7y = 0x0000; + + //$2121 + regs.cgram_addr = 0x0000; + + //$2122 + regs.cgram_latchdata = 0x00; + + //$2123-$2125 + regs.window1_enabled[BG1] = false; + regs.window1_enabled[BG2] = false; + regs.window1_enabled[BG3] = false; + regs.window1_enabled[BG4] = false; + regs.window1_enabled[OAM] = false; + regs.window1_enabled[COL] = false; + + regs.window1_invert [BG1] = false; + regs.window1_invert [BG2] = false; + regs.window1_invert [BG3] = false; + regs.window1_invert [BG4] = false; + regs.window1_invert [OAM] = false; + regs.window1_invert [COL] = false; + + regs.window2_enabled[BG1] = false; + regs.window2_enabled[BG2] = false; + regs.window2_enabled[BG3] = false; + regs.window2_enabled[BG4] = false; + regs.window2_enabled[OAM] = false; + regs.window2_enabled[COL] = false; + + regs.window2_invert [BG1] = false; + regs.window2_invert [BG2] = false; + regs.window2_invert [BG3] = false; + regs.window2_invert [BG4] = false; + regs.window2_invert [OAM] = false; + regs.window2_invert [COL] = false; + + //$2126-$2129 + regs.window1_left = 0x00; + regs.window1_right = 0x00; + regs.window2_left = 0x00; + regs.window2_right = 0x00; + + //$212a-$212b + regs.window_mask[BG1] = 0; + regs.window_mask[BG2] = 0; + regs.window_mask[BG3] = 0; + regs.window_mask[BG4] = 0; + regs.window_mask[OAM] = 0; + regs.window_mask[COL] = 0; + + //$212c-$212d + regs.bg_enabled[BG1] = false; + regs.bg_enabled[BG2] = false; + regs.bg_enabled[BG3] = false; + regs.bg_enabled[BG4] = false; + regs.bg_enabled[OAM] = false; + regs.bgsub_enabled[BG1] = false; + regs.bgsub_enabled[BG2] = false; + regs.bgsub_enabled[BG3] = false; + regs.bgsub_enabled[BG4] = false; + regs.bgsub_enabled[OAM] = false; + + //$212e-$212f + regs.window_enabled[BG1] = false; + regs.window_enabled[BG2] = false; + regs.window_enabled[BG3] = false; + regs.window_enabled[BG4] = false; + regs.window_enabled[OAM] = false; + regs.sub_window_enabled[BG1] = false; + regs.sub_window_enabled[BG2] = false; + regs.sub_window_enabled[BG3] = false; + regs.sub_window_enabled[BG4] = false; + regs.sub_window_enabled[OAM] = false; + + //$2130 + regs.color_mask = 0; + regs.colorsub_mask = 0; + regs.addsub_mode = false; + regs.direct_color = false; + + //$2131 + regs.color_mode = 0; + regs.color_halve = false; + regs.color_enabled[BACK] = false; + regs.color_enabled[OAM] = false; + regs.color_enabled[BG4] = false; + regs.color_enabled[BG3] = false; + regs.color_enabled[BG2] = false; + regs.color_enabled[BG1] = false; + + //$2132 + regs.color_r = 0x00; + regs.color_g = 0x00; + regs.color_b = 0x00; + regs.color_rgb = 0x0000; + + //$2133 + regs.mode7_extbg = false; + regs.pseudo_hires = false; + regs.overscan = false; + regs.scanlines = 224; + regs.oam_interlace = false; + regs.interlace = false; + + //$2137 + regs.hcounter = 0; + regs.vcounter = 0; + regs.latch_hcounter = 0; + regs.latch_vcounter = 0; + regs.counters_latched = false; + + //$2139-$213a + regs.vram_readbuffer = 0x0000; + + //$213e + regs.time_over = false; + regs.range_over = false; + + reset(); +} + +void PPU::reset() { + create(Enter, system.cpu_frequency); + PPUcounter::reset(); + memset(surface, 0, 512 * 512 * sizeof(uint32)); + + frame(); + + //$2100 + regs.display_disabled = true; + + display.interlace = false; + display.overscan = false; + regs.scanlines = 224; + + memset(sprite_list, 0, sizeof(sprite_list)); + sprite_list_valid = false; + + //open bus support + regs.ppu1_mdr = 0xff; + regs.ppu2_mdr = 0xff; + + //bg line counters + regs.bg_y[0] = 0; + regs.bg_y[1] = 0; + regs.bg_y[2] = 0; + regs.bg_y[3] = 0; +} + +void PPU::layer_enable(unsigned layer, unsigned priority, bool enable) { + switch(layer * 4 + priority) { + case 0: layer_enabled[BG1][0] = enable; break; + case 1: layer_enabled[BG1][1] = enable; break; + case 4: layer_enabled[BG2][0] = enable; break; + case 5: layer_enabled[BG2][1] = enable; break; + case 8: layer_enabled[BG3][0] = enable; break; + case 9: layer_enabled[BG3][1] = enable; break; + case 12: layer_enabled[BG4][0] = enable; break; + case 13: layer_enabled[BG4][1] = enable; break; + case 16: layer_enabled[OAM][0] = enable; break; + case 17: layer_enabled[OAM][1] = enable; break; + case 18: layer_enabled[OAM][2] = enable; break; + case 19: layer_enabled[OAM][3] = enable; break; + } +} + +void PPU::set_frameskip(unsigned frameskip_) { + frameskip = frameskip_; + framecounter = 0; +} + +PPU::PPU() { + surface = new uint32[512 * 512]; + output = surface + 16 * 512; + + alloc_tiledata_cache(); + + for(unsigned l = 0; l < 16; l++) { + for(unsigned i = 0; i < 4096; i++) { + mosaic_table[l][i] = (i / (l + 1)) * (l + 1); + } + } + + layer_enabled[BG1][0] = true; + layer_enabled[BG1][1] = true; + layer_enabled[BG2][0] = true; + layer_enabled[BG2][1] = true; + layer_enabled[BG3][0] = true; + layer_enabled[BG3][1] = true; + layer_enabled[BG4][0] = true; + layer_enabled[BG4][1] = true; + layer_enabled[OAM][0] = true; + layer_enabled[OAM][1] = true; + layer_enabled[OAM][2] = true; + layer_enabled[OAM][3] = true; + frameskip = 0; + framecounter = 0; +} + +PPU::~PPU() { + delete[] surface; + free_tiledata_cache(); +} + +} diff --git a/libsnes/snes/alt/ppu-compatibility/ppu.hpp b/libsnes/snes/alt/ppu-compatibility/ppu.hpp new file mode 100644 index 0000000..1b6cd5c --- /dev/null +++ b/libsnes/snes/alt/ppu-compatibility/ppu.hpp @@ -0,0 +1,84 @@ +class PPU : public Processor, public PPUcounter { +public: + uint8 vram[128 * 1024]; + uint8 oam[544]; + uint8 cgram[512]; + + enum{ Threaded = true }; + alwaysinline void step(unsigned clocks); + alwaysinline void synchronize_cpu(); + + #include "memory/memory.hpp" + #include "mmio/mmio.hpp" + #include "render/render.hpp" + + uint32 *surface; + uint32 *output; + + uint8 ppu1_version; + uint8 ppu2_version; + + static void Enter(); + void add_clocks(unsigned clocks); + + uint8 region; + unsigned line; + + enum { NTSC = 0, PAL = 1 }; + enum { BG1 = 0, BG2 = 1, BG3 = 2, BG4 = 3, OAM = 4, BACK = 5, COL = 5 }; + enum { SC_32x32 = 0, SC_64x32 = 1, SC_32x64 = 2, SC_64x64 = 3 }; + + struct { + bool interlace; + bool overscan; + } display; + + struct { + //$2101 + uint8 oam_basesize; + uint8 oam_nameselect; + uint16 oam_tdaddr; + + //$210d-$210e + uint16 m7_hofs, m7_vofs; + + //$211b-$2120 + uint16 m7a, m7b, m7c, m7d, m7x, m7y; + } cache; + + alwaysinline bool interlace() const { return display.interlace; } + alwaysinline bool overscan() const { return display.overscan; } + alwaysinline bool hires() const { return (regs.pseudo_hires || regs.bg_mode == 5 || regs.bg_mode == 6); } + + uint16 mosaic_table[16][4096]; + void render_line(); + + void update_oam_status(); + //required functions + void scanline(); + void render_scanline(); + void frame(); + void enter(); + void enable(); + void power(); + void reset(); + + bool layer_enabled[5][4]; + void layer_enable(unsigned layer, unsigned priority, bool enable); + unsigned frameskip; + unsigned framecounter; + void set_frameskip(unsigned frameskip); + + void serialize(serializer&); + PPU(); + ~PPU(); + + friend class PPUDebugger; +}; + +#if defined(DEBUGGER) + #include "debugger/debugger.hpp" + extern PPUDebugger ppu; +#else + extern PPU ppu; +#endif diff --git a/libsnes/snes/alt/ppu-compatibility/render/addsub.cpp b/libsnes/snes/alt/ppu-compatibility/render/addsub.cpp new file mode 100644 index 0000000..fc88bad --- /dev/null +++ b/libsnes/snes/alt/ppu-compatibility/render/addsub.cpp @@ -0,0 +1,25 @@ +#ifdef PPU_CPP + +//color addition / subtraction +//thanks go to blargg for the optimized algorithms +inline uint16 PPU::addsub(uint32 x, uint32 y, bool halve) { + if(!regs.color_mode) { + if(!halve) { + unsigned sum = x + y; + unsigned carry = (sum - ((x ^ y) & 0x0421)) & 0x8420; + return (sum - carry) | (carry - (carry >> 5)); + } else { + return (x + y - ((x ^ y) & 0x0421)) >> 1; + } + } else { + unsigned diff = x - y + 0x8420; + unsigned borrow = (diff - ((x ^ y) & 0x8420)) & 0x8420; + if(!halve) { + return (diff - borrow) & (borrow - (borrow >> 5)); + } else { + return (((diff - borrow) & (borrow - (borrow >> 5))) & 0x7bde) >> 1; + } + } +} + +#endif diff --git a/libsnes/snes/alt/ppu-compatibility/render/bg.cpp b/libsnes/snes/alt/ppu-compatibility/render/bg.cpp new file mode 100644 index 0000000..6793d74 --- /dev/null +++ b/libsnes/snes/alt/ppu-compatibility/render/bg.cpp @@ -0,0 +1,209 @@ +#ifdef PPU_CPP + +//called once at the start of every rendered scanline +void PPU::update_bg_info() { + const unsigned hires = (regs.bg_mode == 5 || regs.bg_mode == 6); + const unsigned width = (!hires ? 256 : 512); + + for(unsigned bg = 0; bg < 4; bg++) { + bg_info[bg].th = (regs.bg_tilesize[bg] ? 4 : 3); + bg_info[bg].tw = (hires ? 4 : bg_info[bg].th); + + bg_info[bg].mx = (bg_info[bg].th == 4 ? (width << 1) : width); + bg_info[bg].my = bg_info[bg].mx; + if(regs.bg_scsize[bg] & 0x01) bg_info[bg].mx <<= 1; + if(regs.bg_scsize[bg] & 0x02) bg_info[bg].my <<= 1; + bg_info[bg].mx--; + bg_info[bg].my--; + + bg_info[bg].scy = (regs.bg_scsize[bg] & 0x02) ? (32 << 5) : 0; + bg_info[bg].scx = (regs.bg_scsize[bg] & 0x01) ? (32 << 5) : 0; + if(regs.bg_scsize[bg] == 3) bg_info[bg].scy <<= 1; + } +} + +template +uint16 PPU::bg_get_tile(uint16 x, uint16 y) { + x = (x & bg_info[bg].mx) >> bg_info[bg].tw; + y = (y & bg_info[bg].my) >> bg_info[bg].th; + + uint16 pos = ((y & 0x1f) << 5) + (x & 0x1f); + if(y & 0x20) pos += bg_info[bg].scy; + if(x & 0x20) pos += bg_info[bg].scx; + + const uint16 addr = regs.bg_scaddr[bg] + (pos << 1); + return vram[addr] + (vram[addr + 1] << 8); +} + +#define setpixel_main(x) \ + if(pixel_cache[x].pri_main < tile_pri) { \ + pixel_cache[x].pri_main = tile_pri; \ + pixel_cache[x].bg_main = bg; \ + pixel_cache[x].src_main = col; \ + pixel_cache[x].ce_main = false; \ + } + +#define setpixel_sub(x) \ + if(pixel_cache[x].pri_sub < tile_pri) { \ + pixel_cache[x].pri_sub = tile_pri; \ + pixel_cache[x].bg_sub = bg; \ + pixel_cache[x].src_sub = col; \ + pixel_cache[x].ce_sub = false; \ + } + +template +void PPU::render_line_bg(uint8 pri0_pos, uint8 pri1_pos) { + if(layer_enabled[bg][0] == false) pri0_pos = 0; + if(layer_enabled[bg][1] == false) pri1_pos = 0; + if(pri0_pos + pri1_pos == 0) return; + + if(regs.bg_enabled[bg] == false && regs.bgsub_enabled[bg] == false) return; + + const bool bg_enabled = regs.bg_enabled[bg]; + const bool bgsub_enabled = regs.bgsub_enabled[bg]; + + const uint16 opt_valid_bit = (bg == BG1) ? 0x2000 : (bg == BG2) ? 0x4000 : 0x0000; + const uint8 bgpal_index = (mode == 0 ? (bg << 5) : 0); + + const uint8 pal_size = 2 << color_depth; //<<2 (*4), <<4 (*16), <<8 (*256) + const uint16 tile_mask = 0x0fff >> color_depth; //0x0fff, 0x07ff, 0x03ff + //4 + color_depth = >>(4-6) -- / {16, 32, 64 } bytes/tile + //index is a tile number count to add to base tile number + const unsigned tiledata_index = regs.bg_tdaddr[bg] >> (4 + color_depth); + + const uint8 *bg_td = bg_tiledata[color_depth]; + const uint8 *bg_td_state = bg_tiledata_state[color_depth]; + + const uint8 tile_width = bg_info[bg].tw; + const uint8 tile_height = bg_info[bg].th; + const uint16 mask_x = bg_info[bg].mx; //screen width mask + const uint16 mask_y = bg_info[bg].my; //screen height mask + + uint16 y = regs.bg_y[bg]; + uint16 hscroll = regs.bg_hofs[bg]; + uint16 vscroll = regs.bg_vofs[bg]; + + const unsigned hires = (mode == 5 || mode == 6); + const unsigned width = (!hires ? 256 : 512); + + if(hires) { + hscroll <<= 1; + if(regs.interlace) y = (y << 1) + field(); + } + + uint16 hval, vval; + uint16 tile_pri, tile_num; + uint8 pal_index, pal_num; + uint16 hoffset, voffset, opt_x, col; + bool mirror_x, mirror_y; + + const uint8 *tile_ptr; + const uint16 *mtable = mosaic_table[regs.mosaic_enabled[bg] ? regs.mosaic_size : 0]; + const bool is_opt_mode = (mode == 2 || mode == 4 || mode == 6); + const bool is_direct_color_mode = (regs.direct_color == true && bg == BG1 && (mode == 3 || mode == 4)); + + build_window_tables(bg); + const uint8 *wt_main = window[bg].main; + const uint8 *wt_sub = window[bg].sub; + + uint16 prev_x = 0xffff, prev_y = 0xffff, prev_optx = 0xffff; + for(uint16 x = 0; x < width; x++) { + hoffset = mtable[x] + hscroll; + voffset = y + vscroll; + + if(is_opt_mode) { + opt_x = (x + (hscroll & 7)); + + //tile 0 is unaffected by OPT mode... + if(opt_x >= 8) { + //cache tile data in hval, vval if possible + if((opt_x >> 3) != (prev_optx >> 3)) { + prev_optx = opt_x; + + hval = bg_get_tile((opt_x - 8) + (regs.bg_hofs[BG3] & ~7), regs.bg_vofs[BG3]); + if(mode != 4) { + vval = bg_get_tile((opt_x - 8) + (regs.bg_hofs[BG3] & ~7), regs.bg_vofs[BG3] + 8); + } + } + + if(mode == 4) { + if(hval & opt_valid_bit) { + if(!(hval & 0x8000)) { + hoffset = opt_x + (hval & ~7); + } else { + voffset = y + hval; + } + } + } else { + if(hval & opt_valid_bit) { + hoffset = opt_x + (hval & ~7); + } + if(vval & opt_valid_bit) { + voffset = y + vval; + } + } + } + } + + hoffset &= mask_x; + voffset &= mask_y; + + if((hoffset >> 3) != prev_x || (voffset >> 3) != prev_y) { + prev_x = (hoffset >> 3); + prev_y = (voffset >> 3); + + tile_num = bg_get_tile(hoffset, voffset); //format = vhopppcc cccccccc + mirror_y = (tile_num & 0x8000); + mirror_x = (tile_num & 0x4000); + tile_pri = (tile_num & 0x2000) ? pri1_pos : pri0_pos; + pal_num = ((tile_num >> 10) & 7); + pal_index = bgpal_index + (pal_num << pal_size); + + if(tile_width == 4) { //16x16 horizontal tile mirroring + if((bool)(hoffset & 8) != mirror_x) tile_num++; + } + + if(tile_height == 4) { //16x16 vertical tile mirroring + if((bool)(voffset & 8) != mirror_y) tile_num += 16; + } + + tile_num &= 0x03ff; + tile_num += tiledata_index; + tile_num &= tile_mask; + + if(bg_td_state[tile_num] == 1) { + render_bg_tile(tile_num); + } + + if(mirror_y) voffset ^= 7; //invert y tile pos + tile_ptr = bg_td + (tile_num * 64) + ((voffset & 7) * 8); + } + + if(mirror_x) hoffset ^= 7; //invert x tile pos + col = *(tile_ptr + (hoffset & 7)); + if(col) { + if(is_direct_color_mode) { + col = get_direct_color(pal_num, col); + } else { + col = get_palette(col + pal_index); + } + + if(!hires) { + if(bg_enabled == true && !wt_main[x]) { setpixel_main(x); } + if(bgsub_enabled == true && !wt_sub[x]) { setpixel_sub(x); } + } else { + int hx = x >> 1; + if(x & 1) { + if(bg_enabled == true && !wt_main[hx]) { setpixel_main(hx); } + } else { + if(bgsub_enabled == true && !wt_sub[hx]) { setpixel_sub(hx); } + } + } + } + } +} + +#undef setpixel_main +#undef setpixel_sub + +#endif diff --git a/libsnes/snes/alt/ppu-compatibility/render/cache.cpp b/libsnes/snes/alt/ppu-compatibility/render/cache.cpp new file mode 100644 index 0000000..4e3b7be --- /dev/null +++ b/libsnes/snes/alt/ppu-compatibility/render/cache.cpp @@ -0,0 +1,147 @@ +#ifdef PPU_CPP + +#define render_bg_tile_line_2bpp(mask) \ + col = !!(d0 & mask) << 0; \ + col += !!(d1 & mask) << 1; \ + *dest++ = col + +#define render_bg_tile_line_4bpp(mask) \ + col = !!(d0 & mask) << 0; \ + col += !!(d1 & mask) << 1; \ + col += !!(d2 & mask) << 2; \ + col += !!(d3 & mask) << 3; \ + *dest++ = col + +#define render_bg_tile_line_8bpp(mask) \ + col = !!(d0 & mask) << 0; \ + col += !!(d1 & mask) << 1; \ + col += !!(d2 & mask) << 2; \ + col += !!(d3 & mask) << 3; \ + col += !!(d4 & mask) << 4; \ + col += !!(d5 & mask) << 5; \ + col += !!(d6 & mask) << 6; \ + col += !!(d7 & mask) << 7; \ + *dest++ = col + +template +void PPU::render_bg_tile(uint16 tile_num) { + uint8 col, d0, d1, d2, d3, d4, d5, d6, d7; + + if(color_depth == COLORDEPTH_4) { + uint8 *dest = (uint8*)bg_tiledata[TILE_2BIT] + tile_num * 64; + unsigned pos = tile_num * 16; + unsigned y = 8; + while(y--) { + d0 = vram[pos ]; + d1 = vram[pos + 1]; + render_bg_tile_line_2bpp(0x80); + render_bg_tile_line_2bpp(0x40); + render_bg_tile_line_2bpp(0x20); + render_bg_tile_line_2bpp(0x10); + render_bg_tile_line_2bpp(0x08); + render_bg_tile_line_2bpp(0x04); + render_bg_tile_line_2bpp(0x02); + render_bg_tile_line_2bpp(0x01); + pos += 2; + } + bg_tiledata_state[TILE_2BIT][tile_num] = 0; + } + + if(color_depth == COLORDEPTH_16) { + uint8 *dest = (uint8*)bg_tiledata[TILE_4BIT] + tile_num * 64; + unsigned pos = tile_num * 32; + unsigned y = 8; + while(y--) { + d0 = vram[pos ]; + d1 = vram[pos + 1]; + d2 = vram[pos + 16]; + d3 = vram[pos + 17]; + render_bg_tile_line_4bpp(0x80); + render_bg_tile_line_4bpp(0x40); + render_bg_tile_line_4bpp(0x20); + render_bg_tile_line_4bpp(0x10); + render_bg_tile_line_4bpp(0x08); + render_bg_tile_line_4bpp(0x04); + render_bg_tile_line_4bpp(0x02); + render_bg_tile_line_4bpp(0x01); + pos += 2; + } + bg_tiledata_state[TILE_4BIT][tile_num] = 0; + } + + if(color_depth == COLORDEPTH_256) { + uint8 *dest = (uint8*)bg_tiledata[TILE_8BIT] + tile_num * 64; + unsigned pos = tile_num * 64; + unsigned y = 8; + while(y--) { + d0 = vram[pos ]; + d1 = vram[pos + 1]; + d2 = vram[pos + 16]; + d3 = vram[pos + 17]; + d4 = vram[pos + 32]; + d5 = vram[pos + 33]; + d6 = vram[pos + 48]; + d7 = vram[pos + 49]; + render_bg_tile_line_8bpp(0x80); + render_bg_tile_line_8bpp(0x40); + render_bg_tile_line_8bpp(0x20); + render_bg_tile_line_8bpp(0x10); + render_bg_tile_line_8bpp(0x08); + render_bg_tile_line_8bpp(0x04); + render_bg_tile_line_8bpp(0x02); + render_bg_tile_line_8bpp(0x01); + pos += 2; + } + bg_tiledata_state[TILE_8BIT][tile_num] = 0; + } +} + +#undef render_bg_tile_line_2bpp +#undef render_bg_tile_line_4bpp +#undef render_bg_tile_line_8bpp + +void PPU::flush_pixel_cache() { + uint16 main = get_palette(0); + uint16 sub = (regs.pseudo_hires || regs.bg_mode == 5 || regs.bg_mode == 6) + ? main + : regs.color_rgb; + + unsigned i = 255; + do { + pixel_cache[i].src_main = main; + pixel_cache[i].src_sub = sub; + pixel_cache[i].bg_main = BACK; + pixel_cache[i].bg_sub = BACK; + pixel_cache[i].ce_main = false; + pixel_cache[i].ce_sub = false; + pixel_cache[i].pri_main = 0; + pixel_cache[i].pri_sub = 0; + } while(i--); +} + +void PPU::alloc_tiledata_cache() { + bg_tiledata[TILE_2BIT] = new uint8_t[262144](); + bg_tiledata[TILE_4BIT] = new uint8_t[131072](); + bg_tiledata[TILE_8BIT] = new uint8_t[ 65536](); + bg_tiledata_state[TILE_2BIT] = new uint8_t[ 4096](); + bg_tiledata_state[TILE_4BIT] = new uint8_t[ 2048](); + bg_tiledata_state[TILE_8BIT] = new uint8_t[ 1024](); +} + +//marks all tiledata cache entries as dirty +void PPU::flush_tiledata_cache() { + for(unsigned i = 0; i < 4096; i++) bg_tiledata_state[TILE_2BIT][i] = 1; + for(unsigned i = 0; i < 2048; i++) bg_tiledata_state[TILE_4BIT][i] = 1; + for(unsigned i = 0; i < 1024; i++) bg_tiledata_state[TILE_8BIT][i] = 1; +} + +void PPU::free_tiledata_cache() { + delete[] bg_tiledata[TILE_2BIT]; + delete[] bg_tiledata[TILE_4BIT]; + delete[] bg_tiledata[TILE_8BIT]; + delete[] bg_tiledata_state[TILE_2BIT]; + delete[] bg_tiledata_state[TILE_4BIT]; + delete[] bg_tiledata_state[TILE_8BIT]; +} + +#endif diff --git a/libsnes/snes/alt/ppu-compatibility/render/line.cpp b/libsnes/snes/alt/ppu-compatibility/render/line.cpp new file mode 100644 index 0000000..c7e870f --- /dev/null +++ b/libsnes/snes/alt/ppu-compatibility/render/line.cpp @@ -0,0 +1,118 @@ +#ifdef PPU_CPP + +inline uint16 PPU::get_palette(uint8 index) { + const unsigned addr = index << 1; + return cgram[addr] + (cgram[addr + 1] << 8); +} + +//p = 00000bgr +//t = BBGGGRRR +//r = 0BBb00GGGg0RRRr0 +inline uint16 PPU::get_direct_color(uint8 p, uint8 t) { + return ((t & 7) << 2) | ((p & 1) << 1) | + (((t >> 3) & 7) << 7) | (((p >> 1) & 1) << 6) | + ((t >> 6) << 13) | ((p >> 2) << 12); +} + +inline uint16 PPU::get_pixel_normal(uint32 x) { + pixel_t &p = pixel_cache[x]; + uint16 src_main, src_sub; + uint8 bg_sub; + src_main = p.src_main; + + if(!regs.addsub_mode) { + bg_sub = BACK; + src_sub = regs.color_rgb; + } else { + bg_sub = p.bg_sub; + src_sub = p.src_sub; + } + + if(!window[COL].main[x]) { + if(!window[COL].sub[x]) { + return 0x0000; + } + src_main = 0x0000; + } + + if(!p.ce_main && regs.color_enabled[p.bg_main] && window[COL].sub[x]) { + bool halve = false; + if(regs.color_halve && window[COL].main[x]) { + if(regs.addsub_mode && bg_sub == BACK); + else { + halve = true; + } + } + return addsub(src_main, src_sub, halve); + } + + return src_main; +} + +inline uint16 PPU::get_pixel_swap(uint32 x) { + pixel_t &p = pixel_cache[x]; + uint16 src_main, src_sub; + uint8 bg_sub; + src_main = p.src_sub; + + if(!regs.addsub_mode) { + bg_sub = BACK; + src_sub = regs.color_rgb; + } else { + bg_sub = p.bg_main; + src_sub = p.src_main; + } + + if(!window[COL].main[x]) { + if(!window[COL].sub[x]) { + return 0x0000; + } + src_main = 0x0000; + } + + if(!p.ce_sub && regs.color_enabled[p.bg_sub] && window[COL].sub[x]) { + bool halve = false; + if(regs.color_halve && window[COL].main[x]) { + if(regs.addsub_mode && bg_sub == BACK); + else { + halve = true; + } + } + return addsub(src_main, src_sub, halve); + } + + return src_main; +} + +inline void PPU::render_line_output() { + uint32 *ptr = (uint32*)output + (line * 1024) + ((interlace() && field()) ? 512 : 0); + uint32 curr, prev; + + if(!regs.pseudo_hires && regs.bg_mode != 5 && regs.bg_mode != 6) { + for(unsigned x = 0; x < 256; x++) { + curr = (regs.display_brightness << 15) | get_pixel_normal(x); + *ptr++ = curr; + } + } else { + for(unsigned x = 0, prev = 0; x < 256; x++) { + //blending is disabled below, as this should be done via video filtering + //blending code is left for reference purposes + + curr = (regs.display_brightness << 15) | get_pixel_swap(x); + *ptr++ = curr; //(prev + curr - ((prev ^ curr) & 0x0421)) >> 1; + //prev = curr; + + curr = (regs.display_brightness << 15) | get_pixel_normal(x); + *ptr++ = curr; //(prev + curr - ((prev ^ curr) & 0x0421)) >> 1; + //prev = curr; + } + } +} + +inline void PPU::render_line_clear() { + uint32 *ptr = (uint32*)output + (line * 1024) + ((interlace() && field()) ? 512 : 0); + unsigned width = (!regs.pseudo_hires && regs.bg_mode != 5 && regs.bg_mode != 6) ? 256 : 512; + memset(ptr, 0, width * 2 * sizeof(uint32)); +} + +#endif diff --git a/libsnes/snes/alt/ppu-compatibility/render/mode7.cpp b/libsnes/snes/alt/ppu-compatibility/render/mode7.cpp new file mode 100644 index 0000000..747bafe --- /dev/null +++ b/libsnes/snes/alt/ppu-compatibility/render/mode7.cpp @@ -0,0 +1,143 @@ +#ifdef PPU_CPP + +//bsnes mode7 renderer +// +//base algorithm written by anomie +//bsnes implementation written by byuu +// +//supports mode 7 + extbg + rotate + zoom + direct color + scrolling + m7sel + windowing + mosaic +//interlace and pseudo-hires support are automatic via main rendering routine + +//13-bit sign extend +//--s---vvvvvvvvvv -> ssssssvvvvvvvvvv +#define CLIP(x) ( ((x) & 0x2000) ? ( (x) | ~0x03ff) : ((x) & 0x03ff) ) + +template +void PPU::render_line_mode7(uint8 pri0_pos, uint8 pri1_pos) { + if(layer_enabled[bg][0] == false) pri0_pos = 0; + if(layer_enabled[bg][1] == false) pri1_pos = 0; + if(pri0_pos + pri1_pos == 0) return; + + if(regs.bg_enabled[bg] == false && regs.bgsub_enabled[bg] == false) return; + + int32 px, py; + int32 tx, ty, tile, palette; + + int32 a = sclip<16>(cache.m7a); + int32 b = sclip<16>(cache.m7b); + int32 c = sclip<16>(cache.m7c); + int32 d = sclip<16>(cache.m7d); + + int32 cx = sclip<13>(cache.m7x); + int32 cy = sclip<13>(cache.m7y); + int32 hofs = sclip<13>(cache.m7_hofs); + int32 vofs = sclip<13>(cache.m7_vofs); + + int _pri, _x; + bool _bg_enabled = regs.bg_enabled[bg]; + bool _bgsub_enabled = regs.bgsub_enabled[bg]; + + build_window_tables(bg); + uint8 *wt_main = window[bg].main; + uint8 *wt_sub = window[bg].sub; + + int32 y = (regs.mode7_vflip == false ? line : 255 - line); + + uint16 *mtable_x, *mtable_y; + if(bg == BG1) { + mtable_x = (uint16*)mosaic_table[(regs.mosaic_enabled[BG1] == true) ? regs.mosaic_size : 0]; + mtable_y = (uint16*)mosaic_table[(regs.mosaic_enabled[BG1] == true) ? regs.mosaic_size : 0]; + } else { //bg == BG2 + //Mode7 EXTBG BG2 uses BG1 mosaic enable to control vertical mosaic, + //and BG2 mosaic enable to control horizontal mosaic... + mtable_x = (uint16*)mosaic_table[(regs.mosaic_enabled[BG2] == true) ? regs.mosaic_size : 0]; + mtable_y = (uint16*)mosaic_table[(regs.mosaic_enabled[BG1] == true) ? regs.mosaic_size : 0]; + } + + int32 psx = ((a * CLIP(hofs - cx)) & ~63) + ((b * CLIP(vofs - cy)) & ~63) + ((b * mtable_y[y]) & ~63) + (cx << 8); + int32 psy = ((c * CLIP(hofs - cx)) & ~63) + ((d * CLIP(vofs - cy)) & ~63) + ((d * mtable_y[y]) & ~63) + (cy << 8); + for(int32 x = 0; x < 256; x++) { + px = psx + (a * mtable_x[x]); + py = psy + (c * mtable_x[x]); + + //mask floating-point bits (low 8 bits) + px >>= 8; + py >>= 8; + + switch(regs.mode7_repeat) { + case 0: //screen repetition outside of screen area + case 1: { //same as case 0 + px &= 1023; + py &= 1023; + tx = ((px >> 3) & 127); + ty = ((py >> 3) & 127); + tile = vram[(ty * 128 + tx) << 1]; + palette = vram[(((tile << 6) + ((py & 7) << 3) + (px & 7)) << 1) + 1]; + } break; + case 2: { //palette color 0 outside of screen area + if((px | py) & ~1023) { + palette = 0; + } else { + px &= 1023; + py &= 1023; + tx = ((px >> 3) & 127); + ty = ((py >> 3) & 127); + tile = vram[(ty * 128 + tx) << 1]; + palette = vram[(((tile << 6) + ((py & 7) << 3) + (px & 7)) << 1) + 1]; + } + } break; + case 3: { //character 0 repetition outside of screen area + if((px | py) & ~1023) { + tile = 0; + } else { + px &= 1023; + py &= 1023; + tx = ((px >> 3) & 127); + ty = ((py >> 3) & 127); + tile = vram[(ty * 128 + tx) << 1]; + } + palette = vram[(((tile << 6) + ((py & 7) << 3) + (px & 7)) << 1) + 1]; + } break; + } + + if(bg == BG1) { + _pri = pri0_pos; + } else { + _pri = (palette >> 7) ? pri1_pos : pri0_pos; + palette &= 0x7f; + } + + if(!palette) continue; + + _x = (regs.mode7_hflip == false) ? (x) : (255 - x); + + uint32 col; + if(regs.direct_color == true && bg == BG1) { + //direct color mode does not apply to bg2, as it is only 128 colors... + col = get_direct_color(0, palette); + } else { + col = get_palette(palette); + } + + if(regs.bg_enabled[bg] == true && !wt_main[_x]) { + if(pixel_cache[_x].pri_main < _pri) { + pixel_cache[_x].pri_main = _pri; + pixel_cache[_x].bg_main = bg; + pixel_cache[_x].src_main = col; + pixel_cache[_x].ce_main = false; + } + } + if(regs.bgsub_enabled[bg] == true && !wt_sub[_x]) { + if(pixel_cache[_x].pri_sub < _pri) { + pixel_cache[_x].pri_sub = _pri; + pixel_cache[_x].bg_sub = bg; + pixel_cache[_x].src_sub = col; + pixel_cache[_x].ce_sub = false; + } + } + } +} + +#undef CLIP + +#endif diff --git a/libsnes/snes/alt/ppu-compatibility/render/oam.cpp b/libsnes/snes/alt/ppu-compatibility/render/oam.cpp new file mode 100644 index 0000000..42544a0 --- /dev/null +++ b/libsnes/snes/alt/ppu-compatibility/render/oam.cpp @@ -0,0 +1,237 @@ +#ifdef PPU_CPP + +void PPU::update_sprite_list(unsigned addr, uint8 data) { + if(addr < 0x0200) { + unsigned i = addr >> 2; + switch(addr & 3) { + case 0: sprite_list[i].x = (sprite_list[i].x & 0x0100) | data; break; + case 1: sprite_list[i].y = (data + 1) & 0xff; break; + case 2: sprite_list[i].character = data; break; + case 3: sprite_list[i].vflip = data & 0x80; + sprite_list[i].hflip = data & 0x40; + sprite_list[i].priority = (data >> 4) & 3; + sprite_list[i].palette = (data >> 1) & 7; + sprite_list[i].use_nameselect = data & 0x01; + } + } else { + unsigned i = (addr & 0x1f) << 2; + sprite_list[i + 0].x = ((data & 0x01) << 8) | (sprite_list[i + 0].x & 0xff); + sprite_list[i + 0].size = data & 0x02; + sprite_list[i + 1].x = ((data & 0x04) << 6) | (sprite_list[i + 1].x & 0xff); + sprite_list[i + 1].size = data & 0x08; + sprite_list[i + 2].x = ((data & 0x10) << 4) | (sprite_list[i + 2].x & 0xff); + sprite_list[i + 2].size = data & 0x20; + sprite_list[i + 3].x = ((data & 0x40) << 2) | (sprite_list[i + 3].x & 0xff); + sprite_list[i + 3].size = data & 0x80; + } +} + +void PPU::build_sprite_list() { + if(sprite_list_valid == true) return; + sprite_list_valid = true; + + for(unsigned i = 0; i < 128; i++) { + const bool size = sprite_list[i].size; + + switch(cache.oam_basesize) { + case 0: sprite_list[i].width = (!size) ? 8 : 16; + sprite_list[i].height = (!size) ? 8 : 16; + break; + case 1: sprite_list[i].width = (!size) ? 8 : 32; + sprite_list[i].height = (!size) ? 8 : 32; + break; + case 2: sprite_list[i].width = (!size) ? 8 : 64; + sprite_list[i].height = (!size) ? 8 : 64; + break; + case 3: sprite_list[i].width = (!size) ? 16 : 32; + sprite_list[i].height = (!size) ? 16 : 32; + break; + case 4: sprite_list[i].width = (!size) ? 16 : 64; + sprite_list[i].height = (!size) ? 16 : 64; + break; + case 5: sprite_list[i].width = (!size) ? 32 : 64; + sprite_list[i].height = (!size) ? 32 : 64; + break; + case 6: sprite_list[i].width = (!size) ? 16 : 32; + sprite_list[i].height = (!size) ? 32 : 64; + if(regs.oam_interlace && !size) sprite_list[i].height = 16; + //32x64 height is not affected by oam_interlace setting + break; + case 7: sprite_list[i].width = (!size) ? 16 : 32; + sprite_list[i].height = (!size) ? 32 : 32; + if(regs.oam_interlace && !size) sprite_list[i].height = 16; + break; + } + } +} + +bool PPU::is_sprite_on_scanline() { + //if sprite is entirely offscreen and doesn't wrap around to the left side of the screen, + //then it is not counted. this *should* be 256, and not 255, even though dot 256 is offscreen. + sprite_item *spr = &sprite_list[active_sprite]; + if(spr->x > 256 && (spr->x + spr->width - 1) < 512) return false; + + int spr_height = (regs.oam_interlace == false) ? (spr->height) : (spr->height >> 1); + if(line >= spr->y && line < (spr->y + spr_height)) return true; + if((spr->y + spr_height) >= 256 && line < ((spr->y + spr_height) & 255)) return true; + return false; +} + +void PPU::load_oam_tiles() { + sprite_item *spr = &sprite_list[active_sprite]; + uint16 tile_width = spr->width >> 3; + int x = spr->x; + int y = (line - spr->y) & 0xff; + if(regs.oam_interlace == true) { + y <<= 1; + } + + if(spr->vflip == true) { + if(spr->width == spr->height) { + y = (spr->height - 1) - y; + } else { + y = (y < spr->width) ? ((spr->width - 1) - y) : (spr->width + ((spr->width - 1) - (y - spr->width))); + } + } + + if(regs.oam_interlace == true) { + y = (spr->vflip == false) ? (y + field()) : (y - field()); + } + + x &= 511; + y &= 255; + + uint16 tdaddr = cache.oam_tdaddr; + uint16 chrx = (spr->character ) & 15; + uint16 chry = (spr->character >> 4) & 15; + if(spr->use_nameselect == true) { + tdaddr += (256 * 32) + (cache.oam_nameselect << 13); + } + chry += (y >> 3); + chry &= 15; + chry <<= 4; + + for(unsigned tx = 0; tx < tile_width; tx++) { + unsigned sx = (x + (tx << 3)) & 511; + //ignore sprites that are offscreen, x==256 is a special case that loads all tiles in OBJ + if(x != 256 && sx >= 256 && (sx + 7) < 512) continue; + + if(regs.oam_tilecount++ >= 34) break; + unsigned n = regs.oam_tilecount - 1; + oam_tilelist[n].x = sx; + oam_tilelist[n].y = y; + oam_tilelist[n].pri = spr->priority; + oam_tilelist[n].pal = 128 + (spr->palette << 4); + oam_tilelist[n].hflip = spr->hflip; + + unsigned mx = (spr->hflip == false) ? tx : ((tile_width - 1) - tx); + unsigned pos = tdaddr + ((chry + ((chrx + mx) & 15)) << 5); + oam_tilelist[n].tile = (pos >> 5) & 0x07ff; + } +} + +void PPU::render_oam_tile(int tile_num) { + oam_tileitem *t = &oam_tilelist[tile_num]; + uint8 *oam_td = (uint8*)bg_tiledata[COLORDEPTH_16]; + uint8 *oam_td_state = (uint8*)bg_tiledata_state[COLORDEPTH_16]; + + if(oam_td_state[t->tile] == 1) { + render_bg_tile(t->tile); + } + + unsigned sx = t->x; + uint8 *tile_ptr = (uint8*)oam_td + (t->tile << 6) + ((t->y & 7) << 3); + for(unsigned x = 0; x < 8; x++) { + sx &= 511; + if(sx < 256) { + unsigned col = *(tile_ptr + ((t->hflip == false) ? x : (7 - x))); + if(col) { + col += t->pal; + oam_line_pal[sx] = col; + oam_line_pri[sx] = t->pri; + } + } + sx++; + } +} + +void PPU::render_line_oam_rto() { + build_sprite_list(); + + regs.oam_itemcount = 0; + regs.oam_tilecount = 0; + memset(oam_line_pri, OAM_PRI_NONE, 256); + memset(oam_itemlist, 0xff, 32); + for(int s = 0; s < 34; s++) oam_tilelist[s].tile = 0xffff; + + for(int s = 0; s < 128; s++) { + active_sprite = (s + regs.oam_firstsprite) & 127; + if(is_sprite_on_scanline() == false) continue; + if(regs.oam_itemcount++ >= 32) break; + oam_itemlist[regs.oam_itemcount - 1] = (s + regs.oam_firstsprite) & 127; + } + + if(regs.oam_itemcount > 0 && oam_itemlist[regs.oam_itemcount - 1] != 0xff) { + regs.ioamaddr = 0x0200 + (oam_itemlist[regs.oam_itemcount - 1] >> 2); + } + + for(int s = 31; s >= 0; s--) { + if(oam_itemlist[s] == 0xff) continue; + active_sprite = oam_itemlist[s]; + load_oam_tiles(); + } + + regs.time_over |= (regs.oam_tilecount > 34); + regs.range_over |= (regs.oam_itemcount > 32); +} + +#define setpixel_main(x) \ + if(pixel_cache[x].pri_main < pri) { \ + pixel_cache[x].pri_main = pri; \ + pixel_cache[x].bg_main = OAM; \ + pixel_cache[x].src_main = get_palette(oam_line_pal[x]); \ + pixel_cache[x].ce_main = (oam_line_pal[x] < 192); \ + } +#define setpixel_sub(x) \ + if(pixel_cache[x].pri_sub < pri) { \ + pixel_cache[x].pri_sub = pri; \ + pixel_cache[x].bg_sub = OAM; \ + pixel_cache[x].src_sub = get_palette(oam_line_pal[x]); \ + pixel_cache[x].ce_sub = (oam_line_pal[x] < 192); \ + } + +void PPU::render_line_oam(uint8 pri0_pos, uint8 pri1_pos, uint8 pri2_pos, uint8 pri3_pos) { + if(layer_enabled[OAM][0] == false) pri0_pos = 0; + if(layer_enabled[OAM][1] == false) pri1_pos = 0; + if(layer_enabled[OAM][2] == false) pri2_pos = 0; + if(layer_enabled[OAM][3] == false) pri3_pos = 0; + if(pri0_pos + pri1_pos + pri2_pos + pri3_pos == 0) return; + + if(regs.bg_enabled[OAM] == false && regs.bgsub_enabled[OAM] == false) return; + + for(unsigned s = 0; s < 34; s++) { + if(oam_tilelist[s].tile == 0xffff) continue; + render_oam_tile(s); + } + + bool bg_enabled = regs.bg_enabled[OAM]; + bool bgsub_enabled = regs.bgsub_enabled[OAM]; + + build_window_tables(OAM); + uint8 *wt_main = window[OAM].main; + uint8 *wt_sub = window[OAM].sub; + + unsigned pri_tbl[4] = { pri0_pos, pri1_pos, pri2_pos, pri3_pos }; + for(int x = 0; x < 256; x++) { + if(oam_line_pri[x] == OAM_PRI_NONE) continue; + + unsigned pri = pri_tbl[oam_line_pri[x]]; + if(bg_enabled == true && !wt_main[x]) { setpixel_main(x); } + if(bgsub_enabled == true && !wt_sub[x]) { setpixel_sub(x); } + } +} + +#undef setpixel_main +#undef setpixel_sub + +#endif diff --git a/libsnes/snes/alt/ppu-compatibility/render/render.cpp b/libsnes/snes/alt/ppu-compatibility/render/render.cpp new file mode 100644 index 0000000..c185fa0 --- /dev/null +++ b/libsnes/snes/alt/ppu-compatibility/render/render.cpp @@ -0,0 +1,129 @@ +#ifdef PPU_CPP + +#include "cache.cpp" +#include "windows.cpp" +#include "bg.cpp" +#include "oam.cpp" +#include "mode7.cpp" +#include "addsub.cpp" +#include "line.cpp" + +//Mode 0: -> +// 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 +// BG4B, BG3B, OAM0, BG4A, BG3A, OAM1, BG2B, BG1B, OAM2, BG2A, BG1A, OAM3 +void PPU::render_line_mode0() { + render_line_bg<0, BG1, COLORDEPTH_4>(8, 11); + render_line_bg<0, BG2, COLORDEPTH_4>(7, 10); + render_line_bg<0, BG3, COLORDEPTH_4>(2, 5); + render_line_bg<0, BG4, COLORDEPTH_4>(1, 4); + render_line_oam(3, 6, 9, 12); +} + +//Mode 1 (pri=1): -> +// 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 +// BG3B, OAM0, OAM1, BG2B, BG1B, OAM2, BG2A, BG1A, OAM3, BG3A +// +//Mode 1 (pri=0): -> +// 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 +// BG3B, OAM0, BG3A, OAM1, BG2B, BG1B, OAM2, BG2A, BG1A, OAM3 +void PPU::render_line_mode1() { + if(regs.bg3_priority) { + render_line_bg<1, BG1, COLORDEPTH_16>(5, 8); + render_line_bg<1, BG2, COLORDEPTH_16>(4, 7); + render_line_bg<1, BG3, COLORDEPTH_4 >(1, 10); + render_line_oam(2, 3, 6, 9); + } else { + render_line_bg<1, BG1, COLORDEPTH_16>(6, 9); + render_line_bg<1, BG2, COLORDEPTH_16>(5, 8); + render_line_bg<1, BG3, COLORDEPTH_4 >(1, 3); + render_line_oam(2, 4, 7, 10); + } +} + +//Mode 2: -> +// 1, 2, 3, 4, 5, 6, 7, 8 +// BG2B, OAM0, BG1B, OAM1, BG2A, OAM2, BG1A, OAM3 +void PPU::render_line_mode2() { + render_line_bg<2, BG1, COLORDEPTH_16>(3, 7); + render_line_bg<2, BG2, COLORDEPTH_16>(1, 5); + render_line_oam(2, 4, 6, 8); +} + +//Mode 3: -> +// 1, 2, 3, 4, 5, 6, 7, 8 +// BG2B, OAM0, BG1B, OAM1, BG2A, OAM2, BG1A, OAM3 +void PPU::render_line_mode3() { + render_line_bg<3, BG1, COLORDEPTH_256>(3, 7); + render_line_bg<3, BG2, COLORDEPTH_16 >(1, 5); + render_line_oam(2, 4, 6, 8); +} + +//Mode 4: -> +// 1, 2, 3, 4, 5, 6, 7, 8 +// BG2B, OAM0, BG1B, OAM1, BG2A, OAM2, BG1A, OAM3 +void PPU::render_line_mode4() { + render_line_bg<4, BG1, COLORDEPTH_256>(3, 7); + render_line_bg<4, BG2, COLORDEPTH_4 >(1, 5); + render_line_oam(2, 4, 6, 8); +} + +//Mode 5: -> +// 1, 2, 3, 4, 5, 6, 7, 8 +// BG2B, OAM0, BG1B, OAM1, BG2A, OAM2, BG1A, OAM3 +void PPU::render_line_mode5() { + render_line_bg<5, BG1, COLORDEPTH_16>(3, 7); + render_line_bg<5, BG2, COLORDEPTH_4 >(1, 5); + render_line_oam(2, 4, 6, 8); +} + +//Mode 6: -> +// 1, 2, 3, 4, 5, 6 +// OAM0, BG1B, OAM1, OAM2, BG1A, OAM3 +void PPU::render_line_mode6() { + render_line_bg<6, BG1, COLORDEPTH_16>(2, 5); + render_line_oam(1, 3, 4, 6); +} + +//Mode7: -> +// 1, 2, 3, 4, 5 +// OAM0, BG1n, OAM1, OAM2, OAM3 + +//Mode 7 EXTBG: -> +// 1, 2, 3, 4, 5, 6, 7 +// BG2B, OAM0, BG1n, OAM1, BG2A, OAM2, OAM3 +void PPU::render_line_mode7() { + if(regs.mode7_extbg == false) { + render_line_mode7(2, 2); + render_line_oam(1, 3, 4, 5); + } else { + render_line_mode7(3, 3); + render_line_mode7(1, 5); + render_line_oam(2, 4, 6, 7); + } +} + +void PPU::render_line() { + if(regs.display_disabled == true) { + render_line_clear(); + return; + } + + flush_pixel_cache(); + build_window_tables(COL); + update_bg_info(); + + switch(regs.bg_mode) { + case 0: render_line_mode0(); break; + case 1: render_line_mode1(); break; + case 2: render_line_mode2(); break; + case 3: render_line_mode3(); break; + case 4: render_line_mode4(); break; + case 5: render_line_mode5(); break; + case 6: render_line_mode6(); break; + case 7: render_line_mode7(); break; + } + + render_line_output(); +} + +#endif diff --git a/libsnes/snes/alt/ppu-compatibility/render/render.hpp b/libsnes/snes/alt/ppu-compatibility/render/render.hpp new file mode 100644 index 0000000..9143800 --- /dev/null +++ b/libsnes/snes/alt/ppu-compatibility/render/render.hpp @@ -0,0 +1,99 @@ +//render.cpp +inline void render_line_mode0(); +inline void render_line_mode1(); +inline void render_line_mode2(); +inline void render_line_mode3(); +inline void render_line_mode4(); +inline void render_line_mode5(); +inline void render_line_mode6(); +inline void render_line_mode7(); + +//cache.cpp +enum { COLORDEPTH_4 = 0, COLORDEPTH_16 = 1, COLORDEPTH_256 = 2 }; +enum { TILE_2BIT = 0, TILE_4BIT = 1, TILE_8BIT = 2 }; + +struct pixel_t { + //bgr555 color data for main/subscreen pixels: 0x0000 = transparent / use palette color # 0 + //needs to be bgr555 instead of palette index for direct color mode ($2130 bit 0) to work + uint16 src_main, src_sub; + //indicates source of palette # for main/subscreen (BG1-4, OAM, or back) + uint8 bg_main, bg_sub; + //color_exemption -- true when bg == OAM && palette index >= 192, disables color add/sub effects + uint8 ce_main, ce_sub; + //priority level of src_n. to set src_n, + //the priority of the pixel must be >pri_n + uint8 pri_main, pri_sub; +} pixel_cache[256]; + +uint8 *bg_tiledata[3]; +uint8 *bg_tiledata_state[3]; //0 = valid, 1 = dirty + +template void render_bg_tile(uint16 tile_num); +inline void flush_pixel_cache(); +void alloc_tiledata_cache(); +void flush_tiledata_cache(); +void free_tiledata_cache(); + +//windows.cpp +struct window_t { + uint8 main[256], sub[256]; +} window[6]; + +void build_window_table(uint8 bg, bool mainscreen); +void build_window_tables(uint8 bg); + +//bg.cpp +struct { + uint16 tw, th; //tile width, height + uint16 mx, my; //screen mask x, y + uint16 scx, scy; //sc index offsets +} bg_info[4]; +void update_bg_info(); + +template uint16 bg_get_tile(uint16 x, uint16 y); +template void render_line_bg(uint8 pri0_pos, uint8 pri1_pos); + +//oam.cpp +struct sprite_item { + uint8 width, height; + uint16 x, y; + uint8 character; + bool use_nameselect; + bool vflip, hflip; + uint8 palette; + uint8 priority; + bool size; +} sprite_list[128]; +bool sprite_list_valid; +unsigned active_sprite; + +uint8 oam_itemlist[32]; +struct oam_tileitem { + uint16 x, y, pri, pal, tile; + bool hflip; +} oam_tilelist[34]; + +enum { OAM_PRI_NONE = 4 }; +uint8 oam_line_pal[256], oam_line_pri[256]; + +void update_sprite_list(unsigned addr, uint8 data); +void build_sprite_list(); +bool is_sprite_on_scanline(); +void load_oam_tiles(); +void render_oam_tile(int tile_num); +void render_line_oam_rto(); +void render_line_oam(uint8 pri0_pos, uint8 pri1_pos, uint8 pri2_pos, uint8 pri3_pos); + +//mode7.cpp +template void render_line_mode7(uint8 pri0_pos, uint8 pri1_pos); + +//addsub.cpp +inline uint16 addsub(uint32 x, uint32 y, bool halve); + +//line.cpp +inline uint16 get_palette(uint8 index); +inline uint16 get_direct_color(uint8 p, uint8 t); +inline uint16 get_pixel_normal(uint32 x); +inline uint16 get_pixel_swap(uint32 x); +void render_line_output(); +void render_line_clear(); diff --git a/libsnes/snes/alt/ppu-compatibility/render/windows.cpp b/libsnes/snes/alt/ppu-compatibility/render/windows.cpp new file mode 100644 index 0000000..d8b4fd1 --- /dev/null +++ b/libsnes/snes/alt/ppu-compatibility/render/windows.cpp @@ -0,0 +1,70 @@ +#ifdef PPU_CPP + +//screen: 0 = main, 1 = sub +void PPU::build_window_table(uint8 bg, bool screen) { + bool set = 1, clr = 0; + uint8 *table = (screen == 0 ? window[bg].main : window[bg].sub); + + if(bg != COL) { + if(screen == 0 && regs.window_enabled[bg] == false) { + memset(table, 0, 256); + return; + } + if(screen == 1 && regs.sub_window_enabled[bg] == false) { + memset(table, 0, 256); + return; + } + } else { + switch(screen == 0 ? regs.color_mask : regs.colorsub_mask) { + case 0: memset(table, 1, 256); return; //always + case 3: memset(table, 0, 256); return; //never + case 1: set = 1, clr = 0; break; //inside window only + case 2: set = 0, clr = 1; break; //outside window only + } + } + + const uint16 window1_left = regs.window1_left; + const uint16 window1_right = regs.window1_right; + const uint16 window2_left = regs.window2_left; + const uint16 window2_right = regs.window2_right; + + if(regs.window1_enabled[bg] == false && regs.window2_enabled[bg] == false) { + memset(table, clr, 256); + return; + } + + if(regs.window1_enabled[bg] == true && regs.window2_enabled[bg] == false) { + if(regs.window1_invert[bg] == true) set ^= clr ^= set ^= clr; + for(unsigned x = 0; x < 256; x++) { + table[x] = (x >= window1_left && x <= window1_right) ? set : clr; + } + return; + } + + if(regs.window1_enabled[bg] == false && regs.window2_enabled[bg] == true) { + if(regs.window2_invert[bg] == true) set ^= clr ^= set ^= clr; + for(unsigned x = 0; x < 256; x++) { + table[x] = (x >= window2_left && x <= window2_right) ? set : clr; + } + return; + } + + for(unsigned x = 0; x < 256; x++) { + bool w1_mask = (x >= window1_left && x <= window1_right) ^ regs.window1_invert[bg]; + bool w2_mask = (x >= window2_left && x <= window2_right) ^ regs.window2_invert[bg]; + + switch(regs.window_mask[bg]) { + case 0: table[x] = (w1_mask | w2_mask) == 1 ? set : clr; break; //or + case 1: table[x] = (w1_mask & w2_mask) == 1 ? set : clr; break; //and + case 2: table[x] = (w1_mask ^ w2_mask) == 1 ? set : clr; break; //xor + case 3: table[x] = (w1_mask ^ w2_mask) == 0 ? set : clr; break; //xnor + } + } +} + +void PPU::build_window_tables(uint8 bg) { + build_window_table(bg, 0); + build_window_table(bg, 1); +} + +#endif diff --git a/libsnes/snes/alt/ppu-compatibility/serialization.cpp b/libsnes/snes/alt/ppu-compatibility/serialization.cpp new file mode 100644 index 0000000..d2711e4 --- /dev/null +++ b/libsnes/snes/alt/ppu-compatibility/serialization.cpp @@ -0,0 +1,207 @@ +#ifdef PPU_CPP + +void PPUcounter::serialize(serializer &s) { + s.integer(status.interlace); + s.integer(status.field); + s.integer(status.vcounter); + s.integer(status.hcounter); + + s.array(history.field); + s.array(history.vcounter); + s.array(history.hcounter); + s.integer(history.index); +} + +void PPU::serialize(serializer &s) { + Processor::serialize(s); + PPUcounter::serialize(s); + + s.array(vram); + s.array(oam); + s.array(cgram); + + s.integer(ppu1_version); + s.integer(ppu2_version); + + s.integer(region); + s.integer(line); + + s.integer(display.interlace); + s.integer(display.overscan); + + s.integer(cache.oam_basesize); + s.integer(cache.oam_nameselect); + s.integer(cache.oam_tdaddr); + + s.integer(regs.ppu1_mdr); + s.integer(regs.ppu2_mdr); + for(unsigned n = 0; n < 4; n++) s.integer(regs.bg_y[n]); + + s.integer(regs.ioamaddr); + s.integer(regs.icgramaddr); + + s.integer(regs.display_disabled); + s.integer(regs.display_brightness); + + s.integer(regs.oam_basesize); + s.integer(regs.oam_nameselect); + s.integer(regs.oam_tdaddr); + + s.integer(regs.oam_baseaddr); + s.integer(regs.oam_addr); + s.integer(regs.oam_priority); + s.integer(regs.oam_firstsprite); + + s.integer(regs.oam_latchdata); + + for(unsigned n = 0; n < 4; n++) s.integer(regs.bg_tilesize[n]); + s.integer(regs.bg3_priority); + s.integer(regs.bg_mode); + + s.integer(regs.mosaic_size); + for(unsigned n = 0; n < 4; n++) s.integer(regs.mosaic_enabled[n]); + s.integer(regs.mosaic_countdown); + + for(unsigned n = 0; n < 4; n++) s.integer(regs.bg_scaddr[n]); + for(unsigned n = 0; n < 4; n++) s.integer(regs.bg_scsize[n]); + + for(unsigned n = 0; n < 4; n++) s.integer(regs.bg_tdaddr[n]); + + s.integer(regs.bg_ofslatch); + s.integer(regs.m7_hofs); + s.integer(regs.m7_vofs); + for(unsigned n = 0; n < 4; n++) s.integer(regs.bg_hofs[n]); + for(unsigned n = 0; n < 4; n++) s.integer(regs.bg_vofs[n]); + + s.integer(regs.vram_incmode); + s.integer(regs.vram_mapping); + s.integer(regs.vram_incsize); + + s.integer(regs.vram_addr); + + s.integer(regs.mode7_repeat); + s.integer(regs.mode7_vflip); + s.integer(regs.mode7_hflip); + + s.integer(regs.m7_latch); + s.integer(regs.m7a); + s.integer(regs.m7b); + s.integer(regs.m7c); + s.integer(regs.m7d); + s.integer(regs.m7x); + s.integer(regs.m7y); + + s.integer(regs.cgram_addr); + + s.integer(regs.cgram_latchdata); + + for(unsigned n = 0; n < 6; n++) s.integer(regs.window1_enabled[n]); + for(unsigned n = 0; n < 6; n++) s.integer(regs.window1_invert [n]); + for(unsigned n = 0; n < 6; n++) s.integer(regs.window2_enabled[n]); + for(unsigned n = 0; n < 6; n++) s.integer(regs.window2_invert [n]); + + s.integer(regs.window1_left); + s.integer(regs.window1_right); + s.integer(regs.window2_left); + s.integer(regs.window2_right); + + for(unsigned n = 0; n < 6; n++) s.integer(regs.window_mask[n]); + for(unsigned n = 0; n < 5; n++) s.integer(regs.bg_enabled[n]); + for(unsigned n = 0; n < 5; n++) s.integer(regs.bgsub_enabled[n]); + for(unsigned n = 0; n < 5; n++) s.integer(regs.window_enabled[n]); + for(unsigned n = 0; n < 5; n++) s.integer(regs.sub_window_enabled[n]); + + s.integer(regs.color_mask); + s.integer(regs.colorsub_mask); + s.integer(regs.addsub_mode); + s.integer(regs.direct_color); + + s.integer(regs.color_mode); + s.integer(regs.color_halve); + for(unsigned n = 0; n < 6; n++) s.integer(regs.color_enabled[n]); + + s.integer(regs.color_r); + s.integer(regs.color_g); + s.integer(regs.color_b); + s.integer(regs.color_rgb); + + s.integer(regs.mode7_extbg); + s.integer(regs.pseudo_hires); + s.integer(regs.overscan); + s.integer(regs.scanlines); + s.integer(regs.oam_interlace); + s.integer(regs.interlace); + + s.integer(regs.hcounter); + s.integer(regs.vcounter); + s.integer(regs.latch_hcounter); + s.integer(regs.latch_vcounter); + s.integer(regs.counters_latched); + + s.integer(regs.vram_readbuffer); + + s.integer(regs.time_over); + s.integer(regs.range_over); + s.integer(regs.oam_itemcount); + s.integer(regs.oam_tilecount); + + for(unsigned n = 0; n < 256; n++) { + s.integer(pixel_cache[n].src_main); + s.integer(pixel_cache[n].src_sub); + s.integer(pixel_cache[n].bg_main); + s.integer(pixel_cache[n].bg_sub); + s.integer(pixel_cache[n].ce_main); + s.integer(pixel_cache[n].ce_sub); + s.integer(pixel_cache[n].pri_main); + s.integer(pixel_cache[n].pri_sub); + } + + //better to just take a small speed hit than store all of bg_tiledata[3][] ... + flush_tiledata_cache(); + + for(unsigned n = 0; n < 6; n++) { + s.array(window[n].main, 256); + s.array(window[n].sub, 256); + } + + for(unsigned n = 0; n < 4; n++) { + s.integer(bg_info[n].tw); + s.integer(bg_info[n].th); + s.integer(bg_info[n].mx); + s.integer(bg_info[n].my); + s.integer(bg_info[n].scx); + s.integer(bg_info[n].scy); + } + + for(unsigned n = 0; n < 128; n++) { + s.integer(sprite_list[n].width); + s.integer(sprite_list[n].height); + s.integer(sprite_list[n].x); + s.integer(sprite_list[n].y); + s.integer(sprite_list[n].character); + s.integer(sprite_list[n].use_nameselect); + s.integer(sprite_list[n].vflip); + s.integer(sprite_list[n].hflip); + s.integer(sprite_list[n].palette); + s.integer(sprite_list[n].priority); + s.integer(sprite_list[n].size); + } + s.integer(sprite_list_valid); + s.integer(active_sprite); + + s.array(oam_itemlist, 32); + + for(unsigned n = 0; n < 34; n++) { + s.integer(oam_tilelist[n].x); + s.integer(oam_tilelist[n].y); + s.integer(oam_tilelist[n].pri); + s.integer(oam_tilelist[n].pal); + s.integer(oam_tilelist[n].tile); + s.integer(oam_tilelist[n].hflip); + } + + s.array(oam_line_pal, 256); + s.array(oam_line_pri, 256); +} + +#endif diff --git a/libsnes/snes/alt/ppu-parallel/mmio.cpp b/libsnes/snes/alt/ppu-parallel/mmio.cpp new file mode 100644 index 0000000..8abfb74 --- /dev/null +++ b/libsnes/snes/alt/ppu-parallel/mmio.cpp @@ -0,0 +1,14 @@ +#ifdef PPU_CPP + +bool PPU::display_disable() const { return r[0x00] & 0x80; } +unsigned PPU::display_brightness() const { return r[0x00] & 0x0f; } + +uint8 PPU::mmio_read(unsigned addr) { + return cpu.regs.mdr; +} + +void PPU::mmio_write(unsigned addr, uint8 data) { + r[addr & 0x3f] = data; +} + +#endif diff --git a/libsnes/snes/alt/ppu-parallel/ppu.cpp b/libsnes/snes/alt/ppu-parallel/ppu.cpp new file mode 100644 index 0000000..1c3dcb7 --- /dev/null +++ b/libsnes/snes/alt/ppu-parallel/ppu.cpp @@ -0,0 +1,81 @@ +#include + +#define PPU_CPP +namespace SNES { + +#include "mmio.cpp" +PPU ppu; + +void PPU::latch_counters() { +} + +bool PPU::interlace() const { + return false; +} + +bool PPU::overscan() const { + return false; +} + +bool PPU::hires() const { + return false; +} + +void PPU::enter() { + scanline(); + clock += lineclocks(); + tick(lineclocks()); +} + +void PPU::scanline() { + if(vcounter() == 0) return frame(); + if(vcounter() > 224) return; +} + +void PPU::frame() { +} + +void PPU::enable() { + bus.map(Bus::MapMode::Direct, 0x00, 0x3f, 0x2100, 0x213f, { &PPU::mmio_read, this }, { &PPU::mmio_write, this }); + bus.map(Bus::MapMode::Direct, 0x80, 0xbf, 0x2100, 0x213f, { &PPU::mmio_read, this }, { &PPU::mmio_write, this }); +} + +void PPU::power() { + for(unsigned n = 0; n < 512 * 480; n++) output[n] = rand() & ((1 << 19) - 1); + + for(auto &n : vram) n = 0; + for(auto &n : oam) n = 0; + for(auto &n : cgram) n = 0; + for(auto &n : r) n = 0; + reset(); +} + +void PPU::reset() { + PPUcounter::reset(); +} + +void PPUcounter::serialize(serializer &s) { + s.integer(status.interlace); + s.integer(status.field); + s.integer(status.vcounter); + s.integer(status.hcounter); + + s.array(history.field); + s.array(history.vcounter); + s.array(history.hcounter); + s.integer(history.index); +} + +void PPU::serialize(serializer &s) { +} + +PPU::PPU() { + surface = new uint32[512 * 512]; + output = surface + 16 * 512; +} + +PPU::~PPU() { + delete[] surface; +} + +} diff --git a/libsnes/snes/alt/ppu-parallel/ppu.hpp b/libsnes/snes/alt/ppu-parallel/ppu.hpp new file mode 100644 index 0000000..3a47d33 --- /dev/null +++ b/libsnes/snes/alt/ppu-parallel/ppu.hpp @@ -0,0 +1,40 @@ +struct PPU : public Processor, public PPUcounter { + uint8 vram[64 * 1024]; + uint8 oam[544]; + uint8 cgram[512]; + uint8 r[64]; + + enum : bool { Threaded = false }; + + void latch_counters(); + bool interlace() const; + bool overscan() const; + bool hires() const; + + void enter(); + void enable(); + void power(); + void reset(); + + void scanline(); + void frame(); + + void serialize(serializer&); + PPU(); + ~PPU(); + +private: + uint32 *surface; + uint32 *output; + + //mmio.cpp + alwaysinline bool display_disable() const; + alwaysinline unsigned display_brightness() const; + + uint8 mmio_read(unsigned addr); + void mmio_write(unsigned addr, uint8 data); + + friend class Video; +}; + +extern PPU ppu; diff --git a/libsnes/snes/alt/ppu-performance/background/background.cpp b/libsnes/snes/alt/ppu-performance/background/background.cpp new file mode 100644 index 0000000..29c70aa --- /dev/null +++ b/libsnes/snes/alt/ppu-performance/background/background.cpp @@ -0,0 +1,184 @@ +#ifdef PPU_CPP + +#include "mode7.cpp" + +unsigned PPU::Background::get_tile(unsigned hoffset, unsigned voffset) { + unsigned tile_x = (hoffset & mask_x) >> tile_width; + unsigned tile_y = (voffset & mask_y) >> tile_height; + + unsigned tile_pos = ((tile_y & 0x1f) << 5) + (tile_x & 0x1f); + if(tile_y & 0x20) tile_pos += scy; + if(tile_x & 0x20) tile_pos += scx; + + const uint16 tiledata_addr = regs.screen_addr + (tile_pos << 1); + return (ppu.vram[tiledata_addr + 0] << 0) + (ppu.vram[tiledata_addr + 1] << 8); +} + +void PPU::Background::offset_per_tile(unsigned x, unsigned y, unsigned &hoffset, unsigned &voffset) { + unsigned opt_x = (x + (hscroll & 7)), hval, vval; + if(opt_x >= 8) { + hval = self.bg3.get_tile((opt_x - 8) + (self.bg3.regs.hoffset & ~7), self.bg3.regs.voffset + 0); + if(self.regs.bgmode != 4) + vval = self.bg3.get_tile((opt_x - 8) + (self.bg3.regs.hoffset & ~7), self.bg3.regs.voffset + 8); + + if(self.regs.bgmode == 4) { + if(hval & opt_valid_bit) { + if(!(hval & 0x8000)) { + hoffset = opt_x + (hval & ~7); + } else { + voffset = y + hval; + } + } + } else { + if(hval & opt_valid_bit) { + hoffset = opt_x + (hval & ~7); + } + if(vval & opt_valid_bit) { + voffset = y + vval; + } + } + } +} + +void PPU::Background::scanline() { + if(self.vcounter() == 1) { + mosaic_vcounter = regs.mosaic + 1; + mosaic_voffset = 1; + } else if(--mosaic_vcounter == 0) { + mosaic_vcounter = regs.mosaic + 1; + mosaic_voffset += regs.mosaic + 1; + } + if(self.regs.display_disable) return; + + hires = (self.regs.bgmode == 5 || self.regs.bgmode == 6); + width = !hires ? 256 : 512; + + tile_height = regs.tile_size ? 4 : 3; + tile_width = hires ? 4 : tile_height; + + mask_x = (tile_height == 4 ? width << 1 : width); + mask_y = mask_x; + if(regs.screen_size & 1) mask_x <<= 1; + if(regs.screen_size & 2) mask_y <<= 1; + mask_x--; + mask_y--; + + scx = (regs.screen_size & 1 ? 32 << 5 : 0); + scy = (regs.screen_size & 2 ? 32 << 5 : 0); + if(regs.screen_size == 3) scy <<= 1; +} + +void PPU::Background::render() { + if(regs.mode == Mode::Inactive) return; + if(regs.main_enable == false && regs.sub_enable == false) return; + + if(regs.main_enable) window.render(0); + if(regs.sub_enable) window.render(1); + if(regs.mode == Mode::Mode7) return render_mode7(); + + unsigned priority0 = (priority0_enable ? regs.priority0 : 0); + unsigned priority1 = (priority1_enable ? regs.priority1 : 0); + if(priority0 + priority1 == 0) return; + + unsigned mosaic_hcounter = 1; + unsigned mosaic_palette = 0; + unsigned mosaic_priority = 0; + unsigned mosaic_color = 0; + + const unsigned bgpal_index = (self.regs.bgmode == 0 ? id << 5 : 0); + const unsigned pal_size = 2 << regs.mode; + const unsigned tile_mask = 0x0fff >> regs.mode; + const unsigned tiledata_index = regs.tiledata_addr >> (4 + regs.mode); + + hscroll = regs.hoffset; + vscroll = regs.voffset; + + unsigned y = (regs.mosaic == 0 ? self.vcounter() : mosaic_voffset); + if(hires) { + hscroll <<= 1; + if(self.regs.interlace) y = (y << 1) + self.field(); + } + + unsigned tile_pri, tile_num; + unsigned pal_index, pal_num; + unsigned hoffset, voffset, col; + bool mirror_x, mirror_y; + + const bool is_opt_mode = (self.regs.bgmode == 2 || self.regs.bgmode == 4 || self.regs.bgmode == 6); + const bool is_direct_color_mode = (self.screen.regs.direct_color == true && id == ID::BG1 && (self.regs.bgmode == 3 || self.regs.bgmode == 4)); + + signed x = 0 - (hscroll & 7); + while(x < width) { + hoffset = x + hscroll; + voffset = y + vscroll; + if(is_opt_mode) offset_per_tile(x, y, hoffset, voffset); + hoffset &= mask_x; + voffset &= mask_y; + + tile_num = get_tile(hoffset, voffset); + mirror_y = tile_num & 0x8000; + mirror_x = tile_num & 0x4000; + tile_pri = tile_num & 0x2000 ? priority1 : priority0; + pal_num = (tile_num >> 10) & 7; + pal_index = (bgpal_index + (pal_num << pal_size)) & 0xff; + + if(tile_width == 4 && (bool)(hoffset & 8) != mirror_x) tile_num += 1; + if(tile_height == 4 && (bool)(voffset & 8) != mirror_y) tile_num += 16; + tile_num = ((tile_num & 0x03ff) + tiledata_index) & tile_mask; + + if(mirror_y) voffset ^= 7; + unsigned mirror_xmask = !mirror_x ? 0 : 7; + + uint8 *tiledata = self.cache.tile(regs.mode, tile_num); + tiledata += ((voffset & 7) * 8); + + for(unsigned n = 0; n < 8; n++, x++) { + if(x & width) continue; + if(--mosaic_hcounter == 0) { + mosaic_hcounter = regs.mosaic + 1; + mosaic_palette = tiledata[n ^ mirror_xmask]; + mosaic_priority = tile_pri; + if(is_direct_color_mode) { + mosaic_color = self.screen.get_direct_color(pal_num, mosaic_palette); + } else { + mosaic_color = self.screen.get_palette(pal_index + mosaic_palette); + } + } + if(mosaic_palette == 0) continue; + + if(hires == false) { + if(regs.main_enable && !window.main[x]) self.screen.output.plot_main(x, mosaic_color, mosaic_priority, id); + if(regs.sub_enable && !window.sub[x]) self.screen.output.plot_sub(x, mosaic_color, mosaic_priority, id); + } else { + signed half_x = x >> 1; + if(x & 1) { + if(regs.main_enable && !window.main[half_x]) self.screen.output.plot_main(half_x, mosaic_color, mosaic_priority, id); + } else { + if(regs.sub_enable && !window.sub[half_x]) self.screen.output.plot_sub(half_x, mosaic_color, mosaic_priority, id); + } + } + } + } +} + +PPU::Background::Background(PPU &self, unsigned id) : self(self), id(id) { + priority0_enable = true; + priority1_enable = true; + + opt_valid_bit = (id == ID::BG1 ? 0x2000 : id == ID::BG2 ? 0x4000 : 0x0000); + + mosaic_table = new uint16*[16]; + for(unsigned m = 0; m < 16; m++) { + mosaic_table[m] = new uint16[4096]; + for(unsigned x = 0; x < 4096; x++) { + mosaic_table[m][x] = (x / (m + 1)) * (m + 1); + } + } +} + +PPU::Background::~Background() { + for(unsigned m = 0; m < 16; m++) delete[] mosaic_table[m]; + delete[] mosaic_table; +} + +#endif diff --git a/libsnes/snes/alt/ppu-performance/background/background.hpp b/libsnes/snes/alt/ppu-performance/background/background.hpp new file mode 100644 index 0000000..38121a4 --- /dev/null +++ b/libsnes/snes/alt/ppu-performance/background/background.hpp @@ -0,0 +1,65 @@ +struct Background { + struct ID { enum { BG1, BG2, BG3, BG4 }; }; + struct Mode { enum { BPP2, BPP4, BPP8, Mode7, Inactive }; }; + struct ScreenSize { enum { Size32x32, Size32x64, Size64x32, Size64x64 }; }; + struct TileSize { enum { Size8x8, Size16x16 }; }; + + bool priority0_enable; + bool priority1_enable; + + struct Regs { + unsigned mode; + unsigned priority0; + unsigned priority1; + + bool tile_size; + unsigned mosaic; + + unsigned screen_addr; + unsigned screen_size; + unsigned tiledata_addr; + + unsigned hoffset; + unsigned voffset; + + bool main_enable; + bool sub_enable; + } regs; + + uint16 **mosaic_table; + + const unsigned id; + unsigned opt_valid_bit; + + bool hires; + signed width; + + unsigned tile_width; + unsigned tile_height; + + unsigned mask_x; + unsigned mask_y; + + unsigned scx; + unsigned scy; + + unsigned hscroll; + unsigned vscroll; + + unsigned mosaic_vcounter; + unsigned mosaic_voffset; + + LayerWindow window; + + alwaysinline unsigned get_tile(unsigned hoffset, unsigned voffset); + void offset_per_tile(unsigned x, unsigned y, unsigned &hoffset, unsigned &voffset); + void scanline(); + void render(); + void render_mode7(); + + void serialize(serializer&); + Background(PPU &self, unsigned id); + ~Background(); + + PPU &self; +}; diff --git a/libsnes/snes/alt/ppu-performance/background/mode7.cpp b/libsnes/snes/alt/ppu-performance/background/mode7.cpp new file mode 100644 index 0000000..ef2b388 --- /dev/null +++ b/libsnes/snes/alt/ppu-performance/background/mode7.cpp @@ -0,0 +1,105 @@ +#ifdef PPU_CPP + +#define Clip(x) (((x) & 0x2000) ? ((x) | ~0x03ff) : ((x) & 0x03ff)) + +void PPU::Background::render_mode7() { + signed px, py; + signed tx, ty, tile, palette; + + signed a = sclip<16>(self.regs.m7a); + signed b = sclip<16>(self.regs.m7b); + signed c = sclip<16>(self.regs.m7c); + signed d = sclip<16>(self.regs.m7d); + + signed cx = sclip<13>(self.regs.m7x); + signed cy = sclip<13>(self.regs.m7y); + signed hofs = sclip<13>(self.regs.mode7_hoffset); + signed vofs = sclip<13>(self.regs.mode7_voffset); + + signed y = (self.regs.mode7_vflip == false ? self.vcounter() : 255 - self.vcounter()); + + uint16 *mosaic_x, *mosaic_y; + if(id == ID::BG1) { + mosaic_x = mosaic_table[self.bg1.regs.mosaic]; + mosaic_y = mosaic_table[self.bg1.regs.mosaic]; + } else { + mosaic_x = mosaic_table[self.bg2.regs.mosaic]; + mosaic_y = mosaic_table[self.bg1.regs.mosaic]; + } + + unsigned priority0 = (priority0_enable ? regs.priority0 : 0); + unsigned priority1 = (priority1_enable ? regs.priority1 : 0); + if(priority0 + priority1 == 0) return; + + signed psx = ((a * Clip(hofs - cx)) & ~63) + ((b * Clip(vofs - cy)) & ~63) + ((b * mosaic_y[y]) & ~63) + (cx << 8); + signed psy = ((c * Clip(hofs - cx)) & ~63) + ((d * Clip(vofs - cy)) & ~63) + ((d * mosaic_y[y]) & ~63) + (cy << 8); + for(signed x = 0; x < 256; x++) { + px = (psx + (a * mosaic_x[x])) >> 8; + py = (psy + (c * mosaic_x[x])) >> 8; + + switch(self.regs.mode7_repeat) { + case 0: case 1: { + px &= 1023; + py &= 1023; + tx = ((px >> 3) & 127); + ty = ((py >> 3) & 127); + tile = ppu.vram[(ty * 128 + tx) << 1]; + palette = ppu.vram[(((tile << 6) + ((py & 7) << 3) + (px & 7)) << 1) + 1]; + break; + } + + case 2: { + if((px | py) & ~1023) { + palette = 0; + } else { + px &= 1023; + py &= 1023; + tx = ((px >> 3) & 127); + ty = ((py >> 3) & 127); + tile = ppu.vram[(ty * 128 + tx) << 1]; + palette = ppu.vram[(((tile << 6) + ((py & 7) << 3) + (px & 7)) << 1) + 1]; + } + break; + } + + case 3: { + if((px | py) & ~1023) { + tile = 0; + } else { + px &= 1023; + py &= 1023; + tx = ((px >> 3) & 127); + ty = ((py >> 3) & 127); + tile = ppu.vram[(ty * 128 + tx) << 1]; + } + palette = ppu.vram[(((tile << 6) + ((py & 7) << 3) + (px & 7)) << 1) + 1]; + break; + } + } + + unsigned priority; + if(id == ID::BG1) { + priority = priority0; + } else { + priority = (palette & 0x80 ? priority1 : priority0); + palette &= 0x7f; + } + + if(palette == 0) continue; + unsigned plot_x = (self.regs.mode7_hflip == false ? x : 255 - x); + + unsigned color; + if(self.screen.regs.direct_color && id == ID::BG1) { + color = self.screen.get_direct_color(0, palette); + } else { + color = self.screen.get_palette(palette); + } + + if(regs.main_enable && !window.main[plot_x]) self.screen.output.plot_main(plot_x, color, priority, id); + if(regs.sub_enable && !window.sub[plot_x]) self.screen.output.plot_sub(plot_x, color, priority, id); + } +} + +#undef Clip + +#endif diff --git a/libsnes/snes/alt/ppu-performance/cache/cache.cpp b/libsnes/snes/alt/ppu-performance/cache/cache.cpp new file mode 100644 index 0000000..3773624 --- /dev/null +++ b/libsnes/snes/alt/ppu-performance/cache/cache.cpp @@ -0,0 +1,124 @@ +#ifdef PPU_CPP + +uint8* PPU::Cache::tile_2bpp(unsigned tile) { + if(tilevalid[0][tile] == 0) { + tilevalid[0][tile] = 1; + uint8 *output = (uint8*)tiledata[0] + (tile << 6); + unsigned offset = tile << 4; + unsigned y = 8; + unsigned color, d0, d1; + while(y--) { + d0 = ppu.vram[offset + 0]; + d1 = ppu.vram[offset + 1]; + #define render_line(mask) \ + color = !!(d0 & mask) << 0; \ + color |= !!(d1 & mask) << 1; \ + *output++ = color + render_line(0x80); + render_line(0x40); + render_line(0x20); + render_line(0x10); + render_line(0x08); + render_line(0x04); + render_line(0x02); + render_line(0x01); + #undef render_line + offset += 2; + } + } + return tiledata[0] + (tile << 6); +} + +uint8* PPU::Cache::tile_4bpp(unsigned tile) { + if(tilevalid[1][tile] == 0) { + tilevalid[1][tile] = 1; + uint8 *output = (uint8*)tiledata[1] + (tile << 6); + unsigned offset = tile << 5; + unsigned y = 8; + unsigned color, d0, d1, d2, d3; + while(y--) { + d0 = ppu.vram[offset + 0]; + d1 = ppu.vram[offset + 1]; + d2 = ppu.vram[offset + 16]; + d3 = ppu.vram[offset + 17]; + #define render_line(mask) \ + color = !!(d0 & mask) << 0; \ + color |= !!(d1 & mask) << 1; \ + color |= !!(d2 & mask) << 2; \ + color |= !!(d3 & mask) << 3; \ + *output++ = color + render_line(0x80); + render_line(0x40); + render_line(0x20); + render_line(0x10); + render_line(0x08); + render_line(0x04); + render_line(0x02); + render_line(0x01); + #undef render_line + offset += 2; + } + } + return tiledata[1] + (tile << 6); +} + +uint8* PPU::Cache::tile_8bpp(unsigned tile) { + if(tilevalid[2][tile] == 0) { + tilevalid[2][tile] = 1; + uint8 *output = (uint8*)tiledata[2] + (tile << 6); + unsigned offset = tile << 6; + unsigned y = 8; + unsigned color, d0, d1, d2, d3, d4, d5, d6, d7; + while(y--) { + d0 = ppu.vram[offset + 0]; + d1 = ppu.vram[offset + 1]; + d2 = ppu.vram[offset + 16]; + d3 = ppu.vram[offset + 17]; + d4 = ppu.vram[offset + 32]; + d5 = ppu.vram[offset + 33]; + d6 = ppu.vram[offset + 48]; + d7 = ppu.vram[offset + 49]; + #define render_line(mask) \ + color = !!(d0 & mask) << 0; \ + color |= !!(d1 & mask) << 1; \ + color |= !!(d2 & mask) << 2; \ + color |= !!(d3 & mask) << 3; \ + color |= !!(d4 & mask) << 4; \ + color |= !!(d5 & mask) << 5; \ + color |= !!(d6 & mask) << 6; \ + color |= !!(d7 & mask) << 7; \ + *output++ = color + render_line(0x80); + render_line(0x40); + render_line(0x20); + render_line(0x10); + render_line(0x08); + render_line(0x04); + render_line(0x02); + render_line(0x01); + #undef render_line + offset += 2; + } + } + return tiledata[2] + (tile << 6); +} + +uint8* PPU::Cache::tile(unsigned bpp, unsigned tile) { + switch(bpp) { + case 0: return tile_2bpp(tile); + case 1: return tile_4bpp(tile); + case 2: return tile_8bpp(tile); + } + return NULL; // Please warning +} + +PPU::Cache::Cache(PPU &self) : self(self) { + tiledata[0] = new uint8[262144](); + tiledata[1] = new uint8[131072](); + tiledata[2] = new uint8[ 65536](); + tilevalid[0] = new uint8[ 4096](); + tilevalid[1] = new uint8[ 2048](); + tilevalid[2] = new uint8[ 1024](); +} + +#endif diff --git a/libsnes/snes/alt/ppu-performance/cache/cache.hpp b/libsnes/snes/alt/ppu-performance/cache/cache.hpp new file mode 100644 index 0000000..e2cb384 --- /dev/null +++ b/libsnes/snes/alt/ppu-performance/cache/cache.hpp @@ -0,0 +1,16 @@ +struct Cache { +public: + uint8 *tiledata[3]; + uint8 *tilevalid[3]; + + uint8* tile_2bpp(unsigned tile); + uint8* tile_4bpp(unsigned tile); + uint8* tile_8bpp(unsigned tile); + uint8* tile(unsigned bpp, unsigned tile); + + void serialize(serializer&); + Cache(PPU &self); + + PPU &self; + friend class PPU; +}; diff --git a/libsnes/snes/alt/ppu-performance/debugger/debugger.cpp b/libsnes/snes/alt/ppu-performance/debugger/debugger.cpp new file mode 100644 index 0000000..c1a988c --- /dev/null +++ b/libsnes/snes/alt/ppu-performance/debugger/debugger.cpp @@ -0,0 +1,7 @@ +#ifdef PPU_CPP + +bool PPUDebugger::property(unsigned id, string &name, string &value) { + return false; +} + +#endif diff --git a/libsnes/snes/alt/ppu-performance/debugger/debugger.hpp b/libsnes/snes/alt/ppu-performance/debugger/debugger.hpp new file mode 100644 index 0000000..9ce6a4e --- /dev/null +++ b/libsnes/snes/alt/ppu-performance/debugger/debugger.hpp @@ -0,0 +1,4 @@ +class PPUDebugger : public PPU, public ChipDebugger { +public: + bool property(unsigned id, string &name, string &value); +}; diff --git a/libsnes/snes/alt/ppu-performance/mmio/mmio.cpp b/libsnes/snes/alt/ppu-performance/mmio/mmio.cpp new file mode 100644 index 0000000..1440f0b --- /dev/null +++ b/libsnes/snes/alt/ppu-performance/mmio/mmio.cpp @@ -0,0 +1,891 @@ +#ifdef PPU_CPP + +void PPU::latch_counters() { + regs.hcounter = cpu.hdot(); + regs.vcounter = cpu.vcounter(); + regs.counters_latched = true; +} + +bool PPU::interlace() const { return display.interlace; } +bool PPU::overscan() const { return display.overscan; } +bool PPU::hires() const { return regs.pseudo_hires || regs.bgmode == 5 || regs.bgmode == 6; } + +uint16 PPU::get_vram_addr() { + uint16 addr = regs.vram_addr; + switch(regs.vram_mapping) { + case 0: break; + case 1: addr = (addr & 0xff00) | ((addr & 0x001f) << 3) | ((addr >> 5) & 7); break; + case 2: addr = (addr & 0xfe00) | ((addr & 0x003f) << 3) | ((addr >> 6) & 7); break; + case 3: addr = (addr & 0xfc00) | ((addr & 0x007f) << 3) | ((addr >> 7) & 7); break; + } + return (addr << 1); +} + +uint8 PPU::vram_read(unsigned addr) { + if(regs.display_disable) return vram[addr]; + if(cpu.vcounter() >= display.height) return vram[addr]; + return 0x00; +} + +void PPU::vram_write(unsigned addr, uint8 data) { + if(regs.display_disable || cpu.vcounter() >= display.height) { + vram[addr] = data; + cache.tilevalid[0][addr >> 4] = false; + cache.tilevalid[1][addr >> 5] = false; + cache.tilevalid[2][addr >> 6] = false; + return; + } +} + +uint8 PPU::oam_read(unsigned addr) { + if(addr & 0x0200) addr &= 0x021f; + if(regs.display_disable) return oam[addr]; + if(cpu.vcounter() >= display.height) return oam[addr]; + return oam[0x0218]; +} + +void PPU::oam_write(unsigned addr, uint8 data) { + if(addr & 0x0200) addr &= 0x021f; + if(!regs.display_disable && cpu.vcounter() < display.height) addr = 0x0218; + oam[addr] = data; + sprite.update_list(addr, data); +} + +uint8 PPU::cgram_read(unsigned addr) { + return cgram[addr]; +} + +void PPU::cgram_write(unsigned addr, uint8 data) { + cgram[addr] = data; +} + +void PPU::mmio_update_video_mode() { + switch(regs.bgmode) { + case 0: { + bg1.regs.mode = Background::Mode::BPP2; bg1.regs.priority0 = 8; bg1.regs.priority1 = 11; + bg2.regs.mode = Background::Mode::BPP2; bg2.regs.priority0 = 7; bg2.regs.priority1 = 10; + bg3.regs.mode = Background::Mode::BPP2; bg3.regs.priority0 = 2; bg3.regs.priority1 = 5; + bg4.regs.mode = Background::Mode::BPP2; bg4.regs.priority0 = 1; bg4.regs.priority1 = 4; + sprite.regs.priority0 = 3; sprite.regs.priority1 = 6; sprite.regs.priority2 = 9; sprite.regs.priority3 = 12; + } break; + + case 1: { + bg1.regs.mode = Background::Mode::BPP4; + bg2.regs.mode = Background::Mode::BPP4; + bg3.regs.mode = Background::Mode::BPP2; + bg4.regs.mode = Background::Mode::Inactive; + if(regs.bg3_priority) { + bg1.regs.priority0 = 5; bg1.regs.priority1 = 8; + bg2.regs.priority0 = 4; bg2.regs.priority1 = 7; + bg3.regs.priority0 = 1; bg3.regs.priority1 = 10; + sprite.regs.priority0 = 2; sprite.regs.priority1 = 3; sprite.regs.priority2 = 6; sprite.regs.priority3 = 9; + } else { + bg1.regs.priority0 = 6; bg1.regs.priority1 = 9; + bg2.regs.priority0 = 5; bg2.regs.priority1 = 8; + bg3.regs.priority0 = 1; bg3.regs.priority1 = 3; + sprite.regs.priority0 = 2; sprite.regs.priority1 = 4; sprite.regs.priority2 = 7; sprite.regs.priority3 = 10; + } + } break; + + case 2: { + bg1.regs.mode = Background::Mode::BPP4; + bg2.regs.mode = Background::Mode::BPP4; + bg3.regs.mode = Background::Mode::Inactive; + bg4.regs.mode = Background::Mode::Inactive; + bg1.regs.priority0 = 3; bg1.regs.priority1 = 7; + bg2.regs.priority0 = 1; bg2.regs.priority1 = 5; + sprite.regs.priority0 = 2; sprite.regs.priority1 = 4; sprite.regs.priority2 = 6; sprite.regs.priority3 = 8; + } break; + + case 3: { + bg1.regs.mode = Background::Mode::BPP8; + bg2.regs.mode = Background::Mode::BPP4; + bg3.regs.mode = Background::Mode::Inactive; + bg4.regs.mode = Background::Mode::Inactive; + bg1.regs.priority0 = 3; bg1.regs.priority1 = 7; + bg2.regs.priority0 = 1; bg2.regs.priority1 = 5; + sprite.regs.priority0 = 2; sprite.regs.priority1 = 4; sprite.regs.priority2 = 6; sprite.regs.priority3 = 8; + } break; + + case 4: { + bg1.regs.mode = Background::Mode::BPP8; + bg2.regs.mode = Background::Mode::BPP2; + bg3.regs.mode = Background::Mode::Inactive; + bg4.regs.mode = Background::Mode::Inactive; + bg1.regs.priority0 = 3; bg1.regs.priority1 = 7; + bg2.regs.priority0 = 1; bg2.regs.priority1 = 5; + sprite.regs.priority0 = 2; sprite.regs.priority1 = 4; sprite.regs.priority2 = 6; sprite.regs.priority3 = 8; + } break; + + case 5: { + bg1.regs.mode = Background::Mode::BPP4; + bg2.regs.mode = Background::Mode::BPP2; + bg3.regs.mode = Background::Mode::Inactive; + bg4.regs.mode = Background::Mode::Inactive; + bg1.regs.priority0 = 3; bg1.regs.priority1 = 7; + bg2.regs.priority0 = 1; bg2.regs.priority1 = 5; + sprite.regs.priority0 = 2; sprite.regs.priority1 = 4; sprite.regs.priority2 = 6; sprite.regs.priority3 = 8; + } break; + + case 6: { + bg1.regs.mode = Background::Mode::BPP4; + bg2.regs.mode = Background::Mode::Inactive; + bg3.regs.mode = Background::Mode::Inactive; + bg4.regs.mode = Background::Mode::Inactive; + bg1.regs.priority0 = 2; bg1.regs.priority1 = 5; + sprite.regs.priority0 = 1; sprite.regs.priority1 = 3; sprite.regs.priority2 = 4; sprite.regs.priority3 = 6; + } break; + + case 7: { + if(regs.mode7_extbg == false) { + bg1.regs.mode = Background::Mode::Mode7; + bg2.regs.mode = Background::Mode::Inactive; + bg3.regs.mode = Background::Mode::Inactive; + bg4.regs.mode = Background::Mode::Inactive; + bg1.regs.priority0 = 2; bg1.regs.priority1 = 2; + sprite.regs.priority0 = 1; sprite.regs.priority1 = 3; sprite.regs.priority2 = 4; sprite.regs.priority3 = 5; + } else { + bg1.regs.mode = Background::Mode::Mode7; + bg2.regs.mode = Background::Mode::Mode7; + bg3.regs.mode = Background::Mode::Inactive; + bg4.regs.mode = Background::Mode::Inactive; + bg1.regs.priority0 = 3; bg1.regs.priority1 = 3; + bg2.regs.priority0 = 1; bg2.regs.priority1 = 5; + sprite.regs.priority0 = 2; sprite.regs.priority1 = 4; sprite.regs.priority2 = 6; sprite.regs.priority3 = 7; + } + } break; + } +} + +uint8 PPU::mmio_read(unsigned addr) { + cpu.synchronize_ppu(); + + switch(addr & 0xffff) { + case 0x2104: case 0x2105: case 0x2106: case 0x2108: case 0x2109: case 0x210a: + case 0x2114: case 0x2115: case 0x2116: case 0x2118: case 0x2119: case 0x211a: + case 0x2124: case 0x2125: case 0x2126: case 0x2128: case 0x2129: case 0x212a: { + return regs.ppu1_mdr; + } + + case 0x2134: { //MPYL + unsigned result = ((int16)regs.m7a * (int8)(regs.m7b >> 8)); + regs.ppu1_mdr = result >> 0; + return regs.ppu1_mdr; + } + + case 0x2135: { //MPYM + unsigned result = ((int16)regs.m7a * (int8)(regs.m7b >> 8)); + regs.ppu1_mdr = result >> 8; + return regs.ppu1_mdr; + } + + case 0x2136: { //MPYH + unsigned result = ((int16)regs.m7a * (int8)(regs.m7b >> 8)); + regs.ppu1_mdr = result >> 16; + return regs.ppu1_mdr; + } + + case 0x2137: { //SLHV + if(cpu.pio() & 0x80) latch_counters(); + return cpu.regs.mdr; + } + + case 0x2138: { //OAMDATAREAD + regs.ppu1_mdr = oam_read(regs.oam_addr); + regs.oam_addr = (regs.oam_addr + 1) & 0x03ff; + sprite.set_first(); + return regs.ppu1_mdr; + } + + case 0x2139: { //VMDATALREAD + regs.ppu1_mdr = regs.vram_readbuffer >> 0; + if(regs.vram_incmode == 0) { + uint16 addr = get_vram_addr(); + regs.vram_readbuffer = vram_read(addr + 0) << 0; + regs.vram_readbuffer |= vram_read(addr + 1) << 8; + regs.vram_addr += regs.vram_incsize; + } + return regs.ppu1_mdr; + } + + case 0x213a: { //VMDATAHREAD + regs.ppu1_mdr = regs.vram_readbuffer >> 8; + if(regs.vram_incmode == 1) { + uint16 addr = get_vram_addr(); + regs.vram_readbuffer = vram_read(addr + 0) << 0; + regs.vram_readbuffer |= vram_read(addr + 1) << 8; + regs.vram_addr += regs.vram_incsize; + } + return regs.ppu1_mdr; + } + + case 0x213b: { //CGDATAREAD + if((regs.cgram_addr & 1) == 0) { + regs.ppu2_mdr = cgram_read(regs.cgram_addr); + } else { + regs.ppu2_mdr = (regs.ppu2_mdr & 0x80) | (cgram_read(regs.cgram_addr) & 0x7f); + } + regs.cgram_addr = (regs.cgram_addr + 1) & 0x01ff; + return regs.ppu2_mdr; + } + + case 0x213c: { //OPHCT + if(regs.latch_hcounter == 0) { + regs.ppu2_mdr = regs.hcounter & 0xff; + } else { + regs.ppu2_mdr = (regs.ppu2_mdr & 0xfe) | (regs.hcounter >> 8); + } + regs.latch_hcounter ^= 1; + return regs.ppu2_mdr; + } + + case 0x213d: { //OPVCT + if(regs.latch_vcounter == 0) { + regs.ppu2_mdr = regs.vcounter & 0xff; + } else { + regs.ppu2_mdr = (regs.ppu2_mdr & 0xfe) | (regs.vcounter >> 8); + } + regs.latch_vcounter ^= 1; + return regs.ppu2_mdr; + } + + case 0x213e: { //STAT77 + regs.ppu1_mdr &= 0x10; + regs.ppu1_mdr |= sprite.regs.time_over << 7; + regs.ppu1_mdr |= sprite.regs.range_over << 6; + regs.ppu1_mdr |= 0x01; //version + return regs.ppu1_mdr; + } + + case 0x213f: { //STAT78 + regs.latch_hcounter = 0; + regs.latch_vcounter = 0; + + regs.ppu2_mdr &= 0x20; + regs.ppu2_mdr |= cpu.field() << 7; + if((cpu.pio() & 0x80) == 0) { + regs.ppu2_mdr |= 0x40; + } else if(regs.counters_latched) { + regs.ppu2_mdr |= 0x40; + regs.counters_latched = false; + } + regs.ppu2_mdr |= (system.region.i == System::Region::NTSC ? 0 : 1) << 4; + regs.ppu2_mdr |= 0x03; //version + return regs.ppu2_mdr; + } + } + + return cpu.regs.mdr; +} + +void PPU::mmio_write(unsigned addr, uint8 data) { + cpu.synchronize_ppu(); + + switch(addr & 0xffff) { + case 0x2100: { //INIDISP + if(regs.display_disable && cpu.vcounter() == display.height) sprite.address_reset(); + regs.display_disable = data & 0x80; + regs.display_brightness = data & 0x0f; + return; + } + + case 0x2101: { //OBSEL + sprite.regs.base_size = (data >> 5) & 7; + sprite.regs.nameselect = (data >> 3) & 3; + sprite.regs.tiledata_addr = (data & 3) << 14; + sprite.list_valid = false; + return; + } + + case 0x2102: { //OAMADDL + regs.oam_baseaddr = (regs.oam_baseaddr & 0x0100) | (data << 0); + sprite.address_reset(); + return; + } + + case 0x2103: { //OAMADDH + regs.oam_priority = data & 0x80; + regs.oam_baseaddr = ((data & 1) << 8) | (regs.oam_baseaddr & 0x00ff); + sprite.address_reset(); + return; + } + + case 0x2104: { //OAMDATA + if((regs.oam_addr & 1) == 0) regs.oam_latchdata = data; + if(regs.oam_addr & 0x0200) { + oam_write(regs.oam_addr, data); + } else if((regs.oam_addr & 1) == 1) { + oam_write((regs.oam_addr & ~1) + 0, regs.oam_latchdata); + oam_write((regs.oam_addr & ~1) + 1, data); + } + regs.oam_addr = (regs.oam_addr + 1) & 0x03ff; + sprite.set_first(); + return; + } + + case 0x2105: { //BGMODE + bg4.regs.tile_size = data & 0x80; + bg3.regs.tile_size = data & 0x40; + bg2.regs.tile_size = data & 0x20; + bg1.regs.tile_size = data & 0x10; + regs.bg3_priority = data & 0x08; + regs.bgmode = data & 0x07; + mmio_update_video_mode(); + return; + } + + case 0x2106: { //MOSAIC + unsigned mosaic_size = (data >> 4) & 15; + bg4.regs.mosaic = (data & 0x08 ? mosaic_size : 0); + bg3.regs.mosaic = (data & 0x04 ? mosaic_size : 0); + bg2.regs.mosaic = (data & 0x02 ? mosaic_size : 0); + bg1.regs.mosaic = (data & 0x01 ? mosaic_size : 0); + return; + } + + case 0x2107: { //BG1SC + bg1.regs.screen_addr = (data & 0x7c) << 9; + bg1.regs.screen_size = data & 3; + return; + } + + case 0x2108: { //BG2SC + bg2.regs.screen_addr = (data & 0x7c) << 9; + bg2.regs.screen_size = data & 3; + return; + } + + case 0x2109: { //BG3SC + bg3.regs.screen_addr = (data & 0x7c) << 9; + bg3.regs.screen_size = data & 3; + return; + } + + case 0x210a: { //BG4SC + bg4.regs.screen_addr = (data & 0x7c) << 9; + bg4.regs.screen_size = data & 3; + return; + } + + case 0x210b: { //BG12NBA + bg1.regs.tiledata_addr = (data & 0x07) << 13; + bg2.regs.tiledata_addr = (data & 0x70) << 9; + return; + } + + case 0x210c: { //BG34NBA + bg3.regs.tiledata_addr = (data & 0x07) << 13; + bg4.regs.tiledata_addr = (data & 0x70) << 9; + return; + } + + case 0x210d: { //BG1HOFS + regs.mode7_hoffset = (data << 8) | regs.mode7_latchdata; + regs.mode7_latchdata = data; + + bg1.regs.hoffset = (data << 8) | (regs.bgofs_latchdata & ~7) | ((bg1.regs.hoffset >> 8) & 7); + regs.bgofs_latchdata = data; + return; + } + + case 0x210e: { //BG1VOFS + regs.mode7_voffset = (data << 8) | regs.mode7_latchdata; + regs.mode7_latchdata = data; + + bg1.regs.voffset = (data << 8) | regs.bgofs_latchdata; + regs.bgofs_latchdata = data; + return; + } + + case 0x210f: { //BG2HOFS + bg2.regs.hoffset = (data << 8) | (regs.bgofs_latchdata & ~7) | ((bg2.regs.hoffset >> 8) & 7); + regs.bgofs_latchdata = data; + return; + } + + case 0x2110: { //BG2VOFS + bg2.regs.voffset = (data << 8) | regs.bgofs_latchdata; + regs.bgofs_latchdata = data; + return; + } + + case 0x2111: { //BG3HOFS + bg3.regs.hoffset = (data << 8) | (regs.bgofs_latchdata & ~7) | ((bg3.regs.hoffset >> 8) & 7); + regs.bgofs_latchdata = data; + return; + } + + case 0x2112: { //BG3VOFS + bg3.regs.voffset = (data << 8) | regs.bgofs_latchdata; + regs.bgofs_latchdata = data; + return; + } + + case 0x2113: { //BG4HOFS + bg4.regs.hoffset = (data << 8) | (regs.bgofs_latchdata & ~7) | ((bg4.regs.hoffset >> 8) & 7); + regs.bgofs_latchdata = data; + return; + } + + case 0x2114: { //BG4VOFS + bg4.regs.voffset = (data << 8) | regs.bgofs_latchdata; + regs.bgofs_latchdata = data; + return; + } + + case 0x2115: { //VMAIN + regs.vram_incmode = data & 0x80; + regs.vram_mapping = (data >> 2) & 3; + switch(data & 3) { + case 0: regs.vram_incsize = 1; break; + case 1: regs.vram_incsize = 32; break; + case 2: regs.vram_incsize = 128; break; + case 3: regs.vram_incsize = 128; break; + } + return; + } + + case 0x2116: { //VMADDL + regs.vram_addr = (regs.vram_addr & 0xff00) | (data << 0); + uint16 addr = get_vram_addr(); + regs.vram_readbuffer = vram_read(addr + 0) << 0; + regs.vram_readbuffer |= vram_read(addr + 1) << 8; + return; + } + + case 0x2117: { //VMADDH + regs.vram_addr = (data << 8) | (regs.vram_addr & 0x00ff); + uint16 addr = get_vram_addr(); + regs.vram_readbuffer = vram_read(addr + 0) << 0; + regs.vram_readbuffer |= vram_read(addr + 1) << 8; + return; + } + + case 0x2118: { //VMDATAL + vram_write(get_vram_addr() + 0, data); + if(regs.vram_incmode == 0) regs.vram_addr += regs.vram_incsize; + return; + } + + case 0x2119: { //VMDATAH + vram_write(get_vram_addr() + 1, data); + if(regs.vram_incmode == 1) regs.vram_addr += regs.vram_incsize; + return; + } + + case 0x211a: { //M7SEL + regs.mode7_repeat = (data >> 6) & 3; + regs.mode7_vflip = data & 0x02; + regs.mode7_hflip = data & 0x01; + return; + } + + case 0x211b: { //M7A + regs.m7a = (data << 8) | regs.mode7_latchdata; + regs.mode7_latchdata = data; + return; + } + + case 0x211c: { //M7B + regs.m7b = (data << 8) | regs.mode7_latchdata; + regs.mode7_latchdata = data; + return; + } + + case 0x211d: { //M7C + regs.m7c = (data << 8) | regs.mode7_latchdata; + regs.mode7_latchdata = data; + return; + } + + case 0x211e: { //M7D + regs.m7d = (data << 8) | regs.mode7_latchdata; + regs.mode7_latchdata = data; + return; + } + + case 0x211f: { //M7X + regs.m7x = (data << 8) | regs.mode7_latchdata; + regs.mode7_latchdata = data; + return; + } + + case 0x2120: { //M7Y + regs.m7y = (data << 8) | regs.mode7_latchdata; + regs.mode7_latchdata = data; + return; + } + + case 0x2121: { //CGADD + regs.cgram_addr = data << 1; + return; + } + + case 0x2122: { //CGDATA + if((regs.cgram_addr & 1) == 0) { + regs.cgram_latchdata = data; + } else { + cgram_write((regs.cgram_addr & ~1) + 0, regs.cgram_latchdata); + cgram_write((regs.cgram_addr & ~1) + 1, data & 0x7f); + } + regs.cgram_addr = (regs.cgram_addr + 1) & 0x01ff; + return; + } + + case 0x2123: { //W12SEL + bg2.window.two_enable = data & 0x80; + bg2.window.two_invert = data & 0x40; + bg2.window.one_enable = data & 0x20; + bg2.window.one_invert = data & 0x10; + bg1.window.two_enable = data & 0x08; + bg1.window.two_invert = data & 0x04; + bg1.window.one_enable = data & 0x02; + bg1.window.one_invert = data & 0x01; + return; + } + + case 0x2124: { //W34SEL + bg4.window.two_enable = data & 0x80; + bg4.window.two_invert = data & 0x40; + bg4.window.one_enable = data & 0x20; + bg4.window.one_invert = data & 0x10; + bg3.window.two_enable = data & 0x08; + bg3.window.two_invert = data & 0x04; + bg3.window.one_enable = data & 0x02; + bg3.window.one_invert = data & 0x01; + return; + } + + case 0x2125: { //WOBJSEL + screen.window.two_enable = data & 0x80; + screen.window.two_invert = data & 0x40; + screen.window.one_enable = data & 0x20; + screen.window.one_invert = data & 0x10; + sprite.window.two_enable = data & 0x08; + sprite.window.two_invert = data & 0x04; + sprite.window.one_enable = data & 0x02; + sprite.window.one_invert = data & 0x01; + return; + } + + case 0x2126: { //WH0 + regs.window_one_left = data; + return; + } + + case 0x2127: { //WH1 + regs.window_one_right = data; + return; + } + + case 0x2128: { //WH2 + regs.window_two_left = data; + return; + } + + case 0x2129: { //WH3 + regs.window_two_right = data; + return; + } + + case 0x212a: { //WBGLOG + bg4.window.mask = (data >> 6) & 3; + bg3.window.mask = (data >> 4) & 3; + bg2.window.mask = (data >> 2) & 3; + bg1.window.mask = (data >> 0) & 3; + return; + } + + case 0x212b: { //WOBJLOG + screen.window.mask = (data >> 2) & 3; + sprite.window.mask = (data >> 0) & 3; + return; + } + + case 0x212c: { //TM + sprite.regs.main_enable = data & 0x10; + bg4.regs.main_enable = data & 0x08; + bg3.regs.main_enable = data & 0x04; + bg2.regs.main_enable = data & 0x02; + bg1.regs.main_enable = data & 0x01; + return; + } + + case 0x212d: { //TS + sprite.regs.sub_enable = data & 0x10; + bg4.regs.sub_enable = data & 0x08; + bg3.regs.sub_enable = data & 0x04; + bg2.regs.sub_enable = data & 0x02; + bg1.regs.sub_enable = data & 0x01; + return; + } + + case 0x212e: { //TMW + sprite.window.main_enable = data & 0x10; + bg4.window.main_enable = data & 0x08; + bg3.window.main_enable = data & 0x04; + bg2.window.main_enable = data & 0x02; + bg1.window.main_enable = data & 0x01; + return; + } + + case 0x212f: { //TSW + sprite.window.sub_enable = data & 0x10; + bg4.window.sub_enable = data & 0x08; + bg3.window.sub_enable = data & 0x04; + bg2.window.sub_enable = data & 0x02; + bg1.window.sub_enable = data & 0x01; + return; + } + + case 0x2130: { //CGWSEL + screen.window.main_mask = (data >> 6) & 3; + screen.window.sub_mask = (data >> 4) & 3; + screen.regs.addsub_mode = data & 0x02; + screen.regs.direct_color = data & 0x01; + return; + } + + case 0x2131: { //CGADDSUB + screen.regs.color_mode = data & 0x80; + screen.regs.color_halve = data & 0x40; + screen.regs.color_enable[6] = data & 0x20; + screen.regs.color_enable[5] = data & 0x10; + screen.regs.color_enable[4] = data & 0x10; + screen.regs.color_enable[3] = data & 0x08; + screen.regs.color_enable[2] = data & 0x04; + screen.regs.color_enable[1] = data & 0x02; + screen.regs.color_enable[0] = data & 0x01; + return; + } + + case 0x2132: { //COLDATA + if(data & 0x80) screen.regs.color_b = data & 0x1f; + if(data & 0x40) screen.regs.color_g = data & 0x1f; + if(data & 0x20) screen.regs.color_r = data & 0x1f; + screen.regs.color = (screen.regs.color_b << 10) | (screen.regs.color_g << 5) | (screen.regs.color_r << 0); + return; + } + + case 0x2133: { //SETINI + regs.mode7_extbg = data & 0x40; + regs.pseudo_hires = data & 0x08; + regs.overscan = data & 0x04; + sprite.regs.interlace = data & 0x02; + regs.interlace = data & 0x01; + mmio_update_video_mode(); + sprite.list_valid = false; + return; + } + } +} + +void PPU::mmio_reset() { + //internal + regs.ppu1_mdr = 0; + regs.ppu2_mdr = 0; + + regs.vram_readbuffer = 0; + regs.oam_latchdata = 0; + regs.cgram_latchdata = 0; + regs.bgofs_latchdata = 0; + regs.mode7_latchdata = 0; + + regs.counters_latched = 0; + regs.latch_hcounter = 0; + regs.latch_vcounter = 0; + + sprite.regs.first_sprite = 0; + sprite.list_valid = false; + + //$2100 + regs.display_disable = true; + regs.display_brightness = 0; + + //$2101 + sprite.regs.base_size = 0; + sprite.regs.nameselect = 0; + sprite.regs.tiledata_addr = 0; + + //$2102-$2103 + regs.oam_baseaddr = 0; + regs.oam_addr = 0; + regs.oam_priority = 0; + + //$2105 + bg4.regs.tile_size = 0; + bg3.regs.tile_size = 0; + bg2.regs.tile_size = 0; + bg1.regs.tile_size = 0; + regs.bg3_priority = 0; + regs.bgmode = 0; + + //$2106 + bg4.regs.mosaic = 0; + bg3.regs.mosaic = 0; + bg2.regs.mosaic = 0; + bg1.regs.mosaic = 0; + + //$2107-$210a + bg1.regs.screen_addr = 0; + bg1.regs.screen_size = 0; + bg2.regs.screen_addr = 0; + bg2.regs.screen_size = 0; + bg3.regs.screen_addr = 0; + bg3.regs.screen_size = 0; + bg4.regs.screen_addr = 0; + bg4.regs.screen_size = 0; + + //$210b-$210c + bg1.regs.tiledata_addr = 0; + bg2.regs.tiledata_addr = 0; + bg3.regs.tiledata_addr = 0; + bg4.regs.tiledata_addr = 0; + + //$210d-$2114 + regs.mode7_hoffset = 0; + regs.mode7_voffset = 0; + bg1.regs.hoffset = 0; + bg1.regs.voffset = 0; + bg2.regs.hoffset = 0; + bg2.regs.voffset = 0; + bg3.regs.hoffset = 0; + bg3.regs.voffset = 0; + bg4.regs.hoffset = 0; + bg4.regs.voffset = 0; + + //$2115 + regs.vram_incmode = 0; + regs.vram_mapping = 0; + regs.vram_incsize = 1; + + //$2116-$2117 + regs.vram_addr = 0; + + //$211a + regs.mode7_repeat = 0; + regs.mode7_vflip = 0; + regs.mode7_hflip = 0; + + //$211b-$2120 + regs.m7a = 0; + regs.m7b = 0; + regs.m7c = 0; + regs.m7d = 0; + regs.m7x = 0; + regs.m7y = 0; + + //$2121 + regs.cgram_addr = 0; + + //$2123-$2125 + bg1.window.one_enable = 0; + bg1.window.one_invert = 0; + bg1.window.two_enable = 0; + bg1.window.two_invert = 0; + + bg2.window.one_enable = 0; + bg2.window.one_invert = 0; + bg2.window.two_enable = 0; + bg2.window.two_invert = 0; + + bg3.window.one_enable = 0; + bg3.window.one_invert = 0; + bg3.window.two_enable = 0; + bg3.window.two_invert = 0; + + bg4.window.one_enable = 0; + bg4.window.one_invert = 0; + bg4.window.two_enable = 0; + bg4.window.two_invert = 0; + + sprite.window.one_enable = 0; + sprite.window.one_invert = 0; + sprite.window.two_enable = 0; + sprite.window.two_invert = 0; + + screen.window.one_enable = 0; + screen.window.one_invert = 0; + screen.window.two_enable = 0; + screen.window.two_invert = 0; + + //$2126-$2129 + regs.window_one_left = 0; + regs.window_one_right = 0; + regs.window_two_left = 0; + regs.window_two_right = 0; + + //$212a-$212b + bg1.window.mask = 0; + bg2.window.mask = 0; + bg3.window.mask = 0; + bg4.window.mask = 0; + sprite.window.mask = 0; + screen.window.mask = 0; + + //$212c + bg1.regs.main_enable = 0; + bg2.regs.main_enable = 0; + bg3.regs.main_enable = 0; + bg4.regs.main_enable = 0; + sprite.regs.main_enable = 0; + + //$212d + bg1.regs.sub_enable = 0; + bg2.regs.sub_enable = 0; + bg3.regs.sub_enable = 0; + bg4.regs.sub_enable = 0; + sprite.regs.sub_enable = 0; + + //$212e + bg1.window.main_enable = 0; + bg2.window.main_enable = 0; + bg3.window.main_enable = 0; + bg4.window.main_enable = 0; + sprite.window.main_enable = 0; + + //$212f + bg1.window.sub_enable = 0; + bg2.window.sub_enable = 0; + bg3.window.sub_enable = 0; + bg4.window.sub_enable = 0; + sprite.window.sub_enable = 0; + + //$2130 + screen.window.main_mask = 0; + screen.window.sub_mask = 0; + screen.regs.addsub_mode = 0; + screen.regs.direct_color = 0; + + //$2131 + screen.regs.color_mode = 0; + screen.regs.color_halve = 0; + screen.regs.color_enable[6] = 0; + screen.regs.color_enable[5] = 0; + screen.regs.color_enable[4] = 0; + screen.regs.color_enable[3] = 0; + screen.regs.color_enable[2] = 0; + screen.regs.color_enable[1] = 0; + screen.regs.color_enable[0] = 0; + + //$2132 + screen.regs.color_b = 0; + screen.regs.color_g = 0; + screen.regs.color_r = 0; + screen.regs.color = 0; + + //$2133 + regs.mode7_extbg = 0; + regs.pseudo_hires = 0; + regs.overscan = 0; + sprite.regs.interlace = 0; + regs.interlace = 0; + + //$213e + sprite.regs.time_over = 0; + sprite.regs.range_over = 0; + + mmio_update_video_mode(); +} + +#endif diff --git a/libsnes/snes/alt/ppu-performance/mmio/mmio.hpp b/libsnes/snes/alt/ppu-performance/mmio/mmio.hpp new file mode 100644 index 0000000..36b5af4 --- /dev/null +++ b/libsnes/snes/alt/ppu-performance/mmio/mmio.hpp @@ -0,0 +1,95 @@ +public: + uint8 mmio_read(unsigned addr); + void mmio_write(unsigned addr, uint8 data); + +private: + +struct Regs { + //internal + uint8 ppu1_mdr; + uint8 ppu2_mdr; + + uint16 vram_readbuffer; + uint8 oam_latchdata; + uint8 cgram_latchdata; + uint8 bgofs_latchdata; + uint8 mode7_latchdata; + + bool counters_latched; + bool latch_hcounter; + bool latch_vcounter; + + //$2100 + bool display_disable; + unsigned display_brightness; + + //$2102-$2103 + uint16 oam_baseaddr; + uint16 oam_addr; + bool oam_priority; + + //$2105 + bool bg3_priority; + unsigned bgmode; + + //$210d + uint16 mode7_hoffset; + + //$210e + uint16 mode7_voffset; + + //$2115 + bool vram_incmode; + unsigned vram_mapping; + unsigned vram_incsize; + + //$2116-$2117 + uint16 vram_addr; + + //$211a + unsigned mode7_repeat; + bool mode7_vflip; + bool mode7_hflip; + + //$211b-$2120 + uint16 m7a; + uint16 m7b; + uint16 m7c; + uint16 m7d; + uint16 m7x; + uint16 m7y; + + //$2121 + uint16 cgram_addr; + + //$2126-$212a + unsigned window_one_left; + unsigned window_one_right; + unsigned window_two_left; + unsigned window_two_right; + + //$2133 + bool mode7_extbg; + bool pseudo_hires; + bool overscan; + bool interlace; + + //$213c + uint16 hcounter; + + //$213d + uint16 vcounter; +} regs; + +uint16 get_vram_addr(); +uint8 vram_read(unsigned addr); +void vram_write(unsigned addr, uint8 data); + +uint8 oam_read(unsigned addr); +void oam_write(unsigned addr, uint8 data); + +uint8 cgram_read(unsigned addr); +void cgram_write(unsigned addr, uint8 data); + +void mmio_update_video_mode(); +void mmio_reset(); diff --git a/libsnes/snes/alt/ppu-performance/ppu.cpp b/libsnes/snes/alt/ppu-performance/ppu.cpp new file mode 100644 index 0000000..166313a --- /dev/null +++ b/libsnes/snes/alt/ppu-performance/ppu.cpp @@ -0,0 +1,156 @@ +#include + +#define PPU_CPP +namespace SNES { + +#if defined(DEBUGGER) + #include "debugger/debugger.cpp" + PPUDebugger ppu; +#else + PPU ppu; +#endif + +#include "ppu.hpp" +#include "mmio/mmio.cpp" +#include "window/window.cpp" +#include "cache/cache.cpp" +#include "background/background.cpp" +#include "sprite/sprite.cpp" +#include "screen/screen.cpp" +#include "serialization.cpp" + +void PPU::step(unsigned clocks) { + clock += clocks; +} + +void PPU::synchronize_cpu() { + if(CPU::Threaded == true) { + if(clock >= 0 && scheduler.sync.i != Scheduler::SynchronizeMode::All) co_switch(cpu.thread); + } else { + while(clock >= 0) cpu.enter(); + } +} + +void PPU::Enter() { ppu.enter(); } + +void PPU::enter() { + while(true) { + if(scheduler.sync.i == Scheduler::SynchronizeMode::All) { + scheduler.exit(Scheduler::ExitReason::SynchronizeEvent); + } + + scanline(); + if(vcounter() < display.height && vcounter()) { + add_clocks(512); + render_scanline(); + add_clocks(lineclocks() - 512); + } else { + add_clocks(lineclocks()); + } + } +} + +void PPU::add_clocks(unsigned clocks) { + tick(clocks); + step(clocks); + synchronize_cpu(); +} + +void PPU::render_scanline() { + if(display.framecounter) return; //skip this frame? + bg1.scanline(); + bg2.scanline(); + bg3.scanline(); + bg4.scanline(); + if(regs.display_disable) return screen.render_black(); + screen.scanline(); + bg1.render(); + bg2.render(); + bg3.render(); + bg4.render(); + sprite.render(); + screen.render(); +} + +void PPU::scanline() { + display.width = !hires() ? 256 : 512; + display.height = !overscan() ? 225 : 240; + if(vcounter() == 0) frame(); + if(vcounter() == display.height && regs.display_disable == false) sprite.address_reset(); +} + +void PPU::frame() { + sprite.frame(); + system.frame(); + display.interlace = regs.interlace; + display.overscan = regs.overscan; + display.framecounter = display.frameskip == 0 ? 0 : (display.framecounter + 1) % display.frameskip; +} + +void PPU::enable() { + function read(&PPU::mmio_read, (PPU*)&ppu); + function write(&PPU::mmio_write, (PPU*)&ppu); + + bus.map(Bus::MapMode::Direct, 0x00, 0x3f, 0x2100, 0x213f, read, write); + bus.map(Bus::MapMode::Direct, 0x80, 0xbf, 0x2100, 0x213f, read, write); +} + +void PPU::power() { + foreach(n, vram) n = 0; + foreach(n, oam) n = 0; + foreach(n, cgram) n = 0; + reset(); +} + +void PPU::reset() { + create(Enter, system.cpu_frequency); + PPUcounter::reset(); + memset(surface, 0, 512 * 512 * sizeof(uint32)); + mmio_reset(); + display.interlace = false; + display.overscan = false; +} + +void PPU::layer_enable(unsigned layer, unsigned priority, bool enable) { + switch(layer * 4 + priority) { + case 0: bg1.priority0_enable = enable; break; + case 1: bg1.priority1_enable = enable; break; + case 4: bg2.priority0_enable = enable; break; + case 5: bg2.priority1_enable = enable; break; + case 8: bg3.priority0_enable = enable; break; + case 9: bg3.priority1_enable = enable; break; + case 12: bg4.priority0_enable = enable; break; + case 13: bg4.priority1_enable = enable; break; + case 16: sprite.priority0_enable = enable; break; + case 17: sprite.priority1_enable = enable; break; + case 18: sprite.priority2_enable = enable; break; + case 19: sprite.priority3_enable = enable; break; + } +} + +void PPU::set_frameskip(unsigned frameskip) { + display.frameskip = frameskip; + display.framecounter = 0; +} + +PPU::PPU() : +cache(*this), +bg1(*this, Background::ID::BG1), +bg2(*this, Background::ID::BG2), +bg3(*this, Background::ID::BG3), +bg4(*this, Background::ID::BG4), +sprite(*this), +screen(*this) { + surface = new uint32[512 * 512]; + output = surface + 16 * 512; + display.width = 256; + display.height = 224; + display.frameskip = 0; + display.framecounter = 0; +} + +PPU::~PPU() { + delete[] surface; +} + +} diff --git a/libsnes/snes/alt/ppu-performance/ppu.hpp b/libsnes/snes/alt/ppu-performance/ppu.hpp new file mode 100644 index 0000000..a51d98d --- /dev/null +++ b/libsnes/snes/alt/ppu-performance/ppu.hpp @@ -0,0 +1,81 @@ +#ifndef __SNES_PPU_H +#define __SNES_PPU_H + +class PPU; + +class PPU : public Processor, public PPUcounter { +public: + uint8 vram[64 * 1024]; + uint8 oam[544]; + uint8 cgram[512]; + + enum{ Threaded = true }; + alwaysinline void step(unsigned clocks); + alwaysinline void synchronize_cpu(); + + void latch_counters(); + bool interlace() const; + bool overscan() const; + bool hires() const; + + void enter(); + void enable(); + void power(); + void reset(); + void scanline(); + void frame(); + + void layer_enable(unsigned layer, unsigned priority, bool enable); + void set_frameskip(unsigned frameskip); + + void serialize(serializer&); + PPU(); + ~PPU(); + +private: + uint32 *surface; + uint32 *output; + + #include "mmio/mmio.hpp" + #include "window/window.hpp" + #include "cache/cache.hpp" + #include "background/background.hpp" + #include "sprite/sprite.hpp" + #include "screen/screen.hpp" + + Cache cache; + Background bg1; + Background bg2; + Background bg3; + Background bg4; + Sprite sprite; + Screen screen; + + struct Display { + bool interlace; + bool overscan; + unsigned width; + unsigned height; + unsigned frameskip; + unsigned framecounter; + } display; + + static void Enter(); + void add_clocks(unsigned clocks); + void render_scanline(); + + friend class PPU::Cache; + friend class PPU::Background; + friend class PPU::Sprite; + friend class PPU::Screen; + friend class Video; +}; + +#if defined(DEBUGGER) + #include "debugger/debugger.hpp" + extern PPUDebugger ppu; +#else + extern PPU ppu; +#endif + +#endif diff --git a/libsnes/snes/alt/ppu-performance/screen/screen.cpp b/libsnes/snes/alt/ppu-performance/screen/screen.cpp new file mode 100644 index 0000000..08cd889 --- /dev/null +++ b/libsnes/snes/alt/ppu-performance/screen/screen.cpp @@ -0,0 +1,155 @@ +#ifdef PPU_CPP + +unsigned PPU::Screen::get_palette(unsigned color) { + #if defined(ARCH_LSB) + return ((uint16*)ppu.cgram)[color]; + #else + color <<= 1; + return (ppu.cgram[color + 0] << 0) + (ppu.cgram[color + 1] << 8); + #endif +} + +unsigned PPU::Screen::get_direct_color(unsigned p, unsigned t) { + return ((t & 7) << 2) | ((p & 1) << 1) | + (((t >> 3) & 7) << 7) | (((p >> 1) & 1) << 6) | + ((t >> 6) << 13) | ((p >> 2) << 12); +} + +uint16 PPU::Screen::addsub(unsigned x, unsigned y, bool halve) { + if(!regs.color_mode) { + if(!halve) { + unsigned sum = x + y; + unsigned carry = (sum - ((x ^ y) & 0x0421)) & 0x8420; + return (sum - carry) | (carry - (carry >> 5)); + } else { + return (x + y - ((x ^ y) & 0x0421)) >> 1; + } + } else { + unsigned diff = x - y + 0x8420; + unsigned borrow = (diff - ((x ^ y) & 0x8420)) & 0x8420; + if(!halve) { + return (diff - borrow) & (borrow - (borrow >> 5)); + } else { + return (((diff - borrow) & (borrow - (borrow >> 5))) & 0x7bde) >> 1; + } + } +} + +void PPU::Screen::scanline() { + unsigned main_color = get_palette(0); + unsigned sub_color = (self.regs.pseudo_hires == false && self.regs.bgmode != 5 && self.regs.bgmode != 6) + ? regs.color : main_color; + + for(unsigned x = 0; x < 256; x++) { + output.main[x].color = main_color; + output.main[x].priority = 0; + output.main[x].source = 6; + + output.sub[x].color = sub_color; + output.sub[x].priority = 0; + output.sub[x].source = 6; + } + + window.render(0); + window.render(1); +} + +void PPU::Screen::render_black() { + uint32 *data = self.output + self.vcounter() * 1024; + if(self.interlace() && self.field()) data += 512; + memset(data, 0, self.display.width << 2); +} + +uint16 PPU::Screen::get_pixel_main(unsigned x) { + Output::Pixel main = output.main[x]; + Output::Pixel sub = output.sub[x]; + + if(!regs.addsub_mode) { + sub.source = 6; + sub.color = regs.color; + } + + if(!window.main[x]) { + if(!window.sub[x]) { + return 0x0000; + } + main.color = 0x0000; + } + + if(main.source != 5 && regs.color_enable[main.source] && window.sub[x]) { + bool halve = false; + if(regs.color_halve && window.main[x]) { + if(!regs.addsub_mode || sub.source != 6) halve = true; + } + return addsub(main.color, sub.color, halve); + } + + return main.color; +} + +uint16 PPU::Screen::get_pixel_sub(unsigned x) { + Output::Pixel main = output.sub[x]; + Output::Pixel sub = output.main[x]; + + if(!regs.addsub_mode) { + sub.source = 6; + sub.color = regs.color; + } + + if(!window.main[x]) { + if(!window.sub[x]) { + return 0x0000; + } + main.color = 0x0000; + } + + if(main.source != 5 && regs.color_enable[main.source] && window.sub[x]) { + bool halve = false; + if(regs.color_halve && window.main[x]) { + if(!regs.addsub_mode || sub.source != 6) halve = true; + } + return addsub(main.color, sub.color, halve); + } + + return main.color; +} + +void PPU::Screen::render() { + uint32 *data = self.output + self.vcounter() * 1024; + if(self.interlace() && self.field()) data += 512; + + if(!self.regs.pseudo_hires && self.regs.bgmode != 5 && self.regs.bgmode != 6) { + for(unsigned i = 0; i < 256; i++) { + data[i] = (self.regs.display_brightness << 15) | get_pixel_main(i); + } + } else { + for(unsigned i = 0; i < 256; i++) { + *data++ = (self.regs.display_brightness << 15) | get_pixel_sub(i); + *data++ = (self.regs.display_brightness << 15) | get_pixel_main(i); + } + } +} + +PPU::Screen::Screen(PPU &self) : self(self) { +} + +PPU::Screen::~Screen() { +} + +void PPU::Screen::Output::plot_main(unsigned x, unsigned color, unsigned priority, unsigned source) { + if(priority > main[x].priority) { + main[x].color = color; + main[x].priority = priority; + main[x].source = source; + } +} + +void PPU::Screen::Output::plot_sub(unsigned x, unsigned color, unsigned priority, unsigned source) { + if(priority > sub[x].priority) { + sub[x].color = color; + sub[x].priority = priority; + sub[x].source = source; + } +} + +#endif diff --git a/libsnes/snes/alt/ppu-performance/screen/screen.hpp b/libsnes/snes/alt/ppu-performance/screen/screen.hpp new file mode 100644 index 0000000..409ef86 --- /dev/null +++ b/libsnes/snes/alt/ppu-performance/screen/screen.hpp @@ -0,0 +1,43 @@ +struct Screen { + struct Regs { + bool addsub_mode; + bool direct_color; + + bool color_mode; + bool color_halve; + bool color_enable[7]; + + unsigned color_b; + unsigned color_g; + unsigned color_r; + unsigned color; + } regs; + + struct Output { + struct Pixel { + unsigned color; + unsigned priority; + unsigned source; + } main[256], sub[256]; + + alwaysinline void plot_main(unsigned x, unsigned color, unsigned priority, unsigned source); + alwaysinline void plot_sub(unsigned x, unsigned color, unsigned priority, unsigned source); + } output; + + ColorWindow window; + + alwaysinline unsigned get_palette(unsigned color); + unsigned get_direct_color(unsigned palette, unsigned tile); + alwaysinline uint16 addsub(unsigned x, unsigned y, bool halve); + void scanline(); + void render_black(); + alwaysinline uint16 get_pixel_main(unsigned x); + alwaysinline uint16 get_pixel_sub(unsigned x); + void render(); + + void serialize(serializer&); + Screen(PPU &self); + ~Screen(); + + PPU &self; +}; diff --git a/libsnes/snes/alt/ppu-performance/serialization.cpp b/libsnes/snes/alt/ppu-performance/serialization.cpp new file mode 100644 index 0000000..cc4c10c --- /dev/null +++ b/libsnes/snes/alt/ppu-performance/serialization.cpp @@ -0,0 +1,249 @@ +#ifdef PPU_CPP + +void PPUcounter::serialize(serializer &s) { + s.integer(status.interlace); + s.integer(status.field); + s.integer(status.vcounter); + s.integer(status.hcounter); + + s.array(history.field); + s.array(history.vcounter); + s.array(history.hcounter); + s.integer(history.index); +} + +void PPU::serialize(serializer &s) { + Processor::serialize(s); + PPUcounter::serialize(s); + + s.array(vram); + s.array(oam); + s.array(cgram); + + cache.serialize(s); + bg1.serialize(s); + bg2.serialize(s); + bg3.serialize(s); + bg4.serialize(s); + sprite.serialize(s); + screen.serialize(s); + + s.integer(display.interlace); + s.integer(display.overscan); + s.integer(display.width); + s.integer(display.height); + + s.integer(regs.ppu1_mdr); + s.integer(regs.ppu2_mdr); + + s.integer(regs.vram_readbuffer); + s.integer(regs.oam_latchdata); + s.integer(regs.cgram_latchdata); + s.integer(regs.bgofs_latchdata); + s.integer(regs.mode7_latchdata); + + s.integer(regs.counters_latched); + s.integer(regs.latch_hcounter); + s.integer(regs.latch_vcounter); + + s.integer(regs.display_disable); + s.integer(regs.display_brightness); + + s.integer(regs.oam_baseaddr); + s.integer(regs.oam_addr); + s.integer(regs.oam_priority); + + s.integer(regs.bg3_priority); + s.integer(regs.bgmode); + + s.integer(regs.mode7_hoffset); + + s.integer(regs.mode7_voffset); + + s.integer(regs.vram_incmode); + s.integer(regs.vram_mapping); + s.integer(regs.vram_incsize); + + s.integer(regs.vram_addr); + + s.integer(regs.mode7_repeat); + s.integer(regs.mode7_vflip); + s.integer(regs.mode7_hflip); + + s.integer(regs.m7a); + s.integer(regs.m7b); + s.integer(regs.m7c); + s.integer(regs.m7d); + s.integer(regs.m7x); + s.integer(regs.m7y); + + s.integer(regs.cgram_addr); + + s.integer(regs.window_one_left); + s.integer(regs.window_one_right); + s.integer(regs.window_two_left); + s.integer(regs.window_two_right); + + s.integer(regs.mode7_extbg); + s.integer(regs.pseudo_hires); + s.integer(regs.overscan); + s.integer(regs.interlace); + + s.integer(regs.hcounter); + + s.integer(regs.vcounter); +} + +void PPU::Cache::serialize(serializer &s) { + //rather than save ~512KB worth of cached tiledata, invalidate it all + for(unsigned i = 0; i < 4096; i++) tilevalid[0][i] = false; + for(unsigned i = 0; i < 2048; i++) tilevalid[1][i] = false; + for(unsigned i = 0; i < 1024; i++) tilevalid[2][i] = false; +} + +void PPU::Background::serialize(serializer &s) { + s.integer(regs.mode); + s.integer(regs.priority0); + s.integer(regs.priority1); + + s.integer(regs.tile_size); + s.integer(regs.mosaic); + + s.integer(regs.screen_addr); + s.integer(regs.screen_size); + s.integer(regs.tiledata_addr); + + s.integer(regs.hoffset); + s.integer(regs.voffset); + + s.integer(regs.main_enable); + s.integer(regs.sub_enable); + + s.integer(hires); + s.integer(width); + + s.integer(tile_width); + s.integer(tile_height); + + s.integer(mask_x); + s.integer(mask_y); + + s.integer(scx); + s.integer(scy); + + s.integer(hscroll); + s.integer(vscroll); + + s.integer(mosaic_vcounter); + s.integer(mosaic_voffset); + + window.serialize(s); +} + +void PPU::Sprite::serialize(serializer &s) { + s.integer(regs.priority0); + s.integer(regs.priority1); + s.integer(regs.priority2); + s.integer(regs.priority3); + + s.integer(regs.base_size); + s.integer(regs.nameselect); + s.integer(regs.tiledata_addr); + s.integer(regs.first_sprite); + + s.integer(regs.main_enable); + s.integer(regs.sub_enable); + + s.integer(regs.interlace); + + s.integer(regs.time_over); + s.integer(regs.range_over); + + for(unsigned i = 0; i < 128; i++) { + s.integer(list[i].width); + s.integer(list[i].height); + s.integer(list[i].x); + s.integer(list[i].y); + s.integer(list[i].character); + s.integer(list[i].use_nameselect); + s.integer(list[i].vflip); + s.integer(list[i].hflip); + s.integer(list[i].palette); + s.integer(list[i].priority); + s.integer(list[i].size); + } + s.integer(list_valid); + + s.array(itemlist); + for(unsigned i = 0; i < 34; i++) { + s.integer(tilelist[i].x); + s.integer(tilelist[i].y); + s.integer(tilelist[i].priority); + s.integer(tilelist[i].palette); + s.integer(tilelist[i].tile); + s.integer(tilelist[i].hflip); + } + + s.array(output.palette); + s.array(output.priority); + + window.serialize(s); +} + +void PPU::Screen::serialize(serializer &s) { + s.integer(regs.addsub_mode); + s.integer(regs.direct_color); + + s.integer(regs.color_mode); + s.integer(regs.color_halve); + s.array(regs.color_enable); + + s.integer(regs.color_b); + s.integer(regs.color_g); + s.integer(regs.color_r); + s.integer(regs.color); + + for(unsigned i = 0; i < 256; i++) { + s.integer(output.main[i].color); + s.integer(output.main[i].priority); + s.integer(output.main[i].source); + + s.integer(output.sub[i].color); + s.integer(output.sub[i].priority); + s.integer(output.sub[i].source); + } + + window.serialize(s); +} + +void PPU::LayerWindow::serialize(serializer &s) { + s.integer(one_enable); + s.integer(one_invert); + s.integer(two_enable); + s.integer(two_invert); + + s.integer(mask); + + s.integer(main_enable); + s.integer(sub_enable); + + s.array(main); + s.array(sub); +} + +void PPU::ColorWindow::serialize(serializer &s) { + s.integer(one_enable); + s.integer(one_invert); + s.integer(two_enable); + s.integer(two_invert); + + s.integer(mask); + + s.integer(main_mask); + s.integer(sub_mask); + + s.array(main); + s.array(sub); +} + +#endif diff --git a/libsnes/snes/alt/ppu-performance/sprite/sprite.cpp b/libsnes/snes/alt/ppu-performance/sprite/sprite.cpp new file mode 100644 index 0000000..fc04b03 --- /dev/null +++ b/libsnes/snes/alt/ppu-performance/sprite/sprite.cpp @@ -0,0 +1,190 @@ +#ifdef PPU_CPP + +void PPU::Sprite::frame() { + regs.time_over = false; + regs.range_over = false; +} + +void PPU::Sprite::update_list(unsigned addr, uint8 data) { + if(addr < 0x0200) { + unsigned i = addr >> 2; + switch(addr & 3) { + case 0: list[i].x = (list[i].x & 0x0100) | data; break; + case 1: list[i].y = (data + 1) & 0xff; break; + case 2: list[i].character = data; break; + case 3: list[i].vflip = data & 0x80; + list[i].hflip = data & 0x40; + list[i].priority = (data >> 4) & 3; + list[i].palette = (data >> 1) & 7; + list[i].use_nameselect = data & 0x01; + break; + } + } else { + unsigned i = (addr & 0x1f) << 2; + list[i + 0].x = ((data & 0x01) << 8) | (list[i + 0].x & 0xff); + list[i + 0].size = data & 0x02; + list[i + 1].x = ((data & 0x04) << 6) | (list[i + 1].x & 0xff); + list[i + 1].size = data & 0x08; + list[i + 2].x = ((data & 0x10) << 4) | (list[i + 2].x & 0xff); + list[i + 2].size = data & 0x20; + list[i + 3].x = ((data & 0x40) << 2) | (list[i + 3].x & 0xff); + list[i + 3].size = data & 0x80; + list_valid = false; + } +} + +void PPU::Sprite::address_reset() { + self.regs.oam_addr = self.regs.oam_baseaddr << 1; + set_first(); +} + +void PPU::Sprite::set_first() { + regs.first_sprite = (self.regs.oam_priority == false ? 0 : (self.regs.oam_addr >> 2) & 127); +} + +bool PPU::Sprite::on_scanline(unsigned sprite) { + List &s = list[sprite]; + if(s.x > 256 && (s.x + s.width - 1) < 512) return false; + signed height = (regs.interlace == false ? s.height : s.height >> 1); + if(self.vcounter() >= s.y && self.vcounter() < (s.y + height)) return true; + if((s.y + height) >= 256 && self.vcounter() < ((s.y + height) & 255)) return true; + return false; +} + +void PPU::Sprite::render() { + if(list_valid == false) { + list_valid = true; + for(unsigned i = 0; i < 128; i++) { + if(list[i].size == 0) { + static unsigned width[] = { 8, 8, 8, 16, 16, 32, 16, 16 }; + static unsigned height[] = { 8, 8, 8, 16, 16, 32, 32, 32 }; + list[i].width = width[regs.base_size]; + list[i].height = height[regs.base_size]; + } else { + static unsigned width[] = { 16, 32, 64, 32, 64, 64, 32, 32 }; + static unsigned height[] = { 16, 32, 64, 32, 64, 64, 64, 32 }; + list[i].width = width[regs.base_size]; + list[i].height = height[regs.base_size]; + if(regs.interlace && regs.base_size >= 6) list[i].height = 16; + } + } + } + + unsigned itemcount = 0; + unsigned tilecount = 0; + memset(output.priority, 0xff, 256); + memset(itemlist, 0xff, 32); + for(unsigned i = 0; i < 34; i++) tilelist[i].tile = 0xffff; + + for(unsigned i = 0; i < 128; i++) { + unsigned s = (regs.first_sprite + i) & 127; + if(on_scanline(s) == false) continue; + if(itemcount++ >= 32) break; + itemlist[itemcount - 1] = s; + } + + for(signed i = 31; i >= 0; i--) { + if(itemlist[i] == 0xff) continue; + List &s = list[itemlist[i]]; + unsigned tile_width = s.width >> 3; + signed x = s.x; + signed y = (self.vcounter() - s.y) & 0xff; + if(regs.interlace) y <<= 1; + + if(s.vflip) { + if(s.width == s.height) { + y = (s.height - 1) - y; + } else { + y = (y < s.width) ? ((s.width - 1) - y) : (s.width + ((s.width - 1) - (y - s.width))); + } + } + + if(regs.interlace) { + y = (s.vflip == false) ? (y + self.field()) : (y - self.field()); + } + + x &= 511; + y &= 255; + + uint16 tdaddr = regs.tiledata_addr; + uint16 chrx = (s.character >> 0) & 15; + uint16 chry = (s.character >> 4) & 15; + if(s.use_nameselect) { + tdaddr += (256 * 32) + (regs.nameselect << 13); + } + chry += (y >> 3); + chry &= 15; + chry <<= 4; + + for(unsigned tx = 0; tx < tile_width; tx++) { + unsigned sx = (x + (tx << 3)) & 511; + if(x != 256 && sx >= 256 && (sx + 7) < 512) continue; + if(tilecount++ >= 34) break; + + unsigned n = tilecount - 1; + tilelist[n].x = sx; + tilelist[n].y = y; + tilelist[n].priority = s.priority; + tilelist[n].palette = 128 + (s.palette << 4); + tilelist[n].hflip = s.hflip; + + unsigned mx = (s.hflip == false) ? tx : ((tile_width - 1) - tx); + unsigned pos = tdaddr + ((chry + ((chrx + mx) & 15)) << 5); + tilelist[n].tile = (pos >> 5) & 0x07ff; + } + } + + regs.time_over |= (tilecount > 34); + regs.range_over |= (itemcount > 32); + + if(regs.main_enable == false && regs.sub_enable == false) return; + + for(unsigned i = 0; i < 34; i++) { + if(tilelist[i].tile == 0xffff) continue; + + TileList &t = tilelist[i]; + uint8 *tiledata = self.cache.tile_4bpp(t.tile); + tiledata += (t.y & 7) << 3; + unsigned sx = t.x; + for(unsigned x = 0; x < 8; x++) { + sx &= 511; + if(sx < 256) { + unsigned color = *(tiledata + (t.hflip == false ? x : 7 - x)); + if(color) { + color += t.palette; + output.palette[sx] = color; + output.priority[sx] = t.priority; + } + } + sx++; + } + } + + if(regs.main_enable) window.render(0); + if(regs.sub_enable) window.render(1); + + unsigned priority0 = (priority0_enable ? regs.priority0 : 0); + unsigned priority1 = (priority1_enable ? regs.priority1 : 0); + unsigned priority2 = (priority2_enable ? regs.priority2 : 0); + unsigned priority3 = (priority3_enable ? regs.priority3 : 0); + if(priority0 + priority1 + priority2 + priority3 == 0) return; + const unsigned priority_table[] = { priority0, priority1, priority2, priority3 }; + + for(unsigned x = 0; x < 256; x++) { + if(output.priority[x] == 0xff) continue; + unsigned priority = priority_table[output.priority[x]]; + unsigned palette = output.palette[x]; + unsigned color = self.screen.get_palette(output.palette[x]); + if(regs.main_enable && !window.main[x]) self.screen.output.plot_main(x, color, priority, 4 + (palette < 192)); + if(regs.sub_enable && !window.sub[x]) self.screen.output.plot_sub(x, color, priority, 4 + (palette < 192)); + } +} + +PPU::Sprite::Sprite(PPU &self) : self(self) { + priority0_enable = true; + priority1_enable = true; + priority2_enable = true; + priority3_enable = true; +} + +#endif diff --git a/libsnes/snes/alt/ppu-performance/sprite/sprite.hpp b/libsnes/snes/alt/ppu-performance/sprite/sprite.hpp new file mode 100644 index 0000000..8c1d2ba --- /dev/null +++ b/libsnes/snes/alt/ppu-performance/sprite/sprite.hpp @@ -0,0 +1,70 @@ +struct Sprite { + bool priority0_enable; + bool priority1_enable; + bool priority2_enable; + bool priority3_enable; + + struct Regs { + unsigned priority0; + unsigned priority1; + unsigned priority2; + unsigned priority3; + + unsigned base_size; + unsigned nameselect; + unsigned tiledata_addr; + unsigned first_sprite; + + bool main_enable; + bool sub_enable; + + bool interlace; + + bool time_over; + bool range_over; + } regs; + + struct List { + unsigned width; + unsigned height; + unsigned x; + unsigned y; + unsigned character; + bool use_nameselect; + bool vflip; + bool hflip; + unsigned palette; + unsigned priority; + bool size; + } list[128]; + bool list_valid; + + uint8 itemlist[32]; + struct TileList { + unsigned x; + unsigned y; + unsigned priority; + unsigned palette; + unsigned tile; + bool hflip; + } tilelist[34]; + + struct Output { + uint8 palette[256]; + uint8 priority[256]; + } output; + + LayerWindow window; + + void frame(); + void update_list(unsigned addr, uint8 data); + void address_reset(); + void set_first(); + alwaysinline bool on_scanline(unsigned sprite); + void render(); + + void serialize(serializer&); + Sprite(PPU &self); + + PPU &self; +}; diff --git a/libsnes/snes/alt/ppu-performance/window/window.cpp b/libsnes/snes/alt/ppu-performance/window/window.cpp new file mode 100644 index 0000000..fc3204c --- /dev/null +++ b/libsnes/snes/alt/ppu-performance/window/window.cpp @@ -0,0 +1,98 @@ +#ifdef PPU_CPP + +void PPU::LayerWindow::render(bool screen) { + uint8 *output; + if(screen == 0) { + output = main; + if(main_enable == false) { + memset(output, 0, 256); + return; + } + } else { + output = sub; + if(sub_enable == false) { + memset(output, 0, 256); + return; + } + } + + if(one_enable == false && two_enable == false) { + memset(output, 0, 256); + return; + } + + if(one_enable == true && two_enable == false) { + bool set = 1 ^ one_invert, clr = !set; + for(unsigned x = 0; x < 256; x++) { + output[x] = (x >= ppu.regs.window_one_left && x <= ppu.regs.window_one_right) ? set : clr; + } + return; + } + + if(one_enable == false && two_enable == true) { + bool set = 1 ^ two_invert, clr = !set; + for(unsigned x = 0; x < 256; x++) { + output[x] = (x >= ppu.regs.window_two_left && x <= ppu.regs.window_two_right) ? set : clr; + } + return; + } + + for(unsigned x = 0; x < 256; x++) { + bool one_mask = (x >= ppu.regs.window_one_left && x <= ppu.regs.window_one_right) ^ one_invert; + bool two_mask = (x >= ppu.regs.window_two_left && x <= ppu.regs.window_two_right) ^ two_invert; + switch(mask) { + case 0: output[x] = one_mask | (two_mask == 1); break; + case 1: output[x] = one_mask & (two_mask == 1); break; + case 2: output[x] = one_mask ^ (two_mask == 1); break; + case 3: output[x] = one_mask ^ (two_mask == 0); break; + } + } +} + +// + +void PPU::ColorWindow::render(bool screen) { + uint8 *output = (screen == 0 ? main : sub); + bool set = 1, clr = 0; + + switch(screen == 0 ? main_mask : sub_mask) { + case 0: memset(output, 1, 256); return; //always + case 1: set = 1, clr = 0; break; //inside window only + case 2: set = 0, clr = 1; break; //outside window only + case 3: memset(output, 0, 256); return; //never + } + + if(one_enable == false && two_enable == false) { + memset(output, clr, 256); + return; + } + + if(one_enable == true && two_enable == false) { + if(one_invert) { set ^= 1; clr ^= 1; } + for(unsigned x = 0; x < 256; x++) { + output[x] = (x >= ppu.regs.window_one_left && x <= ppu.regs.window_one_right) ? set : clr; + } + return; + } + + if(one_enable == false && two_enable == true) { + if(two_invert) { set ^= 1; clr ^= 1; } + for(unsigned x = 0; x < 256; x++) { + output[x] = (x >= ppu.regs.window_two_left && x <= ppu.regs.window_two_right) ? set : clr; + } + return; + } + + for(unsigned x = 0; x < 256; x++) { + bool one_mask = (x >= ppu.regs.window_one_left && x <= ppu.regs.window_one_right) ^ one_invert; + bool two_mask = (x >= ppu.regs.window_two_left && x <= ppu.regs.window_two_right) ^ two_invert; + switch(mask) { + case 0: output[x] = one_mask | (two_mask == 1) ? set : clr; break; + case 1: output[x] = one_mask & (two_mask == 1) ? set : clr; break; + case 2: output[x] = one_mask ^ (two_mask == 1) ? set : clr; break; + case 3: output[x] = one_mask ^ (two_mask == 0) ? set : clr; break; + } + } +} + +#endif diff --git a/libsnes/snes/alt/ppu-performance/window/window.hpp b/libsnes/snes/alt/ppu-performance/window/window.hpp new file mode 100644 index 0000000..af2fb4d --- /dev/null +++ b/libsnes/snes/alt/ppu-performance/window/window.hpp @@ -0,0 +1,35 @@ +struct LayerWindow { + bool one_enable; + bool one_invert; + bool two_enable; + bool two_invert; + + unsigned mask; + + bool main_enable; + bool sub_enable; + + uint8 main[256]; + uint8 sub[256]; + + void render(bool screen); + void serialize(serializer&); +}; + +struct ColorWindow { + bool one_enable; + bool one_invert; + bool two_enable; + bool two_invert; + + unsigned mask; + + unsigned main_mask; + unsigned sub_mask; + + uint8 main[256]; + uint8 sub[256]; + + void render(bool screen); + void serialize(serializer&); +}; diff --git a/libsnes/snes/alt/smp/algorithms.cpp b/libsnes/snes/alt/smp/algorithms.cpp new file mode 100644 index 0000000..a55369f --- /dev/null +++ b/libsnes/snes/alt/smp/algorithms.cpp @@ -0,0 +1,122 @@ +uint8 SMP::op_adc(uint8 x, uint8 y) { + int r = x + y + regs.p.c; + regs.p.n = r & 0x80; + regs.p.v = ~(x ^ y) & (x ^ r) & 0x80; + regs.p.h = (x ^ y ^ r) & 0x10; + regs.p.z = (uint8)r == 0; + regs.p.c = r > 0xff; + return r; +} + +uint16 SMP::op_addw(uint16 x, uint16 y) { + uint16 r; + regs.p.c = 0; + r = op_adc(x, y); + r |= op_adc(x >> 8, y >> 8) << 8; + regs.p.z = r == 0; + return r; +} + +uint8 SMP::op_and(uint8 x, uint8 y) { + x &= y; + regs.p.n = x & 0x80; + regs.p.z = x == 0; + return x; +} + +uint8 SMP::op_cmp(uint8 x, uint8 y) { + int r = x - y; + regs.p.n = r & 0x80; + regs.p.z = (uint8)r == 0; + regs.p.c = r >= 0; + return x; +} + +uint16 SMP::op_cmpw(uint16 x, uint16 y) { + int r = x - y; + regs.p.n = r & 0x8000; + regs.p.z = (uint16)r == 0; + regs.p.c = r >= 0; + return x; +} + +uint8 SMP::op_eor(uint8 x, uint8 y) { + x ^= y; + regs.p.n = x & 0x80; + regs.p.z = x == 0; + return x; +} + +uint8 SMP::op_or(uint8 x, uint8 y) { + x |= y; + regs.p.n = x & 0x80; + regs.p.z = x == 0; + return x; +} + +uint8 SMP::op_sbc(uint8 x, uint8 y) { + int r = x - y - !regs.p.c; + regs.p.n = r & 0x80; + regs.p.v = (x ^ y) & (x ^ r) & 0x80; + regs.p.h = !((x ^ y ^ r) & 0x10); + regs.p.z = (uint8)r == 0; + regs.p.c = r >= 0; + return r; +} + +uint16 SMP::op_subw(uint16 x, uint16 y) { + uint16 r; + regs.p.c = 1; + r = op_sbc(x, y); + r |= op_sbc(x >> 8, y >> 8) << 8; + regs.p.z = r == 0; + return r; +} + +uint8 SMP::op_inc(uint8 x) { + x++; + regs.p.n = x & 0x80; + regs.p.z = x == 0; + return x; +} + +uint8 SMP::op_dec(uint8 x) { + x--; + regs.p.n = x & 0x80; + regs.p.z = x == 0; + return x; +} + +uint8 SMP::op_asl(uint8 x) { + regs.p.c = x & 0x80; + x <<= 1; + regs.p.n = x & 0x80; + regs.p.z = x == 0; + return x; +} + +uint8 SMP::op_lsr(uint8 x) { + regs.p.c = x & 0x01; + x >>= 1; + regs.p.n = x & 0x80; + regs.p.z = x == 0; + return x; +} + +uint8 SMP::op_rol(uint8 x) { + unsigned carry = (unsigned)regs.p.c; + regs.p.c = x & 0x80; + x = (x << 1) | carry; + regs.p.n = x & 0x80; + regs.p.z = x == 0; + return x; +} + +uint8 SMP::op_ror(uint8 x) { + unsigned carry = (unsigned)regs.p.c << 7; + regs.p.c = x & 0x01; + x = carry | (x >> 1); + regs.p.n = x & 0x80; + regs.p.z = x == 0; + return x; +} diff --git a/libsnes/snes/alt/smp/core.cpp b/libsnes/snes/alt/smp/core.cpp new file mode 100644 index 0000000..21bf5bf --- /dev/null +++ b/libsnes/snes/alt/smp/core.cpp @@ -0,0 +1,104 @@ +void SMP::tick() { + timer0.tick(); + timer1.tick(); + timer2.tick(); + + clock += cycle_step_cpu; + dsp.clock -= 24; + synchronize_dsp(); +} + +void SMP::op_io() { + #if defined(CYCLE_ACCURATE) + tick(); + #endif +} + +uint8 SMP::op_read(uint16 addr) { + #if defined(CYCLE_ACCURATE) + tick(); + #endif + if((addr & 0xfff0) == 0x00f0) return mmio_read(addr); + if(addr >= 0xffc0 && status.iplrom_enable) return iplrom[addr & 0x3f]; + return apuram[addr]; +} + +void SMP::op_write(uint16 addr, uint8 data) { + #if defined(CYCLE_ACCURATE) + tick(); + #endif + if((addr & 0xfff0) == 0x00f0) mmio_write(addr, data); + apuram[addr] = data; //all writes go to RAM, even MMIO writes +} + +void SMP::op_step() { + #define op_readpc() op_read(regs.pc++) + #define op_readdp(addr) op_read((regs.p.p << 8) + addr) + #define op_writedp(addr, data) op_write((regs.p.p << 8) + addr, data) + #define op_readaddr(addr) op_read(addr) + #define op_writeaddr(addr, data) op_write(addr, data) + #define op_readstack() op_read(0x0100 | ++regs.sp) + #define op_writestack(data) op_write(0x0100 | regs.sp--, data) + + #if defined(CYCLE_ACCURATE) + + if(opcode_cycle == 0) { + opcode_number = op_readpc(); + opcode_cycle++; + } else switch(opcode_number) { + #include "core/opcycle_misc.cpp" + #include "core/opcycle_mov.cpp" + #include "core/opcycle_pc.cpp" + #include "core/opcycle_read.cpp" + #include "core/opcycle_rmw.cpp" + } + + #else + + unsigned opcode = op_readpc(); + switch(opcode) { + #include "core/op_misc.cpp" + #include "core/op_mov.cpp" + #include "core/op_pc.cpp" + #include "core/op_read.cpp" + #include "core/op_rmw.cpp" + } + + //TODO: untaken branches should consume less cycles + + timer0.tick(cycle_count_table[opcode]); + timer1.tick(cycle_count_table[opcode]); + timer2.tick(cycle_count_table[opcode]); + + clock += cycle_table_cpu[opcode]; + dsp.clock -= cycle_table_dsp[opcode]; + synchronize_dsp(); + + #endif +} + +const unsigned SMP::cycle_count_table[256] = { + #define c 12 +//0 1 2 3 4 5 6 7 8 9 A B C D E F + 2,8,4,7, 3,4,3,6, 2,6,5,4, 5,4,6,8, //0 + 4,8,4,7, 4,5,5,6, 5,5,6,5, 2,2,4,6, //1 + 2,8,4,7, 3,4,3,6, 2,6,5,4, 5,4,7,4, //2 + 4,8,4,7, 4,5,5,6, 5,5,6,5, 2,2,3,8, //3 + + 2,8,4,7, 3,4,3,6, 2,6,4,4, 5,4,6,6, //4 + 4,8,4,7, 4,5,5,6, 5,5,4,5, 2,2,4,3, //5 + 2,8,4,7, 3,4,3,6, 2,6,4,4, 5,4,7,5, //6 + 4,8,4,7, 4,5,5,6, 5,5,5,5, 2,2,3,6, //7 + + 2,8,4,7, 3,4,3,6, 2,6,5,4, 5,2,4,5, //8 + 4,8,4,7, 4,5,5,6, 5,5,5,5, 2,2,c,5, //9 + 3,8,4,7, 3,4,3,6, 2,6,4,4, 5,2,4,4, //A + 4,8,4,7, 4,5,5,6, 5,5,5,5, 2,2,3,4, //B + + 3,8,4,7, 4,5,4,7, 2,5,6,4, 5,2,4,9, //C + 4,8,4,7, 5,6,6,7, 4,5,5,5, 2,2,8,3, //D + 2,8,4,7, 3,4,3,6, 2,4,5,3, 4,3,4,1, //E + 4,8,4,7, 4,5,5,6, 3,4,5,4, 2,2,6,1, //F + + #undef c +}; diff --git a/libsnes/snes/alt/smp/core/cc.sh b/libsnes/snes/alt/smp/core/cc.sh new file mode 100644 index 0000000..937b713 --- /dev/null +++ b/libsnes/snes/alt/smp/core/cc.sh @@ -0,0 +1 @@ +g++-4.5 -std=gnu++0x -I../../../.. -o generate generate.cpp diff --git a/libsnes/snes/alt/smp/core/generate.cpp b/libsnes/snes/alt/smp/core/generate.cpp new file mode 100644 index 0000000..77ab3ed --- /dev/null +++ b/libsnes/snes/alt/smp/core/generate.cpp @@ -0,0 +1,154 @@ +#include +#include +#include +using namespace nall; + +static bool cycle_accurate; + +struct opcode_t { + string name; + lstring args; + unsigned opcode; +}; + +void generate(const char *sourceFilename, const char *targetFilename) { + file fp; + fp.open(targetFilename, file::mode::write); + + string filedata; + filedata.readfile(sourceFilename); + filedata.replace("\r", ""); + + lstring block; + block.split("\n\n", filedata); + + foreach(data, block) { + lstring lines; + lines.split("\n", data); + + linear_vector array; + + unsigned sourceStart = 0; + foreach(line, lines, currentLine) { + line.transform("()", "``"); + lstring part; + part.split("`", line); + lstring arguments; + arguments.split(", ", part[1]); + + opcode_t opcode; + opcode.name = part[0]; + opcode.args = arguments; + opcode.opcode = hex(arguments[0]); + array.append(opcode); + + line.rtrim<1>(","); + if(line.endswith(" {")) { + line.rtrim<1>("{ "); + sourceStart = currentLine + 1; + break; + } + } + + if(cycle_accurate == false) { + foreach(opcode, array) { + fp.print("case 0x", hex<2>(opcode.opcode), ": {\n"); + + for(unsigned n = sourceStart; n < lines.size(); n++) { + if(lines[n] == "}") break; + + string output; + + if(lines[n].beginswith(" ")) { + output = lines[n]; + } else { + lstring part; + part.split<1>(":", lines[n]); + output = { " ", part[1] }; + } + + output.replace("$1", opcode.args[1]); + output.replace("$2", opcode.args[2]); + output.replace("$3", opcode.args[3]); + output.replace("$4", opcode.args[4]); + output.replace("$5", opcode.args[5]); + output.replace("$6", opcode.args[6]); + output.replace("$7", opcode.args[7]); + output.replace("$8", opcode.args[8]); + output.replace("end;", "break;"); + + fp.print(output, "\n"); + } + + fp.print(" break;\n"); + fp.print("}\n\n"); + } + } else { + foreach(opcode, array) { + fp.print("case 0x", hex<2>(opcode.opcode), ": {\n"); + fp.print(" switch(opcode_cycle++) {\n"); + + for(unsigned n = sourceStart; n < lines.size(); n++) { + if(lines[n] == "}") break; + + bool nextLineEndsCycle = false; + if(lines[n + 1] == "}") nextLineEndsCycle = true; + if(lines[n + 1].beginswith(" ") == false) nextLineEndsCycle = true; + + string output; + + if(lines[n].beginswith(" ")) { + output = { " ", lines[n] }; + } else { + lstring part; + part.split<1>(":", lines[n]); + fp.print(" case ", (unsigned)decimal(part[0]), ":\n"); + output = { " ", part[1] }; + } + + output.replace("$1", opcode.args[1]); + output.replace("$2", opcode.args[2]); + output.replace("$3", opcode.args[3]); + output.replace("$4", opcode.args[4]); + output.replace("$5", opcode.args[5]); + output.replace("$6", opcode.args[6]); + output.replace("$7", opcode.args[7]); + output.replace("$8", opcode.args[8]); + output.replace("end;", "{ opcode_cycle = 0; break; }"); + + fp.print(output, "\n"); + if(nextLineEndsCycle) { + if(lines[n + 1].beginswith("}")) { + fp.print(" opcode_cycle = 0;\n"); + } + fp.print(" break;\n"); + } + } + + fp.print(" }\n"); + fp.print(" break;\n"); + fp.print("}\n\n"); + } + } + } + + fp.close(); +} + +int main() { + cycle_accurate = false; + generate("op_misc.b", "op_misc.cpp"); + generate("op_mov.b", "op_mov.cpp" ); + generate("op_pc.b", "op_pc.cpp" ); + generate("op_read.b", "op_read.cpp"); + generate("op_rmw.b", "op_rmw.cpp" ); + + cycle_accurate = true; + generate("op_misc.b", "opcycle_misc.cpp"); + generate("op_mov.b", "opcycle_mov.cpp" ); + generate("op_pc.b", "opcycle_pc.cpp" ); + generate("op_read.b", "opcycle_read.cpp"); + generate("op_rmw.b", "opcycle_rmw.cpp" ); + + return 0; +} diff --git a/libsnes/snes/alt/smp/core/op_misc.b b/libsnes/snes/alt/smp/core/op_misc.b new file mode 100644 index 0000000..fb25865 --- /dev/null +++ b/libsnes/snes/alt/smp/core/op_misc.b @@ -0,0 +1,163 @@ +nop(0x00) { +1:op_io(); +} + +sleep(0xef), +stop(0xff) { +1:op_io(); +2:op_io(); + regs.pc--; +} + +xcn(0x9f) { +1:op_io(); +2:op_io(); +3:op_io(); +4:op_io(); + regs.a = (regs.a >> 4) | (regs.a << 4); + regs.p.n = !!(regs.a & 0x80); + regs.p.z = (regs.a == 0); +} + +daa(0xdf) { +1:op_io(); +2:op_io(); + if(regs.p.c || (regs.a) > 0x99) { + regs.a += 0x60; + regs.p.c = 1; + } + if(regs.p.h || (regs.a & 15) > 0x09) { + regs.a += 0x06; + } + regs.p.n = !!(regs.a & 0x80); + regs.p.z = (regs.a == 0); +} + +das(0xbe) { +1:op_io(); +2:op_io(); + if(!regs.p.c || (regs.a) > 0x99) { + regs.a -= 0x60; + regs.p.c = 0; + } + if(!regs.p.h || (regs.a & 15) > 0x09) { + regs.a -= 0x06; + } + regs.p.n = !!(regs.a & 0x80); + regs.p.z = (regs.a == 0); +} + +clrc(0x60, regs.p.c = 0), +clrp(0x20, regs.p.p = 0), +setc(0x80, regs.p.c = 1), +setp(0x40, regs.p.p = 1) { +1:op_io(); + $1; +} + +clrv(0xe0) { +1:op_io(); + regs.p.v = 0; + regs.p.h = 0; +} + +notc(0xed) { +1:op_io(); +2:op_io(); + regs.p.c = !regs.p.c; +} + +ei(0xa0, 1), +di(0xc0, 0) { +1:op_io(); +2:op_io(); + regs.p.i = $1; +} + +set0_dp(0x02, rd |= 0x01), +clr0_dp(0x12, rd &= ~0x01), +set1_dp(0x22, rd |= 0x02), +clr1_dp(0x32, rd &= ~0x02), +set2_dp(0x42, rd |= 0x04), +clr2_dp(0x52, rd &= ~0x04), +set3_dp(0x62, rd |= 0x08), +clr3_dp(0x72, rd &= ~0x08), +set4_dp(0x82, rd |= 0x10), +clr4_dp(0x92, rd &= ~0x10), +set5_dp(0xa2, rd |= 0x20), +clr5_dp(0xb2, rd &= ~0x20), +set6_dp(0xc2, rd |= 0x40), +clr6_dp(0xd2, rd &= ~0x40), +set7_dp(0xe2, rd |= 0x80), +clr7_dp(0xf2, rd &= ~0x80) { +1:dp = op_readpc(); +2:rd = op_readdp(dp); +3:$1; + op_writedp(dp, rd); +} + +push_a(0x2d, a), +push_x(0x4d, x), +push_y(0x6d, y), +push_p(0x0d, p) { +1:op_io(); +2:op_io(); +3:op_writestack(regs.$1); +} + +pop_a(0xae, a), +pop_x(0xce, x), +pop_y(0xee, y), +pop_p(0x8e, p) { +1:op_io(); +2:op_io(); +3:regs.$1 = op_readstack(); +} + +mul_ya(0xcf) { +1:op_io(); +2:op_io(); +3:op_io(); +4:op_io(); +5:op_io(); +6:op_io(); +7:op_io(); +8:op_io(); + ya = regs.y * regs.a; + regs.a = ya; + regs.y = ya >> 8; + //result is set based on y (high-byte) only + regs.p.n = !!(regs.y & 0x80); + regs.p.z = (regs.y == 0); +} + +div_ya_x(0x9e) { +1:op_io(); +2:op_io(); +3:op_io(); +4:op_io(); +5:op_io(); +6:op_io(); +7:op_io(); +8:op_io(); +9:op_io(); +10:op_io(); +11:op_io(); + ya = regs.ya; + //overflow set if quotient >= 256 + regs.p.v = !!(regs.y >= regs.x); + regs.p.h = !!((regs.y & 15) >= (regs.x & 15)); + if(regs.y < (regs.x << 1)) { + //if quotient is <= 511 (will fit into 9-bit result) + regs.a = ya / regs.x; + regs.y = ya % regs.x; + } else { + //otherwise, the quotient won't fit into regs.p.v + regs.a + //this emulates the odd behavior of the S-SMP in this case + regs.a = 255 - (ya - (regs.x << 9)) / (256 - regs.x); + regs.y = regs.x + (ya - (regs.x << 9)) % (256 - regs.x); + } + //result is set based on a (quotient) only + regs.p.n = !!(regs.a & 0x80); + regs.p.z = (regs.a == 0); +} diff --git a/libsnes/snes/alt/smp/core/op_misc.cpp b/libsnes/snes/alt/smp/core/op_misc.cpp new file mode 100644 index 0000000..9a6a062 --- /dev/null +++ b/libsnes/snes/alt/smp/core/op_misc.cpp @@ -0,0 +1,346 @@ +case 0x00: { + op_io(); + break; +} + +case 0xef: { + op_io(); + op_io(); + regs.pc--; + break; +} + +case 0xff: { + op_io(); + op_io(); + regs.pc--; + break; +} + +case 0x9f: { + op_io(); + op_io(); + op_io(); + op_io(); + regs.a = (regs.a >> 4) | (regs.a << 4); + regs.p.n = !!(regs.a & 0x80); + regs.p.z = (regs.a == 0); + break; +} + +case 0xdf: { + op_io(); + op_io(); + if(regs.p.c || (regs.a) > 0x99) { + regs.a += 0x60; + regs.p.c = 1; + } + if(regs.p.h || (regs.a & 15) > 0x09) { + regs.a += 0x06; + } + regs.p.n = !!(regs.a & 0x80); + regs.p.z = (regs.a == 0); + break; +} + +case 0xbe: { + op_io(); + op_io(); + if(!regs.p.c || (regs.a) > 0x99) { + regs.a -= 0x60; + regs.p.c = 0; + } + if(!regs.p.h || (regs.a & 15) > 0x09) { + regs.a -= 0x06; + } + regs.p.n = !!(regs.a & 0x80); + regs.p.z = (regs.a == 0); + break; +} + +case 0x60: { + op_io(); + regs.p.c = 0; + break; +} + +case 0x20: { + op_io(); + regs.p.p = 0; + break; +} + +case 0x80: { + op_io(); + regs.p.c = 1; + break; +} + +case 0x40: { + op_io(); + regs.p.p = 1; + break; +} + +case 0xe0: { + op_io(); + regs.p.v = 0; + regs.p.h = 0; + break; +} + +case 0xed: { + op_io(); + op_io(); + regs.p.c = !regs.p.c; + break; +} + +case 0xa0: { + op_io(); + op_io(); + regs.p.i = 1; + break; +} + +case 0xc0: { + op_io(); + op_io(); + regs.p.i = 0; + break; +} + +case 0x02: { + dp = op_readpc(); + rd = op_readdp(dp); + rd |= 0x01; + op_writedp(dp, rd); + break; +} + +case 0x12: { + dp = op_readpc(); + rd = op_readdp(dp); + rd &= ~0x01; + op_writedp(dp, rd); + break; +} + +case 0x22: { + dp = op_readpc(); + rd = op_readdp(dp); + rd |= 0x02; + op_writedp(dp, rd); + break; +} + +case 0x32: { + dp = op_readpc(); + rd = op_readdp(dp); + rd &= ~0x02; + op_writedp(dp, rd); + break; +} + +case 0x42: { + dp = op_readpc(); + rd = op_readdp(dp); + rd |= 0x04; + op_writedp(dp, rd); + break; +} + +case 0x52: { + dp = op_readpc(); + rd = op_readdp(dp); + rd &= ~0x04; + op_writedp(dp, rd); + break; +} + +case 0x62: { + dp = op_readpc(); + rd = op_readdp(dp); + rd |= 0x08; + op_writedp(dp, rd); + break; +} + +case 0x72: { + dp = op_readpc(); + rd = op_readdp(dp); + rd &= ~0x08; + op_writedp(dp, rd); + break; +} + +case 0x82: { + dp = op_readpc(); + rd = op_readdp(dp); + rd |= 0x10; + op_writedp(dp, rd); + break; +} + +case 0x92: { + dp = op_readpc(); + rd = op_readdp(dp); + rd &= ~0x10; + op_writedp(dp, rd); + break; +} + +case 0xa2: { + dp = op_readpc(); + rd = op_readdp(dp); + rd |= 0x20; + op_writedp(dp, rd); + break; +} + +case 0xb2: { + dp = op_readpc(); + rd = op_readdp(dp); + rd &= ~0x20; + op_writedp(dp, rd); + break; +} + +case 0xc2: { + dp = op_readpc(); + rd = op_readdp(dp); + rd |= 0x40; + op_writedp(dp, rd); + break; +} + +case 0xd2: { + dp = op_readpc(); + rd = op_readdp(dp); + rd &= ~0x40; + op_writedp(dp, rd); + break; +} + +case 0xe2: { + dp = op_readpc(); + rd = op_readdp(dp); + rd |= 0x80; + op_writedp(dp, rd); + break; +} + +case 0xf2: { + dp = op_readpc(); + rd = op_readdp(dp); + rd &= ~0x80; + op_writedp(dp, rd); + break; +} + +case 0x2d: { + op_io(); + op_io(); + op_writestack(regs.a); + break; +} + +case 0x4d: { + op_io(); + op_io(); + op_writestack(regs.x); + break; +} + +case 0x6d: { + op_io(); + op_io(); + op_writestack(regs.y); + break; +} + +case 0x0d: { + op_io(); + op_io(); + op_writestack(regs.p); + break; +} + +case 0xae: { + op_io(); + op_io(); + regs.a = op_readstack(); + break; +} + +case 0xce: { + op_io(); + op_io(); + regs.x = op_readstack(); + break; +} + +case 0xee: { + op_io(); + op_io(); + regs.y = op_readstack(); + break; +} + +case 0x8e: { + op_io(); + op_io(); + regs.p = op_readstack(); + break; +} + +case 0xcf: { + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + ya = regs.y * regs.a; + regs.a = ya; + regs.y = ya >> 8; + //result is set based on y (high-byte) only + regs.p.n = !!(regs.y & 0x80); + regs.p.z = (regs.y == 0); + break; +} + +case 0x9e: { + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + ya = regs.ya; + //overflow set if quotient >= 256 + regs.p.v = !!(regs.y >= regs.x); + regs.p.h = !!((regs.y & 15) >= (regs.x & 15)); + if(regs.y < (regs.x << 1)) { + //if quotient is <= 511 (will fit into 9-bit result) + regs.a = ya / regs.x; + regs.y = ya % regs.x; + } else { + //otherwise, the quotient won't fit into regs.p.v + regs.a + //this emulates the odd behavior of the S-SMP in this case + regs.a = 255 - (ya - (regs.x << 9)) / (256 - regs.x); + regs.y = regs.x + (ya - (regs.x << 9)) % (256 - regs.x); + } + //result is set based on a (quotient) only + regs.p.n = !!(regs.a & 0x80); + regs.p.z = (regs.a == 0); + break; +} + diff --git a/libsnes/snes/alt/smp/core/op_mov.b b/libsnes/snes/alt/smp/core/op_mov.b new file mode 100644 index 0000000..dee821a --- /dev/null +++ b/libsnes/snes/alt/smp/core/op_mov.b @@ -0,0 +1,217 @@ +mov_a_x(0x7d, a, x), +mov_a_y(0xdd, a, y), +mov_x_a(0x5d, x, a), +mov_y_a(0xfd, y, a), +mov_x_sp(0x9d, x, sp) { +1:op_io(); + regs.$1 = regs.$2; + regs.p.n = !!(regs.$1 & 0x80); + regs.p.z = (regs.$1 == 0); +} + +mov_sp_x(0xbd, sp, x) { +1:op_io(); + regs.$1 = regs.$2; +} + +mov_a_const(0xe8, a), +mov_x_const(0xcd, x), +mov_y_const(0x8d, y) { +1:regs.$1 = op_readpc(); + regs.p.n = !!(regs.$1 & 0x80); + regs.p.z = (regs.$1 == 0); +} + +mov_a_ix(0xe6) { +1:op_io(); +2:regs.a = op_readdp(regs.x); + regs.p.n = !!(regs.a & 0x80); + regs.p.z = (regs.a == 0); +} + +mov_a_ixinc(0xbf) { +1:op_io(); +2:regs.a = op_readdp(regs.x++); +3:op_io(); + regs.p.n = !!(regs.a & 0x80); + regs.p.z = (regs.a == 0); +} + +mov_a_dp(0xe4, a), +mov_x_dp(0xf8, x), +mov_y_dp(0xeb, y) { +1:sp = op_readpc(); +2:regs.$1 = op_readdp(sp); + regs.p.n = !!(regs.$1 & 0x80); + regs.p.z = (regs.$1 == 0); +} + +mov_a_dpx(0xf4, a, x), +mov_x_dpy(0xf9, x, y), +mov_y_dpx(0xfb, y, x) { +1:sp = op_readpc(); +2:op_io(); +3:regs.$1 = op_readdp(sp + regs.$2); + regs.p.n = !!(regs.$1 & 0x80); + regs.p.z = (regs.$1 == 0); +} + +mov_a_addr(0xe5, a), +mov_x_addr(0xe9, x), +mov_y_addr(0xec, y) { +1:sp = op_readpc(); +2:sp |= op_readpc() << 8; +3:regs.$1 = op_readaddr(sp); + regs.p.n = !!(regs.$1 & 0x80); + regs.p.z = (regs.$1 == 0); +} + +mov_a_addrx(0xf5, x), +mov_a_addry(0xf6, y) { +1:sp = op_readpc(); +2:sp |= op_readpc() << 8; +3:op_io(); +4:regs.a = op_readaddr(sp + regs.$1); + regs.p.n = !!(regs.a & 0x80); + regs.p.z = (regs.a == 0); +} + +mov_a_idpx(0xe7) { +1:dp = op_readpc() + regs.x; +2:op_io(); +3:sp = op_readdp(dp); +4:sp |= op_readdp(dp + 1) << 8; +5:regs.a = op_readaddr(sp); + regs.p.n = !!(regs.a & 0x80); + regs.p.z = (regs.a == 0); +} + +mov_a_idpy(0xf7) { +1:dp = op_readpc(); +2:op_io(); +3:sp = op_readdp(dp); +4:sp |= op_readdp(dp + 1) << 8; +5:regs.a = op_readaddr(sp + regs.y); + regs.p.n = !!(regs.a & 0x80); + regs.p.z = (regs.a == 0); +} + +mov_dp_dp(0xfa) { +1:sp = op_readpc(); +2:rd = op_readdp(sp); +3:dp = op_readpc(); +4:op_writedp(dp, rd); +} + +mov_dp_const(0x8f) { +1:rd = op_readpc(); +2:dp = op_readpc(); +3:op_readdp(dp); +4:op_writedp(dp, rd); +} + +mov_ix_a(0xc6) { +1:op_io(); +2:op_readdp(regs.x); +3:op_writedp(regs.x, regs.a); +} + +mov_ixinc_a(0xaf) { +1:op_io(); +2:op_io(); +3:op_writedp(regs.x++, regs.a); +} + +mov_dp_a(0xc4, a), +mov_dp_x(0xd8, x), +mov_dp_y(0xcb, y) { +1:dp = op_readpc(); +2:op_readdp(dp); +3:op_writedp(dp, regs.$1); +} + +mov_dpx_a(0xd4, x, a), +mov_dpy_x(0xd9, y, x), +mov_dpx_y(0xdb, x, y) { +1:dp = op_readpc(); +2:op_io(); + dp += regs.$1; +3:op_readdp(dp); +4:op_writedp(dp, regs.$2); +} + +mov_addr_a(0xc5, a), +mov_addr_x(0xc9, x), +mov_addr_y(0xcc, y) { +1:dp = op_readpc(); +2:dp |= op_readpc() << 8; +3:op_readaddr(dp); +4:op_writeaddr(dp, regs.$1); +} + +mov_addrx_a(0xd5, x), +mov_addry_a(0xd6, y) { +1:dp = op_readpc(); +2:dp |= op_readpc() << 8; +3:op_io(); + dp += regs.$1; +4:op_readaddr(dp); +5:op_writeaddr(dp, regs.a); +} + +mov_idpx_a(0xc7) { +1:sp = op_readpc(); +2:op_io(); + sp += regs.x; +3:dp = op_readdp(sp); +4:dp |= op_readdp(sp + 1) << 8; +5:op_readaddr(dp); +6:op_writeaddr(dp, regs.a); +} + +mov_idpy_a(0xd7) { +1:sp = op_readpc(); +2:dp = op_readdp(sp); +3:dp |= op_readdp(sp + 1) << 8; +4:op_io(); + dp += regs.y; +5:op_readaddr(dp); +6:op_writeaddr(dp, regs.a); +} + +movw_ya_dp(0xba) { +1:sp = op_readpc(); +2:regs.a = op_readdp(sp); +3:op_io(); +4:regs.y = op_readdp(sp + 1); + regs.p.n = !!(regs.ya & 0x8000); + regs.p.z = (regs.ya == 0); +} + +movw_dp_ya(0xda) { +1:dp = op_readpc(); +2:op_readdp(dp); +3:op_writedp(dp, regs.a); +4:op_writedp(dp + 1, regs.y); +} + +mov1_c_bit(0xaa) { +1:sp = op_readpc(); +2:sp |= op_readpc() << 8; +3:bit = sp >> 13; + sp &= 0x1fff; + rd = op_readaddr(sp); + regs.p.c = !!(rd & (1 << bit)); +} + +mov1_bit_c(0xca) { +1:dp = op_readpc(); +2:dp |= op_readpc() << 8; +3:bit = dp >> 13; + dp &= 0x1fff; + rd = op_readaddr(dp); + if(regs.p.c)rd |= (1 << bit); + else rd &= ~(1 << bit); +4:op_io(); +5:op_writeaddr(dp, rd); +} diff --git a/libsnes/snes/alt/smp/core/op_mov.cpp b/libsnes/snes/alt/smp/core/op_mov.cpp new file mode 100644 index 0000000..f307942 --- /dev/null +++ b/libsnes/snes/alt/smp/core/op_mov.cpp @@ -0,0 +1,389 @@ +case 0x7d: { + op_io(); + regs.a = regs.x; + regs.p.n = !!(regs.a & 0x80); + regs.p.z = (regs.a == 0); + break; +} + +case 0xdd: { + op_io(); + regs.a = regs.y; + regs.p.n = !!(regs.a & 0x80); + regs.p.z = (regs.a == 0); + break; +} + +case 0x5d: { + op_io(); + regs.x = regs.a; + regs.p.n = !!(regs.x & 0x80); + regs.p.z = (regs.x == 0); + break; +} + +case 0xfd: { + op_io(); + regs.y = regs.a; + regs.p.n = !!(regs.y & 0x80); + regs.p.z = (regs.y == 0); + break; +} + +case 0x9d: { + op_io(); + regs.x = regs.sp; + regs.p.n = !!(regs.x & 0x80); + regs.p.z = (regs.x == 0); + break; +} + +case 0xbd: { + op_io(); + regs.sp = regs.x; + break; +} + +case 0xe8: { + regs.a = op_readpc(); + regs.p.n = !!(regs.a & 0x80); + regs.p.z = (regs.a == 0); + break; +} + +case 0xcd: { + regs.x = op_readpc(); + regs.p.n = !!(regs.x & 0x80); + regs.p.z = (regs.x == 0); + break; +} + +case 0x8d: { + regs.y = op_readpc(); + regs.p.n = !!(regs.y & 0x80); + regs.p.z = (regs.y == 0); + break; +} + +case 0xe6: { + op_io(); + regs.a = op_readdp(regs.x); + regs.p.n = !!(regs.a & 0x80); + regs.p.z = (regs.a == 0); + break; +} + +case 0xbf: { + op_io(); + regs.a = op_readdp(regs.x++); + op_io(); + regs.p.n = !!(regs.a & 0x80); + regs.p.z = (regs.a == 0); + break; +} + +case 0xe4: { + sp = op_readpc(); + regs.a = op_readdp(sp); + regs.p.n = !!(regs.a & 0x80); + regs.p.z = (regs.a == 0); + break; +} + +case 0xf8: { + sp = op_readpc(); + regs.x = op_readdp(sp); + regs.p.n = !!(regs.x & 0x80); + regs.p.z = (regs.x == 0); + break; +} + +case 0xeb: { + sp = op_readpc(); + regs.y = op_readdp(sp); + regs.p.n = !!(regs.y & 0x80); + regs.p.z = (regs.y == 0); + break; +} + +case 0xf4: { + sp = op_readpc(); + op_io(); + regs.a = op_readdp(sp + regs.x); + regs.p.n = !!(regs.a & 0x80); + regs.p.z = (regs.a == 0); + break; +} + +case 0xf9: { + sp = op_readpc(); + op_io(); + regs.x = op_readdp(sp + regs.y); + regs.p.n = !!(regs.x & 0x80); + regs.p.z = (regs.x == 0); + break; +} + +case 0xfb: { + sp = op_readpc(); + op_io(); + regs.y = op_readdp(sp + regs.x); + regs.p.n = !!(regs.y & 0x80); + regs.p.z = (regs.y == 0); + break; +} + +case 0xe5: { + sp = op_readpc(); + sp |= op_readpc() << 8; + regs.a = op_readaddr(sp); + regs.p.n = !!(regs.a & 0x80); + regs.p.z = (regs.a == 0); + break; +} + +case 0xe9: { + sp = op_readpc(); + sp |= op_readpc() << 8; + regs.x = op_readaddr(sp); + regs.p.n = !!(regs.x & 0x80); + regs.p.z = (regs.x == 0); + break; +} + +case 0xec: { + sp = op_readpc(); + sp |= op_readpc() << 8; + regs.y = op_readaddr(sp); + regs.p.n = !!(regs.y & 0x80); + regs.p.z = (regs.y == 0); + break; +} + +case 0xf5: { + sp = op_readpc(); + sp |= op_readpc() << 8; + op_io(); + regs.a = op_readaddr(sp + regs.x); + regs.p.n = !!(regs.a & 0x80); + regs.p.z = (regs.a == 0); + break; +} + +case 0xf6: { + sp = op_readpc(); + sp |= op_readpc() << 8; + op_io(); + regs.a = op_readaddr(sp + regs.y); + regs.p.n = !!(regs.a & 0x80); + regs.p.z = (regs.a == 0); + break; +} + +case 0xe7: { + dp = op_readpc() + regs.x; + op_io(); + sp = op_readdp(dp); + sp |= op_readdp(dp + 1) << 8; + regs.a = op_readaddr(sp); + regs.p.n = !!(regs.a & 0x80); + regs.p.z = (regs.a == 0); + break; +} + +case 0xf7: { + dp = op_readpc(); + op_io(); + sp = op_readdp(dp); + sp |= op_readdp(dp + 1) << 8; + regs.a = op_readaddr(sp + regs.y); + regs.p.n = !!(regs.a & 0x80); + regs.p.z = (regs.a == 0); + break; +} + +case 0xfa: { + sp = op_readpc(); + rd = op_readdp(sp); + dp = op_readpc(); + op_writedp(dp, rd); + break; +} + +case 0x8f: { + rd = op_readpc(); + dp = op_readpc(); + op_readdp(dp); + op_writedp(dp, rd); + break; +} + +case 0xc6: { + op_io(); + op_readdp(regs.x); + op_writedp(regs.x, regs.a); + break; +} + +case 0xaf: { + op_io(); + op_io(); + op_writedp(regs.x++, regs.a); + break; +} + +case 0xc4: { + dp = op_readpc(); + op_readdp(dp); + op_writedp(dp, regs.a); + break; +} + +case 0xd8: { + dp = op_readpc(); + op_readdp(dp); + op_writedp(dp, regs.x); + break; +} + +case 0xcb: { + dp = op_readpc(); + op_readdp(dp); + op_writedp(dp, regs.y); + break; +} + +case 0xd4: { + dp = op_readpc(); + op_io(); + dp += regs.x; + op_readdp(dp); + op_writedp(dp, regs.a); + break; +} + +case 0xd9: { + dp = op_readpc(); + op_io(); + dp += regs.y; + op_readdp(dp); + op_writedp(dp, regs.x); + break; +} + +case 0xdb: { + dp = op_readpc(); + op_io(); + dp += regs.x; + op_readdp(dp); + op_writedp(dp, regs.y); + break; +} + +case 0xc5: { + dp = op_readpc(); + dp |= op_readpc() << 8; + op_readaddr(dp); + op_writeaddr(dp, regs.a); + break; +} + +case 0xc9: { + dp = op_readpc(); + dp |= op_readpc() << 8; + op_readaddr(dp); + op_writeaddr(dp, regs.x); + break; +} + +case 0xcc: { + dp = op_readpc(); + dp |= op_readpc() << 8; + op_readaddr(dp); + op_writeaddr(dp, regs.y); + break; +} + +case 0xd5: { + dp = op_readpc(); + dp |= op_readpc() << 8; + op_io(); + dp += regs.x; + op_readaddr(dp); + op_writeaddr(dp, regs.a); + break; +} + +case 0xd6: { + dp = op_readpc(); + dp |= op_readpc() << 8; + op_io(); + dp += regs.y; + op_readaddr(dp); + op_writeaddr(dp, regs.a); + break; +} + +case 0xc7: { + sp = op_readpc(); + op_io(); + sp += regs.x; + dp = op_readdp(sp); + dp |= op_readdp(sp + 1) << 8; + op_readaddr(dp); + op_writeaddr(dp, regs.a); + break; +} + +case 0xd7: { + sp = op_readpc(); + dp = op_readdp(sp); + dp |= op_readdp(sp + 1) << 8; + op_io(); + dp += regs.y; + op_readaddr(dp); + op_writeaddr(dp, regs.a); + break; +} + +case 0xba: { + sp = op_readpc(); + regs.a = op_readdp(sp); + op_io(); + regs.y = op_readdp(sp + 1); + regs.p.n = !!(regs.ya & 0x8000); + regs.p.z = (regs.ya == 0); + break; +} + +case 0xda: { + dp = op_readpc(); + op_readdp(dp); + op_writedp(dp, regs.a); + op_writedp(dp + 1, regs.y); + break; +} + +case 0xaa: { + sp = op_readpc(); + sp |= op_readpc() << 8; + bit = sp >> 13; + sp &= 0x1fff; + rd = op_readaddr(sp); + regs.p.c = !!(rd & (1 << bit)); + break; +} + +case 0xca: { + dp = op_readpc(); + dp |= op_readpc() << 8; + bit = dp >> 13; + dp &= 0x1fff; + rd = op_readaddr(dp); + if(regs.p.c)rd |= (1 << bit); + else rd &= ~(1 << bit); + op_io(); + op_writeaddr(dp, rd); + break; +} + diff --git a/libsnes/snes/alt/smp/core/op_pc.b b/libsnes/snes/alt/smp/core/op_pc.b new file mode 100644 index 0000000..affaf84 --- /dev/null +++ b/libsnes/snes/alt/smp/core/op_pc.b @@ -0,0 +1,179 @@ +bra(0x2f, 0), +beq(0xf0, !regs.p.z), +bne(0xd0, regs.p.z), +bcs(0xb0, !regs.p.c), +bcc(0x90, regs.p.c), +bvs(0x70, !regs.p.v), +bvc(0x50, regs.p.v), +bmi(0x30, !regs.p.n), +bpl(0x10, regs.p.n) { +1:rd = op_readpc(); + if($1)end; +2:op_io(); +3:op_io(); + regs.pc += (int8)rd; +} + +bbs0(0x03, 0x01, !=), +bbc0(0x13, 0x01, ==), +bbs1(0x23, 0x02, !=), +bbc1(0x33, 0x02, ==), +bbs2(0x43, 0x04, !=), +bbc2(0x53, 0x04, ==), +bbs3(0x63, 0x08, !=), +bbc3(0x73, 0x08, ==), +bbs4(0x83, 0x10, !=), +bbc4(0x93, 0x10, ==), +bbs5(0xa3, 0x20, !=), +bbc5(0xb3, 0x20, ==), +bbs6(0xc3, 0x40, !=), +bbc6(0xd3, 0x40, ==), +bbs7(0xe3, 0x80, !=), +bbc7(0xf3, 0x80, ==) { +1:dp = op_readpc(); +2:sp = op_readdp(dp); +3:rd = op_readpc(); +4:op_io(); + if((sp & $1) $2 $1)end; +5:op_io(); +6:op_io(); + regs.pc += (int8)rd; +} + +cbne_dp(0x2e) { +1:dp = op_readpc(); +2:sp = op_readdp(dp); +3:rd = op_readpc(); +4:op_io(); + if(regs.a == sp)end; +5:op_io(); +6:op_io(); + regs.pc += (int8)rd; +} + +cbne_dpx(0xde) { +1:dp = op_readpc(); +2:op_io(); +3:sp = op_readdp(dp + regs.x); +4:rd = op_readpc(); +5:op_io(); + if(regs.a == sp)end; +6:op_io(); +7:op_io(); + regs.pc += (int8)rd; +} + +dbnz_dp(0x6e) { +1:dp = op_readpc(); +2:wr = op_readdp(dp); +3:op_writedp(dp, --wr); +4:rd = op_readpc(); + if(wr == 0x00)end; +5:op_io(); +6:op_io(); + regs.pc += (int8)rd; +} + +dbnz_y(0xfe) { +1:rd = op_readpc(); +2:op_io(); + regs.y--; +3:op_io(); + if(regs.y == 0x00)end; +4:op_io(); +5:op_io(); + regs.pc += (int8)rd; +} + +jmp_addr(0x5f) { +1:rd = op_readpc(); +2:rd |= op_readpc() << 8; + regs.pc = rd; +} + +jmp_iaddrx(0x1f) { +1:dp = op_readpc(); +2:dp |= op_readpc() << 8; +3:op_io(); + dp += regs.x; +4:rd = op_readaddr(dp); +5:rd |= op_readaddr(dp + 1) << 8; + regs.pc = rd; +} + +call(0x3f) { +1:rd = op_readpc(); +2:rd |= op_readpc() << 8; +3:op_io(); +4:op_io(); +5:op_io(); +6:op_writestack(regs.pc >> 8); +7:op_writestack(regs.pc); + regs.pc = rd; +} + +pcall(0x4f) { +1:rd = op_readpc(); +2:op_io(); +3:op_io(); +4:op_writestack(regs.pc >> 8); +5:op_writestack(regs.pc); + regs.pc = 0xff00 | rd; +} + +tcall_0(0x01, 0), +tcall_1(0x11, 1), +tcall_2(0x21, 2), +tcall_3(0x31, 3), +tcall_4(0x41, 4), +tcall_5(0x51, 5), +tcall_6(0x61, 6), +tcall_7(0x71, 7), +tcall_8(0x81, 8), +tcall_9(0x91, 9), +tcall_10(0xa1, 10), +tcall_11(0xb1, 11), +tcall_12(0xc1, 12), +tcall_13(0xd1, 13), +tcall_14(0xe1, 14), +tcall_15(0xf1, 15) { +1:dp = 0xffde - ($1 << 1); + rd = op_readaddr(dp); +2:rd |= op_readaddr(dp + 1) << 8; +3:op_io(); +4:op_io(); +5:op_io(); +6:op_writestack(regs.pc >> 8); +7:op_writestack(regs.pc); + regs.pc = rd; +} + +brk(0x0f) { +1:rd = op_readaddr(0xffde); +2:rd |= op_readaddr(0xffdf) << 8; +3:op_io(); +4:op_io(); +5:op_writestack(regs.pc >> 8); +6:op_writestack(regs.pc); +7:op_writestack(regs.p); + regs.pc = rd; + regs.p.b = 1; + regs.p.i = 0; +} + +ret(0x6f) { +1:rd = op_readstack(); +2:rd |= op_readstack() << 8; +3:op_io(); +4:op_io(); + regs.pc = rd; +} + +reti(0x7f) { +1:regs.p = op_readstack(); +2:rd = op_readstack(); +3:rd |= op_readstack() << 8; +4:op_io(); +5:op_io(); + regs.pc = rd; +} diff --git a/libsnes/snes/alt/smp/core/op_pc.cpp b/libsnes/snes/alt/smp/core/op_pc.cpp new file mode 100644 index 0000000..763a033 --- /dev/null +++ b/libsnes/snes/alt/smp/core/op_pc.cpp @@ -0,0 +1,603 @@ +case 0x2f: { + rd = op_readpc(); + if(0)break; + op_io(); + op_io(); + regs.pc += (int8)rd; + break; +} + +case 0xf0: { + rd = op_readpc(); + if(!regs.p.z)break; + op_io(); + op_io(); + regs.pc += (int8)rd; + break; +} + +case 0xd0: { + rd = op_readpc(); + if(regs.p.z)break; + op_io(); + op_io(); + regs.pc += (int8)rd; + break; +} + +case 0xb0: { + rd = op_readpc(); + if(!regs.p.c)break; + op_io(); + op_io(); + regs.pc += (int8)rd; + break; +} + +case 0x90: { + rd = op_readpc(); + if(regs.p.c)break; + op_io(); + op_io(); + regs.pc += (int8)rd; + break; +} + +case 0x70: { + rd = op_readpc(); + if(!regs.p.v)break; + op_io(); + op_io(); + regs.pc += (int8)rd; + break; +} + +case 0x50: { + rd = op_readpc(); + if(regs.p.v)break; + op_io(); + op_io(); + regs.pc += (int8)rd; + break; +} + +case 0x30: { + rd = op_readpc(); + if(!regs.p.n)break; + op_io(); + op_io(); + regs.pc += (int8)rd; + break; +} + +case 0x10: { + rd = op_readpc(); + if(regs.p.n)break; + op_io(); + op_io(); + regs.pc += (int8)rd; + break; +} + +case 0x03: { + dp = op_readpc(); + sp = op_readdp(dp); + rd = op_readpc(); + op_io(); + if((sp & 0x01) != 0x01)break; + op_io(); + op_io(); + regs.pc += (int8)rd; + break; +} + +case 0x13: { + dp = op_readpc(); + sp = op_readdp(dp); + rd = op_readpc(); + op_io(); + if((sp & 0x01) == 0x01)break; + op_io(); + op_io(); + regs.pc += (int8)rd; + break; +} + +case 0x23: { + dp = op_readpc(); + sp = op_readdp(dp); + rd = op_readpc(); + op_io(); + if((sp & 0x02) != 0x02)break; + op_io(); + op_io(); + regs.pc += (int8)rd; + break; +} + +case 0x33: { + dp = op_readpc(); + sp = op_readdp(dp); + rd = op_readpc(); + op_io(); + if((sp & 0x02) == 0x02)break; + op_io(); + op_io(); + regs.pc += (int8)rd; + break; +} + +case 0x43: { + dp = op_readpc(); + sp = op_readdp(dp); + rd = op_readpc(); + op_io(); + if((sp & 0x04) != 0x04)break; + op_io(); + op_io(); + regs.pc += (int8)rd; + break; +} + +case 0x53: { + dp = op_readpc(); + sp = op_readdp(dp); + rd = op_readpc(); + op_io(); + if((sp & 0x04) == 0x04)break; + op_io(); + op_io(); + regs.pc += (int8)rd; + break; +} + +case 0x63: { + dp = op_readpc(); + sp = op_readdp(dp); + rd = op_readpc(); + op_io(); + if((sp & 0x08) != 0x08)break; + op_io(); + op_io(); + regs.pc += (int8)rd; + break; +} + +case 0x73: { + dp = op_readpc(); + sp = op_readdp(dp); + rd = op_readpc(); + op_io(); + if((sp & 0x08) == 0x08)break; + op_io(); + op_io(); + regs.pc += (int8)rd; + break; +} + +case 0x83: { + dp = op_readpc(); + sp = op_readdp(dp); + rd = op_readpc(); + op_io(); + if((sp & 0x10) != 0x10)break; + op_io(); + op_io(); + regs.pc += (int8)rd; + break; +} + +case 0x93: { + dp = op_readpc(); + sp = op_readdp(dp); + rd = op_readpc(); + op_io(); + if((sp & 0x10) == 0x10)break; + op_io(); + op_io(); + regs.pc += (int8)rd; + break; +} + +case 0xa3: { + dp = op_readpc(); + sp = op_readdp(dp); + rd = op_readpc(); + op_io(); + if((sp & 0x20) != 0x20)break; + op_io(); + op_io(); + regs.pc += (int8)rd; + break; +} + +case 0xb3: { + dp = op_readpc(); + sp = op_readdp(dp); + rd = op_readpc(); + op_io(); + if((sp & 0x20) == 0x20)break; + op_io(); + op_io(); + regs.pc += (int8)rd; + break; +} + +case 0xc3: { + dp = op_readpc(); + sp = op_readdp(dp); + rd = op_readpc(); + op_io(); + if((sp & 0x40) != 0x40)break; + op_io(); + op_io(); + regs.pc += (int8)rd; + break; +} + +case 0xd3: { + dp = op_readpc(); + sp = op_readdp(dp); + rd = op_readpc(); + op_io(); + if((sp & 0x40) == 0x40)break; + op_io(); + op_io(); + regs.pc += (int8)rd; + break; +} + +case 0xe3: { + dp = op_readpc(); + sp = op_readdp(dp); + rd = op_readpc(); + op_io(); + if((sp & 0x80) != 0x80)break; + op_io(); + op_io(); + regs.pc += (int8)rd; + break; +} + +case 0xf3: { + dp = op_readpc(); + sp = op_readdp(dp); + rd = op_readpc(); + op_io(); + if((sp & 0x80) == 0x80)break; + op_io(); + op_io(); + regs.pc += (int8)rd; + break; +} + +case 0x2e: { + dp = op_readpc(); + sp = op_readdp(dp); + rd = op_readpc(); + op_io(); + if(regs.a == sp)break; + op_io(); + op_io(); + regs.pc += (int8)rd; + break; +} + +case 0xde: { + dp = op_readpc(); + op_io(); + sp = op_readdp(dp + regs.x); + rd = op_readpc(); + op_io(); + if(regs.a == sp)break; + op_io(); + op_io(); + regs.pc += (int8)rd; + break; +} + +case 0x6e: { + dp = op_readpc(); + wr = op_readdp(dp); + op_writedp(dp, --wr); + rd = op_readpc(); + if(wr == 0x00)break; + op_io(); + op_io(); + regs.pc += (int8)rd; + break; +} + +case 0xfe: { + rd = op_readpc(); + op_io(); + regs.y--; + op_io(); + if(regs.y == 0x00)break; + op_io(); + op_io(); + regs.pc += (int8)rd; + break; +} + +case 0x5f: { + rd = op_readpc(); + rd |= op_readpc() << 8; + regs.pc = rd; + break; +} + +case 0x1f: { + dp = op_readpc(); + dp |= op_readpc() << 8; + op_io(); + dp += regs.x; + rd = op_readaddr(dp); + rd |= op_readaddr(dp + 1) << 8; + regs.pc = rd; + break; +} + +case 0x3f: { + rd = op_readpc(); + rd |= op_readpc() << 8; + op_io(); + op_io(); + op_io(); + op_writestack(regs.pc >> 8); + op_writestack(regs.pc); + regs.pc = rd; + break; +} + +case 0x4f: { + rd = op_readpc(); + op_io(); + op_io(); + op_writestack(regs.pc >> 8); + op_writestack(regs.pc); + regs.pc = 0xff00 | rd; + break; +} + +case 0x01: { + dp = 0xffde - (0 << 1); + rd = op_readaddr(dp); + rd |= op_readaddr(dp + 1) << 8; + op_io(); + op_io(); + op_io(); + op_writestack(regs.pc >> 8); + op_writestack(regs.pc); + regs.pc = rd; + break; +} + +case 0x11: { + dp = 0xffde - (1 << 1); + rd = op_readaddr(dp); + rd |= op_readaddr(dp + 1) << 8; + op_io(); + op_io(); + op_io(); + op_writestack(regs.pc >> 8); + op_writestack(regs.pc); + regs.pc = rd; + break; +} + +case 0x21: { + dp = 0xffde - (2 << 1); + rd = op_readaddr(dp); + rd |= op_readaddr(dp + 1) << 8; + op_io(); + op_io(); + op_io(); + op_writestack(regs.pc >> 8); + op_writestack(regs.pc); + regs.pc = rd; + break; +} + +case 0x31: { + dp = 0xffde - (3 << 1); + rd = op_readaddr(dp); + rd |= op_readaddr(dp + 1) << 8; + op_io(); + op_io(); + op_io(); + op_writestack(regs.pc >> 8); + op_writestack(regs.pc); + regs.pc = rd; + break; +} + +case 0x41: { + dp = 0xffde - (4 << 1); + rd = op_readaddr(dp); + rd |= op_readaddr(dp + 1) << 8; + op_io(); + op_io(); + op_io(); + op_writestack(regs.pc >> 8); + op_writestack(regs.pc); + regs.pc = rd; + break; +} + +case 0x51: { + dp = 0xffde - (5 << 1); + rd = op_readaddr(dp); + rd |= op_readaddr(dp + 1) << 8; + op_io(); + op_io(); + op_io(); + op_writestack(regs.pc >> 8); + op_writestack(regs.pc); + regs.pc = rd; + break; +} + +case 0x61: { + dp = 0xffde - (6 << 1); + rd = op_readaddr(dp); + rd |= op_readaddr(dp + 1) << 8; + op_io(); + op_io(); + op_io(); + op_writestack(regs.pc >> 8); + op_writestack(regs.pc); + regs.pc = rd; + break; +} + +case 0x71: { + dp = 0xffde - (7 << 1); + rd = op_readaddr(dp); + rd |= op_readaddr(dp + 1) << 8; + op_io(); + op_io(); + op_io(); + op_writestack(regs.pc >> 8); + op_writestack(regs.pc); + regs.pc = rd; + break; +} + +case 0x81: { + dp = 0xffde - (8 << 1); + rd = op_readaddr(dp); + rd |= op_readaddr(dp + 1) << 8; + op_io(); + op_io(); + op_io(); + op_writestack(regs.pc >> 8); + op_writestack(regs.pc); + regs.pc = rd; + break; +} + +case 0x91: { + dp = 0xffde - (9 << 1); + rd = op_readaddr(dp); + rd |= op_readaddr(dp + 1) << 8; + op_io(); + op_io(); + op_io(); + op_writestack(regs.pc >> 8); + op_writestack(regs.pc); + regs.pc = rd; + break; +} + +case 0xa1: { + dp = 0xffde - (10 << 1); + rd = op_readaddr(dp); + rd |= op_readaddr(dp + 1) << 8; + op_io(); + op_io(); + op_io(); + op_writestack(regs.pc >> 8); + op_writestack(regs.pc); + regs.pc = rd; + break; +} + +case 0xb1: { + dp = 0xffde - (11 << 1); + rd = op_readaddr(dp); + rd |= op_readaddr(dp + 1) << 8; + op_io(); + op_io(); + op_io(); + op_writestack(regs.pc >> 8); + op_writestack(regs.pc); + regs.pc = rd; + break; +} + +case 0xc1: { + dp = 0xffde - (12 << 1); + rd = op_readaddr(dp); + rd |= op_readaddr(dp + 1) << 8; + op_io(); + op_io(); + op_io(); + op_writestack(regs.pc >> 8); + op_writestack(regs.pc); + regs.pc = rd; + break; +} + +case 0xd1: { + dp = 0xffde - (13 << 1); + rd = op_readaddr(dp); + rd |= op_readaddr(dp + 1) << 8; + op_io(); + op_io(); + op_io(); + op_writestack(regs.pc >> 8); + op_writestack(regs.pc); + regs.pc = rd; + break; +} + +case 0xe1: { + dp = 0xffde - (14 << 1); + rd = op_readaddr(dp); + rd |= op_readaddr(dp + 1) << 8; + op_io(); + op_io(); + op_io(); + op_writestack(regs.pc >> 8); + op_writestack(regs.pc); + regs.pc = rd; + break; +} + +case 0xf1: { + dp = 0xffde - (15 << 1); + rd = op_readaddr(dp); + rd |= op_readaddr(dp + 1) << 8; + op_io(); + op_io(); + op_io(); + op_writestack(regs.pc >> 8); + op_writestack(regs.pc); + regs.pc = rd; + break; +} + +case 0x0f: { + rd = op_readaddr(0xffde); + rd |= op_readaddr(0xffdf) << 8; + op_io(); + op_io(); + op_writestack(regs.pc >> 8); + op_writestack(regs.pc); + op_writestack(regs.p); + regs.pc = rd; + regs.p.b = 1; + regs.p.i = 0; + break; +} + +case 0x6f: { + rd = op_readstack(); + rd |= op_readstack() << 8; + op_io(); + op_io(); + regs.pc = rd; + break; +} + +case 0x7f: { + regs.p = op_readstack(); + rd = op_readstack(); + rd |= op_readstack() << 8; + op_io(); + op_io(); + regs.pc = rd; + break; +} + diff --git a/libsnes/snes/alt/smp/core/op_read.b b/libsnes/snes/alt/smp/core/op_read.b new file mode 100644 index 0000000..fd2f9d8 --- /dev/null +++ b/libsnes/snes/alt/smp/core/op_read.b @@ -0,0 +1,205 @@ +adc_a_const(0x88, adc, a), +and_a_const(0x28, and, a), +cmp_a_const(0x68, cmp, a), +cmp_x_const(0xc8, cmp, x), +cmp_y_const(0xad, cmp, y), +eor_a_const(0x48, eor, a), +or_a_const(0x08, or, a), +sbc_a_const(0xa8, sbc, a) { +1:rd = op_readpc(); + regs.$2 = op_$1(regs.$2, rd); +} + +adc_a_ix(0x86, adc), +and_a_ix(0x26, and), +cmp_a_ix(0x66, cmp), +eor_a_ix(0x46, eor), +or_a_ix(0x06, or), +sbc_a_ix(0xa6, sbc) { +1:op_io(); +2:rd = op_readdp(regs.x); + regs.a = op_$1(regs.a, rd); +} + +adc_a_dp(0x84, adc, a), +and_a_dp(0x24, and, a), +cmp_a_dp(0x64, cmp, a), +cmp_x_dp(0x3e, cmp, x), +cmp_y_dp(0x7e, cmp, y), +eor_a_dp(0x44, eor, a), +or_a_dp(0x04, or, a), +sbc_a_dp(0xa4, sbc, a) { +1:dp = op_readpc(); +2:rd = op_readdp(dp); + regs.$2 = op_$1(regs.$2, rd); +} + +adc_a_dpx(0x94, adc), +and_a_dpx(0x34, and), +cmp_a_dpx(0x74, cmp), +eor_a_dpx(0x54, eor), +or_a_dpx(0x14, or), +sbc_a_dpx(0xb4, sbc) { +1:dp = op_readpc(); +2:op_io(); +3:rd = op_readdp(dp + regs.x); + regs.a = op_$1(regs.a, rd); +} + +adc_a_addr(0x85, adc, a), +and_a_addr(0x25, and, a), +cmp_a_addr(0x65, cmp, a), +cmp_x_addr(0x1e, cmp, x), +cmp_y_addr(0x5e, cmp, y), +eor_a_addr(0x45, eor, a), +or_a_addr(0x05, or, a), +sbc_a_addr(0xa5, sbc, a) { +1:dp = op_readpc(); +2:dp |= op_readpc() << 8; +3:rd = op_readaddr(dp); + regs.$2 = op_$1(regs.$2, rd); +} + +adc_a_addrx(0x95, adc, x), +adc_a_addry(0x96, adc, y), +and_a_addrx(0x35, and, x), +and_a_addry(0x36, and, y), +cmp_a_addrx(0x75, cmp, x), +cmp_a_addry(0x76, cmp, y), +eor_a_addrx(0x55, eor, x), +eor_a_addry(0x56, eor, y), +or_a_addrx(0x15, or, x), +or_a_addry(0x16, or, y), +sbc_a_addrx(0xb5, sbc, x), +sbc_a_addry(0xb6, sbc, y) { +1:dp = op_readpc(); +2:dp |= op_readpc() << 8; +3:op_io(); +4:rd = op_readaddr(dp + regs.$2); + regs.a = op_$1(regs.a, rd); +} + +adc_a_idpx(0x87, adc), +and_a_idpx(0x27, and), +cmp_a_idpx(0x67, cmp), +eor_a_idpx(0x47, eor), +or_a_idpx(0x07, or), +sbc_a_idpx(0xa7, sbc) { +1:dp = op_readpc() + regs.x; +2:op_io(); +3:sp = op_readdp(dp); +4:sp |= op_readdp(dp + 1) << 8; +5:rd = op_readaddr(sp); + regs.a = op_$1(regs.a, rd); +} + +adc_a_idpy(0x97, adc), +and_a_idpy(0x37, and), +cmp_a_idpy(0x77, cmp), +eor_a_idpy(0x57, eor), +or_a_idpy(0x17, or), +sbc_a_idpy(0xb7, sbc) { +1:dp = op_readpc(); +2:op_io(); +3:sp = op_readdp(dp); +4:sp |= op_readdp(dp + 1) << 8; +5:rd = op_readaddr(sp + regs.y); + regs.a = op_$1(regs.a, rd); +} + +adc_ix_iy(0x99, adc, 1), +and_ix_iy(0x39, and, 1), +cmp_ix_iy(0x79, cmp, 0), +eor_ix_iy(0x59, eor, 1), +or_ix_iy(0x19, or, 1), +sbc_ix_iy(0xb9, sbc, 1) { +1:op_io(); +2:rd = op_readdp(regs.y); +3:wr = op_readdp(regs.x); + wr = op_$1(wr, rd); +4:($2) ? op_writedp(regs.x, wr) : op_io(); +} + +adc_dp_dp(0x89, adc, 1), +and_dp_dp(0x29, and, 1), +cmp_dp_dp(0x69, cmp, 0), +eor_dp_dp(0x49, eor, 1), +or_dp_dp(0x09, or, 1), +sbc_dp_dp(0xa9, sbc, 1) { +1:sp = op_readpc(); +2:rd = op_readdp(sp); +3:dp = op_readpc(); +4:wr = op_readdp(dp); +5:wr = op_$1(wr, rd); + ($2) ? op_writedp(dp, wr) : op_io(); +} + +adc_dp_const(0x98, adc, 1), +and_dp_const(0x38, and, 1), +cmp_dp_const(0x78, cmp, 0), +eor_dp_const(0x58, eor, 1), +or_dp_const(0x18, or, 1), +sbc_dp_const(0xb8, sbc, 1) { +1:rd = op_readpc(); +2:dp = op_readpc(); +3:wr = op_readdp(dp); +4:wr = op_$1(wr, rd); + ($2) ? op_writedp(dp, wr) : op_io(); +} + +addw_ya_dp(0x7a, addw), +subw_ya_dp(0x9a, subw) { +1:dp = op_readpc(); +2:rd = op_readdp(dp); +3:op_io(); +4:rd |= op_readdp(dp + 1) << 8; + regs.ya = op_$1(regs.ya, rd); +} + +cmpw_ya_dp(0x5a) { +1:dp = op_readpc(); +2:rd = op_readdp(dp); +3:rd |= op_readdp(dp + 1) << 8; + op_cmpw(regs.ya, rd); +} + +and1_bit(0x4a, !!), +and1_notbit(0x6a, !) { +1:dp = op_readpc(); +2:dp |= op_readpc() << 8; +3:bit = dp >> 13; + dp &= 0x1fff; + rd = op_readaddr(dp); + regs.p.c = regs.p.c & $1(rd & (1 << bit)); +} + +eor1_bit(0x8a) { +1:dp = op_readpc(); +2:dp |= op_readpc() << 8; +3:bit = dp >> 13; + dp &= 0x1fff; + rd = op_readaddr(dp); +4:op_io(); + regs.p.c = regs.p.c ^ !!(rd & (1 << bit)); +} + +not1_bit(0xea) { +1:dp = op_readpc(); +2:dp |= op_readpc() << 8; +3:bit = dp >> 13; + dp &= 0x1fff; + rd = op_readaddr(dp); + rd ^= (1 << bit); +4:op_writeaddr(dp, rd); +} + +or1_bit(0x0a, !!), +or1_notbit(0x2a, !) { +1:dp = op_readpc(); +2:dp |= op_readpc() << 8; +3:bit = dp >> 13; + dp &= 0x1fff; + rd = op_readaddr(dp); +4:op_io(); + regs.p.c = regs.p.c | $1(rd & (1 << bit)); +} diff --git a/libsnes/snes/alt/smp/core/op_read.cpp b/libsnes/snes/alt/smp/core/op_read.cpp new file mode 100644 index 0000000..2a16a3c --- /dev/null +++ b/libsnes/snes/alt/smp/core/op_read.cpp @@ -0,0 +1,744 @@ +case 0x88: { + rd = op_readpc(); + regs.a = op_adc(regs.a, rd); + break; +} + +case 0x28: { + rd = op_readpc(); + regs.a = op_and(regs.a, rd); + break; +} + +case 0x68: { + rd = op_readpc(); + regs.a = op_cmp(regs.a, rd); + break; +} + +case 0xc8: { + rd = op_readpc(); + regs.x = op_cmp(regs.x, rd); + break; +} + +case 0xad: { + rd = op_readpc(); + regs.y = op_cmp(regs.y, rd); + break; +} + +case 0x48: { + rd = op_readpc(); + regs.a = op_eor(regs.a, rd); + break; +} + +case 0x08: { + rd = op_readpc(); + regs.a = op_or(regs.a, rd); + break; +} + +case 0xa8: { + rd = op_readpc(); + regs.a = op_sbc(regs.a, rd); + break; +} + +case 0x86: { + op_io(); + rd = op_readdp(regs.x); + regs.a = op_adc(regs.a, rd); + break; +} + +case 0x26: { + op_io(); + rd = op_readdp(regs.x); + regs.a = op_and(regs.a, rd); + break; +} + +case 0x66: { + op_io(); + rd = op_readdp(regs.x); + regs.a = op_cmp(regs.a, rd); + break; +} + +case 0x46: { + op_io(); + rd = op_readdp(regs.x); + regs.a = op_eor(regs.a, rd); + break; +} + +case 0x06: { + op_io(); + rd = op_readdp(regs.x); + regs.a = op_or(regs.a, rd); + break; +} + +case 0xa6: { + op_io(); + rd = op_readdp(regs.x); + regs.a = op_sbc(regs.a, rd); + break; +} + +case 0x84: { + dp = op_readpc(); + rd = op_readdp(dp); + regs.a = op_adc(regs.a, rd); + break; +} + +case 0x24: { + dp = op_readpc(); + rd = op_readdp(dp); + regs.a = op_and(regs.a, rd); + break; +} + +case 0x64: { + dp = op_readpc(); + rd = op_readdp(dp); + regs.a = op_cmp(regs.a, rd); + break; +} + +case 0x3e: { + dp = op_readpc(); + rd = op_readdp(dp); + regs.x = op_cmp(regs.x, rd); + break; +} + +case 0x7e: { + dp = op_readpc(); + rd = op_readdp(dp); + regs.y = op_cmp(regs.y, rd); + break; +} + +case 0x44: { + dp = op_readpc(); + rd = op_readdp(dp); + regs.a = op_eor(regs.a, rd); + break; +} + +case 0x04: { + dp = op_readpc(); + rd = op_readdp(dp); + regs.a = op_or(regs.a, rd); + break; +} + +case 0xa4: { + dp = op_readpc(); + rd = op_readdp(dp); + regs.a = op_sbc(regs.a, rd); + break; +} + +case 0x94: { + dp = op_readpc(); + op_io(); + rd = op_readdp(dp + regs.x); + regs.a = op_adc(regs.a, rd); + break; +} + +case 0x34: { + dp = op_readpc(); + op_io(); + rd = op_readdp(dp + regs.x); + regs.a = op_and(regs.a, rd); + break; +} + +case 0x74: { + dp = op_readpc(); + op_io(); + rd = op_readdp(dp + regs.x); + regs.a = op_cmp(regs.a, rd); + break; +} + +case 0x54: { + dp = op_readpc(); + op_io(); + rd = op_readdp(dp + regs.x); + regs.a = op_eor(regs.a, rd); + break; +} + +case 0x14: { + dp = op_readpc(); + op_io(); + rd = op_readdp(dp + regs.x); + regs.a = op_or(regs.a, rd); + break; +} + +case 0xb4: { + dp = op_readpc(); + op_io(); + rd = op_readdp(dp + regs.x); + regs.a = op_sbc(regs.a, rd); + break; +} + +case 0x85: { + dp = op_readpc(); + dp |= op_readpc() << 8; + rd = op_readaddr(dp); + regs.a = op_adc(regs.a, rd); + break; +} + +case 0x25: { + dp = op_readpc(); + dp |= op_readpc() << 8; + rd = op_readaddr(dp); + regs.a = op_and(regs.a, rd); + break; +} + +case 0x65: { + dp = op_readpc(); + dp |= op_readpc() << 8; + rd = op_readaddr(dp); + regs.a = op_cmp(regs.a, rd); + break; +} + +case 0x1e: { + dp = op_readpc(); + dp |= op_readpc() << 8; + rd = op_readaddr(dp); + regs.x = op_cmp(regs.x, rd); + break; +} + +case 0x5e: { + dp = op_readpc(); + dp |= op_readpc() << 8; + rd = op_readaddr(dp); + regs.y = op_cmp(regs.y, rd); + break; +} + +case 0x45: { + dp = op_readpc(); + dp |= op_readpc() << 8; + rd = op_readaddr(dp); + regs.a = op_eor(regs.a, rd); + break; +} + +case 0x05: { + dp = op_readpc(); + dp |= op_readpc() << 8; + rd = op_readaddr(dp); + regs.a = op_or(regs.a, rd); + break; +} + +case 0xa5: { + dp = op_readpc(); + dp |= op_readpc() << 8; + rd = op_readaddr(dp); + regs.a = op_sbc(regs.a, rd); + break; +} + +case 0x95: { + dp = op_readpc(); + dp |= op_readpc() << 8; + op_io(); + rd = op_readaddr(dp + regs.x); + regs.a = op_adc(regs.a, rd); + break; +} + +case 0x96: { + dp = op_readpc(); + dp |= op_readpc() << 8; + op_io(); + rd = op_readaddr(dp + regs.y); + regs.a = op_adc(regs.a, rd); + break; +} + +case 0x35: { + dp = op_readpc(); + dp |= op_readpc() << 8; + op_io(); + rd = op_readaddr(dp + regs.x); + regs.a = op_and(regs.a, rd); + break; +} + +case 0x36: { + dp = op_readpc(); + dp |= op_readpc() << 8; + op_io(); + rd = op_readaddr(dp + regs.y); + regs.a = op_and(regs.a, rd); + break; +} + +case 0x75: { + dp = op_readpc(); + dp |= op_readpc() << 8; + op_io(); + rd = op_readaddr(dp + regs.x); + regs.a = op_cmp(regs.a, rd); + break; +} + +case 0x76: { + dp = op_readpc(); + dp |= op_readpc() << 8; + op_io(); + rd = op_readaddr(dp + regs.y); + regs.a = op_cmp(regs.a, rd); + break; +} + +case 0x55: { + dp = op_readpc(); + dp |= op_readpc() << 8; + op_io(); + rd = op_readaddr(dp + regs.x); + regs.a = op_eor(regs.a, rd); + break; +} + +case 0x56: { + dp = op_readpc(); + dp |= op_readpc() << 8; + op_io(); + rd = op_readaddr(dp + regs.y); + regs.a = op_eor(regs.a, rd); + break; +} + +case 0x15: { + dp = op_readpc(); + dp |= op_readpc() << 8; + op_io(); + rd = op_readaddr(dp + regs.x); + regs.a = op_or(regs.a, rd); + break; +} + +case 0x16: { + dp = op_readpc(); + dp |= op_readpc() << 8; + op_io(); + rd = op_readaddr(dp + regs.y); + regs.a = op_or(regs.a, rd); + break; +} + +case 0xb5: { + dp = op_readpc(); + dp |= op_readpc() << 8; + op_io(); + rd = op_readaddr(dp + regs.x); + regs.a = op_sbc(regs.a, rd); + break; +} + +case 0xb6: { + dp = op_readpc(); + dp |= op_readpc() << 8; + op_io(); + rd = op_readaddr(dp + regs.y); + regs.a = op_sbc(regs.a, rd); + break; +} + +case 0x87: { + dp = op_readpc() + regs.x; + op_io(); + sp = op_readdp(dp); + sp |= op_readdp(dp + 1) << 8; + rd = op_readaddr(sp); + regs.a = op_adc(regs.a, rd); + break; +} + +case 0x27: { + dp = op_readpc() + regs.x; + op_io(); + sp = op_readdp(dp); + sp |= op_readdp(dp + 1) << 8; + rd = op_readaddr(sp); + regs.a = op_and(regs.a, rd); + break; +} + +case 0x67: { + dp = op_readpc() + regs.x; + op_io(); + sp = op_readdp(dp); + sp |= op_readdp(dp + 1) << 8; + rd = op_readaddr(sp); + regs.a = op_cmp(regs.a, rd); + break; +} + +case 0x47: { + dp = op_readpc() + regs.x; + op_io(); + sp = op_readdp(dp); + sp |= op_readdp(dp + 1) << 8; + rd = op_readaddr(sp); + regs.a = op_eor(regs.a, rd); + break; +} + +case 0x07: { + dp = op_readpc() + regs.x; + op_io(); + sp = op_readdp(dp); + sp |= op_readdp(dp + 1) << 8; + rd = op_readaddr(sp); + regs.a = op_or(regs.a, rd); + break; +} + +case 0xa7: { + dp = op_readpc() + regs.x; + op_io(); + sp = op_readdp(dp); + sp |= op_readdp(dp + 1) << 8; + rd = op_readaddr(sp); + regs.a = op_sbc(regs.a, rd); + break; +} + +case 0x97: { + dp = op_readpc(); + op_io(); + sp = op_readdp(dp); + sp |= op_readdp(dp + 1) << 8; + rd = op_readaddr(sp + regs.y); + regs.a = op_adc(regs.a, rd); + break; +} + +case 0x37: { + dp = op_readpc(); + op_io(); + sp = op_readdp(dp); + sp |= op_readdp(dp + 1) << 8; + rd = op_readaddr(sp + regs.y); + regs.a = op_and(regs.a, rd); + break; +} + +case 0x77: { + dp = op_readpc(); + op_io(); + sp = op_readdp(dp); + sp |= op_readdp(dp + 1) << 8; + rd = op_readaddr(sp + regs.y); + regs.a = op_cmp(regs.a, rd); + break; +} + +case 0x57: { + dp = op_readpc(); + op_io(); + sp = op_readdp(dp); + sp |= op_readdp(dp + 1) << 8; + rd = op_readaddr(sp + regs.y); + regs.a = op_eor(regs.a, rd); + break; +} + +case 0x17: { + dp = op_readpc(); + op_io(); + sp = op_readdp(dp); + sp |= op_readdp(dp + 1) << 8; + rd = op_readaddr(sp + regs.y); + regs.a = op_or(regs.a, rd); + break; +} + +case 0xb7: { + dp = op_readpc(); + op_io(); + sp = op_readdp(dp); + sp |= op_readdp(dp + 1) << 8; + rd = op_readaddr(sp + regs.y); + regs.a = op_sbc(regs.a, rd); + break; +} + +case 0x99: { + op_io(); + rd = op_readdp(regs.y); + wr = op_readdp(regs.x); + wr = op_adc(wr, rd); + (1) ? op_writedp(regs.x, wr) : op_io(); + break; +} + +case 0x39: { + op_io(); + rd = op_readdp(regs.y); + wr = op_readdp(regs.x); + wr = op_and(wr, rd); + (1) ? op_writedp(regs.x, wr) : op_io(); + break; +} + +case 0x79: { + op_io(); + rd = op_readdp(regs.y); + wr = op_readdp(regs.x); + wr = op_cmp(wr, rd); + (0) ? op_writedp(regs.x, wr) : op_io(); + break; +} + +case 0x59: { + op_io(); + rd = op_readdp(regs.y); + wr = op_readdp(regs.x); + wr = op_eor(wr, rd); + (1) ? op_writedp(regs.x, wr) : op_io(); + break; +} + +case 0x19: { + op_io(); + rd = op_readdp(regs.y); + wr = op_readdp(regs.x); + wr = op_or(wr, rd); + (1) ? op_writedp(regs.x, wr) : op_io(); + break; +} + +case 0xb9: { + op_io(); + rd = op_readdp(regs.y); + wr = op_readdp(regs.x); + wr = op_sbc(wr, rd); + (1) ? op_writedp(regs.x, wr) : op_io(); + break; +} + +case 0x89: { + sp = op_readpc(); + rd = op_readdp(sp); + dp = op_readpc(); + wr = op_readdp(dp); + wr = op_adc(wr, rd); + (1) ? op_writedp(dp, wr) : op_io(); + break; +} + +case 0x29: { + sp = op_readpc(); + rd = op_readdp(sp); + dp = op_readpc(); + wr = op_readdp(dp); + wr = op_and(wr, rd); + (1) ? op_writedp(dp, wr) : op_io(); + break; +} + +case 0x69: { + sp = op_readpc(); + rd = op_readdp(sp); + dp = op_readpc(); + wr = op_readdp(dp); + wr = op_cmp(wr, rd); + (0) ? op_writedp(dp, wr) : op_io(); + break; +} + +case 0x49: { + sp = op_readpc(); + rd = op_readdp(sp); + dp = op_readpc(); + wr = op_readdp(dp); + wr = op_eor(wr, rd); + (1) ? op_writedp(dp, wr) : op_io(); + break; +} + +case 0x09: { + sp = op_readpc(); + rd = op_readdp(sp); + dp = op_readpc(); + wr = op_readdp(dp); + wr = op_or(wr, rd); + (1) ? op_writedp(dp, wr) : op_io(); + break; +} + +case 0xa9: { + sp = op_readpc(); + rd = op_readdp(sp); + dp = op_readpc(); + wr = op_readdp(dp); + wr = op_sbc(wr, rd); + (1) ? op_writedp(dp, wr) : op_io(); + break; +} + +case 0x98: { + rd = op_readpc(); + dp = op_readpc(); + wr = op_readdp(dp); + wr = op_adc(wr, rd); + (1) ? op_writedp(dp, wr) : op_io(); + break; +} + +case 0x38: { + rd = op_readpc(); + dp = op_readpc(); + wr = op_readdp(dp); + wr = op_and(wr, rd); + (1) ? op_writedp(dp, wr) : op_io(); + break; +} + +case 0x78: { + rd = op_readpc(); + dp = op_readpc(); + wr = op_readdp(dp); + wr = op_cmp(wr, rd); + (0) ? op_writedp(dp, wr) : op_io(); + break; +} + +case 0x58: { + rd = op_readpc(); + dp = op_readpc(); + wr = op_readdp(dp); + wr = op_eor(wr, rd); + (1) ? op_writedp(dp, wr) : op_io(); + break; +} + +case 0x18: { + rd = op_readpc(); + dp = op_readpc(); + wr = op_readdp(dp); + wr = op_or(wr, rd); + (1) ? op_writedp(dp, wr) : op_io(); + break; +} + +case 0xb8: { + rd = op_readpc(); + dp = op_readpc(); + wr = op_readdp(dp); + wr = op_sbc(wr, rd); + (1) ? op_writedp(dp, wr) : op_io(); + break; +} + +case 0x7a: { + dp = op_readpc(); + rd = op_readdp(dp); + op_io(); + rd |= op_readdp(dp + 1) << 8; + regs.ya = op_addw(regs.ya, rd); + break; +} + +case 0x9a: { + dp = op_readpc(); + rd = op_readdp(dp); + op_io(); + rd |= op_readdp(dp + 1) << 8; + regs.ya = op_subw(regs.ya, rd); + break; +} + +case 0x5a: { + dp = op_readpc(); + rd = op_readdp(dp); + rd |= op_readdp(dp + 1) << 8; + op_cmpw(regs.ya, rd); + break; +} + +case 0x4a: { + dp = op_readpc(); + dp |= op_readpc() << 8; + bit = dp >> 13; + dp &= 0x1fff; + rd = op_readaddr(dp); + regs.p.c = regs.p.c & !!(rd & (1 << bit)); + break; +} + +case 0x6a: { + dp = op_readpc(); + dp |= op_readpc() << 8; + bit = dp >> 13; + dp &= 0x1fff; + rd = op_readaddr(dp); + regs.p.c = regs.p.c & !(rd & (1 << bit)); + break; +} + +case 0x8a: { + dp = op_readpc(); + dp |= op_readpc() << 8; + bit = dp >> 13; + dp &= 0x1fff; + rd = op_readaddr(dp); + op_io(); + regs.p.c = regs.p.c ^ !!(rd & (1 << bit)); + break; +} + +case 0xea: { + dp = op_readpc(); + dp |= op_readpc() << 8; + bit = dp >> 13; + dp &= 0x1fff; + rd = op_readaddr(dp); + rd ^= (1 << bit); + op_writeaddr(dp, rd); + break; +} + +case 0x0a: { + dp = op_readpc(); + dp |= op_readpc() << 8; + bit = dp >> 13; + dp &= 0x1fff; + rd = op_readaddr(dp); + op_io(); + regs.p.c = regs.p.c | !!(rd & (1 << bit)); + break; +} + +case 0x2a: { + dp = op_readpc(); + dp |= op_readpc() << 8; + bit = dp >> 13; + dp &= 0x1fff; + rd = op_readaddr(dp); + op_io(); + regs.p.c = regs.p.c | !(rd & (1 << bit)); + break; +} + diff --git a/libsnes/snes/alt/smp/core/op_rmw.b b/libsnes/snes/alt/smp/core/op_rmw.b new file mode 100644 index 0000000..425574e --- /dev/null +++ b/libsnes/snes/alt/smp/core/op_rmw.b @@ -0,0 +1,74 @@ +inc_a(0xbc, inc, a), +inc_x(0x3d, inc, x), +inc_y(0xfc, inc, y), +dec_a(0x9c, dec, a), +dec_x(0x1d, dec, x), +dec_y(0xdc, dec, y), +asl_a(0x1c, asl, a), +lsr_a(0x5c, lsr, a), +rol_a(0x3c, rol, a), +ror_a(0x7c, ror, a) { +1:op_io(); + regs.$2 = op_$1(regs.$2); +} + +inc_dp(0xab, inc), +dec_dp(0x8b, dec), +asl_dp(0x0b, asl), +lsr_dp(0x4b, lsr), +rol_dp(0x2b, rol), +ror_dp(0x6b, ror) { +1:dp = op_readpc(); +2:rd = op_readdp(dp); +3:rd = op_$1(rd); + op_writedp(dp, rd); +} + +inc_dpx(0xbb, inc), +dec_dpx(0x9b, dec), +asl_dpx(0x1b, asl), +lsr_dpx(0x5b, lsr), +rol_dpx(0x3b, rol), +ror_dpx(0x7b, ror) { +1:dp = op_readpc(); +2:op_io(); +3:rd = op_readdp(dp + regs.x); +4:rd = op_$1(rd); + op_writedp(dp + regs.x, rd); +} + +inc_addr(0xac, inc), +dec_addr(0x8c, dec), +asl_addr(0x0c, asl), +lsr_addr(0x4c, lsr), +rol_addr(0x2c, rol), +ror_addr(0x6c, ror) { +1:dp = op_readpc(); +2:dp |= op_readpc() << 8; +3:rd = op_readaddr(dp); +4:rd = op_$1(rd); + op_writeaddr(dp, rd); +} + +tset_addr_a(0x0e, |), +tclr_addr_a(0x4e, &~) { +1:dp = op_readpc(); +2:dp |= op_readpc() << 8; +3:rd = op_readaddr(dp); + regs.p.n = !!((regs.a - rd) & 0x80); + regs.p.z = ((regs.a - rd) == 0); +4:op_readaddr(dp); +5:op_writeaddr(dp, rd $1 regs.a); +} + +incw_dp(0x3a, ++), +decw_dp(0x1a, --) { +1:dp = op_readpc(); +2:rd = op_readdp(dp); + rd$1; +3:op_writedp(dp++, rd); +4:rd += op_readdp(dp) << 8; +5:op_writedp(dp, rd >> 8); + regs.p.n = !!(rd & 0x8000); + regs.p.z = (rd == 0); +} diff --git a/libsnes/snes/alt/smp/core/op_rmw.cpp b/libsnes/snes/alt/smp/core/op_rmw.cpp new file mode 100644 index 0000000..f89ecac --- /dev/null +++ b/libsnes/snes/alt/smp/core/op_rmw.cpp @@ -0,0 +1,262 @@ +case 0xbc: { + op_io(); + regs.a = op_inc(regs.a); + break; +} + +case 0x3d: { + op_io(); + regs.x = op_inc(regs.x); + break; +} + +case 0xfc: { + op_io(); + regs.y = op_inc(regs.y); + break; +} + +case 0x9c: { + op_io(); + regs.a = op_dec(regs.a); + break; +} + +case 0x1d: { + op_io(); + regs.x = op_dec(regs.x); + break; +} + +case 0xdc: { + op_io(); + regs.y = op_dec(regs.y); + break; +} + +case 0x1c: { + op_io(); + regs.a = op_asl(regs.a); + break; +} + +case 0x5c: { + op_io(); + regs.a = op_lsr(regs.a); + break; +} + +case 0x3c: { + op_io(); + regs.a = op_rol(regs.a); + break; +} + +case 0x7c: { + op_io(); + regs.a = op_ror(regs.a); + break; +} + +case 0xab: { + dp = op_readpc(); + rd = op_readdp(dp); + rd = op_inc(rd); + op_writedp(dp, rd); + break; +} + +case 0x8b: { + dp = op_readpc(); + rd = op_readdp(dp); + rd = op_dec(rd); + op_writedp(dp, rd); + break; +} + +case 0x0b: { + dp = op_readpc(); + rd = op_readdp(dp); + rd = op_asl(rd); + op_writedp(dp, rd); + break; +} + +case 0x4b: { + dp = op_readpc(); + rd = op_readdp(dp); + rd = op_lsr(rd); + op_writedp(dp, rd); + break; +} + +case 0x2b: { + dp = op_readpc(); + rd = op_readdp(dp); + rd = op_rol(rd); + op_writedp(dp, rd); + break; +} + +case 0x6b: { + dp = op_readpc(); + rd = op_readdp(dp); + rd = op_ror(rd); + op_writedp(dp, rd); + break; +} + +case 0xbb: { + dp = op_readpc(); + op_io(); + rd = op_readdp(dp + regs.x); + rd = op_inc(rd); + op_writedp(dp + regs.x, rd); + break; +} + +case 0x9b: { + dp = op_readpc(); + op_io(); + rd = op_readdp(dp + regs.x); + rd = op_dec(rd); + op_writedp(dp + regs.x, rd); + break; +} + +case 0x1b: { + dp = op_readpc(); + op_io(); + rd = op_readdp(dp + regs.x); + rd = op_asl(rd); + op_writedp(dp + regs.x, rd); + break; +} + +case 0x5b: { + dp = op_readpc(); + op_io(); + rd = op_readdp(dp + regs.x); + rd = op_lsr(rd); + op_writedp(dp + regs.x, rd); + break; +} + +case 0x3b: { + dp = op_readpc(); + op_io(); + rd = op_readdp(dp + regs.x); + rd = op_rol(rd); + op_writedp(dp + regs.x, rd); + break; +} + +case 0x7b: { + dp = op_readpc(); + op_io(); + rd = op_readdp(dp + regs.x); + rd = op_ror(rd); + op_writedp(dp + regs.x, rd); + break; +} + +case 0xac: { + dp = op_readpc(); + dp |= op_readpc() << 8; + rd = op_readaddr(dp); + rd = op_inc(rd); + op_writeaddr(dp, rd); + break; +} + +case 0x8c: { + dp = op_readpc(); + dp |= op_readpc() << 8; + rd = op_readaddr(dp); + rd = op_dec(rd); + op_writeaddr(dp, rd); + break; +} + +case 0x0c: { + dp = op_readpc(); + dp |= op_readpc() << 8; + rd = op_readaddr(dp); + rd = op_asl(rd); + op_writeaddr(dp, rd); + break; +} + +case 0x4c: { + dp = op_readpc(); + dp |= op_readpc() << 8; + rd = op_readaddr(dp); + rd = op_lsr(rd); + op_writeaddr(dp, rd); + break; +} + +case 0x2c: { + dp = op_readpc(); + dp |= op_readpc() << 8; + rd = op_readaddr(dp); + rd = op_rol(rd); + op_writeaddr(dp, rd); + break; +} + +case 0x6c: { + dp = op_readpc(); + dp |= op_readpc() << 8; + rd = op_readaddr(dp); + rd = op_ror(rd); + op_writeaddr(dp, rd); + break; +} + +case 0x0e: { + dp = op_readpc(); + dp |= op_readpc() << 8; + rd = op_readaddr(dp); + regs.p.n = !!((regs.a - rd) & 0x80); + regs.p.z = ((regs.a - rd) == 0); + op_readaddr(dp); + op_writeaddr(dp, rd | regs.a); + break; +} + +case 0x4e: { + dp = op_readpc(); + dp |= op_readpc() << 8; + rd = op_readaddr(dp); + regs.p.n = !!((regs.a - rd) & 0x80); + regs.p.z = ((regs.a - rd) == 0); + op_readaddr(dp); + op_writeaddr(dp, rd &~ regs.a); + break; +} + +case 0x3a: { + dp = op_readpc(); + rd = op_readdp(dp); + rd++; + op_writedp(dp++, rd); + rd += op_readdp(dp) << 8; + op_writedp(dp, rd >> 8); + regs.p.n = !!(rd & 0x8000); + regs.p.z = (rd == 0); + break; +} + +case 0x1a: { + dp = op_readpc(); + rd = op_readdp(dp); + rd--; + op_writedp(dp++, rd); + rd += op_readdp(dp) << 8; + op_writedp(dp, rd >> 8); + regs.p.n = !!(rd & 0x8000); + regs.p.z = (rd == 0); + break; +} + diff --git a/libsnes/snes/alt/smp/core/opcycle_misc.cpp b/libsnes/snes/alt/smp/core/opcycle_misc.cpp new file mode 100644 index 0000000..963f9fc --- /dev/null +++ b/libsnes/snes/alt/smp/core/opcycle_misc.cpp @@ -0,0 +1,696 @@ +case 0x00: { + switch(opcode_cycle++) { + case 1: + op_io(); + opcode_cycle = 0; + break; + } + break; +} + +case 0xef: { + switch(opcode_cycle++) { + case 1: + op_io(); + break; + case 2: + op_io(); + regs.pc--; + opcode_cycle = 0; + break; + } + break; +} + +case 0xff: { + switch(opcode_cycle++) { + case 1: + op_io(); + break; + case 2: + op_io(); + regs.pc--; + opcode_cycle = 0; + break; + } + break; +} + +case 0x9f: { + switch(opcode_cycle++) { + case 1: + op_io(); + break; + case 2: + op_io(); + break; + case 3: + op_io(); + break; + case 4: + op_io(); + regs.a = (regs.a >> 4) | (regs.a << 4); + regs.p.n = !!(regs.a & 0x80); + regs.p.z = (regs.a == 0); + opcode_cycle = 0; + break; + } + break; +} + +case 0xdf: { + switch(opcode_cycle++) { + case 1: + op_io(); + break; + case 2: + op_io(); + if(regs.p.c || (regs.a) > 0x99) { + regs.a += 0x60; + regs.p.c = 1; + } + if(regs.p.h || (regs.a & 15) > 0x09) { + regs.a += 0x06; + } + regs.p.n = !!(regs.a & 0x80); + regs.p.z = (regs.a == 0); + opcode_cycle = 0; + break; + } + break; +} + +case 0xbe: { + switch(opcode_cycle++) { + case 1: + op_io(); + break; + case 2: + op_io(); + if(!regs.p.c || (regs.a) > 0x99) { + regs.a -= 0x60; + regs.p.c = 0; + } + if(!regs.p.h || (regs.a & 15) > 0x09) { + regs.a -= 0x06; + } + regs.p.n = !!(regs.a & 0x80); + regs.p.z = (regs.a == 0); + opcode_cycle = 0; + break; + } + break; +} + +case 0x60: { + switch(opcode_cycle++) { + case 1: + op_io(); + regs.p.c = 0; + opcode_cycle = 0; + break; + } + break; +} + +case 0x20: { + switch(opcode_cycle++) { + case 1: + op_io(); + regs.p.p = 0; + opcode_cycle = 0; + break; + } + break; +} + +case 0x80: { + switch(opcode_cycle++) { + case 1: + op_io(); + regs.p.c = 1; + opcode_cycle = 0; + break; + } + break; +} + +case 0x40: { + switch(opcode_cycle++) { + case 1: + op_io(); + regs.p.p = 1; + opcode_cycle = 0; + break; + } + break; +} + +case 0xe0: { + switch(opcode_cycle++) { + case 1: + op_io(); + regs.p.v = 0; + regs.p.h = 0; + opcode_cycle = 0; + break; + } + break; +} + +case 0xed: { + switch(opcode_cycle++) { + case 1: + op_io(); + break; + case 2: + op_io(); + regs.p.c = !regs.p.c; + opcode_cycle = 0; + break; + } + break; +} + +case 0xa0: { + switch(opcode_cycle++) { + case 1: + op_io(); + break; + case 2: + op_io(); + regs.p.i = 1; + opcode_cycle = 0; + break; + } + break; +} + +case 0xc0: { + switch(opcode_cycle++) { + case 1: + op_io(); + break; + case 2: + op_io(); + regs.p.i = 0; + opcode_cycle = 0; + break; + } + break; +} + +case 0x02: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + rd = op_readdp(dp); + break; + case 3: + rd |= 0x01; + op_writedp(dp, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x12: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + rd = op_readdp(dp); + break; + case 3: + rd &= ~0x01; + op_writedp(dp, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x22: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + rd = op_readdp(dp); + break; + case 3: + rd |= 0x02; + op_writedp(dp, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x32: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + rd = op_readdp(dp); + break; + case 3: + rd &= ~0x02; + op_writedp(dp, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x42: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + rd = op_readdp(dp); + break; + case 3: + rd |= 0x04; + op_writedp(dp, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x52: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + rd = op_readdp(dp); + break; + case 3: + rd &= ~0x04; + op_writedp(dp, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x62: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + rd = op_readdp(dp); + break; + case 3: + rd |= 0x08; + op_writedp(dp, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x72: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + rd = op_readdp(dp); + break; + case 3: + rd &= ~0x08; + op_writedp(dp, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x82: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + rd = op_readdp(dp); + break; + case 3: + rd |= 0x10; + op_writedp(dp, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x92: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + rd = op_readdp(dp); + break; + case 3: + rd &= ~0x10; + op_writedp(dp, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0xa2: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + rd = op_readdp(dp); + break; + case 3: + rd |= 0x20; + op_writedp(dp, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0xb2: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + rd = op_readdp(dp); + break; + case 3: + rd &= ~0x20; + op_writedp(dp, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0xc2: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + rd = op_readdp(dp); + break; + case 3: + rd |= 0x40; + op_writedp(dp, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0xd2: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + rd = op_readdp(dp); + break; + case 3: + rd &= ~0x40; + op_writedp(dp, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0xe2: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + rd = op_readdp(dp); + break; + case 3: + rd |= 0x80; + op_writedp(dp, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0xf2: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + rd = op_readdp(dp); + break; + case 3: + rd &= ~0x80; + op_writedp(dp, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x2d: { + switch(opcode_cycle++) { + case 1: + op_io(); + break; + case 2: + op_io(); + break; + case 3: + op_writestack(regs.a); + opcode_cycle = 0; + break; + } + break; +} + +case 0x4d: { + switch(opcode_cycle++) { + case 1: + op_io(); + break; + case 2: + op_io(); + break; + case 3: + op_writestack(regs.x); + opcode_cycle = 0; + break; + } + break; +} + +case 0x6d: { + switch(opcode_cycle++) { + case 1: + op_io(); + break; + case 2: + op_io(); + break; + case 3: + op_writestack(regs.y); + opcode_cycle = 0; + break; + } + break; +} + +case 0x0d: { + switch(opcode_cycle++) { + case 1: + op_io(); + break; + case 2: + op_io(); + break; + case 3: + op_writestack(regs.p); + opcode_cycle = 0; + break; + } + break; +} + +case 0xae: { + switch(opcode_cycle++) { + case 1: + op_io(); + break; + case 2: + op_io(); + break; + case 3: + regs.a = op_readstack(); + opcode_cycle = 0; + break; + } + break; +} + +case 0xce: { + switch(opcode_cycle++) { + case 1: + op_io(); + break; + case 2: + op_io(); + break; + case 3: + regs.x = op_readstack(); + opcode_cycle = 0; + break; + } + break; +} + +case 0xee: { + switch(opcode_cycle++) { + case 1: + op_io(); + break; + case 2: + op_io(); + break; + case 3: + regs.y = op_readstack(); + opcode_cycle = 0; + break; + } + break; +} + +case 0x8e: { + switch(opcode_cycle++) { + case 1: + op_io(); + break; + case 2: + op_io(); + break; + case 3: + regs.p = op_readstack(); + opcode_cycle = 0; + break; + } + break; +} + +case 0xcf: { + switch(opcode_cycle++) { + case 1: + op_io(); + break; + case 2: + op_io(); + break; + case 3: + op_io(); + break; + case 4: + op_io(); + break; + case 5: + op_io(); + break; + case 6: + op_io(); + break; + case 7: + op_io(); + break; + case 8: + op_io(); + ya = regs.y * regs.a; + regs.a = ya; + regs.y = ya >> 8; + //result is set based on y (high-byte) only + regs.p.n = !!(regs.y & 0x80); + regs.p.z = (regs.y == 0); + opcode_cycle = 0; + break; + } + break; +} + +case 0x9e: { + switch(opcode_cycle++) { + case 1: + op_io(); + break; + case 2: + op_io(); + break; + case 3: + op_io(); + break; + case 4: + op_io(); + break; + case 5: + op_io(); + break; + case 6: + op_io(); + break; + case 7: + op_io(); + break; + case 8: + op_io(); + break; + case 9: + op_io(); + break; + case 10: + op_io(); + break; + case 11: + op_io(); + ya = regs.ya; + //overflow set if quotient >= 256 + regs.p.v = !!(regs.y >= regs.x); + regs.p.h = !!((regs.y & 15) >= (regs.x & 15)); + if(regs.y < (regs.x << 1)) { + //if quotient is <= 511 (will fit into 9-bit result) + regs.a = ya / regs.x; + regs.y = ya % regs.x; + } else { + //otherwise, the quotient won't fit into regs.p.v + regs.a + //this emulates the odd behavior of the S-SMP in this case + regs.a = 255 - (ya - (regs.x << 9)) / (256 - regs.x); + regs.y = regs.x + (ya - (regs.x << 9)) % (256 - regs.x); + } + //result is set based on a (quotient) only + regs.p.n = !!(regs.a & 0x80); + regs.p.z = (regs.a == 0); + opcode_cycle = 0; + break; + } + break; +} + diff --git a/libsnes/snes/alt/smp/core/opcycle_mov.cpp b/libsnes/snes/alt/smp/core/opcycle_mov.cpp new file mode 100644 index 0000000..635c7ca --- /dev/null +++ b/libsnes/snes/alt/smp/core/opcycle_mov.cpp @@ -0,0 +1,806 @@ +case 0x7d: { + switch(opcode_cycle++) { + case 1: + op_io(); + regs.a = regs.x; + regs.p.n = !!(regs.a & 0x80); + regs.p.z = (regs.a == 0); + opcode_cycle = 0; + break; + } + break; +} + +case 0xdd: { + switch(opcode_cycle++) { + case 1: + op_io(); + regs.a = regs.y; + regs.p.n = !!(regs.a & 0x80); + regs.p.z = (regs.a == 0); + opcode_cycle = 0; + break; + } + break; +} + +case 0x5d: { + switch(opcode_cycle++) { + case 1: + op_io(); + regs.x = regs.a; + regs.p.n = !!(regs.x & 0x80); + regs.p.z = (regs.x == 0); + opcode_cycle = 0; + break; + } + break; +} + +case 0xfd: { + switch(opcode_cycle++) { + case 1: + op_io(); + regs.y = regs.a; + regs.p.n = !!(regs.y & 0x80); + regs.p.z = (regs.y == 0); + opcode_cycle = 0; + break; + } + break; +} + +case 0x9d: { + switch(opcode_cycle++) { + case 1: + op_io(); + regs.x = regs.sp; + regs.p.n = !!(regs.x & 0x80); + regs.p.z = (regs.x == 0); + opcode_cycle = 0; + break; + } + break; +} + +case 0xbd: { + switch(opcode_cycle++) { + case 1: + op_io(); + regs.sp = regs.x; + opcode_cycle = 0; + break; + } + break; +} + +case 0xe8: { + switch(opcode_cycle++) { + case 1: + regs.a = op_readpc(); + regs.p.n = !!(regs.a & 0x80); + regs.p.z = (regs.a == 0); + opcode_cycle = 0; + break; + } + break; +} + +case 0xcd: { + switch(opcode_cycle++) { + case 1: + regs.x = op_readpc(); + regs.p.n = !!(regs.x & 0x80); + regs.p.z = (regs.x == 0); + opcode_cycle = 0; + break; + } + break; +} + +case 0x8d: { + switch(opcode_cycle++) { + case 1: + regs.y = op_readpc(); + regs.p.n = !!(regs.y & 0x80); + regs.p.z = (regs.y == 0); + opcode_cycle = 0; + break; + } + break; +} + +case 0xe6: { + switch(opcode_cycle++) { + case 1: + op_io(); + break; + case 2: + regs.a = op_readdp(regs.x); + regs.p.n = !!(regs.a & 0x80); + regs.p.z = (regs.a == 0); + opcode_cycle = 0; + break; + } + break; +} + +case 0xbf: { + switch(opcode_cycle++) { + case 1: + op_io(); + break; + case 2: + regs.a = op_readdp(regs.x++); + break; + case 3: + op_io(); + regs.p.n = !!(regs.a & 0x80); + regs.p.z = (regs.a == 0); + opcode_cycle = 0; + break; + } + break; +} + +case 0xe4: { + switch(opcode_cycle++) { + case 1: + sp = op_readpc(); + break; + case 2: + regs.a = op_readdp(sp); + regs.p.n = !!(regs.a & 0x80); + regs.p.z = (regs.a == 0); + opcode_cycle = 0; + break; + } + break; +} + +case 0xf8: { + switch(opcode_cycle++) { + case 1: + sp = op_readpc(); + break; + case 2: + regs.x = op_readdp(sp); + regs.p.n = !!(regs.x & 0x80); + regs.p.z = (regs.x == 0); + opcode_cycle = 0; + break; + } + break; +} + +case 0xeb: { + switch(opcode_cycle++) { + case 1: + sp = op_readpc(); + break; + case 2: + regs.y = op_readdp(sp); + regs.p.n = !!(regs.y & 0x80); + regs.p.z = (regs.y == 0); + opcode_cycle = 0; + break; + } + break; +} + +case 0xf4: { + switch(opcode_cycle++) { + case 1: + sp = op_readpc(); + break; + case 2: + op_io(); + break; + case 3: + regs.a = op_readdp(sp + regs.x); + regs.p.n = !!(regs.a & 0x80); + regs.p.z = (regs.a == 0); + opcode_cycle = 0; + break; + } + break; +} + +case 0xf9: { + switch(opcode_cycle++) { + case 1: + sp = op_readpc(); + break; + case 2: + op_io(); + break; + case 3: + regs.x = op_readdp(sp + regs.y); + regs.p.n = !!(regs.x & 0x80); + regs.p.z = (regs.x == 0); + opcode_cycle = 0; + break; + } + break; +} + +case 0xfb: { + switch(opcode_cycle++) { + case 1: + sp = op_readpc(); + break; + case 2: + op_io(); + break; + case 3: + regs.y = op_readdp(sp + regs.x); + regs.p.n = !!(regs.y & 0x80); + regs.p.z = (regs.y == 0); + opcode_cycle = 0; + break; + } + break; +} + +case 0xe5: { + switch(opcode_cycle++) { + case 1: + sp = op_readpc(); + break; + case 2: + sp |= op_readpc() << 8; + break; + case 3: + regs.a = op_readaddr(sp); + regs.p.n = !!(regs.a & 0x80); + regs.p.z = (regs.a == 0); + opcode_cycle = 0; + break; + } + break; +} + +case 0xe9: { + switch(opcode_cycle++) { + case 1: + sp = op_readpc(); + break; + case 2: + sp |= op_readpc() << 8; + break; + case 3: + regs.x = op_readaddr(sp); + regs.p.n = !!(regs.x & 0x80); + regs.p.z = (regs.x == 0); + opcode_cycle = 0; + break; + } + break; +} + +case 0xec: { + switch(opcode_cycle++) { + case 1: + sp = op_readpc(); + break; + case 2: + sp |= op_readpc() << 8; + break; + case 3: + regs.y = op_readaddr(sp); + regs.p.n = !!(regs.y & 0x80); + regs.p.z = (regs.y == 0); + opcode_cycle = 0; + break; + } + break; +} + +case 0xf5: { + switch(opcode_cycle++) { + case 1: + sp = op_readpc(); + break; + case 2: + sp |= op_readpc() << 8; + break; + case 3: + op_io(); + break; + case 4: + regs.a = op_readaddr(sp + regs.x); + regs.p.n = !!(regs.a & 0x80); + regs.p.z = (regs.a == 0); + opcode_cycle = 0; + break; + } + break; +} + +case 0xf6: { + switch(opcode_cycle++) { + case 1: + sp = op_readpc(); + break; + case 2: + sp |= op_readpc() << 8; + break; + case 3: + op_io(); + break; + case 4: + regs.a = op_readaddr(sp + regs.y); + regs.p.n = !!(regs.a & 0x80); + regs.p.z = (regs.a == 0); + opcode_cycle = 0; + break; + } + break; +} + +case 0xe7: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc() + regs.x; + break; + case 2: + op_io(); + break; + case 3: + sp = op_readdp(dp); + break; + case 4: + sp |= op_readdp(dp + 1) << 8; + break; + case 5: + regs.a = op_readaddr(sp); + regs.p.n = !!(regs.a & 0x80); + regs.p.z = (regs.a == 0); + opcode_cycle = 0; + break; + } + break; +} + +case 0xf7: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + op_io(); + break; + case 3: + sp = op_readdp(dp); + break; + case 4: + sp |= op_readdp(dp + 1) << 8; + break; + case 5: + regs.a = op_readaddr(sp + regs.y); + regs.p.n = !!(regs.a & 0x80); + regs.p.z = (regs.a == 0); + opcode_cycle = 0; + break; + } + break; +} + +case 0xfa: { + switch(opcode_cycle++) { + case 1: + sp = op_readpc(); + break; + case 2: + rd = op_readdp(sp); + break; + case 3: + dp = op_readpc(); + break; + case 4: + op_writedp(dp, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x8f: { + switch(opcode_cycle++) { + case 1: + rd = op_readpc(); + break; + case 2: + dp = op_readpc(); + break; + case 3: + op_readdp(dp); + break; + case 4: + op_writedp(dp, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0xc6: { + switch(opcode_cycle++) { + case 1: + op_io(); + break; + case 2: + op_readdp(regs.x); + break; + case 3: + op_writedp(regs.x, regs.a); + opcode_cycle = 0; + break; + } + break; +} + +case 0xaf: { + switch(opcode_cycle++) { + case 1: + op_io(); + break; + case 2: + op_io(); + break; + case 3: + op_writedp(regs.x++, regs.a); + opcode_cycle = 0; + break; + } + break; +} + +case 0xc4: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + op_readdp(dp); + break; + case 3: + op_writedp(dp, regs.a); + opcode_cycle = 0; + break; + } + break; +} + +case 0xd8: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + op_readdp(dp); + break; + case 3: + op_writedp(dp, regs.x); + opcode_cycle = 0; + break; + } + break; +} + +case 0xcb: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + op_readdp(dp); + break; + case 3: + op_writedp(dp, regs.y); + opcode_cycle = 0; + break; + } + break; +} + +case 0xd4: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + op_io(); + dp += regs.x; + break; + case 3: + op_readdp(dp); + break; + case 4: + op_writedp(dp, regs.a); + opcode_cycle = 0; + break; + } + break; +} + +case 0xd9: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + op_io(); + dp += regs.y; + break; + case 3: + op_readdp(dp); + break; + case 4: + op_writedp(dp, regs.x); + opcode_cycle = 0; + break; + } + break; +} + +case 0xdb: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + op_io(); + dp += regs.x; + break; + case 3: + op_readdp(dp); + break; + case 4: + op_writedp(dp, regs.y); + opcode_cycle = 0; + break; + } + break; +} + +case 0xc5: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + dp |= op_readpc() << 8; + break; + case 3: + op_readaddr(dp); + break; + case 4: + op_writeaddr(dp, regs.a); + opcode_cycle = 0; + break; + } + break; +} + +case 0xc9: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + dp |= op_readpc() << 8; + break; + case 3: + op_readaddr(dp); + break; + case 4: + op_writeaddr(dp, regs.x); + opcode_cycle = 0; + break; + } + break; +} + +case 0xcc: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + dp |= op_readpc() << 8; + break; + case 3: + op_readaddr(dp); + break; + case 4: + op_writeaddr(dp, regs.y); + opcode_cycle = 0; + break; + } + break; +} + +case 0xd5: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + dp |= op_readpc() << 8; + break; + case 3: + op_io(); + dp += regs.x; + break; + case 4: + op_readaddr(dp); + break; + case 5: + op_writeaddr(dp, regs.a); + opcode_cycle = 0; + break; + } + break; +} + +case 0xd6: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + dp |= op_readpc() << 8; + break; + case 3: + op_io(); + dp += regs.y; + break; + case 4: + op_readaddr(dp); + break; + case 5: + op_writeaddr(dp, regs.a); + opcode_cycle = 0; + break; + } + break; +} + +case 0xc7: { + switch(opcode_cycle++) { + case 1: + sp = op_readpc(); + break; + case 2: + op_io(); + sp += regs.x; + break; + case 3: + dp = op_readdp(sp); + break; + case 4: + dp |= op_readdp(sp + 1) << 8; + break; + case 5: + op_readaddr(dp); + break; + case 6: + op_writeaddr(dp, regs.a); + opcode_cycle = 0; + break; + } + break; +} + +case 0xd7: { + switch(opcode_cycle++) { + case 1: + sp = op_readpc(); + break; + case 2: + dp = op_readdp(sp); + break; + case 3: + dp |= op_readdp(sp + 1) << 8; + break; + case 4: + op_io(); + dp += regs.y; + break; + case 5: + op_readaddr(dp); + break; + case 6: + op_writeaddr(dp, regs.a); + opcode_cycle = 0; + break; + } + break; +} + +case 0xba: { + switch(opcode_cycle++) { + case 1: + sp = op_readpc(); + break; + case 2: + regs.a = op_readdp(sp); + break; + case 3: + op_io(); + break; + case 4: + regs.y = op_readdp(sp + 1); + regs.p.n = !!(regs.ya & 0x8000); + regs.p.z = (regs.ya == 0); + opcode_cycle = 0; + break; + } + break; +} + +case 0xda: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + op_readdp(dp); + break; + case 3: + op_writedp(dp, regs.a); + break; + case 4: + op_writedp(dp + 1, regs.y); + opcode_cycle = 0; + break; + } + break; +} + +case 0xaa: { + switch(opcode_cycle++) { + case 1: + sp = op_readpc(); + break; + case 2: + sp |= op_readpc() << 8; + break; + case 3: + bit = sp >> 13; + sp &= 0x1fff; + rd = op_readaddr(sp); + regs.p.c = !!(rd & (1 << bit)); + opcode_cycle = 0; + break; + } + break; +} + +case 0xca: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + dp |= op_readpc() << 8; + break; + case 3: + bit = dp >> 13; + dp &= 0x1fff; + rd = op_readaddr(dp); + if(regs.p.c)rd |= (1 << bit); + else rd &= ~(1 << bit); + break; + case 4: + op_io(); + break; + case 5: + op_writeaddr(dp, rd); + opcode_cycle = 0; + break; + } + break; +} + diff --git a/libsnes/snes/alt/smp/core/opcycle_pc.cpp b/libsnes/snes/alt/smp/core/opcycle_pc.cpp new file mode 100644 index 0000000..1cdda64 --- /dev/null +++ b/libsnes/snes/alt/smp/core/opcycle_pc.cpp @@ -0,0 +1,1347 @@ +case 0x2f: { + switch(opcode_cycle++) { + case 1: + rd = op_readpc(); + if(0){ opcode_cycle = 0; break; } + break; + case 2: + op_io(); + break; + case 3: + op_io(); + regs.pc += (int8)rd; + opcode_cycle = 0; + break; + } + break; +} + +case 0xf0: { + switch(opcode_cycle++) { + case 1: + rd = op_readpc(); + if(!regs.p.z){ opcode_cycle = 0; break; } + break; + case 2: + op_io(); + break; + case 3: + op_io(); + regs.pc += (int8)rd; + opcode_cycle = 0; + break; + } + break; +} + +case 0xd0: { + switch(opcode_cycle++) { + case 1: + rd = op_readpc(); + if(regs.p.z){ opcode_cycle = 0; break; } + break; + case 2: + op_io(); + break; + case 3: + op_io(); + regs.pc += (int8)rd; + opcode_cycle = 0; + break; + } + break; +} + +case 0xb0: { + switch(opcode_cycle++) { + case 1: + rd = op_readpc(); + if(!regs.p.c){ opcode_cycle = 0; break; } + break; + case 2: + op_io(); + break; + case 3: + op_io(); + regs.pc += (int8)rd; + opcode_cycle = 0; + break; + } + break; +} + +case 0x90: { + switch(opcode_cycle++) { + case 1: + rd = op_readpc(); + if(regs.p.c){ opcode_cycle = 0; break; } + break; + case 2: + op_io(); + break; + case 3: + op_io(); + regs.pc += (int8)rd; + opcode_cycle = 0; + break; + } + break; +} + +case 0x70: { + switch(opcode_cycle++) { + case 1: + rd = op_readpc(); + if(!regs.p.v){ opcode_cycle = 0; break; } + break; + case 2: + op_io(); + break; + case 3: + op_io(); + regs.pc += (int8)rd; + opcode_cycle = 0; + break; + } + break; +} + +case 0x50: { + switch(opcode_cycle++) { + case 1: + rd = op_readpc(); + if(regs.p.v){ opcode_cycle = 0; break; } + break; + case 2: + op_io(); + break; + case 3: + op_io(); + regs.pc += (int8)rd; + opcode_cycle = 0; + break; + } + break; +} + +case 0x30: { + switch(opcode_cycle++) { + case 1: + rd = op_readpc(); + if(!regs.p.n){ opcode_cycle = 0; break; } + break; + case 2: + op_io(); + break; + case 3: + op_io(); + regs.pc += (int8)rd; + opcode_cycle = 0; + break; + } + break; +} + +case 0x10: { + switch(opcode_cycle++) { + case 1: + rd = op_readpc(); + if(regs.p.n){ opcode_cycle = 0; break; } + break; + case 2: + op_io(); + break; + case 3: + op_io(); + regs.pc += (int8)rd; + opcode_cycle = 0; + break; + } + break; +} + +case 0x03: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + sp = op_readdp(dp); + break; + case 3: + rd = op_readpc(); + break; + case 4: + op_io(); + if((sp & 0x01) != 0x01){ opcode_cycle = 0; break; } + break; + case 5: + op_io(); + break; + case 6: + op_io(); + regs.pc += (int8)rd; + opcode_cycle = 0; + break; + } + break; +} + +case 0x13: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + sp = op_readdp(dp); + break; + case 3: + rd = op_readpc(); + break; + case 4: + op_io(); + if((sp & 0x01) == 0x01){ opcode_cycle = 0; break; } + break; + case 5: + op_io(); + break; + case 6: + op_io(); + regs.pc += (int8)rd; + opcode_cycle = 0; + break; + } + break; +} + +case 0x23: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + sp = op_readdp(dp); + break; + case 3: + rd = op_readpc(); + break; + case 4: + op_io(); + if((sp & 0x02) != 0x02){ opcode_cycle = 0; break; } + break; + case 5: + op_io(); + break; + case 6: + op_io(); + regs.pc += (int8)rd; + opcode_cycle = 0; + break; + } + break; +} + +case 0x33: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + sp = op_readdp(dp); + break; + case 3: + rd = op_readpc(); + break; + case 4: + op_io(); + if((sp & 0x02) == 0x02){ opcode_cycle = 0; break; } + break; + case 5: + op_io(); + break; + case 6: + op_io(); + regs.pc += (int8)rd; + opcode_cycle = 0; + break; + } + break; +} + +case 0x43: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + sp = op_readdp(dp); + break; + case 3: + rd = op_readpc(); + break; + case 4: + op_io(); + if((sp & 0x04) != 0x04){ opcode_cycle = 0; break; } + break; + case 5: + op_io(); + break; + case 6: + op_io(); + regs.pc += (int8)rd; + opcode_cycle = 0; + break; + } + break; +} + +case 0x53: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + sp = op_readdp(dp); + break; + case 3: + rd = op_readpc(); + break; + case 4: + op_io(); + if((sp & 0x04) == 0x04){ opcode_cycle = 0; break; } + break; + case 5: + op_io(); + break; + case 6: + op_io(); + regs.pc += (int8)rd; + opcode_cycle = 0; + break; + } + break; +} + +case 0x63: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + sp = op_readdp(dp); + break; + case 3: + rd = op_readpc(); + break; + case 4: + op_io(); + if((sp & 0x08) != 0x08){ opcode_cycle = 0; break; } + break; + case 5: + op_io(); + break; + case 6: + op_io(); + regs.pc += (int8)rd; + opcode_cycle = 0; + break; + } + break; +} + +case 0x73: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + sp = op_readdp(dp); + break; + case 3: + rd = op_readpc(); + break; + case 4: + op_io(); + if((sp & 0x08) == 0x08){ opcode_cycle = 0; break; } + break; + case 5: + op_io(); + break; + case 6: + op_io(); + regs.pc += (int8)rd; + opcode_cycle = 0; + break; + } + break; +} + +case 0x83: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + sp = op_readdp(dp); + break; + case 3: + rd = op_readpc(); + break; + case 4: + op_io(); + if((sp & 0x10) != 0x10){ opcode_cycle = 0; break; } + break; + case 5: + op_io(); + break; + case 6: + op_io(); + regs.pc += (int8)rd; + opcode_cycle = 0; + break; + } + break; +} + +case 0x93: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + sp = op_readdp(dp); + break; + case 3: + rd = op_readpc(); + break; + case 4: + op_io(); + if((sp & 0x10) == 0x10){ opcode_cycle = 0; break; } + break; + case 5: + op_io(); + break; + case 6: + op_io(); + regs.pc += (int8)rd; + opcode_cycle = 0; + break; + } + break; +} + +case 0xa3: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + sp = op_readdp(dp); + break; + case 3: + rd = op_readpc(); + break; + case 4: + op_io(); + if((sp & 0x20) != 0x20){ opcode_cycle = 0; break; } + break; + case 5: + op_io(); + break; + case 6: + op_io(); + regs.pc += (int8)rd; + opcode_cycle = 0; + break; + } + break; +} + +case 0xb3: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + sp = op_readdp(dp); + break; + case 3: + rd = op_readpc(); + break; + case 4: + op_io(); + if((sp & 0x20) == 0x20){ opcode_cycle = 0; break; } + break; + case 5: + op_io(); + break; + case 6: + op_io(); + regs.pc += (int8)rd; + opcode_cycle = 0; + break; + } + break; +} + +case 0xc3: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + sp = op_readdp(dp); + break; + case 3: + rd = op_readpc(); + break; + case 4: + op_io(); + if((sp & 0x40) != 0x40){ opcode_cycle = 0; break; } + break; + case 5: + op_io(); + break; + case 6: + op_io(); + regs.pc += (int8)rd; + opcode_cycle = 0; + break; + } + break; +} + +case 0xd3: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + sp = op_readdp(dp); + break; + case 3: + rd = op_readpc(); + break; + case 4: + op_io(); + if((sp & 0x40) == 0x40){ opcode_cycle = 0; break; } + break; + case 5: + op_io(); + break; + case 6: + op_io(); + regs.pc += (int8)rd; + opcode_cycle = 0; + break; + } + break; +} + +case 0xe3: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + sp = op_readdp(dp); + break; + case 3: + rd = op_readpc(); + break; + case 4: + op_io(); + if((sp & 0x80) != 0x80){ opcode_cycle = 0; break; } + break; + case 5: + op_io(); + break; + case 6: + op_io(); + regs.pc += (int8)rd; + opcode_cycle = 0; + break; + } + break; +} + +case 0xf3: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + sp = op_readdp(dp); + break; + case 3: + rd = op_readpc(); + break; + case 4: + op_io(); + if((sp & 0x80) == 0x80){ opcode_cycle = 0; break; } + break; + case 5: + op_io(); + break; + case 6: + op_io(); + regs.pc += (int8)rd; + opcode_cycle = 0; + break; + } + break; +} + +case 0x2e: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + sp = op_readdp(dp); + break; + case 3: + rd = op_readpc(); + break; + case 4: + op_io(); + if(regs.a == sp){ opcode_cycle = 0; break; } + break; + case 5: + op_io(); + break; + case 6: + op_io(); + regs.pc += (int8)rd; + opcode_cycle = 0; + break; + } + break; +} + +case 0xde: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + op_io(); + break; + case 3: + sp = op_readdp(dp + regs.x); + break; + case 4: + rd = op_readpc(); + break; + case 5: + op_io(); + if(regs.a == sp){ opcode_cycle = 0; break; } + break; + case 6: + op_io(); + break; + case 7: + op_io(); + regs.pc += (int8)rd; + opcode_cycle = 0; + break; + } + break; +} + +case 0x6e: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + wr = op_readdp(dp); + break; + case 3: + op_writedp(dp, --wr); + break; + case 4: + rd = op_readpc(); + if(wr == 0x00){ opcode_cycle = 0; break; } + break; + case 5: + op_io(); + break; + case 6: + op_io(); + regs.pc += (int8)rd; + opcode_cycle = 0; + break; + } + break; +} + +case 0xfe: { + switch(opcode_cycle++) { + case 1: + rd = op_readpc(); + break; + case 2: + op_io(); + regs.y--; + break; + case 3: + op_io(); + if(regs.y == 0x00){ opcode_cycle = 0; break; } + break; + case 4: + op_io(); + break; + case 5: + op_io(); + regs.pc += (int8)rd; + opcode_cycle = 0; + break; + } + break; +} + +case 0x5f: { + switch(opcode_cycle++) { + case 1: + rd = op_readpc(); + break; + case 2: + rd |= op_readpc() << 8; + regs.pc = rd; + opcode_cycle = 0; + break; + } + break; +} + +case 0x1f: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + dp |= op_readpc() << 8; + break; + case 3: + op_io(); + dp += regs.x; + break; + case 4: + rd = op_readaddr(dp); + break; + case 5: + rd |= op_readaddr(dp + 1) << 8; + regs.pc = rd; + opcode_cycle = 0; + break; + } + break; +} + +case 0x3f: { + switch(opcode_cycle++) { + case 1: + rd = op_readpc(); + break; + case 2: + rd |= op_readpc() << 8; + break; + case 3: + op_io(); + break; + case 4: + op_io(); + break; + case 5: + op_io(); + break; + case 6: + op_writestack(regs.pc >> 8); + break; + case 7: + op_writestack(regs.pc); + regs.pc = rd; + opcode_cycle = 0; + break; + } + break; +} + +case 0x4f: { + switch(opcode_cycle++) { + case 1: + rd = op_readpc(); + break; + case 2: + op_io(); + break; + case 3: + op_io(); + break; + case 4: + op_writestack(regs.pc >> 8); + break; + case 5: + op_writestack(regs.pc); + regs.pc = 0xff00 | rd; + opcode_cycle = 0; + break; + } + break; +} + +case 0x01: { + switch(opcode_cycle++) { + case 1: + dp = 0xffde - (0 << 1); + rd = op_readaddr(dp); + break; + case 2: + rd |= op_readaddr(dp + 1) << 8; + break; + case 3: + op_io(); + break; + case 4: + op_io(); + break; + case 5: + op_io(); + break; + case 6: + op_writestack(regs.pc >> 8); + break; + case 7: + op_writestack(regs.pc); + regs.pc = rd; + opcode_cycle = 0; + break; + } + break; +} + +case 0x11: { + switch(opcode_cycle++) { + case 1: + dp = 0xffde - (1 << 1); + rd = op_readaddr(dp); + break; + case 2: + rd |= op_readaddr(dp + 1) << 8; + break; + case 3: + op_io(); + break; + case 4: + op_io(); + break; + case 5: + op_io(); + break; + case 6: + op_writestack(regs.pc >> 8); + break; + case 7: + op_writestack(regs.pc); + regs.pc = rd; + opcode_cycle = 0; + break; + } + break; +} + +case 0x21: { + switch(opcode_cycle++) { + case 1: + dp = 0xffde - (2 << 1); + rd = op_readaddr(dp); + break; + case 2: + rd |= op_readaddr(dp + 1) << 8; + break; + case 3: + op_io(); + break; + case 4: + op_io(); + break; + case 5: + op_io(); + break; + case 6: + op_writestack(regs.pc >> 8); + break; + case 7: + op_writestack(regs.pc); + regs.pc = rd; + opcode_cycle = 0; + break; + } + break; +} + +case 0x31: { + switch(opcode_cycle++) { + case 1: + dp = 0xffde - (3 << 1); + rd = op_readaddr(dp); + break; + case 2: + rd |= op_readaddr(dp + 1) << 8; + break; + case 3: + op_io(); + break; + case 4: + op_io(); + break; + case 5: + op_io(); + break; + case 6: + op_writestack(regs.pc >> 8); + break; + case 7: + op_writestack(regs.pc); + regs.pc = rd; + opcode_cycle = 0; + break; + } + break; +} + +case 0x41: { + switch(opcode_cycle++) { + case 1: + dp = 0xffde - (4 << 1); + rd = op_readaddr(dp); + break; + case 2: + rd |= op_readaddr(dp + 1) << 8; + break; + case 3: + op_io(); + break; + case 4: + op_io(); + break; + case 5: + op_io(); + break; + case 6: + op_writestack(regs.pc >> 8); + break; + case 7: + op_writestack(regs.pc); + regs.pc = rd; + opcode_cycle = 0; + break; + } + break; +} + +case 0x51: { + switch(opcode_cycle++) { + case 1: + dp = 0xffde - (5 << 1); + rd = op_readaddr(dp); + break; + case 2: + rd |= op_readaddr(dp + 1) << 8; + break; + case 3: + op_io(); + break; + case 4: + op_io(); + break; + case 5: + op_io(); + break; + case 6: + op_writestack(regs.pc >> 8); + break; + case 7: + op_writestack(regs.pc); + regs.pc = rd; + opcode_cycle = 0; + break; + } + break; +} + +case 0x61: { + switch(opcode_cycle++) { + case 1: + dp = 0xffde - (6 << 1); + rd = op_readaddr(dp); + break; + case 2: + rd |= op_readaddr(dp + 1) << 8; + break; + case 3: + op_io(); + break; + case 4: + op_io(); + break; + case 5: + op_io(); + break; + case 6: + op_writestack(regs.pc >> 8); + break; + case 7: + op_writestack(regs.pc); + regs.pc = rd; + opcode_cycle = 0; + break; + } + break; +} + +case 0x71: { + switch(opcode_cycle++) { + case 1: + dp = 0xffde - (7 << 1); + rd = op_readaddr(dp); + break; + case 2: + rd |= op_readaddr(dp + 1) << 8; + break; + case 3: + op_io(); + break; + case 4: + op_io(); + break; + case 5: + op_io(); + break; + case 6: + op_writestack(regs.pc >> 8); + break; + case 7: + op_writestack(regs.pc); + regs.pc = rd; + opcode_cycle = 0; + break; + } + break; +} + +case 0x81: { + switch(opcode_cycle++) { + case 1: + dp = 0xffde - (8 << 1); + rd = op_readaddr(dp); + break; + case 2: + rd |= op_readaddr(dp + 1) << 8; + break; + case 3: + op_io(); + break; + case 4: + op_io(); + break; + case 5: + op_io(); + break; + case 6: + op_writestack(regs.pc >> 8); + break; + case 7: + op_writestack(regs.pc); + regs.pc = rd; + opcode_cycle = 0; + break; + } + break; +} + +case 0x91: { + switch(opcode_cycle++) { + case 1: + dp = 0xffde - (9 << 1); + rd = op_readaddr(dp); + break; + case 2: + rd |= op_readaddr(dp + 1) << 8; + break; + case 3: + op_io(); + break; + case 4: + op_io(); + break; + case 5: + op_io(); + break; + case 6: + op_writestack(regs.pc >> 8); + break; + case 7: + op_writestack(regs.pc); + regs.pc = rd; + opcode_cycle = 0; + break; + } + break; +} + +case 0xa1: { + switch(opcode_cycle++) { + case 1: + dp = 0xffde - (10 << 1); + rd = op_readaddr(dp); + break; + case 2: + rd |= op_readaddr(dp + 1) << 8; + break; + case 3: + op_io(); + break; + case 4: + op_io(); + break; + case 5: + op_io(); + break; + case 6: + op_writestack(regs.pc >> 8); + break; + case 7: + op_writestack(regs.pc); + regs.pc = rd; + opcode_cycle = 0; + break; + } + break; +} + +case 0xb1: { + switch(opcode_cycle++) { + case 1: + dp = 0xffde - (11 << 1); + rd = op_readaddr(dp); + break; + case 2: + rd |= op_readaddr(dp + 1) << 8; + break; + case 3: + op_io(); + break; + case 4: + op_io(); + break; + case 5: + op_io(); + break; + case 6: + op_writestack(regs.pc >> 8); + break; + case 7: + op_writestack(regs.pc); + regs.pc = rd; + opcode_cycle = 0; + break; + } + break; +} + +case 0xc1: { + switch(opcode_cycle++) { + case 1: + dp = 0xffde - (12 << 1); + rd = op_readaddr(dp); + break; + case 2: + rd |= op_readaddr(dp + 1) << 8; + break; + case 3: + op_io(); + break; + case 4: + op_io(); + break; + case 5: + op_io(); + break; + case 6: + op_writestack(regs.pc >> 8); + break; + case 7: + op_writestack(regs.pc); + regs.pc = rd; + opcode_cycle = 0; + break; + } + break; +} + +case 0xd1: { + switch(opcode_cycle++) { + case 1: + dp = 0xffde - (13 << 1); + rd = op_readaddr(dp); + break; + case 2: + rd |= op_readaddr(dp + 1) << 8; + break; + case 3: + op_io(); + break; + case 4: + op_io(); + break; + case 5: + op_io(); + break; + case 6: + op_writestack(regs.pc >> 8); + break; + case 7: + op_writestack(regs.pc); + regs.pc = rd; + opcode_cycle = 0; + break; + } + break; +} + +case 0xe1: { + switch(opcode_cycle++) { + case 1: + dp = 0xffde - (14 << 1); + rd = op_readaddr(dp); + break; + case 2: + rd |= op_readaddr(dp + 1) << 8; + break; + case 3: + op_io(); + break; + case 4: + op_io(); + break; + case 5: + op_io(); + break; + case 6: + op_writestack(regs.pc >> 8); + break; + case 7: + op_writestack(regs.pc); + regs.pc = rd; + opcode_cycle = 0; + break; + } + break; +} + +case 0xf1: { + switch(opcode_cycle++) { + case 1: + dp = 0xffde - (15 << 1); + rd = op_readaddr(dp); + break; + case 2: + rd |= op_readaddr(dp + 1) << 8; + break; + case 3: + op_io(); + break; + case 4: + op_io(); + break; + case 5: + op_io(); + break; + case 6: + op_writestack(regs.pc >> 8); + break; + case 7: + op_writestack(regs.pc); + regs.pc = rd; + opcode_cycle = 0; + break; + } + break; +} + +case 0x0f: { + switch(opcode_cycle++) { + case 1: + rd = op_readaddr(0xffde); + break; + case 2: + rd |= op_readaddr(0xffdf) << 8; + break; + case 3: + op_io(); + break; + case 4: + op_io(); + break; + case 5: + op_writestack(regs.pc >> 8); + break; + case 6: + op_writestack(regs.pc); + break; + case 7: + op_writestack(regs.p); + regs.pc = rd; + regs.p.b = 1; + regs.p.i = 0; + opcode_cycle = 0; + break; + } + break; +} + +case 0x6f: { + switch(opcode_cycle++) { + case 1: + rd = op_readstack(); + break; + case 2: + rd |= op_readstack() << 8; + break; + case 3: + op_io(); + break; + case 4: + op_io(); + regs.pc = rd; + opcode_cycle = 0; + break; + } + break; +} + +case 0x7f: { + switch(opcode_cycle++) { + case 1: + regs.p = op_readstack(); + break; + case 2: + rd = op_readstack(); + break; + case 3: + rd |= op_readstack() << 8; + break; + case 4: + op_io(); + break; + case 5: + op_io(); + regs.pc = rd; + opcode_cycle = 0; + break; + } + break; +} + diff --git a/libsnes/snes/alt/smp/core/opcycle_read.cpp b/libsnes/snes/alt/smp/core/opcycle_read.cpp new file mode 100644 index 0000000..6c19f3a --- /dev/null +++ b/libsnes/snes/alt/smp/core/opcycle_read.cpp @@ -0,0 +1,1599 @@ +case 0x88: { + switch(opcode_cycle++) { + case 1: + rd = op_readpc(); + regs.a = op_adc(regs.a, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x28: { + switch(opcode_cycle++) { + case 1: + rd = op_readpc(); + regs.a = op_and(regs.a, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x68: { + switch(opcode_cycle++) { + case 1: + rd = op_readpc(); + regs.a = op_cmp(regs.a, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0xc8: { + switch(opcode_cycle++) { + case 1: + rd = op_readpc(); + regs.x = op_cmp(regs.x, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0xad: { + switch(opcode_cycle++) { + case 1: + rd = op_readpc(); + regs.y = op_cmp(regs.y, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x48: { + switch(opcode_cycle++) { + case 1: + rd = op_readpc(); + regs.a = op_eor(regs.a, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x08: { + switch(opcode_cycle++) { + case 1: + rd = op_readpc(); + regs.a = op_or(regs.a, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0xa8: { + switch(opcode_cycle++) { + case 1: + rd = op_readpc(); + regs.a = op_sbc(regs.a, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x86: { + switch(opcode_cycle++) { + case 1: + op_io(); + break; + case 2: + rd = op_readdp(regs.x); + regs.a = op_adc(regs.a, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x26: { + switch(opcode_cycle++) { + case 1: + op_io(); + break; + case 2: + rd = op_readdp(regs.x); + regs.a = op_and(regs.a, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x66: { + switch(opcode_cycle++) { + case 1: + op_io(); + break; + case 2: + rd = op_readdp(regs.x); + regs.a = op_cmp(regs.a, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x46: { + switch(opcode_cycle++) { + case 1: + op_io(); + break; + case 2: + rd = op_readdp(regs.x); + regs.a = op_eor(regs.a, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x06: { + switch(opcode_cycle++) { + case 1: + op_io(); + break; + case 2: + rd = op_readdp(regs.x); + regs.a = op_or(regs.a, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0xa6: { + switch(opcode_cycle++) { + case 1: + op_io(); + break; + case 2: + rd = op_readdp(regs.x); + regs.a = op_sbc(regs.a, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x84: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + rd = op_readdp(dp); + regs.a = op_adc(regs.a, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x24: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + rd = op_readdp(dp); + regs.a = op_and(regs.a, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x64: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + rd = op_readdp(dp); + regs.a = op_cmp(regs.a, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x3e: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + rd = op_readdp(dp); + regs.x = op_cmp(regs.x, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x7e: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + rd = op_readdp(dp); + regs.y = op_cmp(regs.y, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x44: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + rd = op_readdp(dp); + regs.a = op_eor(regs.a, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x04: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + rd = op_readdp(dp); + regs.a = op_or(regs.a, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0xa4: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + rd = op_readdp(dp); + regs.a = op_sbc(regs.a, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x94: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + op_io(); + break; + case 3: + rd = op_readdp(dp + regs.x); + regs.a = op_adc(regs.a, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x34: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + op_io(); + break; + case 3: + rd = op_readdp(dp + regs.x); + regs.a = op_and(regs.a, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x74: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + op_io(); + break; + case 3: + rd = op_readdp(dp + regs.x); + regs.a = op_cmp(regs.a, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x54: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + op_io(); + break; + case 3: + rd = op_readdp(dp + regs.x); + regs.a = op_eor(regs.a, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x14: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + op_io(); + break; + case 3: + rd = op_readdp(dp + regs.x); + regs.a = op_or(regs.a, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0xb4: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + op_io(); + break; + case 3: + rd = op_readdp(dp + regs.x); + regs.a = op_sbc(regs.a, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x85: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + dp |= op_readpc() << 8; + break; + case 3: + rd = op_readaddr(dp); + regs.a = op_adc(regs.a, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x25: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + dp |= op_readpc() << 8; + break; + case 3: + rd = op_readaddr(dp); + regs.a = op_and(regs.a, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x65: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + dp |= op_readpc() << 8; + break; + case 3: + rd = op_readaddr(dp); + regs.a = op_cmp(regs.a, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x1e: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + dp |= op_readpc() << 8; + break; + case 3: + rd = op_readaddr(dp); + regs.x = op_cmp(regs.x, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x5e: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + dp |= op_readpc() << 8; + break; + case 3: + rd = op_readaddr(dp); + regs.y = op_cmp(regs.y, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x45: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + dp |= op_readpc() << 8; + break; + case 3: + rd = op_readaddr(dp); + regs.a = op_eor(regs.a, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x05: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + dp |= op_readpc() << 8; + break; + case 3: + rd = op_readaddr(dp); + regs.a = op_or(regs.a, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0xa5: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + dp |= op_readpc() << 8; + break; + case 3: + rd = op_readaddr(dp); + regs.a = op_sbc(regs.a, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x95: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + dp |= op_readpc() << 8; + break; + case 3: + op_io(); + break; + case 4: + rd = op_readaddr(dp + regs.x); + regs.a = op_adc(regs.a, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x96: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + dp |= op_readpc() << 8; + break; + case 3: + op_io(); + break; + case 4: + rd = op_readaddr(dp + regs.y); + regs.a = op_adc(regs.a, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x35: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + dp |= op_readpc() << 8; + break; + case 3: + op_io(); + break; + case 4: + rd = op_readaddr(dp + regs.x); + regs.a = op_and(regs.a, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x36: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + dp |= op_readpc() << 8; + break; + case 3: + op_io(); + break; + case 4: + rd = op_readaddr(dp + regs.y); + regs.a = op_and(regs.a, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x75: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + dp |= op_readpc() << 8; + break; + case 3: + op_io(); + break; + case 4: + rd = op_readaddr(dp + regs.x); + regs.a = op_cmp(regs.a, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x76: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + dp |= op_readpc() << 8; + break; + case 3: + op_io(); + break; + case 4: + rd = op_readaddr(dp + regs.y); + regs.a = op_cmp(regs.a, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x55: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + dp |= op_readpc() << 8; + break; + case 3: + op_io(); + break; + case 4: + rd = op_readaddr(dp + regs.x); + regs.a = op_eor(regs.a, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x56: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + dp |= op_readpc() << 8; + break; + case 3: + op_io(); + break; + case 4: + rd = op_readaddr(dp + regs.y); + regs.a = op_eor(regs.a, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x15: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + dp |= op_readpc() << 8; + break; + case 3: + op_io(); + break; + case 4: + rd = op_readaddr(dp + regs.x); + regs.a = op_or(regs.a, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x16: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + dp |= op_readpc() << 8; + break; + case 3: + op_io(); + break; + case 4: + rd = op_readaddr(dp + regs.y); + regs.a = op_or(regs.a, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0xb5: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + dp |= op_readpc() << 8; + break; + case 3: + op_io(); + break; + case 4: + rd = op_readaddr(dp + regs.x); + regs.a = op_sbc(regs.a, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0xb6: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + dp |= op_readpc() << 8; + break; + case 3: + op_io(); + break; + case 4: + rd = op_readaddr(dp + regs.y); + regs.a = op_sbc(regs.a, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x87: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc() + regs.x; + break; + case 2: + op_io(); + break; + case 3: + sp = op_readdp(dp); + break; + case 4: + sp |= op_readdp(dp + 1) << 8; + break; + case 5: + rd = op_readaddr(sp); + regs.a = op_adc(regs.a, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x27: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc() + regs.x; + break; + case 2: + op_io(); + break; + case 3: + sp = op_readdp(dp); + break; + case 4: + sp |= op_readdp(dp + 1) << 8; + break; + case 5: + rd = op_readaddr(sp); + regs.a = op_and(regs.a, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x67: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc() + regs.x; + break; + case 2: + op_io(); + break; + case 3: + sp = op_readdp(dp); + break; + case 4: + sp |= op_readdp(dp + 1) << 8; + break; + case 5: + rd = op_readaddr(sp); + regs.a = op_cmp(regs.a, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x47: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc() + regs.x; + break; + case 2: + op_io(); + break; + case 3: + sp = op_readdp(dp); + break; + case 4: + sp |= op_readdp(dp + 1) << 8; + break; + case 5: + rd = op_readaddr(sp); + regs.a = op_eor(regs.a, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x07: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc() + regs.x; + break; + case 2: + op_io(); + break; + case 3: + sp = op_readdp(dp); + break; + case 4: + sp |= op_readdp(dp + 1) << 8; + break; + case 5: + rd = op_readaddr(sp); + regs.a = op_or(regs.a, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0xa7: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc() + regs.x; + break; + case 2: + op_io(); + break; + case 3: + sp = op_readdp(dp); + break; + case 4: + sp |= op_readdp(dp + 1) << 8; + break; + case 5: + rd = op_readaddr(sp); + regs.a = op_sbc(regs.a, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x97: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + op_io(); + break; + case 3: + sp = op_readdp(dp); + break; + case 4: + sp |= op_readdp(dp + 1) << 8; + break; + case 5: + rd = op_readaddr(sp + regs.y); + regs.a = op_adc(regs.a, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x37: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + op_io(); + break; + case 3: + sp = op_readdp(dp); + break; + case 4: + sp |= op_readdp(dp + 1) << 8; + break; + case 5: + rd = op_readaddr(sp + regs.y); + regs.a = op_and(regs.a, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x77: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + op_io(); + break; + case 3: + sp = op_readdp(dp); + break; + case 4: + sp |= op_readdp(dp + 1) << 8; + break; + case 5: + rd = op_readaddr(sp + regs.y); + regs.a = op_cmp(regs.a, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x57: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + op_io(); + break; + case 3: + sp = op_readdp(dp); + break; + case 4: + sp |= op_readdp(dp + 1) << 8; + break; + case 5: + rd = op_readaddr(sp + regs.y); + regs.a = op_eor(regs.a, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x17: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + op_io(); + break; + case 3: + sp = op_readdp(dp); + break; + case 4: + sp |= op_readdp(dp + 1) << 8; + break; + case 5: + rd = op_readaddr(sp + regs.y); + regs.a = op_or(regs.a, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0xb7: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + op_io(); + break; + case 3: + sp = op_readdp(dp); + break; + case 4: + sp |= op_readdp(dp + 1) << 8; + break; + case 5: + rd = op_readaddr(sp + regs.y); + regs.a = op_sbc(regs.a, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x99: { + switch(opcode_cycle++) { + case 1: + op_io(); + break; + case 2: + rd = op_readdp(regs.y); + break; + case 3: + wr = op_readdp(regs.x); + wr = op_adc(wr, rd); + break; + case 4: + (1) ? op_writedp(regs.x, wr) : op_io(); + opcode_cycle = 0; + break; + } + break; +} + +case 0x39: { + switch(opcode_cycle++) { + case 1: + op_io(); + break; + case 2: + rd = op_readdp(regs.y); + break; + case 3: + wr = op_readdp(regs.x); + wr = op_and(wr, rd); + break; + case 4: + (1) ? op_writedp(regs.x, wr) : op_io(); + opcode_cycle = 0; + break; + } + break; +} + +case 0x79: { + switch(opcode_cycle++) { + case 1: + op_io(); + break; + case 2: + rd = op_readdp(regs.y); + break; + case 3: + wr = op_readdp(regs.x); + wr = op_cmp(wr, rd); + break; + case 4: + (0) ? op_writedp(regs.x, wr) : op_io(); + opcode_cycle = 0; + break; + } + break; +} + +case 0x59: { + switch(opcode_cycle++) { + case 1: + op_io(); + break; + case 2: + rd = op_readdp(regs.y); + break; + case 3: + wr = op_readdp(regs.x); + wr = op_eor(wr, rd); + break; + case 4: + (1) ? op_writedp(regs.x, wr) : op_io(); + opcode_cycle = 0; + break; + } + break; +} + +case 0x19: { + switch(opcode_cycle++) { + case 1: + op_io(); + break; + case 2: + rd = op_readdp(regs.y); + break; + case 3: + wr = op_readdp(regs.x); + wr = op_or(wr, rd); + break; + case 4: + (1) ? op_writedp(regs.x, wr) : op_io(); + opcode_cycle = 0; + break; + } + break; +} + +case 0xb9: { + switch(opcode_cycle++) { + case 1: + op_io(); + break; + case 2: + rd = op_readdp(regs.y); + break; + case 3: + wr = op_readdp(regs.x); + wr = op_sbc(wr, rd); + break; + case 4: + (1) ? op_writedp(regs.x, wr) : op_io(); + opcode_cycle = 0; + break; + } + break; +} + +case 0x89: { + switch(opcode_cycle++) { + case 1: + sp = op_readpc(); + break; + case 2: + rd = op_readdp(sp); + break; + case 3: + dp = op_readpc(); + break; + case 4: + wr = op_readdp(dp); + break; + case 5: + wr = op_adc(wr, rd); + (1) ? op_writedp(dp, wr) : op_io(); + opcode_cycle = 0; + break; + } + break; +} + +case 0x29: { + switch(opcode_cycle++) { + case 1: + sp = op_readpc(); + break; + case 2: + rd = op_readdp(sp); + break; + case 3: + dp = op_readpc(); + break; + case 4: + wr = op_readdp(dp); + break; + case 5: + wr = op_and(wr, rd); + (1) ? op_writedp(dp, wr) : op_io(); + opcode_cycle = 0; + break; + } + break; +} + +case 0x69: { + switch(opcode_cycle++) { + case 1: + sp = op_readpc(); + break; + case 2: + rd = op_readdp(sp); + break; + case 3: + dp = op_readpc(); + break; + case 4: + wr = op_readdp(dp); + break; + case 5: + wr = op_cmp(wr, rd); + (0) ? op_writedp(dp, wr) : op_io(); + opcode_cycle = 0; + break; + } + break; +} + +case 0x49: { + switch(opcode_cycle++) { + case 1: + sp = op_readpc(); + break; + case 2: + rd = op_readdp(sp); + break; + case 3: + dp = op_readpc(); + break; + case 4: + wr = op_readdp(dp); + break; + case 5: + wr = op_eor(wr, rd); + (1) ? op_writedp(dp, wr) : op_io(); + opcode_cycle = 0; + break; + } + break; +} + +case 0x09: { + switch(opcode_cycle++) { + case 1: + sp = op_readpc(); + break; + case 2: + rd = op_readdp(sp); + break; + case 3: + dp = op_readpc(); + break; + case 4: + wr = op_readdp(dp); + break; + case 5: + wr = op_or(wr, rd); + (1) ? op_writedp(dp, wr) : op_io(); + opcode_cycle = 0; + break; + } + break; +} + +case 0xa9: { + switch(opcode_cycle++) { + case 1: + sp = op_readpc(); + break; + case 2: + rd = op_readdp(sp); + break; + case 3: + dp = op_readpc(); + break; + case 4: + wr = op_readdp(dp); + break; + case 5: + wr = op_sbc(wr, rd); + (1) ? op_writedp(dp, wr) : op_io(); + opcode_cycle = 0; + break; + } + break; +} + +case 0x98: { + switch(opcode_cycle++) { + case 1: + rd = op_readpc(); + break; + case 2: + dp = op_readpc(); + break; + case 3: + wr = op_readdp(dp); + break; + case 4: + wr = op_adc(wr, rd); + (1) ? op_writedp(dp, wr) : op_io(); + opcode_cycle = 0; + break; + } + break; +} + +case 0x38: { + switch(opcode_cycle++) { + case 1: + rd = op_readpc(); + break; + case 2: + dp = op_readpc(); + break; + case 3: + wr = op_readdp(dp); + break; + case 4: + wr = op_and(wr, rd); + (1) ? op_writedp(dp, wr) : op_io(); + opcode_cycle = 0; + break; + } + break; +} + +case 0x78: { + switch(opcode_cycle++) { + case 1: + rd = op_readpc(); + break; + case 2: + dp = op_readpc(); + break; + case 3: + wr = op_readdp(dp); + break; + case 4: + wr = op_cmp(wr, rd); + (0) ? op_writedp(dp, wr) : op_io(); + opcode_cycle = 0; + break; + } + break; +} + +case 0x58: { + switch(opcode_cycle++) { + case 1: + rd = op_readpc(); + break; + case 2: + dp = op_readpc(); + break; + case 3: + wr = op_readdp(dp); + break; + case 4: + wr = op_eor(wr, rd); + (1) ? op_writedp(dp, wr) : op_io(); + opcode_cycle = 0; + break; + } + break; +} + +case 0x18: { + switch(opcode_cycle++) { + case 1: + rd = op_readpc(); + break; + case 2: + dp = op_readpc(); + break; + case 3: + wr = op_readdp(dp); + break; + case 4: + wr = op_or(wr, rd); + (1) ? op_writedp(dp, wr) : op_io(); + opcode_cycle = 0; + break; + } + break; +} + +case 0xb8: { + switch(opcode_cycle++) { + case 1: + rd = op_readpc(); + break; + case 2: + dp = op_readpc(); + break; + case 3: + wr = op_readdp(dp); + break; + case 4: + wr = op_sbc(wr, rd); + (1) ? op_writedp(dp, wr) : op_io(); + opcode_cycle = 0; + break; + } + break; +} + +case 0x7a: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + rd = op_readdp(dp); + break; + case 3: + op_io(); + break; + case 4: + rd |= op_readdp(dp + 1) << 8; + regs.ya = op_addw(regs.ya, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x9a: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + rd = op_readdp(dp); + break; + case 3: + op_io(); + break; + case 4: + rd |= op_readdp(dp + 1) << 8; + regs.ya = op_subw(regs.ya, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x5a: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + rd = op_readdp(dp); + break; + case 3: + rd |= op_readdp(dp + 1) << 8; + op_cmpw(regs.ya, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x4a: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + dp |= op_readpc() << 8; + break; + case 3: + bit = dp >> 13; + dp &= 0x1fff; + rd = op_readaddr(dp); + regs.p.c = regs.p.c & !!(rd & (1 << bit)); + opcode_cycle = 0; + break; + } + break; +} + +case 0x6a: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + dp |= op_readpc() << 8; + break; + case 3: + bit = dp >> 13; + dp &= 0x1fff; + rd = op_readaddr(dp); + regs.p.c = regs.p.c & !(rd & (1 << bit)); + opcode_cycle = 0; + break; + } + break; +} + +case 0x8a: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + dp |= op_readpc() << 8; + break; + case 3: + bit = dp >> 13; + dp &= 0x1fff; + rd = op_readaddr(dp); + break; + case 4: + op_io(); + regs.p.c = regs.p.c ^ !!(rd & (1 << bit)); + opcode_cycle = 0; + break; + } + break; +} + +case 0xea: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + dp |= op_readpc() << 8; + break; + case 3: + bit = dp >> 13; + dp &= 0x1fff; + rd = op_readaddr(dp); + rd ^= (1 << bit); + break; + case 4: + op_writeaddr(dp, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x0a: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + dp |= op_readpc() << 8; + break; + case 3: + bit = dp >> 13; + dp &= 0x1fff; + rd = op_readaddr(dp); + break; + case 4: + op_io(); + regs.p.c = regs.p.c | !!(rd & (1 << bit)); + opcode_cycle = 0; + break; + } + break; +} + +case 0x2a: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + dp |= op_readpc() << 8; + break; + case 3: + bit = dp >> 13; + dp &= 0x1fff; + rd = op_readaddr(dp); + break; + case 4: + op_io(); + regs.p.c = regs.p.c | !(rd & (1 << bit)); + opcode_cycle = 0; + break; + } + break; +} + diff --git a/libsnes/snes/alt/smp/core/opcycle_rmw.cpp b/libsnes/snes/alt/smp/core/opcycle_rmw.cpp new file mode 100644 index 0000000..eca62f0 --- /dev/null +++ b/libsnes/snes/alt/smp/core/opcycle_rmw.cpp @@ -0,0 +1,550 @@ +case 0xbc: { + switch(opcode_cycle++) { + case 1: + op_io(); + regs.a = op_inc(regs.a); + opcode_cycle = 0; + break; + } + break; +} + +case 0x3d: { + switch(opcode_cycle++) { + case 1: + op_io(); + regs.x = op_inc(regs.x); + opcode_cycle = 0; + break; + } + break; +} + +case 0xfc: { + switch(opcode_cycle++) { + case 1: + op_io(); + regs.y = op_inc(regs.y); + opcode_cycle = 0; + break; + } + break; +} + +case 0x9c: { + switch(opcode_cycle++) { + case 1: + op_io(); + regs.a = op_dec(regs.a); + opcode_cycle = 0; + break; + } + break; +} + +case 0x1d: { + switch(opcode_cycle++) { + case 1: + op_io(); + regs.x = op_dec(regs.x); + opcode_cycle = 0; + break; + } + break; +} + +case 0xdc: { + switch(opcode_cycle++) { + case 1: + op_io(); + regs.y = op_dec(regs.y); + opcode_cycle = 0; + break; + } + break; +} + +case 0x1c: { + switch(opcode_cycle++) { + case 1: + op_io(); + regs.a = op_asl(regs.a); + opcode_cycle = 0; + break; + } + break; +} + +case 0x5c: { + switch(opcode_cycle++) { + case 1: + op_io(); + regs.a = op_lsr(regs.a); + opcode_cycle = 0; + break; + } + break; +} + +case 0x3c: { + switch(opcode_cycle++) { + case 1: + op_io(); + regs.a = op_rol(regs.a); + opcode_cycle = 0; + break; + } + break; +} + +case 0x7c: { + switch(opcode_cycle++) { + case 1: + op_io(); + regs.a = op_ror(regs.a); + opcode_cycle = 0; + break; + } + break; +} + +case 0xab: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + rd = op_readdp(dp); + break; + case 3: + rd = op_inc(rd); + op_writedp(dp, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x8b: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + rd = op_readdp(dp); + break; + case 3: + rd = op_dec(rd); + op_writedp(dp, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x0b: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + rd = op_readdp(dp); + break; + case 3: + rd = op_asl(rd); + op_writedp(dp, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x4b: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + rd = op_readdp(dp); + break; + case 3: + rd = op_lsr(rd); + op_writedp(dp, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x2b: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + rd = op_readdp(dp); + break; + case 3: + rd = op_rol(rd); + op_writedp(dp, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x6b: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + rd = op_readdp(dp); + break; + case 3: + rd = op_ror(rd); + op_writedp(dp, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0xbb: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + op_io(); + break; + case 3: + rd = op_readdp(dp + regs.x); + break; + case 4: + rd = op_inc(rd); + op_writedp(dp + regs.x, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x9b: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + op_io(); + break; + case 3: + rd = op_readdp(dp + regs.x); + break; + case 4: + rd = op_dec(rd); + op_writedp(dp + regs.x, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x1b: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + op_io(); + break; + case 3: + rd = op_readdp(dp + regs.x); + break; + case 4: + rd = op_asl(rd); + op_writedp(dp + regs.x, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x5b: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + op_io(); + break; + case 3: + rd = op_readdp(dp + regs.x); + break; + case 4: + rd = op_lsr(rd); + op_writedp(dp + regs.x, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x3b: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + op_io(); + break; + case 3: + rd = op_readdp(dp + regs.x); + break; + case 4: + rd = op_rol(rd); + op_writedp(dp + regs.x, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x7b: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + op_io(); + break; + case 3: + rd = op_readdp(dp + regs.x); + break; + case 4: + rd = op_ror(rd); + op_writedp(dp + regs.x, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0xac: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + dp |= op_readpc() << 8; + break; + case 3: + rd = op_readaddr(dp); + break; + case 4: + rd = op_inc(rd); + op_writeaddr(dp, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x8c: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + dp |= op_readpc() << 8; + break; + case 3: + rd = op_readaddr(dp); + break; + case 4: + rd = op_dec(rd); + op_writeaddr(dp, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x0c: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + dp |= op_readpc() << 8; + break; + case 3: + rd = op_readaddr(dp); + break; + case 4: + rd = op_asl(rd); + op_writeaddr(dp, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x4c: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + dp |= op_readpc() << 8; + break; + case 3: + rd = op_readaddr(dp); + break; + case 4: + rd = op_lsr(rd); + op_writeaddr(dp, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x2c: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + dp |= op_readpc() << 8; + break; + case 3: + rd = op_readaddr(dp); + break; + case 4: + rd = op_rol(rd); + op_writeaddr(dp, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x6c: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + dp |= op_readpc() << 8; + break; + case 3: + rd = op_readaddr(dp); + break; + case 4: + rd = op_ror(rd); + op_writeaddr(dp, rd); + opcode_cycle = 0; + break; + } + break; +} + +case 0x0e: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + dp |= op_readpc() << 8; + break; + case 3: + rd = op_readaddr(dp); + regs.p.n = !!((regs.a - rd) & 0x80); + regs.p.z = ((regs.a - rd) == 0); + break; + case 4: + op_readaddr(dp); + break; + case 5: + op_writeaddr(dp, rd | regs.a); + opcode_cycle = 0; + break; + } + break; +} + +case 0x4e: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + dp |= op_readpc() << 8; + break; + case 3: + rd = op_readaddr(dp); + regs.p.n = !!((regs.a - rd) & 0x80); + regs.p.z = ((regs.a - rd) == 0); + break; + case 4: + op_readaddr(dp); + break; + case 5: + op_writeaddr(dp, rd &~ regs.a); + opcode_cycle = 0; + break; + } + break; +} + +case 0x3a: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + rd = op_readdp(dp); + rd++; + break; + case 3: + op_writedp(dp++, rd); + break; + case 4: + rd += op_readdp(dp) << 8; + break; + case 5: + op_writedp(dp, rd >> 8); + regs.p.n = !!(rd & 0x8000); + regs.p.z = (rd == 0); + opcode_cycle = 0; + break; + } + break; +} + +case 0x1a: { + switch(opcode_cycle++) { + case 1: + dp = op_readpc(); + break; + case 2: + rd = op_readdp(dp); + rd--; + break; + case 3: + op_writedp(dp++, rd); + break; + case 4: + rd += op_readdp(dp) << 8; + break; + case 5: + op_writedp(dp, rd >> 8); + regs.p.n = !!(rd & 0x8000); + regs.p.z = (rd == 0); + opcode_cycle = 0; + break; + } + break; +} + diff --git a/libsnes/snes/alt/smp/iplrom.cpp b/libsnes/snes/alt/smp/iplrom.cpp new file mode 100644 index 0000000..a2ade89 --- /dev/null +++ b/libsnes/snes/alt/smp/iplrom.cpp @@ -0,0 +1,44 @@ +#ifdef SMP_CPP + +//this is the IPLROM for the S-SMP coprocessor. +//the S-SMP does not allow writing to the IPLROM. +//all writes are instead mapped to the extended +//RAM region, accessible when $f1.d7 is clear. + +const uint8 SMP::iplrom[64] = { +/*ffc0*/ 0xcd, 0xef, //mov x,#$ef +/*ffc2*/ 0xbd, //mov sp,x +/*ffc3*/ 0xe8, 0x00, //mov a,#$00 +/*ffc5*/ 0xc6, //mov (x),a +/*ffc6*/ 0x1d, //dec x +/*ffc7*/ 0xd0, 0xfc, //bne $ffc5 +/*ffc9*/ 0x8f, 0xaa, 0xf4, //mov $f4,#$aa +/*ffcc*/ 0x8f, 0xbb, 0xf5, //mov $f5,#$bb +/*ffcf*/ 0x78, 0xcc, 0xf4, //cmp $f4,#$cc +/*ffd2*/ 0xd0, 0xfb, //bne $ffcf +/*ffd4*/ 0x2f, 0x19, //bra $ffef +/*ffd6*/ 0xeb, 0xf4, //mov y,$f4 +/*ffd8*/ 0xd0, 0xfc, //bne $ffd6 +/*ffda*/ 0x7e, 0xf4, //cmp y,$f4 +/*ffdc*/ 0xd0, 0x0b, //bne $ffe9 +/*ffde*/ 0xe4, 0xf5, //mov a,$f5 +/*ffe0*/ 0xcb, 0xf4, //mov $f4,y +/*ffe2*/ 0xd7, 0x00, //mov ($00)+y,a +/*ffe4*/ 0xfc, //inc y +/*ffe5*/ 0xd0, 0xf3, //bne $ffda +/*ffe7*/ 0xab, 0x01, //inc $01 +/*ffe9*/ 0x10, 0xef, //bpl $ffda +/*ffeb*/ 0x7e, 0xf4, //cmp y,$f4 +/*ffed*/ 0x10, 0xeb, //bpl $ffda +/*ffef*/ 0xba, 0xf6, //movw ya,$f6 +/*fff1*/ 0xda, 0x00, //movw $00,ya +/*fff3*/ 0xba, 0xf4, //movw ya,$f4 +/*fff5*/ 0xc4, 0xf4, //mov $f4,a +/*fff7*/ 0xdd, //mov a,y +/*fff8*/ 0x5d, //mov x,a +/*fff9*/ 0xd0, 0xdb, //bne $ffd6 +/*fffb*/ 0x1f, 0x00, 0x00, //jmp ($0000+x) +/*fffe*/ 0xc0, 0xff //reset vector location ($ffc0) +}; + +#endif diff --git a/libsnes/snes/alt/smp/memory.cpp b/libsnes/snes/alt/smp/memory.cpp new file mode 100644 index 0000000..aecba72 --- /dev/null +++ b/libsnes/snes/alt/smp/memory.cpp @@ -0,0 +1,130 @@ +unsigned SMP::port_read(unsigned addr) { + return apuram[0xf4 + (addr & 3)]; +} + +void SMP::port_write(unsigned addr, unsigned data) { + apuram[0xf4 + (addr & 3)] = data; +} + +unsigned SMP::mmio_read(unsigned addr) { + switch(addr) { + + case 0xf2: + return status.dsp_addr; + + case 0xf3: + return dsp.read(status.dsp_addr & 0x7f); + + case 0xf4: + case 0xf5: + case 0xf6: + case 0xf7: + synchronize_cpu(); + return cpu.port_read(addr); + + case 0xf8: + return status.ram00f8; + + case 0xf9: + return status.ram00f9; + + case 0xfd: { + unsigned result = timer0.stage3_ticks & 15; + timer0.stage3_ticks = 0; + return result; + } + + case 0xfe: { + unsigned result = timer1.stage3_ticks & 15; + timer1.stage3_ticks = 0; + return result; + } + + case 0xff: { + unsigned result = timer2.stage3_ticks & 15; + timer2.stage3_ticks = 0; + return result; + } + + } + + return 0x00; +} + +void SMP::mmio_write(unsigned addr, unsigned data) { + switch(addr) { + + case 0xf1: + status.iplrom_enable = data & 0x80; + + if(data & 0x30) { + synchronize_cpu(); + if(data & 0x20) { + cpu.port_write(3, 0x00); + cpu.port_write(2, 0x00); + } + if(data & 0x10) { + cpu.port_write(1, 0x00); + cpu.port_write(0, 0x00); + } + } + + if(timer2.enable == false && (data & 0x04)) { + timer2.stage2_ticks = 0; + timer2.stage3_ticks = 0; + } + timer2.enable = data & 0x04; + + if(timer1.enable == false && (data & 0x02)) { + timer1.stage2_ticks = 0; + timer1.stage3_ticks = 0; + } + timer1.enable = data & 0x02; + + if(timer0.enable == false && (data & 0x01)) { + timer0.stage2_ticks = 0; + timer0.stage3_ticks = 0; + } + timer0.enable = data & 0x01; + + break; + + case 0xf2: + status.dsp_addr = data; + break; + + case 0xf3: + if(status.dsp_addr & 0x80) break; + dsp.write(status.dsp_addr, data); + break; + + case 0xf4: + case 0xf5: + case 0xf6: + case 0xf7: + synchronize_cpu(); + port_write(addr, data); + break; + + case 0xf8: + status.ram00f8 = data; + break; + + case 0xf9: + status.ram00f9 = data; + break; + + case 0xfa: + timer0.target = data; + break; + + case 0xfb: + timer1.target = data; + break; + + case 0xfc: + timer2.target = data; + break; + + } +} diff --git a/libsnes/snes/alt/smp/smp.cpp b/libsnes/snes/alt/smp/smp.cpp new file mode 100644 index 0000000..61006b6 --- /dev/null +++ b/libsnes/snes/alt/smp/smp.cpp @@ -0,0 +1,154 @@ +#define CYCLE_ACCURATE + +#include + +#define SMP_CPP +namespace SNES { + +#if defined(DEBUGGER) + #include "debugger/debugger.cpp" + #include "debugger/disassembler.cpp" + SMPDebugger smp; +#else + SMP smp; +#endif + +#include "algorithms.cpp" +#include "core.cpp" +#include "iplrom.cpp" +#include "memory.cpp" +#include "timing.cpp" + +void SMP::synchronize_cpu() { + if(CPU::Threaded == true) { + //if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(cpu.thread); + } else { + while(clock >= 0) cpu.enter(); + } +} + +void SMP::synchronize_dsp() { + if(DSP::Threaded == true) { + //if(dsp.clock < 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(dsp.thread); + } else { + while(dsp.clock < 0) dsp.enter(); + } +} + +void SMP::enter() { + while(clock < 0) op_step(); +} + +void SMP::power() { + Processor::frequency = system.apu_frequency; + Processor::clock = 0; + + timer0.target = 0; + timer1.target = 0; + timer2.target = 0; + + for(unsigned n = 0; n < 256; n++) { + cycle_table_dsp[n] = (cycle_count_table[n] * 24); + cycle_table_cpu[n] = (cycle_count_table[n] * 24) * cpu.frequency; + } + + cycle_step_cpu = 24 * cpu.frequency; + + reset(); +} + +void SMP::reset() { + for(unsigned n = 0x0000; n <= 0xffff; n++) apuram[n] = 0x00; + + opcode_number = 0; + opcode_cycle = 0; + + regs.pc = 0xffc0; + regs.sp = 0xef; + regs.a = 0x00; + regs.x = 0x00; + regs.y = 0x00; + regs.p = 0x02; + + //$00f1 + status.iplrom_enable = true; + + //$00f2 + status.dsp_addr = 0x00; + + //$00f8,$00f9 + status.ram00f8 = 0x00; + status.ram00f9 = 0x00; + + //timers + timer0.enable = timer1.enable = timer2.enable = false; + timer0.stage1_ticks = timer1.stage1_ticks = timer2.stage1_ticks = 0; + timer0.stage2_ticks = timer1.stage2_ticks = timer2.stage2_ticks = 0; + timer0.stage3_ticks = timer1.stage3_ticks = timer2.stage3_ticks = 0; +} + +void SMP::serialize(serializer &s) { + Processor::serialize(s); + + s.array(apuram, 64 * 1024); + + s.integer(opcode_number); + s.integer(opcode_cycle); + + s.integer(regs.pc); + s.integer(regs.sp); + s.integer(regs.a); + s.integer(regs.x); + s.integer(regs.y); + + s.integer(regs.p.n); + s.integer(regs.p.v); + s.integer(regs.p.p); + s.integer(regs.p.b); + s.integer(regs.p.h); + s.integer(regs.p.i); + s.integer(regs.p.z); + s.integer(regs.p.c); + + s.integer(rd); + s.integer(wr); + s.integer(dp); + s.integer(sp); + s.integer(ya); + s.integer(bit); + + s.integer(status.iplrom_enable); + + s.integer(status.dsp_addr); + + s.integer(status.ram00f8); + s.integer(status.ram00f9); + + s.integer(timer0.enable); + s.integer(timer0.target); + s.integer(timer0.stage1_ticks); + s.integer(timer0.stage2_ticks); + s.integer(timer0.stage3_ticks); + + s.integer(timer1.enable); + s.integer(timer1.target); + s.integer(timer1.stage1_ticks); + s.integer(timer1.stage2_ticks); + s.integer(timer1.stage3_ticks); + + s.integer(timer2.enable); + s.integer(timer2.target); + + s.integer(timer2.stage1_ticks); + s.integer(timer2.stage2_ticks); + s.integer(timer2.stage3_ticks); +} + +SMP::SMP() { + apuram = new uint8[64 * 1024]; +} + +SMP::~SMP() { +} + +} diff --git a/libsnes/snes/alt/smp/smp.hpp b/libsnes/snes/alt/smp/smp.hpp new file mode 100644 index 0000000..e57e094 --- /dev/null +++ b/libsnes/snes/alt/smp/smp.hpp @@ -0,0 +1,120 @@ +class SMP : public Processor { +public: + static const uint8 iplrom[64]; + uint8 *apuram; + + enum { Threaded = false }; + alwaysinline void synchronize_cpu(); + alwaysinline void synchronize_dsp(); + + unsigned port_read(unsigned port); + void port_write(unsigned port, unsigned data); + + unsigned mmio_read(unsigned addr); + void mmio_write(unsigned addr, unsigned data); + + void enter(); + void power(); + void reset(); + + void serialize(serializer&); + SMP(); + ~SMP(); + +//private: + struct Flags { + bool n, v, p, b, h, i, z, c; + + alwaysinline operator unsigned() const { + return (n << 7) | (v << 6) | (p << 5) | (b << 4) + | (h << 3) | (i << 2) | (z << 1) | (c << 0); + }; + + alwaysinline unsigned operator=(unsigned data) { + n = data & 0x80; v = data & 0x40; p = data & 0x20; b = data & 0x10; + h = data & 0x08; i = data & 0x04; z = data & 0x02; c = data & 0x01; + return data; + } + + alwaysinline unsigned operator|=(unsigned data) { return operator=(operator unsigned() | data); } + alwaysinline unsigned operator^=(unsigned data) { return operator=(operator unsigned() ^ data); } + alwaysinline unsigned operator&=(unsigned data) { return operator=(operator unsigned() & data); } + }; + + unsigned opcode_number; + unsigned opcode_cycle; + + struct Regs { + uint16 pc; + uint8 sp; + union { + uint16 ya; + struct { uint8 order_lsb2(a, y); }; + }; + uint8 x; + Flags p; + } regs; + + uint16 rd, wr, dp, sp, ya, bit; + + struct Status { + //$00f1 + bool iplrom_enable; + + //$00f2 + unsigned dsp_addr; + + //$00f8,$00f9 + unsigned ram00f8; + unsigned ram00f9; + } status; + + template + struct Timer { + bool enable; + uint8 target; + uint8 stage1_ticks; + uint8 stage2_ticks; + uint8 stage3_ticks; + + void tick(); + void tick(unsigned clocks); + }; + + Timer<128> timer0; + Timer<128> timer1; + Timer< 16> timer2; + + void tick(); + alwaysinline void op_io(); + debugvirtual alwaysinline uint8 op_read(uint16 addr); + debugvirtual alwaysinline void op_write(uint16 addr, uint8 data); + debugvirtual alwaysinline void op_step(); + static const unsigned cycle_count_table[256]; + uint64 cycle_table_cpu[256]; + unsigned cycle_table_dsp[256]; + uint64 cycle_step_cpu; + + uint8 op_adc (uint8 x, uint8 y); + uint16 op_addw(uint16 x, uint16 y); + uint8 op_and (uint8 x, uint8 y); + uint8 op_cmp (uint8 x, uint8 y); + uint16 op_cmpw(uint16 x, uint16 y); + uint8 op_eor (uint8 x, uint8 y); + uint8 op_inc (uint8 x); + uint8 op_dec (uint8 x); + uint8 op_or (uint8 x, uint8 y); + uint8 op_sbc (uint8 x, uint8 y); + uint16 op_subw(uint16 x, uint16 y); + uint8 op_asl (uint8 x); + uint8 op_lsr (uint8 x); + uint8 op_rol (uint8 x); + uint8 op_ror (uint8 x); +}; + +#if defined(DEBUGGER) + #include "debugger/debugger.hpp" + extern SMPDebugger smp; +#else + extern SMP smp; +#endif diff --git a/libsnes/snes/alt/smp/timing.cpp b/libsnes/snes/alt/smp/timing.cpp new file mode 100644 index 0000000..d278f6f --- /dev/null +++ b/libsnes/snes/alt/smp/timing.cpp @@ -0,0 +1,26 @@ +template +void SMP::Timer::tick() { + if(++stage1_ticks < cycle_frequency) return; + + stage1_ticks = 0; + if(enable == false) return; + + if(++stage2_ticks != target) return; + + stage2_ticks = 0; + stage3_ticks = (stage3_ticks + 1) & 15; +} + +template +void SMP::Timer::tick(unsigned clocks) { + stage1_ticks += clocks; + if(stage1_ticks < cycle_frequency) return; + + stage1_ticks -= cycle_frequency; + if(enable == false) return; + + if(++stage2_ticks != target) return; + + stage2_ticks = 0; + stage3_ticks = (stage3_ticks + 1) & 15; +} diff --git a/libsnes/snes/cartridge/cartridge.cpp b/libsnes/snes/cartridge/cartridge.cpp new file mode 100644 index 0000000..d656a37 --- /dev/null +++ b/libsnes/snes/cartridge/cartridge.cpp @@ -0,0 +1,88 @@ +#include + +#include +#include + +#define CARTRIDGE_CPP +namespace SNES { + +#include "markup.cpp" +#include "serialization.cpp" + +Cartridge cartridge; + +void Cartridge::load(Mode::e cartridge_mode, const char *markup) { + mode.i = cartridge_mode; + region.i = Region::NTSC; + ram_size = 0; + + has_bsx_slot = false; + has_nss_dip = false; + has_superfx = false; + has_sa1 = false; + has_necdsp = false; + has_hitachidsp = false; + has_srtc = false; + has_sdd1 = false; + has_spc7110 = false; + has_spc7110rtc = false; + has_obc1 = false; + has_st0018 = false; + has_msu1 = false; + has_link = false; + + nvram.reset(); + + parse_markup(markup); +//print(markup, "\n\n"); + + if(ram_size > 0) { + ram.map(allocate(ram_size, 0xff), ram_size); + nvram.append(NonVolatileRAM(".srm", ram.data(), ram.size())); + } + + rom.write_protect(true); + ram.write_protect(false); + + crc32 = crc32_calculate(rom.data(), rom.size()); + + switch(mode.i) { + case Mode::Normal: + case Mode::BsxSlotted: + sha256 = nall::sha256(rom.data(), rom.size()); + break; + case Mode::Bsx: + sha256 = nall::sha256(bsxflash.memory.data(), bsxflash.memory.size()); + break; + case Mode::SufamiTurbo: + sha256 = nall::sha256(sufamiturbo.slotA.rom.data(), sufamiturbo.slotA.rom.size()); + break; + case Mode::SuperGameBoy: + sha256 = GameBoy::cartridge.sha256(); + break; + } + + system.load(); + loaded = true; +} + +void Cartridge::unload() { + if(loaded == false) return; + + system.unload(); + rom.reset(); + ram.reset(); + + loaded = false; +} + +Cartridge::Cartridge() { + loaded = false; + unload(); +} + +Cartridge::~Cartridge() { + unload(); +} + +} diff --git a/libsnes/snes/cartridge/cartridge.hpp b/libsnes/snes/cartridge/cartridge.hpp new file mode 100644 index 0000000..f9bc41c --- /dev/null +++ b/libsnes/snes/cartridge/cartridge.hpp @@ -0,0 +1,119 @@ +struct Cartridge : property { + struct Mode { + enum e { + Normal, + BsxSlotted, + Bsx, + SufamiTurbo, + SuperGameBoy, + } i; }; + + struct Region { + enum e { + NTSC, + PAL, + } i; }; + + struct Slot { + enum e { + Base, + Bsx, + SufamiTurbo, + SufamiTurboA, + SufamiTurboB, + GameBoy, + } i; }; + + MappedRAM rom; + MappedRAM ram; + + readonly loaded; + readonly crc32; + readonly sha256; + + Mode mode; + Region region; + readonly ram_size; + + readonly has_bsx_slot; + readonly has_nss_dip; + readonly has_superfx; + readonly has_sa1; + readonly has_necdsp; + readonly has_hitachidsp; + readonly has_srtc; + readonly has_sdd1; + readonly has_spc7110; + readonly has_spc7110rtc; + readonly has_obc1; + readonly has_st0018; + readonly has_msu1; + readonly has_link; + + struct NonVolatileRAM { + const string id; + uint8_t *data; + unsigned size; + Slot::e slot; + NonVolatileRAM() : id(""), data(0), size(0), slot(Slot::Base) {} + NonVolatileRAM(const string id, uint8_t *data, unsigned size, Slot::e slot = Slot::Base) + : id(id), data(data), size(size), slot(slot) {} + }; + linear_vector nvram; + + struct Mapping { + function read; + function write; + Bus::MapMode mode; + unsigned banklo; + unsigned bankhi; + unsigned addrlo; + unsigned addrhi; + unsigned offset; + unsigned size; + + Mapping(); + Mapping(const function&, const function&); + Mapping(Memory&); + }; + linear_vector mapping; + + struct Information { + struct NSS { + lstring setting; + lstring option[16]; + } nss; + } information; + + void load(Mode::e, const char*); + void unload(); + + void serialize(serializer&); + Cartridge(); + ~Cartridge(); + +private: + void parse_markup(const char*); + unsigned parse_markup_integer(string&); + void parse_markup_map(Mapping&, XML::Node&); + + void parse_markup_rom(XML::Node&); + void parse_markup_ram(XML::Node&); + void parse_markup_nss(XML::Node&); + void parse_markup_icd2(XML::Node&); + void parse_markup_superfx(XML::Node&); + void parse_markup_sa1(XML::Node&); + void parse_markup_necdsp(XML::Node&); + void parse_markup_hitachidsp(XML::Node&); + void parse_markup_bsx(XML::Node&); + void parse_markup_sufamiturbo(XML::Node&); + void parse_markup_srtc(XML::Node&); + void parse_markup_sdd1(XML::Node&); + void parse_markup_spc7110(XML::Node&); + void parse_markup_obc1(XML::Node&); + void parse_markup_setarisc(XML::Node&); + void parse_markup_msu1(XML::Node&); + void parse_markup_link(XML::Node&); +}; + +extern Cartridge cartridge; diff --git a/libsnes/snes/cartridge/markup.cpp b/libsnes/snes/cartridge/markup.cpp new file mode 100644 index 0000000..70f2fd3 --- /dev/null +++ b/libsnes/snes/cartridge/markup.cpp @@ -0,0 +1,561 @@ +#ifdef CARTRIDGE_CPP + +#define READER(f, o) function(f, o) +#define WRITER(f, o) function(f, o) + +void Cartridge::parse_markup(const char *markup) { + mapping.reset(); + information.nss.setting.reset(); + + XML::Document document(markup); + XML::Node &cartridge = document["cartridge"]; + region.i = cartridge["region"].data != "PAL" ? Region::NTSC : Region::PAL; + + parse_markup_rom(cartridge["rom"]); + parse_markup_ram(cartridge["ram"]); + parse_markup_nss(cartridge["nss"]); + parse_markup_icd2(cartridge["icd2"]); + parse_markup_sa1(cartridge["sa1"]); + parse_markup_superfx(cartridge["superfx"]); + parse_markup_necdsp(cartridge["necdsp"]); + parse_markup_hitachidsp(cartridge["hitachidsp"]); + parse_markup_bsx(cartridge["bsx"]); + parse_markup_sufamiturbo(cartridge["sufamiturbo"]); + parse_markup_srtc(cartridge["srtc"]); + parse_markup_sdd1(cartridge["sdd1"]); + parse_markup_spc7110(cartridge["spc7110"]); + parse_markup_obc1(cartridge["obc1"]); + parse_markup_setarisc(cartridge["setarisc"]); + parse_markup_msu1(cartridge["msu1"]); + parse_markup_link(cartridge["link"]); +} + +// + +unsigned Cartridge::parse_markup_integer(string &data) { + if(strbegin(data, "0x")) return hex(data); + return decimal(data); +} + +void Cartridge::parse_markup_map(Mapping &m, XML::Node &map) { + m.offset = parse_markup_integer(map["offset"].data); + m.size = parse_markup_integer(map["size"].data); + + string data = map["mode"].data; + if(data == "direct") m.mode.i = Bus::MapMode::Direct; + if(data == "linear") m.mode.i = Bus::MapMode::Linear; + if(data == "shadow") m.mode.i = Bus::MapMode::Shadow; + + lstring part; + part.split(":", map["address"].data); + if(part.size() != 2) return; + + lstring subpart; + subpart.split("-", part[0]); + if(subpart.size() == 1) { + m.banklo = hex(subpart[0]); + m.bankhi = m.banklo; + } else if(subpart.size() == 2) { + m.banklo = hex(subpart[0]); + m.bankhi = hex(subpart[1]); + } + + subpart.split("-", part[1]); + if(subpart.size() == 1) { + m.addrlo = hex(subpart[0]); + m.addrhi = m.addrlo; + } else if(subpart.size() == 2) { + m.addrlo = hex(subpart[0]); + m.addrhi = hex(subpart[1]); + } +} + +// + +void Cartridge::parse_markup_rom(XML::Node &root) { + if(root.exists() == false) return; + foreach(node, root) { + if(node.name != "map") continue; + Mapping m(rom); + parse_markup_map(m, node); + if(m.size == 0) m.size = rom.size(); + mapping.append(m); + } +} + +void Cartridge::parse_markup_ram(XML::Node &root) { + if(root.exists() == false) return; + ram_size = parse_markup_integer(root["size"].data); + foreach(node, root) { + Mapping m(ram); + parse_markup_map(m, node); + if(m.size == 0) m.size = ram_size; + mapping.append(m); + } +} + +void Cartridge::parse_markup_nss(XML::Node &root) { + if(root.exists() == false) return; + has_nss_dip = true; + foreach(node, root) { + if(node.name != "setting") continue; + unsigned number = information.nss.setting.size(); + if(number >= 16) break; //more than 16 DIP switches is not physically possible + + information.nss.option[number].reset(); + information.nss.setting[number] = node["name"].data; + foreach(leaf, node) { + if(leaf.name != "option") continue; + string name = leaf["name"].data; + unsigned value = parse_markup_integer(leaf["value"].data); + information.nss.option[number].append(string( hex<4>(value), ":", name )); + } + } +} + +void Cartridge::parse_markup_icd2(XML::Node &root) { + if(root.exists() == false) return; + if(mode.i != Mode::SuperGameBoy) return; + + icd2.revision = max(1, parse_markup_integer(root["revision"].data)); + + foreach(node, root) { + if(node.name != "map") continue; + Mapping m(READER( &ICD2::read, &icd2 ), WRITER( &ICD2::write, &icd2 )); + parse_markup_map(m, node); + mapping.append(m); + } +} + +void Cartridge::parse_markup_superfx(XML::Node &root) { + if(root.exists() == false) return; + has_superfx = true; + + foreach(node, root) { + if(node.name == "rom") { + foreach(leaf, node) { + if(leaf.name != "map") continue; + Mapping m(superfx.rom); + parse_markup_map(m, leaf); + mapping.append(m); + } + } + if(node.name == "ram") { + foreach(leaf, node) { + if(leaf.name == "size") { + ram_size = parse_markup_integer(leaf.data); + continue; + } + if(leaf.name != "map") continue; + Mapping m(superfx.ram); + parse_markup_map(m, leaf); + if(m.size == 0) m.size = ram_size; + mapping.append(m); + } + } + if(node.name == "mmio") { + foreach(leaf, node) { + if(leaf.name != "map") continue; + Mapping m(READER( &SuperFX::mmio_read, &superfx ), WRITER( &SuperFX::mmio_write, &superfx )); + parse_markup_map(m, leaf); + mapping.append(m); + } + } + } +} + +void Cartridge::parse_markup_sa1(XML::Node &root) { + if(root.exists() == false) return; + has_sa1 = true; + + XML::Node &mcurom = root["mcu"]["rom"]; + XML::Node &mcuram = root["mcu"]["ram"]; + XML::Node &iram = root["iram"]; + XML::Node &bwram = root["bwram"]; + XML::Node &mmio = root["mmio"]; + + foreach(node, mcurom) { + if(node.name != "map") continue; + Mapping m(READER( &SA1::mmc_read, &sa1 ), WRITER( &SA1::mmc_write, &sa1 )); + parse_markup_map(m, node); + mapping.append(m); + } + + foreach(node, mcuram) { + if(node.name != "map") continue; + Mapping m(READER( &SA1::mmc_cpu_read, &sa1 ), WRITER( &SA1::mmc_cpu_write, &sa1 )); + parse_markup_map(m, node); + mapping.append(m); + } + + foreach(node, iram) { + if(node.name != "map") continue; + Mapping m(sa1.cpuiram); + parse_markup_map(m, node); + if(m.size == 0) m.size = 2048; + mapping.append(m); + } + + ram_size = parse_markup_integer(bwram["size"].data); + foreach(node, bwram) { + if(node.name != "map") continue; + Mapping m(sa1.cpubwram); + parse_markup_map(m, node); + if(m.size == 0) m.size = ram_size; + mapping.append(m); + } + + foreach(node, mmio) { + if(node.name != "map") continue; + Mapping m(READER( &SA1::mmio_read, &sa1 ), WRITER( &SA1::mmio_write, &sa1 )); + parse_markup_map(m, node); + mapping.append(m); + } +} + +void Cartridge::parse_markup_necdsp(XML::Node &root) { + if(root.exists() == false) return; + has_necdsp = true; + + for(unsigned n = 0; n < 16384; n++) necdsp.programROM[n] = 0x000000; + for(unsigned n = 0; n < 2048; n++) necdsp.dataROM[n] = 0x0000; + + necdsp.frequency = parse_markup_integer(root["frequency"].data); + if(necdsp.frequency == 0) necdsp.frequency = 8000000; + necdsp.revision.i + = root["model"].data == "uPD7725" ? NECDSP::Revision::uPD7725 + : root["model"].data == "uPD96050" ? NECDSP::Revision::uPD96050 + : NECDSP::Revision::uPD7725; + string firmware = root["firmware"].data; + string sha256 = root["sha256"].data; + + string path( dir(interface->path(Slot::Base, ".dsp")), firmware ); + unsigned promsize = (necdsp.revision.i == NECDSP::Revision::uPD7725 ? 2048 : 16384); + unsigned dromsize = (necdsp.revision.i == NECDSP::Revision::uPD7725 ? 1024 : 2048); + unsigned filesize = promsize * 3 + dromsize * 2; + + file fp; + if(fp.open(path, file::mode_read) == false) { + interface->message(string( "Warning: NEC DSP firmware ", firmware, " is missing." )); + } else if(fp.size() != filesize) { + interface->message(string( "Warning: NEC DSP firmware ", firmware, " is of the wrong file size." )); + fp.close(); + } else { + for(unsigned n = 0; n < promsize; n++) necdsp.programROM[n] = fp.readm(3); + for(unsigned n = 0; n < dromsize; n++) necdsp.dataROM[n] = fp.readm(2); + + if(sha256 != "") { + //XML file specified SHA256 sum for program. Verify file matches the hash. + fp.seek(0); + linear_vector data; + data.reserve(filesize); + fp.read(&data[0], filesize); + + if(sha256 != nall::sha256(&data[0], filesize)) { + interface->message(string( "Warning: NEC DSP firmware ", firmware, " SHA256 sum is incorrect." )); + } + } + + fp.close(); + } + + foreach(node, root) { + if(node.name == "dr") { + foreach(leaf, node) { + Mapping m(READER( &NECDSP::dr_read, &necdsp ), WRITER( &NECDSP::dr_write, &necdsp )); + parse_markup_map(m, leaf); + mapping.append(m); + } + } + if(node.name == "sr") { + foreach(leaf, node) { + Mapping m(READER( &NECDSP::sr_read, &necdsp ), WRITER( &NECDSP::sr_write, &necdsp )); + parse_markup_map(m, leaf); + mapping.append(m); + } + } + if(node.name == "dp") { + foreach(leaf, node) { + Mapping m(READER( &NECDSP::dp_read, &necdsp ), WRITER( &NECDSP::dp_write, &necdsp )); + parse_markup_map(m, leaf); + mapping.append(m); + } + } + } +} + +void Cartridge::parse_markup_hitachidsp(XML::Node &root) { + if(root.exists() == false) return; + has_hitachidsp = true; + + for(unsigned n = 0; n < 1024; n++) hitachidsp.dataROM[n] = 0x000000; + + hitachidsp.frequency = parse_markup_integer(root["frequency"].data); + if(hitachidsp.frequency == 0) hitachidsp.frequency = 20000000; + string firmware = root["firmware"].data; + string sha256 = root["sha256"].data; + + string path( dir(interface->path(Slot::Base, ".dsp")), firmware ); + file fp; + if(fp.open(path, file::mode_read) == false) { + interface->message(string( "Warning: Hitachi DSP firmware ", firmware, " is missing." )); + } else if(fp.size() != 1024 * 3) { + interface->message(string( "Warning: Hitachi DSP firmware ", firmware, " is of the wrong file size." )); + fp.close(); + } else { + for(unsigned n = 0; n < 1024; n++) hitachidsp.dataROM[n] = fp.readl(3); + + if(sha256 != "") { + //XML file specified SHA256 sum for program. Verify file matches the hash. + fp.seek(0); + uint8 data[3072]; + fp.read(data, 3072); + + if(sha256 != nall::sha256(data, 3072)) { + interface->message(string( "Warning: Hitachi DSP firmware ", firmware, " SHA256 sum is incorrect." )); + } + } + + fp.close(); + } + + foreach(node, root) { + if(node.name == "rom") { + foreach(leaf, node) { + if(leaf.name != "map") continue; + Mapping m(READER( &HitachiDSP::rom_read, &hitachidsp ), WRITER( &HitachiDSP::rom_write, &hitachidsp )); + parse_markup_map(m, leaf); + mapping.append(m); + } + } + if(node.name == "mmio") { + foreach(leaf, node) { + Mapping m(READER( &HitachiDSP::dsp_read, &hitachidsp ), WRITER( &HitachiDSP::dsp_write, &hitachidsp )); + parse_markup_map(m, leaf); + mapping.append(m); + } + } + } +} + +void Cartridge::parse_markup_bsx(XML::Node &root) { + if(root.exists() == false) return; + if(mode.i != Mode::BsxSlotted && mode.i != Mode::Bsx) return; + + foreach(node, root["slot"]) { + if(node.name != "map") continue; + Mapping m(bsxflash.memory); + parse_markup_map(m, node); + mapping.append(m); + } + + foreach(node, root["mmio"]) { + if(node.name != "map") continue; + Mapping m(READER( &BSXCartridge::mmio_read, &bsxcartridge ), WRITER( &BSXCartridge::mmio_write, &bsxcartridge )); + parse_markup_map(m, node); + mapping.append(m); + } + + foreach(node, root["mcu"]) { + if(node.name != "map") continue; + Mapping m(READER( &BSXCartridge::mcu_read, &bsxcartridge ), WRITER( &BSXCartridge::mcu_write, &bsxcartridge )); + parse_markup_map(m, node); + mapping.append(m); + } +} + +void Cartridge::parse_markup_sufamiturbo(XML::Node &root) { + if(root.exists() == false) return; + if(mode.i != Mode::SufamiTurbo) return; + + foreach(slot, root) { + if(slot.name != "slot") continue; + bool slotid = slot["id"].data == "A" ? 0 : slot["id"].data == "B" ? 1 : 0; + foreach(node, slot) { + if(node.name == "rom") { + foreach(leaf, node) { + if(leaf.name != "map") continue; + Memory &memory = slotid == 0 ? sufamiturbo.slotA.rom : sufamiturbo.slotB.rom; + Mapping m(memory); + parse_markup_map(m, leaf); + if(m.size == 0) m.size = memory.size(); + if(m.size) mapping.append(m); + } + } + if(node.name == "ram") { + unsigned ram_size = parse_markup_integer(node["size"].data); + foreach(leaf, node) { + if(leaf.name != "map") continue; + Memory &memory = slotid == 0 ? sufamiturbo.slotA.ram : sufamiturbo.slotB.ram; + Mapping m(memory); + parse_markup_map(m, leaf); + if(m.size == 0) m.size = ram_size; + if(m.size) mapping.append(m); + } + } + } + } +} + +void Cartridge::parse_markup_srtc(XML::Node &root) { + if(root.exists() == false) return; + has_srtc = true; + + foreach(node, root) { + if(node.name != "map") continue; + Mapping m(READER( &SRTC::read, &srtc ), WRITER( &SRTC::write, &srtc )); + parse_markup_map(m, node); + mapping.append(m); + } +} + +void Cartridge::parse_markup_sdd1(XML::Node &root) { + if(root.exists() == false) return; + has_sdd1 = true; + + foreach(node, root["mmio"]) { + if(node.name != "map") continue; + Mapping m(READER( &SDD1::mmio_read, &sdd1 ), WRITER( &SDD1::mmio_write, &sdd1 )); + parse_markup_map(m, node); + mapping.append(m); + } + + foreach(node, root["mcu"]) { + if(node.name != "map") continue; + Mapping m(READER( &SDD1::mcu_read, &sdd1 ), WRITER( &SDD1::mcu_write, &sdd1 )); + parse_markup_map(m, node); + mapping.append(m); + } +} + +void Cartridge::parse_markup_spc7110(XML::Node &root) { + if(root.exists() == false) return; + has_spc7110 = true; + + XML::Node &ram = root["ram"]; + XML::Node &mmio = root["mmio"]; + XML::Node &mcu = root["mcu"]; + XML::Node &dcu = root["dcu"]; + XML::Node &rtc = root["rtc"]; + + ram_size = parse_markup_integer(ram["size"].data); + foreach(node, ram) { + if(node.name != "map") continue; + Mapping m(READER( &SPC7110::ram_read, &spc7110 ), WRITER( &SPC7110::ram_write, &spc7110 )); + parse_markup_map(m, node); + mapping.append(m); + } + + foreach(node, mmio) { + if(node.name != "map") continue; + Mapping m(READER( &SPC7110::mmio_read, &spc7110 ), WRITER( &SPC7110::mmio_write, &spc7110 )); + parse_markup_map(m, node); + mapping.append(m); + } + + spc7110.data_rom_offset = parse_markup_integer(mcu["offset"].data); + if(spc7110.data_rom_offset == 0) spc7110.data_rom_offset = 0x100000; + foreach(node, mcu) { + if(node.name != "map") continue; + Mapping m(READER( &SPC7110::mcu_read, &spc7110 ), WRITER( &SPC7110::mcu_write, &spc7110 )); + parse_markup_map(m, node); + mapping.append(m); + } + + foreach(node, dcu) { + if(node.name != "map") continue; + Mapping m(READER( &SPC7110::dcu_read, &spc7110 ), WRITER( &SPC7110::dcu_write, &spc7110 )); + parse_markup_map(m, node); + mapping.append(m); + } + + foreach(node, rtc) { + if(node.name != "map") continue; + Mapping m(READER( &SPC7110::mmio_read, &spc7110 ), WRITER( &SPC7110::mmio_write, &spc7110 )); + parse_markup_map(m, node); + mapping.append(m); + } +} + +void Cartridge::parse_markup_obc1(XML::Node &root) { + if(root.exists() == false) return; + has_obc1 = true; + + foreach(node, root) { + if(node.name != "map") continue; + Mapping m(READER( &OBC1::read, &obc1 ), WRITER( &OBC1::write, &obc1 )); + parse_markup_map(m, node); + mapping.append(m); + } +} + +void Cartridge::parse_markup_setarisc(XML::Node &root) { + if(root.exists() == false) return; + has_st0018 = true; + + foreach(node, root) { + if(node.name != "map") continue; + Mapping m(READER( &ST0018::mmio_read, &st0018 ), WRITER( &ST0018::mmio_write, &st0018 )); + parse_markup_map(m, node); + mapping.append(m); + } +} + +void Cartridge::parse_markup_msu1(XML::Node &root) { + if(root.exists() == false) { + has_msu1 = file::exists(interface->path(Cartridge::Slot::Base, ".msu")); + if(has_msu1) { + Mapping m(READER( &MSU1::mmio_read, &msu1 ), WRITER( &MSU1::mmio_write, &msu1 )); + m.banklo = 0x00, m.bankhi = 0x3f, m.addrlo = 0x2000, m.addrhi = 0x2007; + mapping.append(m); + m.banklo = 0x80, m.bankhi = 0xbf, m.addrlo = 0x2000, m.addrhi = 0x2007; + mapping.append(m); + } + return; + } + + has_msu1 = true; + + foreach(node, root) { + if(node.name != "map") continue; + Mapping m(READER( &MSU1::mmio_read, &msu1 ), WRITER( &MSU1::mmio_write, &msu1 )); + parse_markup_map(m, node); + mapping.append(m); + } +} + +void Cartridge::parse_markup_link(XML::Node &root) { + if(root.exists() == false) return; + has_link = true; + + link.frequency = max(1, parse_markup_integer(root["frequency"].data)); + link.program = root["program"].data; + + foreach(node, root) { + if(node.name != "map") continue; + Mapping m(READER( &Link::read, &link ), WRITER( &Link::write, &link )); + parse_markup_map(m, node); + mapping.append(m); + } +} + +Cartridge::Mapping::Mapping() { + mode.i = Bus::MapMode::Direct; + banklo = bankhi = addrlo = addrhi = offset = size = 0; +} + +Cartridge::Mapping::Mapping(Memory &memory) { + read = READER( &Memory::read, &memory ); + write = WRITER( &Memory::write, &memory ); + mode.i = Bus::MapMode::Direct; + banklo = bankhi = addrlo = addrhi = offset = size = 0; +} + +Cartridge::Mapping::Mapping(const function &read_, const function &write_) { + read = read_; + write = write_; + mode.i = Bus::MapMode::Direct; + banklo = bankhi = addrlo = addrhi = offset = size = 0; +} + +#endif diff --git a/libsnes/snes/cartridge/serialization.cpp b/libsnes/snes/cartridge/serialization.cpp new file mode 100644 index 0000000..0e0a6e2 --- /dev/null +++ b/libsnes/snes/cartridge/serialization.cpp @@ -0,0 +1,9 @@ +#ifdef CARTRIDGE_CPP + +void Cartridge::serialize(serializer &s) { + foreach(ram, nvram) { + if(ram.size) s.array(ram.data, ram.size); + } +} + +#endif diff --git a/libsnes/snes/cheat/cheat.cpp b/libsnes/snes/cheat/cheat.cpp new file mode 100644 index 0000000..1950df6 --- /dev/null +++ b/libsnes/snes/cheat/cheat.cpp @@ -0,0 +1,110 @@ +#include + +#define CHEAT_CPP +namespace SNES { + +Cheat cheat; + +bool Cheat::enabled() const { + return system_enabled; +} + +void Cheat::enable(bool state) { + system_enabled = state; + cheat_enabled = system_enabled && code_enabled; +} + +void Cheat::synchronize() { + memset(override, 0x00, 16 * 1024 * 1024); + code_enabled = size() > 0; + + for(unsigned i = 0; i < size(); i++) { + const CheatCode &code = operator[](i); + + unsigned addr = mirror(code.addr); + override[addr] = true; + if((addr & 0xffe000) == 0x7e0000) { + //mirror $7e:0000-1fff to $00-3f|80-bf:0000-1fff + unsigned mirroraddr; + for(unsigned x = 0; x <= 0x3f; x++) { + mirroraddr = ((0x00 + x) << 16) + (addr & 0x1fff); + override[mirroraddr] = true; + + mirroraddr = ((0x80 + x) << 16) + (addr & 0x1fff); + override[mirroraddr] = true; + } + } + } + + cheat_enabled = system_enabled && code_enabled; +} + +uint8 Cheat::read(unsigned addr) const { + addr = mirror(addr); + + for(unsigned i = 0; i < size(); i++) { + const CheatCode &code = operator[](i); + if(addr == mirror(code.addr)) { + return code.data; + } + } + + return 0x00; +} + +void Cheat::init() { + memset(override, 0x00, 16 * 1024 * 1024); +} + +Cheat::Cheat() { + override = new uint8[16 * 1024 * 1024]; + system_enabled = true; +} + +Cheat::~Cheat() { + delete[] override; +} + +bool Cheat::decode(const string &code, unsigned &addr, unsigned &data) { + string t = code; + t.lower(); + + #define ischr(n) ((n >= '0' && n <= '9') || (n >= 'a' && n <= 'f')) + + if(t.wildcard("??????:??")) { + //Direct + t = string( substr(t, 0, 6), substr(t, 7, 2) ); + for(unsigned n = 0; n < 8; n++) if(!ischr(t[n])) return false; //validate input + unsigned r = hex(t); + + addr = r >> 8; + data = r & 0xff; + return true; + } + + if(t.wildcard("????" "-" "????")) { + //Game Genie + t = string( substr(t, 0, 4), substr(t, 5, 4) ); + for(unsigned n = 0; n < 8; n++) if(!ischr(t[n])) return false; //validate input + t.transform("df4709156bc8a23e", "0123456789abcdef"); + unsigned r = hex(t); + static unsigned bits[] = { 13, 12, 11, 10, 5, 4, 3, 2, 23, 22, 21, 20, 1, 0, 15, 14, 19, 18, 17, 16, 9, 8, 7, 6 }; + + addr = 0; + for(unsigned n = 0; n < 24; n++) addr |= r & (1 << bits[n]) ? 0x800000 >> n : 0; + data = r >> 24; + return true; + } else { + return false; + } + + #undef ischr +} + +unsigned Cheat::mirror(unsigned addr) const { + //$00-3f|80-bf:0000-1fff -> $7e:0000-1fff + if((addr & 0x40e000) == 0x000000) return (0x7e0000 + (addr & 0x1fff)); + return addr; +} + +} diff --git a/libsnes/snes/cheat/cheat.hpp b/libsnes/snes/cheat/cheat.hpp new file mode 100644 index 0000000..306b99b --- /dev/null +++ b/libsnes/snes/cheat/cheat.hpp @@ -0,0 +1,27 @@ +struct CheatCode { + unsigned addr; + unsigned data; +}; + +struct Cheat : public linear_vector { + uint8 *override; + + bool enabled() const; + void enable(bool); + void synchronize(); + uint8 read(unsigned) const; + void init(); + + Cheat(); + ~Cheat(); + + static bool decode(const string&, unsigned&, unsigned&); + +private: + bool system_enabled; + bool code_enabled; + bool cheat_enabled; + unsigned mirror(unsigned) const; +}; + +extern Cheat cheat; diff --git a/libsnes/snes/chip/bsx/bsx.cpp b/libsnes/snes/chip/bsx/bsx.cpp new file mode 100644 index 0000000..450c042 --- /dev/null +++ b/libsnes/snes/chip/bsx/bsx.cpp @@ -0,0 +1,8 @@ +#include + +#define BSX_CPP +namespace SNES { + #include "satellaview/satellaview.cpp" + #include "cartridge/cartridge.cpp" + #include "flash/flash.cpp" +} diff --git a/libsnes/snes/chip/bsx/bsx.hpp b/libsnes/snes/chip/bsx/bsx.hpp new file mode 100644 index 0000000..5f3895b --- /dev/null +++ b/libsnes/snes/chip/bsx/bsx.hpp @@ -0,0 +1,3 @@ +#include "satellaview/satellaview.hpp" +#include "cartridge/cartridge.hpp" +#include "flash/flash.hpp" diff --git a/libsnes/snes/chip/bsx/cartridge/cartridge.cpp b/libsnes/snes/chip/bsx/cartridge/cartridge.cpp new file mode 100644 index 0000000..28e27de --- /dev/null +++ b/libsnes/snes/chip/bsx/cartridge/cartridge.cpp @@ -0,0 +1,150 @@ +#ifdef BSX_CPP + +BSXCartridge bsxcartridge; + +void BSXCartridge::init() { +} + +void BSXCartridge::load() { + sram.map(allocate(32 * 1024, 0xff), 32 * 1024); + sram.write_protect(false); + cartridge.nvram.append(Cartridge::NonVolatileRAM( ".bss", sram.data(), sram.size() )); + + psram.map(allocate(512 * 1024, 0xff), 512 * 1024); + psram.write_protect(false); + cartridge.nvram.append(Cartridge::NonVolatileRAM( ".bsp", psram.data(), psram.size() )); +} + +void BSXCartridge::unload() { +} + +void BSXCartridge::power() { +} + +void BSXCartridge::reset() { + for(unsigned i = 0; i < 16; i++) r[i] = 0x00; + r[0x07] = 0x80; + r[0x08] = 0x80; + mmio_commit(); +} + +uint8 BSXCartridge::memory_access(bool write, Memory &memory, unsigned addr, uint8 data) { + if(write == 0) return memory_read(memory, addr); + memory_write(memory, addr, data); + + return 0; // FIXME: Can it reach here? +} + +uint8 BSXCartridge::memory_read(Memory &memory, unsigned addr) { + addr = bus.mirror(addr, memory.size()); + return memory.read(addr); +} + +void BSXCartridge::memory_write(Memory &memory, unsigned addr, uint8 data) { + addr = bus.mirror(addr, memory.size()); + return memory.write(addr, data); +} + +//mcu_access() allows mcu_read() and mcu_write() to share decoding logic +uint8 BSXCartridge::mcu_access(bool write, unsigned addr, uint8 data) { + if(within<0x00, 0x1f, 0x8000, 0xffff>(addr)) { + if(r07 == 1) { + addr = ((addr & 0x1f0000) >> 1) | (addr & 0x7fff); + return memory_access(write, cartridge.rom, addr, data); + } + } + + if(within<0x80, 0x9f, 0x8000, 0xffff>(addr)) { + if(r08 == 1) { + addr = ((addr & 0x1f0000) >> 1) | (addr & 0x7fff); + return memory_access(write, cartridge.rom, addr, data); + } + } + + if(within<0x20, 0x3f, 0x6000, 0x7fff>(addr)) { + return memory_access(write, psram, addr, data); + } + + if(within<0x40, 0x4f, 0x0000, 0xffff>(addr)) { + if(r05 == 0) return memory_access(write, psram, addr & 0x0fffff, data); + } + + if(within<0x50, 0x5f, 0x0000, 0xffff>(addr)) { + if(r06 == 0) return memory_access(write, psram, addr & 0x0fffff, data); + } + + if(within<0x60, 0x6f, 0x0000, 0xffff>(addr)) { + if(r03 == 1) return memory_access(write, psram, addr & 0x0fffff, data); + } + + if(within<0x70, 0x77, 0x0000, 0xffff>(addr)) { + return memory_access(write, psram, addr & 0x07ffff, data); + } + + if(within<0x00, 0x3f, 0x8000, 0xffff>(addr) + || within<0x40, 0x7f, 0x0000, 0xffff>(addr) + || within<0x80, 0xbf, 0x8000, 0xffff>(addr) + || within<0xc0, 0xff, 0x0000, 0xffff>(addr) + ) { + if(r02 == 0) addr = ((addr & 0x7f0000) >> 1) | (addr & 0x7fff); + Memory &memory = (r01 == 0 ? (Memory&)bsxflash : (Memory&)psram); + return memory_access(write, memory, addr & 0x7fffff, data); + } + + return cpu.regs.mdr; +} + +uint8 BSXCartridge::mcu_read(unsigned addr) { + return mcu_access(0, addr); +} + +void BSXCartridge::mcu_write(unsigned addr, uint8 data) { + mcu_access(1, addr, data); +} + +uint8 BSXCartridge::mmio_read(unsigned addr) { + if(within<0x00, 0x0f, 0x5000, 0x5000>(addr)) { + uint8 n = (addr >> 16) & 15; + return r[n]; + } + + if(within<0x10, 0x17, 0x5000, 0x5fff>(addr)) { + return memory_read(sram, ((addr >> 16) & 7) * 0x1000 + (addr & 0xfff)); + } + + return 0x00; +} + +void BSXCartridge::mmio_write(unsigned addr, uint8 data) { + if(within<0x00, 0x0f, 0x5000, 0x5000>(addr)) { + uint8 n = (addr >> 16) & 15; + r[n] = data; + if(n == 0x0e && data & 0x80) mmio_commit(); + return; + } + + if(within<0x10, 0x17, 0x5000, 0x5fff>(addr)) { + return memory_write(sram, ((addr >> 16) & 7) * 0x1000 + (addr & 0xfff), data); + } +} + +void BSXCartridge::mmio_commit() { + r00 = r[0x00] & 0x80; + r01 = r[0x01] & 0x80; + r02 = r[0x02] & 0x80; + r03 = r[0x03] & 0x80; + r04 = r[0x04] & 0x80; + r05 = r[0x05] & 0x80; + r06 = r[0x06] & 0x80; + r07 = r[0x07] & 0x80; + r08 = r[0x08] & 0x80; + r09 = r[0x09] & 0x80; + r0a = r[0x0a] & 0x80; + r0b = r[0x0b] & 0x80; + r0c = r[0x0c] & 0x80; + r0d = r[0x0d] & 0x80; + r0e = r[0x0e] & 0x80; + r0f = r[0x0f] & 0x80; +} + +#endif diff --git a/libsnes/snes/chip/bsx/cartridge/cartridge.hpp b/libsnes/snes/chip/bsx/cartridge/cartridge.hpp new file mode 100644 index 0000000..eb5f9bf --- /dev/null +++ b/libsnes/snes/chip/bsx/cartridge/cartridge.hpp @@ -0,0 +1,32 @@ +class BSXCartridge { +public: + MappedRAM sram; + MappedRAM psram; + + void init(); + void load(); + void unload(); + void power(); + void reset(); + + uint8 memory_access(bool write, Memory &memory, unsigned addr, uint8 data); + uint8 memory_read(Memory &memory, unsigned addr); + void memory_write(Memory &memory, unsigned addr, uint8 data); + + uint8 mcu_access(bool write, unsigned addr, uint8 data = 0x00); + uint8 mcu_read(unsigned addr); + void mcu_write(unsigned addr, uint8 data); + + uint8 mmio_read(unsigned addr); + void mmio_write(unsigned addr, uint8 data); + void mmio_commit(); + +private: + uint8 r[16]; + bool r00, r01, r02, r03; + bool r04, r05, r06, r07; + bool r08, r09, r0a, r0b; + bool r0c, r0d, r0e, r0f; +}; + +extern BSXCartridge bsxcartridge; diff --git a/libsnes/snes/chip/bsx/flash/flash.cpp b/libsnes/snes/chip/bsx/flash/flash.cpp new file mode 100644 index 0000000..17b525b --- /dev/null +++ b/libsnes/snes/chip/bsx/flash/flash.cpp @@ -0,0 +1,127 @@ +#ifdef BSX_CPP + +BSXFlash bsxflash; + +void BSXFlash::init() { +} + +void BSXFlash::load() { + if(memory.size() == 0) { + memory.map(allocate(1024 * 1024, 0xff), 1024 * 1024); + } +} + +void BSXFlash::unload() { + memory.reset(); +} + +void BSXFlash::power() { +} + +void BSXFlash::reset() { + regs.command = 0; + regs.write_old = 0x00; + regs.write_new = 0x00; + + regs.flash_enable = false; + regs.read_enable = false; + regs.write_enable = false; + memory.write_protect(!regs.write_enable); +} + +unsigned BSXFlash::size() const { + return memory.size(); +} + +uint8 BSXFlash::read(unsigned addr) { + if(addr == 0x0002) { + if(regs.flash_enable) return 0x80; + } + + if(addr == 0x5555) { + if(regs.flash_enable) return 0x80; + } + + if(regs.read_enable && addr >= 0xff00 && addr <= 0xff13) { + //read flash cartridge vendor information + switch(addr - 0xff00) { + case 0x00: return 0x4d; + case 0x01: return 0x00; + case 0x02: return 0x50; + case 0x03: return 0x00; + case 0x04: return 0x00; + case 0x05: return 0x00; + case 0x06: return 0x2a; //0x2a = 8mbit, 0x2b = 16mbit (not known to exist, though BIOS recognizes ID) + case 0x07: return 0x00; + default: return 0x00; + } + } + + return memory.read(addr); +} + +void BSXFlash::write(unsigned addr, uint8 data) { + //there exist both read-only and read-write BS-X flash cartridges ... + //unfortunately, the vendor info is not stored inside memory dumps + //of BS-X flashcarts, so it is impossible to determine whether a + //given flashcart is writeable. + //however, it has been observed that LoROM-mapped BS-X carts always + //use read-write flashcarts, and HiROM-mapped BS-X carts always use + //read-only flashcarts. + //below is an unfortunately necessary workaround to this problem. + //if(cartridge.mapper() == Cartridge::BSCHiROM) return; + + if((addr & 0xff0000) == 0) { + regs.write_old = regs.write_new; + regs.write_new = data; + + if(regs.write_enable && regs.write_old == regs.write_new) { + return memory.write(addr, data); + } + } else { + if(regs.write_enable) { + return memory.write(addr, data); + } + } + + if(addr == 0x0000) { + regs.command <<= 8; + regs.command |= data; + + if((regs.command & 0xffff) == 0x38d0) { + regs.flash_enable = true; + regs.read_enable = true; + } + } + + if(addr == 0x2aaa) { + regs.command <<= 8; + regs.command |= data; + } + + if(addr == 0x5555) { + regs.command <<= 8; + regs.command |= data; + + if((regs.command & 0xffffff) == 0xaa5570) { + regs.write_enable = false; + } + + if((regs.command & 0xffffff) == 0xaa55a0) { + regs.write_old = 0x00; + regs.write_new = 0x00; + regs.flash_enable = true; + regs.write_enable = true; + } + + if((regs.command & 0xffffff) == 0xaa55f0) { + regs.flash_enable = false; + regs.read_enable = false; + regs.write_enable = false; + } + + memory.write_protect(!regs.write_enable); + } +} + +#endif diff --git a/libsnes/snes/chip/bsx/flash/flash.hpp b/libsnes/snes/chip/bsx/flash/flash.hpp new file mode 100644 index 0000000..353bef1 --- /dev/null +++ b/libsnes/snes/chip/bsx/flash/flash.hpp @@ -0,0 +1,27 @@ +class BSXFlash : public Memory { +public: + MappedRAM memory; + + void init(); + void load(); + void unload(); + void power(); + void reset(); + + unsigned size() const; + uint8 read(unsigned addr); + void write(unsigned addr, uint8 data); + +private: + struct { + unsigned command; + uint8 write_old; + uint8 write_new; + + bool flash_enable; + bool read_enable; + bool write_enable; + } regs; +}; + +extern BSXFlash bsxflash; diff --git a/libsnes/snes/chip/bsx/satellaview/satellaview.cpp b/libsnes/snes/chip/bsx/satellaview/satellaview.cpp new file mode 100644 index 0000000..a8fc4cc --- /dev/null +++ b/libsnes/snes/chip/bsx/satellaview/satellaview.cpp @@ -0,0 +1,144 @@ +#ifdef BSX_CPP + +BSXSatellaview bsxsatellaview; + +void BSXSatellaview::init() { +} + +void BSXSatellaview::load() { + function reader(&BSXSatellaview::mmio_read, &bsxsatellaview); + function writer(&BSXSatellaview::mmio_write, &bsxsatellaview); + bus.map(Bus::MapMode::Direct, 0x00, 0x3f, 0x2188, 0x219f, reader, writer); + bus.map(Bus::MapMode::Direct, 0x80, 0xbf, 0x2188, 0x219f, reader, writer); +} + +void BSXSatellaview::unload() { +} + +void BSXSatellaview::power() { +} + +void BSXSatellaview::reset() { + memset(®s, 0x00, sizeof regs); +} + +uint8 BSXSatellaview::mmio_read(unsigned addr) { + addr &= 0xffff; + + switch(addr) { + case 0x2188: return regs.r2188; + case 0x2189: return regs.r2189; + case 0x218a: return regs.r218a; + case 0x218c: return regs.r218c; + case 0x218e: return regs.r218e; + case 0x218f: return regs.r218f; + case 0x2190: return regs.r2190; + + case 0x2192: { + unsigned counter = regs.r2192_counter++; + if(regs.r2192_counter >= 18) regs.r2192_counter = 0; + + if(counter == 0) { + time_t rawtime; + rawtime = SNES::interface->currentTime(); + tm *t = localtime(&rawtime); + + regs.r2192_hour = t->tm_hour; + regs.r2192_minute = t->tm_min; + regs.r2192_second = t->tm_sec; + } + + switch(counter) { + case 0: return 0x00; //??? + case 1: return 0x00; //??? + case 2: return 0x00; //??? + case 3: return 0x00; //??? + case 4: return 0x00; //??? + case 5: return 0x01; + case 6: return 0x01; + case 7: return 0x00; + case 8: return 0x00; + case 9: return 0x00; + case 10: return regs.r2192_second; + case 11: return regs.r2192_minute; + case 12: return regs.r2192_hour; + case 13: return 0x00; //??? + case 14: return 0x00; //??? + case 15: return 0x00; //??? + case 16: return 0x00; //??? + case 17: return 0x00; //??? + } + } break; + + case 0x2193: return regs.r2193 & ~0x0c; + case 0x2194: return regs.r2194; + case 0x2196: return regs.r2196; + case 0x2197: return regs.r2197; + case 0x2199: return regs.r2199; + } + + return cpu.regs.mdr; +} + +void BSXSatellaview::mmio_write(unsigned addr, uint8 data) { + addr &= 0xffff; + + switch(addr) { + case 0x2188: { + regs.r2188 = data; + } break; + + case 0x2189: { + regs.r2189 = data; + } break; + + case 0x218a: { + regs.r218a = data; + } break; + + case 0x218b: { + regs.r218b = data; + } break; + + case 0x218c: { + regs.r218c = data; + } break; + + case 0x218e: { + regs.r218e = data; + } break; + + case 0x218f: { + regs.r218e >>= 1; + regs.r218e = regs.r218f - regs.r218e; + regs.r218f >>= 1; + } break; + + case 0x2191: { + regs.r2191 = data; + regs.r2192_counter = 0; + } break; + + case 0x2192: { + regs.r2190 = 0x80; + } break; + + case 0x2193: { + regs.r2193 = data; + } break; + + case 0x2194: { + regs.r2194 = data; + } break; + + case 0x2197: { + regs.r2197 = data; + } break; + + case 0x2199: { + regs.r2199 = data; + } break; + } +} + +#endif diff --git a/libsnes/snes/chip/bsx/satellaview/satellaview.hpp b/libsnes/snes/chip/bsx/satellaview/satellaview.hpp new file mode 100644 index 0000000..0f99acb --- /dev/null +++ b/libsnes/snes/chip/bsx/satellaview/satellaview.hpp @@ -0,0 +1,26 @@ +class BSXSatellaview { +public: + void init(); + void load(); + void unload(); + void power(); + void reset(); + + uint8 mmio_read(unsigned addr); + void mmio_write(unsigned addr, uint8 data); + +private: + struct { + uint8 r2188, r2189, r218a, r218b; + uint8 r218c, r218d, r218e, r218f; + uint8 r2190, r2191, r2192, r2193; + uint8 r2194, r2195, r2196, r2197; + uint8 r2198, r2199, r219a, r219b; + uint8 r219c, r219d, r219e, r219f; + + uint8 r2192_counter; + uint8 r2192_hour, r2192_minute, r2192_second; + } regs; +}; + +extern BSXSatellaview bsxsatellaview; diff --git a/libsnes/snes/chip/chip.hpp b/libsnes/snes/chip/chip.hpp new file mode 100644 index 0000000..09f5162 --- /dev/null +++ b/libsnes/snes/chip/chip.hpp @@ -0,0 +1,28 @@ +struct Coprocessor : Processor { + alwaysinline void step(unsigned clocks); + alwaysinline void synchronize_cpu(); +}; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void Coprocessor::step(unsigned clocks) { + clock += clocks * (uint64)cpu.frequency; +} + +void Coprocessor::synchronize_cpu() { + if(clock >= 0 && scheduler.sync.i != Scheduler::SynchronizeMode::All) co_switch(cpu.thread); +} diff --git a/libsnes/snes/chip/hitachidsp/hitachidsp.cpp b/libsnes/snes/chip/hitachidsp/hitachidsp.cpp new file mode 100755 index 0000000..6ccba2a --- /dev/null +++ b/libsnes/snes/chip/hitachidsp/hitachidsp.cpp @@ -0,0 +1,78 @@ +#include + +#define HITACHIDSP_CPP +namespace SNES { + +#include "memory.cpp" +#include "opcodes.cpp" +#include "registers.cpp" +#include "serialization.cpp" +HitachiDSP hitachidsp; + +void HitachiDSP::Enter() { hitachidsp.enter(); } + +void HitachiDSP::enter() { + while(true) { + if(scheduler.sync.i == Scheduler::SynchronizeMode::All) { + scheduler.exit(Scheduler::ExitReason::SynchronizeEvent); + } + + switch(state.i) { + case State::Idle: + step(1); + break; + case State::DMA: + for(unsigned n = 0; n < regs.dma_length; n++) { + bus.write(regs.dma_target + n, bus.read(regs.dma_source + n)); + step(2); + } + state.i = State::Idle; + break; + case State::Execute: + unsigned offset = regs.program_offset + regs.pc * 2; + opcode = bus_read(offset + 0) << 0; + opcode |= bus_read(offset + 1) << 8; + regs.pc = (regs.pc & 0xffff00) | ((regs.pc + 1) & 0x0000ff); + exec(); + step(1); + break; + } + + synchronize_cpu(); + } +} + +void HitachiDSP::init() { +} + +void HitachiDSP::load() { +} + +void HitachiDSP::unload() { +} + +void HitachiDSP::power() { +} + +void HitachiDSP::reset() { + create(HitachiDSP::Enter, frequency); + state.i = State::Idle; + + regs.n = 0; + regs.z = 0; + regs.c = 0; + + regs.dma_source = 0x000000; + regs.dma_length = 0x0000; + regs.dma_target = 0x000000; + regs.r1f48 = 0x00; + regs.program_offset = 0x000000; + regs.r1f4c = 0x00; + regs.page_number = 0x0000; + regs.program_counter = 0x00; + regs.r1f50 = 0x33; + regs.r1f51 = 0x00; + regs.r1f52 = 0x01; +} + +} diff --git a/libsnes/snes/chip/hitachidsp/hitachidsp.hpp b/libsnes/snes/chip/hitachidsp/hitachidsp.hpp new file mode 100755 index 0000000..e11243a --- /dev/null +++ b/libsnes/snes/chip/hitachidsp/hitachidsp.hpp @@ -0,0 +1,49 @@ +//Hitachi HG51B169 + +class HitachiDSP : public Coprocessor { +public: + unsigned frequency; +//uint16 programROM[2][256]; + uint24 dataROM[1024]; + uint8 dataRAM[3072]; + uint24 stack[8]; + uint16 opcode; + struct State { enum e { Idle, DMA, Execute } i; } state; + //enum class State : unsigned { Idle, DMA, Execute } state; + #include "registers.hpp" + + static void Enter(); + void enter(); + + void init(); + void load(); + void unload(); + void power(); + void reset(); + + //memory.cpp + uint8 bus_read(unsigned addr); + void bus_write(unsigned addr, uint8 data); + + uint8 rom_read(unsigned addr); + void rom_write(unsigned addr, uint8 data); + + uint8 dsp_read(unsigned addr); + void dsp_write(unsigned addr, uint8 data); + + //opcodes.cpp + void push(); + void pull(); + unsigned sa(); + unsigned ri(); + unsigned np(); + void exec(); + + //registers.cpp + unsigned reg_read(unsigned n) const; + void reg_write(unsigned n, unsigned data); + + void serialize(serializer&); +}; + +extern HitachiDSP hitachidsp; diff --git a/libsnes/snes/chip/hitachidsp/memory.cpp b/libsnes/snes/chip/hitachidsp/memory.cpp new file mode 100755 index 0000000..e5cf0be --- /dev/null +++ b/libsnes/snes/chip/hitachidsp/memory.cpp @@ -0,0 +1,133 @@ +#ifdef HITACHIDSP_CPP + +uint8 HitachiDSP::bus_read(unsigned addr) { + if((addr & 0x408000) == 0x008000) return bus.read(addr); + return 0x00; +} + +void HitachiDSP::bus_write(unsigned addr, uint8 data) { + if((addr & 0x40e000) == 0x006000) return bus.write(addr, data); +} + +uint8 HitachiDSP::rom_read(unsigned addr) { + if(co_active() == cpu.thread) { + if(state.i == State::Idle) return cartridge.rom.read(addr); + if((addr & 0x40ffe0) == 0x00ffe0) return regs.vector[addr & 0x1f]; + return cpu.regs.mdr; + } + if(co_active() == hitachidsp.thread) { + return cartridge.rom.read(addr); + } + return cpu.regs.mdr; +} + +void HitachiDSP::rom_write(unsigned addr, uint8 data) { +} + +uint8 HitachiDSP::dsp_read(unsigned addr) { + addr &= 0x1fff; + + //Data RAM + if((addr <= 0x0bff) || (addr >= 0x1000 && addr <= 0x1bff)) { + return dataRAM[addr & 0x0fff]; + } + + //MMIO + switch(addr) { + case 0x1f40: return regs.dma_source >> 0; + case 0x1f41: return regs.dma_source >> 8; + case 0x1f42: return regs.dma_source >> 16; + case 0x1f43: return regs.dma_length >> 0; + case 0x1f44: return regs.dma_length >> 8; + case 0x1f45: return regs.dma_target >> 0; + case 0x1f46: return regs.dma_target >> 8; + case 0x1f47: return regs.dma_target >> 16; + case 0x1f48: return regs.r1f48; + case 0x1f49: return regs.program_offset >> 0; + case 0x1f4a: return regs.program_offset >> 8; + case 0x1f4b: return regs.program_offset >> 16; + case 0x1f4c: return regs.r1f4c; + case 0x1f4d: return regs.page_number >> 0; + case 0x1f4e: return regs.page_number >> 8; + case 0x1f4f: return regs.program_counter; + case 0x1f50: return regs.r1f50; + case 0x1f51: return regs.r1f51; + case 0x1f52: return regs.r1f52; + case 0x1f53: case 0x1f54: case 0x1f55: case 0x1f56: + case 0x1f57: case 0x1f58: case 0x1f59: case 0x1f5a: + case 0x1f5b: case 0x1f5c: case 0x1f5d: case 0x1f5e: + case 0x1f5f: return ((state.i != State::Idle) << 6) | ((state.i == State::Idle) << 1); + } + + //Vector + if(addr >= 0x1f60 && addr <= 0x1f7f) { + return regs.vector[addr & 0x1f]; + } + + //GPRs + if((addr >= 0x1f80 && addr <= 0x1faf) || (addr >= 0x1fc0 && addr <= 0x1fef)) { + unsigned index = (addr & 0x3f) / 3; //0..15 + unsigned shift = ((addr & 0x3f) % 3) * 8; //0, 8, 16 + return regs.gpr[index] >> shift; + } + + return 0x00; +} + +void HitachiDSP::dsp_write(unsigned addr, uint8 data) { + addr &= 0x1fff; + + //Data RAM + if((addr <= 0x0bff) || (addr >= 0x1000 && addr <= 0x1bff)) { + dataRAM[addr & 0x0fff] = data; + return; + } + + //MMIO + switch(addr) { + case 0x1f40: regs.dma_source = (regs.dma_source & 0xffff00) | (data << 0); return; + case 0x1f41: regs.dma_source = (regs.dma_source & 0xff00ff) | (data << 8); return; + case 0x1f42: regs.dma_source = (regs.dma_source & 0x00ffff) | (data << 16); return; + case 0x1f43: regs.dma_length = (regs.dma_length & 0xff00) | (data << 0); return; + case 0x1f44: regs.dma_length = (regs.dma_length & 0x00ff) | (data << 8); return; + case 0x1f45: regs.dma_target = (regs.dma_target & 0xffff00) | (data << 0); return; + case 0x1f46: regs.dma_target = (regs.dma_target & 0xff00ff) | (data << 8); return; + case 0x1f47: regs.dma_target = (regs.dma_target & 0x00ffff) | (data << 16); + if(state.i == State::Idle) state.i = State::DMA; + return; + case 0x1f48: regs.r1f48 = data & 0x01; return; + case 0x1f49: regs.program_offset = (regs.program_offset & 0xffff00) | (data << 0); return; + case 0x1f4a: regs.program_offset = (regs.program_offset & 0xff00ff) | (data << 8); return; + case 0x1f4b: regs.program_offset = (regs.program_offset & 0x00ffff) | (data << 16); return; + case 0x1f4c: regs.r1f4c = data & 0x03; return; + case 0x1f4d: regs.page_number = (regs.page_number & 0x7f00) | ((data & 0xff) << 0); return; + case 0x1f4e: regs.page_number = (regs.page_number & 0x00ff) | ((data & 0x7f) << 8); return; + case 0x1f4f: regs.program_counter = data; + if(state.i == State::Idle) { + regs.pc = regs.page_number * 256 + regs.program_counter; + state.i = State::Execute; + } + return; + case 0x1f50: regs.r1f50 = data & 0x77; return; + case 0x1f51: regs.r1f51 = data & 0x01; return; + case 0x1f52: regs.r1f52 = data & 0x01; return; + } + + //Vector + if(addr >= 0x1f60 && addr <= 0x1f7f) { + regs.vector[addr & 0x1f] = data; + return; + } + + //GPRs + if((addr >= 0x1f80 && addr <= 0x1faf) || (addr >= 0x1fc0 && addr <= 0x1fef)) { + unsigned index = (addr & 0x3f) / 3; + switch((addr & 0x3f) % 3) { + case 0: regs.gpr[index] = (regs.gpr[index] & 0xffff00) | (data << 0); return; + case 1: regs.gpr[index] = (regs.gpr[index] & 0xff00ff) | (data << 8); return; + case 2: regs.gpr[index] = (regs.gpr[index] & 0x00ffff) | (data << 16); return; + } + } +} + +#endif diff --git a/libsnes/snes/chip/hitachidsp/opcodes.cpp b/libsnes/snes/chip/hitachidsp/opcodes.cpp new file mode 100755 index 0000000..f207274 --- /dev/null +++ b/libsnes/snes/chip/hitachidsp/opcodes.cpp @@ -0,0 +1,356 @@ +#ifdef HITACHIDSP_CPP + +void HitachiDSP::push() { + stack[7] = stack[6]; + stack[6] = stack[5]; + stack[5] = stack[4]; + stack[4] = stack[3]; + stack[3] = stack[2]; + stack[2] = stack[1]; + stack[1] = stack[0]; + stack[0] = regs.pc; +} + +void HitachiDSP::pull() { + regs.pc = stack[0]; + stack[0] = stack[1]; + stack[1] = stack[2]; + stack[2] = stack[3]; + stack[3] = stack[4]; + stack[4] = stack[5]; + stack[5] = stack[6]; + stack[6] = stack[7]; + stack[7] = 0x0000; +} + +//Shift-A: math opcodes can shift A register prior to ALU operation +unsigned HitachiDSP::sa() { + switch(opcode & 0x0300) { default: + case 0x0000: return regs.a << 0; + case 0x0100: return regs.a << 1; + case 0x0200: return regs.a << 8; + case 0x0300: return regs.a << 16; + } +} + +//Register-or-Immediate: most opcodes can load from a register or immediate +unsigned HitachiDSP::ri() { + if(opcode & 0x0400) return opcode & 0xff; + return reg_read(opcode & 0xff); +} + +//New-PC: determine jump target address; opcode.d9 = long jump flag (1 = yes) +unsigned HitachiDSP::np() { + if(opcode & 0x0200) return (regs.p << 8) | (opcode & 0xff); + return (regs.pc & 0xffff00) | (opcode & 0xff); +} + +void HitachiDSP::exec() { + if((opcode & 0xffff) == 0x0000) { + //0000 0000 0000 0000 + //nop + } + + else if((opcode & 0xdd00) == 0x0800) { + //00.0 10.0 .... .... + //jump i + if(opcode & 0x2000) push(); + regs.pc = np(); + } + + else if((opcode & 0xdd00) == 0x0c00) { + //00.0 11.0 .... .... + //jumpeq i + if(regs.z) { + if(opcode & 0x2000) push(); + regs.pc = np(); + } + } + + else if((opcode & 0xdd00) == 0x1000) { + //00.1 00.0 .... .... + //jumpge i + if(regs.c) { + if(opcode & 0x2000) push(); + regs.pc = np(); + } + } + + else if((opcode & 0xdd00) == 0x1400) { + //00.1 01.0 .... .... + //jumpmi i + if(regs.n) { + if(opcode & 0x2000) push(); + regs.pc = np(); + } + } + + else if((opcode & 0xffff) == 0x1c00) { + //0001 1100 0000 0000 + //loop? + } + + else if((opcode & 0xfffe) == 0x2500) { + //0010 0101 0000 000. + //skiplt/skipge + if(regs.c == (opcode & 1)) regs.pc++; + } + + else if((opcode & 0xfffe) == 0x2600) { + //0010 0110 0000 000. + //skipne/skipeq + if(regs.z == (opcode & 1)) regs.pc++; + } + + else if((opcode & 0xfffe) == 0x2700) { + //0010 0111 0000 000. + //skipmi/skippl + if(regs.n == (opcode & 1)) regs.pc++; + } + + else if((opcode & 0xffff) == 0x3c00) { + //0011 1100 0000 0000 + //ret + pull(); + } + + else if((opcode & 0xffff) == 0x4000) { + //0100 0000 0000 0000 + //rdbus + regs.busdata = bus_read(regs.busaddr++); + } + + else if((opcode & 0xf800) == 0x4800) { + //0100 1... .... .... + //cmpr a<= 0; + } + + else if((opcode & 0xf800) == 0x5000) { + //0101 0... .... .... + //cmp a<= 0; + } + + else if((opcode & 0xfb00) == 0x5900) { + //0101 1.01 .... .... + //sxb + regs.a = (int8)ri(); + } + + else if((opcode & 0xfb00) == 0x5a00) { + //0101 1.10 .... .... + //sxw + regs.a = (int16)ri(); + } + + else if((opcode & 0xfb00) == 0x6000) { + //0110 0.00 .... .... + //ld a,ri + regs.a = ri(); + } + + else if((opcode & 0xfb00) == 0x6100) { + //0110 0.01 .... .... + //ld ?,ri + } + + else if((opcode & 0xfb00) == 0x6300) { + //0110 0.11 .... .... + //ld p,ri + regs.p = ri(); + } + + else if((opcode & 0xfb00) == 0x6800) { + //0110 1.00 .... .... + //rdraml + uint24 target = ri() + (opcode & 0x0400 ? regs.ramaddr : (uint24)0); + if(target < 0xc00) regs.ramdata = (regs.ramdata & 0xffff00) | (dataRAM[target] << 0); + } + + else if((opcode & 0xfb00) == 0x6900) { + //0110 1.01 .... .... + //rdramh + uint24 target = ri() + (opcode & 0x0400 ? regs.ramaddr : (uint24)0); + if(target < 0xc00) regs.ramdata = (regs.ramdata & 0xff00ff) | (dataRAM[target] << 8); + } + + else if((opcode & 0xfb00) == 0x6a00) { + //0110 1.10 .... .... + //rdramb + uint24 target = ri() + (opcode & 0x0400 ? regs.ramaddr : (uint24)0); + if(target < 0xc00) regs.ramdata = (regs.ramdata & 0x00ffff) | (dataRAM[target] << 16); + } + + else if((opcode & 0xffff) == 0x7000) { + //0111 0000 0000 0000 + //rdrom + regs.romdata = dataROM[regs.a & 0x3ff]; + } + + else if((opcode & 0xff00) == 0x7c00) { + //0111 1100 .... .... + //ld pl,i + regs.p = (regs.p & 0xff00) | ((opcode & 0xff) << 0); + } + + else if((opcode & 0xff00) == 0x7d00) { + //0111 1101 .... .... + //ld ph,i + regs.p = (regs.p & 0x00ff) | ((opcode & 0xff) << 8); + } + + else if((opcode & 0xf800) == 0x8000) { + //1000 0... .... .... + //add a< 0xffffff; + } + + else if((opcode & 0xf800) == 0x8800) { + //1000 1... .... .... + //subr a<= 0; + } + + else if((opcode & 0xf800) == 0x9000) { + //1001 0... .... .... + //sub a<= 0; + } + + else if((opcode & 0xfb00) == 0x9800) { + //1001 1.00 .... .... + //mul a,ri + int64 x = (int24)regs.a; + int64 y = (int24)ri(); + x *= y; + regs.accl = x >> 0ull; + regs.acch = x >> 24ull; + regs.n = regs.acch & 0x800000; + regs.z = x == 0; + } + + else if((opcode & 0xf800) == 0xa800) { + //1010 1... .... .... + //xor a<> ri(); + regs.n = regs.a & 0x800000; + regs.z = regs.a == 0; + } + + else if((opcode & 0xfb00) == 0xc800) { + //1100 1.00 .... .... + //asr a,ri + regs.a = (int24)regs.a >> ri(); + regs.n = regs.a & 0x800000; + regs.z = regs.a == 0; + } + + else if((opcode & 0xfb00) == 0xd000) { + //1101 0.00 .... .... + //ror a,ri + uint24 length = ri(); + regs.a = (regs.a >> length) | (regs.a << (24 - length)); + regs.n = regs.a & 0x800000; + regs.z = regs.a == 0; + } + + else if((opcode & 0xfb00) == 0xd800) { + //1101 1.00 .... .... + //shl a,ri + regs.a = regs.a << ri(); + regs.n = regs.a & 0x800000; + regs.z = regs.a == 0; + } + + else if((opcode & 0xff00) == 0xe000) { + //1110 0000 .... .... + //st r,a + reg_write(opcode & 0xff, regs.a); + } + + else if((opcode & 0xfb00) == 0xe800) { + //1110 1.00 .... .... + //wrraml + uint24 target = ri() + (opcode & 0x0400 ? regs.ramaddr : (uint24)0); + if(target < 0xc00) dataRAM[target] = regs.ramdata >> 0; + } + + else if((opcode & 0xfb00) == 0xe900) { + //1110 1.01 .... .... + //wrramh + uint24 target = ri() + (opcode & 0x0400 ? regs.ramaddr : (uint24)0); + if(target < 0xc00) dataRAM[target] = regs.ramdata >> 8; + } + + else if((opcode & 0xfb00) == 0xea00) { + //1110 1.10 .... .... + //wrramb + uint24 target = ri() + (opcode & 0x0400 ? regs.ramaddr : (uint24)0); + if(target < 0xc00) dataRAM[target] = regs.ramdata >> 16; + } + + else if((opcode & 0xff00) == 0xf000) { + //1111 0000 .... .... + //swap a,r + uint24 source = reg_read(opcode & 0xff); + uint24 target = regs.a; + regs.a = source; + reg_write(opcode & 0xff, target); + } + + else if((opcode & 0xffff) == 0xfc00) { + //1111 1100 0000 0000 + //halt + state.i = State::Idle; + } + + else { + print("Hitachi DSP: invalid opcode @ ", hex<4>(regs.pc - 1), " = ", hex<4>(opcode), "\n"); + state.i = State::Idle; + } +} + +#endif diff --git a/libsnes/snes/chip/hitachidsp/registers.cpp b/libsnes/snes/chip/hitachidsp/registers.cpp new file mode 100755 index 0000000..396bdf3 --- /dev/null +++ b/libsnes/snes/chip/hitachidsp/registers.cpp @@ -0,0 +1,78 @@ +#ifdef HITACHIDSP_CPP + +unsigned HitachiDSP::reg_read(unsigned n) const { + switch(n) { + case 0x00: return regs.a; + case 0x01: return regs.acch; + case 0x02: return regs.accl; + case 0x03: return regs.busdata; + case 0x08: return regs.romdata; + case 0x0c: return regs.ramdata; + case 0x13: return regs.busaddr; + case 0x1c: return regs.ramaddr; + case 0x50: return 0x000000; + case 0x51: return 0xffffff; + case 0x52: return 0x00ff00; + case 0x53: return 0xff0000; + case 0x54: return 0x00ffff; + case 0x55: return 0xffff00; + case 0x56: return 0x800000; + case 0x57: return 0x7fffff; + case 0x58: return 0x008000; + case 0x59: return 0x007fff; + case 0x5a: return 0xff7fff; + case 0x5b: return 0xffff7f; + case 0x5c: return 0x010000; + case 0x5d: return 0xfeffff; + case 0x5e: return 0x000100; + case 0x5f: return 0x00feff; + case 0x60: return regs.gpr[ 0]; + case 0x61: return regs.gpr[ 1]; + case 0x62: return regs.gpr[ 2]; + case 0x63: return regs.gpr[ 3]; + case 0x64: return regs.gpr[ 4]; + case 0x65: return regs.gpr[ 5]; + case 0x66: return regs.gpr[ 6]; + case 0x67: return regs.gpr[ 7]; + case 0x68: return regs.gpr[ 8]; + case 0x69: return regs.gpr[ 9]; + case 0x6a: return regs.gpr[10]; + case 0x6b: return regs.gpr[11]; + case 0x6c: return regs.gpr[12]; + case 0x6d: return regs.gpr[13]; + case 0x6e: return regs.gpr[14]; + case 0x6f: return regs.gpr[15]; + } + return 0x000000; +} + +void HitachiDSP::reg_write(unsigned n, unsigned data) { + switch(n) { + case 0x00: regs.a = data; return; + case 0x01: regs.acch = data; return; + case 0x02: regs.accl = data; return; + case 0x03: regs.busdata = data; return; + case 0x08: regs.romdata = data; return; + case 0x0c: regs.ramdata = data; return; + case 0x13: regs.busaddr = data; return; + case 0x1c: regs.ramaddr = data; return; + case 0x60: regs.gpr[ 0] = data; return; + case 0x61: regs.gpr[ 1] = data; return; + case 0x62: regs.gpr[ 2] = data; return; + case 0x63: regs.gpr[ 3] = data; return; + case 0x64: regs.gpr[ 4] = data; return; + case 0x65: regs.gpr[ 5] = data; return; + case 0x66: regs.gpr[ 6] = data; return; + case 0x67: regs.gpr[ 7] = data; return; + case 0x68: regs.gpr[ 8] = data; return; + case 0x69: regs.gpr[ 9] = data; return; + case 0x6a: regs.gpr[10] = data; return; + case 0x6b: regs.gpr[11] = data; return; + case 0x6c: regs.gpr[12] = data; return; + case 0x6d: regs.gpr[13] = data; return; + case 0x6e: regs.gpr[14] = data; return; + case 0x6f: regs.gpr[15] = data; return; + } +} + +#endif diff --git a/libsnes/snes/chip/hitachidsp/registers.hpp b/libsnes/snes/chip/hitachidsp/registers.hpp new file mode 100755 index 0000000..73d2370 --- /dev/null +++ b/libsnes/snes/chip/hitachidsp/registers.hpp @@ -0,0 +1,31 @@ +struct Registers { + uint24 pc; + uint16 p; + bool n; + bool z; + bool c; + + uint24 a; + uint24 acch; + uint24 accl; + uint24 busdata; + uint24 romdata; + uint24 ramdata; + uint24 busaddr; + uint24 ramaddr; + uint24 gpr[16]; + + //MMIO + uint24 dma_source; //$1f40-$1f42 + uint24 dma_length; //$1f43-$1f44 + uint24 dma_target; //$1f45-$1f47 + uint8 r1f48; //$1f48 + uint24 program_offset; //$1f49-$1f4b + uint8 r1f4c; //$1f4c + uint16 page_number; //$1f4d-$1f4e + uint8 program_counter; //$1f4f + uint8 r1f50; //$1f50 + uint8 r1f51; //$1f51 + uint8 r1f52; //$1f52 + uint8 vector[32]; //$1f60-$1f7f +} regs; diff --git a/libsnes/snes/chip/hitachidsp/serialization.cpp b/libsnes/snes/chip/hitachidsp/serialization.cpp new file mode 100755 index 0000000..a3c71c2 --- /dev/null +++ b/libsnes/snes/chip/hitachidsp/serialization.cpp @@ -0,0 +1,44 @@ +#ifdef HITACHIDSP_CPP + +void HitachiDSP::serialize(serializer &s) { + Processor::serialize(s); + + s.array(dataRAM); + foreach(n, stack) s.integer(n); + s.integer(opcode); + + unsigned state_ = (unsigned)state.i; + s.integer(state_); + state.i = (State::e)state_; + + s.integer(regs.pc); + s.integer(regs.p); + s.integer(regs.n); + s.integer(regs.z); + s.integer(regs.c); + + s.integer(regs.a); + s.integer(regs.acch); + s.integer(regs.accl); + s.integer(regs.busdata); + s.integer(regs.romdata); + s.integer(regs.ramdata); + s.integer(regs.busaddr); + s.integer(regs.ramaddr); + foreach(n, regs.gpr) s.integer(n); + + s.integer(regs.dma_source); + s.integer(regs.dma_length); + s.integer(regs.dma_target); + s.integer(regs.r1f48); + s.integer(regs.program_offset); + s.integer(regs.r1f4c); + s.integer(regs.page_number); + s.integer(regs.program_counter); + s.integer(regs.r1f50); + s.integer(regs.r1f51); + s.integer(regs.r1f52); + s.array(regs.vector); +} + +#endif diff --git a/libsnes/snes/chip/icd2/icd2.cpp b/libsnes/snes/chip/icd2/icd2.cpp new file mode 100644 index 0000000..451201d --- /dev/null +++ b/libsnes/snes/chip/icd2/icd2.cpp @@ -0,0 +1,75 @@ +#include + +#define ICD2_CPP +namespace SNES { + +#include "interface/interface.cpp" +#include "mmio/mmio.cpp" +#include "serialization.cpp" +ICD2 icd2; + +void ICD2::Enter() { icd2.enter(); } + +void ICD2::enter() { + while(true) { + if(scheduler.sync.i == Scheduler::SynchronizeMode::All) { + GameBoy::system.runtosave(); + scheduler.exit(Scheduler::ExitReason::SynchronizeEvent); + } + + if(r6003 & 0x80) { + GameBoy::system.run(); + step(GameBoy::system.clocks_executed); + GameBoy::system.clocks_executed = 0; + } else { //DMG halted + audio.coprocessor_sample(0x0000, 0x0000); + step(1); + } + synchronize_cpu(); + } +} + +void ICD2::init() { +} + +void ICD2::load() { +} + +void ICD2::unload() { +} + +void ICD2::power() { + audio.coprocessor_enable(true); + audio.coprocessor_frequency(4 * 1024 * 1024); +} + +void ICD2::reset() { + create(ICD2::Enter, cpu.frequency / 5); + + r6000_ly = 0x00; + r6000_row = 0x00; + r6003 = 0x00; + r6004 = 0xff; + r6005 = 0xff; + r6006 = 0xff; + r6007 = 0xff; + for(unsigned n = 0; n < 16; n++) r7000[n] = 0x00; + r7800 = 0x0000; + mlt_req = 0; + + foreach(n, lcd.buffer) n = 0; + foreach(n, lcd.output) n = 0; + lcd.row = 0; + + packetsize = 0; + joyp_id = 3; + joyp15lock = 0; + joyp14lock = 0; + pulselock = true; + + GameBoy::interface = this; + GameBoy::system.init(); + GameBoy::system.power(); +} + +} diff --git a/libsnes/snes/chip/icd2/icd2.hpp b/libsnes/snes/chip/icd2/icd2.hpp new file mode 100644 index 0000000..511ca1c --- /dev/null +++ b/libsnes/snes/chip/icd2/icd2.hpp @@ -0,0 +1,24 @@ +class ICD2 : public GameBoy::Interface, public Coprocessor { +public: + unsigned revision; + + static void Enter(); + void enter(); + + void init(); + void load(); + void unload(); + void power(); + void reset(); + + uint8 read(unsigned addr); + void write(unsigned addr, uint8 data); + + void serialize(serializer&); + +private: + #include "interface/interface.hpp" + #include "mmio/mmio.hpp" +}; + +extern ICD2 icd2; diff --git a/libsnes/snes/chip/icd2/interface/interface.cpp b/libsnes/snes/chip/icd2/interface/interface.cpp new file mode 100644 index 0000000..c1849d7 --- /dev/null +++ b/libsnes/snes/chip/icd2/interface/interface.cpp @@ -0,0 +1,115 @@ +#ifdef ICD2_CPP + +//called on rendered lines 0-143 (not on Vblank lines 144-153) +void ICD2::lcdScanline() { + if((GameBoy::lcd.status.ly & 7) == 0) { + lcd.row = (lcd.row + 1) & 3; + } + + unsigned offset = (lcd.row * 160 * 8) + ((GameBoy::lcd.status.ly & 7) * 160); + memcpy(lcd.buffer + offset, GameBoy::lcd.screen + GameBoy::lcd.status.ly * 160, 160 * sizeof(uint16)); +} + +void ICD2::joypWrite(bool p15, bool p14) { + //joypad handling + if(p15 == 1 && p14 == 1) { + if(joyp15lock == 0 && joyp14lock == 0) { + joyp15lock = 1; + joyp14lock = 1; + joyp_id = (joyp_id + 1) & 3; + } + } + + if(p15 == 0 && p14 == 1) joyp15lock = 0; + if(p15 == 1 && p14 == 0) joyp14lock = 0; + + //packet handling + if(p15 == 0 && p14 == 0) { //pulse + pulselock = false; + packetoffset = 0; + bitoffset = 0; + strobelock = true; + packetlock = false; + return; + } + + if(pulselock) return; + + if(p15 == 1 && p14 == 1) { + strobelock = false; + return; + } + + if(strobelock) { + if(p15 == 1 || p14 == 1) { //malformed packet + packetlock = false; + pulselock = true; + bitoffset = 0; + packetoffset = 0; + } else { + return; + } + } + + //p15:1, p14:0 = 0 + //p15:0, p14:1 = 1 + bool bit = (p15 == 0); + strobelock = true; + + if(packetlock) { + if(p15 == 1 && p14 == 0) { + if((joyp_packet[0] >> 3) == 0x11) { + mlt_req = joyp_packet[1] & 3; + if(mlt_req == 2) mlt_req = 3; + joyp_id = 0; + } + + if(packetsize < 64) packet[packetsize++] = joyp_packet; + packetlock = false; + pulselock = true; + } + return; + } + + bitdata = (bit << 7) | (bitdata >> 1); + if(++bitoffset < 8) return; + + bitoffset = 0; + joyp_packet[packetoffset] = bitdata; + if(++packetoffset < 16) return; + packetlock = true; +} + +void ICD2::videoRefresh(const uint16_t *data) { +} + +void ICD2::audioSample(int16_t center, int16_t left, int16_t right) { + audio.coprocessor_sample(left, right); +} + +bool ICD2::inputPoll(unsigned id) { + GameBoy::cpu.status.mlt_req = joyp_id & mlt_req; + + unsigned data = 0x00; + switch(joyp_id & mlt_req) { + case 0: data = ~r6004; break; + case 1: data = ~r6005; break; + case 2: data = ~r6006; break; + case 3: data = ~r6007; break; + } + + switch(id) { + case GameBoy::Input::Start: return data & 0x80; + case GameBoy::Input::Select: return data & 0x40; + case GameBoy::Input::B: return data & 0x20; + case GameBoy::Input::A: return data & 0x10; + case GameBoy::Input::Down: return data & 0x08; + case GameBoy::Input::Up: return data & 0x04; + case GameBoy::Input::Left: return data & 0x02; + case GameBoy::Input::Right: return data & 0x01; + } + + return 0; +} + +#endif diff --git a/libsnes/snes/chip/icd2/interface/interface.hpp b/libsnes/snes/chip/icd2/interface/interface.hpp new file mode 100644 index 0000000..1c15783 --- /dev/null +++ b/libsnes/snes/chip/icd2/interface/interface.hpp @@ -0,0 +1,22 @@ +void lcdScanline(); +void joypWrite(bool p15, bool p14); +void videoRefresh(const uint16_t *data); +void audioSample(int16_t center, int16_t left, int16_t right); +bool inputPoll(unsigned id); + +struct Packet { + uint8 data[16]; + uint8& operator[](unsigned addr) { return data[addr & 15]; } +}; +Packet packet[64]; +unsigned packetsize; + +unsigned joyp_id; +bool joyp15lock; +bool joyp14lock; +bool pulselock; +bool strobelock; +bool packetlock; +Packet joyp_packet; +uint8 packetoffset; +uint8 bitdata, bitoffset; diff --git a/libsnes/snes/chip/icd2/mmio/mmio.cpp b/libsnes/snes/chip/icd2/mmio/mmio.cpp new file mode 100644 index 0000000..0835c02 --- /dev/null +++ b/libsnes/snes/chip/icd2/mmio/mmio.cpp @@ -0,0 +1,96 @@ +#ifdef ICD2_CPP + +//convert linear pixel data { 0x00, 0x55, 0xaa, 0xff } to 2bpp planar tiledata +void ICD2::render(const uint16 *source) { + memset(lcd.output, 0x00, 320 * sizeof(uint16)); + + for(unsigned y = 0; y < 8; y++) { + for(unsigned x = 0; x < 160; x++) { + unsigned pixel = *source++; + unsigned addr = y * 2 + (x / 8 * 16); + lcd.output[addr + 0] |= ((pixel & 1) >> 0) << (7 - (x & 7)); + lcd.output[addr + 1] |= ((pixel & 2) >> 1) << (7 - (x & 7)); + } + } +} + +uint8 ICD2::read(unsigned addr) { + addr &= 0xffff; + + //LY counter + if(addr == 0x6000) { + r6000_ly = GameBoy::lcd.status.ly; + r6000_row = lcd.row; + return r6000_ly; + } + + //command ready port + if(addr == 0x6002) { + bool data = packetsize > 0; + if(data) { + for(unsigned i = 0; i < 16; i++) r7000[i] = packet[0][i]; + packetsize--; + for(unsigned i = 0; i < packetsize; i++) packet[i] = packet[i + 1]; + } + return data; + } + + //ICD2 revision + if(addr == 0x600f) { + return 0x21; + } + + //command port + if((addr & 0xfff0) == 0x7000) { + return r7000[addr & 15]; + } + + //VRAM port + if(addr == 0x7800) { + uint8 data = lcd.output[r7800]; + r7800 = (r7800 + 1) % 320; + return data; + } + + return 0x00; +} + +void ICD2::write(unsigned addr, uint8 data) { + addr &= 0xffff; + + //VRAM port + if(addr == 0x6001) { + r6001 = data; + r7800 = 0; + + unsigned offset = (r6000_row - (4 - (r6001 - (r6000_ly & 3)))) & 3; + render(lcd.buffer + offset * 160 * 8); + + return; + } + + //control port + //d7: 0 = halt, 1 = reset + //d5,d4: 0 = 1-player, 1 = 2-player, 2 = 4-player, 3 = ??? + //d1,d0: 0 = frequency divider (clock rate adjust) + if(addr == 0x6003) { + if((r6003 & 0x80) == 0x00 && (data & 0x80) == 0x80) { + reset(); + } + switch(data & 3) { + case 0: frequency = cpu.frequency / 4; break; //fast (glitchy, even on real hardware) + case 1: frequency = cpu.frequency / 5; break; //normal + case 2: frequency = cpu.frequency / 7; break; //slow + case 3: frequency = cpu.frequency / 9; break; //very slow + } + r6003 = data; + return; + } + + if(addr == 0x6004) { r6004 = data; return; } //joypad 1 + if(addr == 0x6005) { r6005 = data; return; } //joypad 2 + if(addr == 0x6006) { r6006 = data; return; } //joypad 3 + if(addr == 0x6007) { r6007 = data; return; } //joypad 4 +} + +#endif diff --git a/libsnes/snes/chip/icd2/mmio/mmio.hpp b/libsnes/snes/chip/icd2/mmio/mmio.hpp new file mode 100644 index 0000000..fdd029d --- /dev/null +++ b/libsnes/snes/chip/icd2/mmio/mmio.hpp @@ -0,0 +1,19 @@ +void render(const uint16 *source); + +uint8 r6000_ly; //SGB BIOS' cache of LY +uint8 r6000_row; //SGB BIOS' cache of ROW +uint8 r6001; //VRAM conversion +uint8 r6003; //control port +uint8 r6004; //joypad 1 +uint8 r6005; //joypad 2 +uint8 r6006; //joypad 3 +uint8 r6007; //joypad 4 +uint8 r7000[16]; //JOYP packet data +unsigned r7800; //VRAM offset +uint8 mlt_req; //number of active joypads + +struct LCD { + uint16 buffer[4 * 160 * 8]; //four tile rows of linear video data + uint16 output[320]; //one tile row of 2bpp video data + unsigned row; //active ICD2 rendering tile row +} lcd; diff --git a/libsnes/snes/chip/icd2/serialization.cpp b/libsnes/snes/chip/icd2/serialization.cpp new file mode 100644 index 0000000..5c31448 --- /dev/null +++ b/libsnes/snes/chip/icd2/serialization.cpp @@ -0,0 +1,38 @@ +#ifdef ICD2_CPP + +void ICD2::serialize(serializer &s) { + Processor::serialize(s); + GameBoy::system.serialize_all(s); + + for(unsigned n = 0; n < 64; n++) s.array(packet[n].data); + s.integer(packetsize); + + s.integer(joyp_id); + s.integer(joyp15lock); + s.integer(joyp14lock); + s.integer(pulselock); + s.integer(strobelock); + s.integer(packetlock); + s.array(joyp_packet.data); + s.integer(packetoffset); + s.integer(bitdata); + s.integer(bitoffset); + + s.integer(r6000_ly); + s.integer(r6000_row); + s.integer(r6001); + s.integer(r6003); + s.integer(r6004); + s.integer(r6005); + s.integer(r6006); + s.integer(r6007); + s.array(r7000); + s.integer(r7800); + s.integer(mlt_req); + + s.array(lcd.buffer); + s.array(lcd.output); + s.integer(lcd.row); +} + +#endif diff --git a/libsnes/snes/chip/link/link.cpp b/libsnes/snes/chip/link/link.cpp new file mode 100644 index 0000000..1891f72 --- /dev/null +++ b/libsnes/snes/chip/link/link.cpp @@ -0,0 +1,59 @@ +#include + +#define LINK_HPP +namespace SNES { + +Link link; + +void Link::Enter() { link.enter(); } + +void Link::enter() { + while(true) { + cpu.synchronize_coprocessors(); + unsigned clocks = 1; + if(link_run) clocks = link_run(); + step(clocks); + synchronize_cpu(); + } +} + +void Link::init() { +} + +void Link::load() { + if(opened()) close(); + string basename = interface->path(Cartridge::Slot::Base, ""); + string name = program != "" ? program : notdir(basename); + string path = dir(basename); + if(open(name, path)) { + link_power = sym("link_power"); + link_reset = sym("link_reset"); + link_run = sym("link_run" ); + link_read = sym("link_read" ); + link_write = sym("link_write"); + } +} + +void Link::unload() { + if(opened()) close(); +} + +void Link::power() { + if(link_power) link_power(); +} + +void Link::reset() { + if(link_reset) link_reset(); + create(Link::Enter, frequency); +} + +uint8 Link::read(unsigned addr) { + if(link_read) return link_read(addr); + return cpu.regs.mdr; +} + +void Link::write(unsigned addr, uint8 data) { + if(link_write) return link_write(addr, data); +} + +} diff --git a/libsnes/snes/chip/link/link.hpp b/libsnes/snes/chip/link/link.hpp new file mode 100644 index 0000000..1898fa0 --- /dev/null +++ b/libsnes/snes/chip/link/link.hpp @@ -0,0 +1,24 @@ +class Link : public Coprocessor, public library { +public: + string program; + + static void Enter(); + void enter(); + void init(); + void load(); + void unload(); + void power(); + void reset(); + + uint8 read(unsigned addr); + void write(unsigned addr, uint8 data); + +private: + function link_power; + function link_reset; + function link_run; + function link_read; + function link_write; +}; + +extern Link link; diff --git a/libsnes/snes/chip/msu1/msu1.cpp b/libsnes/snes/chip/msu1/msu1.cpp new file mode 100644 index 0000000..d0bc448 --- /dev/null +++ b/libsnes/snes/chip/msu1/msu1.cpp @@ -0,0 +1,136 @@ +#include + +#define MSU1_CPP +namespace SNES { + +MSU1 msu1; + +#include "serialization.cpp" + +void MSU1::Enter() { msu1.enter(); } + +void MSU1::enter() { + while(true) { + if(scheduler.sync.i == Scheduler::SynchronizeMode::All) { + scheduler.exit(Scheduler::ExitReason::SynchronizeEvent); + } + + int16 left = 0, right = 0; + + if(mmio.audio_play) { + if(audiofile.open()) { + if(audiofile.end()) { + if(!mmio.audio_repeat) { + mmio.audio_play = false; + audiofile.seek(mmio.audio_offset = 8); + } else { + audiofile.seek(mmio.audio_offset = mmio.audio_loop_offset); + } + } else { + mmio.audio_offset += 4; + left = audiofile.readl(2); + right = audiofile.readl(2); + } + } else { + mmio.audio_play = false; + } + } + + left = sclamp<16>(static_cast((double)left * (double)mmio.audio_volume / 255.0)); + right = sclamp<16>(static_cast((double)right * (double)mmio.audio_volume / 255.0)); + + audio.coprocessor_sample(left, right); + step(1); + synchronize_cpu(); + } +} + +void MSU1::init() { +} + +void MSU1::load() { + if(datafile.open()) datafile.close(); + datafile.open(interface->path(Cartridge::Slot::Base, ".msu"), file::mode_read); +} + +void MSU1::unload() { + if(datafile.open()) datafile.close(); +} + +void MSU1::power() { + audio.coprocessor_enable(true); + audio.coprocessor_frequency(44100.0); +} + +void MSU1::reset() { + create(MSU1::Enter, 44100); + + mmio.data_offset = 0; + mmio.audio_offset = 0; + mmio.audio_track = 0; + mmio.audio_volume = 255; + mmio.data_busy = true; + mmio.audio_busy = true; + mmio.audio_repeat = false; + mmio.audio_play = false; +} + +uint8 MSU1::mmio_read(unsigned addr) { + switch(addr & 7) { + case 0: + return (mmio.data_busy << 7) + | (mmio.audio_busy << 6) + | (mmio.audio_repeat << 5) + | (mmio.audio_play << 4) + | (Revision << 0); + case 1: + if(mmio.data_busy) return 0x00; + mmio.data_offset++; + if(datafile.open()) return datafile.read(); + return 0x00; + case 2: return 'S'; + case 3: return '-'; + case 4: return 'M'; + case 5: return 'S'; + case 6: return 'U'; + case 7: return '0' + Revision; + } + throw; +} + +void MSU1::mmio_write(unsigned addr, uint8 data) { + switch(addr & 7) { + case 0: mmio.data_offset = (mmio.data_offset & 0xffffff00) | (data << 0); break; + case 1: mmio.data_offset = (mmio.data_offset & 0xffff00ff) | (data << 8); break; + case 2: mmio.data_offset = (mmio.data_offset & 0xff00ffff) | (data << 16); break; + case 3: mmio.data_offset = (mmio.data_offset & 0x00ffffff) | (data << 24); + if(datafile.open()) datafile.seek(mmio.data_offset); + mmio.data_busy = false; + break; + case 4: mmio.audio_track = (mmio.audio_track & 0xff00) | (data << 0); + case 5: mmio.audio_track = (mmio.audio_track & 0x00ff) | (data << 8); + if(audiofile.open()) audiofile.close(); + if(audiofile.open(interface->path(Cartridge::Slot::Base, string("-", (unsigned)mmio.audio_track, ".pcm")), file::mode_read)) { + uint32 header = audiofile.readm(4); + if(header != 0x4d535531) { //verify 'MSU1' header + audiofile.close(); + } else { + mmio.audio_offset = 8; + mmio.audio_loop_offset = 8 + audiofile.readl(4) * 4; + } + } + mmio.audio_busy = false; + mmio.audio_repeat = false; + mmio.audio_play = false; + break; + case 6: + mmio.audio_volume = data; + break; + case 7: + mmio.audio_repeat = data & 2; + mmio.audio_play = data & 1; + break; + } +} + +} diff --git a/libsnes/snes/chip/msu1/msu1.hpp b/libsnes/snes/chip/msu1/msu1.hpp new file mode 100644 index 0000000..25572ea --- /dev/null +++ b/libsnes/snes/chip/msu1/msu1.hpp @@ -0,0 +1,43 @@ +class MSU1 : public Coprocessor { +public: + static void Enter(); + void enter(); + void init(); + void load(); + void unload(); + void power(); + void reset(); + + uint8 mmio_read(unsigned addr); + void mmio_write(unsigned addr, uint8 data); + + void serialize(serializer&); + +private: + file datafile; + file audiofile; + + enum Flag { + DataBusy = 0x80, + AudioBusy = 0x40, + AudioRepeating = 0x20, + AudioPlaying = 0x10, + Revision = 0x01, + }; + + struct MMIO { + uint32 data_offset; + uint32 audio_offset; + uint32 audio_loop_offset; + + uint16 audio_track; + uint8 audio_volume; + + bool data_busy; + bool audio_busy; + bool audio_repeat; + bool audio_play; + } mmio; +}; + +extern MSU1 msu1; diff --git a/libsnes/snes/chip/msu1/serialization.cpp b/libsnes/snes/chip/msu1/serialization.cpp new file mode 100644 index 0000000..eb207e4 --- /dev/null +++ b/libsnes/snes/chip/msu1/serialization.cpp @@ -0,0 +1,29 @@ +#ifdef MSU1_CPP + +void MSU1::serialize(serializer &s) { + Processor::serialize(s); + + s.integer(mmio.data_offset); + s.integer(mmio.audio_offset); + s.integer(mmio.audio_loop_offset); + + s.integer(mmio.audio_track); + s.integer(mmio.audio_volume); + + s.integer(mmio.data_busy); + s.integer(mmio.audio_busy); + s.integer(mmio.audio_repeat); + s.integer(mmio.audio_play); + + if(datafile.open()) datafile.close(); + if(datafile.open(interface->path(Cartridge::Slot::Base, ".msu"), file::mode_read)) { + datafile.seek(mmio.data_offset); + } + + if(audiofile.open()) audiofile.close(); + if(audiofile.open(interface->path(Cartridge::Slot::Base, string("-", (unsigned)mmio.audio_track, ".pcm")), file::mode_read)) { + audiofile.seek(mmio.audio_offset); + } +} + +#endif diff --git a/libsnes/snes/chip/necdsp/memory.cpp b/libsnes/snes/chip/necdsp/memory.cpp new file mode 100644 index 0000000..0b11d59 --- /dev/null +++ b/libsnes/snes/chip/necdsp/memory.cpp @@ -0,0 +1,74 @@ +#ifdef NECDSP_CPP + +uint8 NECDSP::sr_read(unsigned) { + cpu.synchronize_coprocessors(); + return regs.sr >> 8; +} + +void NECDSP::sr_write(unsigned, uint8 data) { + cpu.synchronize_coprocessors(); +} + +uint8 NECDSP::dr_read(unsigned) { + cpu.synchronize_coprocessors(); + if(regs.sr.drc == 0) { + //16-bit + if(regs.sr.drs == 0) { + regs.sr.drs = 1; + return regs.dr >> 0; + } else { + regs.sr.rqm = 0; + regs.sr.drs = 0; + return regs.dr >> 8; + } + } else { + //8-bit + regs.sr.rqm = 0; + return regs.dr >> 0; + } +} + +void NECDSP::dr_write(unsigned, uint8 data) { + cpu.synchronize_coprocessors(); + if(regs.sr.drc == 0) { + //16-bit + if(regs.sr.drs == 0) { + regs.sr.drs = 1; + regs.dr = (regs.dr & 0xff00) | (data << 0); + } else { + regs.sr.rqm = 0; + regs.sr.drs = 0; + regs.dr = (data << 8) | (regs.dr & 0x00ff); + } + } else { + //8-bit + regs.sr.rqm = 0; + regs.dr = (regs.dr & 0xff00) | (data << 0); + } +} + +uint8 NECDSP::dp_read(unsigned addr) { + cpu.synchronize_coprocessors(); + bool hi = addr & 1; + addr = (addr >> 1) & 2047; + + if(hi == false) { + return dataRAM[addr] >> 0; + } else { + return dataRAM[addr] >> 8; + } +} + +void NECDSP::dp_write(unsigned addr, uint8 data) { + cpu.synchronize_coprocessors(); + bool hi = addr & 1; + addr = (addr >> 1) & 2047; + + if(hi == false) { + dataRAM[addr] = (dataRAM[addr] & 0xff00) | (data << 0); + } else { + dataRAM[addr] = (dataRAM[addr] & 0x00ff) | (data << 8); + } +} + +#endif diff --git a/libsnes/snes/chip/necdsp/necdsp.cpp b/libsnes/snes/chip/necdsp/necdsp.cpp new file mode 100644 index 0000000..2a42d2f --- /dev/null +++ b/libsnes/snes/chip/necdsp/necdsp.cpp @@ -0,0 +1,300 @@ +#include + +#define NECDSP_CPP +namespace SNES { + +#include "memory.cpp" +#include "serialization.cpp" +NECDSP necdsp; + +void NECDSP::Enter() { necdsp.enter(); } + +void NECDSP::enter() { + while(true) { + if(scheduler.sync.i == Scheduler::SynchronizeMode::All) { + scheduler.exit(Scheduler::ExitReason::SynchronizeEvent); + } + + uint24 opcode = programROM[regs.pc++]; + switch(opcode >> 22) { + case 0: exec_op(opcode); break; + case 1: exec_rt(opcode); break; + case 2: exec_jp(opcode); break; + case 3: exec_ld(opcode); break; + } + + int32 result = (int32)regs.k * regs.l; //sign + 30-bit result + regs.m = result >> 15; //store sign + top 15-bits + regs.n = result << 1; //store low 15-bits + zero + + step(1); + synchronize_cpu(); + } +} + +void NECDSP::exec_op(uint24 opcode) { + uint2 pselect = opcode >> 20; //P select + uint4 alu = opcode >> 16; //ALU operation mode + uint1 asl = opcode >> 15; //accumulator select + uint2 dpl = opcode >> 13; //DP low modify + uint4 dphm = opcode >> 9; //DP high XOR modify + uint1 rpdcr = opcode >> 8; //RP decrement + uint4 src = opcode >> 4; //move source + uint4 dst = opcode >> 0; //move destination + + uint16 idb; + switch(src) { + case 0: idb = regs.trb; break; + case 1: idb = regs.a; break; + case 2: idb = regs.b; break; + case 3: idb = regs.tr; break; + case 4: idb = regs.dp; break; + case 5: idb = regs.rp; break; + case 6: idb = dataROM[regs.rp]; break; + case 7: idb = 0x8000 - regs.flaga.s1; break; + case 8: idb = regs.dr; regs.sr.rqm = 1; break; + case 9: idb = regs.dr; break; + case 10: idb = regs.sr; break; + case 11: idb = regs.si; break; //MSB + case 12: idb = regs.si; break; //LSB + case 13: idb = regs.k; break; + case 14: idb = regs.l; break; + case 15: idb = dataRAM[regs.dp]; break; + } + + if(alu) { + uint16 p, q, r; + Flag flag; + bool c; + + switch(pselect) { + case 0: p = dataRAM[regs.dp]; break; + case 1: p = idb; break; + case 2: p = regs.m; break; + case 3: p = regs.n; break; + } + + switch(asl) { + case 0: q = regs.a; flag = regs.flaga; c = regs.flagb.c; break; + case 1: q = regs.b; flag = regs.flagb; c = regs.flaga.c; break; + } + + switch(alu) { + case 1: r = q | p; break; //OR + case 2: r = q & p; break; //AND + case 3: r = q ^ p; break; //XOR + case 4: r = q - p; break; //SUB + case 5: r = q + p; break; //ADD + case 6: r = q - p - c; break; //SBB + case 7: r = q + p + c; break; //ADC + case 8: r = q - 1; p = 1; break; //DEC + case 9: r = q + 1; p = 1; break; //INC + case 10: r = ~q; break; //CMP + case 11: r = (q >> 1) | (q & 0x8000); break; //SHR1 (ASR) + case 12: r = (q << 1) | c; break; //SHL1 (ROL) + case 13: r = (q << 2) | 3; break; //SHL2 + case 14: r = (q << 4) | 15; break; //SHL4 + case 15: r = (q << 8) | (q >> 8); break; //XCHG + } + + flag.s0 = (r & 0x8000); + flag.z = (r == 0); + + switch(alu) { + case 1: case 2: case 3: case 10: case 13: case 14: case 15: { + flag.c = 0; + flag.ov0 = 0; + flag.ov1 = 0; + break; + } + case 4: case 5: case 6: case 7: case 8: case 9: { + if(alu & 1) { + //addition + flag.ov0 = (q ^ r) & ~(q ^ p) & 0x8000; + flag.c = (r < q); + } else { + //subtraction + flag.ov0 = (q ^ r) & (q ^ p) & 0x8000; + flag.c = (r > q); + } + if(flag.ov0) { + flag.s1 = flag.ov1 ^ !(r & 0x8000); + flag.ov1 = !flag.ov1; + } + break; + } + case 11: { + flag.c = q & 1; + flag.ov0 = 0; + flag.ov1 = 0; + break; + } + case 12: { + flag.c = q >> 15; + flag.ov0 = 0; + flag.ov1 = 0; + break; + } + } + + switch(asl) { + case 0: regs.a = r; regs.flaga = flag; break; + case 1: regs.b = r; regs.flagb = flag; break; + } + } + + exec_ld((idb << 6) + dst); + + switch(dpl) { + case 1: regs.dp = (regs.dp & 0xf0) + ((regs.dp + 1) & 0x0f); break; //DPINC + case 2: regs.dp = (regs.dp & 0xf0) + ((regs.dp - 1) & 0x0f); break; //DPDEC + case 3: regs.dp = (regs.dp & 0xf0); break; //DPCLR + } + + regs.dp ^= dphm << 4; + + if(rpdcr) regs.rp--; +} + +void NECDSP::exec_rt(uint24 opcode) { + exec_op(opcode); + regs.pc = regs.stack[--regs.sp]; +} + +void NECDSP::exec_jp(uint24 opcode) { + uint9 brch = opcode >> 13; //branch + uint11 na = opcode >> 2; //next address + uint2 bank = opcode >> 0; //bank address + + uint14 jp = (regs.pc & 0x2000) | (bank << 11) | (na << 0); + + switch(brch) { + case 0x000: regs.pc = regs.so; return; //JMPSO + + case 0x080: if(regs.flaga.c == 0) regs.pc = jp; return; //JNCA + case 0x082: if(regs.flaga.c == 1) regs.pc = jp; return; //JCA + case 0x084: if(regs.flagb.c == 0) regs.pc = jp; return; //JNCB + case 0x086: if(regs.flagb.c == 1) regs.pc = jp; return; //JCB + + case 0x088: if(regs.flaga.z == 0) regs.pc = jp; return; //JNZA + case 0x08a: if(regs.flaga.z == 1) regs.pc = jp; return; //JZA + case 0x08c: if(regs.flagb.z == 0) regs.pc = jp; return; //JNZB + case 0x08e: if(regs.flagb.z == 1) regs.pc = jp; return; //JZB + + case 0x090: if(regs.flaga.ov0 == 0) regs.pc = jp; return; //JNOVA0 + case 0x092: if(regs.flaga.ov0 == 1) regs.pc = jp; return; //JOVA0 + case 0x094: if(regs.flagb.ov0 == 0) regs.pc = jp; return; //JNOVB0 + case 0x096: if(regs.flagb.ov0 == 1) regs.pc = jp; return; //JOVB0 + + case 0x098: if(regs.flaga.ov1 == 0) regs.pc = jp; return; //JNOVA1 + case 0x09a: if(regs.flaga.ov1 == 1) regs.pc = jp; return; //JOVA1 + case 0x09c: if(regs.flagb.ov1 == 0) regs.pc = jp; return; //JNOVB1 + case 0x09e: if(regs.flagb.ov1 == 1) regs.pc = jp; return; //JOVB1 + + case 0x0a0: if(regs.flaga.s0 == 0) regs.pc = jp; return; //JNSA0 + case 0x0a2: if(regs.flaga.s0 == 1) regs.pc = jp; return; //JSA0 + case 0x0a4: if(regs.flagb.s0 == 0) regs.pc = jp; return; //JNSB0 + case 0x0a6: if(regs.flagb.s0 == 1) regs.pc = jp; return; //JSB0 + + case 0x0a8: if(regs.flaga.s1 == 0) regs.pc = jp; return; //JNSA1 + case 0x0aa: if(regs.flaga.s1 == 1) regs.pc = jp; return; //JSA1 + case 0x0ac: if(regs.flagb.s1 == 0) regs.pc = jp; return; //JNSB1 + case 0x0ae: if(regs.flagb.s1 == 1) regs.pc = jp; return; //JSB1 + + case 0x0b0: if((regs.dp & 0x0f) == 0x00) regs.pc = jp; return; //JDPL0 + case 0x0b1: if((regs.dp & 0x0f) != 0x00) regs.pc = jp; return; //JDPLN0 + case 0x0b2: if((regs.dp & 0x0f) == 0x0f) regs.pc = jp; return; //JDPLF + case 0x0b3: if((regs.dp & 0x0f) != 0x0f) regs.pc = jp; return; //JDPLNF + + case 0x0bc: if(regs.sr.rqm == 0) regs.pc = jp; return; //JNRQM + case 0x0be: if(regs.sr.rqm == 1) regs.pc = jp; return; //JRQM + + case 0x100: regs.pc = jp & ~0x2000; return; //LJMP + case 0x101: regs.pc = jp | 0x2000; return; //HJMP + + case 0x140: regs.stack[regs.sp++] = regs.pc; regs.pc = jp & ~0x2000; return; //LCALL + case 0x141: regs.stack[regs.sp++] = regs.pc; regs.pc = jp | 0x2000; return; //HCALL + } +} + +void NECDSP::exec_ld(uint24 opcode) { + uint16 id = opcode >> 6; //immediate data + uint4 dst = opcode >> 0; //destination + + switch(dst) { + case 0: break; + case 1: regs.a = id; break; + case 2: regs.b = id; break; + case 3: regs.tr = id; break; + case 4: regs.dp = id; break; + case 5: regs.rp = id; break; + case 6: regs.dr = id; regs.sr.rqm = 1; break; + case 7: regs.sr = (regs.sr & 0x907c) | (id & ~0x907c); break; + case 8: regs.so = id; break; //LSB + case 9: regs.so = id; break; //MSB + case 10: regs.k = id; break; + case 11: regs.k = id; regs.l = dataROM[regs.rp]; break; + case 12: regs.l = id; regs.k = dataRAM[regs.dp | 0x40]; break; + case 13: regs.l = id; break; + case 14: regs.trb = id; break; + case 15: dataRAM[regs.dp] = id; break; + } +} + +void NECDSP::init() { +} + +void NECDSP::load() { + if(revision.i == Revision::uPD96050) { + cartridge.nvram.append(Cartridge::NonVolatileRAM( ".nec", (uint8_t*)dataRAM, 4096 )); + } +} + +void NECDSP::unload() { +} + +void NECDSP::power() { + if(revision.i == Revision::uPD7725) { + regs.pc.bits(11); + regs.rp.bits(10); + regs.dp.bits( 8); + } + + if(revision.i == Revision::uPD96050) { + regs.pc.bits(14); + regs.rp.bits(11); + regs.dp.bits(11); + } +} + +void NECDSP::reset() { + create(NECDSP::Enter, frequency); + + for(unsigned n = 0; n < 16; n++) regs.stack[n] = 0x0000; + regs.pc = 0x0000; + regs.rp = 0x0000; + regs.dp = 0x0000; + regs.sp = 0x0; + regs.k = 0x0000; + regs.l = 0x0000; + regs.m = 0x0000; + regs.n = 0x0000; + regs.a = 0x0000; + regs.b = 0x0000; + regs.flaga = 0x00; + regs.flagb = 0x00; + regs.tr = 0x0000; + regs.trb = 0x0000; + regs.sr = 0x0000; + regs.dr = 0x0000; + regs.si = 0x0000; + regs.so = 0x0000; +} + +NECDSP::NECDSP() { +} + +NECDSP::~NECDSP() { +} + +} diff --git a/libsnes/snes/chip/necdsp/necdsp.hpp b/libsnes/snes/chip/necdsp/necdsp.hpp new file mode 100644 index 0000000..0b8ff1b --- /dev/null +++ b/libsnes/snes/chip/necdsp/necdsp.hpp @@ -0,0 +1,50 @@ +//NEC uPD7725 +//NEC uPD96050 + +class NECDSP : public Coprocessor { +public: + //enum class Revision : unsigned { uPD7725, uPD96050 } revision; + struct Revision { enum e { uPD7725, uPD96050 } i; } revision; + unsigned frequency; + + #include "registers.hpp" + + uint24 programROM[16384]; + uint16 dataROM[2048]; + uint16 dataRAM[2048]; + + unsigned programROMSize; + unsigned dataROMSize; + unsigned dataRAMSize; + + static void Enter(); + void enter(); + + void exec_op(uint24 opcode); + void exec_rt(uint24 opcode); + void exec_jp(uint24 opcode); + void exec_ld(uint24 opcode); + + string disassemble(uint14 ip); + + uint8 sr_read(unsigned); + void sr_write(unsigned, uint8 data); + + uint8 dr_read(unsigned); + void dr_write(unsigned, uint8 data); + + uint8 dp_read(unsigned addr); + void dp_write(unsigned addr, uint8 data); + + void init(); + void load(); + void unload(); + void power(); + void reset(); + + void serialize(serializer&); + NECDSP(); + ~NECDSP(); +}; + +extern NECDSP necdsp; diff --git a/libsnes/snes/chip/necdsp/registers.hpp b/libsnes/snes/chip/necdsp/registers.hpp new file mode 100644 index 0000000..ff5de61 --- /dev/null +++ b/libsnes/snes/chip/necdsp/registers.hpp @@ -0,0 +1,51 @@ +struct Flag { + bool s1, s0, c, z, ov1, ov0; + + inline operator unsigned() const { + return (s1 << 5) + (s0 << 4) + (c << 3) + (z << 2) + (ov1 << 1) + (ov0 << 0); + } + + inline unsigned operator=(unsigned d) { + s1 = d & 0x20; s0 = d & 0x10; c = d & 0x08; z = d & 0x04; ov1 = d & 0x02; ov0 = d & 0x01; + return d; + } +}; + +struct Status { + bool rqm, usf1, usf0, drs, dma, drc, soc, sic, ei, p1, p0; + + inline operator unsigned() const { + return (rqm << 15) + (usf1 << 14) + (usf0 << 13) + (drs << 12) + + (dma << 11) + (drc << 10) + (soc << 9) + (sic << 8) + + (ei << 7) + (p1 << 1) + (p0 << 0); + } + + inline unsigned operator=(unsigned d) { + rqm = d & 0x8000; usf1 = d & 0x4000; usf0 = d & 0x2000; drs = d & 0x1000; + dma = d & 0x0800; drc = d & 0x0400; soc = d & 0x0200; sic = d & 0x0100; + ei = d & 0x0080; p1 = d & 0x0002; p0 = d & 0x0001; + return d; + } +}; + +struct Regs { + uint16 stack[16]; //LIFO + varuint pc; //program counter + varuint rp; //ROM pointer + varuint dp; //data pointer + uint4 sp; //stack pointer + int16 k; + int16 l; + int16 m; + int16 n; + int16 a; //accumulator + int16 b; //accumulator + Flag flaga; + Flag flagb; + uint16 tr; //temporary register + uint16 trb; //temporary register + Status sr; //status register + uint16 dr; //data register + uint16 si; + uint16 so; +} regs; diff --git a/libsnes/snes/chip/necdsp/serialization.cpp b/libsnes/snes/chip/necdsp/serialization.cpp new file mode 100644 index 0000000..a06510f --- /dev/null +++ b/libsnes/snes/chip/necdsp/serialization.cpp @@ -0,0 +1,55 @@ +#ifdef NECDSP_CPP + +void NECDSP::serialize(serializer &s) { + Processor::serialize(s); + + s.array(dataRAM); + + s.array(regs.stack); + s.integer(regs.pc); + s.integer(regs.rp); + s.integer(regs.dp); + s.integer(regs.sp); + + s.integer(regs.k); + s.integer(regs.l); + s.integer(regs.m); + s.integer(regs.n); + s.integer(regs.a); + s.integer(regs.b); + + s.integer(regs.flaga.s1); + s.integer(regs.flaga.s0); + s.integer(regs.flaga.c); + s.integer(regs.flaga.z); + s.integer(regs.flaga.ov1); + s.integer(regs.flaga.ov0); + + s.integer(regs.flagb.s1); + s.integer(regs.flagb.s0); + s.integer(regs.flagb.c); + s.integer(regs.flagb.z); + s.integer(regs.flagb.ov1); + s.integer(regs.flagb.ov0); + + s.integer(regs.tr); + s.integer(regs.trb); + + s.integer(regs.sr.rqm); + s.integer(regs.sr.usf1); + s.integer(regs.sr.usf0); + s.integer(regs.sr.drs); + s.integer(regs.sr.dma); + s.integer(regs.sr.drc); + s.integer(regs.sr.soc); + s.integer(regs.sr.sic); + s.integer(regs.sr.ei); + s.integer(regs.sr.p1); + s.integer(regs.sr.p0); + + s.integer(regs.dr); + s.integer(regs.si); + s.integer(regs.so); +} + +#endif diff --git a/libsnes/snes/chip/nss/nss.cpp b/libsnes/snes/chip/nss/nss.cpp new file mode 100644 index 0000000..1812087 --- /dev/null +++ b/libsnes/snes/chip/nss/nss.cpp @@ -0,0 +1,41 @@ +#include + +#define NSS_CPP +namespace SNES { + +NSS nss; + +void NSS::init() { +} + +void NSS::load() { + dip = 0x0000; + function read_func(&NSS::read, this); + function write_func(&NSS::write, this); + bus.map(Bus::MapMode::Direct, 0x00, 0x3f, 0x4100, 0x4101, read_func, write_func); + bus.map(Bus::MapMode::Direct, 0x80, 0xbf, 0x4100, 0x4101, read_func, write_func); +} + +void NSS::unload() { +} + +void NSS::power() { +} + +void NSS::reset() { +} + +void NSS::set_dip(uint16 dip) { + this->dip = dip; +} + +uint8 NSS::read(unsigned addr) { + if((addr & 0x40ffff) == 0x004100) return dip >> 0; + if((addr & 0x40ffff) == 0x004101) return dip >> 8; + return cpu.regs.mdr; +} + +void NSS::write(unsigned addr, uint8 data) { +} + +} diff --git a/libsnes/snes/chip/nss/nss.hpp b/libsnes/snes/chip/nss/nss.hpp new file mode 100644 index 0000000..3cab46d --- /dev/null +++ b/libsnes/snes/chip/nss/nss.hpp @@ -0,0 +1,17 @@ +class NSS { +public: + void init(); + void load(); + void unload(); + void power(); + void reset(); + + void set_dip(uint16 dip); + uint8 read(unsigned addr); + void write(unsigned addr, uint8 data); + +private: + uint16 dip; +}; + +extern NSS nss; diff --git a/libsnes/snes/chip/obc1/obc1.cpp b/libsnes/snes/chip/obc1/obc1.cpp new file mode 100644 index 0000000..8d7d337 --- /dev/null +++ b/libsnes/snes/chip/obc1/obc1.cpp @@ -0,0 +1,79 @@ +#include + +#define OBC1_CPP +namespace SNES { + +#include "serialization.cpp" +OBC1 obc1; + +void OBC1::init() { +} + +void OBC1::load() { +} + +void OBC1::unload() { +} + +void OBC1::power() { +} + +void OBC1::reset() { + status.baseptr = (ram_read(0x1ff5) & 1) ? 0x1800 : 0x1c00; + status.address = (ram_read(0x1ff6) & 0x7f); + status.shift = (ram_read(0x1ff6) & 3) << 1; +} + +uint8 OBC1::read(unsigned addr) { + addr &= 0x1fff; + + switch(addr) { + case 0x1ff0: return ram_read(status.baseptr + (status.address << 2) + 0); + case 0x1ff1: return ram_read(status.baseptr + (status.address << 2) + 1); + case 0x1ff2: return ram_read(status.baseptr + (status.address << 2) + 2); + case 0x1ff3: return ram_read(status.baseptr + (status.address << 2) + 3); + case 0x1ff4: return ram_read(status.baseptr + (status.address >> 2) + 0x200); + } + + return ram_read(addr); +} + +void OBC1::write(unsigned addr, uint8 data) { + addr &= 0x1fff; + + switch(addr) { + case 0x1ff0: ram_write(status.baseptr + (status.address << 2) + 0, data); return; + case 0x1ff1: ram_write(status.baseptr + (status.address << 2) + 1, data); return; + case 0x1ff2: ram_write(status.baseptr + (status.address << 2) + 2, data); return; + case 0x1ff3: ram_write(status.baseptr + (status.address << 2) + 3, data); return; + case 0x1ff4: { + uint8 temp = ram_read(status.baseptr + (status.address >> 2) + 0x200); + temp = (temp & ~(3 << status.shift)) | ((data & 3) << status.shift); + ram_write(status.baseptr + (status.address >> 2) + 0x200, temp); + } return; + case 0x1ff5: + status.baseptr = (data & 1) ? 0x1800 : 0x1c00; + ram_write(addr, data); + return; + case 0x1ff6: + status.address = (data & 0x7f); + status.shift = (data & 3) << 1; + ram_write(addr, data); + return; + case 0x1ff7: + ram_write(addr, data); + return; + } + + return ram_write(addr, data); +} + +uint8 OBC1::ram_read(unsigned addr) { + return cartridge.ram.read(addr & 0x1fff); +} + +void OBC1::ram_write(unsigned addr, uint8 data) { + cartridge.ram.write(addr & 0x1fff, data); +} + +} diff --git a/libsnes/snes/chip/obc1/obc1.hpp b/libsnes/snes/chip/obc1/obc1.hpp new file mode 100644 index 0000000..686adbc --- /dev/null +++ b/libsnes/snes/chip/obc1/obc1.hpp @@ -0,0 +1,25 @@ +class OBC1 { +public: + void init(); + void load(); + void unload(); + void power(); + void reset(); + + uint8 read(unsigned addr); + void write(unsigned addr, uint8 data); + + void serialize(serializer&); + +private: + uint8 ram_read(unsigned addr); + void ram_write(unsigned addr, uint8 data); + + struct { + uint16 address; + uint16 baseptr; + uint16 shift; + } status; +}; + +extern OBC1 obc1; diff --git a/libsnes/snes/chip/obc1/serialization.cpp b/libsnes/snes/chip/obc1/serialization.cpp new file mode 100644 index 0000000..3d61aaf --- /dev/null +++ b/libsnes/snes/chip/obc1/serialization.cpp @@ -0,0 +1,9 @@ +#ifdef OBC1_CPP + +void OBC1::serialize(serializer &s) { + s.integer(status.address); + s.integer(status.baseptr); + s.integer(status.shift); +} + +#endif diff --git a/libsnes/snes/chip/sa1/bus/bus.cpp b/libsnes/snes/chip/sa1/bus/bus.cpp new file mode 100644 index 0000000..a632551 --- /dev/null +++ b/libsnes/snes/chip/sa1/bus/bus.cpp @@ -0,0 +1,34 @@ +#ifdef SA1_CPP + +//ROM / RAM access from the S-CPU + +unsigned SA1::CPUIRAM::size() const { + return sa1.iram.size(); +} + +uint8 SA1::CPUIRAM::read(unsigned addr) { + cpu.synchronize_coprocessors(); + return sa1.iram.read(addr); +} + +void SA1::CPUIRAM::write(unsigned addr, uint8 data) { + cpu.synchronize_coprocessors(); + sa1.iram.write(addr, data); +} + +unsigned SA1::CPUBWRAM::size() const { + return cartridge.ram.size(); +} + +uint8 SA1::CPUBWRAM::read(unsigned addr) { + cpu.synchronize_coprocessors(); + if(dma) return sa1.dma_cc1_read(addr); + return cartridge.ram.read(addr); +} + +void SA1::CPUBWRAM::write(unsigned addr, uint8 data) { + cpu.synchronize_coprocessors(); + cartridge.ram.write(addr, data); +} + +#endif diff --git a/libsnes/snes/chip/sa1/bus/bus.hpp b/libsnes/snes/chip/sa1/bus/bus.hpp new file mode 100644 index 0000000..e3b42b9 --- /dev/null +++ b/libsnes/snes/chip/sa1/bus/bus.hpp @@ -0,0 +1,14 @@ +StaticRAM iram; + +struct CPUIRAM : Memory { + unsigned size() const; + alwaysinline uint8 read(unsigned); + alwaysinline void write(unsigned, uint8); +} cpuiram; + +struct CPUBWRAM : Memory { + unsigned size() const; + alwaysinline uint8 read(unsigned); + alwaysinline void write(unsigned, uint8); + bool dma; +} cpubwram; diff --git a/libsnes/snes/chip/sa1/dma/dma.cpp b/libsnes/snes/chip/sa1/dma/dma.cpp new file mode 100644 index 0000000..c929991 --- /dev/null +++ b/libsnes/snes/chip/sa1/dma/dma.cpp @@ -0,0 +1,139 @@ +#ifdef SA1_CPP + +//==================== +//direct data transfer +//==================== + +void SA1::dma_normal() { + while(mmio.dtc--) { + uint8 data = regs.mdr; + uint32 dsa = mmio.dsa++; + uint32 dda = mmio.dda++; + + //source and destination cannot be the same + if(mmio.sd == DMA::SourceBWRAM && mmio.dd == DMA::DestBWRAM) continue; + if(mmio.sd == DMA::SourceIRAM && mmio.dd == DMA::DestIRAM ) continue; + + switch(mmio.sd) { + case DMA::SourceROM: { + if((dsa & 0x408000) == 0x008000 || (dsa & 0xc00000) == 0xc00000) { + data = bus_read(dsa); + } + } break; + + case DMA::SourceBWRAM: { + if((dsa & 0x40e000) == 0x006000 || (dsa & 0xf00000) == 0x400000) { + data = bus_read(dsa); + } + } break; + + case DMA::SourceIRAM: { + data = iram.read(dsa & 0x07ff); + } break; + } + + switch((int)mmio.dd) { + case DMA::DestBWRAM: { + if((dda & 0x40e000) == 0x006000 || (dda & 0xf00000) == 0x400000) { + bus_write(dda, data); + } + } break; + + case DMA::DestIRAM: { + iram.write(dda & 0x07ff, data); + } break; + } + } + + mmio.dma_irqfl = true; + if(mmio.dma_irqen) mmio.dma_irqcl = 0; +} + +//((byte & 6) << 3) + (byte & 1) explanation: +//transforms a byte index (0-7) into a planar index: +//result[] = { 0, 1, 16, 17, 32, 33, 48, 49 }; +//works for 2bpp, 4bpp and 8bpp modes + +//=========================== +//type-1 character conversion +//=========================== + +void SA1::dma_cc1() { + cpubwram.dma = true; + mmio.chdma_irqfl = true; + if(mmio.chdma_irqen) { + mmio.chdma_irqcl = 0; + cpu.regs.irq = 1; + } +} + +uint8 SA1::dma_cc1_read(unsigned addr) { + //16 bytes/char (2bpp); 32 bytes/char (4bpp); 64 bytes/char (8bpp) + unsigned charmask = (1 << (6 - mmio.dmacb)) - 1; + + if((addr & charmask) == 0) { + //buffer next character to I-RAM + unsigned bpp = 2 << (2 - mmio.dmacb); + unsigned bpl = (8 << mmio.dmasize) >> mmio.dmacb; + unsigned bwmask = cartridge.ram.size() - 1; + unsigned tile = ((addr - mmio.dsa) & bwmask) >> (6 - mmio.dmacb); + unsigned ty = (tile >> mmio.dmasize); + unsigned tx = tile & ((1 << mmio.dmasize) - 1); + unsigned bwaddr = mmio.dsa + ty * 8 * bpl + tx * bpp; + + for(unsigned y = 0; y < 8; y++) { + uint64 data = 0; + for(unsigned byte = 0; byte < bpp; byte++) { + data |= (uint64)cartridge.ram.read((bwaddr + byte) & bwmask) << (byte << 3); + } + bwaddr += bpl; + + uint8 out[] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + for(unsigned x = 0; x < 8; x++) { + out[0] |= (data & 1) << (7 - x); data >>= 1; + out[1] |= (data & 1) << (7 - x); data >>= 1; + if(mmio.dmacb == 2) continue; + out[2] |= (data & 1) << (7 - x); data >>= 1; + out[3] |= (data & 1) << (7 - x); data >>= 1; + if(mmio.dmacb == 1) continue; + out[4] |= (data & 1) << (7 - x); data >>= 1; + out[5] |= (data & 1) << (7 - x); data >>= 1; + out[6] |= (data & 1) << (7 - x); data >>= 1; + out[7] |= (data & 1) << (7 - x); data >>= 1; + } + + for(unsigned byte = 0; byte < bpp; byte++) { + unsigned p = mmio.dda + (y << 1) + ((byte & 6) << 3) + (byte & 1); + iram.write(p & 0x07ff, out[byte]); + } + } + } + + return iram.read((mmio.dda + (addr & charmask)) & 0x07ff); +} + +//=========================== +//type-2 character conversion +//=========================== + +void SA1::dma_cc2() { + //select register file index (0-7 or 8-15) + const uint8 *brf = &mmio.brf[(dma.line & 1) << 3]; + unsigned bpp = 2 << (2 - mmio.dmacb); + unsigned addr = mmio.dda & 0x07ff; + addr &= ~((1 << (7 - mmio.dmacb)) - 1); + addr += (dma.line & 8) * bpp; + addr += (dma.line & 7) * 2; + + for(unsigned byte = 0; byte < bpp; byte++) { + uint8 output = 0; + for(unsigned bit = 0; bit < 8; bit++) { + output |= ((brf[bit] >> byte) & 1) << (7 - bit); + } + iram.write(addr + ((byte & 6) << 3) + (byte & 1), output); + } + + dma.line = (dma.line + 1) & 15; +} + +#endif diff --git a/libsnes/snes/chip/sa1/dma/dma.hpp b/libsnes/snes/chip/sa1/dma/dma.hpp new file mode 100644 index 0000000..4d3cff7 --- /dev/null +++ b/libsnes/snes/chip/sa1/dma/dma.hpp @@ -0,0 +1,11 @@ +struct DMA { + enum CDEN { DmaNormal = 0, DmaCharConversion = 1 }; + enum SD { SourceROM = 0, SourceBWRAM = 1, SourceIRAM = 2 }; + enum DD { DestIRAM = 0, DestBWRAM = 1 }; + unsigned line; +} dma; + +void dma_normal(); +void dma_cc1(); +uint8 dma_cc1_read(unsigned addr); +void dma_cc2(); diff --git a/libsnes/snes/chip/sa1/memory/memory.cpp b/libsnes/snes/chip/sa1/memory/memory.cpp new file mode 100644 index 0000000..1d66e7a --- /dev/null +++ b/libsnes/snes/chip/sa1/memory/memory.cpp @@ -0,0 +1,268 @@ +#ifdef SA1_CPP + +uint8 SA1::bus_read(unsigned addr) { + if((addr & 0x40fe00) == 0x002200) { //$00-3f|80-bf:2200-23ff + return mmio_read(addr); + } + + if((addr & 0x408000) == 0x008000) { //$00-3f|80-bf:8000-ffff + return mmc_read(addr); + } + + if((addr & 0xc00000) == 0xc00000) { //$c0-ff:0000-ffff + return mmc_read(addr); + } + + if((addr & 0x40e000) == 0x006000) { //$00-3f|80-bf:6000-7fff + return mmc_sa1_read(addr); + } + + if((addr & 0x40f800) == 0x000000) { //$00-3f|80-bf:0000-07ff + synchronize_cpu(); + return iram.read(addr & 2047); + } + + if((addr & 0x40f800) == 0x003000) { //$00-3f|80-bf:3000-37ff + synchronize_cpu(); + return iram.read(addr & 2047); + } + + if((addr & 0xf00000) == 0x400000) { //$40-4f:0000-ffff + synchronize_cpu(); + return cartridge.ram.read(addr & (cartridge.ram.size() - 1)); + } + + if((addr & 0xf00000) == 0x600000) { //$60-6f:0000-ffff + synchronize_cpu(); + return bitmap_read(addr & 0x0fffff); + } + + return 0; // FIXME: Can it reach here? +} + +void SA1::bus_write(unsigned addr, uint8 data) { + if((addr & 0x40fe00) == 0x002200) { //$00-3f|80-bf:2200-23ff + return mmio_write(addr, data); + } + + if((addr & 0x40e000) == 0x006000) { //$00-3f|80-bf:6000-7fff + return mmc_sa1_write(addr, data); + } + + if((addr & 0x40f800) == 0x000000) { //$00-3f|80-bf:0000-07ff + synchronize_cpu(); + return iram.write(addr & 2047, data); + } + + if((addr & 0x40f800) == 0x003000) { //$00-3f|80-bf:3000-37ff + synchronize_cpu(); + return iram.write(addr & 2047, data); + } + + if((addr & 0xf00000) == 0x400000) { //$40-4f:0000-ffff + synchronize_cpu(); + return cartridge.ram.write(addr & (cartridge.ram.size() - 1), data); + } + + if((addr & 0xf00000) == 0x600000) { //$60-6f:0000-ffff + synchronize_cpu(); + return bitmap_write(addr & 0x0fffff, data); + } +} + +//$230c (VDPL), $230d (VDPH) use this bus to read variable-length data. +//this is used both to keep VBR-reads from accessing MMIO registers, and +//to avoid syncing the S-CPU and SA-1*; as both chips are able to access +//these ports. +uint8 SA1::vbr_read(unsigned addr) { + if((addr & 0x408000) == 0x008000) { //$00-3f|80-bf:8000-ffff + return mmc_read(addr); + } + + if((addr & 0xc00000) == 0xc00000) { //$c0-ff:0000-ffff + return mmc_read(addr); + } + + if((addr & 0x40e000) == 0x006000) { //$00-3f|80-bf:6000-7fff + return cartridge.ram.read(addr & (cartridge.ram.size() - 1)); + } + + if((addr & 0xf00000) == 0x400000) { //$40-4f:0000-ffff + return cartridge.ram.read(addr & (cartridge.ram.size() - 1)); + } + + if((addr & 0x40f800) == 0x000000) { //$00-3f|80-bf:0000-07ff + return iram.read(addr & 2047); + } + + if((addr & 0x40f800) == 0x003000) { //$00-3f|80-bf:3000-37ff + return iram.read(addr & 0x2047); + } + + return 0; // FIXME: Can it reach here? +} + +//ROM, I-RAM and MMIO registers are accessed at ~10.74MHz (2 clock ticks) +//BW-RAM is accessed at ~5.37MHz (4 clock ticks) +//tick() == 2 clock ticks +//note: bus conflict delays are not emulated at this time + +void SA1::op_io() { + tick(); +} + +uint8 SA1::op_read(unsigned addr) { + tick(); + if(((addr & 0x40e000) == 0x006000) || ((addr & 0xd00000) == 0x400000)) tick(); + return bus_read(addr); +} + +void SA1::op_write(unsigned addr, uint8 data) { + tick(); + if(((addr & 0x40e000) == 0x006000) || ((addr & 0xd00000) == 0x400000)) tick(); + bus_write(addr, data); +} + +static uint8 sa1_read(unsigned addr) { + return cartridge.rom.read(bus.mirror(addr, cartridge.rom.size())); +} + +uint8 SA1::mmc_read(unsigned addr) { + if((addr & 0xffffe0) == 0x00ffe0) { + if(addr == 0xffea && sa1.mmio.cpu_nvsw) return sa1.mmio.snv >> 0; + if(addr == 0xffeb && sa1.mmio.cpu_nvsw) return sa1.mmio.snv >> 8; + if(addr == 0xffee && sa1.mmio.cpu_ivsw) return sa1.mmio.siv >> 0; + if(addr == 0xffef && sa1.mmio.cpu_ivsw) return sa1.mmio.siv >> 8; + } + + if((addr & 0xe08000) == 0x008000) { //$00-1f:8000-ffff + addr = ((addr & 0x1f0000) >> 1) | (addr & 0x007fff); + if(mmio.cbmode == 0) return sa1_read(0x000000 | addr); + return sa1_read((mmio.cb << 20) | addr); + } + + if((addr & 0xe08000) == 0x208000) { //$20-3f:8000-ffff + addr = ((addr & 0x1f0000) >> 1) | (addr & 0x007fff); + if(mmio.dbmode == 0) return sa1_read(0x100000 | addr); + return sa1_read((mmio.db << 20) | addr); + } + + if((addr & 0xe08000) == 0x808000) { //$80-9f:8000-ffff + addr = ((addr & 0x1f0000) >> 1) | (addr & 0x007fff); + if(mmio.ebmode == 0) return sa1_read(0x200000 | addr); + return sa1_read((mmio.eb << 20) | addr); + } + + if((addr & 0xe08000) == 0xa08000) { //$a0-bf:8000-ffff + addr = ((addr & 0x1f0000) >> 1) | (addr & 0x007fff); + if(mmio.fbmode == 0) return sa1_read(0x300000 | addr); + return sa1_read((mmio.fb << 20) | addr); + } + + if((addr & 0xf00000) == 0xc00000) { //$c0-cf:0000-ffff + return sa1_read((mmio.cb << 20) | (addr & 0x0fffff)); + } + + if((addr & 0xf00000) == 0xd00000) { //$d0-df:0000-ffff + return sa1_read((mmio.db << 20) | (addr & 0x0fffff)); + } + + if((addr & 0xf00000) == 0xe00000) { //$e0-ef:0000-ffff + return sa1_read((mmio.eb << 20) | (addr & 0x0fffff)); + } + + if((addr & 0xf00000) == 0xf00000) { //$f0-ff:0000-ffff + return sa1_read((mmio.fb << 20) | (addr & 0x0fffff)); + } + + return 0x00; +} + +void SA1::mmc_write(unsigned addr, uint8 data) { +} + +uint8 SA1::mmc_cpu_read(unsigned addr) { + cpu.synchronize_coprocessors(); + addr = bus.mirror(mmio.sbm * 0x2000 + (addr & 0x1fff), cpubwram.size()); + return cpubwram.read(addr); +} + +void SA1::mmc_cpu_write(unsigned addr, uint8 data) { + cpu.synchronize_coprocessors(); + addr = bus.mirror(mmio.sbm * 0x2000 + (addr & 0x1fff), cpubwram.size()); + cpubwram.write(addr, data); +} + +uint8 SA1::mmc_sa1_read(unsigned addr) { + synchronize_cpu(); + if(mmio.sw46 == 0) { + //$40-43:0000-ffff x 32 projection + addr = bus.mirror((mmio.cbm & 0x1f) * 0x2000 + (addr & 0x1fff), cartridge.ram.size()); + return cartridge.ram.read(addr); + } else { + //$60-6f:0000-ffff x 128 projection + addr = bus.mirror(mmio.cbm * 0x2000 + (addr & 0x1fff), 0x100000); + return bitmap_read(addr); + } +} + +void SA1::mmc_sa1_write(unsigned addr, uint8 data) { + synchronize_cpu(); + if(mmio.sw46 == 0) { + //$40-43:0000-ffff x 32 projection + addr = bus.mirror((mmio.cbm & 0x1f) * 0x2000 + (addr & 0x1fff), cartridge.ram.size()); + cartridge.ram.write(addr, data); + } else { + //$60-6f:0000-ffff x 128 projection + addr = bus.mirror(mmio.cbm * 0x2000 + (addr & 0x1fff), 0x100000); + bitmap_write(addr, data); + } +} + +uint8 SA1::bitmap_read(unsigned addr) { + if(mmio.bbf == 0) { + //4bpp + unsigned shift = addr & 1; + addr = (addr >> 1) & (cartridge.ram.size() - 1); + switch(shift) { default: + case 0: return (cartridge.ram.read(addr) >> 0) & 15; + case 1: return (cartridge.ram.read(addr) >> 4) & 15; + } + } else { + //2bpp + unsigned shift = addr & 3; + addr = (addr >> 2) & (cartridge.ram.size() - 1); + switch(shift) { default: + case 0: return (cartridge.ram.read(addr) >> 0) & 3; + case 1: return (cartridge.ram.read(addr) >> 2) & 3; + case 2: return (cartridge.ram.read(addr) >> 4) & 3; + case 3: return (cartridge.ram.read(addr) >> 6) & 3; + } + } +} + +void SA1::bitmap_write(unsigned addr, uint8 data) { + if(mmio.bbf == 0) { + //4bpp + unsigned shift = addr & 1; + addr = (addr >> 1) & (cartridge.ram.size() - 1); + switch(shift) { default: + case 0: data = (cartridge.ram.read(addr) & 0xf0) | ((data & 15) << 0); break; + case 1: data = (cartridge.ram.read(addr) & 0x0f) | ((data & 15) << 4); break; + } + } else { + //2bpp + unsigned shift = addr & 3; + addr = (addr >> 2) & (cartridge.ram.size() - 1); + switch(shift) { default: + case 0: data = (cartridge.ram.read(addr) & 0xfc) | ((data & 3) << 0); break; + case 1: data = (cartridge.ram.read(addr) & 0xf3) | ((data & 3) << 2); break; + case 2: data = (cartridge.ram.read(addr) & 0xcf) | ((data & 3) << 4); break; + case 3: data = (cartridge.ram.read(addr) & 0x3f) | ((data & 3) << 6); break; + } + } + + cartridge.ram.write(addr, data); +} + +#endif diff --git a/libsnes/snes/chip/sa1/memory/memory.hpp b/libsnes/snes/chip/sa1/memory/memory.hpp new file mode 100644 index 0000000..ffb9e9f --- /dev/null +++ b/libsnes/snes/chip/sa1/memory/memory.hpp @@ -0,0 +1,19 @@ +uint8 bus_read(unsigned addr); +void bus_write(unsigned addr, uint8 data); +uint8 vbr_read(unsigned addr); + +alwaysinline void op_io(); +alwaysinline uint8 op_read(unsigned addr); +alwaysinline void op_write(unsigned addr, uint8 data); + +uint8 mmc_read(unsigned addr); +void mmc_write(unsigned addr, uint8 data); + +uint8 mmc_cpu_read(unsigned addr); +void mmc_cpu_write(unsigned addr, uint8 data); + +uint8 mmc_sa1_read(unsigned addr); +void mmc_sa1_write(unsigned addr, uint8 data); + +uint8 bitmap_read(unsigned addr); +void bitmap_write(unsigned addr, uint8 data); diff --git a/libsnes/snes/chip/sa1/mmio/mmio.cpp b/libsnes/snes/chip/sa1/mmio/mmio.cpp new file mode 100644 index 0000000..c4a894b --- /dev/null +++ b/libsnes/snes/chip/sa1/mmio/mmio.cpp @@ -0,0 +1,554 @@ +#ifdef SA1_CPP + +//(CCNT) SA-1 control +void SA1::mmio_w2200(uint8 data) { + if(mmio.sa1_resb && !(data & 0x80)) { + //reset SA-1 CPU + regs.pc.w = mmio.crv; + regs.pc.b = 0x00; + } + + mmio.sa1_irq = (data & 0x80); + mmio.sa1_rdyb = (data & 0x40); + mmio.sa1_resb = (data & 0x20); + mmio.sa1_nmi = (data & 0x10); + mmio.smeg = (data & 0x0f); + + if(mmio.sa1_irq) { + mmio.sa1_irqfl = true; + if(mmio.sa1_irqen) mmio.sa1_irqcl = 0; + } + + if(mmio.sa1_nmi) { + mmio.sa1_nmifl = true; + if(mmio.sa1_nmien) mmio.sa1_nmicl = 0; + } +} + +//(SIE) S-CPU interrupt enable +void SA1::mmio_w2201(uint8 data) { + if(!mmio.cpu_irqen && (data & 0x80)) { + if(mmio.cpu_irqfl) { + mmio.cpu_irqcl = 0; + cpu.regs.irq = 1; + } + } + + if(!mmio.chdma_irqen && (data & 0x20)) { + if(mmio.chdma_irqfl) { + mmio.chdma_irqcl = 0; + cpu.regs.irq = 1; + } + } + + mmio.cpu_irqen = (data & 0x80); + mmio.chdma_irqen = (data & 0x20); +} + +//(SIC) S-CPU interrupt clear +void SA1::mmio_w2202(uint8 data) { + mmio.cpu_irqcl = (data & 0x80); + mmio.chdma_irqcl = (data & 0x20); + + if(mmio.cpu_irqcl ) mmio.cpu_irqfl = false; + if(mmio.chdma_irqcl) mmio.chdma_irqfl = false; + + if(!mmio.cpu_irqfl && !mmio.chdma_irqfl) cpu.regs.irq = 0; +} + +//(CRV) SA-1 reset vector +void SA1::mmio_w2203(uint8 data) { mmio.crv = (mmio.crv & 0xff00) | data; } +void SA1::mmio_w2204(uint8 data) { mmio.crv = (data << 8) | (mmio.crv & 0xff); } + +//(CNV) SA-1 NMI vector +void SA1::mmio_w2205(uint8 data) { mmio.cnv = (mmio.cnv & 0xff00) | data; } +void SA1::mmio_w2206(uint8 data) { mmio.cnv = (data << 8) | (mmio.cnv & 0xff); } + +//(CIV) SA-1 IRQ vector +void SA1::mmio_w2207(uint8 data) { mmio.civ = (mmio.civ & 0xff00) | data; } +void SA1::mmio_w2208(uint8 data) { mmio.civ = (data << 8) | (mmio.civ & 0xff); } + +//(SCNT) S-CPU control +void SA1::mmio_w2209(uint8 data) { + mmio.cpu_irq = (data & 0x80); + mmio.cpu_ivsw = (data & 0x40); + mmio.cpu_nvsw = (data & 0x10); + mmio.cmeg = (data & 0x0f); + + if(mmio.cpu_irq) { + mmio.cpu_irqfl = true; + if(mmio.cpu_irqen) { + mmio.cpu_irqcl = 0; + cpu.regs.irq = 1; + } + } +} + +//(CIE) SA-1 interrupt enable +void SA1::mmio_w220a(uint8 data) { + if(!mmio.sa1_irqen && (data & 0x80) && mmio.sa1_irqfl ) mmio.sa1_irqcl = 0; + if(!mmio.timer_irqen && (data & 0x40) && mmio.timer_irqfl) mmio.timer_irqcl = 0; + if(!mmio.dma_irqen && (data & 0x20) && mmio.dma_irqfl ) mmio.dma_irqcl = 0; + if(!mmio.sa1_nmien && (data & 0x10) && mmio.sa1_nmifl ) mmio.sa1_nmicl = 0; + + mmio.sa1_irqen = (data & 0x80); + mmio.timer_irqen = (data & 0x40); + mmio.dma_irqen = (data & 0x20); + mmio.sa1_nmien = (data & 0x10); +} + +//(CIC) SA-1 interrupt clear +void SA1::mmio_w220b(uint8 data) { + mmio.sa1_irqcl = (data & 0x80); + mmio.timer_irqcl = (data & 0x40); + mmio.dma_irqcl = (data & 0x20); + mmio.sa1_nmicl = (data & 0x10); + + if(mmio.sa1_irqcl) mmio.sa1_irqfl = false; + if(mmio.timer_irqcl) mmio.timer_irqfl = false; + if(mmio.dma_irqcl) mmio.dma_irqfl = false; + if(mmio.sa1_nmicl) mmio.sa1_nmifl = false; +} + +//(SNV) S-CPU NMI vector +void SA1::mmio_w220c(uint8 data) { mmio.snv = (mmio.snv & 0xff00) | data; } +void SA1::mmio_w220d(uint8 data) { mmio.snv = (data << 8) | (mmio.snv & 0xff); } + +//(SIV) S-CPU IRQ vector +void SA1::mmio_w220e(uint8 data) { mmio.siv = (mmio.siv & 0xff00) | data; } +void SA1::mmio_w220f(uint8 data) { mmio.siv = (data << 8) | (mmio.siv & 0xff); } + +//(TMC) H/V timer control +void SA1::mmio_w2210(uint8 data) { + mmio.hvselb = (data & 0x80); + mmio.ven = (data & 0x02); + mmio.hen = (data & 0x01); +} + +//(CTR) SA-1 timer restart +void SA1::mmio_w2211(uint8 data) { + status.vcounter = 0; + status.hcounter = 0; +} + +//(HCNT) H-count +void SA1::mmio_w2212(uint8 data) { mmio.hcnt = (mmio.hcnt & 0xff00) | (data << 0); } +void SA1::mmio_w2213(uint8 data) { mmio.hcnt = (mmio.hcnt & 0x00ff) | (data << 8); } + +//(VCNT) V-count +void SA1::mmio_w2214(uint8 data) { mmio.vcnt = (mmio.vcnt & 0xff00) | (data << 0); } +void SA1::mmio_w2215(uint8 data) { mmio.vcnt = (mmio.vcnt & 0x00ff) | (data << 8); } + +//(CXB) Super MMC bank C +void SA1::mmio_w2220(uint8 data) { + mmio.cbmode = (data & 0x80); + mmio.cb = (data & 0x07); +} + +//(DXB) Super MMC bank D +void SA1::mmio_w2221(uint8 data) { + mmio.dbmode = (data & 0x80); + mmio.db = (data & 0x07); +} + +//(EXB) Super MMC bank E +void SA1::mmio_w2222(uint8 data) { + mmio.ebmode = (data & 0x80); + mmio.eb = (data & 0x07); +} + +//(FXB) Super MMC bank F +void SA1::mmio_w2223(uint8 data) { + mmio.fbmode = (data & 0x80); + mmio.fb = (data & 0x07); +} + +//(BMAPS) S-CPU BW-RAM address mapping +void SA1::mmio_w2224(uint8 data) { + mmio.sbm = (data & 0x1f); +} + +//(BMAP) SA-1 BW-RAM address mapping +void SA1::mmio_w2225(uint8 data) { + mmio.sw46 = (data & 0x80); + mmio.cbm = (data & 0x7f); +} + +//(SWBE) S-CPU BW-RAM write enable +void SA1::mmio_w2226(uint8 data) { + mmio.swen = (data & 0x80); +} + +//(CWBE) SA-1 BW-RAM write enable +void SA1::mmio_w2227(uint8 data) { + mmio.cwen = (data & 0x80); +} + +//(BWPA) BW-RAM write-protected area +void SA1::mmio_w2228(uint8 data) { + mmio.bwp = (data & 0x0f); +} + +//(SIWP) S-CPU I-RAM write protection +void SA1::mmio_w2229(uint8 data) { + mmio.siwp = data; +} + +//(CIWP) SA-1 I-RAM write protection +void SA1::mmio_w222a(uint8 data) { + mmio.ciwp = data; +} + +//(DCNT) DMA control +void SA1::mmio_w2230(uint8 data) { + mmio.dmaen = (data & 0x80); + mmio.dprio = (data & 0x40); + mmio.cden = (data & 0x20); + mmio.cdsel = (data & 0x10); + mmio.dd = (data & 0x04); + mmio.sd = (data & 0x03); + + if(mmio.dmaen == 0) dma.line = 0; +} + +//(CDMA) character conversion DMA parameters +void SA1::mmio_w2231(uint8 data) { + mmio.chdend = (data & 0x80); + mmio.dmasize = (data >> 2) & 7; + mmio.dmacb = (data & 0x03); + + if(mmio.chdend) cpubwram.dma = false; + if(mmio.dmasize > 5) mmio.dmasize = 5; + if(mmio.dmacb > 2) mmio.dmacb = 2; +} + +//(SDA) DMA source device start address +void SA1::mmio_w2232(uint8 data) { mmio.dsa = (mmio.dsa & 0xffff00) | (data << 0); } +void SA1::mmio_w2233(uint8 data) { mmio.dsa = (mmio.dsa & 0xff00ff) | (data << 8); } +void SA1::mmio_w2234(uint8 data) { mmio.dsa = (mmio.dsa & 0x00ffff) | (data << 16); } + +//(DDA) DMA destination start address +void SA1::mmio_w2235(uint8 data) { + mmio.dda = (mmio.dda & 0xffff00) | (data << 0); +} + +void SA1::mmio_w2236(uint8 data) { + mmio.dda = (mmio.dda & 0xff00ff) | (data << 8); + + if(mmio.dmaen == true) { + if(mmio.cden == 0 && mmio.dd == DMA::DestIRAM) { + dma_normal(); + } else if(mmio.cden == 1 && mmio.cdsel == 1) { + dma_cc1(); + } + } +} + +void SA1::mmio_w2237(uint8 data) { + mmio.dda = (mmio.dda & 0x00ffff) | (data << 16); + + if(mmio.dmaen == true) { + if(mmio.cden == 0 && mmio.dd == DMA::DestBWRAM) { + dma_normal(); + } + } +} + +//(DTC) DMA terminal counter +void SA1::mmio_w2238(uint8 data) { mmio.dtc = (mmio.dtc & 0xff00) | (data << 0); } +void SA1::mmio_w2239(uint8 data) { mmio.dtc = (mmio.dtc & 0x00ff) | (data << 8); } + +//(BBF) BW-RAM bitmap format +void SA1::mmio_w223f(uint8 data) { + mmio.bbf = (data & 0x80); +} + +//(BRF) bitmap register files +void SA1::mmio_w2240(uint8 data) { mmio.brf[ 0] = data; } +void SA1::mmio_w2241(uint8 data) { mmio.brf[ 1] = data; } +void SA1::mmio_w2242(uint8 data) { mmio.brf[ 2] = data; } +void SA1::mmio_w2243(uint8 data) { mmio.brf[ 3] = data; } +void SA1::mmio_w2244(uint8 data) { mmio.brf[ 4] = data; } +void SA1::mmio_w2245(uint8 data) { mmio.brf[ 5] = data; } +void SA1::mmio_w2246(uint8 data) { mmio.brf[ 6] = data; } +void SA1::mmio_w2247(uint8 data) { mmio.brf[ 7] = data; + if(mmio.dmaen == true) { + if(mmio.cden == 1 && mmio.cdsel == 0) { + dma_cc2(); + } + } +} + +void SA1::mmio_w2248(uint8 data) { mmio.brf[ 8] = data; } +void SA1::mmio_w2249(uint8 data) { mmio.brf[ 9] = data; } +void SA1::mmio_w224a(uint8 data) { mmio.brf[10] = data; } +void SA1::mmio_w224b(uint8 data) { mmio.brf[11] = data; } +void SA1::mmio_w224c(uint8 data) { mmio.brf[12] = data; } +void SA1::mmio_w224d(uint8 data) { mmio.brf[13] = data; } +void SA1::mmio_w224e(uint8 data) { mmio.brf[14] = data; } +void SA1::mmio_w224f(uint8 data) { mmio.brf[15] = data; + if(mmio.dmaen == true) { + if(mmio.cden == 1 && mmio.cdsel == 0) { + dma_cc2(); + } + } +} + +//(MCNT) arithmetic control +void SA1::mmio_w2250(uint8 data) { + mmio.acm = (data & 0x02); + mmio.md = (data & 0x01); + + if(mmio.acm) mmio.mr = 0; +} + +//(MAL) multiplicand / dividend low +void SA1::mmio_w2251(uint8 data) { + mmio.ma = (mmio.ma & 0xff00) | data; +} + +//(MAH) multiplicand / dividend high +void SA1::mmio_w2252(uint8 data) { + mmio.ma = (data << 8) | (mmio.ma & 0x00ff); +} + +//(MBL) multiplier / divisor low +void SA1::mmio_w2253(uint8 data) { + mmio.mb = (mmio.mb & 0xff00) | data; +} + +//(MBH) multiplier / divisor high +//multiplication / cumulative sum only resets MB +//division resets both MA and MB +void SA1::mmio_w2254(uint8 data) { + mmio.mb = (data << 8) | (mmio.mb & 0x00ff); + + if(mmio.acm == 0) { + if(mmio.md == 0) { + //signed multiplication + mmio.mr = (int16)mmio.ma * (int16)mmio.mb; + mmio.mb = 0; + } else { + //unsigned division + if(mmio.mb == 0) { + mmio.mr = 0; + } else { + int16 quotient = (int16)mmio.ma / (uint16)mmio.mb; + uint16 remainder = (int16)mmio.ma % (uint16)mmio.mb; + mmio.mr = (remainder << 16) | quotient; + } + mmio.ma = 0; + mmio.mb = 0; + } + } else { + //sigma (accumulative multiplication) + mmio.mr += (int16)mmio.ma * (int16)mmio.mb; + mmio.overflow = (mmio.mr >= (1ULL << 40)); + mmio.mr &= (1ULL << 40) - 1; + mmio.mb = 0; + } +} + +//(VBD) variable-length bit processing +void SA1::mmio_w2258(uint8 data) { + mmio.hl = (data & 0x80); + mmio.vb = (data & 0x0f); + if(mmio.vb == 0) mmio.vb = 16; + + if(mmio.hl == 0) { + //fixed mode + mmio.vbit += mmio.vb; + mmio.va += (mmio.vbit >> 3); + mmio.vbit &= 7; + } +} + +//(VDA) variable-length bit game pak ROM start address +void SA1::mmio_w2259(uint8 data) { mmio.va = (mmio.va & 0xffff00) | (data << 0); } +void SA1::mmio_w225a(uint8 data) { mmio.va = (mmio.va & 0xff00ff) | (data << 8); } +void SA1::mmio_w225b(uint8 data) { mmio.va = (mmio.va & 0x00ffff) | (data << 16); mmio.vbit = 0; } + +//(SFR) S-CPU flag read +uint8 SA1::mmio_r2300() { + uint8 data; + data = mmio.cpu_irqfl << 7; + data |= mmio.cpu_ivsw << 6; + data |= mmio.chdma_irqfl << 5; + data |= mmio.cpu_nvsw << 4; + data |= mmio.cmeg; + return data; +} + +//(CFR) SA-1 flag read +uint8 SA1::mmio_r2301() { + uint8 data; + data = mmio.sa1_irqfl << 7; + data |= mmio.timer_irqfl << 6; + data |= mmio.dma_irqfl << 5; + data |= mmio.sa1_nmifl << 4; + data |= mmio.smeg; + return data; +} + +//(HCR) hcounter read +uint8 SA1::mmio_r2302() { + //latch counters + mmio.hcr = status.hcounter >> 2; + mmio.vcr = status.vcounter; + return mmio.hcr >> 0; } +uint8 SA1::mmio_r2303() { return mmio.hcr >> 8; } + +//(VCR) vcounter read +uint8 SA1::mmio_r2304() { return mmio.vcr >> 0; } +uint8 SA1::mmio_r2305() { return mmio.vcr >> 8; } + +//(MR) arithmetic result +uint8 SA1::mmio_r2306() { return mmio.mr >> 0; } +uint8 SA1::mmio_r2307() { return mmio.mr >> 8; } +uint8 SA1::mmio_r2308() { return mmio.mr >> 16; } +uint8 SA1::mmio_r2309() { return mmio.mr >> 24; } +uint8 SA1::mmio_r230a() { return mmio.mr >> 32; } + +//(OF) arithmetic overflow flag +uint8 SA1::mmio_r230b() { return mmio.overflow << 7; } + +//(VDPL) variable-length data read port low +uint8 SA1::mmio_r230c() { + uint32 data = (vbr_read(mmio.va + 0) << 0) + | (vbr_read(mmio.va + 1) << 8) + | (vbr_read(mmio.va + 2) << 16); + data >>= mmio.vbit; + return data >> 0; +} + +//(VDPH) variable-length data read port high +uint8 SA1::mmio_r230d() { + uint32 data = (vbr_read(mmio.va + 0) << 0) + | (vbr_read(mmio.va + 1) << 8) + | (vbr_read(mmio.va + 2) << 16); + data >>= mmio.vbit; + + if(mmio.hl == 1) { + //auto-increment mode + mmio.vbit += mmio.vb; + mmio.va += (mmio.vbit >> 3); + mmio.vbit &= 7; + } + + return data >> 8; +} + +//(VC) version code register +uint8 SA1::mmio_r230e() { + return 0x01; //true value unknown +} + +uint8 SA1::mmio_read(unsigned addr) { + (co_active() == cpu.thread ? cpu.synchronize_coprocessors() : synchronize_cpu()); + addr &= 0xffff; + + switch(addr) { + case 0x2300: return mmio_r2300(); + case 0x2301: return mmio_r2301(); + case 0x2302: return mmio_r2302(); + case 0x2303: return mmio_r2303(); + case 0x2304: return mmio_r2304(); + case 0x2305: return mmio_r2305(); + case 0x2306: return mmio_r2306(); + case 0x2307: return mmio_r2307(); + case 0x2308: return mmio_r2308(); + case 0x2309: return mmio_r2309(); + case 0x230a: return mmio_r230a(); + case 0x230b: return mmio_r230b(); + case 0x230c: return mmio_r230c(); + case 0x230d: return mmio_r230d(); + case 0x230e: return mmio_r230e(); + } + + return 0x00; +} + +void SA1::mmio_write(unsigned addr, uint8 data) { + (co_active() == cpu.thread ? cpu.synchronize_coprocessors() : synchronize_cpu()); + addr &= 0xffff; + + switch(addr) { + case 0x2200: return mmio_w2200(data); + case 0x2201: return mmio_w2201(data); + case 0x2202: return mmio_w2202(data); + case 0x2203: return mmio_w2203(data); + case 0x2204: return mmio_w2204(data); + case 0x2205: return mmio_w2205(data); + case 0x2206: return mmio_w2206(data); + case 0x2207: return mmio_w2207(data); + case 0x2208: return mmio_w2208(data); + case 0x2209: return mmio_w2209(data); + case 0x220a: return mmio_w220a(data); + case 0x220b: return mmio_w220b(data); + case 0x220c: return mmio_w220c(data); + case 0x220d: return mmio_w220d(data); + case 0x220e: return mmio_w220e(data); + case 0x220f: return mmio_w220f(data); + + case 0x2210: return mmio_w2210(data); + case 0x2211: return mmio_w2211(data); + case 0x2212: return mmio_w2212(data); + case 0x2213: return mmio_w2213(data); + case 0x2214: return mmio_w2214(data); + case 0x2215: return mmio_w2215(data); + + case 0x2220: return mmio_w2220(data); + case 0x2221: return mmio_w2221(data); + case 0x2222: return mmio_w2222(data); + case 0x2223: return mmio_w2223(data); + case 0x2224: return mmio_w2224(data); + case 0x2225: return mmio_w2225(data); + case 0x2226: return mmio_w2226(data); + case 0x2227: return mmio_w2227(data); + case 0x2228: return mmio_w2228(data); + case 0x2229: return mmio_w2229(data); + case 0x222a: return mmio_w222a(data); + + case 0x2230: return mmio_w2230(data); + case 0x2231: return mmio_w2231(data); + case 0x2232: return mmio_w2232(data); + case 0x2233: return mmio_w2233(data); + case 0x2234: return mmio_w2234(data); + case 0x2235: return mmio_w2235(data); + case 0x2236: return mmio_w2236(data); + case 0x2237: return mmio_w2237(data); + case 0x2238: return mmio_w2238(data); + case 0x2239: return mmio_w2239(data); + + case 0x223f: return mmio_w223f(data); + case 0x2240: return mmio_w2240(data); + case 0x2241: return mmio_w2241(data); + case 0x2242: return mmio_w2242(data); + case 0x2243: return mmio_w2243(data); + case 0x2244: return mmio_w2244(data); + case 0x2245: return mmio_w2245(data); + case 0x2246: return mmio_w2246(data); + case 0x2247: return mmio_w2247(data); + case 0x2248: return mmio_w2248(data); + case 0x2249: return mmio_w2249(data); + case 0x224a: return mmio_w224a(data); + case 0x224b: return mmio_w224b(data); + case 0x224c: return mmio_w224c(data); + case 0x224d: return mmio_w224d(data); + case 0x224e: return mmio_w224e(data); + case 0x224f: return mmio_w224f(data); + + case 0x2250: return mmio_w2250(data); + case 0x2251: return mmio_w2251(data); + case 0x2252: return mmio_w2252(data); + case 0x2253: return mmio_w2253(data); + case 0x2254: return mmio_w2254(data); + + case 0x2258: return mmio_w2258(data); + case 0x2259: return mmio_w2259(data); + case 0x225a: return mmio_w225a(data); + case 0x225b: return mmio_w225b(data); + } +} + +#endif diff --git a/libsnes/snes/chip/sa1/mmio/mmio.hpp b/libsnes/snes/chip/sa1/mmio/mmio.hpp new file mode 100644 index 0000000..4eaa146 --- /dev/null +++ b/libsnes/snes/chip/sa1/mmio/mmio.hpp @@ -0,0 +1,255 @@ +uint8 mmio_read(unsigned addr); +void mmio_write(unsigned addr, uint8 data); + +struct MMIO { + //$2200 CCNT + bool sa1_irq; + bool sa1_rdyb; + bool sa1_resb; + bool sa1_nmi; + uint8 smeg; + + //$2201 SIE + bool cpu_irqen; + bool chdma_irqen; + + //$2202 SIC + bool cpu_irqcl; + bool chdma_irqcl; + + //$2203,$2204 CRV + uint16 crv; + + //$2205,$2206 CNV + uint16 cnv; + + //$2207,$2208 CIV + uint16 civ; + + //$2209 SCNT + bool cpu_irq; + bool cpu_ivsw; + bool cpu_nvsw; + uint8 cmeg; + + //$220a CIE + bool sa1_irqen; + bool timer_irqen; + bool dma_irqen; + bool sa1_nmien; + + //$220b CIC + bool sa1_irqcl; + bool timer_irqcl; + bool dma_irqcl; + bool sa1_nmicl; + + //$220c,$220d SNV + uint16 snv; + + //$220e,$220f SIV + uint16 siv; + + //$2210 TMC + bool hvselb; + bool ven; + bool hen; + + //$2212,$2213 + uint16 hcnt; + + //$2214,$2215 + uint16 vcnt; + + //$2220 CXB + bool cbmode; + unsigned cb; + + //$2221 DXB + bool dbmode; + unsigned db; + + //$2222 EXB + bool ebmode; + unsigned eb; + + //$2223 FXB + bool fbmode; + unsigned fb; + + //$2224 BMAPS + uint8 sbm; + + //$2225 BMAP + bool sw46; + uint8 cbm; + + //$2226 SBWE + bool swen; + + //$2227 CBWE + bool cwen; + + //$2228 BWPA + uint8 bwp; + + //$2229 SIWP + uint8 siwp; + + //$222a CIWP + uint8 ciwp; + + //$2230 DCNT + bool dmaen; + bool dprio; + bool cden; + bool cdsel; + bool dd; + uint8 sd; + + //$2231 CDMA + bool chdend; + uint8 dmasize; + uint8 dmacb; + + //$2232-$2234 SDA + uint32 dsa; + + //$2235-$2237 DDA + uint32 dda; + + //$2238,$2239 DTC + uint16 dtc; + + //$223f BBF + bool bbf; + + //$2240-224f BRF + uint8 brf[16]; + + //$2250 MCNT + bool acm; + bool md; + + //$2251,$2252 MA + uint16 ma; + + //$2253,$2254 MB + uint16 mb; + + //$2258 VBD + bool hl; + uint8 vb; + + //$2259-$225b VDA + uint32 va; + uint8 vbit; + + //$2300 SFR + bool cpu_irqfl; + bool chdma_irqfl; + + //$2301 CFR + bool sa1_irqfl; + bool timer_irqfl; + bool dma_irqfl; + bool sa1_nmifl; + + //$2302,$2303 HCR + uint16 hcr; + + //$2304,$2305 VCR + uint16 vcr; + + //$2306-230a MR + uint64 mr; + + //$230b OF + bool overflow; +} mmio; + +void mmio_w2200(uint8); //CCNT +void mmio_w2201(uint8); //SIE +void mmio_w2202(uint8); //SIC +void mmio_w2203(uint8); //CRVL +void mmio_w2204(uint8); //CRVH +void mmio_w2205(uint8); //CNVL +void mmio_w2206(uint8); //CNVH +void mmio_w2207(uint8); //CIVL +void mmio_w2208(uint8); //CIVH +void mmio_w2209(uint8); //SCNT +void mmio_w220a(uint8); //CIE +void mmio_w220b(uint8); //CIC +void mmio_w220c(uint8); //SNVL +void mmio_w220d(uint8); //SNVH +void mmio_w220e(uint8); //SIVL +void mmio_w220f(uint8); //SIVH +void mmio_w2210(uint8); //TMC +void mmio_w2211(uint8); //CTR +void mmio_w2212(uint8); //HCNTL +void mmio_w2213(uint8); //HCNTH +void mmio_w2214(uint8); //VCNTL +void mmio_w2215(uint8); //VCNTH +void mmio_w2220(uint8); //CXB +void mmio_w2221(uint8); //DXB +void mmio_w2222(uint8); //EXB +void mmio_w2223(uint8); //FXB +void mmio_w2224(uint8); //BMAPS +void mmio_w2225(uint8); //BMAP +void mmio_w2226(uint8); //SBWE +void mmio_w2227(uint8); //CBWE +void mmio_w2228(uint8); //BWPA +void mmio_w2229(uint8); //SIWP +void mmio_w222a(uint8); //CIWP +void mmio_w2230(uint8); //DCNT +void mmio_w2231(uint8); //CDMA +void mmio_w2232(uint8); //SDAL +void mmio_w2233(uint8); //SDAH +void mmio_w2234(uint8); //SDAB +void mmio_w2235(uint8); //DDAL +void mmio_w2236(uint8); //DDAH +void mmio_w2237(uint8); //DDAB +void mmio_w2238(uint8); //DTCL +void mmio_w2239(uint8); //DTCH +void mmio_w223f(uint8); //BBF +void mmio_w2240(uint8); //BRF0 +void mmio_w2241(uint8); //BRF1 +void mmio_w2242(uint8); //BRF2 +void mmio_w2243(uint8); //BRF3 +void mmio_w2244(uint8); //BRF4 +void mmio_w2245(uint8); //BRF5 +void mmio_w2246(uint8); //BRF6 +void mmio_w2247(uint8); //BRF7 +void mmio_w2248(uint8); //BRF8 +void mmio_w2249(uint8); //BRF9 +void mmio_w224a(uint8); //BRFA +void mmio_w224b(uint8); //BRFB +void mmio_w224c(uint8); //BRFC +void mmio_w224d(uint8); //BRFD +void mmio_w224e(uint8); //BRFE +void mmio_w224f(uint8); //BRFF +void mmio_w2250(uint8); //MCNT +void mmio_w2251(uint8); //MAL +void mmio_w2252(uint8); //MAH +void mmio_w2253(uint8); //MBL +void mmio_w2254(uint8); //MBH +void mmio_w2258(uint8); //VBD +void mmio_w2259(uint8); //VDAL +void mmio_w225a(uint8); //VDAH +void mmio_w225b(uint8); //VDAB + +uint8 mmio_r2300(); //SFR +uint8 mmio_r2301(); //CFR +uint8 mmio_r2302(); //HCRL +uint8 mmio_r2303(); //HCRH +uint8 mmio_r2304(); //VCRL +uint8 mmio_r2305(); //VCRH +uint8 mmio_r2306(); //MR [00-07] +uint8 mmio_r2307(); //MR [08-15] +uint8 mmio_r2308(); //MR [16-23] +uint8 mmio_r2309(); //MR [24-31] +uint8 mmio_r230a(); //MR [32-40] +uint8 mmio_r230b(); //OF +uint8 mmio_r230c(); //VDPL +uint8 mmio_r230d(); //VDPH +uint8 mmio_r230e(); //VC diff --git a/libsnes/snes/chip/sa1/sa1.cpp b/libsnes/snes/chip/sa1/sa1.cpp new file mode 100644 index 0000000..9f379a8 --- /dev/null +++ b/libsnes/snes/chip/sa1/sa1.cpp @@ -0,0 +1,330 @@ +#include + +#define SA1_CPP +namespace SNES { + +SA1 sa1; + +#include "serialization.cpp" +#include "bus/bus.cpp" +#include "dma/dma.cpp" +#include "memory/memory.cpp" +#include "mmio/mmio.cpp" + +void SA1::Enter() { sa1.enter(); } + +void SA1::enter() { + while(true) { + if(scheduler.sync.i == Scheduler::SynchronizeMode::All) { + scheduler.exit(Scheduler::ExitReason::SynchronizeEvent); + } + + if(mmio.sa1_rdyb || mmio.sa1_resb) { + //SA-1 co-processor is asleep + tick(); + synchronize_cpu(); + continue; + } + + if(status.interrupt_pending) { + status.interrupt_pending = false; + op_irq(); + continue; + } + + (this->*opcode_table[op_readpc()])(); + } +} + +void SA1::op_irq() { + op_read(regs.pc.d); + op_io(); + if(!regs.e) op_writestack(regs.pc.b); + op_writestack(regs.pc.h); + op_writestack(regs.pc.l); + op_writestack(regs.e ? (regs.p & ~0x10) : regs.p); + regs.pc.w = regs.vector; + regs.pc.b = 0x00; + regs.p.i = 1; + regs.p.d = 0; +} + +void SA1::last_cycle() { + if(mmio.sa1_nmi && !mmio.sa1_nmicl) { + status.interrupt_pending = true; + regs.vector = mmio.cnv; + mmio.sa1_nmifl = true; + mmio.sa1_nmicl = 1; + regs.wai = false; + } else if(!regs.p.i) { + if(mmio.timer_irqen && !mmio.timer_irqcl) { + status.interrupt_pending = true; + regs.vector = mmio.civ; + mmio.timer_irqfl = true; + regs.wai = false; + } else if(mmio.dma_irqen && !mmio.dma_irqcl) { + status.interrupt_pending = true; + regs.vector = mmio.civ; + mmio.dma_irqfl = true; + regs.wai = false; + } else if(mmio.sa1_irq && !mmio.sa1_irqcl) { + status.interrupt_pending = true; + regs.vector = mmio.civ; + mmio.sa1_irqfl = true; + regs.wai = false; + } + } +} + +bool SA1::interrupt_pending() { + return status.interrupt_pending; +} + +void SA1::tick() { + step(2); + if(++status.tick_counter == 0) synchronize_cpu(); + + //adjust counters: + //note that internally, status counters are in clocks; + //whereas MMIO register counters are in dots (4 clocks = 1 dot) + if(mmio.hvselb == 0) { + //HV timer + status.hcounter += 2; + if(status.hcounter >= 1364) { + status.hcounter = 0; + if(++status.vcounter >= status.scanlines) status.vcounter = 0; + } + } else { + //linear timer + status.hcounter += 2; + status.vcounter += (status.hcounter >> 11); + status.hcounter &= 0x07ff; + status.vcounter &= 0x01ff; + } + + //test counters for timer IRQ + switch((mmio.ven << 1) + (mmio.hen << 0)) { + case 0: break; + case 1: if(status.hcounter == (mmio.hcnt << 2)) trigger_irq(); break; + case 2: if(status.vcounter == mmio.vcnt && status.hcounter == 0) trigger_irq(); break; + case 3: if(status.vcounter == mmio.hcnt && status.hcounter == (mmio.hcnt << 2)) trigger_irq(); break; + } +} + +void SA1::trigger_irq() { + mmio.timer_irqfl = true; + if(mmio.timer_irqen) mmio.timer_irqcl = 0; +} + +void SA1::init() { +} + +void SA1::load() { +} + +void SA1::unload() { +} + +void SA1::power() { + regs.a = regs.x = regs.y = 0x0000; + regs.s = 0x01ff; +} + +void SA1::reset() { + create(SA1::Enter, system.cpu_frequency); + + cpubwram.dma = false; + for(unsigned addr = 0; addr < iram.size(); addr++) { + iram.write(addr, 0x00); + } + + regs.pc.d = 0x000000; + regs.x.h = 0x00; + regs.y.h = 0x00; + regs.s.h = 0x01; + regs.d = 0x0000; + regs.db = 0x00; + regs.p = 0x34; + regs.e = 1; + regs.mdr = 0x00; + regs.wai = false; + regs.vector = 0x0000; + CPUcore::update_table(); + + status.tick_counter = 0; + + status.interrupt_pending = false; + + status.scanlines = (system.region.i == System::Region::NTSC ? 262 : 312); + status.vcounter = 0; + status.hcounter = 0; + + dma.line = 0; + + //$2200 CCNT + mmio.sa1_irq = false; + mmio.sa1_rdyb = false; + mmio.sa1_resb = true; + mmio.sa1_nmi = false; + mmio.smeg = 0; + + //$2201 SIE + mmio.cpu_irqen = false; + mmio.chdma_irqen = false; + + //$2202 SIC + mmio.cpu_irqcl = false; + mmio.chdma_irqcl = false; + + //$2203,$2204 CRV + mmio.crv = 0x0000; + + //$2205,$2206 CNV + mmio.cnv = 0x0000; + + //$2207,$2208 CIV + mmio.civ = 0x0000; + + //$2209 SCNT + mmio.cpu_irq = false; + mmio.cpu_ivsw = false; + mmio.cpu_nvsw = false; + mmio.cmeg = 0; + + //$220a CIE + mmio.sa1_irqen = false; + mmio.timer_irqen = false; + mmio.dma_irqen = false; + mmio.sa1_nmien = false; + + //$220b CIC + mmio.sa1_irqcl = false; + mmio.timer_irqcl = false; + mmio.dma_irqcl = false; + mmio.sa1_nmicl = false; + + //$220c,$220d SNV + mmio.snv = 0x0000; + + //$220e,$220f SIV + mmio.siv = 0x0000; + + //$2210 + mmio.hvselb = false; + mmio.ven = false; + mmio.hen = false; + + //$2212,$2213 HCNT + mmio.hcnt = 0x0000; + + //$2214,$2215 VCNT + mmio.vcnt = 0x0000; + + //$2220-2223 CXB, DXB, EXB, FXB + mmio.cbmode = 1; + mmio.dbmode = 1; + mmio.ebmode = 1; + mmio.fbmode = 1; + + mmio.cb = 0x00; + mmio.db = 0x01; + mmio.eb = 0x00; + mmio.fb = 0x01; + + //$2224 BMAPS + mmio.sbm = 0x00; + + //$2225 BMAP + mmio.sw46 = false; + mmio.cbm = 0x00; + + //$2226 SWBE + mmio.swen = false; + + //$2227 CWBE + mmio.cwen = false; + + //$2228 BWPA + mmio.bwp = 0x0f; + + //$2229 SIWP + mmio.siwp = 0x00; + + //$222a CIWP + mmio.ciwp = 0x00; + + //$2230 DCNT + mmio.dmaen = false; + mmio.dprio = false; + mmio.cden = false; + mmio.cdsel = false; + mmio.dd = 0; + mmio.sd = 0; + + //$2231 CDMA + mmio.chdend = false; + mmio.dmasize = 0; + mmio.dmacb = 0; + + //$2232-$2234 SDA + mmio.dsa = 0x000000; + + //$2235-$2237 DDA + mmio.dda = 0x000000; + + //$2238,$2239 DTC + mmio.dtc = 0x0000; + + //$223f BBF + mmio.bbf = 0; + + //$2240-$224f BRF + for(unsigned i = 0; i < 16; i++) { + mmio.brf[i] = 0x00; + } + + //$2250 MCNT + mmio.acm = 0; + mmio.md = 0; + + //$2251,$2252 MA + mmio.ma = 0x0000; + + //$2253,$2254 MB + mmio.mb = 0x0000; + + //$2258 VBD + mmio.hl = false; + mmio.vb = 16; + + //$2259-$225b + mmio.va = 0x000000; + mmio.vbit = 0; + + //$2300 SFR + mmio.cpu_irqfl = false; + mmio.chdma_irqfl = false; + + //$2301 CFR + mmio.sa1_irqfl = false; + mmio.timer_irqfl = false; + mmio.dma_irqfl = false; + mmio.sa1_nmifl = false; + + //$2302,$2303 HCR + mmio.hcr = 0x0000; + + //$2304,$2305 VCR + mmio.vcr = 0x0000; + + //$2306-$230a MR + mmio.mr = 0; + + //$230b + mmio.overflow = false; +} + +SA1::SA1() : iram(2048) { +} + +} diff --git a/libsnes/snes/chip/sa1/sa1.hpp b/libsnes/snes/chip/sa1/sa1.hpp new file mode 100644 index 0000000..732b2a8 --- /dev/null +++ b/libsnes/snes/chip/sa1/sa1.hpp @@ -0,0 +1,37 @@ +class SA1 : public Coprocessor, public CPUcore { +public: + #include "bus/bus.hpp" + #include "dma/dma.hpp" + #include "memory/memory.hpp" + #include "mmio/mmio.hpp" + + struct Status { + uint8 tick_counter; + + bool interrupt_pending; + + uint16 scanlines; + uint16 vcounter; + uint16 hcounter; + } status; + + static void Enter(); + void enter(); + void tick(); + void op_irq(); + + alwaysinline void trigger_irq(); + alwaysinline void last_cycle(); + alwaysinline bool interrupt_pending(); + + void init(); + void load(); + void unload(); + void power(); + void reset(); + + void serialize(serializer&); + SA1(); +}; + +extern SA1 sa1; diff --git a/libsnes/snes/chip/sa1/serialization.cpp b/libsnes/snes/chip/sa1/serialization.cpp new file mode 100644 index 0000000..3d75588 --- /dev/null +++ b/libsnes/snes/chip/sa1/serialization.cpp @@ -0,0 +1,148 @@ +#ifdef SA1_CPP + +void SA1::serialize(serializer &s) { + Processor::serialize(s); + CPUcore::core_serialize(s); + + //sa1.hpp + s.integer(status.tick_counter); + + s.integer(status.interrupt_pending); + + s.integer(status.scanlines); + s.integer(status.vcounter); + s.integer(status.hcounter); + + //bus/bus.hpp + s.array(iram.data(), iram.size()); + + s.integer(cpubwram.dma); + + //dma/dma.hpp + s.integer(dma.line); + + //mmio/mmio.hpp + s.integer(mmio.sa1_irq); + s.integer(mmio.sa1_rdyb); + s.integer(mmio.sa1_resb); + s.integer(mmio.sa1_nmi); + s.integer(mmio.smeg); + + s.integer(mmio.cpu_irqen); + s.integer(mmio.chdma_irqen); + + s.integer(mmio.cpu_irqcl); + s.integer(mmio.chdma_irqcl); + + s.integer(mmio.crv); + + s.integer(mmio.cnv); + + s.integer(mmio.civ); + + s.integer(mmio.cpu_irq); + s.integer(mmio.cpu_ivsw); + s.integer(mmio.cpu_nvsw); + s.integer(mmio.cmeg); + + s.integer(mmio.sa1_irqen); + s.integer(mmio.timer_irqen); + s.integer(mmio.dma_irqen); + s.integer(mmio.sa1_nmien); + + s.integer(mmio.sa1_irqcl); + s.integer(mmio.timer_irqcl); + s.integer(mmio.dma_irqcl); + s.integer(mmio.sa1_nmicl); + + s.integer(mmio.snv); + + s.integer(mmio.siv); + + s.integer(mmio.hvselb); + s.integer(mmio.ven); + s.integer(mmio.hen); + + s.integer(mmio.hcnt); + + s.integer(mmio.vcnt); + + s.integer(mmio.cbmode); + s.integer(mmio.cb); + + s.integer(mmio.dbmode); + s.integer(mmio.db); + + s.integer(mmio.ebmode); + s.integer(mmio.eb); + + s.integer(mmio.fbmode); + s.integer(mmio.fb); + + s.integer(mmio.sbm); + + s.integer(mmio.sw46); + s.integer(mmio.cbm); + + s.integer(mmio.swen); + + s.integer(mmio.cwen); + + s.integer(mmio.bwp); + + s.integer(mmio.siwp); + + s.integer(mmio.ciwp); + + s.integer(mmio.dmaen); + s.integer(mmio.dprio); + s.integer(mmio.cden); + s.integer(mmio.cdsel); + s.integer(mmio.dd); + s.integer(mmio.sd); + + s.integer(mmio.chdend); + s.integer(mmio.dmasize); + s.integer(mmio.dmacb); + + s.integer(mmio.dsa); + + s.integer(mmio.dda); + + s.integer(mmio.dtc); + + s.integer(mmio.bbf); + + s.array(mmio.brf); + + s.integer(mmio.acm); + s.integer(mmio.md); + + s.integer(mmio.ma); + + s.integer(mmio.mb); + + s.integer(mmio.hl); + s.integer(mmio.vb); + + s.integer(mmio.va); + s.integer(mmio.vbit); + + s.integer(mmio.cpu_irqfl); + s.integer(mmio.chdma_irqfl); + + s.integer(mmio.sa1_irqfl); + s.integer(mmio.timer_irqfl); + s.integer(mmio.dma_irqfl); + s.integer(mmio.sa1_nmifl); + + s.integer(mmio.hcr); + + s.integer(mmio.vcr); + + s.integer(mmio.mr); + + s.integer(mmio.overflow); +} + +#endif diff --git a/libsnes/snes/chip/sdd1/decomp.cpp b/libsnes/snes/chip/sdd1/decomp.cpp new file mode 100644 index 0000000..2f8f7cb --- /dev/null +++ b/libsnes/snes/chip/sdd1/decomp.cpp @@ -0,0 +1,289 @@ +//S-DD1 decompression algorithm implementation +//original code written by Andreas Naive (public domain license) +//bsnes port written by byuu + +//note: decompression module does not need to be serialized with bsnes +//this is because decompression only runs during DMA, and bsnes will complete +//any pending DMA transfers prior to serialization. + +//input manager + +void SDD1::Decomp::IM::init(unsigned offset_) { + offset = offset_; + bit_count = 4; +} + +uint8 SDD1::Decomp::IM::get_codeword(uint8 code_length) { + uint8 codeword; + uint8 comp_count; + + codeword = self.rom_read(offset) << bit_count; + bit_count++; + + if(codeword & 0x80) { + codeword |= self.rom_read(offset + 1) >> (9 - bit_count); + bit_count += code_length; + } + + if(bit_count & 0x08) { + offset++; + bit_count &= 0x07; + } + + return codeword; +} + +//golomb-code decoder + +const uint8 SDD1::Decomp::GCD::run_count[] = { + 0x00, 0x00, 0x01, 0x00, 0x03, 0x01, 0x02, 0x00, + 0x07, 0x03, 0x05, 0x01, 0x06, 0x02, 0x04, 0x00, + 0x0f, 0x07, 0x0b, 0x03, 0x0d, 0x05, 0x09, 0x01, + 0x0e, 0x06, 0x0a, 0x02, 0x0c, 0x04, 0x08, 0x00, + 0x1f, 0x0f, 0x17, 0x07, 0x1b, 0x0b, 0x13, 0x03, + 0x1d, 0x0d, 0x15, 0x05, 0x19, 0x09, 0x11, 0x01, + 0x1e, 0x0e, 0x16, 0x06, 0x1a, 0x0a, 0x12, 0x02, + 0x1c, 0x0c, 0x14, 0x04, 0x18, 0x08, 0x10, 0x00, + 0x3f, 0x1f, 0x2f, 0x0f, 0x37, 0x17, 0x27, 0x07, + 0x3b, 0x1b, 0x2b, 0x0b, 0x33, 0x13, 0x23, 0x03, + 0x3d, 0x1d, 0x2d, 0x0d, 0x35, 0x15, 0x25, 0x05, + 0x39, 0x19, 0x29, 0x09, 0x31, 0x11, 0x21, 0x01, + 0x3e, 0x1e, 0x2e, 0x0e, 0x36, 0x16, 0x26, 0x06, + 0x3a, 0x1a, 0x2a, 0x0a, 0x32, 0x12, 0x22, 0x02, + 0x3c, 0x1c, 0x2c, 0x0c, 0x34, 0x14, 0x24, 0x04, + 0x38, 0x18, 0x28, 0x08, 0x30, 0x10, 0x20, 0x00, + 0x7f, 0x3f, 0x5f, 0x1f, 0x6f, 0x2f, 0x4f, 0x0f, + 0x77, 0x37, 0x57, 0x17, 0x67, 0x27, 0x47, 0x07, + 0x7b, 0x3b, 0x5b, 0x1b, 0x6b, 0x2b, 0x4b, 0x0b, + 0x73, 0x33, 0x53, 0x13, 0x63, 0x23, 0x43, 0x03, + 0x7d, 0x3d, 0x5d, 0x1d, 0x6d, 0x2d, 0x4d, 0x0d, + 0x75, 0x35, 0x55, 0x15, 0x65, 0x25, 0x45, 0x05, + 0x79, 0x39, 0x59, 0x19, 0x69, 0x29, 0x49, 0x09, + 0x71, 0x31, 0x51, 0x11, 0x61, 0x21, 0x41, 0x01, + 0x7e, 0x3e, 0x5e, 0x1e, 0x6e, 0x2e, 0x4e, 0x0e, + 0x76, 0x36, 0x56, 0x16, 0x66, 0x26, 0x46, 0x06, + 0x7a, 0x3a, 0x5a, 0x1a, 0x6a, 0x2a, 0x4a, 0x0a, + 0x72, 0x32, 0x52, 0x12, 0x62, 0x22, 0x42, 0x02, + 0x7c, 0x3c, 0x5c, 0x1c, 0x6c, 0x2c, 0x4c, 0x0c, + 0x74, 0x34, 0x54, 0x14, 0x64, 0x24, 0x44, 0x04, + 0x78, 0x38, 0x58, 0x18, 0x68, 0x28, 0x48, 0x08, + 0x70, 0x30, 0x50, 0x10, 0x60, 0x20, 0x40, 0x00, +}; + +void SDD1::Decomp::GCD::get_run_count(uint8 code_number, uint8 &mps_count, bool &lps_index) { + uint8 codeword = self.im.get_codeword(code_number); + + if(codeword & 0x80) { + lps_index = 1; + mps_count = run_count[codeword >> (code_number ^ 0x07)]; + } else { + mps_count = 1 << code_number; + } +} + +//bits generator + +void SDD1::Decomp::BG::init() { + mps_count = 0; + lps_index = 0; +} + +uint8 SDD1::Decomp::BG::get_bit(bool &end_of_run) { + if(!(mps_count || lps_index)) self.gcd.get_run_count(code_number, mps_count, lps_index); + + uint8 bit; + if(mps_count) { + bit = 0; + mps_count--; + } else { + bit = 1; + lps_index = 0; + } + + end_of_run = !(mps_count || lps_index); + return bit; +} + +//probability estimation module + +const SDD1::Decomp::PEM::State SDD1::Decomp::PEM::evolution_table[33] = { + { 0, 25, 25 }, + { 0, 2, 1 }, + { 0, 3, 1 }, + { 0, 4, 2 }, + { 0, 5, 3 }, + { 1, 6, 4 }, + { 1, 7, 5 }, + { 1, 8, 6 }, + { 1, 9, 7 }, + { 2, 10, 8 }, + { 2, 11, 9 }, + { 2, 12, 10 }, + { 2, 13, 11 }, + { 3, 14, 12 }, + { 3, 15, 13 }, + { 3, 16, 14 }, + { 3, 17, 15 }, + { 4, 18, 16 }, + { 4, 19, 17 }, + { 5, 20, 18 }, + { 5, 21, 19 }, + { 6, 22, 20 }, + { 6, 23, 21 }, + { 7, 24, 22 }, + { 7, 24, 23 }, + { 0, 26, 1 }, + { 1, 27, 2 }, + { 2, 28, 4 }, + { 3, 29, 8 }, + { 4, 30, 12 }, + { 5, 31, 16 }, + { 6, 32, 18 }, + { 7, 24, 22 }, +}; + +void SDD1::Decomp::PEM::init() { + for(unsigned i = 0; i < 32; i++) { + context_info[i].status = 0; + context_info[i].mps = 0; + } +} + +uint8 SDD1::Decomp::PEM::get_bit(uint8 context) { + ContextInfo &info = context_info[context]; + uint8 current_status = info.status; + uint8 current_mps = info.mps; + const State &s = SDD1::Decomp::PEM::evolution_table[current_status]; + + uint8 bit; + bool end_of_run; + switch(s.code_number) { + case 0: bit = self.bg0.get_bit(end_of_run); break; + case 1: bit = self.bg1.get_bit(end_of_run); break; + case 2: bit = self.bg2.get_bit(end_of_run); break; + case 3: bit = self.bg3.get_bit(end_of_run); break; + case 4: bit = self.bg4.get_bit(end_of_run); break; + case 5: bit = self.bg5.get_bit(end_of_run); break; + case 6: bit = self.bg6.get_bit(end_of_run); break; + case 7: bit = self.bg7.get_bit(end_of_run); break; + } + + if(end_of_run) { + if(bit) { + if(!(current_status & 0xfe)) info.mps ^= 0x01; + info.status = s.next_if_lps; + } else { + info.status = s.next_if_mps; + } + } + + return bit ^ current_mps; +} + +//context model + +void SDD1::Decomp::CM::init(unsigned offset) { + bitplanes_info = self.rom_read(offset) & 0xc0; + context_bits_info = self.rom_read(offset) & 0x30; + bit_number = 0; + for(unsigned i = 0; i < 8; i++) previous_bitplane_bits[i] = 0; + switch(bitplanes_info) { + case 0x00: current_bitplane = 1; break; + case 0x40: current_bitplane = 7; break; + case 0x80: current_bitplane = 3; break; + } +} + +uint8 SDD1::Decomp::CM::get_bit() { + switch(bitplanes_info) { + case 0x00: + current_bitplane ^= 0x01; + break; + case 0x40: + current_bitplane ^= 0x01; + if(!(bit_number & 0x7f)) current_bitplane = ((current_bitplane + 2) & 0x07); + break; + case 0x80: + current_bitplane ^= 0x01; + if(!(bit_number & 0x7f)) current_bitplane ^= 0x02; + break; + case 0xc0: + current_bitplane = bit_number & 0x07; + break; + } + + uint16 &context_bits = previous_bitplane_bits[current_bitplane]; + uint8 current_context = (current_bitplane & 0x01) << 4; + switch(context_bits_info) { + case 0x00: current_context |= ((context_bits & 0x01c0) >> 5) | (context_bits & 0x0001); break; + case 0x10: current_context |= ((context_bits & 0x0180) >> 5) | (context_bits & 0x0001); break; + case 0x20: current_context |= ((context_bits & 0x00c0) >> 5) | (context_bits & 0x0001); break; + case 0x30: current_context |= ((context_bits & 0x0180) >> 5) | (context_bits & 0x0003); break; + } + + uint8 bit = self.pem.get_bit(current_context); + context_bits <<= 1; + context_bits |= bit; + bit_number++; + return bit; +} + +//output logic + +void SDD1::Decomp::OL::init(unsigned offset) { + bitplanes_info = self.rom_read(offset) & 0xc0; + r0 = 0x01; +} + +uint8 SDD1::Decomp::OL::decompress() { + switch(bitplanes_info) { + case 0x00: case 0x40: case 0x80: + if(r0 == 0) { + r0 = ~r0; + return r2; + } + for(r0 = 0x80, r1 = 0, r2 = 0; r0; r0 >>= 1) { + if(self.cm.get_bit()) r1 |= r0; + if(self.cm.get_bit()) r2 |= r0; + } + return r1; + case 0xc0: + for(r0 = 0x01, r1 = 0; r0; r0 <<= 1) { + if(self.cm.get_bit()) r1 |= r0; + } + return r1; + } + + return 0; // Not verifed. +} + +//core + +void SDD1::Decomp::init(unsigned offset) { + im.init(offset); + bg0.init(); + bg1.init(); + bg2.init(); + bg3.init(); + bg4.init(); + bg5.init(); + bg6.init(); + bg7.init(); + pem.init(); + cm.init(offset); + ol.init(offset); +} + +uint8 SDD1::Decomp::read() { + return ol.decompress(); +} + +uint8 SDD1::Decomp::rom_read(unsigned offset) { + return sdd1.rom_read(offset); +} + +SDD1::Decomp::Decomp() : im(*this), gcd(*this), + bg0(*this, 0), bg1(*this, 1), bg2(*this, 2), bg3(*this, 3), + bg4(*this, 4), bg5(*this, 5), bg6(*this, 6), bg7(*this, 7), + pem(*this), cm(*this), ol(*this) { +} diff --git a/libsnes/snes/chip/sdd1/decomp.hpp b/libsnes/snes/chip/sdd1/decomp.hpp new file mode 100644 index 0000000..6c83d1d --- /dev/null +++ b/libsnes/snes/chip/sdd1/decomp.hpp @@ -0,0 +1,82 @@ +struct Decomp { + struct IM { //input manager + Decomp &self; + void init(unsigned offset); + uint8 get_codeword(uint8 code_length); + IM(SDD1::Decomp &self) : self(self) {} + private: + unsigned offset; + unsigned bit_count; + }; + + struct GCD { //golomb-code decoder + Decomp &self; + static const uint8 run_count[256]; + void get_run_count(uint8 code_number, uint8 &mps_count, bool &lps_index); + GCD(SDD1::Decomp &self) : self(self) {} + }; + + struct BG { //bits generator + Decomp &self; + void init(); + uint8 get_bit(bool &end_of_run); + BG(SDD1::Decomp &self, uint8 code_number) : self(self), code_number(code_number) {} + private: + const uint8 code_number; + uint8 mps_count; + bool lps_index; + }; + + struct PEM { //probability estimation module + Decomp &self; + void init(); + uint8 get_bit(uint8 context); + PEM(SDD1::Decomp &self) : self(self) {} + private: + struct State { + uint8 code_number; + uint8 next_if_mps; + uint8 next_if_lps; + }; + static const State evolution_table[33]; + struct ContextInfo { + uint8 status; + uint8 mps; + } context_info[32]; + }; + + struct CM { //context model + Decomp &self; + void init(unsigned offset); + uint8 get_bit(); + CM(SDD1::Decomp &self) : self(self) {} + private: + uint8 bitplanes_info; + uint8 context_bits_info; + uint8 bit_number; + uint8 current_bitplane; + uint16 previous_bitplane_bits[8]; + }; + + struct OL { //output logic + Decomp &self; + void init(unsigned offset); + uint8 decompress(); + OL(SDD1::Decomp &self) : self(self) {} + private: + uint8 bitplanes_info; + uint8 r0, r1, r2; + }; + + void init(unsigned offset); + uint8 read(); + uint8 rom_read(unsigned offset); + Decomp(); + + IM im; + GCD gcd; + BG bg0, bg1, bg2, bg3, bg4, bg5, bg6, bg7; + PEM pem; + CM cm; + OL ol; +}; diff --git a/libsnes/snes/chip/sdd1/sdd1.cpp b/libsnes/snes/chip/sdd1/sdd1.cpp new file mode 100644 index 0000000..f188bed --- /dev/null +++ b/libsnes/snes/chip/sdd1/sdd1.cpp @@ -0,0 +1,150 @@ +#include + +#define SDD1_CPP +namespace SNES { + +SDD1 sdd1; + +#include "decomp.cpp" +#include "serialization.cpp" + +void SDD1::init() { +} + +void SDD1::load() { + //hook S-CPU DMA MMIO registers to gather information for struct dma[]; + //buffer address and transfer size information for use in SDD1::mcu_read() + function reader(&SDD1::mmio_read, &sdd1); + function writer(&SDD1::mmio_write, &sdd1); + bus.map(Bus::MapMode::Direct, 0x00, 0x3f, 0x4300, 0x437f, reader, writer); + bus.map(Bus::MapMode::Direct, 0x80, 0xbf, 0x4300, 0x437f, reader, writer); +} + +void SDD1::unload() { +} + +void SDD1::power() { +} + +void SDD1::reset() { + sdd1_enable = 0x00; + xfer_enable = 0x00; + dma_ready = false; + + mmc[0] = 0 << 20; + mmc[1] = 1 << 20; + mmc[2] = 2 << 20; + mmc[3] = 3 << 20; + + for(unsigned i = 0; i < 8; i++) { + dma[i].addr = 0; + dma[i].size = 0; + } +} + +uint8 SDD1::mmio_read(unsigned addr) { + addr &= 0xffff; + + if((addr & 0x4380) == 0x4300) { + return cpu.mmio_read(addr); + } + + switch(addr) { + case 0x4804: return mmc[0] >> 20; + case 0x4805: return mmc[1] >> 20; + case 0x4806: return mmc[2] >> 20; + case 0x4807: return mmc[3] >> 20; + } + + return cpu.regs.mdr; +} + +void SDD1::mmio_write(unsigned addr, uint8 data) { + addr &= 0xffff; + + 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 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_write(addr, data); + } + + switch(addr) { + case 0x4800: sdd1_enable = data; break; + case 0x4801: xfer_enable = data; break; + + case 0x4804: mmc[0] = data << 20; break; + case 0x4805: mmc[1] = data << 20; break; + case 0x4806: mmc[2] = data << 20; break; + case 0x4807: mmc[3] = data << 20; break; + } +} + +uint8 SDD1::rom_read(unsigned addr) { + return cartridge.rom.read(mmc[(addr >> 20) & 3] + (addr & 0x0fffff)); +} + +//SDD1::mcu_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::mcu_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(!dma_ready) { + //prepare streaming decompression + decomp.init(addr); + dma_ready = true; + } + + //fetch a decompressed byte; once finished, disable channel and invalidate buffer + uint8 data = decomp.read(); + if(--dma[i].size == 0) { + dma_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 cartridge.rom.read(mmc[(addr >> 20) & 3] + (addr & 0x0fffff)); +} + +void SDD1::mcu_write(unsigned addr, uint8 data) { +} + +SDD1::SDD1() { +} + +SDD1::~SDD1() { +} + +} diff --git a/libsnes/snes/chip/sdd1/sdd1.hpp b/libsnes/snes/chip/sdd1/sdd1.hpp new file mode 100644 index 0000000..b0af2ac --- /dev/null +++ b/libsnes/snes/chip/sdd1/sdd1.hpp @@ -0,0 +1,36 @@ +class SDD1 { +public: + void init(); + void load(); + void unload(); + void power(); + void reset(); + + uint8 mmio_read(unsigned addr); + void mmio_write(unsigned addr, uint8 data); + + uint8 rom_read(unsigned addr); + uint8 mcu_read(unsigned addr); + void mcu_write(unsigned addr, uint8 data); + + void serialize(serializer&); + SDD1(); + ~SDD1(); + +private: + uint8 sdd1_enable; //channel bit-mask + uint8 xfer_enable; //channel bit-mask + bool dma_ready; //used to initialize decompression module + 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]; + +public: + #include "decomp.hpp" + Decomp decomp; +}; + +extern SDD1 sdd1; diff --git a/libsnes/snes/chip/sdd1/serialization.cpp b/libsnes/snes/chip/sdd1/serialization.cpp new file mode 100644 index 0000000..1741e1a --- /dev/null +++ b/libsnes/snes/chip/sdd1/serialization.cpp @@ -0,0 +1,15 @@ +#ifdef SDD1_CPP + +void SDD1::serialize(serializer &s) { + s.integer(sdd1_enable); + s.integer(xfer_enable); + s.integer(dma_ready); + s.array(mmc); + + for(unsigned n = 0; n < 8; n++) { + s.integer(dma[n].addr); + s.integer(dma[n].size); + } +} + +#endif diff --git a/libsnes/snes/chip/spc7110/decomp.cpp b/libsnes/snes/chip/spc7110/decomp.cpp new file mode 100644 index 0000000..d6b4cce --- /dev/null +++ b/libsnes/snes/chip/spc7110/decomp.cpp @@ -0,0 +1,498 @@ +#ifdef SPC7110_CPP + +uint8 SPC7110::Decomp::read() { + if(decomp_buffer_length == 0) { + //decompress at least (decomp_buffer_size / 2) bytes to the buffer + switch(decomp_mode) { + case 0: mode0(false); break; + case 1: mode1(false); break; + case 2: mode2(false); break; + default: return 0x00; + } + } + + uint8 data = decomp_buffer[decomp_buffer_rdoffset++]; + decomp_buffer_rdoffset &= decomp_buffer_size - 1; + decomp_buffer_length--; + return data; +} + +void SPC7110::Decomp::write(uint8 data) { + decomp_buffer[decomp_buffer_wroffset++] = data; + decomp_buffer_wroffset &= decomp_buffer_size - 1; + decomp_buffer_length++; +} + +uint8 SPC7110::Decomp::dataread() { + unsigned size = cartridge.rom.size() - spc7110.data_rom_offset; + while(decomp_offset >= size) decomp_offset -= size; + return cartridge.rom.read(spc7110.data_rom_offset + decomp_offset++); +} + +void SPC7110::Decomp::init(unsigned mode, unsigned offset, unsigned index) { + decomp_mode = mode; + decomp_offset = offset; + + decomp_buffer_rdoffset = 0; + decomp_buffer_wroffset = 0; + decomp_buffer_length = 0; + + //reset context states + for(unsigned i = 0; i < 32; i++) { + context[i].index = 0; + context[i].invert = 0; + } + + switch(decomp_mode) { + case 0: mode0(true); break; + case 1: mode1(true); break; + case 2: mode2(true); break; + } + + //decompress up to requested output data index + while(index--) read(); +} + +// + +void SPC7110::Decomp::mode0(bool init) { + static uint8 val, in, span; + static int out, inverts, lps, in_count; + + if(init == true) { + out = inverts = lps = 0; + span = 0xff; + val = dataread(); + in = dataread(); + in_count = 8; + return; + } + + while(decomp_buffer_length < (decomp_buffer_size >> 1)) { + for(unsigned bit = 0; bit < 8; bit++) { + //get context + uint8 mask = (1 << (bit & 3)) - 1; + uint8 con = mask + ((inverts & mask) ^ (lps & mask)); + if(bit > 3) con += 15; + + //get prob and mps + unsigned prob = probability(con); + unsigned mps = (((out >> 15) & 1) ^ context[con].invert); + + //get bit + unsigned flag_lps; + if(val <= span - prob) { //mps + span = span - prob; + out = (out << 1) + mps; + flag_lps = 0; + } else { //lps + val = val - (span - (prob - 1)); + span = prob - 1; + out = (out << 1) + 1 - mps; + flag_lps = 1; + } + + //renormalize + unsigned shift = 0; + while(span < 0x7f) { + shift++; + + span = (span << 1) + 1; + val = (val << 1) + (in >> 7); + + in <<= 1; + if(--in_count == 0) { + in = dataread(); + in_count = 8; + } + } + + //update processing info + lps = (lps << 1) + flag_lps; + inverts = (inverts << 1) + context[con].invert; + + //update context state + if(flag_lps & toggle_invert(con)) context[con].invert ^= 1; + if(flag_lps) context[con].index = next_lps(con); + else if(shift) context[con].index = next_mps(con); + } + + //save byte + write(out); + } +} + +void SPC7110::Decomp::mode1(bool init) { + static int pixelorder[4], realorder[4]; + static uint8 in, val, span; + static int out, inverts, lps, in_count; + + if(init == true) { + for(unsigned i = 0; i < 4; i++) pixelorder[i] = i; + out = inverts = lps = 0; + span = 0xff; + val = dataread(); + in = dataread(); + in_count = 8; + return; + } + + while(decomp_buffer_length < (decomp_buffer_size >> 1)) { + for(unsigned pixel = 0; pixel < 8; pixel++) { + //get first symbol context + unsigned a = ((out >> (1 * 2)) & 3); + unsigned b = ((out >> (7 * 2)) & 3); + unsigned c = ((out >> (8 * 2)) & 3); + unsigned con = (a == b) ? (b != c) : (b == c) ? 2 : 4 - (a == c); + + //update pixel order + unsigned m, n; + for(m = 0; m < 4; m++) if(pixelorder[m] == a) break; + for(n = m; n > 0; n--) pixelorder[n] = pixelorder[n - 1]; + pixelorder[0] = a; + + //calculate the real pixel order + for(m = 0; m < 4; m++) realorder[m] = pixelorder[m]; + + //rotate reference pixel c value to top + for(m = 0; m < 4; m++) if(realorder[m] == c) break; + for(n = m; n > 0; n--) realorder[n] = realorder[n - 1]; + realorder[0] = c; + + //rotate reference pixel b value to top + for(m = 0; m < 4; m++) if(realorder[m] == b) break; + for(n = m; n > 0; n--) realorder[n] = realorder[n - 1]; + realorder[0] = b; + + //rotate reference pixel a value to top + for(m = 0; m < 4; m++) if(realorder[m] == a) break; + for(n = m; n > 0; n--) realorder[n] = realorder[n - 1]; + realorder[0] = a; + + //get 2 symbols + for(unsigned bit = 0; bit < 2; bit++) { + //get prob + unsigned prob = probability(con); + + //get symbol + unsigned flag_lps; + if(val <= span - prob) { //mps + span = span - prob; + flag_lps = 0; + } else { //lps + val = val - (span - (prob - 1)); + span = prob - 1; + flag_lps = 1; + } + + //renormalize + unsigned shift = 0; + while(span < 0x7f) { + shift++; + + span = (span << 1) + 1; + val = (val << 1) + (in >> 7); + + in <<= 1; + if(--in_count == 0) { + in = dataread(); + in_count = 8; + } + } + + //update processing info + lps = (lps << 1) + flag_lps; + inverts = (inverts << 1) + context[con].invert; + + //update context state + if(flag_lps & toggle_invert(con)) context[con].invert ^= 1; + if(flag_lps) context[con].index = next_lps(con); + else if(shift) context[con].index = next_mps(con); + + //get next context + con = 5 + (con << 1) + ((lps ^ inverts) & 1); + } + + //get pixel + b = realorder[(lps ^ inverts) & 3]; + out = (out << 2) + b; + } + + //turn pixel data into bitplanes + unsigned data = deinterleave_2x8(out); + write(data >> 8); + write(data >> 0); + } +} + +void SPC7110::Decomp::mode2(bool init) { + static int pixelorder[16], realorder[16]; + static uint8 bitplanebuffer[16], buffer_index; + static uint8 in, val, span; + static int out0, out1, inverts, lps, in_count; + + if(init == true) { + for(unsigned i = 0; i < 16; i++) pixelorder[i] = i; + buffer_index = 0; + out0 = out1 = inverts = lps = 0; + span = 0xff; + val = dataread(); + in = dataread(); + in_count = 8; + return; + } + + while(decomp_buffer_length < (decomp_buffer_size >> 1)) { + for(unsigned pixel = 0; pixel < 8; pixel++) { + //get first symbol context + unsigned a = ((out0 >> (0 * 4)) & 15); + unsigned b = ((out0 >> (7 * 4)) & 15); + unsigned c = ((out1 >> (0 * 4)) & 15); + unsigned con = 0; + unsigned refcon = (a == b) ? (b != c) : (b == c) ? 2 : 4 - (a == c); + + //update pixel order + unsigned m, n; + for(m = 0; m < 16; m++) if(pixelorder[m] == a) break; + for(n = m; n > 0; n--) pixelorder[n] = pixelorder[n - 1]; + pixelorder[0] = a; + + //calculate the real pixel order + for(m = 0; m < 16; m++) realorder[m] = pixelorder[m]; + + //rotate reference pixel c value to top + for(m = 0; m < 16; m++) if(realorder[m] == c) break; + for(n = m; n > 0; n--) realorder[n] = realorder[n - 1]; + realorder[0] = c; + + //rotate reference pixel b value to top + for(m = 0; m < 16; m++) if(realorder[m] == b) break; + for(n = m; n > 0; n--) realorder[n] = realorder[n - 1]; + realorder[0] = b; + + //rotate reference pixel a value to top + for(m = 0; m < 16; m++) if(realorder[m] == a) break; + for(n = m; n > 0; n--) realorder[n] = realorder[n - 1]; + realorder[0] = a; + + //get 4 symbols + for(unsigned bit = 0; bit < 4; bit++) { + //get prob + unsigned prob = probability(con); + + //get symbol + unsigned flag_lps; + if(val <= span - prob) { //mps + span = span - prob; + flag_lps = 0; + } else { //lps + val = val - (span - (prob - 1)); + span = prob - 1; + flag_lps = 1; + } + + //renormalize + unsigned shift = 0; + while(span < 0x7f) { + shift++; + + span = (span << 1) + 1; + val = (val << 1) + (in >> 7); + + in <<= 1; + if(--in_count == 0) { + in = dataread(); + in_count = 8; + } + } + + //update processing info + lps = (lps << 1) + flag_lps; + unsigned invertbit = context[con].invert; + inverts = (inverts << 1) + invertbit; + + //update context state + if(flag_lps & toggle_invert(con)) context[con].invert ^= 1; + if(flag_lps) context[con].index = next_lps(con); + else if(shift) context[con].index = next_mps(con); + + //get next context + con = mode2_context_table[con][flag_lps ^ invertbit] + (con == 1 ? refcon : 0); + } + + //get pixel + b = realorder[(lps ^ inverts) & 0x0f]; + out1 = (out1 << 4) + ((out0 >> 28) & 0x0f); + out0 = (out0 << 4) + b; + } + + //convert pixel data into bitplanes + unsigned data = deinterleave_4x8(out0); + write(data >> 24); + write(data >> 16); + bitplanebuffer[buffer_index++] = data >> 8; + bitplanebuffer[buffer_index++] = data >> 0; + + if(buffer_index == 16) { + for(unsigned i = 0; i < 16; i++) write(bitplanebuffer[i]); + buffer_index = 0; + } + } +} + +// + +const uint8 SPC7110::Decomp::evolution_table[53][4] = { +//{ prob, nextlps, nextmps, toggle invert }, + + { 0x5a, 1, 1, 1 }, + { 0x25, 6, 2, 0 }, + { 0x11, 8, 3, 0 }, + { 0x08, 10, 4, 0 }, + { 0x03, 12, 5, 0 }, + { 0x01, 15, 5, 0 }, + + { 0x5a, 7, 7, 1 }, + { 0x3f, 19, 8, 0 }, + { 0x2c, 21, 9, 0 }, + { 0x20, 22, 10, 0 }, + { 0x17, 23, 11, 0 }, + { 0x11, 25, 12, 0 }, + { 0x0c, 26, 13, 0 }, + { 0x09, 28, 14, 0 }, + { 0x07, 29, 15, 0 }, + { 0x05, 31, 16, 0 }, + { 0x04, 32, 17, 0 }, + { 0x03, 34, 18, 0 }, + { 0x02, 35, 5, 0 }, + + { 0x5a, 20, 20, 1 }, + { 0x48, 39, 21, 0 }, + { 0x3a, 40, 22, 0 }, + { 0x2e, 42, 23, 0 }, + { 0x26, 44, 24, 0 }, + { 0x1f, 45, 25, 0 }, + { 0x19, 46, 26, 0 }, + { 0x15, 25, 27, 0 }, + { 0x11, 26, 28, 0 }, + { 0x0e, 26, 29, 0 }, + { 0x0b, 27, 30, 0 }, + { 0x09, 28, 31, 0 }, + { 0x08, 29, 32, 0 }, + { 0x07, 30, 33, 0 }, + { 0x05, 31, 34, 0 }, + { 0x04, 33, 35, 0 }, + { 0x04, 33, 36, 0 }, + { 0x03, 34, 37, 0 }, + { 0x02, 35, 38, 0 }, + { 0x02, 36, 5, 0 }, + + { 0x58, 39, 40, 1 }, + { 0x4d, 47, 41, 0 }, + { 0x43, 48, 42, 0 }, + { 0x3b, 49, 43, 0 }, + { 0x34, 50, 44, 0 }, + { 0x2e, 51, 45, 0 }, + { 0x29, 44, 46, 0 }, + { 0x25, 45, 24, 0 }, + + { 0x56, 47, 48, 1 }, + { 0x4f, 47, 49, 0 }, + { 0x47, 48, 50, 0 }, + { 0x41, 49, 51, 0 }, + { 0x3c, 50, 52, 0 }, + { 0x37, 51, 43, 0 }, +}; + +const uint8 SPC7110::Decomp::mode2_context_table[32][2] = { +//{ next 0, next 1 }, + + { 1, 2 }, + + { 3, 8 }, + { 13, 14 }, + + { 15, 16 }, + { 17, 18 }, + { 19, 20 }, + { 21, 22 }, + { 23, 24 }, + { 25, 26 }, + { 25, 26 }, + { 25, 26 }, + { 25, 26 }, + { 25, 26 }, + { 27, 28 }, + { 29, 30 }, + + { 31, 31 }, + { 31, 31 }, + { 31, 31 }, + { 31, 31 }, + { 31, 31 }, + { 31, 31 }, + { 31, 31 }, + { 31, 31 }, + { 31, 31 }, + { 31, 31 }, + { 31, 31 }, + { 31, 31 }, + { 31, 31 }, + { 31, 31 }, + { 31, 31 }, + { 31, 31 }, + + { 31, 31 }, +}; + +uint8 SPC7110::Decomp::probability (unsigned n) { return evolution_table[context[n].index][0]; } +uint8 SPC7110::Decomp::next_lps (unsigned n) { return evolution_table[context[n].index][1]; } +uint8 SPC7110::Decomp::next_mps (unsigned n) { return evolution_table[context[n].index][2]; } +bool SPC7110::Decomp::toggle_invert(unsigned n) { return evolution_table[context[n].index][3]; } + +unsigned SPC7110::Decomp::deinterleave_2x8(unsigned data) { + //reverse morton lookup: de-interleave two 8-bit values + //15, 13, 11, 9, 7, 5, 3, 1 -> 15- 8 + //14, 12, 10, 8, 6, 4, 2, 0 -> 7- 0 + unsigned result = 0; + for(unsigned mask = 1u << 15; mask; mask >>= 2) result = (result << 1) | (bool)(data & mask); + for(unsigned mask = 1u << 14; mask; mask >>= 2) result = (result << 1) | (bool)(data & mask); + return result; +} + +unsigned SPC7110::Decomp::deinterleave_4x8(unsigned data) { + //reverse morton lookup: de-interleave four 8-bit values + //31, 27, 23, 19, 15, 11, 7, 3 -> 31-24 + //30, 26, 22, 18, 14, 10, 6, 2 -> 23-16 + //29, 25, 21, 17, 13, 9, 5, 1 -> 15- 8 + //28, 24, 20, 16, 12, 8, 4, 0 -> 7- 0 + unsigned result = 0; + for(unsigned mask = 1u << 31; mask; mask >>= 4) result = (result << 1) | (bool)(data & mask); + for(unsigned mask = 1u << 30; mask; mask >>= 4) result = (result << 1) | (bool)(data & mask); + for(unsigned mask = 1u << 29; mask; mask >>= 4) result = (result << 1) | (bool)(data & mask); + for(unsigned mask = 1u << 28; mask; mask >>= 4) result = (result << 1) | (bool)(data & mask); + return result; +} + +// + +void SPC7110::Decomp::reset() { + //mode 3 is invalid; this is treated as a special case to always return 0x00 + //set to mode 3 so that reading decomp port before starting first decomp will return 0x00 + decomp_mode = 3; + + decomp_buffer_rdoffset = 0; + decomp_buffer_wroffset = 0; + decomp_buffer_length = 0; +} + +SPC7110::Decomp::Decomp() { + decomp_buffer = new uint8_t[decomp_buffer_size]; + reset(); +} + +SPC7110::Decomp::~Decomp() { + delete[] decomp_buffer; +} + +#endif diff --git a/libsnes/snes/chip/spc7110/decomp.hpp b/libsnes/snes/chip/spc7110/decomp.hpp new file mode 100644 index 0000000..c11c6ad --- /dev/null +++ b/libsnes/snes/chip/spc7110/decomp.hpp @@ -0,0 +1,44 @@ +class Decomp { +public: + uint8 read(); + void init(unsigned mode, unsigned offset, unsigned index); + void reset(); + + void serialize(serializer&); + Decomp(); + ~Decomp(); + +private: + unsigned decomp_mode; + unsigned decomp_offset; + + //read() will spool chunks half the size of decomp_buffer_size + enum { decomp_buffer_size = 64 }; //must be >= 64, and must be a power of two + uint8 *decomp_buffer; + unsigned decomp_buffer_rdoffset; + unsigned decomp_buffer_wroffset; + unsigned decomp_buffer_length; + + void write(uint8 data); + uint8 dataread(); + + void mode0(bool init); + void mode1(bool init); + void mode2(bool init); + + static const uint8 evolution_table[53][4]; + static const uint8 mode2_context_table[32][2]; + + struct ContextState { + uint8 index; + uint8 invert; + } context[32]; + + uint8 probability(unsigned n); + uint8 next_lps(unsigned n); + uint8 next_mps(unsigned n); + bool toggle_invert(unsigned n); + + unsigned deinterleave_2x8(unsigned data); + unsigned deinterleave_4x8(unsigned data); +}; diff --git a/libsnes/snes/chip/spc7110/serialization.cpp b/libsnes/snes/chip/spc7110/serialization.cpp new file mode 100644 index 0000000..196d04c --- /dev/null +++ b/libsnes/snes/chip/spc7110/serialization.cpp @@ -0,0 +1,82 @@ +#ifdef SPC7110_CPP + +void SPC7110::Decomp::serialize(serializer &s) { + s.integer(decomp_mode); + s.integer(decomp_offset); + + s.array(decomp_buffer, decomp_buffer_size); + s.integer(decomp_buffer_rdoffset); + s.integer(decomp_buffer_wroffset); + s.integer(decomp_buffer_length); + + for(unsigned n = 0; n < 32; n++) { + s.integer(context[n].index); + s.integer(context[n].invert); + } +} + +void SPC7110::serialize(serializer &s) { + s.integer(r4801); + s.integer(r4802); + s.integer(r4803); + s.integer(r4804); + s.integer(r4805); + s.integer(r4806); + s.integer(r4807); + s.integer(r4808); + s.integer(r4809); + s.integer(r480a); + s.integer(r480b); + s.integer(r480c); + decomp.serialize(s); + + s.integer(r4811); + s.integer(r4812); + s.integer(r4813); + s.integer(r4814); + s.integer(r4815); + s.integer(r4816); + s.integer(r4817); + s.integer(r4818); + s.integer(r481x); + s.integer(r4814_latch); + s.integer(r4815_latch); + + s.integer(r4820); + s.integer(r4821); + s.integer(r4822); + s.integer(r4823); + s.integer(r4824); + s.integer(r4825); + s.integer(r4826); + s.integer(r4827); + s.integer(r4828); + s.integer(r4829); + s.integer(r482a); + s.integer(r482b); + s.integer(r482c); + s.integer(r482d); + s.integer(r482e); + s.integer(r482f); + + s.integer(r4830); + s.integer(r4831); + s.integer(r4832); + s.integer(r4833); + s.integer(r4834); + + s.integer(dx_offset); + s.integer(ex_offset); + s.integer(fx_offset); + + s.integer(r4840); + s.integer(r4841); + s.integer(r4842); + + s.array(rtc); + s.integer(rtc_state); + s.integer(rtc_mode); + s.integer(rtc_index); +} + +#endif diff --git a/libsnes/snes/chip/spc7110/spc7110.cpp b/libsnes/snes/chip/spc7110/spc7110.cpp new file mode 100644 index 0000000..c178afe --- /dev/null +++ b/libsnes/snes/chip/spc7110/spc7110.cpp @@ -0,0 +1,676 @@ +#include +#include + +#define SPC7110_CPP +using std::numeric_limits; +namespace SNES { + +SPC7110 spc7110; + +#include "serialization.cpp" +#include "decomp.cpp" + +const unsigned SPC7110::months[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + +void SPC7110::init() { +} + +void SPC7110::load() { + for(unsigned n = 0; n < 20; n++) rtc[n] = 0xff; + if(cartridge.has_spc7110rtc()) cartridge.nvram.append(Cartridge::NonVolatileRAM( ".rtc", rtc, 20 )); +} + +void SPC7110::unload() { +} + +void SPC7110::power() { +} + +void SPC7110::reset() { + r4801 = 0x00; + r4802 = 0x00; + r4803 = 0x00; + r4804 = 0x00; + r4805 = 0x00; + r4806 = 0x00; + r4807 = 0x00; + r4808 = 0x00; + r4809 = 0x00; + r480a = 0x00; + r480b = 0x00; + r480c = 0x00; + + decomp.reset(); + + r4811 = 0x00; + r4812 = 0x00; + r4813 = 0x00; + r4814 = 0x00; + r4815 = 0x00; + r4816 = 0x00; + r4817 = 0x00; + r4818 = 0x00; + + r481x = 0x00; + r4814_latch = false; + r4815_latch = false; + + r4820 = 0x00; + r4821 = 0x00; + r4822 = 0x00; + r4823 = 0x00; + r4824 = 0x00; + r4825 = 0x00; + r4826 = 0x00; + r4827 = 0x00; + r4828 = 0x00; + r4829 = 0x00; + r482a = 0x00; + r482b = 0x00; + r482c = 0x00; + r482d = 0x00; + r482e = 0x00; + r482f = 0x00; + + r4830 = 0x00; + mmio_write(0x4831, 0); + mmio_write(0x4832, 1); + mmio_write(0x4833, 2); + r4834 = 0x00; + + r4840 = 0x00; + r4841 = 0x00; + r4842 = 0x00; + + if(cartridge.has_spc7110rtc()) { + rtc_state = RTCS_Inactive; + rtc_mode = RTCM_Linear; + rtc_index = 0; + } +} + +unsigned SPC7110::datarom_addr(unsigned addr) { + unsigned size = cartridge.rom.size() - data_rom_offset; + while(addr >= size) addr -= size; + return data_rom_offset + addr; +} + +unsigned SPC7110::data_pointer() { return r4811 + (r4812 << 8) + (r4813 << 16); } +unsigned SPC7110::data_adjust() { return r4814 + (r4815 << 8); } +unsigned SPC7110::data_increment() { return r4816 + (r4817 << 8); } +void SPC7110::set_data_pointer(unsigned addr) { r4811 = addr; r4812 = addr >> 8; r4813 = addr >> 16; } +void SPC7110::set_data_adjust(unsigned addr) { r4814 = addr; r4815 = addr >> 8; } + +void SPC7110::update_time(int offset) { + time_t rtc_time = (rtc[16] << 0) | (rtc[17] << 8) | (rtc[18] << 16) | (rtc[19] << 24); + time_t current_time = SNES::interface->currentTime() - offset; + + //sizeof(time_t) is platform-dependent; though rtc[] needs to be platform-agnostic. + //yet platforms with 32-bit signed time_t will overflow every ~68 years. handle this by + //accounting for overflow at the cost of 1-bit precision (to catch underflow). this will allow + //rtc[] timestamp to remain valid for up to ~34 years from the last update, even if + //time_t overflows. calculation should be valid regardless of number representation, time_t size, + //or whether time_t is signed or unsigned. + time_t diff + = (current_time >= rtc_time) + ? (current_time - rtc_time) + : (numeric_limits::max() - rtc_time + current_time + 1); //compensate for overflow + if(diff > numeric_limits::max() / 2) diff = 0; //compensate for underflow + + bool update = true; + if(rtc[13] & 1) update = false; //do not update if CR0 timer disable flag is set + if(rtc[15] & 3) update = false; //do not update if CR2 timer disable flags are set + + if(diff > 0 && update == true) { + unsigned second = rtc[ 0] + rtc[ 1] * 10; + unsigned minute = rtc[ 2] + rtc[ 3] * 10; + unsigned hour = rtc[ 4] + rtc[ 5] * 10; + unsigned day = rtc[ 6] + rtc[ 7] * 10; + unsigned month = rtc[ 8] + rtc[ 9] * 10; + unsigned year = rtc[10] + rtc[11] * 10; + unsigned weekday = rtc[12]; + + day--; + month--; + year += (year >= 90) ? 1900 : 2000; //range = 1990-2089 + + second += diff; + while(second >= 60) { + second -= 60; + + minute++; + if(minute < 60) continue; + minute = 0; + + hour++; + if(hour < 24) continue; + hour = 0; + + day++; + weekday = (weekday + 1) % 7; + unsigned days = months[month % 12]; + if(days == 28) { + bool leapyear = false; + if((year % 4) == 0) { + leapyear = true; + if((year % 100) == 0 && (year % 400) != 0) leapyear = false; + } + if(leapyear) days++; + } + if(day < days) continue; + day = 0; + + month++; + if(month < 12) continue; + month = 0; + + year++; + } + + day++; + month++; + year %= 100; + + rtc[ 0] = second % 10; + rtc[ 1] = second / 10; + rtc[ 2] = minute % 10; + rtc[ 3] = minute / 10; + rtc[ 4] = hour % 10; + rtc[ 5] = hour / 10; + rtc[ 6] = day % 10; + rtc[ 7] = day / 10; + rtc[ 8] = month % 10; + rtc[ 9] = month / 10; + rtc[10] = year % 10; + rtc[11] = (year / 10) % 10; + rtc[12] = weekday % 7; + } + + rtc[16] = current_time >> 0; + rtc[17] = current_time >> 8; + rtc[18] = current_time >> 16; + rtc[19] = current_time >> 24; +} + +uint8 SPC7110::mmio_read(unsigned addr) { + addr &= 0xffff; + + switch(addr) { + //================== + //decompression unit + //================== + + case 0x4800: { + uint16 counter = (r4809 + (r480a << 8)); + counter--; + r4809 = counter; + r480a = counter >> 8; + return decomp.read(); + } + case 0x4801: return r4801; + case 0x4802: return r4802; + case 0x4803: return r4803; + case 0x4804: return r4804; + case 0x4805: return r4805; + case 0x4806: return r4806; + case 0x4807: return r4807; + case 0x4808: return r4808; + case 0x4809: return r4809; + case 0x480a: return r480a; + case 0x480b: return r480b; + case 0x480c: { + uint8 status = r480c; + r480c &= 0x7f; + return status; + } + + //============== + //data port unit + //============== + + case 0x4810: { + if(r481x != 0x07) return 0x00; + + unsigned addr = data_pointer(); + unsigned adjust = data_adjust(); + if(r4818 & 8) adjust = (int16)adjust; //16-bit sign extend + + unsigned adjustaddr = addr; + if(r4818 & 2) { + adjustaddr += adjust; + set_data_adjust(adjust + 1); + } + + uint8 data = cartridge.rom.read(datarom_addr(adjustaddr)); + if(!(r4818 & 2)) { + unsigned increment = (r4818 & 1) ? data_increment() : 1; + if(r4818 & 4) increment = (int16)increment; //16-bit sign extend + + if((r4818 & 16) == 0) { + set_data_pointer(addr + increment); + } else { + set_data_adjust(adjust + increment); + } + } + + return data; + } + case 0x4811: return r4811; + case 0x4812: return r4812; + case 0x4813: return r4813; + case 0x4814: return r4814; + case 0x4815: return r4815; + case 0x4816: return r4816; + case 0x4817: return r4817; + case 0x4818: return r4818; + case 0x481a: { + if(r481x != 0x07) return 0x00; + + unsigned addr = data_pointer(); + unsigned adjust = data_adjust(); + if(r4818 & 8) adjust = (int16)adjust; //16-bit sign extend + + uint8 data = cartridge.rom.read(datarom_addr(addr + adjust)); + if((r4818 & 0x60) == 0x60) { + if((r4818 & 16) == 0) { + set_data_pointer(addr + adjust); + } else { + set_data_adjust(adjust + adjust); + } + } + + return data; + } + + //========= + //math unit + //========= + + case 0x4820: return r4820; + case 0x4821: return r4821; + case 0x4822: return r4822; + case 0x4823: return r4823; + case 0x4824: return r4824; + case 0x4825: return r4825; + case 0x4826: return r4826; + case 0x4827: return r4827; + case 0x4828: return r4828; + case 0x4829: return r4829; + case 0x482a: return r482a; + case 0x482b: return r482b; + case 0x482c: return r482c; + case 0x482d: return r482d; + case 0x482e: return r482e; + case 0x482f: { + uint8 status = r482f; + r482f &= 0x7f; + return status; + } + + //=================== + //memory mapping unit + //=================== + + case 0x4830: return r4830; + case 0x4831: return r4831; + case 0x4832: return r4832; + case 0x4833: return r4833; + case 0x4834: return r4834; + + //==================== + //real-time clock unit + //==================== + + case 0x4840: return r4840; + case 0x4841: { + if(rtc_state == RTCS_Inactive || rtc_state == RTCS_ModeSelect) return 0x00; + + r4842 = 0x80; + uint8 data = rtc[rtc_index]; + rtc_index = (rtc_index + 1) & 15; + return data; + } + case 0x4842: { + uint8 status = r4842; + r4842 &= 0x7f; + return status; + } + } + + return cpu.regs.mdr; +} + +void SPC7110::mmio_write(unsigned addr, uint8 data) { + addr &= 0xffff; + + switch(addr) { + //================== + //decompression unit + //================== + + case 0x4801: r4801 = data; break; + case 0x4802: r4802 = data; break; + case 0x4803: r4803 = data; break; + case 0x4804: r4804 = data; break; + case 0x4805: r4805 = data; break; + case 0x4806: { + r4806 = data; + + unsigned table = (r4801 + (r4802 << 8) + (r4803 << 16)); + unsigned index = (r4804 << 2); + unsigned length = (r4809 + (r480a << 8)); + unsigned addr = datarom_addr(table + index); + unsigned mode = (cartridge.rom.read(addr + 0)); + unsigned offset = (cartridge.rom.read(addr + 1) << 16) + + (cartridge.rom.read(addr + 2) << 8) + + (cartridge.rom.read(addr + 3) << 0); + + decomp.init(mode, offset, (r4805 + (r4806 << 8)) << mode); + r480c = 0x80; + } break; + + case 0x4807: r4807 = data; break; + case 0x4808: r4808 = data; break; + case 0x4809: r4809 = data; break; + case 0x480a: r480a = data; break; + case 0x480b: r480b = data; break; + + //============== + //data port unit + //============== + + case 0x4811: r4811 = data; r481x |= 0x01; break; + case 0x4812: r4812 = data; r481x |= 0x02; break; + case 0x4813: r4813 = data; r481x |= 0x04; break; + case 0x4814: { + r4814 = data; + r4814_latch = true; + if(!r4815_latch) break; + if(!(r4818 & 2)) break; + if(r4818 & 0x10) break; + + if((r4818 & 0x60) == 0x20) { + unsigned increment = data_adjust() & 0xff; + if(r4818 & 8) increment = (int8)increment; //8-bit sign extend + set_data_pointer(data_pointer() + increment); + } else if((r4818 & 0x60) == 0x40) { + unsigned increment = data_adjust(); + if(r4818 & 8) increment = (int16)increment; //16-bit sign extend + set_data_pointer(data_pointer() + increment); + } + } break; + case 0x4815: { + r4815 = data; + r4815_latch = true; + if(!r4814_latch) break; + if(!(r4818 & 2)) break; + if(r4818 & 0x10) break; + + if((r4818 & 0x60) == 0x20) { + unsigned increment = data_adjust() & 0xff; + if(r4818 & 8) increment = (int8)increment; //8-bit sign extend + set_data_pointer(data_pointer() + increment); + } else if((r4818 & 0x60) == 0x40) { + unsigned increment = data_adjust(); + if(r4818 & 8) increment = (int16)increment; //16-bit sign extend + set_data_pointer(data_pointer() + increment); + } + } break; + case 0x4816: r4816 = data; break; + case 0x4817: r4817 = data; break; + case 0x4818: { + if(r481x != 0x07) break; + + r4818 = data; + r4814_latch = r4815_latch = false; + } break; + + //========= + //math unit + //========= + + case 0x4820: r4820 = data; break; + case 0x4821: r4821 = data; break; + case 0x4822: r4822 = data; break; + case 0x4823: r4823 = data; break; + case 0x4824: r4824 = data; break; + case 0x4825: { + r4825 = data; + + if(r482e & 1) { + //signed 16-bit x 16-bit multiplication + int16 r0 = (int16)(r4824 + (r4825 << 8)); + int16 r1 = (int16)(r4820 + (r4821 << 8)); + + signed result = r0 * r1; + r4828 = result; + r4829 = result >> 8; + r482a = result >> 16; + r482b = result >> 24; + } else { + //unsigned 16-bit x 16-bit multiplication + uint16 r0 = (uint16)(r4824 + (r4825 << 8)); + uint16 r1 = (uint16)(r4820 + (r4821 << 8)); + + unsigned result = r0 * r1; + r4828 = result; + r4829 = result >> 8; + r482a = result >> 16; + r482b = result >> 24; + } + + r482f = 0x80; + } break; + case 0x4826: r4826 = data; break; + case 0x4827: { + r4827 = data; + + if(r482e & 1) { + //signed 32-bit x 16-bit division + int32 dividend = (int32)(r4820 + (r4821 << 8) + (r4822 << 16) + (r4823 << 24)); + int16 divisor = (int16)(r4826 + (r4827 << 8)); + + int32 quotient; + int16 remainder; + + if(divisor) { + quotient = (int32)(dividend / divisor); + remainder = (int32)(dividend % divisor); + } else { + //illegal division by zero + quotient = 0; + remainder = dividend & 0xffff; + } + + r4828 = quotient; + r4829 = quotient >> 8; + r482a = quotient >> 16; + r482b = quotient >> 24; + + r482c = remainder; + r482d = remainder >> 8; + } else { + //unsigned 32-bit x 16-bit division + uint32 dividend = (uint32)(r4820 + (r4821 << 8) + (r4822 << 16) + (r4823 << 24)); + uint16 divisor = (uint16)(r4826 + (r4827 << 8)); + + uint32 quotient; + uint16 remainder; + + if(divisor) { + quotient = (uint32)(dividend / divisor); + remainder = (uint16)(dividend % divisor); + } else { + //illegal division by zero + quotient = 0; + remainder = dividend & 0xffff; + } + + r4828 = quotient; + r4829 = quotient >> 8; + r482a = quotient >> 16; + r482b = quotient >> 24; + + r482c = remainder; + r482d = remainder >> 8; + } + + r482f = 0x80; + } break; + + case 0x482e: { + //reset math unit + r4820 = r4821 = r4822 = r4823 = 0; + r4824 = r4825 = r4826 = r4827 = 0; + r4828 = r4829 = r482a = r482b = 0; + r482c = r482d = 0; + + r482e = data; + } break; + + //=================== + //memory mapping unit + //=================== + + case 0x4830: r4830 = data; break; + + case 0x4831: { + r4831 = data; + dx_offset = datarom_addr(data * 0x100000); + } break; + + case 0x4832: { + r4832 = data; + ex_offset = datarom_addr(data * 0x100000); + } break; + + case 0x4833: { + r4833 = data; + fx_offset = datarom_addr(data * 0x100000); + } break; + + case 0x4834: r4834 = data; break; + + //==================== + //real-time clock unit + //==================== + + case 0x4840: { + r4840 = data; + if(!(r4840 & 1)) { + //disable RTC + rtc_state = RTCS_Inactive; + update_time(); + } else { + //enable RTC + r4842 = 0x80; + rtc_state = RTCS_ModeSelect; + } + } break; + + case 0x4841: { + r4841 = data; + + switch(rtc_state) { + case RTCS_ModeSelect: { + if(data == RTCM_Linear || data == RTCM_Indexed) { + r4842 = 0x80; + rtc_state = RTCS_IndexSelect; + rtc_mode = (RTC_Mode)data; + rtc_index = 0; + } + } break; + + case RTCS_IndexSelect: { + r4842 = 0x80; + rtc_index = data & 15; + if(rtc_mode == RTCM_Linear) rtc_state = RTCS_Write; + } break; + + case RTCS_Write: { + r4842 = 0x80; + + //control register 0 + if(rtc_index == 13) { + //increment second counter + if(data & 2) update_time(+1); + + //round minute counter + if(data & 8) { + update_time(); + + unsigned second = rtc[ 0] + rtc[ 1] * 10; + //clear seconds + rtc[0] = 0; + rtc[1] = 0; + + if(second >= 30) update_time(+60); + } + } + + //control register 2 + if(rtc_index == 15) { + //disable timer and clear second counter + if((data & 1) && !(rtc[15] & 1)) { + update_time(); + + //clear seconds + rtc[0] = 0; + rtc[1] = 0; + } + + //disable timer + if((data & 2) && !(rtc[15] & 2)) { + update_time(); + } + } + + rtc[rtc_index] = data & 15; + rtc_index = (rtc_index + 1) & 15; + } break; + } //switch(rtc_state) + } break; + } +} + +SPC7110::SPC7110() { +} + +//============ +//SPC7110::MCU +//============ + +uint8 SPC7110::mcu_read(unsigned addr) { + if(addr <= 0xdfffff) return cartridge.rom.read(dx_offset + (addr & 0x0fffff)); + if(addr <= 0xefffff) return cartridge.rom.read(ex_offset + (addr & 0x0fffff)); + if(addr <= 0xffffff) return cartridge.rom.read(fx_offset + (addr & 0x0fffff)); + return cpu.regs.mdr; +} + +void SPC7110::mcu_write(unsigned addr, uint8 data) { +} + +//============ +//SPC7110::DCU +//============ + +uint8 SPC7110::dcu_read(unsigned) { + return mmio_read(0x4800); +} + +void SPC7110::dcu_write(unsigned, uint8) { +} + +//============ +//SPC7110::RAM +//============ + +uint8 SPC7110::ram_read(unsigned addr) { + return cartridge.ram.read(addr & 0x1fff); +} + +void SPC7110::ram_write(unsigned addr, uint8 data) { + if(r4830 & 0x80) cartridge.ram.write(addr & 0x1fff, data); +} + +} diff --git a/libsnes/snes/chip/spc7110/spc7110.hpp b/libsnes/snes/chip/spc7110/spc7110.hpp new file mode 100644 index 0000000..003c423 --- /dev/null +++ b/libsnes/snes/chip/spc7110/spc7110.hpp @@ -0,0 +1,131 @@ +//SPC7110 emulator - version 0.05 (2011-06-27) +//Copyright (c) 2008-2011, byuu and neviksti + +class SPC7110 { +public: + uint8 rtc[20]; + unsigned data_rom_offset; + + void init(); + void load(); + void unload(); + void power(); + void reset(); + + unsigned datarom_addr(unsigned addr); + + unsigned data_pointer(); + unsigned data_adjust(); + unsigned data_increment(); + void set_data_pointer(unsigned addr); + void set_data_adjust(unsigned addr); + + void update_time(int offset = 0); + time_t create_time(); + + uint8 mmio_read(unsigned addr); + void mmio_write(unsigned addr, uint8 data); + + uint8 mcu_read(unsigned addr); + void mcu_write(unsigned addr, uint8 data); + + uint8 dcu_read(unsigned); + void dcu_write(unsigned, uint8); + + uint8 ram_read(unsigned addr); + void ram_write(unsigned addr, uint8 data); + + //spc7110decomp + void decomp_init(); + uint8 decomp_read(); + + void serialize(serializer&); + SPC7110(); + +private: + //================== + //decompression unit + //================== + uint8 r4801; //compression table low + uint8 r4802; //compression table high + uint8 r4803; //compression table bank + uint8 r4804; //compression table index + uint8 r4805; //decompression buffer index low + uint8 r4806; //decompression buffer index high + uint8 r4807; //??? + uint8 r4808; //??? + uint8 r4809; //compression length low + uint8 r480a; //compression length high + uint8 r480b; //decompression control register + uint8 r480c; //decompression status + + #include "decomp.hpp" + Decomp decomp; + + //============== + //data port unit + //============== + uint8 r4811; //data pointer low + uint8 r4812; //data pointer high + uint8 r4813; //data pointer bank + uint8 r4814; //data adjust low + uint8 r4815; //data adjust high + uint8 r4816; //data increment low + uint8 r4817; //data increment high + uint8 r4818; //data port control register + + uint8 r481x; + + bool r4814_latch; + bool r4815_latch; + + //========= + //math unit + //========= + uint8 r4820; //16-bit multiplicand B0, 32-bit dividend B0 + uint8 r4821; //16-bit multiplicand B1, 32-bit dividend B1 + uint8 r4822; //32-bit dividend B2 + uint8 r4823; //32-bit dividend B3 + uint8 r4824; //16-bit multiplier B0 + uint8 r4825; //16-bit multiplier B1 + uint8 r4826; //16-bit divisor B0 + uint8 r4827; //16-bit divisor B1 + uint8 r4828; //32-bit product B0, 32-bit quotient B0 + uint8 r4829; //32-bit product B1, 32-bit quotient B1 + uint8 r482a; //32-bit product B2, 32-bit quotient B2 + uint8 r482b; //32-bit product B3, 32-bit quotient B3 + uint8 r482c; //16-bit remainder B0 + uint8 r482d; //16-bit remainder B1 + uint8 r482e; //math control register + uint8 r482f; //math status + + //=================== + //memory mapping unit + //=================== + uint8 r4830; //SRAM write enable + uint8 r4831; //$[d0-df]:[0000-ffff] mapping + uint8 r4832; //$[e0-ef]:[0000-ffff] mapping + uint8 r4833; //$[f0-ff]:[0000-ffff] mapping + uint8 r4834; //??? + + unsigned dx_offset; + unsigned ex_offset; + unsigned fx_offset; + + //==================== + //real-time clock unit + //==================== + uint8 r4840; //RTC latch + uint8 r4841; //RTC index/data port + uint8 r4842; //RTC status + + enum RTC_State { RTCS_Inactive, RTCS_ModeSelect, RTCS_IndexSelect, RTCS_Write }; + enum RTC_Mode { RTCM_Linear = 0x03, RTCM_Indexed = 0x0c }; + unsigned rtc_state; + unsigned rtc_mode; + unsigned rtc_index; + + static const unsigned months[12]; +}; + +extern SPC7110 spc7110; diff --git a/libsnes/snes/chip/srtc/serialization.cpp b/libsnes/snes/chip/srtc/serialization.cpp new file mode 100644 index 0000000..4614112 --- /dev/null +++ b/libsnes/snes/chip/srtc/serialization.cpp @@ -0,0 +1,9 @@ +#ifdef SRTC_CPP + +void SRTC::serialize(serializer &s) { + s.array(rtc); + s.integer(rtc_mode); + s.integer(rtc_index); +} + +#endif diff --git a/libsnes/snes/chip/srtc/srtc.cpp b/libsnes/snes/chip/srtc/srtc.cpp new file mode 100644 index 0000000..ff6a145 --- /dev/null +++ b/libsnes/snes/chip/srtc/srtc.cpp @@ -0,0 +1,234 @@ +#include +#include +#include + +#define SRTC_CPP +using std::numeric_limits; +namespace SNES { + +SRTC srtc; + +#include "serialization.cpp" + +const unsigned SRTC::months[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + +void SRTC::init() { +} + +void SRTC::load() { + for(unsigned n = 0; n < 20; n++) rtc[n] = 0xff; + cartridge.nvram.append(Cartridge::NonVolatileRAM( ".rtc", rtc, 20 )); +} + +void SRTC::unload() { +} + +void SRTC::power() { +} + +void SRTC::reset() { + rtc_mode = RtcRead; + rtc_index = -1; + update_time(); +} + +void SRTC::update_time() { + time_t rtc_time = (rtc[16] << 0) | (rtc[17] << 8) | (rtc[18] << 16) | (rtc[19] << 24); + time_t current_time = SNES::interface->currentTime(); + + //sizeof(time_t) is platform-dependent; though rtc[] needs to be platform-agnostic. + //yet platforms with 32-bit signed time_t will overflow every ~68 years. handle this by + //accounting for overflow at the cost of 1-bit precision (to catch underflow). this will allow + //rtc[] timestamp to remain valid for up to ~34 years from the last update, even if + //time_t overflows. calculation should be valid regardless of number representation, time_t size, + //or whether time_t is signed or unsigned. + time_t diff + = (current_time >= rtc_time) + ? (current_time - rtc_time) + : (numeric_limits::max() - rtc_time + current_time + 1); //compensate for overflow + if(diff > numeric_limits::max() / 2) diff = 0; //compensate for underflow + + if(diff > 0) { + unsigned second = rtc[ 0] + rtc[ 1] * 10; + unsigned minute = rtc[ 2] + rtc[ 3] * 10; + unsigned hour = rtc[ 4] + rtc[ 5] * 10; + unsigned day = rtc[ 6] + rtc[ 7] * 10; + unsigned month = rtc[ 8]; + unsigned year = rtc[ 9] + rtc[10] * 10 + rtc[11] * 100; + unsigned weekday = rtc[12]; + + day--; + month--; + year += 1000; + + second += diff; + while(second >= 60) { + second -= 60; + + minute++; + if(minute < 60) continue; + minute = 0; + + hour++; + if(hour < 24) continue; + hour = 0; + + day++; + weekday = (weekday + 1) % 7; + unsigned days = months[month % 12]; + if(days == 28) { + bool leapyear = false; + if((year % 4) == 0) { + leapyear = true; + if((year % 100) == 0 && (year % 400) != 0) leapyear = false; + } + if(leapyear) days++; + } + if(day < days) continue; + day = 0; + + month++; + if(month < 12) continue; + month = 0; + + year++; + } + + day++; + month++; + year -= 1000; + + rtc[ 0] = second % 10; + rtc[ 1] = second / 10; + rtc[ 2] = minute % 10; + rtc[ 3] = minute / 10; + rtc[ 4] = hour % 10; + rtc[ 5] = hour / 10; + rtc[ 6] = day % 10; + rtc[ 7] = day / 10; + rtc[ 8] = month; + rtc[ 9] = year % 10; + rtc[10] = (year / 10) % 10; + rtc[11] = year / 100; + rtc[12] = weekday % 7; + } + + rtc[16] = current_time >> 0; + rtc[17] = current_time >> 8; + rtc[18] = current_time >> 16; + rtc[19] = current_time >> 24; +} + +//returns day of week for specified date +//eg 0 = Sunday, 1 = Monday, ... 6 = Saturday +//usage: weekday(2008, 1, 1) returns weekday of January 1st, 2008 +unsigned SRTC::weekday(unsigned year, unsigned month, unsigned day) { + unsigned y = 1900, m = 1; //epoch is 1900-01-01 + unsigned sum = 0; //number of days passed since epoch + + year = max(1900, year); + month = max(1, min(12, month)); + day = max(1, min(31, day)); + + while(y < year) { + bool leapyear = false; + if((y % 4) == 0) { + leapyear = true; + if((y % 100) == 0 && (y % 400) != 0) leapyear = false; + } + sum += leapyear ? 366 : 365; + y++; + } + + while(m < month) { + unsigned days = months[m - 1]; + if(days == 28) { + bool leapyear = false; + if((y % 4) == 0) { + leapyear = true; + if((y % 100) == 0 && (y % 400) != 0) leapyear = false; + } + if(leapyear) days++; + } + sum += days; + m++; + } + + sum += day - 1; + return (sum + 1) % 7; //1900-01-01 was a Monday +} + +uint8 SRTC::read(unsigned addr) { + addr &= 0xffff; + + if(addr == 0x2800) { + if(rtc_mode != RtcRead) return 0x00; + + if(rtc_index < 0) { + update_time(); + rtc_index++; + return 0x0f; + } else if(rtc_index > 12) { + rtc_index = -1; + return 0x0f; + } else { + return rtc[rtc_index++]; + } + } + + return cpu.regs.mdr; +} + +void SRTC::write(unsigned addr, uint8 data) { + addr &= 0xffff; + + if(addr == 0x2801) { + data &= 0x0f; //only the low four bits are used + + if(data == 0x0d) { + rtc_mode = RtcRead; + rtc_index = -1; + return; + } + + if(data == 0x0e) { + rtc_mode = RtcCommand; + return; + } + + if(data == 0x0f) return; //unknown behavior + + if(rtc_mode == RtcWrite) { + if(rtc_index >= 0 && rtc_index < 12) { + rtc[rtc_index++] = data; + + if(rtc_index == 12) { + //day of week is automatically calculated and written + unsigned day = rtc[ 6] + rtc[ 7] * 10; + unsigned month = rtc[ 8]; + unsigned year = rtc[ 9] + rtc[10] * 10 + rtc[11] * 100; + year += 1000; + + rtc[rtc_index++] = weekday(year, month, day); + } + } + } else if(rtc_mode == RtcCommand) { + if(data == 0) { + rtc_mode = RtcWrite; + rtc_index = 0; + } else if(data == 4) { + rtc_mode = RtcReady; + rtc_index = -1; + for(unsigned i = 0; i < 13; i++) rtc[i] = 0; + } else { + //unknown behavior + rtc_mode = RtcReady; + } + } + } +} + +SRTC::SRTC() { +} + +} diff --git a/libsnes/snes/chip/srtc/srtc.hpp b/libsnes/snes/chip/srtc/srtc.hpp new file mode 100644 index 0000000..93dcbe5 --- /dev/null +++ b/libsnes/snes/chip/srtc/srtc.hpp @@ -0,0 +1,27 @@ +class SRTC { +public: + uint8 rtc[20]; + + void init(); + void load(); + void unload(); + void power(); + void reset(); + + uint8 read(unsigned addr); + void write(unsigned addr, uint8 data); + + void serialize(serializer&); + SRTC(); + +private: + static const unsigned months[12]; + enum RtcMode { RtcReady, RtcCommand, RtcRead, RtcWrite }; + unsigned rtc_mode; + signed rtc_index; + + void update_time(); + unsigned weekday(unsigned year, unsigned month, unsigned day); +}; + +extern SRTC srtc; diff --git a/libsnes/snes/chip/st0018/st0018.cpp b/libsnes/snes/chip/st0018/st0018.cpp new file mode 100644 index 0000000..ee13e38 --- /dev/null +++ b/libsnes/snes/chip/st0018/st0018.cpp @@ -0,0 +1,125 @@ +#include + +#define ST0018_CPP +namespace SNES { + +ST0018 st0018; + +uint8 ST0018::mmio_read(unsigned addr) { + addr &= 0xffff; + if(addr == 0x3800) return regs.r3800; + if(addr == 0x3804) return regs.r3804; + return cpu.regs.mdr; +} + +void ST0018::mmio_write(unsigned addr, uint8 data) { + addr &= 0xffff; + + if(addr == 0x3802) { + switch(regs.mode) { + case Waiting: { + switch(data) { + case 0x01: regs.r3800 = regs.r3800_01; break; + case 0xaa: op_board_upload(); break; + case 0xb2: op_b2(); break; + case 0xb3: op_b3(); break; + case 0xb4: op_b4(); break; + case 0xb5: op_b5(); break; + case 0xf1: op_query_chip(); break; + case 0xf2: op_query_chip(); break; + default: fprintf(stdout, "* ST018 w3802::%.2x\n", data); break; + } + } return; + + case BoardUpload: { + op_board_upload(data); + } return; + } + } + + if(addr == 0x3804) { + regs.w3804 <<= 8; + regs.w3804 |= data; + regs.w3804 &= 0xffffff; + return; + } +} + +void ST0018::init() { +} + +void ST0018::load() { +} + +void ST0018::unload() { +} + +void ST0018::power() { +} + +void ST0018::reset() { + regs.mode = Waiting; + regs.r3800 = 0x00; + regs.r3804 = 0x85; + regs.w3804 = 0; + for(unsigned i = 0; i < 97; i++) board[i] = 0; +} + +//=============== +//ST-0018 opcodes +//=============== + +void ST0018::op_board_upload() { + regs.mode = BoardUpload; + regs.counter = 0; + regs.r3800 = 0xe0; +} + +void ST0018::op_board_upload(uint8 data) { + board[regs.counter] = data; + regs.r3800 = 96 - regs.counter; + regs.counter++; + if(regs.counter >= 97) { + regs.mode = Waiting; + #if 0 + for(unsigned y = 0; y < 9; y++) { + for(unsigned x = 0; x < 9; x++) { + fprintf(stdout, "%.2x ", board[y * 9 + x]); + } + fprintf(stdout, "\n"); + } + for(unsigned n = 0; n < 16; n++) fprintf(stdout, "%.2x ", board[81 + n]); + fprintf(stdout, "\n\n"); + #endif + } +} + +void ST0018::op_b2() { + fprintf(stdout, "* ST018 w3802::b2\n"); + regs.r3800 = 0xe0; + regs.r3800_01 = 0; //unknown +} + +void ST0018::op_b3() { + fprintf(stdout, "* ST018 w3802::b3\n"); + regs.r3800 = 0xe0; + regs.r3800_01 = 1; //0 = player lost? +} + +void ST0018::op_b4() { + fprintf(stdout, "* ST018 w3802::b4\n"); + regs.r3800 = 0xe0; + regs.r3800_01 = 1; //0 = player won? +} + +void ST0018::op_b5() { + fprintf(stdout, "* ST018 w3802::b5\n"); + regs.r3800 = 0xe0; + regs.r3800_01 = 0; //1 = move will result in checkmate? +} + +void ST0018::op_query_chip() { + regs.r3800 = 0x00; +} + +} diff --git a/libsnes/snes/chip/st0018/st0018.hpp b/libsnes/snes/chip/st0018/st0018.hpp new file mode 100644 index 0000000..3fa381d --- /dev/null +++ b/libsnes/snes/chip/st0018/st0018.hpp @@ -0,0 +1,52 @@ +class ST0018 { +public: + void init(); + void load(); + void unload(); + void power(); + void reset(); + + uint8 mmio_read(unsigned addr); + void mmio_write(unsigned addr, uint8 data); + + enum mode_t { Waiting, BoardUpload }; + struct regs_t { + mode_t mode; + + uint8 r3800; + uint8 r3800_01; + uint8 r3804; + + unsigned w3804; + unsigned counter; + } regs; + + enum PieceID { + Pawn = 0x00, //foot soldier + Lance = 0x04, //incense chariot + Knight = 0x08, //cassia horse + Silver = 0x0c, //silver general + Gold = 0x10, //gold general + Rook = 0x14, //flying chariot + Bishop = 0x18, //angle mover + King = 0x1c, //king + }; + + enum PieceFlag { + PlayerA = 0x20, + PlayerB = 0x40, + }; + + uint8 board[9 * 9 + 16]; + +private: + void op_board_upload(); + void op_board_upload(uint8 data); + void op_b2(); + void op_b3(); + void op_b4(); + void op_b5(); + void op_query_chip(); +}; + +extern ST0018 st0018; diff --git a/libsnes/snes/chip/sufamiturbo/serialization.cpp b/libsnes/snes/chip/sufamiturbo/serialization.cpp new file mode 100644 index 0000000..526c349 --- /dev/null +++ b/libsnes/snes/chip/sufamiturbo/serialization.cpp @@ -0,0 +1,8 @@ +#ifdef SUFAMITURBO_CPP + +void SufamiTurbo::serialize(serializer &s) { + if(slotA.ram.data()) s.array(slotA.ram.data(), slotA.ram.size()); + if(slotB.ram.data()) s.array(slotB.ram.data(), slotB.ram.size()); +} + +#endif diff --git a/libsnes/snes/chip/sufamiturbo/sufamiturbo.cpp b/libsnes/snes/chip/sufamiturbo/sufamiturbo.cpp new file mode 100644 index 0000000..b68dde8 --- /dev/null +++ b/libsnes/snes/chip/sufamiturbo/sufamiturbo.cpp @@ -0,0 +1,33 @@ +#include + +#define SUFAMITURBO_CPP +namespace SNES { + +#include "serialization.cpp" +SufamiTurbo sufamiturbo; + +void SufamiTurbo::load() { + slotA.ram.map(allocate(128 * 1024, 0xff), 128 * 1024); + slotB.ram.map(allocate(128 * 1024, 0xff), 128 * 1024); + + if(slotA.rom.data()) { + cartridge.nvram.append(Cartridge::NonVolatileRAM( ".sts", slotA.ram.data(), slotA.ram.size(), Cartridge::Slot::SufamiTurboA )); + } else { + slotA.rom.map(allocate(128 * 1024, 0xff), 128 * 1024); + } + + if(slotB.rom.data()) { + cartridge.nvram.append(Cartridge::NonVolatileRAM( ".sts", slotB.ram.data(), slotB.ram.size(), Cartridge::Slot::SufamiTurboB )); + } else { + slotB.rom.map(allocate(128 * 1024, 0xff), 128 * 1024); + } +} + +void SufamiTurbo::unload() { + slotA.rom.reset(); + slotA.ram.reset(); + slotB.rom.reset(); + slotB.ram.reset(); +} + +} diff --git a/libsnes/snes/chip/sufamiturbo/sufamiturbo.hpp b/libsnes/snes/chip/sufamiturbo/sufamiturbo.hpp new file mode 100644 index 0000000..b38d75b --- /dev/null +++ b/libsnes/snes/chip/sufamiturbo/sufamiturbo.hpp @@ -0,0 +1,13 @@ +class SufamiTurbo { +public: + struct Slot { + MappedRAM rom; + MappedRAM ram; + } slotA, slotB; + + void load(); + void unload(); + void serialize(serializer&); +}; + +extern SufamiTurbo sufamiturbo; diff --git a/libsnes/snes/chip/superfx/bus/bus.cpp b/libsnes/snes/chip/superfx/bus/bus.cpp new file mode 100644 index 0000000..2ce33a7 --- /dev/null +++ b/libsnes/snes/chip/superfx/bus/bus.cpp @@ -0,0 +1,37 @@ +#ifdef SUPERFX_CPP + +//ROM / RAM access from the S-CPU + +unsigned SuperFX::ROM::size() const { + return cartridge.rom.size(); +} + +uint8 SuperFX::ROM::read(unsigned addr) { + if(superfx.regs.sfr.g && superfx.regs.scmr.ron) { + static const uint8_t data[16] = { + 0x00, 0x01, 0x00, 0x01, 0x04, 0x01, 0x00, 0x01, + 0x00, 0x01, 0x08, 0x01, 0x00, 0x01, 0x0c, 0x01, + }; + return data[addr & 15]; + } + return cartridge.rom.read(addr); +} + +void SuperFX::ROM::write(unsigned addr, uint8 data) { + cartridge.rom.write(addr, data); +} + +unsigned SuperFX::RAM::size() const { + return cartridge.ram.size(); +} + +uint8 SuperFX::RAM::read(unsigned addr) { + if(superfx.regs.sfr.g && superfx.regs.scmr.ran) return cpu.regs.mdr; + return cartridge.ram.read(addr); +} + +void SuperFX::RAM::write(unsigned addr, uint8 data) { + cartridge.ram.write(addr, data); +} + +#endif diff --git a/libsnes/snes/chip/superfx/bus/bus.hpp b/libsnes/snes/chip/superfx/bus/bus.hpp new file mode 100644 index 0000000..6e698e6 --- /dev/null +++ b/libsnes/snes/chip/superfx/bus/bus.hpp @@ -0,0 +1,11 @@ +struct ROM : Memory { + unsigned size() const; + uint8 read(unsigned); + void write(unsigned, uint8); +} rom; + +struct RAM : Memory { + unsigned size() const; + uint8 read(unsigned); + void write(unsigned, uint8); +} ram; diff --git a/libsnes/snes/chip/superfx/core/core.cpp b/libsnes/snes/chip/superfx/core/core.cpp new file mode 100644 index 0000000..92c2afd --- /dev/null +++ b/libsnes/snes/chip/superfx/core/core.cpp @@ -0,0 +1,107 @@ +#ifdef SUPERFX_CPP + +#include "opcodes.cpp" +#include "opcode_table.cpp" + +uint8 SuperFX::color(uint8 source) { + if(regs.por.highnibble) return (regs.colr & 0xf0) | (source >> 4); + if(regs.por.freezehigh) return (regs.colr & 0xf0) | (source & 0x0f); + return source; +} + +void SuperFX::plot(uint8 x, uint8 y) { + uint8 color = regs.colr; + + if(regs.por.dither && regs.scmr.md != 3) { + if((x ^ y) & 1) color >>= 4; + color &= 0x0f; + } + + if(!regs.por.transparent) { + if(regs.scmr.md == 3) { + if(regs.por.freezehigh) { + if((color & 0x0f) == 0) return; + } else { + if(color == 0) return; + } + } else { + if((color & 0x0f) == 0) return; + } + } + + uint16 offset = (y << 5) + (x >> 3); + if(offset != pixelcache[0].offset) { + pixelcache_flush(pixelcache[1]); + pixelcache[1] = pixelcache[0]; + pixelcache[0].bitpend = 0x00; + pixelcache[0].offset = offset; + } + + x = (x & 7) ^ 7; + pixelcache[0].data[x] = color; + pixelcache[0].bitpend |= 1 << x; + if(pixelcache[0].bitpend == 0xff) { + pixelcache_flush(pixelcache[1]); + pixelcache[1] = pixelcache[0]; + pixelcache[0].bitpend = 0x00; + } +} + +uint8 SuperFX::rpix(uint8 x, uint8 y) { + pixelcache_flush(pixelcache[1]); + pixelcache_flush(pixelcache[0]); + + unsigned cn; //character number + switch(regs.por.obj ? 3 : regs.scmr.ht) { + case 0: cn = ((x & 0xf8) << 1) + ((y & 0xf8) >> 3); break; + case 1: cn = ((x & 0xf8) << 1) + ((x & 0xf8) >> 1) + ((y & 0xf8) >> 3); break; + case 2: cn = ((x & 0xf8) << 1) + ((x & 0xf8) << 0) + ((y & 0xf8) >> 3); break; + case 3: cn = ((y & 0x80) << 2) + ((x & 0x80) << 1) + ((y & 0x78) << 1) + ((x & 0x78) >> 3); break; + } + unsigned bpp = 2 << (regs.scmr.md - (regs.scmr.md >> 1)); // = [regs.scmr.md]{ 2, 4, 4, 8 }; + unsigned addr = 0x700000 + (cn * (bpp << 3)) + (regs.scbr << 10) + ((y & 0x07) * 2); + uint8 data = 0x00; + x = (x & 7) ^ 7; + + for(unsigned n = 0; n < bpp; n++) { + unsigned byte = ((n >> 1) << 4) + (n & 1); // = [n]{ 0, 1, 16, 17, 32, 33, 48, 49 }; + add_clocks(memory_access_speed); + data |= ((bus_read(addr + byte) >> x) & 1) << n; + } + + return data; +} + +void SuperFX::pixelcache_flush(pixelcache_t &cache) { + if(cache.bitpend == 0x00) return; + + uint8 x = cache.offset << 3; + uint8 y = cache.offset >> 5; + + unsigned cn; //character number + switch(regs.por.obj ? 3 : regs.scmr.ht) { + case 0: cn = ((x & 0xf8) << 1) + ((y & 0xf8) >> 3); break; + case 1: cn = ((x & 0xf8) << 1) + ((x & 0xf8) >> 1) + ((y & 0xf8) >> 3); break; + case 2: cn = ((x & 0xf8) << 1) + ((x & 0xf8) << 0) + ((y & 0xf8) >> 3); break; + case 3: cn = ((y & 0x80) << 2) + ((x & 0x80) << 1) + ((y & 0x78) << 1) + ((x & 0x78) >> 3); break; + } + unsigned bpp = 2 << (regs.scmr.md - (regs.scmr.md >> 1)); // = [regs.scmr.md]{ 2, 4, 4, 8 }; + unsigned addr = 0x700000 + (cn * (bpp << 3)) + (regs.scbr << 10) + ((y & 0x07) * 2); + + for(unsigned n = 0; n < bpp; n++) { + unsigned byte = ((n >> 1) << 4) + (n & 1); // = [n]{ 0, 1, 16, 17, 32, 33, 48, 49 }; + uint8 data = 0x00; + for(unsigned x = 0; x < 8; x++) data |= ((cache.data[x] >> n) & 1) << x; + if(cache.bitpend != 0xff) { + add_clocks(memory_access_speed); + data &= cache.bitpend; + data |= bus_read(addr + byte) & ~cache.bitpend; + } + add_clocks(memory_access_speed); + bus_write(addr + byte, data); + } + + cache.bitpend = 0x00; +} + +#endif diff --git a/libsnes/snes/chip/superfx/core/core.hpp b/libsnes/snes/chip/superfx/core/core.hpp new file mode 100644 index 0000000..1c13f1c --- /dev/null +++ b/libsnes/snes/chip/superfx/core/core.hpp @@ -0,0 +1,92 @@ +#include "registers.hpp" + +uint8 color(uint8 source); +void plot(uint8 x, uint8 y); +uint8 rpix(uint8 x, uint8 y); +void pixelcache_flush(pixelcache_t &cache); + +void (SuperFX::*opcode_table[1024])(); +void initialize_opcode_table(); + +//opcodes.cpp +template void op_adc_i(); +template void op_adc_r(); +template void op_add_i(); +template void op_add_r(); +void op_alt1(); +void op_alt2(); +void op_alt3(); +template void op_and_i(); +template void op_and_r(); +void op_asr(); +void op_bge(); +void op_bcc(); +void op_bcs(); +void op_beq(); +template void op_bic_i(); +template void op_bic_r(); +void op_blt(); +void op_bmi(); +void op_bne(); +void op_bpl(); +void op_bra(); +void op_bvc(); +void op_bvs(); +void op_cache(); +void op_cmode(); +template void op_cmp_r(); +void op_color(); +template void op_dec_r(); +void op_div2(); +void op_fmult(); +template void op_from_r(); +void op_getb(); +void op_getbl(); +void op_getbh(); +void op_getbs(); +void op_getc(); +void op_hib(); +template void op_ibt_r(); +template void op_inc_r(); +template void op_iwt_r(); +template void op_jmp_r(); +template void op_ldb_ir(); +template void op_ldw_ir(); +template void op_link(); +template void op_ljmp_r(); +template void op_lm_r(); +template void op_lms_r(); +void op_lmult(); +void op_lob(); +void op_loop(); +void op_lsr(); +void op_merge(); +template void op_mult_i(); +template void op_mult_r(); +void op_nop(); +void op_not(); +template void op_or_i(); +template void op_or_r(); +void op_plot(); +void op_ramb(); +void op_rol(); +void op_romb(); +void op_ror(); +void op_rpix(); +template void op_sbc_r(); +void op_sbk(); +void op_sex(); +template void op_sm_r(); +template void op_sms_r(); +template void op_stb_ir(); +void op_stop(); +template void op_stw_ir(); +template void op_sub_i(); +template void op_sub_r(); +void op_swap(); +template void op_to_r(); +template void op_umult_i(); +template void op_umult_r(); +template void op_with_r(); +template void op_xor_i(); +template void op_xor_r(); diff --git a/libsnes/snes/chip/superfx/core/opcode_table.cpp b/libsnes/snes/chip/superfx/core/opcode_table.cpp new file mode 100644 index 0000000..7908253 --- /dev/null +++ b/libsnes/snes/chip/superfx/core/opcode_table.cpp @@ -0,0 +1,270 @@ +#ifdef SUPERFX_CPP + +void SuperFX::initialize_opcode_table() { + #define op4(id, name) \ + op(id+ 0, name< 1>) op(id+ 1, name< 2>) op(id+ 2, name< 3>) op(id+ 3, name< 4>) + + #define op6(id, name) \ + op(id+ 0, name< 8>) op(id+ 1, name< 9>) op(id+ 2, name<10>) op(id+ 3, name<11>) \ + op(id+ 4, name<12>) op(id+ 5, name<13>) + + #define op12(id, name) \ + op(id+ 0, name< 0>) op(id+ 1, name< 1>) op(id+ 2, name< 2>) op(id+ 3, name< 3>) \ + op(id+ 4, name< 4>) op(id+ 5, name< 5>) op(id+ 6, name< 6>) op(id+ 7, name< 7>) \ + op(id+ 8, name< 8>) op(id+ 9, name< 9>) op(id+10, name<10>) op(id+11, name<11>) + + #define op15l(id, name) \ + op(id+ 0, name< 0>) op(id+ 1, name< 1>) op(id+ 2, name< 2>) op(id+ 3, name< 3>) \ + op(id+ 4, name< 4>) op(id+ 5, name< 5>) op(id+ 6, name< 6>) op(id+ 7, name< 7>) \ + op(id+ 8, name< 8>) op(id+ 9, name< 9>) op(id+10, name<10>) op(id+11, name<11>) \ + op(id+12, name<12>) op(id+13, name<13>) op(id+14, name<14>) + + #define op15h(id, name) \ + op(id+ 0, name< 1>) op(id+ 1, name< 2>) op(id+ 2, name< 3>) op(id+ 3, name< 4>) \ + op(id+ 4, name< 5>) op(id+ 5, name< 6>) op(id+ 6, name< 7>) op(id+ 7, name< 8>) \ + op(id+ 8, name< 9>) op(id+ 9, name<10>) op(id+10, name<11>) op(id+11, name<12>) \ + op(id+12, name<13>) op(id+13, name<14>) op(id+14, name<15>) + + #define op16(id, name) \ + op(id+ 0, name< 0>) op(id+ 1, name< 1>) op(id+ 2, name< 2>) op(id+ 3, name< 3>) \ + op(id+ 4, name< 4>) op(id+ 5, name< 5>) op(id+ 6, name< 6>) op(id+ 7, name< 7>) \ + op(id+ 8, name< 8>) op(id+ 9, name< 9>) op(id+10, name<10>) op(id+11, name<11>) \ + op(id+12, name<12>) op(id+13, name<13>) op(id+14, name<14>) op(id+15, name<15>) + + //====== + // ALT0 + //====== + + #define op(id, name) opcode_table[ 0 + id] = &SuperFX::op_##name; + op (0x00, stop) + op (0x01, nop) + op (0x02, cache) + op (0x03, lsr) + op (0x04, rol) + op (0x05, bra) + op (0x06, blt) + op (0x07, bge) + op (0x08, bne) + op (0x09, beq) + op (0x0a, bpl) + op (0x0b, bmi) + op (0x0c, bcc) + op (0x0d, bcs) + op (0x0e, bvc) + op (0x0f, bvs) + op16 (0x10, to_r) + op16 (0x20, with_r) + op12 (0x30, stw_ir) + op (0x3c, loop) + op (0x3d, alt1) + op (0x3e, alt2) + op (0x3f, alt3) + op12 (0x40, ldw_ir) + op (0x4c, plot) + op (0x4d, swap) + op (0x4e, color) + op (0x4f, not) + op16 (0x50, add_r) + op16 (0x60, sub_r) + op (0x70, merge) + op15h(0x71, and_r) + op16 (0x80, mult_r) + op (0x90, sbk) + op4 (0x91, link) + op (0x95, sex) + op (0x96, asr) + op (0x97, ror) + op6 (0x98, jmp_r) + op (0x9e, lob) + op (0x9f, fmult) + op16 (0xa0, ibt_r) + op16 (0xb0, from_r) + op (0xc0, hib) + op15h(0xc1, or_r) + op15l(0xd0, inc_r) + op (0xdf, getc) + op15l(0xe0, dec_r) + op (0xef, getb) + op16 (0xf0, iwt_r) + #undef op + + //====== + // ALT1 + //====== + + #define op(id, name) opcode_table[256 + id] = &SuperFX::op_##name; + op (0x00, stop) + op (0x01, nop) + op (0x02, cache) + op (0x03, lsr) + op (0x04, rol) + op (0x05, bra) + op (0x06, blt) + op (0x07, bge) + op (0x08, bne) + op (0x09, beq) + op (0x0a, bpl) + op (0x0b, bmi) + op (0x0c, bcc) + op (0x0d, bcs) + op (0x0e, bvc) + op (0x0f, bvs) + op16 (0x10, to_r) + op16 (0x20, with_r) + op12 (0x30, stb_ir) + op (0x3c, loop) + op (0x3d, alt1) + op (0x3e, alt2) + op (0x3f, alt3) + op12 (0x40, ldb_ir) + op (0x4c, rpix) + op (0x4d, swap) + op (0x4e, cmode) + op (0x4f, not) + op16 (0x50, adc_r) + op16 (0x60, sbc_r) + op (0x70, merge) + op15h(0x71, bic_r) + op16 (0x80, umult_r) + op (0x90, sbk) + op4 (0x91, link) + op (0x95, sex) + op (0x96, div2) + op (0x97, ror) + op6 (0x98, ljmp_r) + op (0x9e, lob) + op (0x9f, lmult) + op16 (0xa0, lms_r) + op16 (0xb0, from_r) + op (0xc0, hib) + op15h(0xc1, xor_r) + op15l(0xd0, inc_r) + op (0xdf, getc) + op15l(0xe0, dec_r) + op (0xef, getbh) + op16 (0xf0, lm_r) + #undef op + + //====== + // ALT2 + //====== + + #define op(id, name) opcode_table[512 + id] = &SuperFX::op_##name; + op (0x00, stop) + op (0x01, nop) + op (0x02, cache) + op (0x03, lsr) + op (0x04, rol) + op (0x05, bra) + op (0x06, blt) + op (0x07, bge) + op (0x08, bne) + op (0x09, beq) + op (0x0a, bpl) + op (0x0b, bmi) + op (0x0c, bcc) + op (0x0d, bcs) + op (0x0e, bvc) + op (0x0f, bvs) + op16 (0x10, to_r) + op16 (0x20, with_r) + op12 (0x30, stw_ir) + op (0x3c, loop) + op (0x3d, alt1) + op (0x3e, alt2) + op (0x3f, alt3) + op12 (0x40, ldw_ir) + op (0x4c, plot) + op (0x4d, swap) + op (0x4e, color) + op (0x4f, not) + op16 (0x50, add_i) + op16 (0x60, sub_i) + op (0x70, merge) + op15h(0x71, and_i) + op16 (0x80, mult_i) + op (0x90, sbk) + op4 (0x91, link) + op (0x95, sex) + op (0x96, asr) + op (0x97, ror) + op6 (0x98, jmp_r) + op (0x9e, lob) + op (0x9f, fmult) + op16 (0xa0, sms_r) + op16 (0xb0, from_r) + op (0xc0, hib) + op15h(0xc1, or_i) + op15l(0xd0, inc_r) + op (0xdf, ramb) + op15l(0xe0, dec_r) + op (0xef, getbl) + op16 (0xf0, sm_r) + #undef op + + //====== + // ALT3 + //====== + + #define op(id, name) opcode_table[768 + id] = &SuperFX::op_##name; + op (0x00, stop) + op (0x01, nop) + op (0x02, cache) + op (0x03, lsr) + op (0x04, rol) + op (0x05, bra) + op (0x06, blt) + op (0x07, bge) + op (0x08, bne) + op (0x09, beq) + op (0x0a, bpl) + op (0x0b, bmi) + op (0x0c, bcc) + op (0x0d, bcs) + op (0x0e, bvc) + op (0x0f, bvs) + op16 (0x10, to_r) + op16 (0x20, with_r) + op12 (0x30, stb_ir) + op (0x3c, loop) + op (0x3d, alt1) + op (0x3e, alt2) + op (0x3f, alt3) + op12 (0x40, ldb_ir) + op (0x4c, rpix) + op (0x4d, swap) + op (0x4e, cmode) + op (0x4f, not) + op16 (0x50, adc_i) + op16 (0x60, cmp_r) + op (0x70, merge) + op15h(0x71, bic_i) + op16 (0x80, umult_i) + op (0x90, sbk) + op4 (0x91, link) + op (0x95, sex) + op (0x96, div2) + op (0x97, ror) + op6 (0x98, ljmp_r) + op (0x9e, lob) + op (0x9f, lmult) + op16 (0xa0, lms_r) + op16 (0xb0, from_r) + op (0xc0, hib) + op15h(0xc1, xor_i) + op15l(0xd0, inc_r) + op (0xdf, romb) + op15l(0xe0, dec_r) + op (0xef, getbs) + op16 (0xf0, lm_r) + #undef op + + #undef op4 + #undef op6 + #undef op12 + #undef op15l + #undef op15h + #undef op16 +} + +#endif diff --git a/libsnes/snes/chip/superfx/core/opcodes.cpp b/libsnes/snes/chip/superfx/core/opcodes.cpp new file mode 100644 index 0000000..7d2f13a --- /dev/null +++ b/libsnes/snes/chip/superfx/core/opcodes.cpp @@ -0,0 +1,661 @@ +#ifdef SUPERFX_CPP + +//$00 stop +void SuperFX::op_stop() { + if(regs.cfgr.irq == 0) { + regs.sfr.irq = 1; + cpu.regs.irq = 1; + } + + regs.sfr.g = 0; + regs.pipeline = 0x01; + regs.reset(); +} + +//$01 nop +void SuperFX::op_nop() { + regs.reset(); +} + +//$02 cache +void SuperFX::op_cache() { + if(regs.cbr != (regs.r[15] & 0xfff0)) { + regs.cbr = regs.r[15] & 0xfff0; + cache_flush(); + } + regs.reset(); +} + +//$03 lsr +void SuperFX::op_lsr() { + regs.sfr.cy = (regs.sr() & 1); + regs.dr() = regs.sr() >> 1; + regs.sfr.s = (regs.dr() & 0x8000); + regs.sfr.z = (regs.dr() == 0); + regs.reset(); +} + +//$04 rol +void SuperFX::op_rol() { + bool carry = (regs.sr() & 0x8000); + regs.dr() = (regs.sr() << 1) | regs.sfr.cy; + regs.sfr.s = (regs.dr() & 0x8000); + regs.sfr.cy = carry; + regs.sfr.z = (regs.dr() == 0); + regs.reset(); +} + +//$05 bra e +void SuperFX::op_bra() { + regs.r[15] += (int8)pipe(); +} + +//$06 blt e +void SuperFX::op_blt() { + int e = (int8)pipe(); + if((regs.sfr.s ^ regs.sfr.ov) == 0) regs.r[15] += e; +} + +//$07 bge e +void SuperFX::op_bge() { + int e = (int8)pipe(); + if((regs.sfr.s ^ regs.sfr.ov) == 1) regs.r[15] += e; +} + +//$08 bne e +void SuperFX::op_bne() { + int e = (int8)pipe(); + if(regs.sfr.z == 0) regs.r[15] += e; +} + +//$09 beq e +void SuperFX::op_beq() { + int e = (int8)pipe(); + if(regs.sfr.z == 1) regs.r[15] += e; +} + +//$0a bpl e +void SuperFX::op_bpl() { + int e = (int8)pipe(); + if(regs.sfr.s == 0) regs.r[15] += e; +} + +//$0b bmi e +void SuperFX::op_bmi() { + int e = (int8)pipe(); + if(regs.sfr.s == 1) regs.r[15] += e; +} + +//$0c bcc e +void SuperFX::op_bcc() { + int e = (int8)pipe(); + if(regs.sfr.cy == 0) regs.r[15] += e; +} + +//$0d bcs e +void SuperFX::op_bcs() { + int e = (int8)pipe(); + if(regs.sfr.cy == 1) regs.r[15] += e; +} + +//$0e bvc e +void SuperFX::op_bvc() { + int e = (int8)pipe(); + if(regs.sfr.ov == 0) regs.r[15] += e; +} + +//$0f bvs e +void SuperFX::op_bvs() { + int e = (int8)pipe(); + if(regs.sfr.ov == 1) regs.r[15] += e; +} + +//$10-1f(b0): to rN +//$10-1f(b1): move rN +template void SuperFX::op_to_r() { + if(regs.sfr.b == 0) { + regs.dreg = n; + } else { + regs.r[n] = regs.sr(); + regs.reset(); + } +} + +//$20-2f: with rN +template void SuperFX::op_with_r() { + regs.sreg = n; + regs.dreg = n; + regs.sfr.b = 1; +} + +//$30-3b(alt0): stw (rN) +template void SuperFX::op_stw_ir() { + regs.ramaddr = regs.r[n]; + rambuffer_write(regs.ramaddr ^ 0, regs.sr() >> 0); + rambuffer_write(regs.ramaddr ^ 1, regs.sr() >> 8); + regs.reset(); +} + +//$30-3b(alt1): stb (rN) +template void SuperFX::op_stb_ir() { + regs.ramaddr = regs.r[n]; + rambuffer_write(regs.ramaddr, regs.sr()); + regs.reset(); +} + +//$3c loop +void SuperFX::op_loop() { + regs.r[12]--; + regs.sfr.s = (regs.r[12] & 0x8000); + regs.sfr.z = (regs.r[12] == 0); + if(!regs.sfr.z) regs.r[15] = regs.r[13]; + regs.reset(); +} + +//$3d alt1 +void SuperFX::op_alt1() { + regs.sfr.b = 0; + regs.sfr.alt1 = 1; +} + +//$3e alt2 +void SuperFX::op_alt2() { + regs.sfr.b = 0; + regs.sfr.alt2 = 1; +} + +//$3f alt3 +void SuperFX::op_alt3() { + regs.sfr.b = 0; + regs.sfr.alt1 = 1; + regs.sfr.alt2 = 1; +} + +//$40-4b(alt0): ldw (rN) +template void SuperFX::op_ldw_ir() { + regs.ramaddr = regs.r[n]; + uint16_t data; + data = rambuffer_read(regs.ramaddr ^ 0) << 0; + data |= rambuffer_read(regs.ramaddr ^ 1) << 8; + regs.dr() = data; + regs.reset(); +} + +//$40-4b(alt1): ldb (rN) +template void SuperFX::op_ldb_ir() { + regs.ramaddr = regs.r[n]; + regs.dr() = rambuffer_read(regs.ramaddr); + regs.reset(); +} + +//$4c(alt0): plot +void SuperFX::op_plot() { + plot(regs.r[1], regs.r[2]); + regs.r[1]++; + regs.reset(); +} + +//$4c(alt1): rpix +void SuperFX::op_rpix() { + regs.dr() = rpix(regs.r[1], regs.r[2]); + regs.sfr.s = (regs.dr() & 0x8000); + regs.sfr.z = (regs.dr() == 0); + regs.reset(); +} + +//$4d: swap +void SuperFX::op_swap() { + regs.dr() = (regs.sr() >> 8) | (regs.sr() << 8); + regs.sfr.s = (regs.dr() & 0x8000); + regs.sfr.z = (regs.dr() == 0); + regs.reset(); +} + +//$4e(alt0): color +void SuperFX::op_color() { + regs.colr = color(regs.sr()); + regs.reset(); +} + +//$4e(alt1): cmode +void SuperFX::op_cmode() { + regs.por = regs.sr(); + regs.reset(); +} + +//$4f: not +void SuperFX::op_not() { + regs.dr() = ~regs.sr(); + regs.sfr.s = (regs.dr() & 0x8000); + regs.sfr.z = (regs.dr() == 0); + regs.reset(); +} + +//$50-5f(alt0): add rN +template void SuperFX::op_add_r() { + int r = regs.sr() + regs.r[n]; + regs.sfr.ov = ~(regs.sr() ^ regs.r[n]) & (regs.r[n] ^ r) & 0x8000; + regs.sfr.s = (r & 0x8000); + regs.sfr.cy = (r >= 0x10000); + regs.sfr.z = ((uint16_t)r == 0); + regs.dr() = r; + regs.reset(); +} + +//$50-5f(alt1): adc rN +template void SuperFX::op_adc_r() { + int r = regs.sr() + regs.r[n] + regs.sfr.cy; + regs.sfr.ov = ~(regs.sr() ^ regs.r[n]) & (regs.r[n] ^ r) & 0x8000; + regs.sfr.s = (r & 0x8000); + regs.sfr.cy = (r >= 0x10000); + regs.sfr.z = ((uint16_t)r == 0); + regs.dr() = r; + regs.reset(); +} + +//$50-5f(alt2): add #N +template void SuperFX::op_add_i() { + int r = regs.sr() + n; + regs.sfr.ov = ~(regs.sr() ^ n) & (n ^ r) & 0x8000; + regs.sfr.s = (r & 0x8000); + regs.sfr.cy = (r >= 0x10000); + regs.sfr.z = ((uint16_t)r == 0); + regs.dr() = r; + regs.reset(); +} + +//$50-5f(alt3): adc #N +template void SuperFX::op_adc_i() { + int r = regs.sr() + n + regs.sfr.cy; + regs.sfr.ov = ~(regs.sr() ^ n) & (n ^ r) & 0x8000; + regs.sfr.s = (r & 0x8000); + regs.sfr.cy = (r >= 0x10000); + regs.sfr.z = ((uint16_t)r == 0); + regs.dr() = r; + regs.reset(); +} + +//$60-6f(alt0): sub rN +template void SuperFX::op_sub_r() { + int r = regs.sr() - regs.r[n]; + regs.sfr.ov = (regs.sr() ^ regs.r[n]) & (regs.sr() ^ r) & 0x8000; + regs.sfr.s = (r & 0x8000); + regs.sfr.cy = (r >= 0); + regs.sfr.z = ((uint16_t)r == 0); + regs.dr() = r; + regs.reset(); +} + +//$60-6f(alt1): sbc rN +template void SuperFX::op_sbc_r() { + int r = regs.sr() - regs.r[n] - !regs.sfr.cy; + regs.sfr.ov = (regs.sr() ^ regs.r[n]) & (regs.sr() ^ r) & 0x8000; + regs.sfr.s = (r & 0x8000); + regs.sfr.cy = (r >= 0); + regs.sfr.z = ((uint16_t)r == 0); + regs.dr() = r; + regs.reset(); +} + +//$60-6f(alt2): sub #N +template void SuperFX::op_sub_i() { + int r = regs.sr() - n; + regs.sfr.ov = (regs.sr() ^ n) & (regs.sr() ^ r) & 0x8000; + regs.sfr.s = (r & 0x8000); + regs.sfr.cy = (r >= 0); + regs.sfr.z = ((uint16_t)r == 0); + regs.dr() = r; + regs.reset(); +} + +//$60-6f(alt3): cmp rN +template void SuperFX::op_cmp_r() { + int r = regs.sr() - regs.r[n]; + regs.sfr.ov = (regs.sr() ^ regs.r[n]) & (regs.sr() ^ r) & 0x8000; + regs.sfr.s = (r & 0x8000); + regs.sfr.cy = (r >= 0); + regs.sfr.z = ((uint16_t)r == 0); + regs.reset(); +} + +//$70: merge +void SuperFX::op_merge() { + regs.dr() = (regs.r[7] & 0xff00) | (regs.r[8] >> 8); + regs.sfr.ov = (regs.dr() & 0xc0c0); + regs.sfr.s = (regs.dr() & 0x8080); + regs.sfr.cy = (regs.dr() & 0xe0e0); + regs.sfr.z = (regs.dr() & 0xf0f0); + regs.reset(); +} + +//$71-7f(alt0): and rN +template void SuperFX::op_and_r() { + regs.dr() = regs.sr() & regs.r[n]; + regs.sfr.s = (regs.dr() & 0x8000); + regs.sfr.z = (regs.dr() == 0); + regs.reset(); +} + +//$71-7f(alt1): bic rN +template void SuperFX::op_bic_r() { + regs.dr() = regs.sr() & ~regs.r[n]; + regs.sfr.s = (regs.dr() & 0x8000); + regs.sfr.z = (regs.dr() == 0); + regs.reset(); +} + +//$71-7f(alt2): and #N +template void SuperFX::op_and_i() { + regs.dr() = regs.sr() & n; + regs.sfr.s = (regs.dr() & 0x8000); + regs.sfr.z = (regs.dr() == 0); + regs.reset(); +} + +//$71-7f(alt3): bic #N +template void SuperFX::op_bic_i() { + regs.dr() = regs.sr() & ~n; + regs.sfr.s = (regs.dr() & 0x8000); + regs.sfr.z = (regs.dr() == 0); + regs.reset(); +} + +//$80-8f(alt0): mult rN +template void SuperFX::op_mult_r() { + regs.dr() = (int8)regs.sr() * (int8)regs.r[n]; + regs.sfr.s = (regs.dr() & 0x8000); + regs.sfr.z = (regs.dr() == 0); + regs.reset(); + if(!regs.cfgr.ms0) add_clocks(2); +} + +//$80-8f(alt1): umult rN +template void SuperFX::op_umult_r() { + regs.dr() = (uint8)regs.sr() * (uint8)regs.r[n]; + regs.sfr.s = (regs.dr() & 0x8000); + regs.sfr.z = (regs.dr() == 0); + regs.reset(); + if(!regs.cfgr.ms0) add_clocks(2); +} + +//$80-8f(alt2): mult #N +template void SuperFX::op_mult_i() { + regs.dr() = (int8)regs.sr() * (int8)n; + regs.sfr.s = (regs.dr() & 0x8000); + regs.sfr.z = (regs.dr() == 0); + regs.reset(); + if(!regs.cfgr.ms0) add_clocks(2); +} + +//$80-8f(alt3): umult #N +template void SuperFX::op_umult_i() { + regs.dr() = (uint8)regs.sr() * (uint8)n; + regs.sfr.s = (regs.dr() & 0x8000); + regs.sfr.z = (regs.dr() == 0); + regs.reset(); + if(!regs.cfgr.ms0) add_clocks(2); +} + +//$90: sbk +void SuperFX::op_sbk() { + rambuffer_write(regs.ramaddr ^ 0, regs.sr() >> 0); + rambuffer_write(regs.ramaddr ^ 1, regs.sr() >> 8); + regs.reset(); +} + +//$91-94: link #N +template void SuperFX::op_link() { + regs.r[11] = regs.r[15] + n; + regs.reset(); +} + +//$95: sex +void SuperFX::op_sex() { + regs.dr() = (int8)regs.sr(); + regs.sfr.s = (regs.dr() & 0x8000); + regs.sfr.z = (regs.dr() == 0); + regs.reset(); +} + +//$96(alt0): asr +void SuperFX::op_asr() { + regs.sfr.cy = (regs.sr() & 1); + regs.dr() = (int16_t)regs.sr() >> 1; + regs.sfr.s = (regs.dr() & 0x8000); + regs.sfr.z = (regs.dr() == 0); + regs.reset(); +} + +//$96(alt1): div2 +void SuperFX::op_div2() { + regs.sfr.cy = (regs.sr() & 1); + regs.dr() = ((int16_t)regs.sr() >> 1) + ((regs.sr() + 1) >> 16); + regs.sfr.s = (regs.dr() & 0x8000); + regs.sfr.z = (regs.dr() == 0); + regs.reset(); +} + +//$97: ror +void SuperFX::op_ror() { + bool carry = (regs.sr() & 1); + regs.dr() = (regs.sfr.cy << 15) | (regs.sr() >> 1); + regs.sfr.s = (regs.dr() & 0x8000); + regs.sfr.cy = carry; + regs.sfr.z = (regs.dr() == 0); + regs.reset(); +} + +//$98-9d(alt0): jmp rN +template void SuperFX::op_jmp_r() { + regs.r[15] = regs.r[n]; + regs.reset(); +} + +//$98-9d(alt1): ljmp rN +template void SuperFX::op_ljmp_r() { + regs.pbr = regs.r[n] & 0x7f; + regs.r[15] = regs.sr(); + regs.cbr = regs.r[15] & 0xfff0; + cache_flush(); + regs.reset(); +} + +//$9e: lob +void SuperFX::op_lob() { + regs.dr() = regs.sr() & 0xff; + regs.sfr.s = (regs.dr() & 0x80); + regs.sfr.z = (regs.dr() == 0); + regs.reset(); +} + +//$9f(alt0): fmult +void SuperFX::op_fmult() { + uint32_t result = (int16_t)regs.sr() * (int16_t)regs.r[6]; + regs.dr() = result >> 16; + regs.sfr.s = (regs.dr() & 0x8000); + regs.sfr.cy = (result & 0x8000); + regs.sfr.z = (regs.dr() == 0); + regs.reset(); + add_clocks(4 + (regs.cfgr.ms0 << 2)); +} + +//$9f(alt1): lmult +void SuperFX::op_lmult() { + uint32_t result = (int16_t)regs.sr() * (int16_t)regs.r[6]; + regs.r[4] = result; + regs.dr() = result >> 16; + regs.sfr.s = (regs.dr() & 0x8000); + regs.sfr.cy = (result & 0x8000); + regs.sfr.z = (regs.dr() == 0); + regs.reset(); + add_clocks(4 + (regs.cfgr.ms0 << 2)); +} + +//$a0-af(alt0): ibt rN,#pp +template void SuperFX::op_ibt_r() { + regs.r[n] = (int8)pipe(); + regs.reset(); +} + +//$a0-af(alt1): lms rN,(yy) +template void SuperFX::op_lms_r() { + regs.ramaddr = pipe() << 1; + uint16_t data; + data = rambuffer_read(regs.ramaddr ^ 0) << 0; + data |= rambuffer_read(regs.ramaddr ^ 1) << 8; + regs.r[n] = data; + regs.reset(); +} + +//$a0-af(alt2): sms (yy),rN +template void SuperFX::op_sms_r() { + regs.ramaddr = pipe() << 1; + rambuffer_write(regs.ramaddr ^ 0, regs.r[n] >> 0); + rambuffer_write(regs.ramaddr ^ 1, regs.r[n] >> 8); + regs.reset(); +} + +//$b0-bf(b0): from rN +//$b0-bf(b1): moves rN +template void SuperFX::op_from_r() { + if(regs.sfr.b == 0) { + regs.sreg = n; + } else { + regs.dr() = regs.r[n]; + regs.sfr.ov = (regs.dr() & 0x80); + regs.sfr.s = (regs.dr() & 0x8000); + regs.sfr.z = (regs.dr() == 0); + regs.reset(); + } +} + +//$c0: hib +void SuperFX::op_hib() { + regs.dr() = regs.sr() >> 8; + regs.sfr.s = (regs.dr() & 0x80); + regs.sfr.z = (regs.dr() == 0); + regs.reset(); +} + +//$c1-cf(alt0): or rN +template void SuperFX::op_or_r() { + regs.dr() = regs.sr() | regs.r[n]; + regs.sfr.s = (regs.dr() & 0x8000); + regs.sfr.z = (regs.dr() == 0); + regs.reset(); +} + +//$c1-cf(alt1): xor rN +template void SuperFX::op_xor_r() { + regs.dr() = regs.sr() ^ regs.r[n]; + regs.sfr.s = (regs.dr() & 0x8000); + regs.sfr.z = (regs.dr() == 0); + regs.reset(); +} + +//$c1-cf(alt2): or #N +template void SuperFX::op_or_i() { + regs.dr() = regs.sr() | n; + regs.sfr.s = (regs.dr() & 0x8000); + regs.sfr.z = (regs.dr() == 0); + regs.reset(); +} + +//$c1-cf(alt3): xor #N +template void SuperFX::op_xor_i() { + regs.dr() = regs.sr() ^ n; + regs.sfr.s = (regs.dr() & 0x8000); + regs.sfr.z = (regs.dr() == 0); + regs.reset(); +} + +//$d0-de: inc rN +template void SuperFX::op_inc_r() { + regs.r[n]++; + regs.sfr.s = (regs.r[n] & 0x8000); + regs.sfr.z = (regs.r[n] == 0); + regs.reset(); +} + +//$df(alt0): getc +void SuperFX::op_getc() { + regs.colr = color(rombuffer_read()); + regs.reset(); +} + +//$df(alt2): ramb +void SuperFX::op_ramb() { + rambuffer_sync(); + regs.rambr = regs.sr(); + regs.reset(); +} + +//$df(alt3): romb +void SuperFX::op_romb() { + rombuffer_sync(); + regs.rombr = regs.sr() & 0x7f; + regs.reset(); +} + +//$e0-ee: dec rN +template void SuperFX::op_dec_r() { + regs.r[n]--; + regs.sfr.s = (regs.r[n] & 0x8000); + regs.sfr.z = (regs.r[n] == 0); + regs.reset(); +} + +//$ef(alt0): getb +void SuperFX::op_getb() { + regs.dr() = rombuffer_read(); + regs.reset(); +} + +//$ef(alt1): getbh +void SuperFX::op_getbh() { + regs.dr() = (rombuffer_read() << 8) | (regs.sr() & 0x00ff); + regs.reset(); +} + +//$ef(alt2): getbl +void SuperFX::op_getbl() { + regs.dr() = (regs.sr() & 0xff00) | (rombuffer_read() << 0); + regs.reset(); +} + +//$ef(alt3): getbs +void SuperFX::op_getbs() { + regs.dr() = (int8)rombuffer_read(); + regs.reset(); +} + +//$f0-ff(alt0): iwt rN,#xx +template void SuperFX::op_iwt_r() { + uint16_t data; + data = pipe() << 0; + data |= pipe() << 8; + regs.r[n] = data; + regs.reset(); +} + +//$f0-ff(alt1): lm rN,(xx) +template void SuperFX::op_lm_r() { + regs.ramaddr = pipe() << 0; + regs.ramaddr |= pipe() << 8; + uint16_t data; + data = rambuffer_read(regs.ramaddr ^ 0) << 0; + data |= rambuffer_read(regs.ramaddr ^ 1) << 8; + regs.r[n] = data; + regs.reset(); +} + +//$f0-ff(alt2): sm (xx),rN +template void SuperFX::op_sm_r() { + regs.ramaddr = pipe() << 0; + regs.ramaddr |= pipe() << 8; + rambuffer_write(regs.ramaddr ^ 0, regs.r[n] >> 0); + rambuffer_write(regs.ramaddr ^ 1, regs.r[n] >> 8); + regs.reset(); +} + +#endif diff --git a/libsnes/snes/chip/superfx/core/registers.hpp b/libsnes/snes/chip/superfx/core/registers.hpp new file mode 100644 index 0000000..6c95786 --- /dev/null +++ b/libsnes/snes/chip/superfx/core/registers.hpp @@ -0,0 +1,176 @@ +//accepts a callback binding so r14 writes can trigger ROM buffering transparently +struct reg16_t { + uint16 data; + function on_modify; + + inline operator unsigned() const { return data; } + inline uint16 assign(uint16 i) { + if(on_modify) on_modify(i); + else data = i; + return data; + } + + inline unsigned operator++() { return assign(data + 1); } + inline unsigned operator--() { return assign(data - 1); } + inline unsigned operator++(int) { unsigned r = data; assign(data + 1); return r; } + inline unsigned operator--(int) { unsigned r = data; assign(data - 1); return r; } + inline unsigned operator = (unsigned i) { return assign(i); } + inline unsigned operator |= (unsigned i) { return assign(data | i); } + inline unsigned operator ^= (unsigned i) { return assign(data ^ i); } + inline unsigned operator &= (unsigned i) { return assign(data & i); } + inline unsigned operator <<= (unsigned i) { return assign(data << i); } + inline unsigned operator >>= (unsigned i) { return assign(data >> i); } + inline unsigned operator += (unsigned i) { return assign(data + i); } + inline unsigned operator -= (unsigned i) { return assign(data - i); } + inline unsigned operator *= (unsigned i) { return assign(data * i); } + inline unsigned operator /= (unsigned i) { return assign(data / i); } + inline unsigned operator %= (unsigned i) { return assign(data % i); } + + inline unsigned operator = (const reg16_t& i) { return assign(i); } + + reg16_t() : data(0) {} + reg16_t(const reg16_t&) : data(0) {} +}; + +struct sfr_t { + bool irq; //interrupt flag + bool b; //WITH flag + bool ih; //immediate higher 8-bit flag + bool il; //immediate lower 8-bit flag + bool alt2; //ALT2 mode + bool alt1; //ALT2 instruction mode + bool r; //ROM r14 read flag + bool g; //GO flag + bool ov; //overflow flag + bool s; //sign flag + bool cy; //carry flag + bool z; //zero flag + + operator unsigned() const { + return (irq << 15) | (b << 12) | (ih << 11) | (il << 10) | (alt2 << 9) | (alt1 << 8) + | (r << 6) | (g << 5) | (ov << 4) | (s << 3) | (cy << 2) | (z << 1); + } + + sfr_t& operator=(uint16_t data) { + irq = data & 0x8000; + b = data & 0x1000; + ih = data & 0x0800; + il = data & 0x0400; + alt2 = data & 0x0200; + alt1 = data & 0x0100; + r = data & 0x0040; + g = data & 0x0020; + ov = data & 0x0010; + s = data & 0x0008; + cy = data & 0x0004; + z = data & 0x0002; + return *this; + } +}; + +struct scmr_t { + unsigned ht; + bool ron; + bool ran; + unsigned md; + + operator unsigned() const { + return ((ht >> 1) << 5) | (ron << 4) | (ran << 3) | ((ht & 1) << 2) | (md); + } + + scmr_t& operator=(uint8 data) { + ht = (bool)(data & 0x20) << 1; + ht |= (bool)(data & 0x04) << 0; + ron = data & 0x10; + ran = data & 0x08; + md = data & 0x03; + return *this; + } +}; + +struct por_t { + bool obj; + bool freezehigh; + bool highnibble; + bool dither; + bool transparent; + + operator unsigned() const { + return (obj << 4) | (freezehigh << 3) | (highnibble << 2) | (dither << 1) | (transparent); + } + + por_t& operator=(uint8 data) { + obj = data & 0x10; + freezehigh = data & 0x08; + highnibble = data & 0x04; + dither = data & 0x02; + transparent = data & 0x01; + return *this; + } +}; + +struct cfgr_t { + bool irq; + bool ms0; + + operator unsigned() const { + return (irq << 7) | (ms0 << 5); + } + + cfgr_t& operator=(uint8 data) { + irq = data & 0x80; + ms0 = data & 0x20; + return *this; + } +}; + +struct regs_t { + uint8 pipeline; + uint16 ramaddr; + + reg16_t r[16]; //general purpose registers + sfr_t sfr; //status flag register + uint8 pbr; //program bank register + uint8 rombr; //game pack ROM bank register + bool rambr; //game pack RAM bank register + uint16 cbr; //cache base register + uint8 scbr; //screen base register + scmr_t scmr; //screen mode register + uint8 colr; //color register + por_t por; //plot option register + bool bramr; //back-up RAM register + uint8 vcr; //version code register + cfgr_t cfgr; //config register + bool clsr; //clock select register + + unsigned romcl; //clock ticks until romdr is valid + uint8 romdr; //ROM buffer data register + + unsigned ramcl; //clock ticks until ramdr is valid + uint16 ramar; //RAM buffer address register + uint8 ramdr; //RAM buffer data register + + unsigned sreg, dreg; + reg16_t& sr() { return r[sreg]; } //source register (from) + reg16_t& dr() { return r[dreg]; } //destination register (to) + + void reset() { + sfr.b = 0; + sfr.alt1 = 0; + sfr.alt2 = 0; + + sreg = 0; + dreg = 0; + } +} regs; + +struct cache_t { + uint8 buffer[512]; + bool valid[32]; +} cache; + +struct pixelcache_t { + uint16 offset; + uint8 bitpend; + uint8 data[8]; +} pixelcache[2]; diff --git a/libsnes/snes/chip/superfx/disasm/disasm.cpp b/libsnes/snes/chip/superfx/disasm/disasm.cpp new file mode 100644 index 0000000..f3454e6 --- /dev/null +++ b/libsnes/snes/chip/superfx/disasm/disasm.cpp @@ -0,0 +1,279 @@ +#ifdef SUPERFX_CPP + +void SuperFX::disassemble_opcode(char *output) { + *output = 0; + + if(!regs.sfr.alt2) { + if(!regs.sfr.alt1) { + disassemble_alt0(output); + } else { + disassemble_alt1(output); + } + } else { + if(!regs.sfr.alt1) { + disassemble_alt2(output); + } else { + disassemble_alt3(output); + } + } + + unsigned length = strlen(output); + while(length++ < 20) strcat(output, " "); +} + +#define case4(id) \ + case id+ 0: case id+ 1: case id+ 2: case id+ 3 +#define case6(id) \ + case id+ 0: case id+ 1: case id+ 2: case id+ 3: case id+ 4: case id+ 5 +#define case12(id) \ + case id+ 0: case id+ 1: case id+ 2: case id+ 3: case id+ 4: case id+ 5: case id+ 6: case id+ 7: \ + case id+ 8: case id+ 9: case id+10: case id+11 +#define case15(id) \ + case id+ 0: case id+ 1: case id+ 2: case id+ 3: case id+ 4: case id+ 5: case id+ 6: case id+ 7: \ + case id+ 8: case id+ 9: case id+10: case id+11: case id+12: case id+13: case id+14 +#define case16(id) \ + case id+ 0: case id+ 1: case id+ 2: case id+ 3: case id+ 4: case id+ 5: case id+ 6: case id+ 7: \ + case id+ 8: case id+ 9: case id+10: case id+11: case id+12: case id+13: case id+14: case id+15 + +#define op0 regs.pipeline +#define op1 bus_read((regs.pbr << 16) + regs.r[15] + 0) +#define op2 bus_read((regs.pbr << 16) + regs.r[15] + 1) + +void SuperFX::disassemble_alt0(char *output) { + char t[256] = ""; + switch(op0) { + case (0x00): sprintf(t, "stop"); break; + case (0x01): sprintf(t, "nop"); break; + case (0x02): sprintf(t, "cache"); break; + case (0x03): sprintf(t, "lsr"); break; + case (0x04): sprintf(t, "rol"); break; + case (0x05): sprintf(t, "bra %+d", (int8_t)op1); break; + case (0x06): sprintf(t, "blt %+d", (int8_t)op1); break; + case (0x07): sprintf(t, "bge %+d", (int8_t)op1); break; + case (0x08): sprintf(t, "bne %+d", (int8_t)op1); break; + case (0x09): sprintf(t, "beq %+d", (int8_t)op1); break; + case (0x0a): sprintf(t, "bpl %+d", (int8_t)op1); break; + case (0x0b): sprintf(t, "bmi %+d", (int8_t)op1); break; + case (0x0c): sprintf(t, "bcc %+d", (int8_t)op1); break; + case (0x0d): sprintf(t, "bcs %+d", (int8_t)op1); break; + case (0x0e): sprintf(t, "bvc %+d", (int8_t)op1); break; + case (0x0f): sprintf(t, "bvs %+d", (int8_t)op1); break; + case16(0x10): sprintf(t, "to r%u", op0 & 15); break; + case16(0x20): sprintf(t, "with r%u", op0 & 15); break; + case12(0x30): sprintf(t, "stw (r%u)", op0 & 15); break; + case (0x3c): sprintf(t, "loop"); break; + case (0x3d): sprintf(t, "alt1"); break; + case (0x3e): sprintf(t, "alt2"); break; + case (0x3f): sprintf(t, "alt3"); break; + case12(0x40): sprintf(t, "ldw (r%u)", op0 & 15); break; + case (0x4c): sprintf(t, "plot"); break; + case (0x4d): sprintf(t, "swap"); break; + case (0x4e): sprintf(t, "color"); break; + case (0x4f): sprintf(t, "not"); break; + case16(0x50): sprintf(t, "add r%u", op0 & 15); break; + case16(0x60): sprintf(t, "sub r%u", op0 & 15); break; + case (0x70): sprintf(t, "merge"); break; + case15(0x71): sprintf(t, "and r%u", op0 & 15); break; + case16(0x80): sprintf(t, "mult r%u", op0 & 15); break; + case (0x90): sprintf(t, "sbk"); break; + case4 (0x91): sprintf(t, "link #%u", op0 & 15); break; + case (0x95): sprintf(t, "sex"); break; + case (0x96): sprintf(t, "asr"); break; + case (0x97): sprintf(t, "ror"); break; + case6 (0x98): sprintf(t, "jmp r%u", op0 & 15); break; + case (0x9e): sprintf(t, "lob"); break; + case (0x9f): sprintf(t, "fmult"); break; + case16(0xa0): sprintf(t, "ibt r%u,#$%.2x", op0 & 15, op1); break; + case16(0xb0): sprintf(t, "from r%u", op0 & 15); break; + case (0xc0): sprintf(t, "hib"); + case15(0xc1): sprintf(t, "or r%u", op0 & 15); break; + case15(0xd0): sprintf(t, "inc r%u", op0 & 15); break; + case (0xdf): sprintf(t, "getc"); break; + case15(0xe0): sprintf(t, "dec r%u", op0 & 15); break; + case (0xef): sprintf(t, "getb"); break; + case16(0xf0): sprintf(t, "iwt r%u,#$%.2x%.2x", op0 & 15, op2, op1); break; + } + strcat(output, t); +} + +void SuperFX::disassemble_alt1(char *output) { + char t[256] = ""; + switch(op0) { + case (0x00): sprintf(t, "stop"); break; + case (0x01): sprintf(t, "nop"); break; + case (0x02): sprintf(t, "cache"); break; + case (0x03): sprintf(t, "lsr"); break; + case (0x04): sprintf(t, "rol"); break; + case (0x05): sprintf(t, "bra %+d", (int8_t)op1); break; + case (0x06): sprintf(t, "blt %+d", (int8_t)op1); break; + case (0x07): sprintf(t, "bge %+d", (int8_t)op1); break; + case (0x08): sprintf(t, "bne %+d", (int8_t)op1); break; + case (0x09): sprintf(t, "beq %+d", (int8_t)op1); break; + case (0x0a): sprintf(t, "bpl %+d", (int8_t)op1); break; + case (0x0b): sprintf(t, "bmi %+d", (int8_t)op1); break; + case (0x0c): sprintf(t, "bcc %+d", (int8_t)op1); break; + case (0x0d): sprintf(t, "bcs %+d", (int8_t)op1); break; + case (0x0e): sprintf(t, "bvc %+d", (int8_t)op1); break; + case (0x0f): sprintf(t, "bvs %+d", (int8_t)op1); break; + case16(0x10): sprintf(t, "to r%u", op0 & 15); break; + case16(0x20): sprintf(t, "with r%u", op0 & 15); break; + case12(0x30): sprintf(t, "stb (r%u)", op0 & 15); break; + case (0x3c): sprintf(t, "loop"); break; + case (0x3d): sprintf(t, "alt1"); break; + case (0x3e): sprintf(t, "alt2"); break; + case (0x3f): sprintf(t, "alt3"); break; + case12(0x40): sprintf(t, "ldb (r%u)", op0 & 15); break; + case (0x4c): sprintf(t, "rpix"); break; + case (0x4d): sprintf(t, "swap"); break; + case (0x4e): sprintf(t, "cmode"); break; + case (0x4f): sprintf(t, "not"); break; + case16(0x50): sprintf(t, "adc r%u", op0 & 15); break; + case16(0x60): sprintf(t, "sbc r%u", op0 & 15); break; + case (0x70): sprintf(t, "merge"); break; + case15(0x71): sprintf(t, "bic r%u", op0 & 15); break; + case16(0x80): sprintf(t, "umult r%u", op0 & 15); break; + case (0x90): sprintf(t, "sbk"); break; + case4 (0x91): sprintf(t, "link #%u", op0 & 15); break; + case (0x95): sprintf(t, "sex"); break; + case (0x96): sprintf(t, "div2"); break; + case (0x97): sprintf(t, "ror"); break; + case6 (0x98): sprintf(t, "ljmp r%u", op0 & 15); break; + case (0x9e): sprintf(t, "lob"); break; + case (0x9f): sprintf(t, "lmult"); break; + case16(0xa0): sprintf(t, "lms r%u,(#$%.4x)", op0 & 15, op1 << 1); break; + case16(0xb0): sprintf(t, "from r%u", op0 & 15); break; + case (0xc0): sprintf(t, "hib"); break; + case15(0xc1): sprintf(t, "xor r%u", op0 & 15); break; + case15(0xd0): sprintf(t, "inc r%u", op0 & 15); break; + case (0xdf): sprintf(t, "getc"); break; + case15(0xe0): sprintf(t, "dec r%u", op0 & 15); break; + case (0xef): sprintf(t, "getbh"); break; + case16(0xf0): sprintf(t, "lm r%u", op0 & 15); break; + } + strcat(output, t); +} + +void SuperFX::disassemble_alt2(char *output) { + char t[256] = ""; + switch(op0) { + case (0x00): sprintf(t, "stop"); break; + case (0x01): sprintf(t, "nop"); break; + case (0x02): sprintf(t, "cache"); break; + case (0x03): sprintf(t, "lsr"); break; + case (0x04): sprintf(t, "rol"); break; + case (0x05): sprintf(t, "bra %+d", (int8_t)op1); break; + case (0x06): sprintf(t, "blt %+d", (int8_t)op1); break; + case (0x07): sprintf(t, "bge %+d", (int8_t)op1); break; + case (0x08): sprintf(t, "bne %+d", (int8_t)op1); break; + case (0x09): sprintf(t, "beq %+d", (int8_t)op1); break; + case (0x0a): sprintf(t, "bpl %+d", (int8_t)op1); break; + case (0x0b): sprintf(t, "bmi %+d", (int8_t)op1); break; + case (0x0c): sprintf(t, "bcc %+d", (int8_t)op1); break; + case (0x0d): sprintf(t, "bcs %+d", (int8_t)op1); break; + case (0x0e): sprintf(t, "bvc %+d", (int8_t)op1); break; + case (0x0f): sprintf(t, "bvs %+d", (int8_t)op1); break; + case16(0x10): sprintf(t, "to r%u", op0 & 15); break; + case16(0x20): sprintf(t, "with r%u", op0 & 15); break; + case12(0x30): sprintf(t, "stw (r%u)", op0 & 15); break; + case (0x3c): sprintf(t, "loop"); break; + case (0x3d): sprintf(t, "alt1"); break; + case (0x3e): sprintf(t, "alt2"); break; + case (0x3f): sprintf(t, "alt3"); break; + case12(0x40): sprintf(t, "ldw (r%u)", op0 & 15); break; + case (0x4c): sprintf(t, "plot"); break; + case (0x4d): sprintf(t, "swap"); break; + case (0x4e): sprintf(t, "color"); break; + case (0x4f): sprintf(t, "not"); break; + case16(0x50): sprintf(t, "add #%u", op0 & 15); break; + case16(0x60): sprintf(t, "sub #%u", op0 & 15); break; + case (0x70): sprintf(t, "merge"); break; + case15(0x71): sprintf(t, "and #%u", op0 & 15); break; + case16(0x80): sprintf(t, "mult #%u", op0 & 15); break; + case (0x90): sprintf(t, "sbk"); break; + case4 (0x91): sprintf(t, "link #%u", op0 & 15); break; + case (0x95): sprintf(t, "sex"); break; + case (0x96): sprintf(t, "asr"); break; + case (0x97): sprintf(t, "ror"); break; + case6 (0x98): sprintf(t, "jmp r%u", op0 & 15); break; + case (0x9e): sprintf(t, "lob"); break; + case (0x9f): sprintf(t, "fmult"); break; + case16(0xa0): sprintf(t, "sms r%u,(#$%.4x)", op0 & 15, op1 << 1); break; + case16(0xb0): sprintf(t, "from r%u", op0 & 15); break; + case (0xc0): sprintf(t, "hib"); break; + case15(0xc1): sprintf(t, "or #%u", op0 & 15); break; + case15(0xd0): sprintf(t, "inc r%u", op0 & 15); break; + case (0xdf): sprintf(t, "ramb"); break; + case15(0xe0): sprintf(t, "dec r%u", op0 & 15); break; + case (0xef): sprintf(t, "getbl"); break; + case16(0xf0): sprintf(t, "sm r%u", op0 & 15); break; + } + strcat(output, t); +} + +void SuperFX::disassemble_alt3(char *output) { + char t[256] = ""; + switch(op0) { + case (0x00): sprintf(t, "stop"); break; + case (0x01): sprintf(t, "nop"); break; + case (0x02): sprintf(t, "cache"); break; + case (0x03): sprintf(t, "lsr"); break; + case (0x04): sprintf(t, "rol"); break; + case (0x05): sprintf(t, "bra %+d", (int8_t)op1); break; + case (0x06): sprintf(t, "blt %+d", (int8_t)op1); break; + case (0x07): sprintf(t, "bge %+d", (int8_t)op1); break; + case (0x08): sprintf(t, "bne %+d", (int8_t)op1); break; + case (0x09): sprintf(t, "beq %+d", (int8_t)op1); break; + case (0x0a): sprintf(t, "bpl %+d", (int8_t)op1); break; + case (0x0b): sprintf(t, "bmi %+d", (int8_t)op1); break; + case (0x0c): sprintf(t, "bcc %+d", (int8_t)op1); break; + case (0x0d): sprintf(t, "bcs %+d", (int8_t)op1); break; + case (0x0e): sprintf(t, "bvc %+d", (int8_t)op1); break; + case (0x0f): sprintf(t, "bvs %+d", (int8_t)op1); break; + case16(0x10): sprintf(t, "to r%u", op0 & 15); break; + case16(0x20): sprintf(t, "with r%u", op0 & 15); break; + case12(0x30): sprintf(t, "stb (r%u)", op0 & 15); break; + case (0x3c): sprintf(t, "loop"); break; + case (0x3d): sprintf(t, "alt1"); break; + case (0x3e): sprintf(t, "alt2"); break; + case (0x3f): sprintf(t, "alt3"); break; + case12(0x40): sprintf(t, "ldb (r%u)", op0 & 15); break; + case (0x4c): sprintf(t, "rpix"); break; + case (0x4d): sprintf(t, "swap"); break; + case (0x4e): sprintf(t, "cmode"); break; + case (0x4f): sprintf(t, "not"); break; + case16(0x50): sprintf(t, "adc #%u", op0 & 15); break; + case16(0x60): sprintf(t, "cmp r%u", op0 & 15); break; + case (0x70): sprintf(t, "merge"); break; + case15(0x71): sprintf(t, "bic #%u", op0 & 15); break; + case16(0x80): sprintf(t, "umult #%u", op0 & 15); break; + case (0x90): sprintf(t, "sbk"); break; + case4 (0x91): sprintf(t, "link #%u", op0 & 15); break; + case (0x95): sprintf(t, "sex"); break; + case (0x96): sprintf(t, "div2"); break; + case (0x97): sprintf(t, "ror"); break; + case6 (0x98): sprintf(t, "ljmp r%u", op0 & 15); break; + case (0x9e): sprintf(t, "lob"); break; + case (0x9f): sprintf(t, "lmult"); break; + case16(0xa0): sprintf(t, "lms r%u", op0 & 15); break; + case16(0xb0): sprintf(t, "from r%u", op0 & 15); break; + case (0xc0): sprintf(t, "hib"); break; + case15(0xc1): sprintf(t, "xor #%u", op0 & 15); break; + case15(0xd0): sprintf(t, "inc r%u", op0 & 15); break; + case (0xdf): sprintf(t, "romb"); break; + case15(0xe0): sprintf(t, "dec r%u", op0 & 15); break; + case (0xef): sprintf(t, "getbs"); break; + case16(0xf0): sprintf(t, "lm r%u", op0 & 15); break; + } + strcat(output, t); +} + +#undef case4 +#undef case6 +#undef case12 +#undef case15 +#undef case16 +#undef op0 +#undef op1 +#undef op2 + +#endif diff --git a/libsnes/snes/chip/superfx/disasm/disasm.hpp b/libsnes/snes/chip/superfx/disasm/disasm.hpp new file mode 100644 index 0000000..903615e --- /dev/null +++ b/libsnes/snes/chip/superfx/disasm/disasm.hpp @@ -0,0 +1,5 @@ +void disassemble_opcode(char *output); +void disassemble_alt0(char *output); +void disassemble_alt1(char *output); +void disassemble_alt2(char *output); +void disassemble_alt3(char *output); diff --git a/libsnes/snes/chip/superfx/memory/memory.cpp b/libsnes/snes/chip/superfx/memory/memory.cpp new file mode 100644 index 0000000..6cac020 --- /dev/null +++ b/libsnes/snes/chip/superfx/memory/memory.cpp @@ -0,0 +1,112 @@ +#ifdef SUPERFX_CPP + +uint8 SuperFX::bus_read(unsigned addr) { + if((addr & 0xc00000) == 0x000000) { //$00-3f:0000-7fff, $00-3f:8000-ffff + while(!regs.scmr.ron && scheduler.sync.i != Scheduler::SynchronizeMode::All) { + add_clocks(6); + synchronize_cpu(); + } + return cartridge.rom.read((((addr & 0x3f0000) >> 1) | (addr & 0x7fff)) & rom_mask); + } + + if((addr & 0xe00000) == 0x400000) { //$40-5f:0000-ffff + while(!regs.scmr.ron && scheduler.sync.i != Scheduler::SynchronizeMode::All) { + add_clocks(6); + synchronize_cpu(); + } + return cartridge.rom.read(addr & rom_mask); + } + + if((addr & 0xe00000) == 0x600000) { //$60-7f:0000-ffff + while(!regs.scmr.ran && scheduler.sync.i != Scheduler::SynchronizeMode::All) { + add_clocks(6); + synchronize_cpu(); + } + return cartridge.ram.read(addr & ram_mask); + } + + return 0; // FIXME: Can it reach here? +} + +void SuperFX::bus_write(unsigned addr, uint8 data) { + if((addr & 0xe00000) == 0x600000) { //$60-7f:0000-ffff + while(!regs.scmr.ran && scheduler.sync.i != Scheduler::SynchronizeMode::All) { + add_clocks(6); + synchronize_cpu(); + } + return cartridge.ram.write(addr & ram_mask, data); + } +} + +uint8 SuperFX::op_read(uint16 addr) { + uint16 offset = addr - regs.cbr; + if(offset < 512) { + if(cache.valid[offset >> 4] == false) { + unsigned dp = offset & 0xfff0; + unsigned sp = (regs.pbr << 16) + ((regs.cbr + dp) & 0xfff0); + for(unsigned n = 0; n < 16; n++) { + add_clocks(memory_access_speed); + cache.buffer[dp++] = bus_read(sp++); + } + cache.valid[offset >> 4] = true; + } else { + add_clocks(cache_access_speed); + } + return cache.buffer[offset]; + } + + if(regs.pbr <= 0x5f) { + //$[00-5f]:[0000-ffff] ROM + rombuffer_sync(); + add_clocks(memory_access_speed); + return bus_read((regs.pbr << 16) + addr); + } else { + //$[60-7f]:[0000-ffff] RAM + rambuffer_sync(); + add_clocks(memory_access_speed); + return bus_read((regs.pbr << 16) + addr); + } +} + +uint8 SuperFX::peekpipe() { + uint8 result = regs.pipeline; + regs.pipeline = op_read(regs.r[15]); + r15_modified = false; + return result; +} + +uint8 SuperFX::pipe() { + uint8 result = regs.pipeline; + regs.pipeline = op_read(++regs.r[15]); + r15_modified = false; + return result; +} + +void SuperFX::cache_flush() { + for(unsigned n = 0; n < 32; n++) cache.valid[n] = false; +} + +uint8 SuperFX::cache_mmio_read(uint16 addr) { + addr = (addr + regs.cbr) & 511; + return cache.buffer[addr]; +} + +void SuperFX::cache_mmio_write(uint16 addr, uint8 data) { + addr = (addr + regs.cbr) & 511; + cache.buffer[addr] = data; + if((addr & 15) == 15) cache.valid[addr >> 4] = true; +} + +void SuperFX::memory_reset() { + rom_mask = cartridge.rom.size() - 1; + ram_mask = cartridge.ram.size() - 1; + + for(unsigned n = 0; n < 512; n++) cache.buffer[n] = 0x00; + for(unsigned n = 0; n < 32; n++) cache.valid[n] = false; + for(unsigned n = 0; n < 2; n++) { + pixelcache[n].offset = ~0; + pixelcache[n].bitpend = 0x00; + } +} + +#endif diff --git a/libsnes/snes/chip/superfx/memory/memory.hpp b/libsnes/snes/chip/superfx/memory/memory.hpp new file mode 100644 index 0000000..2ff6285 --- /dev/null +++ b/libsnes/snes/chip/superfx/memory/memory.hpp @@ -0,0 +1,14 @@ +unsigned rom_mask; //rom_size - 1 +unsigned ram_mask; //ram_size - 1 +uint8 bus_read(unsigned addr); +void bus_write(unsigned addr, uint8 data); + +uint8 op_read(uint16 addr); +alwaysinline uint8 peekpipe(); +alwaysinline uint8 pipe(); + +void cache_flush(); +uint8 cache_mmio_read(uint16 addr); +void cache_mmio_write(uint16 addr, uint8 data); + +void memory_reset(); diff --git a/libsnes/snes/chip/superfx/mmio/mmio.cpp b/libsnes/snes/chip/superfx/mmio/mmio.cpp new file mode 100644 index 0000000..5650a77 --- /dev/null +++ b/libsnes/snes/chip/superfx/mmio/mmio.cpp @@ -0,0 +1,118 @@ +#ifdef SUPERFX_CPP + +uint8 SuperFX::mmio_read(unsigned addr) { + cpu.synchronize_coprocessors(); + addr &= 0xffff; + + if(addr >= 0x3100 && addr <= 0x32ff) { + return cache_mmio_read(addr - 0x3100); + } + + if(addr >= 0x3000 && addr <= 0x301f) { + return regs.r[(addr >> 1) & 15] >> ((addr & 1) << 3); + } + + switch(addr) { + case 0x3030: { + return regs.sfr >> 0; + } + + case 0x3031: { + uint8 r = regs.sfr >> 8; + regs.sfr.irq = 0; + cpu.regs.irq = 0; + return r; + } + + case 0x3034: { + return regs.pbr; + } + + case 0x3036: { + return regs.rombr; + } + + case 0x303b: { + return regs.vcr; + } + + case 0x303c: { + return regs.rambr; + } + + case 0x303e: { + return regs.cbr >> 0; + } + + case 0x303f: { + return regs.cbr >> 8; + } + } + + return 0x00; +} + +void SuperFX::mmio_write(unsigned addr, uint8 data) { + cpu.synchronize_coprocessors(); + addr &= 0xffff; + + if(addr >= 0x3100 && addr <= 0x32ff) { + return cache_mmio_write(addr - 0x3100, data); + } + + if(addr >= 0x3000 && addr <= 0x301f) { + unsigned n = (addr >> 1) & 15; + if((addr & 1) == 0) { + regs.r[n] = (regs.r[n] & 0xff00) | data; + } else { + regs.r[n] = (data << 8) | (regs.r[n] & 0xff); + } + + if(addr == 0x301f) regs.sfr.g = 1; + return; + } + + switch(addr) { + case 0x3030: { + bool g = regs.sfr.g; + regs.sfr = (regs.sfr & 0xff00) | (data << 0); + if(g == 1 && regs.sfr.g == 0) { + regs.cbr = 0x0000; + cache_flush(); + } + } break; + + case 0x3031: { + regs.sfr = (data << 8) | (regs.sfr & 0x00ff); + } break; + + case 0x3033: { + regs.bramr = data; + } break; + + case 0x3034: { + regs.pbr = data & 0x7f; + cache_flush(); + } break; + + case 0x3037: { + regs.cfgr = data; + update_speed(); + } break; + + case 0x3038: { + regs.scbr = data; + } break; + + case 0x3039: { + regs.clsr = data; + update_speed(); + } break; + + case 0x303a: { + regs.scmr = data; + } break; + } +} + +#endif diff --git a/libsnes/snes/chip/superfx/mmio/mmio.hpp b/libsnes/snes/chip/superfx/mmio/mmio.hpp new file mode 100644 index 0000000..08cc85a --- /dev/null +++ b/libsnes/snes/chip/superfx/mmio/mmio.hpp @@ -0,0 +1,2 @@ +uint8 mmio_read(unsigned addr); +void mmio_write(unsigned addr, uint8 data); diff --git a/libsnes/snes/chip/superfx/serialization.cpp b/libsnes/snes/chip/superfx/serialization.cpp new file mode 100644 index 0000000..67e5385 --- /dev/null +++ b/libsnes/snes/chip/superfx/serialization.cpp @@ -0,0 +1,96 @@ +#ifdef SUPERFX_CPP + +void SuperFX::serialize(serializer &s) { + Processor::serialize(s); + + //superfx.hpp + s.integer(clockmode); + s.integer(instruction_counter); + + //core/registers.hpp + s.integer(regs.pipeline); + s.integer(regs.ramaddr); + + s.integer(regs.r[ 0].data); + s.integer(regs.r[ 1].data); + s.integer(regs.r[ 2].data); + s.integer(regs.r[ 3].data); + s.integer(regs.r[ 4].data); + s.integer(regs.r[ 5].data); + s.integer(regs.r[ 6].data); + s.integer(regs.r[ 7].data); + s.integer(regs.r[ 8].data); + s.integer(regs.r[ 9].data); + s.integer(regs.r[10].data); + s.integer(regs.r[11].data); + s.integer(regs.r[12].data); + s.integer(regs.r[13].data); + s.integer(regs.r[14].data); + s.integer(regs.r[15].data); + + s.integer(regs.sfr.irq); + s.integer(regs.sfr.b); + s.integer(regs.sfr.ih); + s.integer(regs.sfr.il); + s.integer(regs.sfr.alt2); + s.integer(regs.sfr.alt1); + s.integer(regs.sfr.r); + s.integer(regs.sfr.g); + s.integer(regs.sfr.ov); + s.integer(regs.sfr.s); + s.integer(regs.sfr.cy); + s.integer(regs.sfr.z); + + s.integer(regs.pbr); + s.integer(regs.rombr); + s.integer(regs.rambr); + s.integer(regs.cbr); + s.integer(regs.scbr); + + s.integer(regs.scmr.ht); + s.integer(regs.scmr.ron); + s.integer(regs.scmr.ran); + s.integer(regs.scmr.md); + + s.integer(regs.colr); + + s.integer(regs.por.obj); + s.integer(regs.por.freezehigh); + s.integer(regs.por.highnibble); + s.integer(regs.por.dither); + s.integer(regs.por.transparent); + + s.integer(regs.bramr); + s.integer(regs.vcr); + + s.integer(regs.cfgr.irq); + s.integer(regs.cfgr.ms0); + + s.integer(regs.clsr); + + s.integer(regs.romcl); + s.integer(regs.romdr); + + s.integer(regs.ramcl); + s.integer(regs.ramar); + s.integer(regs.ramdr); + + s.integer(regs.sreg); + s.integer(regs.dreg); + + s.array(cache.buffer); + s.array(cache.valid); + + for(unsigned i = 0; i < 2; i++) { + s.integer(pixelcache[i].offset); + s.integer(pixelcache[i].bitpend); + s.array(pixelcache[i].data); + } + + //timing/timing.hpp + s.integer(cache_access_speed); + s.integer(memory_access_speed); + s.integer(r15_modified); +} + +#endif diff --git a/libsnes/snes/chip/superfx/superfx.cpp b/libsnes/snes/chip/superfx/superfx.cpp new file mode 100644 index 0000000..f0eeaa5 --- /dev/null +++ b/libsnes/snes/chip/superfx/superfx.cpp @@ -0,0 +1,82 @@ +#include + +#define SUPERFX_CPP +namespace SNES { + +#include "serialization.cpp" +#include "bus/bus.cpp" +#include "core/core.cpp" +#include "memory/memory.cpp" +#include "mmio/mmio.cpp" +#include "timing/timing.cpp" +#include "disasm/disasm.cpp" + +SuperFX superfx; + +void SuperFX::Enter() { superfx.enter(); } + +void SuperFX::enter() { + while(true) { + if(scheduler.sync.i == Scheduler::SynchronizeMode::All) { + scheduler.exit(Scheduler::ExitReason::SynchronizeEvent); + } + + if(regs.sfr.g == 0) { + add_clocks(6); + synchronize_cpu(); + continue; + } + + (this->*opcode_table[(regs.sfr & 0x0300) + peekpipe()])(); + if(r15_modified == false) regs.r[15]++; + + if(++instruction_counter >= 128) { + instruction_counter = 0; + synchronize_cpu(); + } + } +} + +void SuperFX::init() { + initialize_opcode_table(); + regs.r[14].on_modify = bind( &SuperFX::r14_modify, this ); + regs.r[15].on_modify = bind( &SuperFX::r15_modify, this ); +} + +void SuperFX::load() { +} + +void SuperFX::unload() { +} + +void SuperFX::power() { + clockmode = config.superfx.speed; +} + +void SuperFX::reset() { + create(SuperFX::Enter, system.cpu_frequency); + instruction_counter = 0; + + for(unsigned n = 0; n < 16; n++) regs.r[n] = 0x0000; + regs.sfr = 0x0000; + regs.pbr = 0x00; + regs.rombr = 0x00; + regs.rambr = 0; + regs.cbr = 0x0000; + regs.scbr = 0x00; + regs.scmr = 0x00; + regs.colr = 0x00; + regs.por = 0x00; + regs.bramr = 0; + regs.vcr = 0x04; + regs.cfgr = 0x00; + regs.clsr = 0; + regs.pipeline = 0x01; //nop + regs.ramaddr = 0x0000; + regs.reset(); + + memory_reset(); + timing_reset(); +} + +} diff --git a/libsnes/snes/chip/superfx/superfx.hpp b/libsnes/snes/chip/superfx/superfx.hpp new file mode 100644 index 0000000..cf28952 --- /dev/null +++ b/libsnes/snes/chip/superfx/superfx.hpp @@ -0,0 +1,24 @@ +class SuperFX : public Coprocessor { +public: + #include "bus/bus.hpp" + #include "core/core.hpp" + #include "memory/memory.hpp" + #include "mmio/mmio.hpp" + #include "timing/timing.hpp" + #include "disasm/disasm.hpp" + + static void Enter(); + void enter(); + void init(); + void load(); + void unload(); + void power(); + void reset(); + void serialize(serializer&); + +private: + unsigned clockmode; + unsigned instruction_counter; +}; + +extern SuperFX superfx; diff --git a/libsnes/snes/chip/superfx/timing/timing.cpp b/libsnes/snes/chip/superfx/timing/timing.cpp new file mode 100644 index 0000000..5276564 --- /dev/null +++ b/libsnes/snes/chip/superfx/timing/timing.cpp @@ -0,0 +1,97 @@ +#ifdef SUPERFX_CPP + +void SuperFX::add_clocks(unsigned clocks) { + if(regs.romcl) { + regs.romcl -= min(clocks, regs.romcl); + if(regs.romcl == 0) { + regs.sfr.r = 0; + regs.romdr = bus_read((regs.rombr << 16) + regs.r[14]); + } + } + + if(regs.ramcl) { + regs.ramcl -= min(clocks, regs.ramcl); + if(regs.ramcl == 0) { + bus_write(0x700000 + (regs.rambr << 16) + regs.ramar, regs.ramdr); + } + } + + step(clocks); + synchronize_cpu(); +} + +void SuperFX::rombuffer_sync() { + if(regs.romcl) add_clocks(regs.romcl); +} + +void SuperFX::rombuffer_update() { + regs.sfr.r = 1; + regs.romcl = memory_access_speed; +} + +uint8 SuperFX::rombuffer_read() { + rombuffer_sync(); + return regs.romdr; +} + +void SuperFX::rambuffer_sync() { + if(regs.ramcl) add_clocks(regs.ramcl); +} + +uint8 SuperFX::rambuffer_read(uint16 addr) { + rambuffer_sync(); + return bus_read(0x700000 + (regs.rambr << 16) + addr); +} + +void SuperFX::rambuffer_write(uint16 addr, uint8 data) { + rambuffer_sync(); + regs.ramcl = memory_access_speed; + regs.ramar = addr; + regs.ramdr = data; +} + +void SuperFX::r14_modify(uint16 data) { + regs.r[14].data = data; + rombuffer_update(); +} + +void SuperFX::r15_modify(uint16 data) { + regs.r[15].data = data; + r15_modified = true; +} + +void SuperFX::update_speed() { + //force SuperFX1 mode? + if(clockmode == 1) { + cache_access_speed = 2; + memory_access_speed = 6; + return; + } + + //force SuperFX2 mode? + if(clockmode == 2) { + cache_access_speed = 1; + memory_access_speed = 5; + regs.cfgr.ms0 = 0; //cannot use high-speed multiplication in 21MHz mode + return; + } + + //default: allow S-CPU to select mode + cache_access_speed = (regs.clsr ? 1 : 2); + memory_access_speed = (regs.clsr ? 5 : 6); + if(regs.clsr) regs.cfgr.ms0 = 0; //cannot use high-speed multiplication in 21MHz mode +} + +void SuperFX::timing_reset() { + update_speed(); + r15_modified = false; + + regs.romcl = 0; + regs.romdr = 0; + + regs.ramcl = 0; + regs.ramar = 0; + regs.ramdr = 0; +} + +#endif diff --git a/libsnes/snes/chip/superfx/timing/timing.hpp b/libsnes/snes/chip/superfx/timing/timing.hpp new file mode 100644 index 0000000..9ae7e8d --- /dev/null +++ b/libsnes/snes/chip/superfx/timing/timing.hpp @@ -0,0 +1,19 @@ +unsigned cache_access_speed; +unsigned memory_access_speed; +bool r15_modified; + +void add_clocks(unsigned clocks); + +void rombuffer_sync(); +void rombuffer_update(); +uint8 rombuffer_read(); + +void rambuffer_sync(); +uint8 rambuffer_read(uint16 addr); +void rambuffer_write(uint16 addr, uint8 data); + +void r14_modify(uint16); +void r15_modify(uint16); + +void update_speed(); +void timing_reset(); diff --git a/libsnes/snes/config/config.cpp b/libsnes/snes/config/config.cpp new file mode 100644 index 0000000..78884f1 --- /dev/null +++ b/libsnes/snes/config/config.cpp @@ -0,0 +1,26 @@ +#ifdef SYSTEM_CPP + +Configuration config; + +Configuration::Configuration() { + controller_port1.i = Input::Device::Joypad; + controller_port2.i = Input::Device::Joypad; + expansion_port.i = System::ExpansionPortDevice::BSX; + region.i = System::Region::Autodetect; + random = false; // Do this for now until there is an interface for libsnes. + + cpu.version = 2; + cpu.ntsc_frequency = 21477272; //315 / 88 * 6000000 + cpu.pal_frequency = 21281370; + cpu.wram_init_value = 0x55; + + smp.ntsc_frequency = 24607104; //32040.5 * 768 + smp.pal_frequency = 24607104; + + ppu1.version = 1; + ppu2.version = 3; + + superfx.speed = 0; //0 = auto-select, 1 = force 10.74MHz, 2 = force 21.48MHz +} + +#endif diff --git a/libsnes/snes/config/config.hpp b/libsnes/snes/config/config.hpp new file mode 100644 index 0000000..1f4d037 --- /dev/null +++ b/libsnes/snes/config/config.hpp @@ -0,0 +1,35 @@ +struct Configuration { + Input::Device controller_port1; + Input::Device controller_port2; + System::ExpansionPortDevice expansion_port; + System::Region region; + bool random; + + struct CPU { + unsigned version; + unsigned ntsc_frequency; + unsigned pal_frequency; + unsigned wram_init_value; + } cpu; + + struct SMP { + unsigned ntsc_frequency; + unsigned pal_frequency; + } smp; + + struct PPU1 { + unsigned version; + } ppu1; + + struct PPU2 { + unsigned version; + } ppu2; + + struct SuperFX { + unsigned speed; + } superfx; + + Configuration(); +}; + +extern Configuration config; diff --git a/libsnes/snes/controller/controller.cpp b/libsnes/snes/controller/controller.cpp new file mode 100644 index 0000000..539e8c7 --- /dev/null +++ b/libsnes/snes/controller/controller.cpp @@ -0,0 +1,55 @@ +#include + +#define CONTROLLER_CPP +namespace SNES { + +#include "gamepad/gamepad.cpp" +#include "multitap/multitap.cpp" +#include "mouse/mouse.cpp" +#include "superscope/superscope.cpp" +#include "justifier/justifier.cpp" +#include "serial/serial.cpp" + +void Controller::Enter() { + if(co_active() == input.port1->thread) input.port1->enter(); + if(co_active() == input.port2->thread) input.port2->enter(); +} + +void Controller::enter() { + while(true) step(1); +} + +void Controller::step(unsigned clocks) { + clock += clocks * (uint64)cpu.frequency; + synchronize_cpu(); +} + +void Controller::synchronize_cpu() { + if(CPU::Threaded == true) { + if(clock >= 0 && scheduler.sync.i != Scheduler::SynchronizeMode::All) co_switch(cpu.thread); + } else { + while(clock >= 0) cpu.enter(); + } +} + +bool Controller::iobit() { + switch(port ? 1 : 0) { + case Controller::Port1: return cpu.pio() & 0x40; + case Controller::Port2: return cpu.pio() & 0x80; + } + + return false; +} + +void Controller::iobit(bool data) { + switch(port ? 1 : 0) { + case Controller::Port1: bus.write(0x4201, (cpu.pio() & ~0x40) | (data << 6)); break; + case Controller::Port2: bus.write(0x4201, (cpu.pio() & ~0x80) | (data << 7)); break; + } +} + +Controller::Controller(bool port) : port(port) { + if(!thread) create(Controller::Enter, 1); +} + +} diff --git a/libsnes/snes/controller/controller.hpp b/libsnes/snes/controller/controller.hpp new file mode 100644 index 0000000..6a2512a --- /dev/null +++ b/libsnes/snes/controller/controller.hpp @@ -0,0 +1,36 @@ +// SNES controller port pinout: +// ------------------------------- +// | (1) (2) (3) (4) | (5) (6) (7) ) +// ------------------------------- +// pin name port1 port2 +// 1: +5v +// 2: clock $4016 read $4017 read +// 3: latch $4016.d0 write $4016.d0 write +// 4: data1 $4016.d0 read $4017.d0 read +// 5: data2 $4016.d1 read $4017.d1 read +// 6: iobit $4201.d6 write; $4213.d6 read $4201.d7 write; $4213.d7 read +// 7: gnd + +struct Controller : Processor { + enum { Port1 = 0, Port2 = 1 }; + enum { SaveSize = 16 }; + const bool port; + + static void Enter(); + virtual void enter(); + void step(unsigned clocks); + void synchronize_cpu(); + + bool iobit(); + void iobit(bool data); + virtual uint2 data() { return 0; } + virtual void latch(bool data) {} + Controller(bool port); +}; + +#include "gamepad/gamepad.hpp" +#include "multitap/multitap.hpp" +#include "mouse/mouse.hpp" +#include "superscope/superscope.hpp" +#include "justifier/justifier.hpp" +#include "serial/serial.hpp" diff --git a/libsnes/snes/controller/gamepad/gamepad.cpp b/libsnes/snes/controller/gamepad/gamepad.cpp new file mode 100644 index 0000000..47f64bb --- /dev/null +++ b/libsnes/snes/controller/gamepad/gamepad.cpp @@ -0,0 +1,35 @@ +#ifdef CONTROLLER_CPP + +uint2 Gamepad::data() { + if(counter >= 16) return 1; + uint2 result = interface->inputPoll(port, Input::Device::Joypad, 0, counter); + if(latched == 0) counter++; + return result; +} + +void Gamepad::latch(bool data) { + if(latched == data) return; + latched = data; + counter = 0; +} + +void Gamepad::serialize(serializer& s) { + Processor::serialize(s); + //Save block. + uint8 block[Controller::SaveSize] = {0}; + block[0] = latched ? 1 : 0; + block[1] = counter; + s.array(block, Controller::SaveSize); + if(s.mode() == nall::serializer::Load) { + latched = (block[0] != 0); + counter = block[1]; + } +} + + +Gamepad::Gamepad(bool port) : Controller(port) { + latched = 0; + counter = 0; +} + +#endif diff --git a/libsnes/snes/controller/gamepad/gamepad.hpp b/libsnes/snes/controller/gamepad/gamepad.hpp new file mode 100644 index 0000000..444da61 --- /dev/null +++ b/libsnes/snes/controller/gamepad/gamepad.hpp @@ -0,0 +1,10 @@ +struct Gamepad : Controller { + uint2 data(); + void latch(bool data); + Gamepad(bool port); + + void serialize(serializer &s); +private: + bool latched; + unsigned counter; +}; diff --git a/libsnes/snes/controller/justifier/justifier.cpp b/libsnes/snes/controller/justifier/justifier.cpp new file mode 100644 index 0000000..93460bf --- /dev/null +++ b/libsnes/snes/controller/justifier/justifier.cpp @@ -0,0 +1,130 @@ +#ifdef CONTROLLER_CPP + +void Justifier::enter() { + unsigned prev = 0; + while(true) { + unsigned next = cpu.vcounter() * 1364 + cpu.hcounter(); + + signed x = (active == 0 ? player1.x : player2.x), y = (active == 0 ? player1.y : player2.y); + bool offscreen = (x < 0 || y < 0 || x >= 256 || y >= (ppu.overscan() ? 240 : 225)); + + if(offscreen == false) { + unsigned target = y * 1364 + (x + 24) * 4; + if(next >= target && prev < target) { + //CRT raster detected, toggle iobit to latch counters + iobit(0); + iobit(1); + } + } + + if(next < prev) { + int nx1 = interface->inputPoll(port, Input::Device::Justifier, 0, (unsigned)Input::JustifierID::X); + int ny1 = interface->inputPoll(port, Input::Device::Justifier, 0, (unsigned)Input::JustifierID::Y); + nx1 += player1.x; + ny1 += player1.y; + player1.x = max(-16, min(256 + 16, nx1)); + player1.y = max(-16, min(240 + 16, ny1)); + } + + if(next < prev && chained) { + int nx2 = interface->inputPoll(port, Input::Device::Justifiers, 1, (unsigned)Input::JustifierID::X); + int ny2 = interface->inputPoll(port, Input::Device::Justifiers, 1, (unsigned)Input::JustifierID::Y); + nx2 += player2.x; + ny2 += player2.y; + player2.x = max(-16, min(256 + 16, nx2)); + player2.y = max(-16, min(240 + 16, ny2)); + } + + prev = next; + step(2); + } +} + +uint2 Justifier::data() { + if(counter >= 32) return 1; + + if(counter == 0) { + player1.trigger = interface->inputPoll(port, Input::Device::Justifier, 0, (unsigned)Input::JustifierID::Trigger); + player1.start = interface->inputPoll(port, Input::Device::Justifier, 0, (unsigned)Input::JustifierID::Start); + } + + if(counter == 0 && chained) { + player2.trigger = interface->inputPoll(port, Input::Device::Justifiers, 1, (unsigned)Input::JustifierID::Trigger); + player2.start = interface->inputPoll(port, Input::Device::Justifiers, 1, (unsigned)Input::JustifierID::Start); + } + + switch(counter++) { + case 0: return 0; + case 1: return 0; + case 2: return 0; + case 3: return 0; + case 4: return 0; + case 5: return 0; + case 6: return 0; + case 7: return 0; + case 8: return 0; + case 9: return 0; + case 10: return 0; + case 11: return 0; + + case 12: return 1; //signature + case 13: return 1; // || + case 14: return 1; // || + case 15: return 0; // || + + case 16: return 0; + case 17: return 1; + case 18: return 0; + case 19: return 1; + case 20: return 0; + case 21: return 1; + case 22: return 0; + case 23: return 1; + + case 24: return player1.trigger; + case 25: return player2.trigger; + case 26: return player1.start; + case 27: return player2.start; + case 28: return active; + + case 29: return 0; + case 30: return 0; + case 31: return 0; + } + + return 0; +} + +void Justifier::latch(bool data) { + if(latched == data) return; + latched = data; + counter = 0; + if(latched == 0) active = !active; //toggle between both controllers, even when unchained +} + +Justifier::Justifier(bool port, bool chained) : Controller(port), chained(chained) { + create(Controller::Enter, 21477272); + latched = 0; + counter = 0; + active = 0; + + player1.x = 256 / 2; + player1.y = 240 / 2; + player1.trigger = false; + player2.start = false; + + player2.x = 256 / 2; + player2.y = 240 / 2; + player2.trigger = false; + player2.start = false; + + if(chained == false) { + player2.x = -1; + player2.y = -1; + } else { + player1.x -= 16; + player2.x += 16; + } +} + +#endif diff --git a/libsnes/snes/controller/justifier/justifier.hpp b/libsnes/snes/controller/justifier/justifier.hpp new file mode 100644 index 0000000..f927acf --- /dev/null +++ b/libsnes/snes/controller/justifier/justifier.hpp @@ -0,0 +1,17 @@ +struct Justifier : Controller { + void enter(); + uint2 data(); + void latch(bool data); + Justifier(bool port, bool chained); + +//private: + const bool chained; //true if the second justifier is attached to the first + bool latched; + unsigned counter; + + bool active; + struct Player { + signed x, y; + bool trigger, start; + } player1, player2; +}; diff --git a/libsnes/snes/controller/mouse/mouse.cpp b/libsnes/snes/controller/mouse/mouse.cpp new file mode 100644 index 0000000..51ef568 --- /dev/null +++ b/libsnes/snes/controller/mouse/mouse.cpp @@ -0,0 +1,82 @@ +#ifdef CONTROLLER_CPP + +uint2 Mouse::data() { + if(counter >= 32) return 1; + + int position_x = interface->inputPoll(port, Input::Device::Mouse, 0, (unsigned)Input::MouseID::X); //-n = left, 0 = center, +n = right + int position_y = interface->inputPoll(port, Input::Device::Mouse, 0, (unsigned)Input::MouseID::Y); //-n = up, 0 = center, +n = down + + bool direction_x = position_x < 0; //0 = right, 1 = left + bool direction_y = position_y < 0; //0 = down, 1 = up + + if(position_x < 0) position_x = -position_x; //abs(position_x) + if(position_y < 0) position_y = -position_y; //abs(position_y) + + position_x = min(127, position_x); //range = 0 - 127 + position_y = min(127, position_y); + + switch(counter++) { default: + case 0: return 0; + case 1: return 0; + case 2: return 0; + case 3: return 0; + case 4: return 0; + case 5: return 0; + case 6: return 0; + case 7: return 0; + + case 8: return interface->inputPoll(port, Input::Device::Mouse, 0, (unsigned)Input::MouseID::Right); + case 9: return interface->inputPoll(port, Input::Device::Mouse, 0, (unsigned)Input::MouseID::Left); + case 10: return 0; //speed (0 = slow, 1 = normal, 2 = fast, 3 = unused) + case 11: return 0; // || + + case 12: return 0; //signature + case 13: return 0; // || + case 14: return 0; // || + case 15: return 1; // || + + case 16: return (direction_y); + case 17: return (position_y >> 6) & 1; + case 18: return (position_y >> 5) & 1; + case 19: return (position_y >> 4) & 1; + case 20: return (position_y >> 3) & 1; + case 21: return (position_y >> 2) & 1; + case 22: return (position_y >> 1) & 1; + case 23: return (position_y >> 0) & 1; + + case 24: return (direction_x); + case 25: return (position_x >> 6) & 1; + case 26: return (position_x >> 5) & 1; + case 27: return (position_x >> 4) & 1; + case 28: return (position_x >> 3) & 1; + case 29: return (position_x >> 2) & 1; + case 30: return (position_x >> 1) & 1; + case 31: return (position_x >> 0) & 1; + } +} + +void Mouse::latch(bool data) { + if(latched == data) return; + latched = data; + counter = 0; +} + +void Mouse::serialize(serializer& s) { + Processor::serialize(s); + //Save block. + uint8 block[Controller::SaveSize] = {0}; + block[0] = latched ? 1 : 0; + block[1] = counter; + s.array(block, Controller::SaveSize); + if(s.mode() == nall::serializer::Load) { + latched = (block[0] != 0); + counter = block[1]; + } +} + +Mouse::Mouse(bool port) : Controller(port) { + latched = 0; + counter = 0; +} + +#endif diff --git a/libsnes/snes/controller/mouse/mouse.hpp b/libsnes/snes/controller/mouse/mouse.hpp new file mode 100644 index 0000000..ae53f7f --- /dev/null +++ b/libsnes/snes/controller/mouse/mouse.hpp @@ -0,0 +1,10 @@ +struct Mouse : Controller { + uint2 data(); + void latch(bool data); + Mouse(bool port); + + void serialize(serializer &s); +private: + bool latched; + unsigned counter; +}; diff --git a/libsnes/snes/controller/multitap/multitap.cpp b/libsnes/snes/controller/multitap/multitap.cpp new file mode 100644 index 0000000..a13ba89 --- /dev/null +++ b/libsnes/snes/controller/multitap/multitap.cpp @@ -0,0 +1,54 @@ +#ifdef CONTROLLER_CPP + +uint2 Multitap::data() { + if(latched) return 2; //multitap detection + unsigned index, port1, port2; + + if(iobit()) { + index = counter1; + if(index >= 16) return 3; + counter1++; + port1 = 0; //controller 1 + port2 = 1; //controller 2 + } else { + index = counter2; + if(index >= 16) return 3; + counter2++; + port1 = 2; //controller 3 + port2 = 3; //controller 4 + } + + bool data1 = interface->inputPoll(port, Input::Device::Multitap, port1, index); + bool data2 = interface->inputPoll(port, Input::Device::Multitap, port2, index); + return (data2 << 1) | (data1 << 0); +} + +void Multitap::latch(bool data) { + if(latched == data) return; + latched = data; + counter1 = 0; + counter2 = 0; +} + +void Multitap::serialize(serializer& s) { + Processor::serialize(s); + //Save block. + unsigned char block[Controller::SaveSize] = {0}; + block[0] = latched ? 1 : 0; + block[1] = counter1; + block[2] = counter2; + s.array(block, Controller::SaveSize); + if(s.mode() == nall::serializer::Load) { + latched = (block[0] != 0); + counter1 = block[1]; + counter2 = block[2]; + } +} + +Multitap::Multitap(bool port) : Controller(port) { + latched = 0; + counter1 = 0; + counter2 = 0; +} + +#endif diff --git a/libsnes/snes/controller/multitap/multitap.hpp b/libsnes/snes/controller/multitap/multitap.hpp new file mode 100644 index 0000000..e219c96 --- /dev/null +++ b/libsnes/snes/controller/multitap/multitap.hpp @@ -0,0 +1,11 @@ +struct Multitap : Controller { + uint2 data(); + void latch(bool data); + Multitap(bool port); + + void serialize(serializer &s); +private: + bool latched; + unsigned counter1; + unsigned counter2; +}; diff --git a/libsnes/snes/controller/serial/serial.cpp b/libsnes/snes/controller/serial/serial.cpp new file mode 100644 index 0000000..fc8e8bb --- /dev/null +++ b/libsnes/snes/controller/serial/serial.cpp @@ -0,0 +1,148 @@ +#ifdef CONTROLLER_CPP + +//Serial communications cable emulation: +//The SNES controller ports can be used for bi-directional serial communication +//when wired to a specialized controller. This class implements said controller, +//for the primary purpose of testing code outside of real hardware. + +//The basic idea is to wire the SNES controller pins to a UART, such as +//the MAX232N; or a serial->USB cable, such as the FTDI TTL-232R-5V. + +//Connection Diagram: +//[SNES] [UART] [Purpose] +// Latch RXD Data transfer +// Data1 TXD Data transfer +// Data2 RTS Flow control (optional) +// IOBit CTS Flow control (optional) +// GND GND Circuit completion + +//The SNES software program will have to use specially timed code to send and +//receive data at a specific baud-rate; whereas the PC handles timing via the +//UART. + +//The emulator implementation is designed so that the same PC-side program can +//be used both under emulation and on real hardware. It does this by linking to +//a dynamic library for timing, read and write operations. This library is +//responsible for setting both the baud-rate and flow control setting. The +//SNES-side software program must know about and respect the library setting. + +static void snesserial_tick(unsigned clocks); +static uint8 snesserial_read(); +static void snesserial_write(uint8 data); + +void Serial::enter() { + if(enable == false) while(true) step(1); //fallback, in case library was not found + step(256 * 8); //simulate warm-up delay + if(flowcontrol()) data2 = 1; + main(snesserial_tick, snesserial_read, snesserial_write); //stubs for Serial::step, Serial::read, Serial::write + while(true) step(1); //fallback, in case snesserial_main() returns (it should never do so) +} + +uint8 Serial::read() { + while(latched == 0) step(1); + while(latched == 1) step(1); + step(4); + + uint8 data = 0; + for(unsigned i = 0; i < 8; i++) { + step(8); + data = (latched << 7) | (data >> 1); + } + + return data; +} + +void Serial::write(uint8 data) { + if(flowcontrol()) while(iobit()) step(1); + step(8); + + data1 = 1; + step(8); + + for(unsigned i = 0; i < 8; i++) { + data1 = (data & 1) ^ 1; + data >>= 1; + step(8); + } + + data1 = 0; + step(8); +} + +uint2 Serial::data() { + return (data2 << 1) | (data1 << 0); +} + +void Serial::latch(bool data) { + latched = data; +} + +Serial::Serial(bool port) : Controller(port) { + enable = false; + string basename = interface->path(Cartridge::Slot::Base, ""); + string name = notdir(basename); + string path = dir(basename); + if(open(name, path)) { + baudrate = sym("snesserial_baudrate"); + flowcontrol = sym("snesserial_flowcontrol"); + main = sym("snesserial_main"); + if(baudrate && flowcontrol && main) enable = true; + } + create(Controller::Enter, enable ? baudrate() * 8 : 1); + + latched = false; + data1 = 0; + data2 = 0; +} + +Serial::~Serial() { + if(opened()) close(); +} + +//stubs needed to call into class objects from global function pointers + +static void snesserial_tick(unsigned clocks) { + if(co_active() == input.port1->thread) { + if(dynamic_cast(input.port1)) { + return ((Serial*)input.port1)->step(clocks); + } + } + + if(co_active() == input.port2->thread) { + if(dynamic_cast(input.port2)) { + return ((Serial*)input.port2)->step(clocks); + } + } +} + +static uint8 snesserial_read() { + if(co_active() == input.port1->thread) { + if(dynamic_cast(input.port1)) { + return ((Serial*)input.port1)->read(); + } + } + + if(co_active() == input.port2->thread) { + if(dynamic_cast(input.port2)) { + return ((Serial*)input.port2)->read(); + } + } + + return 0; +} + +static void snesserial_write(uint8 data) { + if(co_active() == input.port1->thread) { + if(dynamic_cast(input.port1)) { + return ((Serial*)input.port1)->write(data); + } + } + + if(co_active() == input.port2->thread) { + if(dynamic_cast(input.port2)) { + return ((Serial*)input.port2)->write(data); + } + } +} + +#endif diff --git a/libsnes/snes/controller/serial/serial.hpp b/libsnes/snes/controller/serial/serial.hpp new file mode 100644 index 0000000..2a68ba5 --- /dev/null +++ b/libsnes/snes/controller/serial/serial.hpp @@ -0,0 +1,19 @@ +struct Serial : Controller, public library { + void enter(); + uint8 read(); + void write(uint8 data); + uint2 data(); + void latch(bool data); + Serial(bool port); + ~Serial(); + +private: + bool enable; + function baudrate; + function flowcontrol; + function main; + + bool latched; + bool data1; + bool data2; +}; diff --git a/libsnes/snes/controller/superscope/superscope.cpp b/libsnes/snes/controller/superscope/superscope.cpp new file mode 100644 index 0000000..3c369e9 --- /dev/null +++ b/libsnes/snes/controller/superscope/superscope.cpp @@ -0,0 +1,155 @@ +#ifdef CONTROLLER_CPP + +//The Super Scope is a light-gun: it detects the CRT beam cannon position, +//and latches the counters by toggling iobit. This only works on controller +//port 2, as iobit there is connected to the PPU H/V counter latch. +//(PIO $4201.d7) + +//It is obviously not possible to perfectly simulate an IR light detecting +//a CRT beam cannon, hence this class will read the PPU raster counters. + +//A Super Scope can still technically be used in port 1, however it would +//require manual polling of PIO ($4201.d6) to determine when iobit was written. +//Note that no commercial game ever utilizes a Super Scope in port 1. + +void SuperScope::enter() { + unsigned prev = 0; + while(true) { + unsigned next = cpu.vcounter() * 1364 + cpu.hcounter(); + + if(offscreen == false) { + unsigned target = y * 1364 + (x + 24) * 4; + if(next >= target && prev < target) { + //CRT raster detected, toggle iobit to latch counters + iobit(0); + iobit(1); + } + } + + if(next < prev) { + //Vcounter wrapped back to zero; update cursor coordinates for start of new frame + int nx = interface->inputPoll(port, Input::Device::SuperScope, 0, (unsigned)Input::SuperScopeID::X); + int ny = interface->inputPoll(port, Input::Device::SuperScope, 0, (unsigned)Input::SuperScopeID::Y); + nx += x; + ny += y; + x = max(-16, min(256 + 16, nx)); + y = max(-16, min(240 + 16, ny)); + offscreen = (x < 0 || y < 0 || x >= 256 || y >= (ppu.overscan() ? 240 : 225)); + } + + prev = next; + step(2); + } +} + +uint2 SuperScope::data() { + if(counter >= 8) return 1; + + if(counter == 0) { + //turbo is a switch; toggle is edge sensitive + bool newturbo = interface->inputPoll(port, Input::Device::SuperScope, 0, (unsigned)Input::SuperScopeID::Turbo); + if(newturbo && !turbo) { + turbo = !turbo; //toggle state + turbolock = true; + } else { + turbolock = false; + } + + //trigger is a button + //if turbo is active, trigger is level sensitive; otherwise, it is edge sensitive + trigger = false; + bool newtrigger = interface->inputPoll(port, Input::Device::SuperScope, 0, (unsigned)Input::SuperScopeID::Trigger); + if(newtrigger && (turbo || !triggerlock)) { + trigger = true; + triggerlock = true; + } else if(!newtrigger) { + triggerlock = false; + } + + //cursor is a button; it is always level sensitive + cursor = interface->inputPoll(port, Input::Device::SuperScope, 0, (unsigned)Input::SuperScopeID::Cursor); + + //pause is a button; it is always edge sensitive + pause = false; + bool newpause = interface->inputPoll(port, Input::Device::SuperScope, 0, (unsigned)Input::SuperScopeID::Pause); + if(newpause && !pauselock) { + pause = true; + pauselock = true; + } else if(!newpause) { + pauselock = false; + } + + offscreen = (x < 0 || y < 0 || x >= 256 || y >= (ppu.overscan() ? 240 : 225)); + } + + switch(counter++) { + case 0: return offscreen ? 0 : trigger; + case 1: return cursor; + case 2: return turbo; + case 3: return pause; + case 4: return 0; + case 5: return 0; + case 6: return offscreen; + case 7: return 0; //noise (1 = yes) + } + + return 0; +} + +void SuperScope::latch(bool data) { + if(latched == data) return; + latched = data; + counter = 0; +} + +void SuperScope::serialize(serializer& s) { + Processor::serialize(s); + //Save block. + uint8 block[Controller::SaveSize] = {0}; + block[0] = latched ? 1 : 0; + block[1] = counter; + block[2] = trigger ? 1 : 0; + block[3] = cursor ? 1 : 0; + block[4] = turbo ? 1 : 0; + block[5] = pause ? 1 : 0; + block[6] = offscreen ? 1 : 0; + block[7] = (uint16)x >> 8; + block[8] = (uint16)x; + block[9] = (uint16)y >> 8; + block[10] = (uint16)y; + + s.array(block, Controller::SaveSize); + if(s.mode() == nall::serializer::Load) { + latched = (block[0] != 0); + counter = block[1]; + trigger = (block[2] != 0); + cursor = (block[3] != 0); + turbo = (block[4] != 0); + pause = (block[5] != 0); + offscreen = (block[6] != 0); + x = (int16)(((uint16)block[7] << 8) | (uint16)block[8]); + y = (int16)(((uint16)block[9] << 8) | (uint16)block[10]); + } +} + +SuperScope::SuperScope(bool port) : Controller(port) { + create(Controller::Enter, 21477272); + latched = 0; + counter = 0; + + //center cursor onscreen + x = 256 / 2; + y = 240 / 2; + + trigger = false; + cursor = false; + turbo = false; + pause = false; + offscreen = false; + + turbolock = false; + triggerlock = false; + pauselock = false; +} + +#endif diff --git a/libsnes/snes/controller/superscope/superscope.hpp b/libsnes/snes/controller/superscope/superscope.hpp new file mode 100644 index 0000000..6db6a32 --- /dev/null +++ b/libsnes/snes/controller/superscope/superscope.hpp @@ -0,0 +1,23 @@ +struct SuperScope : Controller { + void enter(); + uint2 data(); + void latch(bool data); + void serialize(serializer &s); + SuperScope(bool port); + +//private: + bool latched; + unsigned counter; + + signed x, y; + + bool trigger; + bool cursor; + bool turbo; + bool pause; + bool offscreen; + + bool turbolock; + bool triggerlock; + bool pauselock; +}; diff --git a/libsnes/snes/cpu/core/algorithms.cpp b/libsnes/snes/cpu/core/algorithms.cpp new file mode 100644 index 0000000..9b6ba03 --- /dev/null +++ b/libsnes/snes/cpu/core/algorithms.cpp @@ -0,0 +1,331 @@ +#ifdef CPUCORE_CPP + +inline void CPUcore::op_adc_b() { + int result; + + if(!regs.p.d) { + result = regs.a.l + rd.l + regs.p.c; + } else { + result = (regs.a.l & 0x0f) + (rd.l & 0x0f) + (regs.p.c << 0); + if(result > 0x09) result += 0x06; + regs.p.c = result > 0x0f; + result = (regs.a.l & 0xf0) + (rd.l & 0xf0) + (regs.p.c << 4) + (result & 0x0f); + } + + regs.p.v = ~(regs.a.l ^ rd.l) & (regs.a.l ^ result) & 0x80; + if(regs.p.d && result > 0x9f) result += 0x60; + regs.p.c = result > 0xff; + regs.p.n = result & 0x80; + regs.p.z = (uint8_t)result == 0; + + regs.a.l = result; +} + +inline void CPUcore::op_adc_w() { + int result; + + if(!regs.p.d) { + result = regs.a.w + rd.w + regs.p.c; + } else { + result = (regs.a.w & 0x000f) + (rd.w & 0x000f) + (regs.p.c << 0); + if(result > 0x0009) result += 0x0006; + regs.p.c = result > 0x000f; + result = (regs.a.w & 0x00f0) + (rd.w & 0x00f0) + (regs.p.c << 4) + (result & 0x000f); + if(result > 0x009f) result += 0x0060; + regs.p.c = result > 0x00ff; + result = (regs.a.w & 0x0f00) + (rd.w & 0x0f00) + (regs.p.c << 8) + (result & 0x00ff); + if(result > 0x09ff) result += 0x0600; + regs.p.c = result > 0x0fff; + result = (regs.a.w & 0xf000) + (rd.w & 0xf000) + (regs.p.c << 12) + (result & 0x0fff); + } + + regs.p.v = ~(regs.a.w ^ rd.w) & (regs.a.w ^ result) & 0x8000; + if(regs.p.d && result > 0x9fff) result += 0x6000; + regs.p.c = result > 0xffff; + regs.p.n = result & 0x8000; + regs.p.z = (uint16_t)result == 0; + + regs.a.w = result; +} + +inline void CPUcore::op_and_b() { + regs.a.l &= rd.l; + regs.p.n = regs.a.l & 0x80; + regs.p.z = regs.a.l == 0; +} + +inline void CPUcore::op_and_w() { + regs.a.w &= rd.w; + regs.p.n = regs.a.w & 0x8000; + regs.p.z = regs.a.w == 0; +} + +inline void CPUcore::op_bit_b() { + regs.p.n = rd.l & 0x80; + regs.p.v = rd.l & 0x40; + regs.p.z = (rd.l & regs.a.l) == 0; +} + +inline void CPUcore::op_bit_w() { + regs.p.n = rd.w & 0x8000; + regs.p.v = rd.w & 0x4000; + regs.p.z = (rd.w & regs.a.w) == 0; +} + +inline void CPUcore::op_cmp_b() { + int r = regs.a.l - rd.l; + regs.p.n = r & 0x80; + regs.p.z = (uint8)r == 0; + regs.p.c = r >= 0; +} + +inline void CPUcore::op_cmp_w() { + int r = regs.a.w - rd.w; + regs.p.n = r & 0x8000; + regs.p.z = (uint16)r == 0; + regs.p.c = r >= 0; +} + +inline void CPUcore::op_cpx_b() { + int r = regs.x.l - rd.l; + regs.p.n = r & 0x80; + regs.p.z = (uint8)r == 0; + regs.p.c = r >= 0; +} + +inline void CPUcore::op_cpx_w() { + int r = regs.x.w - rd.w; + regs.p.n = r & 0x8000; + regs.p.z = (uint16)r == 0; + regs.p.c = r >= 0; +} + +inline void CPUcore::op_cpy_b() { + int r = regs.y.l - rd.l; + regs.p.n = r & 0x80; + regs.p.z = (uint8)r == 0; + regs.p.c = r >= 0; +} + +inline void CPUcore::op_cpy_w() { + int r = regs.y.w - rd.w; + regs.p.n = r & 0x8000; + regs.p.z = (uint16)r == 0; + regs.p.c = r >= 0; +} + +inline void CPUcore::op_eor_b() { + regs.a.l ^= rd.l; + regs.p.n = regs.a.l & 0x80; + regs.p.z = regs.a.l == 0; +} + +inline void CPUcore::op_eor_w() { + regs.a.w ^= rd.w; + regs.p.n = regs.a.w & 0x8000; + regs.p.z = regs.a.w == 0; +} + +inline void CPUcore::op_lda_b() { + regs.a.l = rd.l; + regs.p.n = regs.a.l & 0x80; + regs.p.z = regs.a.l == 0; +} + +inline void CPUcore::op_lda_w() { + regs.a.w = rd.w; + regs.p.n = regs.a.w & 0x8000; + regs.p.z = regs.a.w == 0; +} + +inline void CPUcore::op_ldx_b() { + regs.x.l = rd.l; + regs.p.n = regs.x.l & 0x80; + regs.p.z = regs.x.l == 0; +} + +inline void CPUcore::op_ldx_w() { + regs.x.w = rd.w; + regs.p.n = regs.x.w & 0x8000; + regs.p.z = regs.x.w == 0; +} + +inline void CPUcore::op_ldy_b() { + regs.y.l = rd.l; + regs.p.n = regs.y.l & 0x80; + regs.p.z = regs.y.l == 0; +} + +inline void CPUcore::op_ldy_w() { + regs.y.w = rd.w; + regs.p.n = regs.y.w & 0x8000; + regs.p.z = regs.y.w == 0; +} + +inline void CPUcore::op_ora_b() { + regs.a.l |= rd.l; + regs.p.n = regs.a.l & 0x80; + regs.p.z = regs.a.l == 0; +} + +inline void CPUcore::op_ora_w() { + regs.a.w |= rd.w; + regs.p.n = regs.a.w & 0x8000; + regs.p.z = regs.a.w == 0; +} + +inline void CPUcore::op_sbc_b() { + int result; + rd.l ^= 0xff; + + if(!regs.p.d) { + result = regs.a.l + rd.l + regs.p.c; + } else { + result = (regs.a.l & 0x0f) + (rd.l & 0x0f) + (regs.p.c << 0); + if(result <= 0x0f) result -= 0x06; + regs.p.c = result > 0x0f; + result = (regs.a.l & 0xf0) + (rd.l & 0xf0) + (regs.p.c << 4) + (result & 0x0f); + } + + regs.p.v = ~(regs.a.l ^ rd.l) & (regs.a.l ^ result) & 0x80; + if(regs.p.d && result <= 0xff) result -= 0x60; + regs.p.c = result > 0xff; + regs.p.n = result & 0x80; + regs.p.z = (uint8_t)result == 0; + + regs.a.l = result; +} + +inline void CPUcore::op_sbc_w() { + int result; + rd.w ^= 0xffff; + + if(!regs.p.d) { + result = regs.a.w + rd.w + regs.p.c; + } else { + result = (regs.a.w & 0x000f) + (rd.w & 0x000f) + (regs.p.c << 0); + if(result <= 0x000f) result -= 0x0006; + regs.p.c = result > 0x000f; + result = (regs.a.w & 0x00f0) + (rd.w & 0x00f0) + (regs.p.c << 4) + (result & 0x000f); + if(result <= 0x00ff) result -= 0x0060; + regs.p.c = result > 0x00ff; + result = (regs.a.w & 0x0f00) + (rd.w & 0x0f00) + (regs.p.c << 8) + (result & 0x00ff); + if(result <= 0x0fff) result -= 0x0600; + regs.p.c = result > 0x0fff; + result = (regs.a.w & 0xf000) + (rd.w & 0xf000) + (regs.p.c << 12) + (result & 0x0fff); + } + + regs.p.v = ~(regs.a.w ^ rd.w) & (regs.a.w ^ result) & 0x8000; + if(regs.p.d && result <= 0xffff) result -= 0x6000; + regs.p.c = result > 0xffff; + regs.p.n = result & 0x8000; + regs.p.z = (uint16_t)result == 0; + + regs.a.w = result; +} + +inline void CPUcore::op_inc_b() { + rd.l++; + regs.p.n = rd.l & 0x80; + regs.p.z = rd.l == 0; +} + +inline void CPUcore::op_inc_w() { + rd.w++; + regs.p.n = rd.w & 0x8000; + regs.p.z = rd.w == 0; +} + +inline void CPUcore::op_dec_b() { + rd.l--; + regs.p.n = rd.l & 0x80; + regs.p.z = rd.l == 0; +} + +inline void CPUcore::op_dec_w() { + rd.w--; + regs.p.n = rd.w & 0x8000; + regs.p.z = rd.w == 0; +} + +inline void CPUcore::op_asl_b() { + regs.p.c = rd.l & 0x80; + rd.l <<= 1; + regs.p.n = rd.l & 0x80; + regs.p.z = rd.l == 0; +} + +inline void CPUcore::op_asl_w() { + regs.p.c = rd.w & 0x8000; + rd.w <<= 1; + regs.p.n = rd.w & 0x8000; + regs.p.z = rd.w == 0; +} + +inline void CPUcore::op_lsr_b() { + regs.p.c = rd.l & 1; + rd.l >>= 1; + regs.p.n = rd.l & 0x80; + regs.p.z = rd.l == 0; +} + +inline void CPUcore::op_lsr_w() { + regs.p.c = rd.w & 1; + rd.w >>= 1; + regs.p.n = rd.w & 0x8000; + regs.p.z = rd.w == 0; +} + +inline void CPUcore::op_rol_b() { + unsigned carry = (unsigned)regs.p.c; + regs.p.c = rd.l & 0x80; + rd.l = (rd.l << 1) | carry; + regs.p.n = rd.l & 0x80; + regs.p.z = rd.l == 0; +} + +inline void CPUcore::op_rol_w() { + unsigned carry = (unsigned)regs.p.c; + regs.p.c = rd.w & 0x8000; + rd.w = (rd.w << 1) | carry; + regs.p.n = rd.w & 0x8000; + regs.p.z = rd.w == 0; +} + +inline void CPUcore::op_ror_b() { + unsigned carry = (unsigned)regs.p.c << 7; + regs.p.c = rd.l & 1; + rd.l = carry | (rd.l >> 1); + regs.p.n = rd.l & 0x80; + regs.p.z = rd.l == 0; +} + +inline void CPUcore::op_ror_w() { + unsigned carry = (unsigned)regs.p.c << 15; + regs.p.c = rd.w & 1; + rd.w = carry | (rd.w >> 1); + regs.p.n = rd.w & 0x8000; + regs.p.z = rd.w == 0; +} + +inline void CPUcore::op_trb_b() { + regs.p.z = (rd.l & regs.a.l) == 0; + rd.l &= ~regs.a.l; +} + +inline void CPUcore::op_trb_w() { + regs.p.z = (rd.w & regs.a.w) == 0; + rd.w &= ~regs.a.w; +} + +inline void CPUcore::op_tsb_b() { + regs.p.z = (rd.l & regs.a.l) == 0; + rd.l |= regs.a.l; +} + +inline void CPUcore::op_tsb_w() { + regs.p.z = (rd.w & regs.a.w) == 0; + rd.w |= regs.a.w; +} + +#endif diff --git a/libsnes/snes/cpu/core/core.cpp b/libsnes/snes/cpu/core/core.cpp new file mode 100644 index 0000000..aaa286c --- /dev/null +++ b/libsnes/snes/cpu/core/core.cpp @@ -0,0 +1,88 @@ +#include + +#define CPUCORE_CPP +namespace SNES { + +#include "serialization.cpp" +#include "algorithms.cpp" + +#define L last_cycle(); +#define A 0 +#define X 1 +#define Y 2 +#define Z 3 +#define S 4 +#define D 5 +#define call(op) (this->*op)() + +#include "opcode_read.cpp" +#include "opcode_write.cpp" +#include "opcode_rmw.cpp" +#include "opcode_pc.cpp" +#include "opcode_misc.cpp" +#include "table.cpp" + +#undef L +#undef A +#undef X +#undef Y +#undef Z +#undef S +#undef D +#undef call + +//immediate, 2-cycle opcodes with I/O cycle will become bus read +//when an IRQ is to be triggered immediately after opcode completion. +//this affects the following opcodes: +// clc, cld, cli, clv, sec, sed, sei, +// tax, tay, txa, txy, tya, tyx, +// tcd, tcs, tdc, tsc, tsx, txs, +// inc, inx, iny, dec, dex, dey, +// asl, lsr, rol, ror, nop, xce. +alwaysinline void CPUcore::op_io_irq() { + if(interrupt_pending()) { + //modify I/O cycle to bus read cycle, do not increment PC + op_read(regs.pc.d); + } else { + op_io(); + } +} + +alwaysinline void CPUcore::op_io_cond2() { + if(regs.d.l != 0x00) { + op_io(); + } +} + +alwaysinline void CPUcore::op_io_cond4(uint16 x, uint16 y) { + if(!regs.p.x || (x & 0xff00) != (y & 0xff00)) { + op_io(); + } +} + +alwaysinline void CPUcore::op_io_cond6(uint16 addr) { + if(regs.e && (regs.pc.w & 0xff00) != (addr & 0xff00)) { + op_io(); + } +} + +void CPUcore::op_irq() { + op_read(regs.pc.d); + op_io(); + if(!regs.e) op_writestack(regs.pc.b); + op_writestack(regs.pc.h); + op_writestack(regs.pc.l); + op_writestack(regs.e ? (regs.p & ~0x10) : regs.p); + rd.l = op_read(regs.vector + 0); + regs.pc.b = 0x00; + regs.p.i = 1; + regs.p.d = 0; + rd.h = op_read(regs.vector + 1); + regs.pc.w = rd.w; +} + +CPUcore::CPUcore() { + initialize_opcode_table(); +} + +} diff --git a/libsnes/snes/cpu/core/core.hpp b/libsnes/snes/cpu/core/core.hpp new file mode 100644 index 0000000..a5daef2 --- /dev/null +++ b/libsnes/snes/cpu/core/core.hpp @@ -0,0 +1,216 @@ +struct CPUcore { + #include "registers.hpp" + #include "memory.hpp" + + regs_t regs; + reg24_t aa, rd; + uint8_t sp, dp; + + virtual void op_io() = 0; + virtual uint8_t op_read(uint32_t addr) = 0; + virtual void op_write(uint32_t addr, uint8_t data) = 0; + virtual void last_cycle() = 0; + virtual bool interrupt_pending() = 0; + virtual void op_irq(); + + void op_io_irq(); + void op_io_cond2(); + void op_io_cond4(uint16 x, uint16 y); + void op_io_cond6(uint16 addr); + + void op_adc_b(); + void op_adc_w(); + void op_and_b(); + void op_and_w(); + void op_bit_b(); + void op_bit_w(); + void op_cmp_b(); + void op_cmp_w(); + void op_cpx_b(); + void op_cpx_w(); + void op_cpy_b(); + void op_cpy_w(); + void op_eor_b(); + void op_eor_w(); + void op_lda_b(); + void op_lda_w(); + void op_ldx_b(); + void op_ldx_w(); + void op_ldy_b(); + void op_ldy_w(); + void op_ora_b(); + void op_ora_w(); + void op_sbc_b(); + void op_sbc_w(); + + void op_inc_b(); + void op_inc_w(); + void op_dec_b(); + void op_dec_w(); + void op_asl_b(); + void op_asl_w(); + void op_lsr_b(); + void op_lsr_w(); + void op_rol_b(); + void op_rol_w(); + void op_ror_b(); + void op_ror_w(); + void op_trb_b(); + void op_trb_w(); + void op_tsb_b(); + void op_tsb_w(); + + template void op_read_const_b(); + template void op_read_const_w(); + void op_read_bit_const_b(); + void op_read_bit_const_w(); + template void op_read_addr_b(); + template void op_read_addr_w(); + template void op_read_addrx_b(); + template void op_read_addrx_w(); + template void op_read_addry_b(); + template void op_read_addry_w(); + template void op_read_long_b(); + template void op_read_long_w(); + template void op_read_longx_b(); + template void op_read_longx_w(); + template void op_read_dp_b(); + template void op_read_dp_w(); + template void op_read_dpr_b(); + template void op_read_dpr_w(); + template void op_read_idp_b(); + template void op_read_idp_w(); + template void op_read_idpx_b(); + template void op_read_idpx_w(); + template void op_read_idpy_b(); + template void op_read_idpy_w(); + template void op_read_ildp_b(); + template void op_read_ildp_w(); + template void op_read_ildpy_b(); + template void op_read_ildpy_w(); + template void op_read_sr_b(); + template void op_read_sr_w(); + template void op_read_isry_b(); + template void op_read_isry_w(); + + template void op_write_addr_b(); + template void op_write_addr_w(); + template void op_write_addrr_b(); + template void op_write_addrr_w(); + template void op_write_longr_b(); + template void op_write_longr_w(); + template void op_write_dp_b(); + template void op_write_dp_w(); + template void op_write_dpr_b(); + template void op_write_dpr_w(); + void op_sta_idp_b(); + void op_sta_idp_w(); + void op_sta_ildp_b(); + void op_sta_ildp_w(); + void op_sta_idpx_b(); + void op_sta_idpx_w(); + void op_sta_idpy_b(); + void op_sta_idpy_w(); + void op_sta_ildpy_b(); + void op_sta_ildpy_w(); + void op_sta_sr_b(); + void op_sta_sr_w(); + void op_sta_isry_b(); + void op_sta_isry_w(); + + template void op_adjust_imm_b(); + template void op_adjust_imm_w(); + void op_asl_imm_b(); + void op_asl_imm_w(); + void op_lsr_imm_b(); + void op_lsr_imm_w(); + void op_rol_imm_b(); + void op_rol_imm_w(); + void op_ror_imm_b(); + void op_ror_imm_w(); + template void op_adjust_addr_b(); + template void op_adjust_addr_w(); + template void op_adjust_addrx_b(); + template void op_adjust_addrx_w(); + template void op_adjust_dp_b(); + template void op_adjust_dp_w(); + template void op_adjust_dpx_b(); + template void op_adjust_dpx_w(); + + template void op_branch(); + void op_bra(); + void op_brl(); + void op_jmp_addr(); + void op_jmp_long(); + void op_jmp_iaddr(); + void op_jmp_iaddrx(); + void op_jmp_iladdr(); + void op_jsr_addr(); + void op_jsr_long_e(); + void op_jsr_long_n(); + void op_jsr_iaddrx_e(); + void op_jsr_iaddrx_n(); + void op_rti_e(); + void op_rti_n(); + void op_rts(); + void op_rtl_e(); + void op_rtl_n(); + + void op_nop(); + void op_wdm(); + void op_xba(); + template void op_move_b(); + template void op_move_w(); + template void op_interrupt_e(); + template void op_interrupt_n(); + void op_stp(); + void op_wai(); + void op_xce(); + template void op_flag(); + template void op_pflag_e(); + template void op_pflag_n(); + template void op_transfer_b(); + template void op_transfer_w(); + void op_tcs_e(); + void op_tcs_n(); + void op_tsx_b(); + void op_tsx_w(); + void op_txs_e(); + void op_txs_n(); + template void op_push_b(); + template void op_push_w(); + void op_phd_e(); + void op_phd_n(); + void op_phb(); + void op_phk(); + void op_php(); + template void op_pull_b(); + template void op_pull_w(); + void op_pld_e(); + void op_pld_n(); + void op_plb(); + void op_plp_e(); + void op_plp_n(); + void op_pea_e(); + void op_pea_n(); + void op_pei_e(); + void op_pei_n(); + void op_per_e(); + void op_per_n(); + + void (CPUcore::**opcode_table)(); + void (CPUcore::*op_table[256 * 5])(); + void initialize_opcode_table(); + void update_table(); + + enum { + table_EM = 0, // 8-bit accumulator, 8-bit index (emulation mode) + table_MX = 256, // 8-bit accumulator, 8-bit index + table_Mx = 512, // 8-bit accumulator, 16-bit index + table_mX = 768, //16-bit accumulator, 8-bit index + table_mx = 1024, //16-bit accumulator, 16-bit index + }; + + void core_serialize(serializer&); + CPUcore(); +}; diff --git a/libsnes/snes/cpu/core/memory.hpp b/libsnes/snes/cpu/core/memory.hpp new file mode 100644 index 0000000..4992657 --- /dev/null +++ b/libsnes/snes/cpu/core/memory.hpp @@ -0,0 +1,77 @@ +alwaysinline uint8_t op_readpc() { + return op_read((regs.pc.b << 16) + regs.pc.w++); +} + +alwaysinline uint8_t op_readstack() { + regs.e ? regs.s.l++ : regs.s.w++; + return op_read(regs.s.w); +} + +alwaysinline uint8_t op_readstackn() { + return op_read(++regs.s.w); +} + +alwaysinline uint8_t op_readaddr(uint32_t addr) { + return op_read(addr & 0xffff); +} + +alwaysinline uint8_t op_readlong(uint32_t addr) { + return op_read(addr & 0xffffff); +} + +alwaysinline uint8_t op_readdbr(uint32_t addr) { + return op_read(((regs.db << 16) + addr) & 0xffffff); +} + +alwaysinline uint8_t op_readpbr(uint32_t addr) { + return op_read((regs.pc.b << 16) + (addr & 0xffff)); +} + +alwaysinline uint8_t op_readdp(uint32_t addr) { + if(regs.e && regs.d.l == 0x00) { + return op_read((regs.d & 0xff00) + ((regs.d + (addr & 0xffff)) & 0xff)); + } else { + return op_read((regs.d + (addr & 0xffff)) & 0xffff); + } +} + +alwaysinline uint8_t op_readsp(uint32_t addr) { + return op_read((regs.s + (addr & 0xffff)) & 0xffff); +} + +alwaysinline void op_writestack(uint8_t data) { + op_write(regs.s.w, data); + regs.e ? regs.s.l-- : regs.s.w--; +} + +alwaysinline void op_writestackn(uint8_t data) { + op_write(regs.s.w--, data); +} + +alwaysinline void op_writeaddr(uint32_t addr, uint8_t data) { + op_write(addr & 0xffff, data); +} + +alwaysinline void op_writelong(uint32_t addr, uint8_t data) { + op_write(addr & 0xffffff, data); +} + +alwaysinline void op_writedbr(uint32_t addr, uint8_t data) { + op_write(((regs.db << 16) + addr) & 0xffffff, data); +} + +alwaysinline void op_writepbr(uint32_t addr, uint8_t data) { + op_write((regs.pc.b << 16) + (addr & 0xffff), data); +} + +alwaysinline void op_writedp(uint32_t addr, uint8_t data) { + if(regs.e && regs.d.l == 0x00) { + op_write((regs.d & 0xff00) + ((regs.d + (addr & 0xffff)) & 0xff), data); + } else { + op_write((regs.d + (addr & 0xffff)) & 0xffff, data); + } +} + +alwaysinline void op_writesp(uint32_t addr, uint8_t data) { + op_write((regs.s + (addr & 0xffff)) & 0xffff, data); +} diff --git a/libsnes/snes/cpu/core/opcode_misc.cpp b/libsnes/snes/cpu/core/opcode_misc.cpp new file mode 100644 index 0000000..9ac07ee --- /dev/null +++ b/libsnes/snes/cpu/core/opcode_misc.cpp @@ -0,0 +1,338 @@ +#ifdef CPUCORE_CPP + +void CPUcore::op_nop() { +L op_io_irq(); +} + +void CPUcore::op_wdm() { +L op_readpc(); +} + +void CPUcore::op_xba() { + op_io(); +L op_io(); + regs.a.l ^= regs.a.h; + regs.a.h ^= regs.a.l; + regs.a.l ^= regs.a.h; + regs.p.n = (regs.a.l & 0x80); + regs.p.z = (regs.a.l == 0); +} + +template void CPUcore::op_move_b() { + dp = op_readpc(); + sp = op_readpc(); + regs.db = dp; + rd.l = op_readlong((sp << 16) | regs.x.w); + op_writelong((dp << 16) | regs.y.w, rd.l); + op_io(); + regs.x.l += adjust; + regs.y.l += adjust; +L op_io(); + if(regs.a.w--) regs.pc.w -= 3; +} + +template void CPUcore::op_move_w() { + dp = op_readpc(); + sp = op_readpc(); + regs.db = dp; + rd.l = op_readlong((sp << 16) | regs.x.w); + op_writelong((dp << 16) | regs.y.w, rd.l); + op_io(); + regs.x.w += adjust; + regs.y.w += adjust; +L op_io(); + if(regs.a.w--) regs.pc.w -= 3; +} + +template void CPUcore::op_interrupt_e() { + op_readpc(); + op_writestack(regs.pc.h); + op_writestack(regs.pc.l); + op_writestack(regs.p); + rd.l = op_readlong(vectorE + 0); + regs.pc.b = 0; + regs.p.i = 1; + regs.p.d = 0; +L rd.h = op_readlong(vectorE + 1); + regs.pc.w = rd.w; +} + +template void CPUcore::op_interrupt_n() { + op_readpc(); + op_writestack(regs.pc.b); + op_writestack(regs.pc.h); + op_writestack(regs.pc.l); + op_writestack(regs.p); + rd.l = op_readlong(vectorN + 0); + regs.pc.b = 0x00; + regs.p.i = 1; + regs.p.d = 0; +L rd.h = op_readlong(vectorN + 1); + regs.pc.w = rd.w; +} + +void CPUcore::op_stp() { + while((regs.wai = true)) { +L op_io(); + } +} + +void CPUcore::op_wai() { + regs.wai = true; + while(regs.wai) { +L op_io(); + } + op_io(); +} + +void CPUcore::op_xce() { +L op_io_irq(); + bool carry = regs.p.c; + regs.p.c = regs.e; + regs.e = carry; + if(regs.e) { + regs.p |= 0x30; + regs.s.h = 0x01; + } + if(regs.p.x) { + regs.x.h = 0x00; + regs.y.h = 0x00; + } + update_table(); +} + +template void CPUcore::op_flag() { +L op_io_irq(); + regs.p = (regs.p & ~mask) | value; +} + +template void CPUcore::op_pflag_e() { + rd.l = op_readpc(); +L op_io(); + regs.p = (mode ? regs.p | rd.l : regs.p & ~rd.l); + regs.p |= 0x30; + if(regs.p.x) { + regs.x.h = 0x00; + regs.y.h = 0x00; + } + update_table(); +} + +template void CPUcore::op_pflag_n() { + rd.l = op_readpc(); +L op_io(); + regs.p = (mode ? regs.p | rd.l : regs.p & ~rd.l); + if(regs.p.x) { + regs.x.h = 0x00; + regs.y.h = 0x00; + } + update_table(); +} + +template void CPUcore::op_transfer_b() { +L op_io_irq(); + regs.r[to].l = regs.r[from].l; + regs.p.n = (regs.r[to].l & 0x80); + regs.p.z = (regs.r[to].l == 0); +} + +template void CPUcore::op_transfer_w() { +L op_io_irq(); + regs.r[to].w = regs.r[from].w; + regs.p.n = (regs.r[to].w & 0x8000); + regs.p.z = (regs.r[to].w == 0); +} + +void CPUcore::op_tcs_e() { +L op_io_irq(); + regs.s.l = regs.a.l; +} + +void CPUcore::op_tcs_n() { +L op_io_irq(); + regs.s.w = regs.a.w; +} + +void CPUcore::op_tsx_b() { +L op_io_irq(); + regs.x.l = regs.s.l; + regs.p.n = (regs.x.l & 0x80); + regs.p.z = (regs.x.l == 0); +} + +void CPUcore::op_tsx_w() { +L op_io_irq(); + regs.x.w = regs.s.w; + regs.p.n = (regs.x.w & 0x8000); + regs.p.z = (regs.x.w == 0); +} + +void CPUcore::op_txs_e() { +L op_io_irq(); + regs.s.l = regs.x.l; +} + +void CPUcore::op_txs_n() { +L op_io_irq(); + regs.s.w = regs.x.w; +} + +template void CPUcore::op_push_b() { + op_io(); +L op_writestack(regs.r[n].l); +} + +template void CPUcore::op_push_w() { + op_io(); + op_writestack(regs.r[n].h); +L op_writestack(regs.r[n].l); +} + +void CPUcore::op_phd_e() { + op_io(); + op_writestackn(regs.d.h); +L op_writestackn(regs.d.l); + regs.s.h = 0x01; +} + +void CPUcore::op_phd_n() { + op_io(); + op_writestackn(regs.d.h); +L op_writestackn(regs.d.l); +} + +void CPUcore::op_phb() { + op_io(); +L op_writestack(regs.db); +} + +void CPUcore::op_phk() { + op_io(); +L op_writestack(regs.pc.b); +} + +void CPUcore::op_php() { + op_io(); +L op_writestack(regs.p); +} + +template void CPUcore::op_pull_b() { + op_io(); + op_io(); +L regs.r[n].l = op_readstack(); + regs.p.n = (regs.r[n].l & 0x80); + regs.p.z = (regs.r[n].l == 0); +} + +template void CPUcore::op_pull_w() { + op_io(); + op_io(); + regs.r[n].l = op_readstack(); +L regs.r[n].h = op_readstack(); + regs.p.n = (regs.r[n].w & 0x8000); + regs.p.z = (regs.r[n].w == 0); +} + +void CPUcore::op_pld_e() { + op_io(); + op_io(); + regs.d.l = op_readstackn(); +L regs.d.h = op_readstackn(); + regs.p.n = (regs.d.w & 0x8000); + regs.p.z = (regs.d.w == 0); + regs.s.h = 0x01; +} + +void CPUcore::op_pld_n() { + op_io(); + op_io(); + regs.d.l = op_readstackn(); +L regs.d.h = op_readstackn(); + regs.p.n = (regs.d.w & 0x8000); + regs.p.z = (regs.d.w == 0); +} + +void CPUcore::op_plb() { + op_io(); + op_io(); +L regs.db = op_readstack(); + regs.p.n = (regs.db & 0x80); + regs.p.z = (regs.db == 0); +} + +void CPUcore::op_plp_e() { + op_io(); + op_io(); +L regs.p = op_readstack() | 0x30; + if(regs.p.x) { + regs.x.h = 0x00; + regs.y.h = 0x00; + } + update_table(); +} + +void CPUcore::op_plp_n() { + op_io(); + op_io(); +L regs.p = op_readstack(); + if(regs.p.x) { + regs.x.h = 0x00; + regs.y.h = 0x00; + } + update_table(); +} + +void CPUcore::op_pea_e() { + aa.l = op_readpc(); + aa.h = op_readpc(); + op_writestackn(aa.h); +L op_writestackn(aa.l); + regs.s.h = 0x01; +} + +void CPUcore::op_pea_n() { + aa.l = op_readpc(); + aa.h = op_readpc(); + op_writestackn(aa.h); +L op_writestackn(aa.l); +} + +void CPUcore::op_pei_e() { + dp = op_readpc(); + op_io_cond2(); + aa.l = op_readdp(dp + 0); + aa.h = op_readdp(dp + 1); + op_writestackn(aa.h); +L op_writestackn(aa.l); + regs.s.h = 0x01; +} + +void CPUcore::op_pei_n() { + dp = op_readpc(); + op_io_cond2(); + aa.l = op_readdp(dp + 0); + aa.h = op_readdp(dp + 1); + op_writestackn(aa.h); +L op_writestackn(aa.l); +} + +void CPUcore::op_per_e() { + aa.l = op_readpc(); + aa.h = op_readpc(); + op_io(); + rd.w = regs.pc.d + (int16)aa.w; + op_writestackn(rd.h); +L op_writestackn(rd.l); + regs.s.h = 0x01; +} + +void CPUcore::op_per_n() { + aa.l = op_readpc(); + aa.h = op_readpc(); + op_io(); + rd.w = regs.pc.d + (int16)aa.w; + op_writestackn(rd.h); +L op_writestackn(rd.l); +} + +#endif diff --git a/libsnes/snes/cpu/core/opcode_pc.cpp b/libsnes/snes/cpu/core/opcode_pc.cpp new file mode 100644 index 0000000..3b4543f --- /dev/null +++ b/libsnes/snes/cpu/core/opcode_pc.cpp @@ -0,0 +1,181 @@ +#ifdef CPUCORE_CPP + +template void CPUcore::op_branch() { + if((bool)(regs.p & bit) != val) { +L rd.l = op_readpc(); + } else { + rd.l = op_readpc(); + aa.w = regs.pc.d + (int8)rd.l; + op_io_cond6(aa.w); +L op_io(); + regs.pc.w = aa.w; + } +} + +void CPUcore::op_bra() { + rd.l = op_readpc(); + aa.w = regs.pc.d + (int8)rd.l; + op_io_cond6(aa.w); +L op_io(); + regs.pc.w = aa.w; +} + +void CPUcore::op_brl() { + rd.l = op_readpc(); + rd.h = op_readpc(); +L op_io(); + regs.pc.w = regs.pc.d + (int16)rd.w; +} + +void CPUcore::op_jmp_addr() { + rd.l = op_readpc(); +L rd.h = op_readpc(); + regs.pc.w = rd.w; +} + +void CPUcore::op_jmp_long() { + rd.l = op_readpc(); + rd.h = op_readpc(); +L rd.b = op_readpc(); + regs.pc.d = rd.d & 0xffffff; +} + +void CPUcore::op_jmp_iaddr() { + aa.l = op_readpc(); + aa.h = op_readpc(); + rd.l = op_readaddr(aa.w + 0); +L rd.h = op_readaddr(aa.w + 1); + regs.pc.w = rd.w; +} + +void CPUcore::op_jmp_iaddrx() { + aa.l = op_readpc(); + aa.h = op_readpc(); + op_io(); + rd.l = op_readpbr(aa.w + regs.x.w + 0); +L rd.h = op_readpbr(aa.w + regs.x.w + 1); + regs.pc.w = rd.w; +} + +void CPUcore::op_jmp_iladdr() { + aa.l = op_readpc(); + aa.h = op_readpc(); + rd.l = op_readaddr(aa.w + 0); + rd.h = op_readaddr(aa.w + 1); +L rd.b = op_readaddr(aa.w + 2); + regs.pc.d = rd.d & 0xffffff; +} + +void CPUcore::op_jsr_addr() { + aa.l = op_readpc(); + aa.h = op_readpc(); + op_io(); + regs.pc.w--; + op_writestack(regs.pc.h); +L op_writestack(regs.pc.l); + regs.pc.w = aa.w; +} + +void CPUcore::op_jsr_long_e() { + aa.l = op_readpc(); + aa.h = op_readpc(); + op_writestackn(regs.pc.b); + op_io(); + aa.b = op_readpc(); + regs.pc.w--; + op_writestackn(regs.pc.h); +L op_writestackn(regs.pc.l); + regs.pc.d = aa.d & 0xffffff; + regs.s.h = 0x01; +} + +void CPUcore::op_jsr_long_n() { + aa.l = op_readpc(); + aa.h = op_readpc(); + op_writestackn(regs.pc.b); + op_io(); + aa.b = op_readpc(); + regs.pc.w--; + op_writestackn(regs.pc.h); +L op_writestackn(regs.pc.l); + regs.pc.d = aa.d & 0xffffff; +} + +void CPUcore::op_jsr_iaddrx_e() { + aa.l = op_readpc(); + op_writestackn(regs.pc.h); + op_writestackn(regs.pc.l); + aa.h = op_readpc(); + op_io(); + rd.l = op_readpbr(aa.w + regs.x.w + 0); +L rd.h = op_readpbr(aa.w + regs.x.w + 1); + regs.pc.w = rd.w; + regs.s.h = 0x01; +} + +void CPUcore::op_jsr_iaddrx_n() { + aa.l = op_readpc(); + op_writestackn(regs.pc.h); + op_writestackn(regs.pc.l); + aa.h = op_readpc(); + op_io(); + rd.l = op_readpbr(aa.w + regs.x.w + 0); +L rd.h = op_readpbr(aa.w + regs.x.w + 1); + regs.pc.w = rd.w; +} + +void CPUcore::op_rti_e() { + op_io(); + op_io(); + regs.p = op_readstack() | 0x30; + rd.l = op_readstack(); +L rd.h = op_readstack(); + regs.pc.w = rd.w; +} + +void CPUcore::op_rti_n() { + op_io(); + op_io(); + regs.p = op_readstack(); + if(regs.p.x) { + regs.x.h = 0x00; + regs.y.h = 0x00; + } + rd.l = op_readstack(); + rd.h = op_readstack(); +L rd.b = op_readstack(); + regs.pc.d = rd.d & 0xffffff; + update_table(); +} + +void CPUcore::op_rts() { + op_io(); + op_io(); + rd.l = op_readstack(); + rd.h = op_readstack(); +L op_io(); + regs.pc.w = ++rd.w; +} + +void CPUcore::op_rtl_e() { + op_io(); + op_io(); + rd.l = op_readstackn(); + rd.h = op_readstackn(); +L rd.b = op_readstackn(); + regs.pc.b = rd.b; + regs.pc.w = ++rd.w; + regs.s.h = 0x01; +} + +void CPUcore::op_rtl_n() { + op_io(); + op_io(); + rd.l = op_readstackn(); + rd.h = op_readstackn(); +L rd.b = op_readstackn(); + regs.pc.b = rd.b; + regs.pc.w = ++rd.w; +} + +#endif diff --git a/libsnes/snes/cpu/core/opcode_read.cpp b/libsnes/snes/cpu/core/opcode_read.cpp new file mode 100644 index 0000000..61a7feb --- /dev/null +++ b/libsnes/snes/cpu/core/opcode_read.cpp @@ -0,0 +1,279 @@ +#ifdef CPUCORE_CPP + +template void CPUcore::op_read_const_b() { +L rd.l = op_readpc(); + call(op); +} + +template void CPUcore::op_read_const_w() { + rd.l = op_readpc(); +L rd.h = op_readpc(); + call(op); +} + +void CPUcore::op_read_bit_const_b() { +L rd.l = op_readpc(); + regs.p.z = ((rd.l & regs.a.l) == 0); +} + +void CPUcore::op_read_bit_const_w() { + rd.l = op_readpc(); +L rd.h = op_readpc(); + regs.p.z = ((rd.w & regs.a.w) == 0); +} + +template void CPUcore::op_read_addr_b() { + aa.l = op_readpc(); + aa.h = op_readpc(); +L rd.l = op_readdbr(aa.w); + call(op); +} + +template void CPUcore::op_read_addr_w() { + aa.l = op_readpc(); + aa.h = op_readpc(); + rd.l = op_readdbr(aa.w + 0); +L rd.h = op_readdbr(aa.w + 1); + call(op); +} + +template void CPUcore::op_read_addrx_b() { + aa.l = op_readpc(); + aa.h = op_readpc(); + op_io_cond4(aa.w, aa.w + regs.x.w); +L rd.l = op_readdbr(aa.w + regs.x.w); + call(op); +} + +template void CPUcore::op_read_addrx_w() { + aa.l = op_readpc(); + aa.h = op_readpc(); + op_io_cond4(aa.w, aa.w + regs.x.w); + rd.l = op_readdbr(aa.w + regs.x.w + 0); +L rd.h = op_readdbr(aa.w + regs.x.w + 1); + call(op); +} + +template void CPUcore::op_read_addry_b() { + aa.l = op_readpc(); + aa.h = op_readpc(); + op_io_cond4(aa.w, aa.w + regs.y.w); +L rd.l = op_readdbr(aa.w + regs.y.w); + call(op); +} + +template void CPUcore::op_read_addry_w() { + aa.l = op_readpc(); + aa.h = op_readpc(); + op_io_cond4(aa.w, aa.w + regs.y.w); + rd.l = op_readdbr(aa.w + regs.y.w + 0); +L rd.h = op_readdbr(aa.w + regs.y.w + 1); + call(op); +} + +template void CPUcore::op_read_long_b() { + aa.l = op_readpc(); + aa.h = op_readpc(); + aa.b = op_readpc(); +L rd.l = op_readlong(aa.d); + call(op); +} + +template void CPUcore::op_read_long_w() { + aa.l = op_readpc(); + aa.h = op_readpc(); + aa.b = op_readpc(); + rd.l = op_readlong(aa.d + 0); +L rd.h = op_readlong(aa.d + 1); + call(op); +} + +template void CPUcore::op_read_longx_b() { + aa.l = op_readpc(); + aa.h = op_readpc(); + aa.b = op_readpc(); +L rd.l = op_readlong(aa.d + regs.x.w); + call(op); +} + +template void CPUcore::op_read_longx_w() { + aa.l = op_readpc(); + aa.h = op_readpc(); + aa.b = op_readpc(); + rd.l = op_readlong(aa.d + regs.x.w + 0); +L rd.h = op_readlong(aa.d + regs.x.w + 1); + call(op); +} + +template void CPUcore::op_read_dp_b() { + dp = op_readpc(); + op_io_cond2(); +L rd.l = op_readdp(dp); + call(op); +} + +template void CPUcore::op_read_dp_w() { + dp = op_readpc(); + op_io_cond2(); + rd.l = op_readdp(dp + 0); +L rd.h = op_readdp(dp + 1); + call(op); +} + +template void CPUcore::op_read_dpr_b() { + dp = op_readpc(); + op_io_cond2(); + op_io(); +L rd.l = op_readdp(dp + regs.r[n].w); + call(op); +} + +template void CPUcore::op_read_dpr_w() { + dp = op_readpc(); + op_io_cond2(); + op_io(); + rd.l = op_readdp(dp + regs.r[n].w + 0); +L rd.h = op_readdp(dp + regs.r[n].w + 1); + call(op); +} + +template void CPUcore::op_read_idp_b() { + dp = op_readpc(); + op_io_cond2(); + aa.l = op_readdp(dp + 0); + aa.h = op_readdp(dp + 1); +L rd.l = op_readdbr(aa.w); + call(op); +} + +template void CPUcore::op_read_idp_w() { + dp = op_readpc(); + op_io_cond2(); + aa.l = op_readdp(dp + 0); + aa.h = op_readdp(dp + 1); + rd.l = op_readdbr(aa.w + 0); +L rd.h = op_readdbr(aa.w + 1); + call(op); +} + +template void CPUcore::op_read_idpx_b() { + dp = op_readpc(); + op_io_cond2(); + op_io(); + aa.l = op_readdp(dp + regs.x.w + 0); + aa.h = op_readdp(dp + regs.x.w + 1); +L rd.l = op_readdbr(aa.w); + call(op); +} + +template void CPUcore::op_read_idpx_w() { + dp = op_readpc(); + op_io_cond2(); + op_io(); + aa.l = op_readdp(dp + regs.x.w + 0); + aa.h = op_readdp(dp + regs.x.w + 1); + rd.l = op_readdbr(aa.w + 0); +L rd.h = op_readdbr(aa.w + 1); + call(op); +} + +template void CPUcore::op_read_idpy_b() { + dp = op_readpc(); + op_io_cond2(); + aa.l = op_readdp(dp + 0); + aa.h = op_readdp(dp + 1); + op_io_cond4(aa.w, aa.w + regs.y.w); +L rd.l = op_readdbr(aa.w + regs.y.w); + call(op); +} + +template void CPUcore::op_read_idpy_w() { + dp = op_readpc(); + op_io_cond2(); + aa.l = op_readdp(dp + 0); + aa.h = op_readdp(dp + 1); + op_io_cond4(aa.w, aa.w + regs.y.w); + rd.l = op_readdbr(aa.w + regs.y.w + 0); +L rd.h = op_readdbr(aa.w + regs.y.w + 1); + call(op); +} + +template void CPUcore::op_read_ildp_b() { + dp = op_readpc(); + op_io_cond2(); + aa.l = op_readdp(dp + 0); + aa.h = op_readdp(dp + 1); + aa.b = op_readdp(dp + 2); +L rd.l = op_readlong(aa.d); + call(op); +} + +template void CPUcore::op_read_ildp_w() { + dp = op_readpc(); + op_io_cond2(); + aa.l = op_readdp(dp + 0); + aa.h = op_readdp(dp + 1); + aa.b = op_readdp(dp + 2); + rd.l = op_readlong(aa.d + 0); +L rd.h = op_readlong(aa.d + 1); + call(op); +} + +template void CPUcore::op_read_ildpy_b() { + dp = op_readpc(); + op_io_cond2(); + aa.l = op_readdp(dp + 0); + aa.h = op_readdp(dp + 1); + aa.b = op_readdp(dp + 2); +L rd.l = op_readlong(aa.d + regs.y.w); + call(op); +} + +template void CPUcore::op_read_ildpy_w() { + dp = op_readpc(); + op_io_cond2(); + aa.l = op_readdp(dp + 0); + aa.h = op_readdp(dp + 1); + aa.b = op_readdp(dp + 2); + rd.l = op_readlong(aa.d + regs.y.w + 0); +L rd.h = op_readlong(aa.d + regs.y.w + 1); + call(op); +} + +template void CPUcore::op_read_sr_b() { + sp = op_readpc(); + op_io(); +L rd.l = op_readsp(sp); + call(op); +} + +template void CPUcore::op_read_sr_w() { + sp = op_readpc(); + op_io(); + rd.l = op_readsp(sp + 0); +L rd.h = op_readsp(sp + 1); + call(op); +} + +template void CPUcore::op_read_isry_b() { + sp = op_readpc(); + op_io(); + aa.l = op_readsp(sp + 0); + aa.h = op_readsp(sp + 1); + op_io(); +L rd.l = op_readdbr(aa.w + regs.y.w); + call(op); +} + +template void CPUcore::op_read_isry_w() { + sp = op_readpc(); + op_io(); + aa.l = op_readsp(sp + 0); + aa.h = op_readsp(sp + 1); + op_io(); + rd.l = op_readdbr(aa.w + regs.y.w + 0); +L rd.h = op_readdbr(aa.w + regs.y.w + 1); + call(op); +} + +#endif diff --git a/libsnes/snes/cpu/core/opcode_rmw.cpp b/libsnes/snes/cpu/core/opcode_rmw.cpp new file mode 100644 index 0000000..fed939e --- /dev/null +++ b/libsnes/snes/cpu/core/opcode_rmw.cpp @@ -0,0 +1,169 @@ +#ifdef CPUCORE_CPP + +template void CPUcore::op_adjust_imm_b() { +L op_io_irq(); + regs.r[n].l += adjust; + regs.p.n = (regs.r[n].l & 0x80); + regs.p.z = (regs.r[n].l == 0); +} + +template void CPUcore::op_adjust_imm_w() { +L op_io_irq(); + regs.r[n].w += adjust; + regs.p.n = (regs.r[n].w & 0x8000); + regs.p.z = (regs.r[n].w == 0); +} + +void CPUcore::op_asl_imm_b() { +L op_io_irq(); + regs.p.c = (regs.a.l & 0x80); + regs.a.l <<= 1; + regs.p.n = (regs.a.l & 0x80); + regs.p.z = (regs.a.l == 0); +} + +void CPUcore::op_asl_imm_w() { +L op_io_irq(); + regs.p.c = (regs.a.w & 0x8000); + regs.a.w <<= 1; + regs.p.n = (regs.a.w & 0x8000); + regs.p.z = (regs.a.w == 0); +} + +void CPUcore::op_lsr_imm_b() { +L op_io_irq(); + regs.p.c = (regs.a.l & 0x01); + regs.a.l >>= 1; + regs.p.n = (regs.a.l & 0x80); + regs.p.z = (regs.a.l == 0); +} + +void CPUcore::op_lsr_imm_w() { +L op_io_irq(); + regs.p.c = (regs.a.w & 0x0001); + regs.a.w >>= 1; + regs.p.n = (regs.a.w & 0x8000); + regs.p.z = (regs.a.w == 0); +} + +void CPUcore::op_rol_imm_b() { +L op_io_irq(); + bool carry = regs.p.c; + regs.p.c = (regs.a.l & 0x80); + regs.a.l = (regs.a.l << 1) | carry; + regs.p.n = (regs.a.l & 0x80); + regs.p.z = (regs.a.l == 0); +} + +void CPUcore::op_rol_imm_w() { +L op_io_irq(); + bool carry = regs.p.c; + regs.p.c = (regs.a.w & 0x8000); + regs.a.w = (regs.a.w << 1) | carry; + regs.p.n = (regs.a.w & 0x8000); + regs.p.z = (regs.a.w == 0); +} + +void CPUcore::op_ror_imm_b() { +L op_io_irq(); + bool carry = regs.p.c; + regs.p.c = (regs.a.l & 0x01); + regs.a.l = (carry << 7) | (regs.a.l >> 1); + regs.p.n = (regs.a.l & 0x80); + regs.p.z = (regs.a.l == 0); +} + +void CPUcore::op_ror_imm_w() { +L op_io_irq(); + bool carry = regs.p.c; + regs.p.c = (regs.a.w & 0x0001); + regs.a.w = (carry << 15) | (regs.a.w >> 1); + regs.p.n = (regs.a.w & 0x8000); + regs.p.z = (regs.a.w == 0); +} + +template void CPUcore::op_adjust_addr_b() { + aa.l = op_readpc(); + aa.h = op_readpc(); + rd.l = op_readdbr(aa.w); + op_io(); + call(op); +L op_writedbr(aa.w, rd.l); +} + +template void CPUcore::op_adjust_addr_w() { + aa.l = op_readpc(); + aa.h = op_readpc(); + rd.l = op_readdbr(aa.w + 0); + rd.h = op_readdbr(aa.w + 1); + op_io(); + call(op); + op_writedbr(aa.w + 1, rd.h); +L op_writedbr(aa.w + 0, rd.l); +} + +template void CPUcore::op_adjust_addrx_b() { + aa.l = op_readpc(); + aa.h = op_readpc(); + op_io(); + rd.l = op_readdbr(aa.w + regs.x.w); + op_io(); + call(op); +L op_writedbr(aa.w + regs.x.w, rd.l); +} + +template void CPUcore::op_adjust_addrx_w() { + aa.l = op_readpc(); + aa.h = op_readpc(); + op_io(); + rd.l = op_readdbr(aa.w + regs.x.w + 0); + rd.h = op_readdbr(aa.w + regs.x.w + 1); + op_io(); + call(op); + op_writedbr(aa.w + regs.x.w + 1, rd.h); +L op_writedbr(aa.w + regs.x.w + 0, rd.l); +} + +template void CPUcore::op_adjust_dp_b() { + dp = op_readpc(); + op_io_cond2(); + rd.l = op_readdp(dp); + op_io(); + call(op); +L op_writedp(dp, rd.l); +} + +template void CPUcore::op_adjust_dp_w() { + dp = op_readpc(); + op_io_cond2(); + rd.l = op_readdp(dp + 0); + rd.h = op_readdp(dp + 1); + op_io(); + call(op); + op_writedp(dp + 1, rd.h); +L op_writedp(dp + 0, rd.l); +} + +template void CPUcore::op_adjust_dpx_b() { + dp = op_readpc(); + op_io_cond2(); + op_io(); + rd.l = op_readdp(dp + regs.x.w); + op_io(); + call(op); +L op_writedp(dp + regs.x.w, rd.l); +} + +template void CPUcore::op_adjust_dpx_w() { + dp = op_readpc(); + op_io_cond2(); + op_io(); + rd.l = op_readdp(dp + regs.x.w + 0); + rd.h = op_readdp(dp + regs.x.w + 1); + op_io(); + call(op); + op_writedp(dp + regs.x.w + 1, rd.h); +L op_writedp(dp + regs.x.w + 0, rd.l); +} + +#endif diff --git a/libsnes/snes/cpu/core/opcode_write.cpp b/libsnes/snes/cpu/core/opcode_write.cpp new file mode 100644 index 0000000..de85e67 --- /dev/null +++ b/libsnes/snes/cpu/core/opcode_write.cpp @@ -0,0 +1,199 @@ +#ifdef CPUCORE_CPP + +template void CPUcore::op_write_addr_b() { + aa.l = op_readpc(); + aa.h = op_readpc(); +L op_writedbr(aa.w, regs.r[n]); +} + +template void CPUcore::op_write_addr_w() { + aa.l = op_readpc(); + aa.h = op_readpc(); + op_writedbr(aa.w + 0, regs.r[n] >> 0); +L op_writedbr(aa.w + 1, regs.r[n] >> 8); +} + +template void CPUcore::op_write_addrr_b() { + aa.l = op_readpc(); + aa.h = op_readpc(); + op_io(); +L op_writedbr(aa.w + regs.r[i], regs.r[n]); +} + +template void CPUcore::op_write_addrr_w() { + aa.l = op_readpc(); + aa.h = op_readpc(); + op_io(); + op_writedbr(aa.w + regs.r[i] + 0, regs.r[n] >> 0); +L op_writedbr(aa.w + regs.r[i] + 1, regs.r[n] >> 8); +} + +template void CPUcore::op_write_longr_b() { + aa.l = op_readpc(); + aa.h = op_readpc(); + aa.b = op_readpc(); +L op_writelong(aa.d + regs.r[i], regs.a.l); +} + +template void CPUcore::op_write_longr_w() { + aa.l = op_readpc(); + aa.h = op_readpc(); + aa.b = op_readpc(); + op_writelong(aa.d + regs.r[i] + 0, regs.a.l); +L op_writelong(aa.d + regs.r[i] + 1, regs.a.h); +} + +template void CPUcore::op_write_dp_b() { + dp = op_readpc(); + op_io_cond2(); +L op_writedp(dp, regs.r[n]); +} + +template void CPUcore::op_write_dp_w() { + dp = op_readpc(); + op_io_cond2(); + op_writedp(dp + 0, regs.r[n] >> 0); +L op_writedp(dp + 1, regs.r[n] >> 8); +} + +template void CPUcore::op_write_dpr_b() { + dp = op_readpc(); + op_io_cond2(); + op_io(); +L op_writedp(dp + regs.r[i], regs.r[n]); +} + +template void CPUcore::op_write_dpr_w() { + dp = op_readpc(); + op_io_cond2(); + op_io(); + op_writedp(dp + regs.r[i] + 0, regs.r[n] >> 0); +L op_writedp(dp + regs.r[i] + 1, regs.r[n] >> 8); +} + +void CPUcore::op_sta_idp_b() { + dp = op_readpc(); + op_io_cond2(); + aa.l = op_readdp(dp + 0); + aa.h = op_readdp(dp + 1); +L op_writedbr(aa.w, regs.a.l); +} + +void CPUcore::op_sta_idp_w() { + dp = op_readpc(); + op_io_cond2(); + aa.l = op_readdp(dp + 0); + aa.h = op_readdp(dp + 1); + op_writedbr(aa.w + 0, regs.a.l); +L op_writedbr(aa.w + 1, regs.a.h); +} + +void CPUcore::op_sta_ildp_b() { + dp = op_readpc(); + op_io_cond2(); + aa.l = op_readdp(dp + 0); + aa.h = op_readdp(dp + 1); + aa.b = op_readdp(dp + 2); +L op_writelong(aa.d, regs.a.l); +} + +void CPUcore::op_sta_ildp_w() { + dp = op_readpc(); + op_io_cond2(); + aa.l = op_readdp(dp + 0); + aa.h = op_readdp(dp + 1); + aa.b = op_readdp(dp + 2); + op_writelong(aa.d + 0, regs.a.l); +L op_writelong(aa.d + 1, regs.a.h); +} + +void CPUcore::op_sta_idpx_b() { + dp = op_readpc(); + op_io_cond2(); + op_io(); + aa.l = op_readdp(dp + regs.x.w + 0); + aa.h = op_readdp(dp + regs.x.w + 1); +L op_writedbr(aa.w, regs.a.l); +} + +void CPUcore::op_sta_idpx_w() { + dp = op_readpc(); + op_io_cond2(); + op_io(); + aa.l = op_readdp(dp + regs.x.w + 0); + aa.h = op_readdp(dp + regs.x.w + 1); + op_writedbr(aa.w + 0, regs.a.l); +L op_writedbr(aa.w + 1, regs.a.h); +} + +void CPUcore::op_sta_idpy_b() { + dp = op_readpc(); + op_io_cond2(); + aa.l = op_readdp(dp + 0); + aa.h = op_readdp(dp + 1); + op_io(); +L op_writedbr(aa.w + regs.y.w, regs.a.l); +} + +void CPUcore::op_sta_idpy_w() { + dp = op_readpc(); + op_io_cond2(); + aa.l = op_readdp(dp + 0); + aa.h = op_readdp(dp + 1); + op_io(); + op_writedbr(aa.w + regs.y.w + 0, regs.a.l); +L op_writedbr(aa.w + regs.y.w + 1, regs.a.h); +} + +void CPUcore::op_sta_ildpy_b() { + dp = op_readpc(); + op_io_cond2(); + aa.l = op_readdp(dp + 0); + aa.h = op_readdp(dp + 1); + aa.b = op_readdp(dp + 2); +L op_writelong(aa.d + regs.y.w, regs.a.l); +} + +void CPUcore::op_sta_ildpy_w() { + dp = op_readpc(); + op_io_cond2(); + aa.l = op_readdp(dp + 0); + aa.h = op_readdp(dp + 1); + aa.b = op_readdp(dp + 2); + op_writelong(aa.d + regs.y.w + 0, regs.a.l); +L op_writelong(aa.d + regs.y.w + 1, regs.a.h); +} + +void CPUcore::op_sta_sr_b() { + sp = op_readpc(); + op_io(); +L op_writesp(sp, regs.a.l); +} + +void CPUcore::op_sta_sr_w() { + sp = op_readpc(); + op_io(); + op_writesp(sp + 0, regs.a.l); +L op_writesp(sp + 1, regs.a.h); +} + +void CPUcore::op_sta_isry_b() { + sp = op_readpc(); + op_io(); + aa.l = op_readsp(sp + 0); + aa.h = op_readsp(sp + 1); + op_io(); +L op_writedbr(aa.w + regs.y.w, regs.a.l); +} + +void CPUcore::op_sta_isry_w() { + sp = op_readpc(); + op_io(); + aa.l = op_readsp(sp + 0); + aa.h = op_readsp(sp + 1); + op_io(); + op_writedbr(aa.w + regs.y.w + 0, regs.a.l); +L op_writedbr(aa.w + regs.y.w + 1, regs.a.h); +} + +#endif diff --git a/libsnes/snes/cpu/core/registers.hpp b/libsnes/snes/cpu/core/registers.hpp new file mode 100644 index 0000000..adcf797 --- /dev/null +++ b/libsnes/snes/cpu/core/registers.hpp @@ -0,0 +1,83 @@ +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 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 r[6], &a, &x, &y, &z, &s, &d; + flag_t p; + uint8 db; + bool e; + + bool irq; //IRQ pin (0 = low, 1 = trigger) + bool wai; //raised during wai, cleared after interrupt triggered + uint8 mdr; //memory data register + uint16 vector; //interrupt vector address + + regs_t(): + a(r[0]), x(r[1]), y(r[2]), z(r[3]), s(r[4]), d(r[5]), db(0), e(false), irq(false), wai(false), mdr(0), vector(0) { + z = 0; + } +}; diff --git a/libsnes/snes/cpu/core/serialization.cpp b/libsnes/snes/cpu/core/serialization.cpp new file mode 100644 index 0000000..81753e9 --- /dev/null +++ b/libsnes/snes/cpu/core/serialization.cpp @@ -0,0 +1,37 @@ +#ifdef CPUCORE_CPP + +void CPUcore::core_serialize(serializer &s) { + s.integer(regs.pc.d); + + s.integer(regs.a.w); + s.integer(regs.x.w); + s.integer(regs.y.w); + s.integer(regs.z.w); + s.integer(regs.s.w); + s.integer(regs.d.w); + + s.integer(regs.p.n); + s.integer(regs.p.v); + s.integer(regs.p.m); + s.integer(regs.p.x); + s.integer(regs.p.d); + s.integer(regs.p.i); + s.integer(regs.p.z); + s.integer(regs.p.c); + + s.integer(regs.db); + s.integer(regs.e); + s.integer(regs.irq); + s.integer(regs.wai); + s.integer(regs.mdr); + s.integer(regs.vector); + + s.integer(aa.d); + s.integer(rd.d); + s.integer(sp); + s.integer(dp); + + update_table(); +} + +#endif diff --git a/libsnes/snes/cpu/core/table.cpp b/libsnes/snes/cpu/core/table.cpp new file mode 100644 index 0000000..6575a4d --- /dev/null +++ b/libsnes/snes/cpu/core/table.cpp @@ -0,0 +1,312 @@ +#ifdef CPUCORE_CPP + +void CPUcore::initialize_opcode_table() { + #define opA( id, name ) op_table[table_EM + id] = op_table[table_MX + id] = op_table[table_Mx + id] = op_table[table_mX + id] = op_table[table_mx + id] = &CPUcore::op_##name; + #define opAII(id, name, x, y ) op_table[table_EM + id] = op_table[table_MX + id] = op_table[table_Mx + id] = op_table[table_mX + id] = op_table[table_mx + id] = &CPUcore::op_##name; + #define opE( id, name ) op_table[table_EM + id] = &CPUcore::op_##name##_e; op_table[table_MX + id] = op_table[table_Mx + id] = op_table[table_mX + id] = op_table[table_mx + id] = &CPUcore::op_##name##_n; + #define opEI( id, name, x ) op_table[table_EM + id] = &CPUcore::op_##name##_e; op_table[table_MX + id] = op_table[table_Mx + id] = op_table[table_mX + id] = op_table[table_mx + id] = &CPUcore::op_##name##_n; + #define opEII(id, name, x, y ) op_table[table_EM + id] = &CPUcore::op_##name##_e; op_table[table_MX + id] = op_table[table_Mx + id] = op_table[table_mX + id] = op_table[table_mx + id] = &CPUcore::op_##name##_n; + #define opM( id, name ) op_table[table_EM + id] = op_table[table_MX + id] = op_table[table_Mx + id] = &CPUcore::op_##name##_b; op_table[table_mX + id] = op_table[table_mx + id] = &CPUcore::op_##name##_w; + #define opMI( id, name, x ) op_table[table_EM + id] = op_table[table_MX + id] = op_table[table_Mx + id] = &CPUcore::op_##name##_b; op_table[table_mX + id] = op_table[table_mx + id] = &CPUcore::op_##name##_w; + #define opMII(id, name, x, y ) op_table[table_EM + id] = op_table[table_MX + id] = op_table[table_Mx + id] = &CPUcore::op_##name##_b; op_table[table_mX + id] = op_table[table_mx + id] = &CPUcore::op_##name##_w; + #define opMF( id, name, fn ) op_table[table_EM + id] = op_table[table_MX + id] = op_table[table_Mx + id] = &CPUcore::op_##name##_b<&CPUcore::op_##fn##_b>; op_table[table_mX + id] = op_table[table_mx + id] = &CPUcore::op_##name##_w<&CPUcore::op_##fn##_w>; + #define opMFI(id, name, fn, x) op_table[table_EM + id] = op_table[table_MX + id] = op_table[table_Mx + id] = &CPUcore::op_##name##_b<&CPUcore::op_##fn##_b, x>; op_table[table_mX + id] = op_table[table_mx + id] = &CPUcore::op_##name##_w<&CPUcore::op_##fn##_w, x>; + #define opX( id, name ) op_table[table_EM + id] = op_table[table_MX + id] = op_table[table_mX + id] = &CPUcore::op_##name##_b; op_table[table_Mx + id] = op_table[table_mx + id] = &CPUcore::op_##name##_w; + #define opXI( id, name, x ) op_table[table_EM + id] = op_table[table_MX + id] = op_table[table_mX + id] = &CPUcore::op_##name##_b; op_table[table_Mx + id] = op_table[table_mx + id] = &CPUcore::op_##name##_w; + #define opXII(id, name, x, y ) op_table[table_EM + id] = op_table[table_MX + id] = op_table[table_mX + id] = &CPUcore::op_##name##_b; op_table[table_Mx + id] = op_table[table_mx + id] = &CPUcore::op_##name##_w; + #define opXF( id, name, fn ) op_table[table_EM + id] = op_table[table_MX + id] = op_table[table_mX + id] = &CPUcore::op_##name##_b<&CPUcore::op_##fn##_b>; op_table[table_Mx + id] = op_table[table_mx + id] = &CPUcore::op_##name##_w<&CPUcore::op_##fn##_w>; + #define opXFI(id, name, fn, x) op_table[table_EM + id] = op_table[table_MX + id] = op_table[table_mX + id] = &CPUcore::op_##name##_b<&CPUcore::op_##fn##_b, x>; op_table[table_Mx + id] = op_table[table_mx + id] = &CPUcore::op_##name##_w<&CPUcore::op_##fn##_w, x>; + + opEII(0x00, interrupt, 0xfffe, 0xffe6) + opMF (0x01, read_idpx, ora) + opEII(0x02, interrupt, 0xfff4, 0xffe4) + opMF (0x03, read_sr, ora) + opMF (0x04, adjust_dp, tsb) + opMF (0x05, read_dp, ora) + opMF (0x06, adjust_dp, asl) + opMF (0x07, read_ildp, ora) + opA (0x08, php) + opMF (0x09, read_const, ora) + opM (0x0a, asl_imm) + opE (0x0b, phd) + opMF (0x0c, adjust_addr, tsb) + opMF (0x0d, read_addr, ora) + opMF (0x0e, adjust_addr, asl) + opMF (0x0f, read_long, ora) + opAII(0x10, branch, 0x80, false) + opMF (0x11, read_idpy, ora) + opMF (0x12, read_idp, ora) + opMF (0x13, read_isry, ora) + opMF (0x14, adjust_dp, trb) + opMFI(0x15, read_dpr, ora, X) + opMF (0x16, adjust_dpx, asl) + opMF (0x17, read_ildpy, ora) + opAII(0x18, flag, 0x01, 0x00) + opMF (0x19, read_addry, ora) + opMII(0x1a, adjust_imm, A, +1) + opE (0x1b, tcs) + opMF (0x1c, adjust_addr, trb) + opMF (0x1d, read_addrx, ora) + opMF (0x1e, adjust_addrx, asl) + opMF (0x1f, read_longx, ora) + opA (0x20, jsr_addr) + opMF (0x21, read_idpx, and) + opE (0x22, jsr_long) + opMF (0x23, read_sr, and) + opMF (0x24, read_dp, bit) + opMF (0x25, read_dp, and) + opMF (0x26, adjust_dp, rol) + opMF (0x27, read_ildp, and) + opE (0x28, plp) + opMF (0x29, read_const, and) + opM (0x2a, rol_imm) + opE (0x2b, pld) + opMF (0x2c, read_addr, bit) + opMF (0x2d, read_addr, and) + opMF (0x2e, adjust_addr, rol) + opMF (0x2f, read_long, and) + opAII(0x30, branch, 0x80, true) + opMF (0x31, read_idpy, and) + opMF (0x32, read_idp, and) + opMF (0x33, read_isry, and) + opMFI(0x34, read_dpr, bit, X) + opMFI(0x35, read_dpr, and, X) + opMF (0x36, adjust_dpx, rol) + opMF (0x37, read_ildpy, and) + opAII(0x38, flag, 0x01, 0x01) + opMF (0x39, read_addry, and) + opMII(0x3a, adjust_imm, A, -1) + opAII(0x3b, transfer_w, S, A) + opMF (0x3c, read_addrx, bit) + opMF (0x3d, read_addrx, and) + opMF (0x3e, adjust_addrx, rol) + opMF (0x3f, read_longx, and) + opE (0x40, rti) + opMF (0x41, read_idpx, eor) + opA (0x42, wdm) + opMF (0x43, read_sr, eor) + opXI (0x44, move, -1) + opMF (0x45, read_dp, eor) + opMF (0x46, adjust_dp, lsr) + opMF (0x47, read_ildp, eor) + opMI (0x48, push, A) + opMF (0x49, read_const, eor) + opM (0x4a, lsr_imm) + opA (0x4b, phk) + opA (0x4c, jmp_addr) + opMF (0x4d, read_addr, eor) + opMF (0x4e, adjust_addr, lsr) + opMF (0x4f, read_long, eor) + opAII(0x50, branch, 0x40, false) + opMF (0x51, read_idpy, eor) + opMF (0x52, read_idp, eor) + opMF (0x53, read_isry, eor) + opXI (0x54, move, +1) + opMFI(0x55, read_dpr, eor, X) + opMF (0x56, adjust_dpx, lsr) + opMF (0x57, read_ildpy, eor) + opAII(0x58, flag, 0x04, 0x00) + opMF (0x59, read_addry, eor) + opXI (0x5a, push, Y) + opAII(0x5b, transfer_w, A, D) + opA (0x5c, jmp_long) + opMF (0x5d, read_addrx, eor) + opMF (0x5e, adjust_addrx, lsr) + opMF (0x5f, read_longx, eor) + opA (0x60, rts) + opMF (0x61, read_idpx, adc) + opE (0x62, per) + opMF (0x63, read_sr, adc) + opMI (0x64, write_dp, Z) + opMF (0x65, read_dp, adc) + opMF (0x66, adjust_dp, ror) + opMF (0x67, read_ildp, adc) + opMI (0x68, pull, A) + opMF (0x69, read_const, adc) + opM (0x6a, ror_imm) + opE (0x6b, rtl) + opA (0x6c, jmp_iaddr) + opMF (0x6d, read_addr, adc) + opMF (0x6e, adjust_addr, ror) + opMF (0x6f, read_long, adc) + opAII(0x70, branch, 0x40, true) + opMF (0x71, read_idpy, adc) + opMF (0x72, read_idp, adc) + opMF (0x73, read_isry, adc) + opMII(0x74, write_dpr, Z, X) + opMFI(0x75, read_dpr, adc, X) + opMF (0x76, adjust_dpx, ror) + opMF (0x77, read_ildpy, adc) + opAII(0x78, flag, 0x04, 0x04) + opMF (0x79, read_addry, adc) + opXI (0x7a, pull, Y) + opAII(0x7b, transfer_w, D, A) + opA (0x7c, jmp_iaddrx) + opMF (0x7d, read_addrx, adc) + opMF (0x7e, adjust_addrx, ror) + opMF (0x7f, read_longx, adc) + opA (0x80, bra) + opM (0x81, sta_idpx) + opA (0x82, brl) + opM (0x83, sta_sr) + opXI (0x84, write_dp, Y) + opMI (0x85, write_dp, A) + opXI (0x86, write_dp, X) + opM (0x87, sta_ildp) + opXII(0x88, adjust_imm, Y, -1) + opM (0x89, read_bit_const) + opMII(0x8a, transfer, X, A) + opA (0x8b, phb) + opXI (0x8c, write_addr, Y) + opMI (0x8d, write_addr, A) + opXI (0x8e, write_addr, X) + opMI (0x8f, write_longr, Z) + opAII(0x90, branch, 0x01, false) + opM (0x91, sta_idpy) + opM (0x92, sta_idp) + opM (0x93, sta_isry) + opXII(0x94, write_dpr, Y, X) + opMII(0x95, write_dpr, A, X) + opXII(0x96, write_dpr, X, Y) + opM (0x97, sta_ildpy) + opMII(0x98, transfer, Y, A) + opMII(0x99, write_addrr, A, Y) + opE (0x9a, txs) + opXII(0x9b, transfer, X, Y) + opMI (0x9c, write_addr, Z) + opMII(0x9d, write_addrr, A, X) + opMII(0x9e, write_addrr, Z, X) + opMI (0x9f, write_longr, X) + opXF (0xa0, read_const, ldy) + opMF (0xa1, read_idpx, lda) + opXF (0xa2, read_const, ldx) + opMF (0xa3, read_sr, lda) + opXF (0xa4, read_dp, ldy) + opMF (0xa5, read_dp, lda) + opXF (0xa6, read_dp, ldx) + opMF (0xa7, read_ildp, lda) + opXII(0xa8, transfer, A, Y) + opMF (0xa9, read_const, lda) + opXII(0xaa, transfer, A, X) + opA (0xab, plb) + opXF (0xac, read_addr, ldy) + opMF (0xad, read_addr, lda) + opXF (0xae, read_addr, ldx) + opMF (0xaf, read_long, lda) + opAII(0xb0, branch, 0x01, true) + opMF (0xb1, read_idpy, lda) + opMF (0xb2, read_idp, lda) + opMF (0xb3, read_isry, lda) + opXFI(0xb4, read_dpr, ldy, X) + opMFI(0xb5, read_dpr, lda, X) + opXFI(0xb6, read_dpr, ldx, Y) + opMF (0xb7, read_ildpy, lda) + opAII(0xb8, flag, 0x40, 0x00) + opMF (0xb9, read_addry, lda) + opX (0xba, tsx) + opXII(0xbb, transfer, Y, X) + opXF (0xbc, read_addrx, ldy) + opMF (0xbd, read_addrx, lda) + opXF (0xbe, read_addry, ldx) + opMF (0xbf, read_longx, lda) + opXF (0xc0, read_const, cpy) + opMF (0xc1, read_idpx, cmp) + opEI (0xc2, pflag, 0) + opMF (0xc3, read_sr, cmp) + opXF (0xc4, read_dp, cpy) + opMF (0xc5, read_dp, cmp) + opMF (0xc6, adjust_dp, dec) + opMF (0xc7, read_ildp, cmp) + opXII(0xc8, adjust_imm, Y, +1) + opMF (0xc9, read_const, cmp) + opXII(0xca, adjust_imm, X, -1) + opA (0xcb, wai) + opXF (0xcc, read_addr, cpy) + opMF (0xcd, read_addr, cmp) + opMF (0xce, adjust_addr, dec) + opMF (0xcf, read_long, cmp) + opAII(0xd0, branch, 0x02, false) + opMF (0xd1, read_idpy, cmp) + opMF (0xd2, read_idp, cmp) + opMF (0xd3, read_isry, cmp) + opE (0xd4, pei) + opMFI(0xd5, read_dpr, cmp, X) + opMF (0xd6, adjust_dpx, dec) + opMF (0xd7, read_ildpy, cmp) + opAII(0xd8, flag, 0x08, 0x00) + opMF (0xd9, read_addry, cmp) + opXI (0xda, push, X) + opA (0xdb, stp) + opA (0xdc, jmp_iladdr) + opMF (0xdd, read_addrx, cmp) + opMF (0xde, adjust_addrx, dec) + opMF (0xdf, read_longx, cmp) + opXF (0xe0, read_const, cpx) + opMF (0xe1, read_idpx, sbc) + opEI (0xe2, pflag, 1) + opMF (0xe3, read_sr, sbc) + opXF (0xe4, read_dp, cpx) + opMF (0xe5, read_dp, sbc) + opMF (0xe6, adjust_dp, inc) + opMF (0xe7, read_ildp, sbc) + opXII(0xe8, adjust_imm, X, +1) + opMF (0xe9, read_const, sbc) + opA (0xea, nop) + opA (0xeb, xba) + opXF (0xec, read_addr, cpx) + opMF (0xed, read_addr, sbc) + opMF (0xee, adjust_addr, inc) + opMF (0xef, read_long, sbc) + opAII(0xf0, branch, 0x02, true) + opMF (0xf1, read_idpy, sbc) + opMF (0xf2, read_idp, sbc) + opMF (0xf3, read_isry, sbc) + opE (0xf4, pea) + opMFI(0xf5, read_dpr, sbc, X) + opMF (0xf6, adjust_dpx, inc) + opMF (0xf7, read_ildpy, sbc) + opAII(0xf8, flag, 0x08, 0x08) + opMF (0xf9, read_addry, sbc) + opXI (0xfa, pull, X) + opA (0xfb, xce) + opE (0xfc, jsr_iaddrx) + opMF (0xfd, read_addrx, sbc) + opMF (0xfe, adjust_addrx, inc) + opMF (0xff, read_longx, sbc) + + #undef opA + #undef opAII + #undef opE + #undef opEI + #undef opEII + #undef opM + #undef opMI + #undef opMII + #undef opMF + #undef opMFI + #undef opX + #undef opXI + #undef opXII + #undef opXF + #undef opXFI +} + +void CPUcore::update_table() { + if(regs.e) { + opcode_table = &op_table[table_EM]; + } else if(regs.p.m) { + if(regs.p.x) { + opcode_table = &op_table[table_MX]; + } else { + opcode_table = &op_table[table_Mx]; + } + } else { + if(regs.p.x) { + opcode_table = &op_table[table_mX]; + } else { + opcode_table = &op_table[table_mx]; + } + } +} + +#endif diff --git a/libsnes/snes/cpu/cpu.cpp b/libsnes/snes/cpu/cpu.cpp new file mode 100644 index 0000000..9821f85 --- /dev/null +++ b/libsnes/snes/cpu/cpu.cpp @@ -0,0 +1,169 @@ +#include + +#define CPU_CPP +namespace SNES { + +#if defined(DEBUGGER) + #include "debugger/debugger.cpp" + CPUDebugger cpu; +#else + CPU cpu; +#endif + +#include "serialization.cpp" +#include "dma/dma.cpp" +#include "memory/memory.cpp" +#include "mmio/mmio.cpp" +#include "timing/timing.cpp" + +void CPU::step(unsigned clocks) { + smp.clock -= clocks * (uint64)smp.frequency; + ppu.clock -= clocks; + for(unsigned i = 0; i < coprocessors.size(); i++) { + Processor &chip = *coprocessors[i]; + chip.clock -= clocks * (uint64)chip.frequency; + } + input.port1->clock -= clocks * (uint64)input.port1->frequency; + input.port2->clock -= clocks * (uint64)input.port2->frequency; + synchronize_controllers(); +} + +void CPU::synchronize_smp() { + if(SMP::Threaded == true) { + if(smp.clock < 0) co_switch(smp.thread); + } else { + while(smp.clock < 0) smp.enter(); + } +} + +void CPU::synchronize_ppu() { + if(PPU::Threaded == true) { + if(ppu.clock < 0) co_switch(ppu.thread); + } else { + while(ppu.clock < 0) ppu.enter(); + } +} + +void CPU::synchronize_coprocessors() { + for(unsigned i = 0; i < coprocessors.size(); i++) { + Processor &chip = *coprocessors[i]; + if(chip.clock < 0) co_switch(chip.thread); + } +} + +void CPU::synchronize_controllers() { + if(input.port1->clock < 0) co_switch(input.port1->thread); + if(input.port2->clock < 0) co_switch(input.port2->thread); +} + +void CPU::Enter() { cpu.enter(); } + +void CPU::enter() { + while(true) { + if(scheduler.sync.i == Scheduler::SynchronizeMode::CPU) { + scheduler.sync.i = Scheduler::SynchronizeMode::All; + scheduler.exit(Scheduler::ExitReason::SynchronizeEvent); + } + + if(status.interrupt_pending) { + status.interrupt_pending = false; + if(status.nmi_pending) { + status.nmi_pending = false; + regs.vector = (regs.e == false ? 0xffea : 0xfffa); + op_irq(); + } else if(status.irq_pending) { + status.irq_pending = false; + regs.vector = (regs.e == false ? 0xffee : 0xfffe); + op_irq(); + } else if(status.reset_pending) { + status.reset_pending = false; + add_clocks(186); + regs.pc.l = bus.read(0xfffc); + regs.pc.h = bus.read(0xfffd); + } + } + + op_step(); + } +} + +void CPU::op_step() { + (this->*opcode_table[op_readpc()])(); +} + +static uint8 cpu_default_read(unsigned addr) { + return cpu.wram[addr]; +} + +static void cpu_default_write(unsigned addr, uint8 data) { + cpu.wram[addr] = data; +} + +void CPU::enable() { + function read(&CPU::mmio_read, (CPU*)&cpu); + function write(&CPU::mmio_write, (CPU*)&cpu); + + bus.map(Bus::MapMode::Direct, 0x00, 0x3f, 0x2140, 0x2183, read, write); + bus.map(Bus::MapMode::Direct, 0x80, 0xbf, 0x2140, 0x2183, read, write); + + bus.map(Bus::MapMode::Direct, 0x00, 0x3f, 0x4016, 0x4017, read, write); + bus.map(Bus::MapMode::Direct, 0x80, 0xbf, 0x4016, 0x4017, read, write); + + bus.map(Bus::MapMode::Direct, 0x00, 0x3f, 0x4200, 0x421f, read, write); + bus.map(Bus::MapMode::Direct, 0x80, 0xbf, 0x4200, 0x421f, read, write); + + bus.map(Bus::MapMode::Direct, 0x00, 0x3f, 0x4300, 0x437f, read, write); + bus.map(Bus::MapMode::Direct, 0x80, 0xbf, 0x4300, 0x437f, read, write); + + read = function(cpu_default_read); + write = function(cpu_default_write); + + bus.map(Bus::MapMode::Linear, 0x00, 0x3f, 0x0000, 0x1fff, read, write, 0x000000, 0x002000); + bus.map(Bus::MapMode::Linear, 0x80, 0xbf, 0x0000, 0x1fff, read, write, 0x000000, 0x002000); + bus.map(Bus::MapMode::Linear, 0x7e, 0x7f, 0x0000, 0xffff, read, write); +} + +void CPU::power() { + cpu_version = config.cpu.version; + foreach(n, wram) n = random(config.cpu.wram_init_value); + + regs.a = regs.x = regs.y = 0x0000; + regs.s = 0x01ff; + + mmio_power(); + dma_power(); + timing_power(); +} + +void CPU::reset() { + create(Enter, system.cpu_frequency); + coprocessors.reset(); + PPUcounter::reset(); + + //note: some registers are not fully reset by SNES + regs.pc = 0x000000; + regs.x.h = 0x00; + regs.y.h = 0x00; + regs.s.h = 0x01; + regs.d = 0x0000; + regs.db = 0x00; + regs.p = 0x34; + regs.e = 1; + regs.mdr = 0x00; + regs.wai = false; + regs.vector = 0xfffc; //reset vector address + update_table(); + + mmio_reset(); + dma_reset(); + timing_reset(); +} + +CPU::CPU() { + PPUcounter::scanline = bind( &CPU::scanline, this ); +} + +CPU::~CPU() { +} + +} diff --git a/libsnes/snes/cpu/cpu.hpp b/libsnes/snes/cpu/cpu.hpp new file mode 100644 index 0000000..f705e79 --- /dev/null +++ b/libsnes/snes/cpu/cpu.hpp @@ -0,0 +1,145 @@ +struct CPU : public Processor, public CPUcore, public PPUcounter { + uint8 wram[128 * 1024]; + + enum{ Threaded = true }; + array coprocessors; + alwaysinline void step(unsigned clocks); + alwaysinline void synchronize_smp(); + void synchronize_ppu(); + void synchronize_coprocessors(); + void synchronize_controllers(); + + uint8 port_read(uint2 port) const; + void port_write(uint2 port, uint8 data); + + uint8 pio(); + bool joylatch(); + alwaysinline bool interrupt_pending() { return status.interrupt_pending; } + + void enter(); + void enable(); + void power(); + void reset(); + + void serialize(serializer&); + CPU(); + ~CPU(); + +private: + #include "dma/dma.hpp" + #include "memory/memory.hpp" + #include "mmio/mmio.hpp" + #include "timing/timing.hpp" + + uint8 cpu_version; + + struct Status { + bool interrupt_pending; + + unsigned clock_count; + unsigned line_clocks; + + //timing + bool irq_lock; + + unsigned dram_refresh_position; + bool dram_refreshed; + + unsigned hdma_init_position; + bool hdma_init_triggered; + + unsigned hdma_position; + bool hdma_triggered; + + bool nmi_valid; + bool nmi_line; + bool nmi_transition; + bool nmi_pending; + bool nmi_hold; + + bool irq_valid; + bool irq_line; + bool irq_transition; + bool irq_pending; + bool irq_hold; + + bool reset_pending; + + //DMA + bool dma_active; + unsigned dma_counter; + unsigned dma_clocks; + bool dma_pending; + bool hdma_pending; + bool hdma_mode; //0 = init, 1 = run + + //auto joypad polling + bool auto_joypad_active; + bool auto_joypad_latch; + unsigned auto_joypad_counter; + unsigned auto_joypad_clock; + + //MMIO + //$2140-217f + uint8 port[4]; + + //$2181-$2183 + uint17 wram_addr; + + //$4016-$4017 + bool joypad_strobe_latch; + uint32 joypad1_bits; + uint32 joypad2_bits; + + //$4200 + bool nmi_enabled; + bool hirq_enabled, virq_enabled; + bool auto_joypad_poll; + + //$4201 + uint8 pio; + + //$4202-$4203 + uint8 wrmpya; + uint8 wrmpyb; + + //$4204-$4206 + uint16 wrdiva; + uint8 wrdivb; + + //$4207-$420a + uint9 hirq_pos; + uint9 virq_pos; + + //$420d + unsigned rom_speed; + + //$4214-$4217 + uint16 rddiv; + uint16 rdmpy; + + //$4218-$421f + uint16 joy1; + uint16 joy2; + uint16 joy3; + uint16 joy4; + } status; + + struct ALU { + unsigned mpyctr; + unsigned divctr; + unsigned shift; + } alu; + + static void Enter(); + debugvirtual void op_step(); + + friend class CPUDebugger; +}; + +#if defined(DEBUGGER) + #include "debugger/debugger.hpp" + extern CPUDebugger cpu; +#else + extern CPU cpu; +#endif diff --git a/libsnes/snes/cpu/debugger/debugger.cpp b/libsnes/snes/cpu/debugger/debugger.cpp new file mode 100644 index 0000000..a33518e --- /dev/null +++ b/libsnes/snes/cpu/debugger/debugger.cpp @@ -0,0 +1,156 @@ +#ifdef CPU_CPP + +void CPUDebugger::op_step() { + bool break_event = false; + + usage[regs.pc] &= ~(UsageFlagM | UsageFlagX); + usage[regs.pc] |= UsageExec | (regs.p.m << 1) | (regs.p.x << 0); + opcode_pc = regs.pc; + + opcode_edge = true; + debugger.breakpoint_test(Debugger::Breakpoint::Source::CPUBus, Debugger::Breakpoint::Mode::Exec, regs.pc, 0x00); + if(step_event && step_event() == true) { + debugger.break_event = Debugger::BreakEvent::CPUStep; + scheduler.exit(Scheduler::ExitReason::DebuggerEvent); + } + opcode_edge = false; + + CPU::op_step(); + synchronize_smp(); +} + +uint8 CPUDebugger::op_read(uint32 addr) { + uint8 data = CPU::op_read(addr); + usage[addr] |= UsageRead; + debugger.breakpoint_test(Debugger::Breakpoint::Source::CPUBus, Debugger::Breakpoint::Mode::Read, addr, data); + return data; +} + +void CPUDebugger::op_write(uint32 addr, uint8 data) { + CPU::op_write(addr, data); + usage[addr] |= UsageWrite; + usage[addr] &= ~UsageExec; + debugger.breakpoint_test(Debugger::Breakpoint::Source::CPUBus, Debugger::Breakpoint::Mode::Write, addr, data); +} + +CPUDebugger::CPUDebugger() { + usage = new uint8[1 << 24](); + opcode_pc = 0x8000; + opcode_edge = false; +} + +CPUDebugger::~CPUDebugger() { + delete[] usage; +} + +bool CPUDebugger::property(unsigned id, string &name, string &value) { + unsigned n = 0; + + #define item(name_, value_) \ + if(id == n++) { \ + name = name_; \ + value = value_; \ + return true; \ + } + + //internal + item("S-CPU MDR", string("0x", hex<2>(regs.mdr))); + + //$2181-2183 + item("$2181-$2183", ""); + item("WRAM Address", string("0x", hex<6>(status.wram_addr))); + + //$4016 + item("$4016", ""); + item("Joypad Strobe Latch", status.joypad_strobe_latch); + + //$4200 + item("$4200", ""); + item("NMI Enable", status.nmi_enabled); + item("H-IRQ Enable", status.hirq_enabled); + item("V-IRQ Enable", status.virq_enabled); + item("Auto Joypad Poll", status.auto_joypad_poll); + + //$4201 + item("$4201", ""); + item("PIO", string("0x", hex<2>(status.pio))); + + //$4202 + item("$4202", ""); + item("Multiplicand", string("0x", hex<2>(status.wrmpya))); + + //$4203 + item("$4203", ""); + item("Multiplier", string("0x", hex<2>(status.wrmpyb))); + + //$4204-$4205 + item("$4204-$4205", ""); + item("Dividend", string("0x", hex<4>(status.wrdiva))); + + //$4206 + item("$4206", ""); + item("Divisor", string("0x", hex<2>(status.wrdivb))); + + //$4207-$4208 + item("$4207-$4208", ""); + item("H-Time", string("0x", hex<4>(status.hirq_pos))); + + //$4209-$420a + item("$4209-$420a", ""); + item("V-Time", string("0x", hex<4>(status.virq_pos))); + + //$420b + unsigned dma_enable = 0; + for(unsigned n = 0; n < 8; n++) dma_enable |= channel[n].dma_enabled << n; + + item("$420b", ""); + item("DMA Enable", string("0x", hex<2>(dma_enable))); + + //$420c + unsigned hdma_enable = 0; + for(unsigned n = 0; n < 8; n++) hdma_enable |= channel[n].hdma_enabled << n; + + item("$420c", ""); + item("HDMA Enable", string("0x", hex<2>(hdma_enable))); + + //$420d + item("$420d", ""); + item("FastROM Enable", status.rom_speed == 6); + + for(unsigned i = 0; i < 8; i++) { + item(string("DMA Channel ", i), ""); + + //$43x0 + item("Direction", channel[i].direction); + item("Indirect", channel[i].indirect); + item("Reverse Transfer", channel[i].reverse_transfer); + item("Fixed Transfer", channel[i].fixed_transfer); + item("Transfer Mode", (unsigned)channel[i].transfer_mode); + + //$43x1 + item("B-Bus Address", string("0x", hex<4>(channel[i].dest_addr))); + + //$43x2-$43x3 + item("A-Bus Address", string("0x", hex<4>(channel[i].source_addr))); + + //$43x4 + item("A-Bus Bank", string("0x", hex<2>(channel[i].source_bank))); + + //$43x5-$43x6 + item("Transfer Size / Indirect Address", string("0x", hex<4>(channel[i].transfer_size))); + + //$43x7 + item("Indirect Bank", string("0x", hex<2>(channel[i].indirect_bank))); + + //$43x8-$43x9 + item("Table Address", string("0x", hex<4>(channel[i].hdma_addr))); + + //$43xa + item("Line Counter", string("0x", hex<2>(channel[i].line_counter))); + } + + #undef item + return false; +} + +#endif diff --git a/libsnes/snes/cpu/debugger/debugger.hpp b/libsnes/snes/cpu/debugger/debugger.hpp new file mode 100644 index 0000000..579f6f0 --- /dev/null +++ b/libsnes/snes/cpu/debugger/debugger.hpp @@ -0,0 +1,24 @@ +class CPUDebugger : public CPU, public ChipDebugger { +public: + bool property(unsigned id, string &name, string &value); + + function step_event; + + enum Usage { + UsageRead = 0x80, + UsageWrite = 0x40, + UsageExec = 0x20, + UsageFlagM = 0x02, + UsageFlagX = 0x01, + }; + uint8 *usage; + uint24 opcode_pc; //points to the current opcode, used to backtrace on read/write breakpoints + bool opcode_edge; //true right before an opcode execues, used to skip over opcodes + + void op_step(); + uint8 op_read(uint32 addr); + void op_write(uint32 addr, uint8 data); + + CPUDebugger(); + ~CPUDebugger(); +}; diff --git a/libsnes/snes/cpu/dma/dma.cpp b/libsnes/snes/cpu/dma/dma.cpp new file mode 100644 index 0000000..e8cdb3e --- /dev/null +++ b/libsnes/snes/cpu/dma/dma.cpp @@ -0,0 +1,289 @@ +#ifdef CPU_CPP + +void CPU::dma_add_clocks(unsigned clocks) { + status.dma_clocks += clocks; + add_clocks(clocks); +} + +//============= +//memory access +//============= + +bool CPU::dma_transfer_valid(uint8 bbus, uint32 abus) { + //transfers from WRAM to WRAM are invalid; chip only has one address bus + if(bbus == 0x80 && ((abus & 0xfe0000) == 0x7e0000 || (abus & 0x40e000) == 0x0000)) return false; + return true; +} + +bool CPU::dma_addr_valid(uint32 abus) { + //A-bus access to 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] + return true; +} + +uint8 CPU::dma_read(uint32 abus) { + if(dma_addr_valid(abus) == false) return 0x00; + return bus.read(abus); +} + +//simulate two-stage pipeline for DMA transfers; example: +//cycle 0: read N+0 +//cycle 1: write N+0 & read N+1 (parallel; one on A-bus, one on B-bus) +//cycle 2: write N+1 & read N+2 (parallel) +//cycle 3: write N+2 +void CPU::dma_write(bool valid, unsigned addr, uint8 data) { + if(pipe.valid) bus.write(pipe.addr, pipe.data); + pipe.valid = valid; + pipe.addr = addr; + pipe.data = data; +} + +void CPU::dma_transfer(bool direction, uint8 bbus, uint32 abus) { + if(direction == 0) { + dma_add_clocks(4); + regs.mdr = dma_read(abus); + dma_add_clocks(4); + dma_write(dma_transfer_valid(bbus, abus), 0x2100 | bbus, regs.mdr); + } else { + dma_add_clocks(4); + regs.mdr = dma_transfer_valid(bbus, abus) ? bus.read(0x2100 | bbus) : 0x00; + dma_add_clocks(4); + dma_write(dma_addr_valid(abus), abus, regs.mdr); + } +} + +//=================== +//address calculation +//=================== + +uint8 CPU::dma_bbus(unsigned i, unsigned index) { + switch(channel[i].transfer_mode) { default: + case 0: return (channel[i].dest_addr); //0 + case 1: return (channel[i].dest_addr + (index & 1)); //0,1 + case 2: return (channel[i].dest_addr); //0,0 + case 3: return (channel[i].dest_addr + ((index >> 1) & 1)); //0,0,1,1 + case 4: return (channel[i].dest_addr + (index & 3)); //0,1,2,3 + case 5: return (channel[i].dest_addr + (index & 1)); //0,1,0,1 + case 6: return (channel[i].dest_addr); //0,0 [2] + case 7: return (channel[i].dest_addr + ((index >> 1) & 1)); //0,0,1,1 [3] + } +} + +inline uint32 CPU::dma_addr(unsigned i) { + uint32 r = (channel[i].source_bank << 16) | (channel[i].source_addr); + + if(channel[i].fixed_transfer == false) { + if(channel[i].reverse_transfer == false) { + channel[i].source_addr++; + } else { + channel[i].source_addr--; + } + } + + return r; +} + +inline uint32 CPU::hdma_addr(unsigned i) { + return (channel[i].source_bank << 16) | (channel[i].hdma_addr++); +} + +inline uint32 CPU::hdma_iaddr(unsigned i) { + return (channel[i].indirect_bank << 16) | (channel[i].indirect_addr++); +} + +//============== +//channel status +//============== + +uint8 CPU::dma_enabled_channels() { + uint8 r = 0; + for(unsigned i = 0; i < 8; i++) { + if(channel[i].dma_enabled) r++; + } + return r; +} + +inline bool CPU::hdma_active(unsigned i) { + return (channel[i].hdma_enabled && !channel[i].hdma_completed); +} + +inline bool CPU::hdma_active_after(unsigned i) { + for(unsigned n = i + 1; n < 8; n++) { + if(hdma_active(n) == true) return true; + } + return false; +} + +inline uint8 CPU::hdma_enabled_channels() { + uint8 r = 0; + for(unsigned i = 0; i < 8; i++) { + if(channel[i].hdma_enabled) r++; + } + return r; +} + +inline uint8 CPU::hdma_active_channels() { + uint8 r = 0; + for(unsigned i = 0; i < 8; i++) { + if(hdma_active(i) == true) r++; + } + return r; +} + +//============== +//core functions +//============== + +void CPU::dma_run() { + dma_add_clocks(8); + dma_write(false); + dma_edge(); + + for(unsigned i = 0; i < 8; i++) { + if(channel[i].dma_enabled == false) continue; + + unsigned index = 0; + do { + dma_transfer(channel[i].direction, dma_bbus(i, index++), dma_addr(i)); + dma_edge(); + } while(channel[i].dma_enabled && --channel[i].transfer_size); + + dma_add_clocks(8); + dma_write(false); + dma_edge(); + + channel[i].dma_enabled = false; + } + + status.irq_lock = true; +} + +void CPU::hdma_update(unsigned i) { + dma_add_clocks(4); + regs.mdr = dma_read((channel[i].source_bank << 16) | channel[i].hdma_addr); + dma_add_clocks(4); + dma_write(false); + + if((channel[i].line_counter & 0x7f) == 0) { + channel[i].line_counter = regs.mdr; + channel[i].hdma_addr++; + + channel[i].hdma_completed = (channel[i].line_counter == 0); + channel[i].hdma_do_transfer = !channel[i].hdma_completed; + + if(channel[i].indirect) { + dma_add_clocks(4); + regs.mdr = dma_read(hdma_addr(i)); + channel[i].indirect_addr = regs.mdr << 8; + dma_add_clocks(4); + dma_write(false); + + if(!channel[i].hdma_completed || hdma_active_after(i)) { + dma_add_clocks(4); + regs.mdr = dma_read(hdma_addr(i)); + channel[i].indirect_addr >>= 8; + channel[i].indirect_addr |= regs.mdr << 8; + dma_add_clocks(4); + dma_write(false); + } + } + } +} + +void CPU::hdma_run() { + dma_add_clocks(8); + dma_write(false); + + 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 + + if(channel[i].hdma_do_transfer) { + static const unsigned transfer_length[8] = { 1, 2, 2, 4, 4, 4, 2, 4 }; + unsigned length = transfer_length[channel[i].transfer_mode]; + for(unsigned index = 0; index < length; index++) { + unsigned addr = channel[i].indirect == false ? hdma_addr(i) : hdma_iaddr(i); + dma_transfer(channel[i].direction, dma_bbus(i, index), addr); + } + } + } + + for(unsigned i = 0; i < 8; i++) { + if(hdma_active(i) == false) continue; + + channel[i].line_counter--; + channel[i].hdma_do_transfer = channel[i].line_counter & 0x80; + hdma_update(i); + } + + status.irq_lock = true; +} + +void CPU::hdma_init_reset() { + for(unsigned i = 0; i < 8; i++) { + channel[i].hdma_completed = false; + channel[i].hdma_do_transfer = false; + } +} + +void CPU::hdma_init() { + dma_add_clocks(8); + dma_write(false); + + 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].hdma_addr = channel[i].source_addr; + channel[i].line_counter = 0; + hdma_update(i); + } + + status.irq_lock = true; +} + +//============== +//initialization +//============== + +void CPU::dma_power() { + for(unsigned i = 0; i < 8; i++) { + channel[i].direction = 1; + channel[i].indirect = true; + channel[i].unused = true; + channel[i].reverse_transfer = true; + channel[i].fixed_transfer = true; + channel[i].transfer_mode = 7; + + channel[i].dest_addr = 0xff; + + channel[i].source_addr = 0xffff; + channel[i].source_bank = 0xff; + + channel[i].transfer_size = 0xffff; + channel[i].indirect_bank = 0xff; + + channel[i].hdma_addr = 0xffff; + channel[i].line_counter = 0xff; + channel[i].unknown = 0xff; + } +} + +void CPU::dma_reset() { + for(unsigned i = 0; i < 8; i++) { + channel[i].dma_enabled = false; + channel[i].hdma_enabled = false; + + channel[i].hdma_completed = false; + channel[i].hdma_do_transfer = false; + } + + pipe.valid = false; + pipe.addr = 0; + pipe.data = 0; +} + +#endif diff --git a/libsnes/snes/cpu/dma/dma.hpp b/libsnes/snes/cpu/dma/dma.hpp new file mode 100644 index 0000000..33755bd --- /dev/null +++ b/libsnes/snes/cpu/dma/dma.hpp @@ -0,0 +1,79 @@ +struct { + //$420b + bool dma_enabled; + + //$420c + bool hdma_enabled; + + //$43x0 + bool direction; + bool indirect; + bool unused; + bool reverse_transfer; + bool fixed_transfer; + uint3 transfer_mode; + + //$43x1 + uint8 dest_addr; + + //$43x2-$43x3 + uint16 source_addr; + + //$43x4 + uint8 source_bank; + + //$43x5-$43x6 + union { + uint16 transfer_size; + uint16 indirect_addr; + }; + + //$43x7 + uint8 indirect_bank; + + //$43x8-$43x9 + uint16 hdma_addr; + + //$43xa + uint8 line_counter; + + //$43xb/$43xf + uint8 unknown; + + //internal state + bool hdma_completed; + bool hdma_do_transfer; +} channel[8]; + +struct { + bool valid; + unsigned addr; + uint8 data; +} pipe; + +void dma_add_clocks(unsigned clocks); +bool dma_transfer_valid(uint8 bbus, uint32 abus); +bool dma_addr_valid(uint32 abus); +uint8 dma_read(uint32 abus); +void dma_write(bool valid, unsigned addr = 0, uint8 data = 0); +void dma_transfer(bool direction, uint8 bbus, uint32 abus); + +uint8 dma_bbus(unsigned i, unsigned channel); +uint32 dma_addr(unsigned i); +uint32 hdma_addr(unsigned i); +uint32 hdma_iaddr(unsigned i); + +uint8 dma_enabled_channels(); +bool hdma_active(unsigned i); +bool hdma_active_after(unsigned i); +uint8 hdma_enabled_channels(); +uint8 hdma_active_channels(); + +void dma_run(); +void hdma_update(unsigned i); +void hdma_run(); +void hdma_init_reset(); +void hdma_init(); + +void dma_power(); +void dma_reset(); diff --git a/libsnes/snes/cpu/memory/memory.cpp b/libsnes/snes/cpu/memory/memory.cpp new file mode 100644 index 0000000..c2c8f1f --- /dev/null +++ b/libsnes/snes/cpu/memory/memory.cpp @@ -0,0 +1,41 @@ +#ifdef CPU_CPP + +uint8 CPU::port_read(uint2 port) const { return status.port[port]; } +void CPU::port_write(uint2 port, uint8 data) { status.port[port] = data; } + +void CPU::op_io() { + status.clock_count = 6; + dma_edge(); + add_clocks(6); + alu_edge(); +} + +uint8 CPU::op_read(uint32 addr) { + status.clock_count = speed(addr); + dma_edge(); + add_clocks(status.clock_count - 4); + regs.mdr = bus.read(addr); + add_clocks(4); + alu_edge(); + return regs.mdr; +} + +void CPU::op_write(uint32 addr, uint8 data) { + alu_edge(); + status.clock_count = speed(addr); + dma_edge(); + add_clocks(status.clock_count); + bus.write(addr, regs.mdr = data); +} + +unsigned CPU::speed(unsigned addr) const { + if(addr & 0x408000) { + if(addr & 0x800000) return status.rom_speed; + return 8; + } + if((addr + 0x6000) & 0x4000) return 8; + if((addr - 0x4000) & 0x7e00) return 6; + return 12; +} + +#endif diff --git a/libsnes/snes/cpu/memory/memory.hpp b/libsnes/snes/cpu/memory/memory.hpp new file mode 100644 index 0000000..d33861d --- /dev/null +++ b/libsnes/snes/cpu/memory/memory.hpp @@ -0,0 +1,4 @@ +void op_io(); +debugvirtual uint8 op_read(uint32 addr); +debugvirtual void op_write(uint32 addr, uint8 data); +alwaysinline unsigned speed(unsigned addr) const; diff --git a/libsnes/snes/cpu/mmio/mmio.cpp b/libsnes/snes/cpu/mmio/mmio.cpp new file mode 100644 index 0000000..6cf8111 --- /dev/null +++ b/libsnes/snes/cpu/mmio/mmio.cpp @@ -0,0 +1,519 @@ +#ifdef CPU_CPP + +uint8 CPU::pio() { return status.pio; } +bool CPU::joylatch() { return status.joypad_strobe_latch; } + +//WMDATA +uint8 CPU::mmio_r2180() { + return bus.read(0x7e0000 | status.wram_addr++); +} + +//WMDATA +void CPU::mmio_w2180(uint8 data) { + bus.write(0x7e0000 | status.wram_addr++, data); +} + +//WMADDL +void CPU::mmio_w2181(uint8 data) { + status.wram_addr = (status.wram_addr & 0x01ff00) | (data << 0); +} + +//WMADDM +void CPU::mmio_w2182(uint8 data) { + status.wram_addr = (status.wram_addr & 0x0100ff) | (data << 8); +} + +//WMADDH +void CPU::mmio_w2183(uint8 data) { + status.wram_addr = (status.wram_addr & 0x00ffff) | (data << 16); +} + +//JOYSER0 +//bit 0 is shared between JOYSER0 and JOYSER1, therefore +//strobing $4016.d0 affects both controller port latches. +//$4017 bit 0 writes are ignored. +void CPU::mmio_w4016(uint8 data) { + input.port1->latch(data & 1); + input.port2->latch(data & 1); +} + +//JOYSER0 +//7-2 = MDR +//1-0 = Joypad serial data +uint8 CPU::mmio_r4016() { + uint8 r = regs.mdr & 0xfc; + r |= input.port1->data(); + return r; +} + +//JOYSER1 +//7-5 = MDR +//4-2 = Always 1 (pins are connected to GND) +//1-0 = Joypad serial data +uint8 CPU::mmio_r4017() { + uint8 r = (regs.mdr & 0xe0) | 0x1c; + r |= input.port2->data(); + return r; +} + +//NMITIMEN +void CPU::mmio_w4200(uint8 data) { + status.auto_joypad_poll = data & 1; + nmitimen_update(data); +} + +//WRIO +void CPU::mmio_w4201(uint8 data) { + if((status.pio & 0x80) && !(data & 0x80)) ppu.latch_counters(); + status.pio = data; +} + +//WRMPYA +void CPU::mmio_w4202(uint8 data) { + status.wrmpya = data; +} + +//WRMPYB +void CPU::mmio_w4203(uint8 data) { + status.rdmpy = 0; + if(alu.mpyctr || alu.divctr) return; + + status.wrmpyb = data; + status.rddiv = (status.wrmpyb << 8) | status.wrmpya; + + alu.mpyctr = 8; //perform multiplication over the next eight cycles + alu.shift = status.wrmpyb; +} + +//WRDIVL +void CPU::mmio_w4204(uint8 data) { + status.wrdiva = (status.wrdiva & 0xff00) | (data << 0); +} + +//WRDIVH +void CPU::mmio_w4205(uint8 data) { + status.wrdiva = (status.wrdiva & 0x00ff) | (data << 8); +} + +//WRDIVB +void CPU::mmio_w4206(uint8 data) { + status.rdmpy = status.wrdiva; + if(alu.mpyctr || alu.divctr) return; + + status.wrdivb = data; + + alu.divctr = 16; //perform division over the next sixteen cycles + alu.shift = status.wrdivb << 16; +} + +//HTIMEL +void CPU::mmio_w4207(uint8 data) { + status.hirq_pos = (status.hirq_pos & 0x0100) | (data << 0); +} + +//HTIMEH +void CPU::mmio_w4208(uint8 data) { + status.hirq_pos = (status.hirq_pos & 0x00ff) | (data << 8); +} + +//VTIMEL +void CPU::mmio_w4209(uint8 data) { + status.virq_pos = (status.virq_pos & 0x0100) | (data << 0); +} + +//VTIMEH +void CPU::mmio_w420a(uint8 data) { + status.virq_pos = (status.virq_pos & 0x00ff) | (data << 8); +} + +//DMAEN +void CPU::mmio_w420b(uint8 data) { + for(unsigned i = 0; i < 8; i++) { + channel[i].dma_enabled = data & (1 << i); + } + if(data) status.dma_pending = true; +} + +//HDMAEN +void CPU::mmio_w420c(uint8 data) { + for(unsigned i = 0; i < 8; i++) { + channel[i].hdma_enabled = data & (1 << i); + } +} + +//MEMSEL +void CPU::mmio_w420d(uint8 data) { + status.rom_speed = (data & 1 ? 6 : 8); +} + +//RDNMI +//7 = NMI acknowledge +//6-4 = MDR +//3-0 = CPU (5a22) version +uint8 CPU::mmio_r4210() { + uint8 r = (regs.mdr & 0x70); + r |= (uint8)(rdnmi()) << 7; + r |= (cpu_version & 0x0f); + return r; +} + +//TIMEUP +//7 = IRQ acknowledge +//6-0 = MDR +uint8 CPU::mmio_r4211() { + uint8 r = (regs.mdr & 0x7f); + r |= (uint8)(timeup()) << 7; + return r; +} + +//HVBJOY +//7 = VBLANK acknowledge +//6 = HBLANK acknowledge +//5-1 = MDR +//0 = JOYPAD acknowledge +uint8 CPU::mmio_r4212() { + uint8 r = (regs.mdr & 0x3e); + if(status.auto_joypad_active) r |= 0x01; + if(hcounter() <= 2 || hcounter() >= 1096) r |= 0x40; //hblank + if(vcounter() >= (ppu.overscan() == false ? 225 : 240)) r |= 0x80; //vblank + return r; +} + +//RDIO +uint8 CPU::mmio_r4213() { + return status.pio; +} + +//RDDIVL +uint8 CPU::mmio_r4214() { + return status.rddiv >> 0; +} + +//RDDIVH +uint8 CPU::mmio_r4215() { + return status.rddiv >> 8; +} + +//RDMPYL +uint8 CPU::mmio_r4216() { + return status.rdmpy >> 0; +} + +//RDMPYH +uint8 CPU::mmio_r4217() { + return status.rdmpy >> 8; +} + +uint8 CPU::mmio_r4218() { return status.joy1 >> 0; } //JOY1L +uint8 CPU::mmio_r4219() { return status.joy1 >> 8; } //JOY1H +uint8 CPU::mmio_r421a() { return status.joy2 >> 0; } //JOY2L +uint8 CPU::mmio_r421b() { return status.joy2 >> 8; } //JOY2H +uint8 CPU::mmio_r421c() { return status.joy3 >> 0; } //JOY3L +uint8 CPU::mmio_r421d() { return status.joy3 >> 8; } //JOY3H +uint8 CPU::mmio_r421e() { return status.joy4 >> 0; } //JOY4L +uint8 CPU::mmio_r421f() { return status.joy4 >> 8; } //JOY4H + +//DMAPx +uint8 CPU::mmio_r43x0(uint8 i) { + return (channel[i].direction << 7) + | (channel[i].indirect << 6) + | (channel[i].unused << 5) + | (channel[i].reverse_transfer << 4) + | (channel[i].fixed_transfer << 3) + | (channel[i].transfer_mode << 0); +} + +//BBADx +uint8 CPU::mmio_r43x1(uint8 i) { + return channel[i].dest_addr; +} + +//A1TxL +uint8 CPU::mmio_r43x2(uint8 i) { + return channel[i].source_addr >> 0; +} + +//A1TxH +uint8 CPU::mmio_r43x3(uint8 i) { + return channel[i].source_addr >> 8; +} + +//A1Bx +uint8 CPU::mmio_r43x4(uint8 i) { + return channel[i].source_bank; +} + +//DASxL +//union { uint16 transfer_size; uint16 indirect_addr; }; +uint8 CPU::mmio_r43x5(uint8 i) { + return channel[i].transfer_size >> 0; +} + +//DASxH +//union { uint16 transfer_size; uint16 indirect_addr; }; +uint8 CPU::mmio_r43x6(uint8 i) { + return channel[i].transfer_size >> 8; +} + +//DASBx +uint8 CPU::mmio_r43x7(uint8 i) { + return channel[i].indirect_bank; +} + +//A2AxL +uint8 CPU::mmio_r43x8(uint8 i) { + return channel[i].hdma_addr >> 0; +} + +//A2AxH +uint8 CPU::mmio_r43x9(uint8 i) { + return channel[i].hdma_addr >> 8; +} + +//NTRLx +uint8 CPU::mmio_r43xa(uint8 i) { + return channel[i].line_counter; +} + +//??? +uint8 CPU::mmio_r43xb(uint8 i) { + return channel[i].unknown; +} + +//DMAPx +void CPU::mmio_w43x0(uint8 i, uint8 data) { + channel[i].direction = data & 0x80; + channel[i].indirect = data & 0x40; + channel[i].unused = data & 0x20; + channel[i].reverse_transfer = data & 0x10; + channel[i].fixed_transfer = data & 0x08; + channel[i].transfer_mode = data & 0x07; +} + +//DDBADx +void CPU::mmio_w43x1(uint8 i, uint8 data) { + channel[i].dest_addr = data; +} + +//A1TxL +void CPU::mmio_w43x2(uint8 i, uint8 data) { + channel[i].source_addr = (channel[i].source_addr & 0xff00) | (data << 0); +} + +//A1TxH +void CPU::mmio_w43x3(uint8 i, uint8 data) { + channel[i].source_addr = (channel[i].source_addr & 0x00ff) | (data << 8); +} + +//A1Bx +void CPU::mmio_w43x4(uint8 i, uint8 data) { + channel[i].source_bank = data; +} + +//DASxL +//union { uint16 transfer_size; uint16 indirect_addr; }; +void CPU::mmio_w43x5(uint8 i, uint8 data) { + channel[i].transfer_size = (channel[i].transfer_size & 0xff00) | (data << 0); +} + +//DASxH +//union { uint16 transfer_size; uint16 indirect_addr; }; +void CPU::mmio_w43x6(uint8 i, uint8 data) { + channel[i].transfer_size = (channel[i].transfer_size & 0x00ff) | (data << 8); +} + +//DASBx +void CPU::mmio_w43x7(uint8 i, uint8 data) { + channel[i].indirect_bank = data; +} + +//A2AxL +void CPU::mmio_w43x8(uint8 i, uint8 data) { + channel[i].hdma_addr = (channel[i].hdma_addr & 0xff00) | (data << 0); +} + +//A2AxH +void CPU::mmio_w43x9(uint8 i, uint8 data) { + channel[i].hdma_addr = (channel[i].hdma_addr & 0x00ff) | (data << 8); +} + +//NTRLx +void CPU::mmio_w43xa(uint8 i, uint8 data) { + channel[i].line_counter = data; +} + +//??? +void CPU::mmio_w43xb(uint8 i, uint8 data) { + channel[i].unknown = data; +} + +void CPU::mmio_power() { +} + +void CPU::mmio_reset() { + //$2140-217f + foreach(port, status.port) port = 0x00; + + //$2181-$2183 + status.wram_addr = 0x000000; + + //$4016-$4017 + status.joypad_strobe_latch = 0; + status.joypad1_bits = ~0; + status.joypad2_bits = ~0; + + //$4200 + status.nmi_enabled = false; + status.hirq_enabled = false; + status.virq_enabled = false; + status.auto_joypad_poll = false; + + //$4201 + status.pio = 0xff; + + //$4202-$4203 + status.wrmpya = 0xff; + status.wrmpyb = 0xff; + + //$4204-$4206 + status.wrdiva = 0xffff; + status.wrdivb = 0xff; + + //$4207-$420a + status.hirq_pos = 0x01ff; + status.virq_pos = 0x01ff; + + //$420d + status.rom_speed = 8; + + //$4214-$4217 + status.rddiv = 0x0000; + status.rdmpy = 0x0000; + + //$4218-$421f + status.joy1 = 0x0000; + status.joy2 = 0x0000; + status.joy3 = 0x0000; + status.joy4 = 0x0000; + + //ALU + alu.mpyctr = 0; + alu.divctr = 0; + alu.shift = 0; +} + +uint8 CPU::mmio_read(unsigned addr) { + addr &= 0xffff; + + //APU + if((addr & 0xffc0) == 0x2140) { //$2140-$217f + synchronize_smp(); + return smp.port_read(addr); + } + + //DMA + if((addr & 0xff80) == 0x4300) { //$4300-$437f + unsigned i = (addr >> 4) & 7; + switch(addr & 0xf) { + case 0x0: return mmio_r43x0(i); + case 0x1: return mmio_r43x1(i); + case 0x2: return mmio_r43x2(i); + case 0x3: return mmio_r43x3(i); + case 0x4: return mmio_r43x4(i); + case 0x5: return mmio_r43x5(i); + case 0x6: return mmio_r43x6(i); + case 0x7: return mmio_r43x7(i); + case 0x8: return mmio_r43x8(i); + case 0x9: return mmio_r43x9(i); + case 0xa: return mmio_r43xa(i); + case 0xb: return mmio_r43xb(i); + case 0xc: return regs.mdr; //unmapped + case 0xd: return regs.mdr; //unmapped + case 0xe: return regs.mdr; //unmapped + case 0xf: return mmio_r43xb(i); //mirror of $43xb + } + } + + switch(addr) { + case 0x2180: return mmio_r2180(); + case 0x4016: return mmio_r4016(); + case 0x4017: return mmio_r4017(); + case 0x4210: return mmio_r4210(); + case 0x4211: return mmio_r4211(); + case 0x4212: return mmio_r4212(); + case 0x4213: return mmio_r4213(); + case 0x4214: return mmio_r4214(); + case 0x4215: return mmio_r4215(); + case 0x4216: return mmio_r4216(); + case 0x4217: return mmio_r4217(); + case 0x4218: return mmio_r4218(); + case 0x4219: return mmio_r4219(); + case 0x421a: return mmio_r421a(); + case 0x421b: return mmio_r421b(); + case 0x421c: return mmio_r421c(); + case 0x421d: return mmio_r421d(); + case 0x421e: return mmio_r421e(); + case 0x421f: return mmio_r421f(); + } + + return regs.mdr; +} + +void CPU::mmio_write(unsigned addr, uint8 data) { + addr &= 0xffff; + + //APU + if((addr & 0xffc0) == 0x2140) { //$2140-$217f + synchronize_smp(); + port_write(addr, data); + return; + } + + //DMA + if((addr & 0xff80) == 0x4300) { //$4300-$437f + unsigned i = (addr >> 4) & 7; + switch(addr & 0xf) { + case 0x0: mmio_w43x0(i, data); return; + case 0x1: mmio_w43x1(i, data); return; + case 0x2: mmio_w43x2(i, data); return; + case 0x3: mmio_w43x3(i, data); return; + case 0x4: mmio_w43x4(i, data); return; + case 0x5: mmio_w43x5(i, data); return; + case 0x6: mmio_w43x6(i, data); return; + case 0x7: mmio_w43x7(i, data); return; + case 0x8: mmio_w43x8(i, data); return; + case 0x9: mmio_w43x9(i, data); return; + case 0xa: mmio_w43xa(i, data); return; + case 0xb: mmio_w43xb(i, data); return; + case 0xc: return; //unmapped + case 0xd: return; //unmapped + case 0xe: return; //unmapped + case 0xf: mmio_w43xb(i, data); return; //mirror of $43xb + } + } + + switch(addr) { + case 0x2180: mmio_w2180(data); return; + case 0x2181: mmio_w2181(data); return; + case 0x2182: mmio_w2182(data); return; + case 0x2183: mmio_w2183(data); return; + case 0x4016: mmio_w4016(data); return; + case 0x4017: return; //unmapped + case 0x4200: mmio_w4200(data); return; + case 0x4201: mmio_w4201(data); return; + case 0x4202: mmio_w4202(data); return; + case 0x4203: mmio_w4203(data); return; + case 0x4204: mmio_w4204(data); return; + case 0x4205: mmio_w4205(data); return; + case 0x4206: mmio_w4206(data); return; + case 0x4207: mmio_w4207(data); return; + case 0x4208: mmio_w4208(data); return; + case 0x4209: mmio_w4209(data); return; + case 0x420a: mmio_w420a(data); return; + case 0x420b: mmio_w420b(data); return; + case 0x420c: mmio_w420c(data); return; + case 0x420d: mmio_w420d(data); return; + } +} + +#endif diff --git a/libsnes/snes/cpu/mmio/mmio.hpp b/libsnes/snes/cpu/mmio/mmio.hpp new file mode 100644 index 0000000..01cb2d1 --- /dev/null +++ b/libsnes/snes/cpu/mmio/mmio.hpp @@ -0,0 +1,71 @@ +public: +uint8 mmio_read(unsigned addr); +void mmio_write(unsigned addr, uint8 data); + +private: +void mmio_power(); +void mmio_reset(); + +uint8 mmio_r2180(); +uint8 mmio_r4016(); +uint8 mmio_r4017(); +uint8 mmio_r4210(); +uint8 mmio_r4211(); +uint8 mmio_r4212(); +uint8 mmio_r4213(); +uint8 mmio_r4214(); +uint8 mmio_r4215(); +uint8 mmio_r4216(); +uint8 mmio_r4217(); +uint8 mmio_r4218(); +uint8 mmio_r4219(); +uint8 mmio_r421a(); +uint8 mmio_r421b(); +uint8 mmio_r421c(); +uint8 mmio_r421d(); +uint8 mmio_r421e(); +uint8 mmio_r421f(); +uint8 mmio_r43x0(uint8 i); +uint8 mmio_r43x1(uint8 i); +uint8 mmio_r43x2(uint8 i); +uint8 mmio_r43x3(uint8 i); +uint8 mmio_r43x4(uint8 i); +uint8 mmio_r43x5(uint8 i); +uint8 mmio_r43x6(uint8 i); +uint8 mmio_r43x7(uint8 i); +uint8 mmio_r43x8(uint8 i); +uint8 mmio_r43x9(uint8 i); +uint8 mmio_r43xa(uint8 i); +uint8 mmio_r43xb(uint8 i); + +void mmio_w2180(uint8 data); +void mmio_w2181(uint8 data); +void mmio_w2182(uint8 data); +void mmio_w2183(uint8 data); +void mmio_w4016(uint8 data); +void mmio_w4200(uint8 data); +void mmio_w4201(uint8 data); +void mmio_w4202(uint8 data); +void mmio_w4203(uint8 data); +void mmio_w4204(uint8 data); +void mmio_w4205(uint8 data); +void mmio_w4206(uint8 data); +void mmio_w4207(uint8 data); +void mmio_w4208(uint8 data); +void mmio_w4209(uint8 data); +void mmio_w420a(uint8 data); +void mmio_w420b(uint8 data); +void mmio_w420c(uint8 data); +void mmio_w420d(uint8 data); +void mmio_w43x0(uint8 i, uint8 data); +void mmio_w43x1(uint8 i, uint8 data); +void mmio_w43x2(uint8 i, uint8 data); +void mmio_w43x3(uint8 i, uint8 data); +void mmio_w43x4(uint8 i, uint8 data); +void mmio_w43x5(uint8 i, uint8 data); +void mmio_w43x6(uint8 i, uint8 data); +void mmio_w43x7(uint8 i, uint8 data); +void mmio_w43x8(uint8 i, uint8 data); +void mmio_w43x9(uint8 i, uint8 data); +void mmio_w43xa(uint8 i, uint8 data); +void mmio_w43xb(uint8 i, uint8 data); diff --git a/libsnes/snes/cpu/serialization.cpp b/libsnes/snes/cpu/serialization.cpp new file mode 100644 index 0000000..010d1ba --- /dev/null +++ b/libsnes/snes/cpu/serialization.cpp @@ -0,0 +1,118 @@ +#ifdef CPU_CPP + +void CPU::serialize(serializer &s) { + Processor::serialize(s); + CPUcore::core_serialize(s); + PPUcounter::serialize(s); + + s.array(wram); + + s.integer(cpu_version); + + s.integer(status.interrupt_pending); + + s.integer(status.clock_count); + s.integer(status.line_clocks); + + s.integer(status.irq_lock); + + s.integer(status.dram_refresh_position); + s.integer(status.dram_refreshed); + + s.integer(status.hdma_init_position); + s.integer(status.hdma_init_triggered); + + s.integer(status.hdma_position); + s.integer(status.hdma_triggered); + + s.integer(status.nmi_valid); + s.integer(status.nmi_line); + s.integer(status.nmi_transition); + s.integer(status.nmi_pending); + s.integer(status.nmi_hold); + + s.integer(status.irq_valid); + s.integer(status.irq_line); + s.integer(status.irq_transition); + s.integer(status.irq_pending); + s.integer(status.irq_hold); + + s.integer(status.reset_pending); + + s.integer(status.dma_active); + s.integer(status.dma_counter); + s.integer(status.dma_clocks); + s.integer(status.dma_pending); + s.integer(status.hdma_pending); + s.integer(status.hdma_mode); + + s.integer(status.auto_joypad_active); + s.integer(status.auto_joypad_latch); + s.integer(status.auto_joypad_counter); + s.integer(status.auto_joypad_clock); + + s.array(status.port); + + s.integer(status.wram_addr); + + s.integer(status.joypad_strobe_latch); + s.integer(status.joypad1_bits); + s.integer(status.joypad2_bits); + + s.integer(status.nmi_enabled); + s.integer(status.hirq_enabled); + s.integer(status.virq_enabled); + s.integer(status.auto_joypad_poll); + + s.integer(status.pio); + + s.integer(status.wrmpya); + s.integer(status.wrmpyb); + + s.integer(status.wrdiva); + s.integer(status.wrdivb); + + s.integer(status.hirq_pos); + s.integer(status.virq_pos); + + s.integer(status.rom_speed); + + s.integer(status.rddiv); + s.integer(status.rdmpy); + + s.integer(status.joy1); + s.integer(status.joy2); + s.integer(status.joy3); + s.integer(status.joy4); + + s.integer(alu.mpyctr); + s.integer(alu.divctr); + s.integer(alu.shift); + + for(unsigned i = 0; i < 8; i++) { + s.integer(channel[i].dma_enabled); + s.integer(channel[i].hdma_enabled); + s.integer(channel[i].direction); + s.integer(channel[i].indirect); + s.integer(channel[i].unused); + s.integer(channel[i].reverse_transfer); + s.integer(channel[i].fixed_transfer); + s.integer(channel[i].transfer_mode); + s.integer(channel[i].dest_addr); + s.integer(channel[i].source_addr); + s.integer(channel[i].source_bank); + s.integer(channel[i].transfer_size); + s.integer(channel[i].indirect_bank); + s.integer(channel[i].hdma_addr); + s.integer(channel[i].line_counter); + s.integer(channel[i].unknown); + s.integer(channel[i].hdma_completed); + s.integer(channel[i].hdma_do_transfer); + } + + s.integer(pipe.valid); + s.integer(pipe.addr); + s.integer(pipe.data); +} + +#endif diff --git a/libsnes/snes/cpu/timing/irq.cpp b/libsnes/snes/cpu/timing/irq.cpp new file mode 100644 index 0000000..506a435 --- /dev/null +++ b/libsnes/snes/cpu/timing/irq.cpp @@ -0,0 +1,106 @@ +#ifdef CPU_CPP + +//called once every four clock cycles; +//as NMI steps by scanlines (divisible by 4) and IRQ by PPU 4-cycle dots. +// +//ppu.(vh)counter(n) returns the value of said counters n-clocks before current time; +//it is used to emulate hardware communication delay between opcode and interrupt units. +void CPU::poll_interrupts() { + //NMI hold + if(status.nmi_hold) { + status.nmi_hold = false; + if(status.nmi_enabled) status.nmi_transition = true; + } + + //NMI test + bool nmi_valid = (vcounter(2) >= (!ppu.overscan() ? 225 : 240)); + if(!status.nmi_valid && nmi_valid) { + //0->1 edge sensitive transition + status.nmi_line = true; + status.nmi_hold = true; //hold /NMI for four cycles + } else if(status.nmi_valid && !nmi_valid) { + //1->0 edge sensitive transition + status.nmi_line = false; + } + status.nmi_valid = nmi_valid; + + //IRQ hold + status.irq_hold = false; + if(status.irq_line) { + if(status.virq_enabled || status.hirq_enabled) status.irq_transition = true; + } + + //IRQ test + bool irq_valid = (status.virq_enabled || status.hirq_enabled); + if(irq_valid) { + if((status.virq_enabled && vcounter(10) != (status.virq_pos)) + || (status.hirq_enabled && hcounter(10) != (status.hirq_pos + 1) * 4) + || (status.virq_pos && vcounter(6) == 0) //IRQs cannot trigger on last dot of field + ) irq_valid = false; + } + if(!status.irq_valid && irq_valid) { + //0->1 edge sensitive transition + status.irq_line = true; + status.irq_hold = true; //hold /IRQ for four cycles + } + status.irq_valid = irq_valid; +} + +void CPU::nmitimen_update(uint8 data) { + bool nmi_enabled = status.nmi_enabled; + bool virq_enabled = status.virq_enabled; + bool hirq_enabled = status.hirq_enabled; + status.nmi_enabled = data & 0x80; + status.virq_enabled = data & 0x20; + status.hirq_enabled = data & 0x10; + + //0->1 edge sensitive transition + if(!nmi_enabled && status.nmi_enabled && status.nmi_line) { + status.nmi_transition = true; + } + + //?->1 level sensitive transition + if(status.virq_enabled && !status.hirq_enabled && status.irq_line) { + status.irq_transition = true; + } + + if(!status.virq_enabled && !status.hirq_enabled) { + status.irq_line = false; + status.irq_transition = false; + } + + status.irq_lock = true; +} + +bool CPU::rdnmi() { + bool result = status.nmi_line; + if(!status.nmi_hold) { + status.nmi_line = false; + } + return result; +} + +bool CPU::timeup() { + bool result = status.irq_line; + if(!status.irq_hold) { + status.irq_line = false; + status.irq_transition = false; + } + return result; +} + +bool CPU::nmi_test() { + if(!status.nmi_transition) return false; + status.nmi_transition = false; + regs.wai = false; + return true; +} + +bool CPU::irq_test() { + if(!status.irq_transition && !regs.irq) return false; + status.irq_transition = false; + regs.wai = false; + return !regs.p.i; +} + +#endif diff --git a/libsnes/snes/cpu/timing/joypad.cpp b/libsnes/snes/cpu/timing/joypad.cpp new file mode 100644 index 0000000..179df27 --- /dev/null +++ b/libsnes/snes/cpu/timing/joypad.cpp @@ -0,0 +1,31 @@ +#ifdef CPU_CPP + +//called every 256 clocks; see CPU::add_clocks() +void CPU::step_auto_joypad_poll() { + if(vcounter() >= (ppu.overscan() == false ? 225 : 240)) { + //cache enable state at first iteration + if(status.auto_joypad_counter == 0) status.auto_joypad_latch = status.auto_joypad_poll; + status.auto_joypad_active = status.auto_joypad_counter <= 15; + + if(status.auto_joypad_active && status.auto_joypad_latch) { + if(status.auto_joypad_counter == 0) { + input.port1->latch(1); + input.port2->latch(1); + input.port1->latch(0); + input.port2->latch(0); + } + + uint2 port0 = input.port1->data(); + uint2 port1 = input.port2->data(); + + status.joy1 = (status.joy1 << 1) | (bool)(port0 & 1); + status.joy2 = (status.joy2 << 1) | (bool)(port1 & 1); + status.joy3 = (status.joy3 << 1) | (bool)(port0 & 2); + status.joy4 = (status.joy4 << 1) | (bool)(port1 & 2); + } + + status.auto_joypad_counter++; + } +} + +#endif diff --git a/libsnes/snes/cpu/timing/timing.cpp b/libsnes/snes/cpu/timing/timing.cpp new file mode 100644 index 0000000..f1378f0 --- /dev/null +++ b/libsnes/snes/cpu/timing/timing.cpp @@ -0,0 +1,199 @@ +#ifdef CPU_CPP + +#include "irq.cpp" +#include "joypad.cpp" + +unsigned CPU::dma_counter() { + return (status.dma_counter + hcounter()) & 7; +} + +void CPU::add_clocks(unsigned clocks) { + status.irq_lock = false; + unsigned ticks = clocks >> 1; + while(ticks--) { + tick(); + if(hcounter() & 2) poll_interrupts(); + } + + step(clocks); + + status.auto_joypad_clock += clocks; + if(status.auto_joypad_clock >= 256) { + status.auto_joypad_clock -= 256; + step_auto_joypad_poll(); + } + + if(status.dram_refreshed == false && hcounter() >= status.dram_refresh_position) { + status.dram_refreshed = true; + add_clocks(40); + } +} + +//called by ppu.tick() when Hcounter=0 +void CPU::scanline() { + status.dma_counter = (status.dma_counter + status.line_clocks) & 7; + status.line_clocks = lineclocks(); + + //forcefully sync S-CPU to other processors, in case chips are not communicating + synchronize_ppu(); + synchronize_smp(); + synchronize_coprocessors(); + system.scanline(); + + if(vcounter() == 0) { + //HDMA init triggers once every frame + status.hdma_init_position = (cpu_version == 1 ? 12 + 8 - dma_counter() : 12 + dma_counter()); + status.hdma_init_triggered = false; + + status.auto_joypad_counter = 0; + } + + //DRAM refresh occurs once every scanline + if(cpu_version == 2) status.dram_refresh_position = 530 + 8 - dma_counter(); + status.dram_refreshed = false; + + //HDMA triggers once every visible scanline + if(vcounter() <= (ppu.overscan() == false ? 224 : 239)) { + status.hdma_position = 1104; + status.hdma_triggered = false; + } +} + +void CPU::alu_edge() { + if(alu.mpyctr) { + alu.mpyctr--; + if(status.rddiv & 1) status.rdmpy += alu.shift; + status.rddiv >>= 1; + alu.shift <<= 1; + } + + if(alu.divctr) { + alu.divctr--; + status.rddiv <<= 1; + alu.shift >>= 1; + if(status.rdmpy >= alu.shift) { + status.rdmpy -= alu.shift; + status.rddiv |= 1; + } + } +} + +void CPU::dma_edge() { + //H/DMA pending && DMA inactive? + //.. Run one full CPU cycle + //.. HDMA pending && HDMA enabled ? DMA sync + HDMA run + //.. DMA pending && DMA enabled ? DMA sync + DMA run + //.... HDMA during DMA && HDMA enabled ? DMA sync + HDMA run + //.. Run one bus CPU cycle + //.. CPU sync + + if(status.dma_active == true) { + if(status.hdma_pending) { + status.hdma_pending = false; + if(hdma_enabled_channels()) { + if(!dma_enabled_channels()) { + dma_add_clocks(8 - dma_counter()); + } + status.hdma_mode == 0 ? hdma_init() : hdma_run(); + if(!dma_enabled_channels()) { + add_clocks(status.clock_count - (status.dma_clocks % status.clock_count)); + status.dma_active = false; + } + } + } + + if(status.dma_pending) { + status.dma_pending = false; + if(dma_enabled_channels()) { + dma_add_clocks(8 - dma_counter()); + dma_run(); + add_clocks(status.clock_count - (status.dma_clocks % status.clock_count)); + status.dma_active = false; + } + } + } + + if(status.hdma_init_triggered == false && hcounter() >= status.hdma_init_position) { + status.hdma_init_triggered = true; + hdma_init_reset(); + if(hdma_enabled_channels()) { + status.hdma_pending = true; + status.hdma_mode = 0; + } + } + + if(status.hdma_triggered == false && hcounter() >= status.hdma_position) { + status.hdma_triggered = true; + if(hdma_active_channels()) { + status.hdma_pending = true; + status.hdma_mode = 1; + } + } + + if(status.dma_active == false) { + if(status.dma_pending || status.hdma_pending) { + status.dma_clocks = 0; + status.dma_active = true; + } + } +} + +//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_lock is used to simulate hardware delay before interrupts can +//trigger during certain events (immediately after DMA, writes to $4200, etc) +void CPU::last_cycle() { + if(status.irq_lock == false) { + status.nmi_pending |= nmi_test(); + status.irq_pending |= irq_test(); + status.interrupt_pending = (status.nmi_pending || status.irq_pending); + } +} + +void CPU::timing_power() { +} + +void CPU::timing_reset() { + status.clock_count = 0; + status.line_clocks = lineclocks(); + + status.irq_lock = false; + status.dram_refresh_position = (cpu_version == 1 ? 530 : 538); + status.dram_refreshed = false; + + status.hdma_init_position = (cpu_version == 1 ? 12 + 8 - dma_counter() : 12 + dma_counter()); + status.hdma_init_triggered = false; + + status.hdma_position = 1104; + status.hdma_triggered = false; + + status.nmi_valid = false; + status.nmi_line = false; + status.nmi_transition = false; + status.nmi_pending = false; + status.nmi_hold = false; + + status.irq_valid = false; + status.irq_line = false; + status.irq_transition = false; + status.irq_pending = false; + status.irq_hold = false; + + status.reset_pending = true; + status.interrupt_pending = true; + + status.dma_active = false; + status.dma_counter = 0; + status.dma_clocks = 0; + status.dma_pending = false; + status.hdma_pending = false; + status.hdma_mode = 0; + + status.auto_joypad_active = false; + status.auto_joypad_latch = false; + status.auto_joypad_counter = 0; + status.auto_joypad_clock = 0; +} + +#endif diff --git a/libsnes/snes/cpu/timing/timing.hpp b/libsnes/snes/cpu/timing/timing.hpp new file mode 100644 index 0000000..6c225da --- /dev/null +++ b/libsnes/snes/cpu/timing/timing.hpp @@ -0,0 +1,24 @@ +//timing.cpp +unsigned dma_counter(); + +void add_clocks(unsigned clocks); +void scanline(); + +alwaysinline void alu_edge(); +alwaysinline void dma_edge(); +alwaysinline void last_cycle(); + +void timing_power(); +void timing_reset(); + +//irq.cpp +alwaysinline void poll_interrupts(); +void nmitimen_update(uint8 data); +bool rdnmi(); +bool timeup(); + +alwaysinline bool nmi_test(); +alwaysinline bool irq_test(); + +//joypad.cpp +void step_auto_joypad_poll(); diff --git a/libsnes/snes/debugger/debugger.cpp b/libsnes/snes/debugger/debugger.cpp new file mode 100644 index 0000000..315fa80 --- /dev/null +++ b/libsnes/snes/debugger/debugger.cpp @@ -0,0 +1,112 @@ +#ifdef SYSTEM_CPP + +Debugger debugger; + +void Debugger::breakpoint_test(Debugger::Breakpoint::Source::e source, Debugger::Breakpoint::Mode::e mode, unsigned addr, uint8 data) { + for(unsigned i = 0; i < Breakpoints; i++) { + if(breakpoint[i].enabled == false) continue; + + bool source_wram = ((breakpoint[i].addr & 0x40e000) == 0x000000) || ((breakpoint[i].addr & 0xffe000) == 0x7e0000); + bool offset_wram = ((addr & 0x40e000) == 0x000000) || ((addr & 0xffe000) == 0x7e0000); + + if(source == Debugger::Breakpoint::Source::CPUBus && source_wram && offset_wram) { + //shadow S-CPU WRAM addresses ($00-3f|80-bf:0000-1fff mirrors $7e:0000-1fff) + if((breakpoint[i].addr & 0x1fff) != (addr & 0x1fff)) continue; + } else { + if(breakpoint[i].addr != addr) continue; + } + + if(breakpoint[i].data != -1 && breakpoint[i].data != data) continue; + if(breakpoint[i].source.i != source) continue; + if(breakpoint[i].mode.i != mode) continue; + + breakpoint[i].counter++; + breakpoint_hit = i; + break_event.i = BreakEvent::BreakpointHit; + scheduler.exit(Scheduler::ExitReason::DebuggerEvent); + break; + } +} + +uint8 Debugger::read(Debugger::MemorySource::e source, unsigned addr) { + switch(source) { + case MemorySource::CPUBus: { + //do not read from memory-mapped registers that could affect program behavior + if(((addr - 0x2000) & 0x40c000) == 0x000000) break; //$00-3f:2000-5fff MMIO + return bus.read(addr & 0xffffff); + } break; + + case MemorySource::APUBus: { + if((addr & 0xffc0) == 0xffc0) return smp.iplrom[addr & 0x3f]; + return smp.apuram[addr & 0xffff]; + } break; + + case MemorySource::APURAM: { + return smp.apuram[addr & 0xffff]; + } break; + + case MemorySource::VRAM: { + return ppu.vram[addr & 0xffff]; + } break; + + case MemorySource::OAM: { + if(addr & 0x0200) return ppu.oam[0x0200 + (addr & 0x1f)]; + return ppu.oam[addr & 0x01ff]; + } break; + + case MemorySource::CGRAM: { + return ppu.cgram[addr & 0x01ff]; + } break; + } + + return 0x00; +} + +void Debugger::write(Debugger::MemorySource::e source, unsigned addr, uint8 data) { + switch(source) { + case MemorySource::CPUBus: { + //do not write to memory-mapped registers that could affect program behavior + if(((addr - 0x2000) & 0x40c000) == 0x000000) break; //$00-3f:2000-5fff MMIO + cartridge.rom.write_protect(false); + bus.write(addr & 0xffffff, data); + cartridge.rom.write_protect(true); + } break; + + case MemorySource::APUBus: { + break; + } + + case MemorySource::APURAM: { + smp.apuram[addr & 0xffff] = data; + } break; + + case MemorySource::VRAM: { + ppu.vram[addr & 0xffff] = data; + } break; + + case MemorySource::OAM: { + if(addr & 0x0200) ppu.oam[0x0200 + (addr & 0x1f)] = data; + else ppu.oam[addr & 0x01ff] = data; + } break; + + case MemorySource::CGRAM: { + ppu.cgram[addr & 0x01ff] = data; + } break; + } +} + +Debugger::Debugger() { + break_event.i = BreakEvent::None; + + for(unsigned n = 0; n < Breakpoints; n++) { + breakpoint[n].enabled = false; + breakpoint[n].addr = 0; + breakpoint[n].data = -1; + breakpoint[n].mode.i = Breakpoint::Mode::Exec; + breakpoint[n].source.i = Breakpoint::Source::CPUBus; + breakpoint[n].counter = 0; + } + breakpoint_hit = 0; +} + +#endif diff --git a/libsnes/snes/debugger/debugger.hpp b/libsnes/snes/debugger/debugger.hpp new file mode 100644 index 0000000..a7f9b22 --- /dev/null +++ b/libsnes/snes/debugger/debugger.hpp @@ -0,0 +1,28 @@ +struct Debugger { + struct BreakEvent{ enum e{ + None, + BreakpointHit, + CPUStep, + SMPStep, + } i; } break_event; + + enum { Breakpoints = 8 }; + struct Breakpoint { + bool enabled; + unsigned addr; + signed data; //-1 = unused + struct Mode{ enum e{ Exec, Read, Write } i; } mode; + struct Source{ enum e{ CPUBus, APURAM, VRAM, OAM, CGRAM } i; } source; + unsigned counter; //number of times breakpoint has been hit since being set + } breakpoint[Breakpoints]; + unsigned breakpoint_hit; + void breakpoint_test(Breakpoint::Source::e source, Breakpoint::Mode::e mode, unsigned addr, uint8 data); + + struct MemorySource{ enum e{ CPUBus, APUBus, APURAM, VRAM, OAM, CGRAM } i; }; + uint8 read(MemorySource::e, unsigned addr); + void write(MemorySource::e, unsigned addr, uint8 data); + + Debugger(); +}; + +extern Debugger debugger; diff --git a/libsnes/snes/dsp/brr.cpp b/libsnes/snes/dsp/brr.cpp new file mode 100644 index 0000000..8552d7a --- /dev/null +++ b/libsnes/snes/dsp/brr.cpp @@ -0,0 +1,62 @@ +#ifdef DSP_CPP + +void DSP::brr_decode(voice_t &v) { + //state.t_brr_byte = ram[v.brr_addr + v.brr_offset] cached from previous clock cycle + int nybbles = (state.t_brr_byte << 8) + smp.apuram[(uint16)(v.brr_addr + v.brr_offset + 1)]; + + const int filter = (state.t_brr_header >> 2) & 3; + const int scale = (state.t_brr_header >> 4); + + //decode four samples + for(unsigned i = 0; i < 4; i++) { + //bits 12-15 = current nybble; sign extend, then shift right to 4-bit precision + //result: s = 4-bit sign-extended sample value + int s = (int16)nybbles >> 12; + nybbles <<= 4; //slide nybble so that on next loop iteration, bits 12-15 = current nybble + + if(scale <= 12) { + s <<= scale; + s >>= 1; + } else { + s &= ~0x7ff; + } + + //apply IIR filter (2 is the most commonly used) + const int p1 = v.buffer[v.buf_pos - 1]; + const int p2 = v.buffer[v.buf_pos - 2] >> 1; + + switch(filter) { + case 0: break; //no filter + + case 1: { + //s += p1 * 0.46875 + s += p1 >> 1; + s += (-p1) >> 5; + } break; + + case 2: { + //s += p1 * 0.953125 - p2 * 0.46875 + s += p1; + s -= p2; + s += p2 >> 4; + s += (p1 * -3) >> 6; + } break; + + case 3: { + //s += p1 * 0.8984375 - p2 * 0.40625 + s += p1; + s -= p2; + s += (p1 * -13) >> 7; + s += (p2 * 3) >> 4; + } break; + } + + //adjust and write sample + s = sclamp<16>(s); + s = (int16)(s << 1); + v.buffer.write(v.buf_pos++, s); + if(v.buf_pos >= brr_buf_size) v.buf_pos = 0; + } +} + +#endif diff --git a/libsnes/snes/dsp/counter.cpp b/libsnes/snes/dsp/counter.cpp new file mode 100644 index 0000000..f65fdd2 --- /dev/null +++ b/libsnes/snes/dsp/counter.cpp @@ -0,0 +1,52 @@ +#ifdef DSP_CPP + +//counter_rate = number of samples per counter event +//all rates are evenly divisible by counter_range (0x7800, 30720, or 2048 * 5 * 3) +//note that rate[0] is a special case, which never triggers + +const uint16 DSP::counter_rate[32] = { + 0, 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, +}; + +//counter_offset = counter offset from zero +//counters do not appear to be aligned at zero for all rates + +const uint16 DSP::counter_offset[32] = { + 0, 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 void DSP::counter_tick() { + state.counter--; + if(state.counter < 0) state.counter = counter_range - 1; +} + +//return true if counter event should trigger + +inline bool DSP::counter_poll(unsigned rate) { + if(rate == 0) return false; + return (((unsigned)state.counter + counter_offset[rate]) % counter_rate[rate]) == 0; +} + +#endif diff --git a/libsnes/snes/dsp/debugger/debugger.cpp b/libsnes/snes/dsp/debugger/debugger.cpp new file mode 100644 index 0000000..b7a5e3b --- /dev/null +++ b/libsnes/snes/dsp/debugger/debugger.cpp @@ -0,0 +1,53 @@ +#ifdef DSP_CPP + +bool DSPDebugger::property(unsigned id, string &name, string &value) { + unsigned n = 0; + + #define item(name_, value_) \ + if(id == n++) { \ + name = name_; \ + value = value_; \ + return true; \ + } + + item("Main Volume - Left", (unsigned)state.regs[0x0c]); + item("Main Volume - Right", (unsigned)state.regs[0x1c]); + item("Echo Volume - Left", (unsigned)state.regs[0x2c]); + item("Echo Volume - Right", (unsigned)state.regs[0x3c]); + item("Key On", string("0x", hex<2>(state.regs[0x4c]))); + item("Key Off", string("0x", hex<2>(state.regs[0x5c]))); + item("Flag - Reset", (bool)(state.regs[0x6c] & 0x80)); + item("Flag - Mute", (bool)(state.regs[0x6c] & 0x40)); + item("Flag - Echo Disable", (bool)(state.regs[0x6c] & 0x20)); + item("Flag - Noise Clock", (unsigned)state.regs[0x6c] & 0x1f); + item("Source End Block", (unsigned)state.regs[0x7c]); + item("Echo Feedback", (unsigned)state.regs[0x0d]); + item("Pitch Modulation Enable", string("0x", hex<2>(state.regs[0x2d]))); + item("Noise Enable", string("0x", hex<2>(state.regs[0x3d]))); + item("Echo Enable", string("0x", hex<2>(state.regs[0x4d]))); + item("Source Directory", (unsigned)state.regs[0x5d]); + item("Echo Start Address", (unsigned)state.regs[0x6d]); + item("Echo Directory", (unsigned)state.regs[0x7d]); + + for(unsigned i = 0; i < 8; i++) { + item(string("Coefficient ", i), string("0x", hex<2>(state.regs[(i << 4) + 0x0f]))); + } + + for(unsigned i = 0; i < 8; i++) { + item(string("Voice ", i), ""); + item("Volume - Left", (unsigned)state.regs[(i << 4) + 0x00]); + item("Volume - Right", (unsigned)state.regs[(i << 4) + 0x01]); + item("Pitch Height", string("0x", hex<4>(state.regs[(i << 4) + 0x02] + (state.regs[(i << 4) + 0x03] << 8)))); + item("Source Number", (unsigned)state.regs[(i << 4) + 0x04]); + item("ADSR1", (unsigned)state.regs[(i << 4) + 0x05]); + item("ADSR2", (unsigned)state.regs[(i << 4) + 0x06]); + item("GAIN", (unsigned)state.regs[(i << 4) + 0x07]); + item("ENVX", (unsigned)state.regs[(i << 4) + 0x08]); + item("OUTX", (unsigned)state.regs[(i << 4) + 0x09]); + } + + #undef item + return false; +} + +#endif diff --git a/libsnes/snes/dsp/debugger/debugger.hpp b/libsnes/snes/dsp/debugger/debugger.hpp new file mode 100644 index 0000000..3339ea3 --- /dev/null +++ b/libsnes/snes/dsp/debugger/debugger.hpp @@ -0,0 +1,4 @@ +class DSPDebugger : public DSP, public ChipDebugger { +public: + bool property(unsigned id, string &name, string &value); +}; diff --git a/libsnes/snes/dsp/dsp.cpp b/libsnes/snes/dsp/dsp.cpp new file mode 100644 index 0000000..f88a9df --- /dev/null +++ b/libsnes/snes/dsp/dsp.cpp @@ -0,0 +1,300 @@ +#include + +#define DSP_CPP +namespace SNES { + +#if defined(DEBUGGER) + #include "debugger/debugger.cpp" + DSPDebugger dsp; +#else + DSP dsp; +#endif + +#include "serialization.cpp" + +#define REG(n) state.regs[r_##n] +#define VREG(n) state.regs[v.vidx + v_##n] + +#include "gaussian.cpp" +#include "counter.cpp" +#include "envelope.cpp" +#include "brr.cpp" +#include "misc.cpp" +#include "voice.cpp" +#include "echo.cpp" + +/* timing */ + +void DSP::step(unsigned clocks) { + clock += clocks; +} + +void DSP::synchronize_smp() { + if(SMP::Threaded == true) { + if(clock >= 0 && scheduler.sync.i != Scheduler::SynchronizeMode::All) co_switch(smp.thread); + } else { + while(clock >= 0) smp.enter(); + } +} + +void DSP::Enter() { dsp.enter(); } + +void DSP::enter() { + while(true) { + if(scheduler.sync.i == Scheduler::SynchronizeMode::All) { + scheduler.exit(Scheduler::ExitReason::SynchronizeEvent); + } + + voice_5(voice[0]); + voice_2(voice[1]); + tick(); + + voice_6(voice[0]); + voice_3(voice[1]); + tick(); + + voice_7(voice[0]); + voice_4(voice[1]); + voice_1(voice[3]); + tick(); + + voice_8(voice[0]); + voice_5(voice[1]); + voice_2(voice[2]); + tick(); + + voice_9(voice[0]); + voice_6(voice[1]); + voice_3(voice[2]); + tick(); + + voice_7(voice[1]); + voice_4(voice[2]); + voice_1(voice[4]); + tick(); + + voice_8(voice[1]); + voice_5(voice[2]); + voice_2(voice[3]); + tick(); + + voice_9(voice[1]); + voice_6(voice[2]); + voice_3(voice[3]); + tick(); + + voice_7(voice[2]); + voice_4(voice[3]); + voice_1(voice[5]); + tick(); + + voice_8(voice[2]); + voice_5(voice[3]); + voice_2(voice[4]); + tick(); + + voice_9(voice[2]); + voice_6(voice[3]); + voice_3(voice[4]); + tick(); + + voice_7(voice[3]); + voice_4(voice[4]); + voice_1(voice[6]); + tick(); + + voice_8(voice[3]); + voice_5(voice[4]); + voice_2(voice[5]); + tick(); + + voice_9(voice[3]); + voice_6(voice[4]); + voice_3(voice[5]); + tick(); + + voice_7(voice[4]); + voice_4(voice[5]); + voice_1(voice[7]); + tick(); + + voice_8(voice[4]); + voice_5(voice[5]); + voice_2(voice[6]); + tick(); + + voice_9(voice[4]); + voice_6(voice[5]); + voice_3(voice[6]); + tick(); + + voice_1(voice[0]); + voice_7(voice[5]); + voice_4(voice[6]); + tick(); + + voice_8(voice[5]); + voice_5(voice[6]); + voice_2(voice[7]); + tick(); + + voice_9(voice[5]); + voice_6(voice[6]); + voice_3(voice[7]); + tick(); + + voice_1(voice[1]); + voice_7(voice[6]); + voice_4(voice[7]); + tick(); + + voice_8(voice[6]); + voice_5(voice[7]); + voice_2(voice[0]); + tick(); + + voice_3a(voice[0]); + voice_9(voice[6]); + voice_6(voice[7]); + echo_22(); + tick(); + + voice_7(voice[7]); + echo_23(); + tick(); + + voice_8(voice[7]); + echo_24(); + tick(); + + voice_3b(voice[0]); + voice_9(voice[7]); + echo_25(); + tick(); + + echo_26(); + tick(); + + misc_27(); + echo_27(); + tick(); + + misc_28(); + echo_28(); + tick(); + + misc_29(); + echo_29(); + tick(); + + misc_30(); + voice_3c(voice[0]); + echo_30(); + tick(); + + voice_4(voice[0]); + voice_1(voice[2]); + tick(); + } +} + +void DSP::tick() { + step(3 * 8); + synchronize_smp(); +} + +/* register interface for S-SMP $00f2,$00f3 */ + +uint8 DSP::read(uint8 addr) { + return state.regs[addr]; +} + +void DSP::write(uint8 addr, uint8 data) { + state.regs[addr] = data; + + if((addr & 0x0f) == v_envx) { + state.envx_buf = data; + } else if((addr & 0x0f) == v_outx) { + state.outx_buf = data; + } else if(addr == r_kon) { + state.new_kon = data; + } else if(addr == r_endx) { + //always cleared, regardless of data written + state.endx_buf = 0; + state.regs[r_endx] = 0; + } +} + +/* initialization */ + +void DSP::power() { + memset(&state.regs, 0, sizeof state.regs); + state.echo_hist_pos = 0; + state.every_other_sample = false; + state.kon = 0; + state.noise = 0; + state.counter = 0; + state.echo_offset = 0; + state.echo_length = 0; + state.new_kon = 0; + state.endx_buf = 0; + state.envx_buf = 0; + state.outx_buf = 0; + state.t_pmon = 0; + state.t_non = 0; + state.t_eon = 0; + state.t_dir = 0; + state.t_koff = 0; + state.t_brr_next_addr = 0; + state.t_adsr0 = 0; + state.t_brr_header = 0; + state.t_brr_byte = 0; + state.t_srcn = 0; + state.t_esa = 0; + state.t_echo_disabled = 0; + state.t_dir_addr = 0; + state.t_pitch = 0; + state.t_output = 0; + state.t_looped = 0; + state.t_echo_ptr = 0; + state.t_main_out[0] = state.t_main_out[1] = 0; + state.t_echo_out[0] = state.t_echo_out[1] = 0; + state.t_echo_in[0] = state.t_echo_in[1] = 0; + + for(unsigned i = 0; i < 8; i++) { + voice[i].buf_pos = 0; + voice[i].interp_pos = 0; + voice[i].brr_addr = 0; + voice[i].brr_offset = 1; + voice[i].vbit = 1 << i; + voice[i].vidx = i * 0x10; + voice[i].kon_delay = 0; + voice[i].env_mode = env_release; + voice[i].env = 0; + voice[i].t_envx_out = 0; + voice[i].hidden_env = 0; + } +} + +void DSP::reset() { + create(Enter, system.apu_frequency); + + REG(flg) = 0xe0; + + state.noise = 0x4000; + state.echo_hist_pos = 0; + state.every_other_sample = 1; + state.echo_offset = 0; + state.counter = 0; +} + +DSP::DSP() { + //-0x8000 <= n <= +0x7fff + assert(sclamp<16>(+0x8000) == +0x7fff); + assert(sclamp<16>(-0x8001) == -0x8000); +} + +DSP::~DSP() { +} + +} diff --git a/libsnes/snes/dsp/dsp.hpp b/libsnes/snes/dsp/dsp.hpp new file mode 100644 index 0000000..9f56103 --- /dev/null +++ b/libsnes/snes/dsp/dsp.hpp @@ -0,0 +1,179 @@ +struct DSP : public Processor { + enum { Threaded = true }; + alwaysinline void step(unsigned clocks); + alwaysinline void synchronize_smp(); + + uint8 read(uint8 addr); + void write(uint8 addr, uint8 data); + + void enter(); + void power(); + void reset(); + + void serialize(serializer&); + DSP(); + ~DSP(); + +private: + //global registers + enum global_reg_t { + r_mvoll = 0x0c, r_mvolr = 0x1c, + r_evoll = 0x2c, r_evolr = 0x3c, + r_kon = 0x4c, r_koff = 0x5c, + r_flg = 0x6c, r_endx = 0x7c, + r_efb = 0x0d, r_pmon = 0x2d, + r_non = 0x3d, r_eon = 0x4d, + r_dir = 0x5d, r_esa = 0x6d, + r_edl = 0x7d, r_fir = 0x0f, //8 coefficients at 0x0f, 0x1f, ... 0x7f + }; + + //voice registers + enum voice_reg_t { + v_voll = 0x00, v_volr = 0x01, + v_pitchl = 0x02, v_pitchh = 0x03, + v_srcn = 0x04, v_adsr0 = 0x05, + v_adsr1 = 0x06, v_gain = 0x07, + v_envx = 0x08, v_outx = 0x09, + }; + + //internal envelope modes + enum env_mode_t { env_release, env_attack, env_decay, env_sustain }; + + //internal constants + enum { echo_hist_size = 8 }; + enum { brr_buf_size = 12 }; + enum { brr_block_size = 9 }; + + //global state + struct state_t { + uint8 regs[128]; + + modulo_array echo_hist[2]; //echo history keeps most recent 8 samples + int echo_hist_pos; + + bool every_other_sample; //toggles every sample + int kon; //KON value when last checked + int noise; + int counter; + int echo_offset; //offset from ESA in echo buffer + int echo_length; //number of bytes that echo_offset will stop at + + //hidden registers also written to when main register is written to + int new_kon; + int endx_buf; + int envx_buf; + int 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 before used + int t_brr_next_addr; + int t_adsr0; + int t_brr_header; + int t_brr_byte; + int t_srcn; + int t_esa; + int t_echo_disabled; + + //internal state that is recalculated every sample + int t_dir_addr; + int t_pitch; + int t_output; + int t_looped; + int t_echo_ptr; + + //left/right sums + int t_main_out[2]; + int t_echo_out[2]; + int t_echo_in [2]; + } state; + + //voice state + struct voice_t { + modulo_array buffer; //decoded samples + int buf_pos; //place in buffer where next samples will be decoded + int interp_pos; //relative fractional position in sample (0x1000 = 1.0) + int brr_addr; //address of current BRR block + int brr_offset; //current decoding offset in BRR block + int vbit; //bitmask for voice: 0x01 for voice 0, 0x02 for voice 1, etc + int vidx; //voice channel register index: 0x00 for voice 0, 0x10 for voice 1, etc + int kon_delay; //KON delay/current setup phase + int env_mode; + int env; //current envelope level + int t_envx_out; + int hidden_env; //used by GAIN mode 7, very obscure quirk + } voice[8]; + + //gaussian + static const int16 gaussian_table[512]; + int gaussian_interpolate(const voice_t &v); + + //counter + enum { counter_range = 2048 * 5 * 3 }; //30720 (0x7800) + static const uint16 counter_rate[32]; + static const uint16 counter_offset[32]; + void counter_tick(); + bool counter_poll(unsigned rate); + + //envelope + void envelope_run(voice_t &v); + + //brr + void brr_decode(voice_t &v); + + //misc + void misc_27(); + void misc_28(); + void misc_29(); + void misc_30(); + + //voice + void voice_output(voice_t &v, bool channel); + void voice_1 (voice_t &v); + void voice_2 (voice_t &v); + void voice_3 (voice_t &v); + void voice_3a(voice_t &v); + void voice_3b(voice_t &v); + void voice_3c(voice_t &v); + void voice_4 (voice_t &v); + void voice_5 (voice_t &v); + void voice_6 (voice_t &v); + void voice_7 (voice_t &v); + void voice_8 (voice_t &v); + void voice_9 (voice_t &v); + + //echo + int calc_fir(int i, bool channel); + int echo_output(bool channel); + void echo_read(bool channel); + void echo_write(bool channel); + void echo_22(); + void echo_23(); + void echo_24(); + void echo_25(); + void echo_26(); + void echo_27(); + void echo_28(); + void echo_29(); + void echo_30(); + + //dsp + static void Enter(); + void tick(); + + friend class DSPDebugger; +}; + +#if defined(DEBUGGER) + #include "debugger/debugger.hpp" + extern DSPDebugger dsp; +#else + extern DSP dsp; +#endif diff --git a/libsnes/snes/dsp/echo.cpp b/libsnes/snes/dsp/echo.cpp new file mode 100644 index 0000000..b80de85 --- /dev/null +++ b/libsnes/snes/dsp/echo.cpp @@ -0,0 +1,135 @@ +#ifdef DSP_CPP + +int DSP::calc_fir(int i, bool channel) { + int s = state.echo_hist[channel][state.echo_hist_pos + i + 1]; + return (s * (int8)REG(fir + i * 0x10)) >> 6; +} + +int DSP::echo_output(bool channel) { + int output = (int16)((state.t_main_out[channel] * (int8)REG(mvoll + channel * 0x10)) >> 7) + + (int16)((state.t_echo_in [channel] * (int8)REG(evoll + channel * 0x10)) >> 7); + return sclamp<16>(output); +} + +void DSP::echo_read(bool channel) { + unsigned addr = state.t_echo_ptr + channel * 2; + uint8 lo = smp.apuram[(uint16)(addr + 0)]; + uint8 hi = smp.apuram[(uint16)(addr + 1)]; + int s = (int16)((hi << 8) + lo); + state.echo_hist[channel].write(state.echo_hist_pos, s >> 1); +} + +void DSP::echo_write(bool channel) { + if(!(state.t_echo_disabled & 0x20)) { + unsigned addr = state.t_echo_ptr + channel * 2; + int s = state.t_echo_out[channel]; + smp.apuram[(uint16)(addr + 0)] = s; + smp.apuram[(uint16)(addr + 1)] = s >> 8; + } + + state.t_echo_out[channel] = 0; +} + +void DSP::echo_22() { + //history + state.echo_hist_pos++; + if(state.echo_hist_pos >= echo_hist_size) state.echo_hist_pos = 0; + + state.t_echo_ptr = (uint16)((state.t_esa << 8) + state.echo_offset); + echo_read(0); + + //FIR + int l = calc_fir(0, 0); + int r = calc_fir(0, 1); + + state.t_echo_in[0] = l; + state.t_echo_in[1] = r; +} + +void DSP::echo_23() { + int l = calc_fir(1, 0) + calc_fir(2, 0); + int r = calc_fir(1, 1) + calc_fir(2, 1); + + state.t_echo_in[0] += l; + state.t_echo_in[1] += r; + + echo_read(1); +} + +void DSP::echo_24() { + int l = calc_fir(3, 0) + calc_fir(4, 0) + calc_fir(5, 0); + int r = calc_fir(3, 1) + calc_fir(4, 1) + calc_fir(5, 1); + + state.t_echo_in[0] += l; + state.t_echo_in[1] += r; +} + +void DSP::echo_25() { + int l = state.t_echo_in[0] + calc_fir(6, 0); + int r = state.t_echo_in[1] + calc_fir(6, 1); + + l = (int16)l; + r = (int16)r; + + l += (int16)calc_fir(7, 0); + r += (int16)calc_fir(7, 1); + + state.t_echo_in[0] = sclamp<16>(l) & ~1; + state.t_echo_in[1] = sclamp<16>(r) & ~1; +} + +void DSP::echo_26() { + //left output volumes + //(save sample for next clock so we can output both together) + state.t_main_out[0] = echo_output(0); + + //echo feedback + int l = state.t_echo_out[0] + (int16)((state.t_echo_in[0] * (int8)REG(efb)) >> 7); + int r = state.t_echo_out[1] + (int16)((state.t_echo_in[1] * (int8)REG(efb)) >> 7); + + state.t_echo_out[0] = sclamp<16>(l) & ~1; + state.t_echo_out[1] = sclamp<16>(r) & ~1; +} + +void DSP::echo_27() { + //output + int outl = state.t_main_out[0]; + int outr = echo_output(1); + state.t_main_out[0] = 0; + state.t_main_out[1] = 0; + + //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 + audio.sample(outl, outr); +} + +void DSP::echo_28() { + state.t_echo_disabled = REG(flg); +} + +void DSP::echo_29() { + state.t_esa = REG(esa); + + if(!state.echo_offset) state.echo_length = (REG(edl) & 0x0f) << 11; + + state.echo_offset += 4; + if(state.echo_offset >= state.echo_length) state.echo_offset = 0; + + //write left echo + echo_write(0); + + state.t_echo_disabled = REG(flg); +} + +void DSP::echo_30() { + //write right echo + echo_write(1); +} + +#endif diff --git a/libsnes/snes/dsp/envelope.cpp b/libsnes/snes/dsp/envelope.cpp new file mode 100644 index 0000000..9ae0d3e --- /dev/null +++ b/libsnes/snes/dsp/envelope.cpp @@ -0,0 +1,62 @@ +#ifdef DSP_CPP + +void DSP::envelope_run(voice_t &v) { + int env = v.env; + + if(v.env_mode == env_release) { //60% + env -= 0x8; + if(env < 0) env = 0; + v.env = env; + return; + } + + int rate; + int env_data = VREG(adsr1); + if(state.t_adsr0 & 0x80) { //99% ADSR + if(v.env_mode >= env_decay) { //99% + env--; + env -= env >> 8; + rate = env_data & 0x1f; + if(v.env_mode == env_decay) { //1% + rate = ((state.t_adsr0 >> 3) & 0x0e) + 0x10; + } + } else { //env_attack + rate = ((state.t_adsr0 & 0x0f) << 1) + 1; + env += rate < 31 ? 0x20 : 0x400; + } + } else { //GAIN + env_data = VREG(gain); + int mode = env_data >> 5; + if(mode < 4) { //direct + env = env_data << 4; + 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 underflowing also triggers this + if((unsigned)env > 0x7ff) { + env = (env < 0 ? 0 : 0x7ff); + if(v.env_mode == env_attack) v.env_mode = env_decay; + } + + if(counter_poll(rate) == true) v.env = env; +} + +#endif diff --git a/libsnes/snes/dsp/gaussian.cpp b/libsnes/snes/dsp/gaussian.cpp new file mode 100644 index 0000000..80aed8a --- /dev/null +++ b/libsnes/snes/dsp/gaussian.cpp @@ -0,0 +1,54 @@ +#ifdef DSP_CPP + +const int16 DSP::gaussian_table[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, +}; + +int DSP::gaussian_interpolate(const voice_t &v) { + //make pointers into gaussian table based on fractional position between samples + int offset = (v.interp_pos >> 4) & 0xff; + const int16 *fwd = gaussian_table + 255 - offset; + const int16 *rev = gaussian_table + offset; //mirror left half of gaussian table + + offset = v.buf_pos + (v.interp_pos >> 12); + int output; + output = (fwd[ 0] * v.buffer[offset + 0]) >> 11; + output += (fwd[256] * v.buffer[offset + 1]) >> 11; + output += (rev[256] * v.buffer[offset + 2]) >> 11; + output = (int16)output; + output += (rev[ 0] * v.buffer[offset + 3]) >> 11; + return sclamp<16>(output) & ~1; +} + +#endif diff --git a/libsnes/snes/dsp/misc.cpp b/libsnes/snes/dsp/misc.cpp new file mode 100644 index 0000000..244fc51 --- /dev/null +++ b/libsnes/snes/dsp/misc.cpp @@ -0,0 +1,35 @@ +#ifdef DSP_CPP + +void DSP::misc_27() { + state.t_pmon = REG(pmon) & ~1; //voice 0 doesn't support PMON +} + +void DSP::misc_28() { + state.t_non = REG(non); + state.t_eon = REG(eon); + state.t_dir = REG(dir); +} + +void DSP::misc_29() { + state.every_other_sample ^= 1; + if(state.every_other_sample) { + state.new_kon &= ~state.kon; //clears KON 63 clocks after it was last read + } +} + +void DSP::misc_30() { + if(state.every_other_sample) { + state.kon = state.new_kon; + state.t_koff = REG(koff); + } + + counter_tick(); + + //noise + if(counter_poll(REG(flg) & 0x1f) == true) { + int feedback = (state.noise << 13) ^ (state.noise << 14); + state.noise = (feedback & 0x4000) ^ (state.noise >> 1); + } +} + +#endif diff --git a/libsnes/snes/dsp/serialization.cpp b/libsnes/snes/dsp/serialization.cpp new file mode 100644 index 0000000..0595c11 --- /dev/null +++ b/libsnes/snes/dsp/serialization.cpp @@ -0,0 +1,66 @@ +#ifdef DSP_CPP + +void DSP::serialize(serializer &s) { + Processor::serialize(s); + + s.array(state.regs, 128); + state.echo_hist[0].serialize(s); + state.echo_hist[1].serialize(s); + s.integer(state.echo_hist_pos); + + s.integer(state.every_other_sample); + s.integer(state.kon); + s.integer(state.noise); + s.integer(state.counter); + s.integer(state.echo_offset); + s.integer(state.echo_length); + + s.integer(state.new_kon); + s.integer(state.endx_buf); + s.integer(state.envx_buf); + s.integer(state.outx_buf); + + s.integer(state.t_pmon); + s.integer(state.t_non); + s.integer(state.t_eon); + s.integer(state.t_dir); + s.integer(state.t_koff); + + s.integer(state.t_brr_next_addr); + s.integer(state.t_adsr0); + s.integer(state.t_brr_header); + s.integer(state.t_brr_byte); + s.integer(state.t_srcn); + s.integer(state.t_esa); + s.integer(state.t_echo_disabled); + + s.integer(state.t_dir_addr); + s.integer(state.t_pitch); + s.integer(state.t_output); + s.integer(state.t_looped); + s.integer(state.t_echo_ptr); + + s.integer(state.t_main_out[0]); + s.integer(state.t_main_out[1]); + s.integer(state.t_echo_out[0]); + s.integer(state.t_echo_out[1]); + s.integer(state.t_echo_in [0]); + s.integer(state.t_echo_in [1]); + + for(unsigned n = 0; n < 8; n++) { + voice[n].buffer.serialize(s); + s.integer(voice[n].buf_pos); + s.integer(voice[n].interp_pos); + s.integer(voice[n].brr_addr); + s.integer(voice[n].brr_offset); + s.integer(voice[n].vbit); + s.integer(voice[n].vidx); + s.integer(voice[n].kon_delay); + s.integer(voice[n].env_mode); + s.integer(voice[n].env); + s.integer(voice[n].t_envx_out); + s.integer(voice[n].hidden_env); + } +} + +#endif diff --git a/libsnes/snes/dsp/voice.cpp b/libsnes/snes/dsp/voice.cpp new file mode 100644 index 0000000..f4fcd8c --- /dev/null +++ b/libsnes/snes/dsp/voice.cpp @@ -0,0 +1,174 @@ +#ifdef DSP_CPP + +inline void DSP::voice_output(voice_t &v, bool channel) { + //apply left/right volume + int amp = (state.t_output * (int8)VREG(voll + channel)) >> 7; + + //add to output total + state.t_main_out[channel] += amp; + state.t_main_out[channel] = sclamp<16>(state.t_main_out[channel]); + + //optionally add to echo total + if(state.t_eon & v.vbit) { + state.t_echo_out[channel] += amp; + state.t_echo_out[channel] = sclamp<16>(state.t_echo_out[channel]); + } +} + +void DSP::voice_1(voice_t &v) { + state.t_dir_addr = (state.t_dir << 8) + (state.t_srcn << 2); + state.t_srcn = VREG(srcn); +} + +void DSP::voice_2(voice_t &v) { + //read sample pointer (ignored if not needed) + uint16 addr = state.t_dir_addr; + if(!v.kon_delay) addr += 2; + uint8 lo = smp.apuram[(uint16)(addr + 0)]; + uint8 hi = smp.apuram[(uint16)(addr + 1)]; + state.t_brr_next_addr = ((hi << 8) + lo); + + state.t_adsr0 = VREG(adsr0); + + //read pitch, spread over two clocks + state.t_pitch = VREG(pitchl); +} + +void DSP::voice_3(voice_t &v) { + voice_3a(v); + voice_3b(v); + voice_3c(v); +} + +void DSP::voice_3a(voice_t &v) { + state.t_pitch += (VREG(pitchh) & 0x3f) << 8; +} + +void DSP::voice_3b(voice_t &v) { + state.t_brr_byte = smp.apuram[(uint16)(v.brr_addr + v.brr_offset)]; + state.t_brr_header = smp.apuram[(uint16)(v.brr_addr)]; +} + +void DSP::voice_3c(voice_t &v) { + //pitch modulation using previous voice's output + + if(state.t_pmon & v.vbit) { + state.t_pitch += ((state.t_output >> 5) * state.t_pitch) >> 10; + } + + if(v.kon_delay) { + //get ready to start BRR decoding on next sample + if(v.kon_delay == 5) { + v.brr_addr = state.t_brr_next_addr; + v.brr_offset = 1; + v.buf_pos = 0; + state.t_brr_header = 0; //header is ignored on this sample + } + + //envelope is never run during KON + v.env = 0; + v.hidden_env = 0; + + //disable BRR decoding until last three samples + v.interp_pos = 0; + v.kon_delay--; + if(v.kon_delay & 3) v.interp_pos = 0x4000; + + //pitch is never added during KON + state.t_pitch = 0; + } + + //gaussian interpolation + int output = gaussian_interpolate(v); + + //noise + if(state.t_non & v.vbit) { + output = (int16)(state.noise << 1); + } + + //apply envelope + state.t_output = ((output * v.env) >> 11) & ~1; + v.t_envx_out = v.env >> 4; + + //immediate silence due to end of sample or soft reset + if(REG(flg) & 0x80 || (state.t_brr_header & 3) == 1) { + v.env_mode = env_release; + v.env = 0; + } + + if(state.every_other_sample) { + //KOFF + if(state.t_koff & v.vbit) { + v.env_mode = env_release; + } + + //KON + if(state.kon & v.vbit) { + v.kon_delay = 5; + v.env_mode = env_attack; + } + } + + //run envelope for next sample + if(!v.kon_delay) envelope_run(v); +} + +void DSP::voice_4(voice_t &v) { + //decode BRR + state.t_looped = 0; + if(v.interp_pos >= 0x4000) { + brr_decode(v); + v.brr_offset += 2; + if(v.brr_offset >= 9) { + //start decoding next BRR block + v.brr_addr = (uint16)(v.brr_addr + 9); + if(state.t_brr_header & 1) { + v.brr_addr = state.t_brr_next_addr; + state.t_looped = v.vbit; + } + v.brr_offset = 1; + } + } + + //apply pitch + v.interp_pos = (v.interp_pos & 0x3fff) + state.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); +} + +void DSP::voice_5(voice_t &v) { + //output right + voice_output(v, 1); + + //ENDX, OUTX and ENVX won't update if you wrote to them 1-2 clocks earlier + state.endx_buf = REG(endx) | state.t_looped; + + //clear bit in ENDX if KON just began + if(v.kon_delay == 5) state.endx_buf &= ~v.vbit; +} + +void DSP::voice_6(voice_t &v) { + state.outx_buf = state.t_output >> 8; +} + +void DSP::voice_7(voice_t &v) { + //update ENDX + REG(endx) = (uint8)state.endx_buf; + state.envx_buf = v.t_envx_out; +} + +void DSP::voice_8(voice_t &v) { + //update OUTX + VREG(outx) = (uint8)state.outx_buf; +} + +void DSP::voice_9(voice_t &v) { + //update ENVX + VREG(envx) = (uint8)state.envx_buf; +} + +#endif diff --git a/libsnes/snes/interface/interface.cpp b/libsnes/snes/interface/interface.cpp new file mode 100644 index 0000000..ce55a6c --- /dev/null +++ b/libsnes/snes/interface/interface.cpp @@ -0,0 +1,29 @@ +#include + +namespace SNES { + +Interface *interface = 0; + +void Interface::videoRefresh(const uint32_t *data, bool hires, bool interlace, bool overscan) { +} + +void Interface::audioSample(int16_t l_sample, int16_t r_sample) { +} + +int16_t Interface::inputPoll(bool port, Input::Device::e device, unsigned index, unsigned id) { + return 0; +} + +void Interface::message(const string &text) { + print(text, "\n"); +} + +time_t Interface::currentTime() { + return time(0); +} + +time_t Interface::randomSeed() { + return time(0); +} + +} diff --git a/libsnes/snes/interface/interface.hpp b/libsnes/snes/interface/interface.hpp new file mode 100644 index 0000000..93d1969 --- /dev/null +++ b/libsnes/snes/interface/interface.hpp @@ -0,0 +1,12 @@ +struct Interface { + virtual void videoRefresh(const uint32_t *data, bool hires, bool interlace, bool overscan); + virtual void audioSample(int16_t lsample, int16_t rsample); + virtual int16_t inputPoll(bool port, Input::Device::e device, unsigned index, unsigned id); + + virtual string path(Cartridge::Slot::e slot, const string &hint) = 0; + virtual void message(const string &text); + virtual time_t currentTime(); + virtual time_t randomSeed(); +}; + +extern Interface *interface; diff --git a/libsnes/snes/libsnes/libsnes.cpp b/libsnes/snes/libsnes/libsnes.cpp new file mode 100644 index 0000000..a55a4b8 --- /dev/null +++ b/libsnes/snes/libsnes/libsnes.cpp @@ -0,0 +1,401 @@ +#define LIBSNES_CORE +#include "libsnes.hpp" +#include + +#include +#include +using namespace nall; + +struct Interface : public SNES::Interface { + snes_video_refresh_t pvideo_refresh; + snes_audio_sample_t paudio_sample; + snes_input_poll_t pinput_poll; + snes_input_state_t pinput_state; + string basename; + uint16_t *buffer; + SNES::Video video; + + void videoRefresh(const uint32_t *data, bool hires, bool interlace, bool overscan) { + unsigned width = hires ? 512 : 256; + unsigned height = overscan ? 239 : 224; + unsigned pitch = 1024 >> interlace; + if(interlace) height <<= 1; + data += 9 * 1024; //skip front porch + + for(unsigned y = 0; y < height; y++) { + const uint32_t *sp = data + y * pitch; + uint16_t *dp = buffer + y * pitch; + for(unsigned x = 0; x < width; x++) { + *dp++ = video.palette[*sp++]; + } + } + + if(pvideo_refresh) pvideo_refresh(buffer, width, height); + if(pinput_poll) pinput_poll(); + } + + void audioSample(int16_t left, int16_t right) { + if(paudio_sample) return paudio_sample(left, right); + } + + int16_t inputPoll(bool port, SNES::Input::Device::e device, unsigned index, unsigned id) { + if(id > 11) return 0; + if(pinput_state) return pinput_state(port, (unsigned)device, index, id); + return 0; + } + + void message(const string &text) { + print(text, "\n"); + } + + string path(SNES::Cartridge::Slot::e slot, const string &hint) { + return string( basename, hint ); + } + + Interface() : pvideo_refresh(0), paudio_sample(0), pinput_poll(0), pinput_state(0) { + buffer = new uint16_t[512 * 480]; + video.generate(SNES::Video::Format::RGB15); + } + + void setCheats(const lstring &list = lstring()) { + if(SNES::cartridge.mode.i == SNES::Cartridge::Mode::SuperGameBoy) { + GameBoy::cheat.reset(); + foreach(code, list) { + lstring codelist; + codelist.split("+", code); + foreach(part, codelist) { + unsigned addr, data, comp; + if(GameBoy::Cheat::decode(part, addr, data, comp)) { + GameBoy::CheatCode code_ = { addr, data, comp }; + GameBoy::cheat.append(code_); + } + } + } + GameBoy::cheat.synchronize(); + return; + } + + SNES::cheat.reset(); + foreach(code, list) { + lstring codelist; + codelist.split("+", code); + foreach(part, codelist) { + unsigned addr, data; + if(SNES::Cheat::decode(part, addr, data)) { + SNES::CheatCode code_ = { addr, data }; + SNES::cheat.append(code_); + } + } + } + SNES::cheat.synchronize(); + } + + ~Interface() { + delete[] buffer; + } +}; + +static Interface interface; +static snes_environment_t environment_cb; + +EXPORT const char* snes_library_id(void) { + static string version("bsnes v085 (", SNES::Info::Profile, ")"); + return version; +} + +EXPORT unsigned snes_library_revision_major(void) { + return 1; +} + +EXPORT unsigned snes_library_revision_minor(void) { + return 3; +} + +EXPORT void snes_set_environment(snes_environment_t environ_cb) { + environment_cb = environ_cb; +} + +EXPORT void snes_set_video_refresh(snes_video_refresh_t video_refresh) { + interface.pvideo_refresh = video_refresh; +} + +EXPORT void snes_set_audio_sample(snes_audio_sample_t audio_sample) { + interface.paudio_sample = audio_sample; +} + +EXPORT void snes_set_input_poll(snes_input_poll_t input_poll) { + interface.pinput_poll = input_poll; +} + +EXPORT void snes_set_input_state(snes_input_state_t input_state) { + interface.pinput_state = input_state; +} + +EXPORT void snes_set_controller_port_device(bool port, unsigned device) { + SNES::input.connect(port, (SNES::Input::Device::e)device); +} + +EXPORT void snes_set_cartridge_basename(const char *basename) { + interface.basename = basename; +} + +EXPORT void snes_init(void) { + SNES::interface = &interface; + SNES::system.init(); + SNES::input.connect(SNES::Controller::Port1, SNES::Input::Device::Joypad); + SNES::input.connect(SNES::Controller::Port2, SNES::Input::Device::Joypad); +} + +EXPORT void snes_term(void) { + SNES::system.term(); +} + +EXPORT void snes_power(void) { + SNES::system.power(); +} + +EXPORT void snes_reset(void) { + SNES::system.reset(); +} + +EXPORT void snes_run(void) { + SNES::system.run(); +} + +EXPORT unsigned snes_serialize_size(void) { + return SNES::system.serialize_size; +} + +EXPORT bool snes_serialize(uint8_t *data, unsigned size) { + SNES::system.runtosave(); + serializer s = SNES::system.serialize(); + if(s.size() > size) return false; + memcpy(data, s.data(), s.size()); + return true; +} + +EXPORT bool snes_unserialize(const uint8_t *data, unsigned size) { + serializer s(data, size); + return SNES::system.unserialize(s); +} + +struct CheatList { + bool enable; + string code; + CheatList() : enable(false) {} +}; + +static linear_vector cheatList; + +EXPORT void snes_cheat_reset(void) { + cheatList.reset(); + interface.setCheats(); +} + +EXPORT void snes_cheat_set(unsigned index, bool enable, const char *code) { + cheatList[index].enable = enable; + cheatList[index].code = code; + lstring list; + for(unsigned n = 0; n < cheatList.size(); n++) { + if(cheatList[n].enable) list.append(cheatList[n].code); + } + interface.setCheats(list); +} + +static void set_timing() { + if (environment_cb) { + snes_system_timing timing = { 0.0, 32040.5 }; + timing.fps = snes_get_region() == SNES_REGION_NTSC ? 21477272.0 / 357366.0 : 21281370.0 / 425568.0; + environment_cb(SNES_ENVIRONMENT_SET_TIMING, &timing); + } +} + +EXPORT bool snes_load_cartridge_normal( + const char *rom_xml, const uint8_t *rom_data, unsigned rom_size +) { + snes_cheat_reset(); + if(rom_data) SNES::cartridge.rom.copy(rom_data, rom_size); + string xmlrom = (rom_xml && *rom_xml) ? string(rom_xml) : SnesCartridge(rom_data, rom_size).markup; + SNES::cartridge.load(SNES::Cartridge::Mode::Normal, xmlrom); + SNES::system.power(); + set_timing(); + return true; +} + +EXPORT bool snes_load_cartridge_bsx_slotted( + const char *rom_xml, const uint8_t *rom_data, unsigned rom_size, + const char *bsx_xml, const uint8_t *bsx_data, unsigned bsx_size +) { + snes_cheat_reset(); + if(rom_data) SNES::cartridge.rom.copy(rom_data, rom_size); + string xmlrom = (rom_xml && *rom_xml) ? string(rom_xml) : SnesCartridge(rom_data, rom_size).markup; + if(bsx_data) SNES::bsxflash.memory.copy(bsx_data, bsx_size); + string xmlbsx = (bsx_xml && *bsx_xml) ? string(bsx_xml) : SnesCartridge(bsx_data, bsx_size).markup; + SNES::cartridge.load(SNES::Cartridge::Mode::BsxSlotted, xmlrom); + SNES::system.power(); + set_timing(); + return true; +} + +EXPORT bool snes_load_cartridge_bsx( + const char *rom_xml, const uint8_t *rom_data, unsigned rom_size, + const char *bsx_xml, const uint8_t *bsx_data, unsigned bsx_size +) { + snes_cheat_reset(); + if(rom_data) SNES::cartridge.rom.copy(rom_data, rom_size); + string xmlrom = (rom_xml && *rom_xml) ? string(rom_xml) : SnesCartridge(rom_data, rom_size).markup; + if(bsx_data) SNES::bsxflash.memory.copy(bsx_data, bsx_size); + string xmlbsx = (bsx_xml && *bsx_xml) ? string(bsx_xml) : SnesCartridge(bsx_data, bsx_size).markup; + SNES::cartridge.load(SNES::Cartridge::Mode::Bsx, xmlrom); + SNES::system.power(); + set_timing(); + return true; +} + +EXPORT bool snes_load_cartridge_sufami_turbo( + const char *rom_xml, const uint8_t *rom_data, unsigned rom_size, + const char *sta_xml, const uint8_t *sta_data, unsigned sta_size, + const char *stb_xml, const uint8_t *stb_data, unsigned stb_size +) { + snes_cheat_reset(); + if(rom_data) SNES::cartridge.rom.copy(rom_data, rom_size); + string xmlrom = (rom_xml && *rom_xml) ? string(rom_xml) : SnesCartridge(rom_data, rom_size).markup; + if(sta_data) SNES::sufamiturbo.slotA.rom.copy(sta_data, sta_size); + string xmlsta = (sta_xml && *sta_xml) ? string(sta_xml) : SnesCartridge(sta_data, sta_size).markup; + if(stb_data) SNES::sufamiturbo.slotB.rom.copy(stb_data, stb_size); + string xmlstb = (stb_xml && *stb_xml) ? string(stb_xml) : SnesCartridge(stb_data, stb_size).markup; + SNES::cartridge.load(SNES::Cartridge::Mode::SufamiTurbo, xmlrom); + SNES::system.power(); + set_timing(); + return true; +} + +EXPORT bool snes_load_cartridge_super_game_boy( + const char *rom_xml, const uint8_t *rom_data, unsigned rom_size, + const char *dmg_xml, const uint8_t *dmg_data, unsigned dmg_size +) { + snes_cheat_reset(); + if(rom_data) SNES::cartridge.rom.copy(rom_data, rom_size); + string xmlrom = (rom_xml && *rom_xml) ? string(rom_xml) : SnesCartridge(rom_data, rom_size).markup; + if(dmg_data) { + //GameBoyCartridge needs to modify dmg_data (for MMM01 emulation); so copy data + uint8_t *data = new uint8_t[dmg_size]; + memcpy(data, dmg_data, dmg_size); + string xmldmg = (dmg_xml && *dmg_xml) ? string(dmg_xml) : GameBoyCartridge(data, dmg_size).markup; + GameBoy::cartridge.load(GameBoy::System::Revision::SuperGameBoy, xmldmg, data, dmg_size); + delete[] data; + } + SNES::cartridge.load(SNES::Cartridge::Mode::SuperGameBoy, xmlrom); + SNES::system.power(); + set_timing(); + return true; +} + +EXPORT void snes_unload_cartridge(void) { + SNES::cartridge.unload(); +} + +EXPORT bool snes_get_region(void) { + return SNES::system.region.i == SNES::System::Region::NTSC ? 0 : 1; +} + +EXPORT uint8_t* snes_get_memory_data(unsigned id) { + if(SNES::cartridge.loaded() == false) return 0; + + switch(id) { + case SNES_MEMORY_CARTRIDGE_RAM: + return SNES::cartridge.ram.data(); + case SNES_MEMORY_CARTRIDGE_RTC: + if(SNES::cartridge.has_srtc()) return SNES::srtc.rtc; + if(SNES::cartridge.has_spc7110rtc()) return SNES::spc7110.rtc; + return 0; + case SNES_MEMORY_BSX_RAM: + if(SNES::cartridge.mode.i != SNES::Cartridge::Mode::Bsx) break; + return SNES::bsxcartridge.sram.data(); + case SNES_MEMORY_BSX_PRAM: + if(SNES::cartridge.mode.i != SNES::Cartridge::Mode::Bsx) break; + return SNES::bsxcartridge.psram.data(); + case SNES_MEMORY_SUFAMI_TURBO_A_RAM: + if(SNES::cartridge.mode.i != SNES::Cartridge::Mode::SufamiTurbo) break; + return SNES::sufamiturbo.slotA.ram.data(); + case SNES_MEMORY_SUFAMI_TURBO_B_RAM: + if(SNES::cartridge.mode.i != SNES::Cartridge::Mode::SufamiTurbo) break; + return SNES::sufamiturbo.slotB.ram.data(); + case SNES_MEMORY_GAME_BOY_RAM: + if(SNES::cartridge.mode.i != SNES::Cartridge::Mode::SuperGameBoy) break; + return GameBoy::cartridge.ramdata; + //case SNES_MEMORY_GAME_BOY_RTC: + // if(SNES::cartridge.mode.i != SNES::Cartridge::Mode::SuperGameBoy) break; + // return GameBoy::cartridge.rtcdata; + + case SNES_MEMORY_WRAM: + return SNES::cpu.wram; + case SNES_MEMORY_APURAM: + return SNES::smp.apuram; + case SNES_MEMORY_VRAM: + return SNES::ppu.vram; + case SNES_MEMORY_OAM: + return SNES::ppu.oam; + case SNES_MEMORY_CGRAM: + return SNES::ppu.cgram; + } + + return 0; +} + +EXPORT unsigned snes_get_memory_size(unsigned id) { + if(SNES::cartridge.loaded() == false) return 0; + unsigned size = 0; + + switch(id) { + case SNES_MEMORY_CARTRIDGE_RAM: + size = SNES::cartridge.ram.size(); + break; + case SNES_MEMORY_CARTRIDGE_RTC: + if(SNES::cartridge.has_srtc() || SNES::cartridge.has_spc7110rtc()) size = 20; + break; + case SNES_MEMORY_BSX_RAM: + if(SNES::cartridge.mode.i != SNES::Cartridge::Mode::Bsx) break; + size = SNES::bsxcartridge.sram.size(); + break; + case SNES_MEMORY_BSX_PRAM: + if(SNES::cartridge.mode.i != SNES::Cartridge::Mode::Bsx) break; + size = SNES::bsxcartridge.psram.size(); + break; + case SNES_MEMORY_SUFAMI_TURBO_A_RAM: + if(SNES::cartridge.mode.i != SNES::Cartridge::Mode::SufamiTurbo) break; + size = SNES::sufamiturbo.slotA.ram.size(); + break; + case SNES_MEMORY_SUFAMI_TURBO_B_RAM: + if(SNES::cartridge.mode.i != SNES::Cartridge::Mode::SufamiTurbo) break; + size = SNES::sufamiturbo.slotB.ram.size(); + break; + case SNES_MEMORY_GAME_BOY_RAM: + if(SNES::cartridge.mode.i != SNES::Cartridge::Mode::SuperGameBoy) break; + size = GameBoy::cartridge.ramsize; + break; + //case SNES_MEMORY_GAME_BOY_RTC: + // if(SNES::cartridge.mode.i != SNES::Cartridge::Mode::SuperGameBoy) break; + // size = GameBoy::cartridge.rtcsize; + // break; + + case SNES_MEMORY_WRAM: + size = 128 * 1024; + break; + case SNES_MEMORY_APURAM: + size = 64 * 1024; + break; + case SNES_MEMORY_VRAM: + size = 64 * 1024; + break; + case SNES_MEMORY_OAM: + size = 544; + break; + case SNES_MEMORY_CGRAM: + size = 512; + break; + } + + if(size == -1U) size = 0; + return size; +} diff --git a/libsnes/snes/libsnes/libsnes.hpp b/libsnes/snes/libsnes/libsnes.hpp new file mode 100644 index 0000000..745e6cd --- /dev/null +++ b/libsnes/snes/libsnes/libsnes.hpp @@ -0,0 +1,1297 @@ +#ifndef LIBSNES_HPP +#define LIBSNES_HPP + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(_MSC_VER) && defined(LIBSNES_CORE) +#define EXPORT __declspec(dllexport) +#else +#define EXPORT +#endif + +/////////////////////////////////////////////////////////////////////////////// +// LIBSNES Super Nintendo emulation API +// +// Things you should know: +// - Linking against libsnes requires a C++ compiler. It can be compiled with +// a C99 compiler if you #include and if your C99 compiler's +// bool type is compatible with the bool type used by the C++ compiler used +// to compile libsnes. +// - libsnes supports exactly one emulated SNES; if you want to run two SNESes +// in a single process, you'll need to link against or dlopen() two +// different copies of the library. +// +// Typical usage of the libsnes API looks like this: +// +// 1. Call snes_init() to initialize the library. +// 2. Tell libsnes which callback should be called for each event (see the +// documentation on the individual callback types below. +// 3. Call one of the snes_load_cartridge_* functions to load cartridge data +// into the emulated SNES. +// 4. If the physical cart had any non-volatile storage, there may be data from +// a previous emulation run that needs to be loaded. Find the storage buffer +// by calling the snes_get_memory_* functions and load any saved data into +// it. +// 5. Call snes_set_controller_port_device() to connect appropriate controllers +// to the emulated SNES. +// 6. Call snes_get_region() to determine the intended screen refresh rate for +// this cartridge.. +// 7. Call snes_run() to emulate a single frame. Before snes_run() returns, the +// installed callbacks will be called - possibly multiple times. +// 8. When you're done, call snes_term() to free all memory allocated +// associated with the emulated SNES. +// +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// Constants {{{ + +// These constants represent the two controller ports on the front of the SNES, +// for use with the snes_set_controller_port_device() function and the +// snes_input_state_t callback. +#define SNES_PORT_1 0 +#define SNES_PORT_2 1 + +// These constants represent the different kinds of controllers that can be +// connected to a controller port, for use with the +// snes_set_controller_port_device() function and the snes_input_state_t +// callback. +#define SNES_DEVICE_NONE 0 +#define SNES_DEVICE_JOYPAD 1 +#define SNES_DEVICE_MULTITAP 2 +#define SNES_DEVICE_MOUSE 3 +#define SNES_DEVICE_SUPER_SCOPE 4 +#define SNES_DEVICE_JUSTIFIER 5 +#define SNES_DEVICE_JUSTIFIERS 6 +#define SNES_DEVICE_SERIAL_CABLE 7 + +// These constants represent the button and axis inputs on various controllers, +// for use with the snes_input_state_t callback. +#define SNES_DEVICE_ID_JOYPAD_B 0 +#define SNES_DEVICE_ID_JOYPAD_Y 1 +#define SNES_DEVICE_ID_JOYPAD_SELECT 2 +#define SNES_DEVICE_ID_JOYPAD_START 3 +#define SNES_DEVICE_ID_JOYPAD_UP 4 +#define SNES_DEVICE_ID_JOYPAD_DOWN 5 +#define SNES_DEVICE_ID_JOYPAD_LEFT 6 +#define SNES_DEVICE_ID_JOYPAD_RIGHT 7 +#define SNES_DEVICE_ID_JOYPAD_A 8 +#define SNES_DEVICE_ID_JOYPAD_X 9 +#define SNES_DEVICE_ID_JOYPAD_L 10 +#define SNES_DEVICE_ID_JOYPAD_R 11 + +#define SNES_DEVICE_ID_MOUSE_X 0 +#define SNES_DEVICE_ID_MOUSE_Y 1 +#define SNES_DEVICE_ID_MOUSE_LEFT 2 +#define SNES_DEVICE_ID_MOUSE_RIGHT 3 + +#define SNES_DEVICE_ID_SUPER_SCOPE_X 0 +#define SNES_DEVICE_ID_SUPER_SCOPE_Y 1 +#define SNES_DEVICE_ID_SUPER_SCOPE_TRIGGER 2 +#define SNES_DEVICE_ID_SUPER_SCOPE_CURSOR 3 +#define SNES_DEVICE_ID_SUPER_SCOPE_TURBO 4 +#define SNES_DEVICE_ID_SUPER_SCOPE_PAUSE 5 + +#define SNES_DEVICE_ID_JUSTIFIER_X 0 +#define SNES_DEVICE_ID_JUSTIFIER_Y 1 +#define SNES_DEVICE_ID_JUSTIFIER_TRIGGER 2 +#define SNES_DEVICE_ID_JUSTIFIER_START 3 + +// These constants will be returned by snes_get_region(), representing the +// region of the last loaded cartridge. +#define SNES_REGION_NTSC 0 +#define SNES_REGION_PAL 1 + +// These constants represent the kinds of non-volatile memory a SNES cartridge +// might have, for use with the snes_get_memory_* functions. +#define SNES_MEMORY_CARTRIDGE_RAM 0 +#define SNES_MEMORY_CARTRIDGE_RTC 1 +#define SNES_MEMORY_BSX_RAM 2 +#define SNES_MEMORY_BSX_PRAM 3 +#define SNES_MEMORY_SUFAMI_TURBO_A_RAM 4 +#define SNES_MEMORY_SUFAMI_TURBO_B_RAM 5 +#define SNES_MEMORY_GAME_BOY_RAM 6 +#define SNES_MEMORY_GAME_BOY_RTC 7 + +// These constants represent the various kinds of volatile storage the SNES +// offers, to allow libsnes clients to implement things like cheat-searching +// and certain kinds of debugging. They are for use with the snes_get_memory_* +// functions. +#define SNES_MEMORY_WRAM 100 +#define SNES_MEMORY_APURAM 101 +#define SNES_MEMORY_VRAM 102 +#define SNES_MEMORY_OAM 103 +#define SNES_MEMORY_CGRAM 104 + +// SSNES extension. Not required to be implemented for a working implementation. +#define SNES_ENVIRONMENT_GET_FULLPATH 0 // const char ** -- + // Full path of game loaded. + // +#define SNES_ENVIRONMENT_SET_GEOMETRY 1 // const struct snes_geometry * -- + // Window geometry information for the system/game. + // +#define SNES_ENVIRONMENT_SET_PITCH 2 // const unsigned * -- + // Pitch of game image. + // +#define SNES_ENVIRONMENT_GET_OVERSCAN 3 // bool * -- + // Boolean value whether or not the implementation should use overscan. + // +#define SNES_ENVIRONMENT_SET_TIMING 4 // const struct snes_system_timing * -- + // Set exact timings of the system. Used primarily for video recording. + // +#define SNES_ENVIRONMENT_GET_CAN_DUPE 5 // bool * -- + // Boolean value whether or not SSNES supports frame duping, + // passing NULL to video frame callback. + // + // +#define SNES_ENVIRONMENT_SET_NEED_FULLPATH 6 // const bool * -- + // Boolean value telling if implementation needs a valid fullpath to be able to run. + // If this is the case, SSNES will not open the rom directly, + // and pass NULL to rom data. + // Implementation must then use SNES_ENVIRONMENT_GET_FULLPATH. + // This is useful for implementations with very large roms, + // which are impractical to load fully into RAM. + // +#define SNES_ENVIRONMENT_GET_CAN_REWIND 7 // bool * -- + // Boolean value telling if SSNES is able to rewind. + // Some implementations might need to take extra precautions + // to allow this as smoothly as possible. + +struct snes_geometry +{ + unsigned base_width; // Nominal video width of system. + unsigned base_height; // Nominal video height of system. + unsigned max_width; // Maximum possible width of system. + unsigned max_height; // Maximum possible height of system. +}; + +struct snes_system_timing +{ + double fps; + double sample_rate; +}; + +typedef bool (*snes_environment_t)(unsigned cmd, void *data); + +// Must be called before calling snes_init(). +void snes_set_environment(snes_environment_t); +//// + + +////////////////////////////////////////////////////////////////////////////}}} + +/////////////////////////////////////////////////////////////////////////////// +// Callback types {{{ +// +// In order to deliver controller input to the emulated SNES, and retrieve +// video frames and audio samples, you will need to register callbacks. + +// snes_audio_sample_t: +// +// This callback delivers a stereo audio sample pair generated by the +// emulated SNES. +// +// This function is called once for every audio frame (one sample from left +// and right channels). The SNES generates audio samples at a rate of about +// 32040Hz (varies from unit to unit). +// +// Because the SNES generates video at exactly 59.94fps and most computer +// monitors only support a 60fps refresh rate, real-time emulation needs to +// run slightly fast so that each computer frame displays one emulated SNES +// frame. Because the emulation runs slightly fast, and because most +// consumer audio hardware does not play audio at precisely the requested +// sample rate, you'll likely need to let the end-user tweak the effective +// sample rate by 100Hz or so in either direction. +// +// Although the parameters are declared as unsigned for historical reasons, +// the data they contain is actually signed. To work with the audio (e.g. +// resample), you will need to reinterpret the sample value: +// +// int16_t real_left = *(int16_t*)(&left); +// +// Parameters: +// +// left: +// A signed 16-bit integer containing the next audio sample from the +// left audio channel. Yes, it's declared as unsigned for historical +// reasons. +// +// right: +// A signed 16-bit integer containing the next audio sample from the +// right audio channel. Yes, it's declared as unsigned for historical +// reasons. +// + +typedef void (*snes_audio_sample_t)(uint16_t left, uint16_t right); + + +// snes_video_refresh_t: +// +// This callback delivers a single SNES frame, generated by the emulated +// SNES. The same memory buffer may be re-used later, so take a copy of the +// data if you want to refer to it after your callback returns. +// +// The framebuffer is an array of unsigned 16-bit pixels, in a somewhat +// complicated format. A quick refresher on SNES video modes: +// - The basic SNES video-mode renders 256 pixels per scanline for a total +// of 224 scanlines. +// - When "overscan" mode is enabled, the SNES renders a few extra +// scanlines at the end of the frame, for a total of 239 scanlines. +// - When "hi-res" mode is enabled, the SNES speeds up its pixel rendering +// to fit 512 pixels per scanline. +// - Normally the SNES renders its pixels to one field of the interlaced +// NTSC signal, but if "interlaced" mode is enabled the SNES renders +// a second set of scanlines inbetween the regular set, for a total of +// 448 (normal) or 478 (overscan) scanlines. +// +// Thus, the framebuffer memory layout for a standard 256x240 frame looks +// something like this (note that 'height' has been reduced to 4 or 8 for +// these examples): +// +// 0 1024b +// ,---------------------------------------. +// |====== width ======|...................| -. +// |.......................................| | +// |===================|...................| | +// |.......................................| +- height = 4 +// |===================|...................| | +// |.......................................| | +// |===================|...................| | +// |.......................................| -' +// `---------------------------------------' +// +// A hi-res frame would look like this: +// +// 0 1024b +// ,---------------------------------------. +// |================ width ================| -. +// |.......................................| | +// |=======================================| | +// |.......................................| +- height = 4 +// |=======================================| | +// |.......................................| | +// |=======================================| | +// |.......................................| -' +// `---------------------------------------' +// +// An interlaced frame would look like this: +// +// 0 1024b +// ,---------------------------------------. +// |====== width ======|...................| -. +// |===================|...................| | +// |===================|...................| | +// |===================|...................| +- height = 8 +// |===================|...................| | +// |===================|...................| | +// |===================|...................| | +// |===================|...................| -' +// `---------------------------------------' +// +// And of course a hi-res, interlaced frame would look like this: +// +// 0 1024b +// ,---------------------------------------. +// |================ width ================| -. +// |=======================================| | +// |=======================================| | +// |=======================================| |+- height = 8 +// |=======================================| | +// |=======================================| | +// |=======================================| | +// |=======================================| -' +// `---------------------------------------' +// +// More succinctly: +// - the buffer begins at the top-left of the frame +// - the first "width" bytes contain the first scanline. +// - if the emulated SNES is in an interlaced video-mode (that is, if the +// "height" parameter" is 448 or 478) then the second scanline begins at +// an offset of 1024 bytes (512 pixels) after the first. +// - otherwise the second scanline begins at an offset of 2048 bytes (1024 +// pixels) after the first. +// - there are "height" scanlines in total. +// +// Each pixel contains a 15-bit RGB tuple: 0RRRRRGGGGGBBBBB (XRGB1555) +// +// Example code: +// +// void pack_frame (uint16_t * restrict out, const uint16_t * restrict in, +// unsigned width, unsigned height) +// { +// // Normally our pitch is 2048 bytes. +// int pitch_pixels = 1024; +// // If we have an interlaced mode, pitch is 1024 bytes. +// if ( height == 448 || height == 478 ) +// pitch_pixels = 512; +// +// for ( int y = 0; y < height; y++ ) +// { +// const uint16_t *src = in + y * pitch_pixels; +// uint16_t *dst = out + y * width; +// +// memcpy(dst, src, width * sizeof(uint16_t)); +// } +// } +// +// Parameters: +// +// data: +// a pointer to the beginning of the framebuffer described above. +// +// width: +// the width of the frame, in pixels. +// +// height: +// the number of scanlines in the frame. + +typedef void (*snes_video_refresh_t)(const uint16_t *data, unsigned width, + unsigned height); + +// snes_input_poll_t: +// +// This callback requests that you poll your input devices for events, if +// required. +// +// Generally called once per frame before the snes_input_state_t callback is +// called. +// + +typedef void (*snes_input_poll_t)(void); + +// snes_input_state_t: +// +// This callback asks for information about the state of a particular input. +// +// The callback may be called multiple times per frame with the same +// parameters. +// +// The callback might not be called at all, if the software running in the +// emulated SNES does not try to probe the controllers. +// +// The callback will not be called for a particular port if DEVICE_NONE is +// connected to it. +// +// If you wish to emulate any kind of turbo-fire, etc. then you will need to +// put that logic into this callback. +// +// Parameters: +// +// port: +// One of the constants SNES_PORT_1 or SNES_PORT_2, describing which +// controller port you should report. +// +// device: +// One of the SNES_DEVICE_* constants describing which type of device +// is currently connected to the given port. +// +// index: +// A number describing which of the devices connected to the port is +// being reported. It's only useful for SNES_DEVICE_MULTITAP and +// SNES_DEVICE_JUSTIFIERS - for other device types, this parameter is +// always 0. +// +// id: +// One of the SNES_DEVICE_ID_* constants for the given device, +// describing which button or axis is being reported (for +// SNES_DEVICE_MULTITAP, use the SNES_DEVICE_ID_JOYPAD_* IDs; for +// SNES_DEVICE_JUSTIFIERS use the SNES_DEVICE_ID_JUSTIFIER_* IDs.). +// +// Returns: +// +// An integer representing the state of the described button or axis. +// +// - If it represents a digital input such as SNES_DEVICE_ID_JOYPAD_B or +// SNES_DEVICE_ID_MOUSE_LEFT), return 1 if the button is pressed, and +// 0 otherwise. +// - If "id" is SNES_DEVICE_ID_MOUSE_X or SNES_DEVICE_ID_MOUSE_Y then +// return the relative movement of the mouse during the current frame; +// values outside the range -127 to +127 will be clamped. +// - If "id" is one of the light-gun axes (such as +// SNES_DEVICE_ID_JUSTIFIER_Y or SNES_DEVICE_ID_SUPER_SCOPE_X), you +// should return the relative movement of the pointing device during the +// current frame. + +typedef int16_t (*snes_input_state_t)(bool port, unsigned device, + unsigned index, unsigned id); + +////////////////////////////////////////////////////////////////////////////}}} + +/////////////////////////////////////////////////////////////////////////////// +// libsnes setup {{{ +// +// These functions are used to get information about and manipulate the libsnes +// library itself, not the emulated SNES it implements. + +// snes_library_id: +// +// Returns a human readable string describing this libsnes implementation. +// It is not supposed to be parsed or used in any other way than being +// printed to screen on request by user or otherwise. +// +// Returns: +// +// A human-readable string describing this implementation. + +EXPORT const char* snes_library_id(void); + + +// snes_library_revision_major: +// +// Returns the major API version of this libsnes implementation. +// +// This number is increased every time there is a compatibility-breaking +// change to the libsnes API. At startup, your program should call this +// function and compare the return value to the major API version the +// program was designed to work with. If they are different, your program +// will (very likely) not work with this libsnes implementation. +// +// For example, if your program was designed to work with the libsnes API +// whose major.minor revision was 1.5, and this function returns a major +// version of 2, you have a problem. +// +// Returns: +// +// An integer, the major API version of this libsnes implementation. + +EXPORT unsigned snes_library_revision_major(void); + +// snes_library_revision_minor: +// +// Returns the minor API version of this libsnes implementation. +// +// This number is increased every time there is a backwards-compatible +// change to the libsnes API. At startup, your program should call this +// function and compare the return value to the minor API version the +// program was designed to work with. If the return value is less than the +// expected minor version, your program will (very likely) not work with +// this libsnes implementation. +// +// For example, if your program was designed to work with the libsnes API +// whose major.minor revision was 1.5, and this libsnes implementation's +// major.minor version is 1.3, it's probably missing features you require. +// On the other hand, if this libsnes implementation's major.minor version +// is 1.9, it probably has extra fancy features you don't need to worry +// about. +// +// Returns: +// +// An integer, the minor API version of this libsnes implementation. + +EXPORT unsigned snes_library_revision_minor(void); + +// snes_init: +// +// Initializes the libsnes implementation. +// +// This function must be called exactly once before any other library +// functions are called. + +EXPORT void snes_init(void); + +// snes_term: +// +// Shuts down the libsnes implementation. +// +// This function must be called exactly once. Once called, you should not +// call any other libsnes functions besides (perhaps) snes_init(). + +EXPORT void snes_term(void); + +////////////////////////////////////////////////////////////////////////////}}} + +/////////////////////////////////////////////////////////////////////////////// +// Callback registration {{{ +// +// Note that all callbacks should be set up before snes_run() is called for the +// first time. + +// snes_set_video_refresh: +// +// Sets the callback that will receive new video frames. +// +// See the documentation for snes_video_refresh_t for details. +// +// Parameters: +// +// A pointer to a function matching the snes_video_refresh_t call +// signature. + +EXPORT void snes_set_video_refresh(snes_video_refresh_t); + +// snes_set_audio_sample +// +// Sets the callback that will receive new audio sample pairs. +// +// See the documentation for snes_audio_sample_t for details. +// +// Parameters: +// +// A pointer to a function matching the snes_audio_sample_t call +// signature. + +EXPORT void snes_set_audio_sample(snes_audio_sample_t); + +// snes_set_input_poll: +// +// Sets the callback that will be notified to poll input devices. +// +// See the documentation for snes_input_poll_t for details. +// +// Parameters: +// +// A pointer to a function matching the snes_input_poll_t call signature. + +EXPORT void snes_set_input_poll(snes_input_poll_t); + +// snes_set_input_state: +// +// Sets the callback that will be used to read input device state. +// +// See the documentation for snes_input_state_t for details. +// +// Parameters: +// +// A pointer to a function matching the snes_input_state_t call signature. + +EXPORT void snes_set_input_state(snes_input_state_t); + +////////////////////////////////////////////////////////////////////////////}}} + +/////////////////////////////////////////////////////////////////////////////// +// SNES operation {{{ +// +// Functions for manipulating the emulated SNES. + +// snes_set_controller_port_device: +// +// Sets the input device connected to a given controller port. +// +// Connecting a device to a port implicitly removes any device previously +// connected to that port. To remove a device without connecting a new one, +// pass DEVICE_NONE as the device parameter. From this point onward, the +// callback passed to set_input_state_cb() will be called with the +// appropriate device, index and id parameters. +// +// If this function is never called, the default is to have a DEVICE_JOYPAD +// connected to both ports. +// +// Calling this callback from inside the set_input_state_cb() has undefined +// results, so don't do that. +// +// Parameters: +// +// port: +// One of the constants SNES_PORT_1 or SNES_PORT_2, describing which +// controller port is being configured. +// +// device: +// One of the SNES_DEVICE_* constants describing which type of device +// should be connected to the given port. Note that some devices can +// only be connected to SNES_PORT_2. Attempting to connect +// a port-2-only device to SNES_PORT_1 has undefined results. +// +// These devices work in either port: +// - SNES_DEVICE_NONE: No device is connected to this port. +// - SNES_DEVICE_JOYPAD: A standard SNES gamepad. +// - SNES_DEVICE_MULTITAP: A multitap controller, which acts like +// 4 SNES_DEVICE_JOYPADs. Your input state callback will be +// passed "id" parameters between 0 and 3, inclusive. +// - SNES_DEVICE_MOUSE: A SNES mouse controller, as shipped with +// Mario Paint. +// +// These devices only work properly when connected to port 2: +// - SNES_DEVICE_SUPER_SCOPE: A Nintendo Super Scope light-gun +// device. +// - SNES_DEVICE_JUSTIFIER: A Konami Justifier light-gun device. +// - SNES_DEVICE_JUSTIFIERS: Two Konami Justifier light-gun +// devices, daisy-chained together. Your input state callback +// will be passed "id" parameters 0 and 1. + +EXPORT void snes_set_controller_port_device(bool port, unsigned device); + +// snes_power: +// +// Turns the emulated console off and back on. +// +// This functionality is sometimes called "hard reset" and guarantees that +// all hardware state is reset to a reasonable default. +// +// Before bsnes v070r07, this resets the controller ports to both contain +// SNES_DEVICE_JOYPADs. +// +// This requires that a cartridge is loaded. + +EXPORT void snes_power(void); + +// snes_reset: +// +// Presses the "reset" button on the emulated SNES. +// +// This functionality is sometimes called "soft reset". Most hardware state +// is reset to a reasonable befault, but not all. +// +// As of bsnes v073r01, this function (as a side-effect) resets the +// controller ports to both contain SNES_DEVICE_JOYPADs. +// +// This requires that a cartridge is loaded. + +EXPORT void snes_reset(void); + +// snes_run(): +// +// Runs the emulated SNES until the end of the next video frame. +// +// Usually causes each registered callback to be called before returning. +// +// This function will run as fast as possible. It is up to the caller to +// make sure that the game runs at the intended speed. +// +// For optimal A/V sync, make sure that the audio callback never blocks for +// longer than a frame (approx 16ms for NTSC, 20ms for PAL) +// +// Optimally, it should never block for more than a few ms at a time. + +EXPORT void snes_run(void); + +// snes_get_region(): +// +// Determines the intended frame-rate of the loaded cartridge. +// +// The two main SNES hardware variants are the US/Japan variant, designed +// for NTSC output, and the European variant, designed for PAL output. +// However, the world is not quite so tidy as that, and there are countries +// like Brazil that use PAL output at NTSC frame-rates. +// +// For historical reasons this function is named snes_get_region(), but +// effectively the only information you can reliably infer is the +// frame-rate. +// +// Returns: +// +// One of the SNES_REGION_* constants. SNES_REGION_PAL means 50fps, +// SNES_REGION_NTSC means 60fps. + +EXPORT bool snes_get_region(void); + +////////////////////////////////////////////////////////////////////////////}}} + +/////////////////////////////////////////////////////////////////////////////// +// Save state support {{{ +// +// libsnes has the ability to save the current emulation state and restore it +// at a later time. +// +// Note 1: It is impossible to reliably restore the *exact* state, although the +// difference is only a few cycles. If you demand the ability to reliably +// restore state, call snes_serialize() after each frame to ensure the emulated +// SNES is in a state that can be reliably restored. +// +// Note 2: The save state information is specific to a particular cartridge +// loaded into a particular version of a particular libsnes implementation. +// Unfortunately, there is not yet a way to determine whether a given save +// state is compatible with a given libsnes implementation, other than by +// loading it. However, if snes_serialize_size() does not match the size of an +// old save state, that's a strong hint that something has incompatibly +// changed. + +// snes_serialize_size: +// +// Determines the minimum size of a save state. +// +// This value can change depending on the features used by the loaded +// cartridge, and the version of the libsnes implementation used. +// +// Returns: +// +// An integer representing the number of bytes required to store the +// current emulation state. + +EXPORT unsigned snes_serialize_size(void); + +// snes_serialize: +// +// Serialize the current emulation state to a buffer. +// +// If the allocated buffer is smaller than the size returned by +// snes_serialize_size(), serialization will fail. If the allocated buffer +// is larger, only the first snes_serialize_size() bytes will be written to. +// +// The resulting buffer may be stored, and later passed to +// snes_unserialize() to restore the saved emulation state. +// +// Parameters: +// +// data: +// A pointer to an allocated buffer of memory. +// +// size: +// The size of the buffer pointed to by "data". Should be greater than +// or equal to the value returned by snes_serialize_size(). +// +// Returns: +// +// A boolean; True means the emulation state was serialized successfully, +// False means a problem was encountered. + +EXPORT bool snes_serialize(uint8_t *data, unsigned size); + +// snes_unserialize: +// +// Unserialize the emulation state from a buffer. +// +// If the serialization data in the buffer does not appear to be compatible +// with the current libsnes implementation, the function returns False and +// the current emulation state is not modified. +// +// Parameters: +// +// data: +// A pointer to an allocated buffer of memory. +// +// size: +// The size of the buffer pointed to by "data". Should be greater than +// or equal to the value returned by snes_serialize_size(). +// +// Returns: +// +// A boolean; True means the emulation state was loaded successfully, +// False means a problem was encountered. + +EXPORT bool snes_unserialize(const uint8_t *data, unsigned size); + +////////////////////////////////////////////////////////////////////////////}}} + +/////////////////////////////////////////////////////////////////////////////// +// Cheat support {{{ +// +// libsnes does not include any kind of cheat management API; the intention is +// that any change to the set of applied cheats will cause the containing +// application to call snes_cheat_reset() then apply the new set of cheats with +// snes_cheat_set(). +// +// Any currently-applied cheats are discarded when a new cartridge is loaded. + +// snes_cheat_reset: +// +// Discards all cheat codes applied to the emulated SNES. + +EXPORT void snes_cheat_reset(void); + +// snes_cheat_set: +// +// Apply a sequence of cheat codes to the emulated SNES. +// +// Since a "cheat code" is basically an instruction to override the value of +// a particular byte in the SNES' memory, more complex cheats may require +// several individual codes applied at once. There's no effective difference +// between applying these codes in a group with one call to +// snes_cheat_set(), or applying them one at a time with individual calls. +// However, most cheat databases will have a collection of available cheats +// for each game, where each item in the collection has a description and +// a sequence of codes to be applied as a unit. This API makes it easy to +// present the list of descriptions to the user, and apply each cheat the +// user selects. +// +// Parameters: +// +// index: +// The given cheat code will be stored at this index in the array of +// applied cheats. If a cheat already exists at this location, it will +// be replaced by the new cheat. If the index is larger than any +// previously specififed index, the array will be resized to +// accommodate. +// +// enabled: +// True means that the cheat will actually be applied, False means +// that the cheat will have no effect. There is no way to enable or +// disable a cheat after it has been added, other than to call +// snes_cheat_set() a second time with the same values for "index" and +// "code". +// +// code: +// A string containing a sequence of cheat codes separated by '+' +// characters. Any spaces in the string will be removed before +// parsing. +// +// Each code in the sequence must be in either GameGenie format +// ("1234-ABCD") or ProActionReplay format ("1234AB:CD" or +// "1234ABCD"). + +EXPORT void snes_cheat_set(unsigned index, bool enabled, const char *code); + +////////////////////////////////////////////////////////////////////////////}}} + +/////////////////////////////////////////////////////////////////////////////// +// Cartridge loading and unloading {{{ +// +// Before calling snes_run(), a cartridge must be loaded into the emulated SNES +// so that it has code to run. +// +// Loading a cartridge of any kind calls snes_cheat_reset() as a side-effect. + +// snes_load_cartridge_normal: +// +// Load a normal ROM image into the emulated SNES. +// +// Parameters: +// +// rom_xml: +// A pointer to a null-terminated string containing an XML memory map +// that describes where the ROM image is mapped into the SNES address +// space, what special chips it uses (and where they're mapped), etc. +// +// If NULL, libsnes will guess a memory map. The guessed memory map +// should be correct for all licenced games in all regions. +// +// rom_data: +// A pointer to a byte array containing the uncompressed, +// de-interleaved, headerless ROM image. +// +// rom_size: +// The length of the rom_data array, in bytes. +// +// Returns: +// +// A boolean; True means the cartridge was loaded correctly, False means +// an error occurred. + +EXPORT bool snes_load_cartridge_normal( + const char *rom_xml, const uint8_t *rom_data, unsigned rom_size +); + +// snes_load_cartridge_bsx: +// +// Load a BS-X base cart image, optionally with a memory pack. +// +// The Satellaview system, abbreviated "BS-X" for unclear reasons, was an +// addon for the Super Famicom that connected it to the St. GIGA satellite +// network. The network would broadcast games at a particular time, and +// users could download them to replaceable memory packs. +// +// For more information, see http://en.wikipedia.org/wiki/Satellaview +// +// Parameters: +// +// rom_xml: +// A pointer to a null-terminated string containing an XML memory map +// that describes where the BS-X base cartridge ROM image is mapped +// into the SNES address space. +// +// If NULL, libsnes will guess a memory map. The guessed memory map +// should be correct for all known BS-X base cartridge images. +// +// rom_data: +// A pointer to a byte array containing the uncompressed, +// de-interleaved, headerless ROM image of the BS-X base cartridge. +// +// The BS-X base cartridge is named "BS-X - Sore wa Namae o Nusumareta +// Machi no Monogatari" in some SNES game databases. +// +// rom_size: +// The length of the rom_data array, in bytes. +// +// bsx_xml: +// A pointer to a null-terminated string containing an XML memory map +// that describes the BS-X memory pack. +// +// This parameter is currently ignored and should be passed as NULL. +// +// bsx_data: +// A pointer to a byte array containing the uncompressed, +// de-interleaved, headerless image of the BS-X memory-pack. +// +// If NULL, libsnes will behave as though no memory-pack were inserted +// into the base cartridge. +// +// bsx_size: +// The length of the bsx_data array, in bytes. +// +// Returns: +// +// A boolean; True means the cartridge was loaded correctly, False means +// an error occurred. + +EXPORT bool snes_load_cartridge_bsx( + const char *rom_xml, const uint8_t *rom_data, unsigned rom_size, + const char *bsx_xml, const uint8_t *bsx_data, unsigned bsx_size +); + +// snes_load_cartridge_bsx_slotted: +// +// Load a BS-X slotted cartridge, optionally with a memory pack. +// +// A BS-X slotted cartridge is an ordinary SNES cartridge, with a slot in +// the top that accepts the same memory packs used by the BS-X base +// cartridge. +// +// Parameters: +// +// rom_xml: +// A pointer to a null-terminated string containing an XML memory map +// that describes where the ROM image is mapped into the SNES address +// space, what special chips it uses (and where they're mapped), etc. +// +// If NULL, libsnes will guess a memory map. The guessed memory map +// should be correct for all licenced games in all regions. +// +// rom_data: +// A pointer to a byte array containing the uncompressed, +// de-interleaved, headerless ROM image. +// +// rom_size: +// The length of the rom_data array, in bytes. +// +// bsx_xml: +// A pointer to a null-terminated string containing an XML memory map +// that describes the BS-X memory pack. +// +// This parameter is currently ignored and should be passed as NULL. +// +// bsx_data: +// A pointer to a byte array containing the uncompressed, +// de-interleaved, headerless image of the BS-X memory-pack. +// +// If NULL, libsnes will behave as though no memory-pack were inserted +// into the base cartridge. +// +// bsx_size: +// The length of the bsx_data array, in bytes. +// +// Returns: +// +// A boolean; True means the cartridge was loaded correctly, False means +// an error occurred. + +EXPORT bool snes_load_cartridge_bsx_slotted( + const char *rom_xml, const uint8_t *rom_data, unsigned rom_size, + const char *bsx_xml, const uint8_t *bsx_data, unsigned bsx_size +); + +// snes_load_cartridge_sufami_turbo: +// +// Load a SuFami Turbo base cart image, optionally with game packs. +// +// The SuFami Turbo was a cartridge available for the Super Famicom, created +// by Bandai, with two slots in the top designed to accept special +// mini-cartridges. The cartridge in Slot A was the cartridge that actually +// ran, while the cartridge in Slot B was made available to the Slot +// A cartridge, enabling sharing of save-game data or using characters from +// one game in another. +// +// For more information, see: http://en.wikipedia.org/wiki/Sufami_Turbo +// +// Parameters: +// +// rom_xml: +// A pointer to a null-terminated string containing an XML memory map +// that describes where the SuFami Turbo base cartridge ROM image is +// mapped into the SNES address space. +// +// If NULL, libsnes will guess a memory map. The guessed memory map +// should be correct for all known SuFami Turbo base cartridge images. +// +// rom_data: +// A pointer to a byte array containing the uncompressed, +// de-interleaved, headerless ROM image of the SuFami Turbo base +// cartridge. +// +// The SuFami Turbo base cartridge is named "Sufami Turbo" in some +// SNES game databases. +// +// rom_size: +// The length of the rom_data array, in bytes. +// +// sta_xml: +// A pointer to a null-terminated string containing an XML memory map +// that describes the Sufami Turbo cartridge in Slot A. +// +// This parameter is currently ignored and should be passed as NULL. +// +// sta_data: +// A pointer to a byte array containing the uncompressed, +// de-interleaved, headerless ROM image of the SuFami Turbo cartridge +// in Slot A. +// +// This is the cartridge that will be executed by the SNES. +// +// If NULL, libsnes will behave as though no cartridge were inserted +// into the Slot A. +// +// sta_size: +// The length of the sta_data array, in bytes. +// +// stb_xml: +// A pointer to a null-terminated string containing an XML memory map +// that describes the Sufami Turbo cartridge in Slot B. +// +// This parameter is currently ignored and should be passed as NULL. +// +// stb_data: +// A pointer to a byte array containing the uncompressed, +// de-interleaved, headerless ROM image of the SuFami Turbo cartridge +// in Slot B. +// +// The data in this cartridge will be made available to the cartridge +// in Slot A. +// +// If NULL, libsnes will behave as though no cartridge were inserted +// into Slot B. +// +// stb_size: +// The length of the stb_data array, in bytes. +// +// Returns: +// +// A boolean; True means the cartridge was loaded correctly, False means +// an error occurred. + +EXPORT bool snes_load_cartridge_sufami_turbo( + const char *rom_xml, const uint8_t *rom_data, unsigned rom_size, + const char *sta_xml, const uint8_t *sta_data, unsigned sta_size, + const char *stb_xml, const uint8_t *stb_data, unsigned stb_size +); + +// snes_load_cartridge_super_game_boy: +// +// Load a Super Game Boy base cart, optionally with a Gameboy cartridge. +// +// The Super Game Boy was a cartridge available for the Super Famicom and +// Super Nintendo that accepted ordinary (original) Gameboy cartridges and +// allowed the user to play them with a Super Nintendo controller, on a TV. +// It extended the orginal Gameboy hardware in a few ways, including the +// ability to display games in various palettes (rather than strictly +// monochrome), to display a full-colour border image around the Gameboy +// video output, or even run native SNES code to enhance the game. +// +// For more information, see: http://en.wikipedia.org/wiki/Super_Game_Boy +// +// Up until bsnes v073, loading Super Game Boy cartridges only works if the +// libsupergameboy library from the bsnes release is installed. bsnes v074 +// includes a custom Gameboy emulation core, and external code is no longer +// required. +// +// Parameters: +// +// rom_xml: +// A pointer to a null-terminated string containing an XML memory map +// that describes where the Super Game Boy base cartridge ROM image is +// mapped into the SNES address space. +// +// If NULL, libsnes will guess a memory map. The guessed memory map +// should be correct for all known Super Game Boy base cartridge +// images. +// +// rom_data: +// A pointer to a byte array containing the uncompressed, +// de-interleaved, headerless ROM image of the Super Game Boy base +// cartridge. +// +// Appropriate base cartridge images are named "Super Game Boy" or +// "Super Game Boy 2" in some SNES game databases. +// +// rom_size: +// The length of the rom_data array, in bytes. +// +// dmg_xml: +// A pointer to a null-terminated string containing an XML memory map +// that describes the inserted Gameboy cartridge. +// +// If NULL, libsnes will guess a memory map. The guesed memory map +// should be correct for all licensed original Gameboy games in all +// regions. +// +// dmg_data: +// A pointer to a byte array containing the uncompressed, headerless +// ROM image of the inserted Gameboy cartridge. +// +// If NULL, libsnes will behave as though no cartridge were inserted. +// +// dmg_size: +// The length of the dmg_size array, in bytes. +// +// Returns: +// +// A boolean; True means the cartridge was loaded correctly, False means +// an error occurred. + +EXPORT bool snes_load_cartridge_super_game_boy( + const char *rom_xml, const uint8_t *rom_data, unsigned rom_size, + const char *dmg_xml, const uint8_t *dmg_data, unsigned dmg_size +); + +// snes_set_cartridge_basename: +// +// Set the location and name of the loaded cartridge. +// +// libsnes uses this information to locate additional resources the +// cartridge might require. Currently, these resources include: +// +// - The MSU-1 data pack and associated audio tracks, if the cartridge makes +// use of bsnes' MSU-1 special-chip. +// - The serial-port data receiving library, if the cartridge makes uses of +// bsnes' serial-data-over-controller-port feature. +// +// Parameters: +// +// basename: +// The path and basename of the loaded cartridge. For example, if the +// full path to the loaded cartridge is "/path/to/filename.sfc", this +// parameter should be set to "/path/to/filename". + +EXPORT void snes_set_cartridge_basename(const char *basename); + +// snes_unload_cartridge: +// +// Unloads the currently loaded cartridge from the emulated SNES. +// +// You will be unable to call snes_run() until another cartridge is loaded. + +EXPORT void snes_unload_cartridge(void); + +////////////////////////////////////////////////////////////////////////////}}} + +/////////////////////////////////////////////////////////////////////////////// +// Volatile and non-volatile storage {{{ +// +// Certain SNES cartridges include non-volatile storage or other kinds of data +// that would persist after the SNES is turned off. libsnes exposes this +// information via the snes_get_memory_data() and snes_get_memory_size() +// functions. Since version 1.2 of the libsnes API, libsnes also exposes the +// contents of volatile storage such as WRAM and VRAM. +// +// After a cartridge is loaded, call snes_get_memory_size() and +// snes_get_memory_data() with the various SNES_MEMORY_* constants to determine +// which kinds of non-volatile storage the cartridge supports - unsupported +// storage types will have a size of 0 and the data pointer NULL. +// +// If you have non-volatile storage data from a previous run, you can memcpy() +// the data from your storage into the buffer described by the data and size +// values before calling snes_run(). Do not load non-volatile storage data if +// the size of the data you have is different from the size returned by +// snes_get_memory_size(). +// +// Before calling snes_unload_cartridge(), you should copy the contents of the +// relevant storage buffers into a file (or some non-volatile storage of your +// own) so that you can load it the next time you load the same cartridge into +// the emulated SNES. Do not call free() on the storage buffers; they will be +// handled by libsnes. Note: It is not necessary to store the contents of +// volatile storage; the emulated SNES expects information in volatile storage +// to be lost (hence the name 'volatile'). +// +// Because non-volatile storage is read and written by the software running on +// the emulated SNES, it should be compatible between different versions of +// different emulators running on different platforms, unlike save states. +// +// The various kinds of non-volatile storage and their uses are: +// +// SNES_MEMORY_CARTRIDGE_RAM: +// Standard battery-backed static RAM (SRAM). Traditionally, the SRAM for +// a ROM image named "foo.sfc" is stored in a file named "foo.srm" beside +// it. +// +// SNES_MEMORY_CARTRIDGE_RTC: +// Real-time clock data. Traditionally, the RTC data for a ROM image named +// "foo.sfc" is stored in a file named "foo.rtc" beside it. +// +// SNES_MEMORY_BSX_RAM: +// RAM data used with the BS-X base cartridge. +// +// SNES_MEMORY_BSX_PRAM: +// PRAM data used with the BS-X base cartridge. +// +// SNES_MEMORY_SUFAMI_TURBO_A_RAM: +// RAM data stored in the mini-cartridge inserted into Slot A of the +// SuFami Turbo base cartridge. +// +// SNES_MEMORY_SUFAMI_TURBO_B_RAM: +// RAM data stored in the mini-cartridge inserted into Slot B of the +// SuFami Turbo base cartridge. +// +// SNES_MEMORY_GAME_BOY_RAM: +// Standard battery-backed static RAM (SRAM) in the Gameboy cartridge +// inserted into the Super Game Boy base cartridge. Not all Gameboy games +// have SRAM. +// +// SNES_MEMORY_GAME_BOY_RTC: +// Real-time clock data in the Gameboy cartridge inserted into the Super +// Game Boy base cartridge. Not all Gameboy games have an RTC. +// +// The various kinds of volatile storage are: +// +// SNES_MEMORY_WRAM: +// Working RAM, accessible by the CPU. SNES software tends to keep runtime +// information in here; games' life-bars and inventory contents and so +// forth are in here somewhere. +// +// SNES_MEMORY_APURAM: +// RAM accessible by the Audio Processing Unit. Contains audio samples, +// music data and the code responsible for feeding the right notes to the +// DSP at the right times. +// +// SNES_MEMORY_VRAM: +// Video RAM. Stores almost everything related to video output, including +// the patterns used for each tile and sprite, tilemaps for each +// background. The exact format used depends on the current video mode of +// the emulated SNES. +// +// SNES_MEMORY_OAM: +// Object Attribute Memory. Stores the location, orientation and priority +// of all the sprites the SNES displays. +// +// SNES_MEMORY_CGRAM: +// Color Generator RAM. Contains the colour palettes used by tiles and +// sprites. Each palette entry is stored in a 16-bit int, in the standard +// XBGR1555 format. + +// snes_get_memory_data: +// +// Returns a pointer to the given non-volatile storage buffer. +// +// This requires that a cartridge is loaded. +// +// Parameters: +// +// id: +// One of the SNES_MEMORY_* constants. +// +// Returns: +// +// A pointer to the memory buffer used for storing the given type of data. +// The size of the buffer can be obtained from snes_get_memory_size(). +// +// If NULL, the loaded cartridge does not store the given type of data. + +EXPORT uint8_t* snes_get_memory_data(unsigned id); + +// snes_get_memory_size: +// +// Returns the size of the given non-volatile storage buffer. +// +// This requires that a cartridge is loaded. +// +// Parameters: +// +// id: +// One of the SNES_MEMORY_* constants. +// +// Returns: +// +// The size of the memory buffer used for storing the given type of data. +// A pointer to the buffer can be obtained from snes_get_memory_data(). +// +// If 0, the loaded cartridge does not store the given type of data. + +EXPORT unsigned snes_get_memory_size(unsigned id); + +////////////////////////////////////////////////////////////////////////////}}} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libsnes/snes/memory/memory-inline.hpp b/libsnes/snes/memory/memory-inline.hpp new file mode 100644 index 0000000..70503be --- /dev/null +++ b/libsnes/snes/memory/memory-inline.hpp @@ -0,0 +1,61 @@ +//Memory + +unsigned Memory::size() const { return 0; } + +//StaticRAM + +uint8* StaticRAM::data() { return data_; } +unsigned StaticRAM::size() const { return size_; } + +uint8 StaticRAM::read(unsigned addr) { return data_[addr]; } +void StaticRAM::write(unsigned addr, uint8 n) { data_[addr] = n; } +uint8& StaticRAM::operator[](unsigned addr) { return data_[addr]; } +const uint8& StaticRAM::operator[](unsigned addr) const { return data_[addr]; } + +StaticRAM::StaticRAM(unsigned n) : size_(n) { data_ = new uint8[size_]; } +StaticRAM::~StaticRAM() { delete[] data_; } + +//MappedRAM + +void MappedRAM::reset() { + if(data_) { + delete[] data_; + data_ = 0; + } + size_ = 0; + write_protect_ = false; +} + +void MappedRAM::map(uint8 *source, unsigned length) { + reset(); + data_ = source; + size_ = data_ ? length : 0; +} + +void MappedRAM::copy(const uint8 *data, unsigned size) { + if(!data_) { + size_ = (size & ~255) + ((bool)(size & 255) << 8); + data_ = new uint8[size_](); + } + memcpy(data_, data, min(size_, size)); +} + +void MappedRAM::write_protect(bool status) { write_protect_ = status; } +uint8* MappedRAM::data() { return data_; } +unsigned MappedRAM::size() const { return size_; } + +uint8 MappedRAM::read(unsigned addr) { return data_[addr]; } +void MappedRAM::write(unsigned addr, uint8 n) { if(!write_protect_) data_[addr] = n; } +const uint8& MappedRAM::operator[](unsigned addr) const { return data_[addr]; } +MappedRAM::MappedRAM() : data_(0), size_(0), write_protect_(false) {} + +//Bus + +uint8 Bus::read(unsigned addr) { + if(cheat.override[addr]) return cheat.read(addr); + return reader[lookup[addr]](target[addr]); +} + +void Bus::write(unsigned addr, uint8 data) { + return writer[lookup[addr]](target[addr], data); +} diff --git a/libsnes/snes/memory/memory.cpp b/libsnes/snes/memory/memory.cpp new file mode 100644 index 0000000..fb6c978 --- /dev/null +++ b/libsnes/snes/memory/memory.cpp @@ -0,0 +1,85 @@ +#include + +#define MEMORY_CPP +namespace SNES { + +Bus bus; + +unsigned Bus::mirror(unsigned addr, unsigned size) { + unsigned base = 0; + if(size) { + unsigned mask = 1 << 23; + while(addr >= size) { + while(!(addr & mask)) mask >>= 1; + addr -= mask; + if(size > mask) { + size -= mask; + base += mask; + } + mask >>= 1; + } + base += addr; + } + return base; +} + +void Bus::map( + MapMode::e mode, + unsigned bank_lo, unsigned bank_hi, + unsigned addr_lo, unsigned addr_hi, + const function &rd, + const function &wr, + unsigned base, unsigned length +) { + assert(bank_lo <= bank_hi && bank_lo <= 0xff); + assert(addr_lo <= addr_hi && addr_lo <= 0xffff); + unsigned id = idcount++; + assert(id < 255); + reader[id] = rd; + writer[id] = wr; + + if(length == 0) length = (bank_hi - bank_lo + 1) * (addr_hi - addr_lo + 1); + + unsigned offset = 0; + for(unsigned bank = bank_lo; bank <= bank_hi; bank++) { + for(unsigned addr = addr_lo; addr <= addr_hi; addr++) { + unsigned destaddr = (bank << 16) | addr; + if(mode == MapMode::Linear) destaddr = mirror(base + offset++, length); + if(mode == MapMode::Shadow) destaddr = mirror(base + destaddr, length); + lookup[(bank << 16) | addr] = id; + target[(bank << 16) | addr] = destaddr; + } + } +} + +static uint8 bus_reader_dummy(unsigned) { + return cpu.regs.mdr; +} + +static void bus_writer_dummy(unsigned, uint8) {} + +void Bus::map_reset() { + function reader(bus_reader_dummy); + function writer(bus_writer_dummy); + + idcount = 0; + map(MapMode::Direct, 0x00, 0xff, 0x0000, 0xffff, reader, writer); +} + +void Bus::map_xml() { + foreach(m, cartridge.mapping) { + map(m.mode.i, m.banklo, m.bankhi, m.addrlo, m.addrhi, m.read, m.write, m.offset, m.size); + } +} + +Bus::Bus() { + lookup = new uint8 [16 * 1024 * 1024]; + target = new uint32[16 * 1024 * 1024]; +} + +Bus::~Bus() { + delete[] lookup; + delete[] target; +} + +} diff --git a/libsnes/snes/memory/memory.hpp b/libsnes/snes/memory/memory.hpp new file mode 100644 index 0000000..5d2a4a3 --- /dev/null +++ b/libsnes/snes/memory/memory.hpp @@ -0,0 +1,74 @@ +struct Memory { + virtual inline unsigned size() const; + virtual uint8 read(unsigned addr) = 0; + virtual void write(unsigned addr, uint8 data) = 0; +}; + +struct StaticRAM : Memory { + inline uint8* data(); + inline unsigned size() const; + + inline uint8 read(unsigned addr); + inline void write(unsigned addr, uint8 n); + inline uint8& operator[](unsigned addr); + inline const uint8& operator[](unsigned addr) const; + + inline StaticRAM(unsigned size); + inline ~StaticRAM(); + +private: + uint8 *data_; + unsigned size_; +}; + +struct MappedRAM : Memory { + inline void reset(); + inline void map(uint8*, unsigned); + inline void copy(const uint8*, unsigned); + + inline void write_protect(bool status); + inline uint8* data(); + inline unsigned size() const; + + inline uint8 read(unsigned addr); + inline void write(unsigned addr, uint8 n); + inline const uint8& operator[](unsigned addr) const; + inline MappedRAM(); + +private: + uint8 *data_; + unsigned size_; + bool write_protect_; +}; + +struct Bus { + unsigned mirror(unsigned addr, unsigned size); + + alwaysinline uint8 read(unsigned addr); + alwaysinline void write(unsigned addr, uint8 data); + + uint8 *lookup; + uint32 *target; + + unsigned idcount; + function reader[256]; + function writer[256]; + + struct MapMode { enum e { Direct, Linear, Shadow } i; }; + void map( + MapMode::e mode, + unsigned bank_lo, unsigned bank_hi, + unsigned addr_lo, unsigned addr_hi, + const function &read, + const function &write, + unsigned base = 0, unsigned length = 0 + ); + + void map_reset(); + void map_xml(); + + Bus(); + ~Bus(); +}; + +extern Bus bus; diff --git a/libsnes/snes/ppu/background/background.cpp b/libsnes/snes/ppu/background/background.cpp new file mode 100644 index 0000000..cb3544c --- /dev/null +++ b/libsnes/snes/ppu/background/background.cpp @@ -0,0 +1,278 @@ +#ifdef PPU_CPP + +#include "mode7.cpp" + +//V = 0, H = 0 +void PPU::Background::frame() { +} + +//H = 0 +void PPU::Background::scanline() { +} + +//H = 60 +void PPU::Background::begin() { + bool hires = (self.regs.bgmode == 5 || self.regs.bgmode == 6); + x = -7; + y = self.vcounter(); + + if(y == 1) { + mosaic.vcounter = regs.mosaic + 1; + mosaic.voffset = 1; + cache.hoffset = regs.hoffset; + cache.voffset = regs.voffset; + } else if(--mosaic.vcounter == 0) { + mosaic.vcounter = regs.mosaic + 1; + mosaic.voffset += regs.mosaic + 1; + cache.hoffset = regs.hoffset; + cache.voffset = regs.voffset; + } + + tile_counter = (7 - (cache.hoffset & 7)) << hires; + for(unsigned n = 0; n < 8; n++) data[n] = 0; + + mosaic.hcounter = regs.mosaic + 1; + mosaic.hoffset = 0; + + if(regs.mode == Mode::Mode7) return begin_mode7(); + if(regs.mosaic == 0) { + cache.hoffset = regs.hoffset; + cache.voffset = regs.voffset; + } +} + +void PPU::Background::get_tile() { + bool hires = (self.regs.bgmode == 5 || self.regs.bgmode == 6); + + unsigned color_depth = (regs.mode == Mode::BPP2 ? 0 : regs.mode == Mode::BPP4 ? 1 : 2); + unsigned palette_offset = (self.regs.bgmode == 0 ? id << 5 : 0); + unsigned palette_size = 2 << color_depth; + unsigned tile_mask = 0x0fff >> color_depth; + unsigned tiledata_index = regs.tiledata_addr >> (4 + color_depth); + + unsigned tile_height = (regs.tile_size == TileSize::Size8x8 ? 3 : 4); + unsigned tile_width = (!hires ? tile_height : 4); + + unsigned width = 256 << hires; + + unsigned hmask = (tile_height == 3 ? width : width << 1); + unsigned vmask = hmask; + if(regs.screen_size & 1) hmask <<= 1; + if(regs.screen_size & 2) vmask <<= 1; + hmask--; + vmask--; + + unsigned px = x << hires; + unsigned py = (regs.mosaic == 0 ? y : mosaic.voffset); + + unsigned hscroll = cache.hoffset; + unsigned vscroll = cache.voffset; + if(hires) { + hscroll <<= 1; + if(self.regs.interlace) py = (py << 1) + self.field(); + } + + unsigned hoffset = hscroll + px; + unsigned voffset = vscroll + py; + + if(self.regs.bgmode == 2 || self.regs.bgmode == 4 || self.regs.bgmode == 6) { + uint16 offset_x = (x + (hscroll & 7)); + + if(offset_x >= 8) { + unsigned hval = self.bg3.get_tile((offset_x - 8) + (self.bg3.cache.hoffset & ~7), self.bg3.cache.voffset + 0); + unsigned vval = self.bg3.get_tile((offset_x - 8) + (self.bg3.cache.hoffset & ~7), self.bg3.cache.voffset + 8); + unsigned valid_mask = (id == ID::BG1 ? 0x2000 : 0x4000); + + if(self.regs.bgmode == 4) { + if(hval & valid_mask) { + if((hval & 0x8000) == 0) { + hoffset = offset_x + (hval & ~7); + } else { + voffset = y + hval; + } + } + } else { + if(hval & valid_mask) hoffset = offset_x + (hval & ~7); + if(vval & valid_mask) voffset = y + vval; + } + } + } + + hoffset &= hmask; + voffset &= vmask; + + unsigned screen_x = (regs.screen_size & 1 ? 32 << 5 : 0); + unsigned screen_y = (regs.screen_size & 2 ? 32 << 5 : 0); + if(regs.screen_size == 3) screen_y <<= 1; + + unsigned tx = hoffset >> tile_width; + unsigned ty = voffset >> tile_height; + + uint16 offset = ((ty & 0x1f) << 5) + (tx & 0x1f); + if(tx & 0x20) offset += screen_x; + if(ty & 0x20) offset += screen_y; + + uint16 addr = regs.screen_addr + (offset << 1); + tile = (ppu.vram[addr + 0] << 0) + (ppu.vram[addr + 1] << 8); + bool mirror_y = tile & 0x8000; + bool mirror_x = tile & 0x4000; + priority = (tile & 0x2000 ? regs.priority1 : regs.priority0); + palette_number = (tile >> 10) & 7; + palette_index = palette_offset + (palette_number << palette_size); + + if(tile_width == 4 && (bool)(hoffset & 8) != mirror_x) tile += 1; + if(tile_height == 4 && (bool)(voffset & 8) != mirror_y) tile += 16; + uint16 character = ((tile & 0x03ff) + tiledata_index) & tile_mask; + + if(mirror_y) voffset ^= 7; + offset = (character << (4 + color_depth)) + ((voffset & 7) << 1); + + if(regs.mode >= Mode::BPP2) { + data[0] = ppu.vram[offset + 0]; + data[1] = ppu.vram[offset + 1]; + } + if(regs.mode >= Mode::BPP4) { + data[2] = ppu.vram[offset + 16]; + data[3] = ppu.vram[offset + 17]; + } + if(regs.mode >= Mode::BPP8) { + data[4] = ppu.vram[offset + 32]; + data[5] = ppu.vram[offset + 33]; + data[6] = ppu.vram[offset + 48]; + data[7] = ppu.vram[offset + 49]; + } + + if(mirror_x) for(unsigned n = 0; n < 8; n++) { + //reverse data bits in data[n]: 01234567 -> 76543210 + data[n] = ((data[n] >> 4) & 0x0f) | ((data[n] << 4) & 0xf0); + data[n] = ((data[n] >> 2) & 0x33) | ((data[n] << 2) & 0xcc); + data[n] = ((data[n] >> 1) & 0x55) | ((data[n] << 1) & 0xaa); + } +} + +void PPU::Background::run(bool screen) { + if(self.vcounter() == 0) return; + bool hires = (self.regs.bgmode == 5 || self.regs.bgmode == 6); + + if(screen == Screen::Sub) { + output.main.priority = 0; + output.sub.priority = 0; + if(hires == false) return; + } + + if(regs.main_enable == false && regs.sub_enable == false) return; + if(regs.mode == Mode::Inactive) return; + if(regs.mode == Mode::Mode7) return run_mode7(); + + if(tile_counter-- == 0) { + tile_counter = 7; + get_tile(); + } + + uint8 palette = get_tile_color(); + if(x == 0) mosaic.hcounter = 1; + if(x >= 0 && --mosaic.hcounter == 0) { + mosaic.hcounter = regs.mosaic + 1; + mosaic.priority = priority; + mosaic.palette = palette ? palette_index + palette : 0; + mosaic.tile = tile; + } + if(screen == Screen::Main) x++; + if(mosaic.palette == 0) return; + + if(hires == false || screen == Screen::Main) if(regs.main_enable) output.main = mosaic; + if(hires == false || screen == Screen::Sub ) if(regs.sub_enable ) output.sub = mosaic; +} + +unsigned PPU::Background::get_tile_color() { + unsigned color = 0; + if(regs.mode >= Mode::BPP2) { + color += (data[0] & 0x80) ? 0x01 : 0; data[0] <<= 1; + color += (data[1] & 0x80) ? 0x02 : 0; data[1] <<= 1; + } + if(regs.mode >= Mode::BPP4) { + color += (data[2] & 0x80) ? 0x04 : 0; data[2] <<= 1; + color += (data[3] & 0x80) ? 0x08 : 0; data[3] <<= 1; + } + if(regs.mode >= Mode::BPP8) { + color += (data[4] & 0x80) ? 0x10 : 0; data[4] <<= 1; + color += (data[5] & 0x80) ? 0x20 : 0; data[5] <<= 1; + color += (data[6] & 0x80) ? 0x40 : 0; data[6] <<= 1; + color += (data[7] & 0x80) ? 0x80 : 0; data[7] <<= 1; + } + return color; +} + +void PPU::Background::reset() { + regs.tiledata_addr = random(0x0000); + regs.screen_addr = random(0x0000); + regs.screen_size = random(0); + regs.mosaic = random(0); + regs.tile_size = random(0); + regs.mode = 0; + regs.priority0 = 0; + regs.priority1 = 0; + regs.main_enable = random(0); + regs.sub_enable = random(0); + regs.hoffset = random(0x0000); + regs.voffset = random(0x0000); + + cache.hoffset = 0; + cache.voffset = 0; + + output.main.palette = 0; + output.main.priority = 0; + output.sub.palette = 0; + output.sub.priority = 0; + + mosaic.priority = 0; + mosaic.palette = 0; + mosaic.tile = 0; + + mosaic.vcounter = 0; + mosaic.voffset = 0; + mosaic.hcounter = 0; + mosaic.hoffset = 0; + + x = 0; + y = 0; + + tile_counter = 0; + tile = 0; + priority = 0; + palette_number = 0; + palette_index = 0; + for(unsigned n = 0; n < 8; n++) data[n] = 0; +} + +unsigned PPU::Background::get_tile(unsigned x, unsigned y) { + bool hires = (self.regs.bgmode == 5 || self.regs.bgmode == 6); + unsigned tile_height = (regs.tile_size == TileSize::Size8x8 ? 3 : 4); + unsigned tile_width = (!hires ? tile_height : 4); + unsigned width = (!hires ? 256 : 512); + unsigned mask_x = (tile_height == 3 ? width : width << 1); + unsigned mask_y = mask_x; + if(regs.screen_size & 1) mask_x <<= 1; + if(regs.screen_size & 2) mask_y <<= 1; + mask_x--; + mask_y--; + + unsigned screen_x = (regs.screen_size & 1 ? 32 << 5 : 0); + unsigned screen_y = (regs.screen_size & 2 ? 32 << 5 : 0); + if(regs.screen_size == 3) screen_y <<= 1; + + x = (x & mask_x) >> tile_width; + y = (y & mask_y) >> tile_height; + + uint16 offset = ((y & 0x1f) << 5) + (x & 0x1f); + if(x & 0x20) offset += screen_x; + if(y & 0x20) offset += screen_y; + + uint16 addr = regs.screen_addr + (offset << 1); + return (ppu.vram[addr + 0] << 0) + (ppu.vram[addr + 1] << 8); +} + +PPU::Background::Background(PPU &self, unsigned id) : self(self), id(id) { +} + +#endif diff --git a/libsnes/snes/ppu/background/background.hpp b/libsnes/snes/ppu/background/background.hpp new file mode 100644 index 0000000..41ac592 --- /dev/null +++ b/libsnes/snes/ppu/background/background.hpp @@ -0,0 +1,77 @@ +struct Background { + struct ID { enum { BG1, BG2, BG3, BG4 }; }; + unsigned id; + + struct Mode { enum { BPP2, BPP4, BPP8, Mode7, Inactive }; }; + struct ScreenSize { enum { Size32x32, Size32x64, Size64x32, Size64x64 }; }; + struct TileSize { enum { Size8x8, Size16x16 }; }; + struct Screen { enum { Main, Sub }; }; + + struct Regs { + uint16 tiledata_addr; + uint16 screen_addr; + uint2 screen_size; + uint4 mosaic; + bool tile_size; + + unsigned mode; + unsigned priority0; + unsigned priority1; + + bool main_enable; + bool sub_enable; + + uint16 hoffset; + uint16 voffset; + } regs; + + struct Cache { + uint16 hoffset; + uint16 voffset; + } cache; + + struct Output { + struct Pixel { + unsigned priority; //0 = none (transparent) + uint8 palette; + uint16 tile; + } main, sub; + } output; + + struct Mosaic : Output::Pixel { + unsigned vcounter; + unsigned voffset; + unsigned hcounter; + unsigned hoffset; + } mosaic; + + struct { + signed x; + signed y; + + unsigned tile_counter; + unsigned tile; + unsigned priority; + unsigned palette_number; + unsigned palette_index; + uint8 data[8]; + }; + + void frame(); + void scanline(); + void begin(); + void run(bool screen); + void reset(); + + void get_tile(); + unsigned get_tile_color(); + unsigned get_tile(unsigned x, unsigned y); + signed clip(signed n); + void begin_mode7(); + void run_mode7(); + + void serialize(serializer&); + Background(PPU &self, unsigned id); + + PPU &self; +}; diff --git a/libsnes/snes/ppu/background/mode7.cpp b/libsnes/snes/ppu/background/mode7.cpp new file mode 100644 index 0000000..bb63064 --- /dev/null +++ b/libsnes/snes/ppu/background/mode7.cpp @@ -0,0 +1,110 @@ +#ifdef PPU_CPP + +signed PPU::Background::clip(signed n) { + //13-bit sign extend: --s---nnnnnnnnnn -> ssssssnnnnnnnnnn + return n & 0x2000 ? (n | ~1023) : (n & 1023); +} + +//H = 60 +void PPU::Background::begin_mode7() { + cache.hoffset = self.regs.mode7_hoffset; + cache.voffset = self.regs.mode7_voffset; +} + +void PPU::Background::run_mode7() { + signed a = sclip<16>(self.regs.m7a); + signed b = sclip<16>(self.regs.m7b); + signed c = sclip<16>(self.regs.m7c); + signed d = sclip<16>(self.regs.m7d); + + signed cx = sclip<13>(self.regs.m7x); + signed cy = sclip<13>(self.regs.m7y); + signed hoffset = sclip<13>(cache.hoffset); + signed voffset = sclip<13>(cache.voffset); + + if(Background::x++ & ~255) return; + unsigned x = mosaic.hoffset; + unsigned y = self.bg1.mosaic.voffset; //BG2 vertical mosaic uses BG1 mosaic size + + if(--mosaic.hcounter == 0) { + mosaic.hcounter = regs.mosaic + 1; + mosaic.hoffset += regs.mosaic + 1; + } + + if(self.regs.mode7_hflip) x = 255 - x; + if(self.regs.mode7_vflip) y = 255 - y; + + signed psx = ((a * clip(hoffset - cx)) & ~63) + ((b * clip(voffset - cy)) & ~63) + ((b * y) & ~63) + (cx << 8); + signed psy = ((c * clip(hoffset - cx)) & ~63) + ((d * clip(voffset - cy)) & ~63) + ((d * y) & ~63) + (cy << 8); + + signed px = psx + (a * x); + signed py = psy + (c * x); + + //mask pseudo-FP bits + px >>= 8; + py >>= 8; + + unsigned tile; + unsigned palette; + switch(self.regs.mode7_repeat) { + //screen repetition outside of screen area + case 0: + case 1: { + px &= 1023; + py &= 1023; + tile = ppu.vram[((py >> 3) * 128 + (px >> 3)) << 1]; + palette = ppu.vram[(((tile << 6) + ((py & 7) << 3) + (px & 7)) << 1) + 1]; + break; + } + + //palette color 0 outside of screen area + case 2: { + if((px | py) & ~1023) { + palette = 0; + } else { + px &= 1023; + py &= 1023; + tile = ppu.vram[((py >> 3) * 128 + (px >> 3)) << 1]; + palette = ppu.vram[(((tile << 6) + ((py & 7) << 3) + (px & 7)) << 1) + 1]; + } + break; + } + + //character 0 repetition outside of screen area + case 3: { + if((px | py) & ~1023) { + tile = 0; + } else { + px &= 1023; + py &= 1023; + tile = ppu.vram[((py >> 3) * 128 + (px >> 3)) << 1]; + } + palette = ppu.vram[(((tile << 6) + ((py & 7) << 3) + (px & 7)) << 1) + 1]; + break; + } + } + + unsigned priority; + if(id == ID::BG1) { + priority = regs.priority0; + } else if(id == ID::BG2) { + priority = (palette & 0x80 ? regs.priority1 : regs.priority0); + palette &= 0x7f; + } + + if(palette == 0) return; + + if(regs.main_enable) { + output.main.palette = palette; + output.main.priority = priority; + output.main.tile = 0; + } + + if(regs.sub_enable) { + output.sub.palette = palette; + output.sub.priority = priority; + output.sub.tile = 0; + } +} + +#endif diff --git a/libsnes/snes/ppu/counter/counter-inline.hpp b/libsnes/snes/ppu/counter/counter-inline.hpp new file mode 100644 index 0000000..b349cf1 --- /dev/null +++ b/libsnes/snes/ppu/counter/counter-inline.hpp @@ -0,0 +1,85 @@ +//this should only be called by CPU::PPUcounter::tick(); +//keeps track of previous counter positions in history table +void PPUcounter::tick() { + status.hcounter += 2; //increment by smallest unit of time + if(status.hcounter >= 1360 && status.hcounter == lineclocks()) { + status.hcounter = 0; + vcounter_tick(); + } + + history.index = (history.index + 1) & 2047; + history.field [history.index] = status.field; + history.vcounter[history.index] = status.vcounter; + history.hcounter[history.index] = status.hcounter; +} + +//this should only be called by PPU::PPUcounter::tick(n); +//allows stepping by more than the smallest unit of time +void PPUcounter::tick(unsigned clocks) { + status.hcounter += clocks; + if(status.hcounter >= lineclocks()) { + status.hcounter -= lineclocks(); + vcounter_tick(); + } +} + +//internal +void PPUcounter::vcounter_tick() { + if(++status.vcounter == 128) status.interlace = ppu.interlace(); + + if((system.region.i == System::Region::NTSC && status.interlace == false && status.vcounter == 262) + || (system.region.i == System::Region::NTSC && status.interlace == true && status.vcounter == 263) + || (system.region.i == System::Region::NTSC && status.interlace == true && status.vcounter == 262 && status.field == 1) + || (system.region.i == System::Region::PAL && status.interlace == false && status.vcounter == 312) + || (system.region.i == System::Region::PAL && status.interlace == true && status.vcounter == 313) + || (system.region.i == System::Region::PAL && status.interlace == true && status.vcounter == 312 && status.field == 1) + ) { + status.vcounter = 0; + status.field = !status.field; + } + if(scanline) scanline(); +} + +bool PPUcounter::field () const { return status.field; } +uint16 PPUcounter::vcounter() const { return status.vcounter; } +uint16 PPUcounter::hcounter() const { return status.hcounter; } + +bool PPUcounter::field (unsigned offset) const { return history.field [(history.index - (offset >> 1)) & 2047]; } +uint16 PPUcounter::vcounter(unsigned offset) const { return history.vcounter[(history.index - (offset >> 1)) & 2047]; } +uint16 PPUcounter::hcounter(unsigned offset) const { return history.hcounter[(history.index - (offset >> 1)) & 2047]; } + +//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 } + +uint16 PPUcounter::hdot() const { + if(system.region.i == System::Region::NTSC && status.interlace == false && vcounter() == 240 && field() == 1) { + return (hcounter() >> 2); + } else { + return (hcounter() - ((hcounter() > 1292) << 1) - ((hcounter() > 1310) << 1)) >> 2; + } +} + +uint16 PPUcounter::lineclocks() const { + if(system.region.i == System::Region::NTSC && status.interlace == false && vcounter() == 240 && field() == 1) return 1360; + return 1364; +} + +void PPUcounter::reset() { + status.interlace = false; + status.field = 0; + status.vcounter = 0; + status.hcounter = 0; + history.index = 0; + + for(unsigned i = 0; i < 2048; i++) { + history.field [i] = 0; + history.vcounter[i] = 0; + history.hcounter[i] = 0; + } +} diff --git a/libsnes/snes/ppu/counter/counter.hpp b/libsnes/snes/ppu/counter/counter.hpp new file mode 100644 index 0000000..b8d7aa7 --- /dev/null +++ b/libsnes/snes/ppu/counter/counter.hpp @@ -0,0 +1,49 @@ +//PPUcounter emulates the H/V latch counters of the S-PPU2. +// +//real hardware has the S-CPU maintain its own copy of these counters that are +//updated based on the state of the S-PPU Vblank and Hblank pins. emulating this +//would require full lock-step synchronization for every clock tick. +//to bypass this and allow the two to run out-of-order, both the CPU and PPU +//classes inherit PPUcounter and keep their own counters. +//the timers are kept in sync, as the only differences occur on V=240 and V=261, +//based on interlace. thus, we need only synchronize and fetch interlace at any +//point before this in the frame, which is handled internally by this class at +//V=128. + +class PPUcounter { +public: + alwaysinline void tick(); + alwaysinline void tick(unsigned clocks); + + alwaysinline bool field () const; + alwaysinline uint16 vcounter() const; + alwaysinline uint16 hcounter() const; + inline uint16 hdot() const; + inline uint16 lineclocks() const; + + alwaysinline bool field (unsigned offset) const; + alwaysinline uint16 vcounter(unsigned offset) const; + alwaysinline uint16 hcounter(unsigned offset) const; + + inline void reset(); + function scanline; + void serialize(serializer&); + +private: + inline void vcounter_tick(); + + struct { + bool interlace; + bool field; + uint16 vcounter; + uint16 hcounter; + } status; + + struct { + bool field[2048]; + uint16 vcounter[2048]; + uint16 hcounter[2048]; + + int32 index; + } history; +}; diff --git a/libsnes/snes/ppu/debugger/debugger.cpp b/libsnes/snes/ppu/debugger/debugger.cpp new file mode 100644 index 0000000..28a519f --- /dev/null +++ b/libsnes/snes/ppu/debugger/debugger.cpp @@ -0,0 +1,17 @@ +#ifdef PPU_CPP + +bool PPUDebugger::property(unsigned id, string &name, string &value) { + unsigned n = 0; + + #define item(name_, value_) \ + if(id == n++) { \ + name = name_; \ + value = value_; \ + return true; \ + } + + #undef item + return false; +} + +#endif diff --git a/libsnes/snes/ppu/debugger/debugger.hpp b/libsnes/snes/ppu/debugger/debugger.hpp new file mode 100644 index 0000000..9ce6a4e --- /dev/null +++ b/libsnes/snes/ppu/debugger/debugger.hpp @@ -0,0 +1,4 @@ +class PPUDebugger : public PPU, public ChipDebugger { +public: + bool property(unsigned id, string &name, string &value); +}; diff --git a/libsnes/snes/ppu/mmio/mmio.cpp b/libsnes/snes/ppu/mmio/mmio.cpp new file mode 100644 index 0000000..517b8b9 --- /dev/null +++ b/libsnes/snes/ppu/mmio/mmio.cpp @@ -0,0 +1,865 @@ +#ifdef PPU_CPP + +bool PPU::interlace() const { + return display.interlace; +} + +bool PPU::overscan() const { + return display.overscan; +} + +bool PPU::hires() const { + return true; +} + +void PPU::latch_counters() { + cpu.synchronize_ppu(); + regs.hcounter = hdot(); + regs.vcounter = vcounter(); + regs.counters_latched = true; +} + +uint16 PPU::get_vram_address() { + uint16 addr = regs.vram_addr; + switch(regs.vram_mapping) { + case 0: break; //direct mapping + case 1: addr = (addr & 0xff00) | ((addr & 0x001f) << 3) | ((addr >> 5) & 7); break; + case 2: addr = (addr & 0xfe00) | ((addr & 0x003f) << 3) | ((addr >> 6) & 7); break; + case 3: addr = (addr & 0xfc00) | ((addr & 0x007f) << 3) | ((addr >> 7) & 7); break; + } + return (addr << 1); +} + +uint8 PPU::vram_read(unsigned addr) { + if(regs.display_disable || vcounter() >= (!regs.overscan ? 225 : 240)) { + return vram[addr]; + } + return 0x00; +} + +void PPU::vram_write(unsigned addr, uint8 data) { + if(regs.display_disable || vcounter() >= (!regs.overscan ? 225 : 240)) { + vram[addr] = data; + } +} + +void PPU::mmio_update_video_mode() { + switch(regs.bgmode) { + case 0: { + bg1.regs.mode = Background::Mode::BPP2; bg1.regs.priority0 = 8; bg1.regs.priority1 = 11; + bg2.regs.mode = Background::Mode::BPP2; bg2.regs.priority0 = 7; bg2.regs.priority1 = 10; + bg3.regs.mode = Background::Mode::BPP2; bg3.regs.priority0 = 2; bg3.regs.priority1 = 5; + bg4.regs.mode = Background::Mode::BPP2; bg4.regs.priority0 = 1; bg4.regs.priority1 = 4; + sprite.regs.priority0 = 3; sprite.regs.priority1 = 6; sprite.regs.priority2 = 9; sprite.regs.priority3 = 12; + } break; + + case 1: { + bg1.regs.mode = Background::Mode::BPP4; + bg2.regs.mode = Background::Mode::BPP4; + bg3.regs.mode = Background::Mode::BPP2; + bg4.regs.mode = Background::Mode::Inactive; + if(regs.bg3_priority) { + bg1.regs.priority0 = 5; bg1.regs.priority1 = 8; + bg2.regs.priority0 = 4; bg2.regs.priority1 = 7; + bg3.regs.priority0 = 1; bg3.regs.priority1 = 10; + sprite.regs.priority0 = 2; sprite.regs.priority1 = 3; sprite.regs.priority2 = 6; sprite.regs.priority3 = 9; + } else { + bg1.regs.priority0 = 6; bg1.regs.priority1 = 9; + bg2.regs.priority0 = 5; bg2.regs.priority1 = 8; + bg3.regs.priority0 = 1; bg3.regs.priority1 = 3; + sprite.regs.priority0 = 2; sprite.regs.priority1 = 4; sprite.regs.priority2 = 7; sprite.regs.priority3 = 10; + } + } break; + + case 2: { + bg1.regs.mode = Background::Mode::BPP4; + bg2.regs.mode = Background::Mode::BPP4; + bg3.regs.mode = Background::Mode::Inactive; + bg4.regs.mode = Background::Mode::Inactive; + bg1.regs.priority0 = 3; bg1.regs.priority1 = 7; + bg2.regs.priority0 = 1; bg2.regs.priority1 = 5; + sprite.regs.priority0 = 2; sprite.regs.priority1 = 4; sprite.regs.priority2 = 6; sprite.regs.priority3 = 8; + } break; + + case 3: { + bg1.regs.mode = Background::Mode::BPP8; + bg2.regs.mode = Background::Mode::BPP4; + bg3.regs.mode = Background::Mode::Inactive; + bg4.regs.mode = Background::Mode::Inactive; + bg1.regs.priority0 = 3; bg1.regs.priority1 = 7; + bg2.regs.priority0 = 1; bg2.regs.priority1 = 5; + sprite.regs.priority0 = 2; sprite.regs.priority1 = 4; sprite.regs.priority2 = 6; sprite.regs.priority3 = 8; + } break; + + case 4: { + bg1.regs.mode = Background::Mode::BPP8; + bg2.regs.mode = Background::Mode::BPP2; + bg3.regs.mode = Background::Mode::Inactive; + bg4.regs.mode = Background::Mode::Inactive; + bg1.regs.priority0 = 3; bg1.regs.priority1 = 7; + bg2.regs.priority0 = 1; bg2.regs.priority1 = 5; + sprite.regs.priority0 = 2; sprite.regs.priority1 = 4; sprite.regs.priority2 = 6; sprite.regs.priority3 = 8; + } break; + + case 5: { + bg1.regs.mode = Background::Mode::BPP4; + bg2.regs.mode = Background::Mode::BPP2; + bg3.regs.mode = Background::Mode::Inactive; + bg4.regs.mode = Background::Mode::Inactive; + bg1.regs.priority0 = 3; bg1.regs.priority1 = 7; + bg2.regs.priority0 = 1; bg2.regs.priority1 = 5; + sprite.regs.priority0 = 2; sprite.regs.priority1 = 4; sprite.regs.priority2 = 6; sprite.regs.priority3 = 8; + } break; + + case 6: { + bg1.regs.mode = Background::Mode::BPP4; + bg2.regs.mode = Background::Mode::Inactive; + bg3.regs.mode = Background::Mode::Inactive; + bg4.regs.mode = Background::Mode::Inactive; + bg1.regs.priority0 = 2; bg1.regs.priority1 = 5; + sprite.regs.priority0 = 1; sprite.regs.priority1 = 3; sprite.regs.priority2 = 4; sprite.regs.priority3 = 6; + } break; + + case 7: { + if(regs.mode7_extbg == false) { + bg1.regs.mode = Background::Mode::Mode7; + bg2.regs.mode = Background::Mode::Inactive; + bg3.regs.mode = Background::Mode::Inactive; + bg4.regs.mode = Background::Mode::Inactive; + bg1.regs.priority0 = 2; bg1.regs.priority1 = 2; + sprite.regs.priority0 = 1; sprite.regs.priority1 = 3; sprite.regs.priority2 = 4; sprite.regs.priority3 = 5; + } else { + bg1.regs.mode = Background::Mode::Mode7; + bg2.regs.mode = Background::Mode::Mode7; + bg3.regs.mode = Background::Mode::Inactive; + bg4.regs.mode = Background::Mode::Inactive; + bg1.regs.priority0 = 3; bg1.regs.priority1 = 3; + bg2.regs.priority0 = 1; bg2.regs.priority1 = 5; + sprite.regs.priority0 = 2; sprite.regs.priority1 = 4; sprite.regs.priority2 = 6; sprite.regs.priority3 = 7; + } + } break; + } +} + +//INIDISP +void PPU::mmio_w2100(uint8 data) { + if(regs.display_disable && vcounter() == (!regs.overscan ? 225 : 240)) sprite.address_reset(); + regs.display_disable = data & 0x80; + regs.display_brightness = data & 0x0f; +} + +//OBSEL +void PPU::mmio_w2101(uint8 data) { + sprite.regs.base_size = (data >> 5) & 7; + sprite.regs.nameselect = (data >> 3) & 3; + sprite.regs.tiledata_addr = (data & 3) << 14; +} + +//OAMADDL +void PPU::mmio_w2102(uint8 data) { + regs.oam_baseaddr = (regs.oam_baseaddr & 0x0200) | (data << 1); + sprite.address_reset(); +} + +//OAMADDH +void PPU::mmio_w2103(uint8 data) { + regs.oam_priority = data & 0x80; + regs.oam_baseaddr = ((data & 0x01) << 9) | (regs.oam_baseaddr & 0x01fe); + sprite.address_reset(); +} + +//OAMDATA +void PPU::mmio_w2104(uint8 data) { + bool latch = regs.oam_addr & 1; + uint10 addr = regs.oam_addr++; + if(regs.display_disable == false && vcounter() < (!regs.overscan ? 225 : 240)) addr = regs.oam_iaddr; + if(addr & 0x0200) addr &= 0x021f; + + if(latch == 0) regs.oam_latchdata = data; + if(addr & 0x0200) { + sprite.update(addr, data); + } else if(latch == 1) { + sprite.update((addr & ~1) + 0, regs.oam_latchdata); + sprite.update((addr & ~1) + 1, data); + } + sprite.set_first_sprite(); +} + +//BGMODE +void PPU::mmio_w2105(uint8 data) { + bg4.regs.tile_size = (data & 0x80); + bg3.regs.tile_size = (data & 0x40); + bg2.regs.tile_size = (data & 0x20); + bg1.regs.tile_size = (data & 0x10); + regs.bg3_priority = (data & 0x08); + regs.bgmode = (data & 0x07); + mmio_update_video_mode(); +} + +//MOSAIC +void PPU::mmio_w2106(uint8 data) { + unsigned mosaic_size = (data >> 4) & 15; + bg4.regs.mosaic = (data & 0x08 ? mosaic_size : 0); + bg3.regs.mosaic = (data & 0x04 ? mosaic_size : 0); + bg2.regs.mosaic = (data & 0x02 ? mosaic_size : 0); + bg1.regs.mosaic = (data & 0x01 ? mosaic_size : 0); +} + +//BG1SC +void PPU::mmio_w2107(uint8 data) { + bg1.regs.screen_addr = (data & 0x7c) << 9; + bg1.regs.screen_size = data & 3; +} + +//BG2SC +void PPU::mmio_w2108(uint8 data) { + bg2.regs.screen_addr = (data & 0x7c) << 9; + bg2.regs.screen_size = data & 3; +} + +//BG3SC +void PPU::mmio_w2109(uint8 data) { + bg3.regs.screen_addr = (data & 0x7c) << 9; + bg3.regs.screen_size = data & 3; +} + +//BG4SC +void PPU::mmio_w210a(uint8 data) { + bg4.regs.screen_addr = (data & 0x7c) << 9; + bg4.regs.screen_size = data & 3; +} + +//BG12NBA +void PPU::mmio_w210b(uint8 data) { + bg1.regs.tiledata_addr = (data & 0x07) << 13; + bg2.regs.tiledata_addr = (data & 0x70) << 9; +} + +//BG34NBA +void PPU::mmio_w210c(uint8 data) { + bg3.regs.tiledata_addr = (data & 0x07) << 13; + bg4.regs.tiledata_addr = (data & 0x70) << 9; +} + +//BG1HOFS +void PPU::mmio_w210d(uint8 data) { + regs.mode7_hoffset = (data << 8) | regs.mode7_latchdata; + regs.mode7_latchdata = data; + + bg1.regs.hoffset = (data << 8) | (regs.bgofs_latchdata & ~7) | ((bg1.regs.hoffset >> 8) & 7); + regs.bgofs_latchdata = data; +} + +//BG1VOFS +void PPU::mmio_w210e(uint8 data) { + regs.mode7_voffset = (data << 8) | regs.mode7_latchdata; + regs.mode7_latchdata = data; + + bg1.regs.voffset = (data << 8) | regs.bgofs_latchdata; + regs.bgofs_latchdata = data; +} + +//BG2HOFS +void PPU::mmio_w210f(uint8 data) { + bg2.regs.hoffset = (data << 8) | (regs.bgofs_latchdata & ~7) | ((bg2.regs.hoffset >> 8) & 7); + regs.bgofs_latchdata = data; +} + +//BG2VOFS +void PPU::mmio_w2110(uint8 data) { + bg2.regs.voffset = (data << 8) | regs.bgofs_latchdata; + regs.bgofs_latchdata = data; +} + +//BG3HOFS +void PPU::mmio_w2111(uint8 data) { + bg3.regs.hoffset = (data << 8) | (regs.bgofs_latchdata & ~7) | ((bg3.regs.hoffset >> 8) & 7); + regs.bgofs_latchdata = data; +} + +//BG3VOFS +void PPU::mmio_w2112(uint8 data) { + bg3.regs.voffset = (data << 8) | regs.bgofs_latchdata; + regs.bgofs_latchdata = data; +} + +//BG4HOFS +void PPU::mmio_w2113(uint8 data) { + bg4.regs.hoffset = (data << 8) | (regs.bgofs_latchdata & ~7) | ((bg4.regs.hoffset >> 8) & 7); + regs.bgofs_latchdata = data; +} + +//BG4VOFS +void PPU::mmio_w2114(uint8 data) { + bg4.regs.voffset = (data << 8) | regs.bgofs_latchdata; + regs.bgofs_latchdata = data; +} + +//VMAIN +void PPU::mmio_w2115(uint8 data) { + regs.vram_incmode = data & 0x80; + regs.vram_mapping = (data >> 2) & 3; + switch(data & 3) { + case 0: regs.vram_incsize = 1; break; + case 1: regs.vram_incsize = 32; break; + case 2: regs.vram_incsize = 128; break; + case 3: regs.vram_incsize = 128; break; + } +} + +//VMADDL +void PPU::mmio_w2116(uint8 data) { + regs.vram_addr &= 0xff00; + regs.vram_addr |= (data << 0); + uint16 addr = get_vram_address(); + regs.vram_readbuffer = vram_read(addr + 0) << 0; + regs.vram_readbuffer |= vram_read(addr + 1) << 8; +} + +//VMADDH +void PPU::mmio_w2117(uint8 data) { + regs.vram_addr &= 0x00ff; + regs.vram_addr |= (data << 8); + uint16 addr = get_vram_address(); + regs.vram_readbuffer = vram_read(addr + 0) << 0; + regs.vram_readbuffer |= vram_read(addr + 1) << 8; +} + +//VMDATAL +void PPU::mmio_w2118(uint8 data) { + uint16 addr = get_vram_address() + 0; + vram_write(addr, data); + if(regs.vram_incmode == 0) regs.vram_addr += regs.vram_incsize; +} + +//VMDATAH +void PPU::mmio_w2119(uint8 data) { + uint16 addr = get_vram_address() + 1; + vram_write(addr, data); + if(regs.vram_incmode == 1) regs.vram_addr += regs.vram_incsize; +} + +//M7SEL +void PPU::mmio_w211a(uint8 data) { + regs.mode7_repeat = (data >> 6) & 3; + regs.mode7_vflip = data & 0x02; + regs.mode7_hflip = data & 0x01; +} + +//M7A +void PPU::mmio_w211b(uint8 data) { + regs.m7a = (data << 8) | regs.mode7_latchdata; + regs.mode7_latchdata = data; +} + +//M7B +void PPU::mmio_w211c(uint8 data) { + regs.m7b = (data << 8) | regs.mode7_latchdata; + regs.mode7_latchdata = data; +} + +//M7C +void PPU::mmio_w211d(uint8 data) { + regs.m7c = (data << 8) | regs.mode7_latchdata; + regs.mode7_latchdata = data; +} + +//M7D +void PPU::mmio_w211e(uint8 data) { + regs.m7d = (data << 8) | regs.mode7_latchdata; + regs.mode7_latchdata = data; +} + +//M7X +void PPU::mmio_w211f(uint8 data) { + regs.m7x = (data << 8) | regs.mode7_latchdata; + regs.mode7_latchdata = data; +} + +//M7Y +void PPU::mmio_w2120(uint8 data) { + regs.m7y = (data << 8) | regs.mode7_latchdata; + regs.mode7_latchdata = data; +} + +//CGADD +void PPU::mmio_w2121(uint8 data) { + regs.cgram_addr = data << 1; +} + +//CGDATA +void PPU::mmio_w2122(uint8 data) { + bool latch = regs.cgram_addr & 1; + uint9 addr = regs.cgram_addr++; + if(regs.display_disable == false + && vcounter() > 0 && vcounter() < (!regs.overscan ? 225 : 240) + && hcounter() >= 88 && hcounter() < 1096 + ) addr = regs.cgram_iaddr; + + if(latch == 0) { + regs.cgram_latchdata = data; + } else { + cgram[(addr & ~1) + 0] = regs.cgram_latchdata; + cgram[(addr & ~1) + 1] = data & 0x7f; + } +} + +//W12SEL +void PPU::mmio_w2123(uint8 data) { + window.regs.bg2_two_enable = data & 0x80; + window.regs.bg2_two_invert = data & 0x40; + window.regs.bg2_one_enable = data & 0x20; + window.regs.bg2_one_invert = data & 0x10; + window.regs.bg1_two_enable = data & 0x08; + window.regs.bg1_two_invert = data & 0x04; + window.regs.bg1_one_enable = data & 0x02; + window.regs.bg1_one_invert = data & 0x01; +} + +//W34SEL +void PPU::mmio_w2124(uint8 data) { + window.regs.bg4_two_enable = data & 0x80; + window.regs.bg4_two_invert = data & 0x40; + window.regs.bg4_one_enable = data & 0x20; + window.regs.bg4_one_invert = data & 0x10; + window.regs.bg3_two_enable = data & 0x08; + window.regs.bg3_two_invert = data & 0x04; + window.regs.bg3_one_enable = data & 0x02; + window.regs.bg3_one_invert = data & 0x01; +} + +//WOBJSEL +void PPU::mmio_w2125(uint8 data) { + window.regs.col_two_enable = data & 0x80; + window.regs.col_two_invert = data & 0x40; + window.regs.col_one_enable = data & 0x20; + window.regs.col_one_invert = data & 0x10; + window.regs.oam_two_enable = data & 0x08; + window.regs.oam_two_invert = data & 0x04; + window.regs.oam_one_enable = data & 0x02; + window.regs.oam_one_invert = data & 0x01; +} + +//WH0 +void PPU::mmio_w2126(uint8 data) { + window.regs.one_left = data; +} + +//WH1 +void PPU::mmio_w2127(uint8 data) { + window.regs.one_right = data; +} + +//WH2 +void PPU::mmio_w2128(uint8 data) { + window.regs.two_left = data; +} + +//WH3 +void PPU::mmio_w2129(uint8 data) { + window.regs.two_right = data; +} + +//WBGLOG +void PPU::mmio_w212a(uint8 data) { + window.regs.bg4_mask = (data >> 6) & 3; + window.regs.bg3_mask = (data >> 4) & 3; + window.regs.bg2_mask = (data >> 2) & 3; + window.regs.bg1_mask = (data >> 0) & 3; +} + +//WOBJLOG +void PPU::mmio_w212b(uint8 data) { + window.regs.col_mask = (data >> 2) & 3; + window.regs.oam_mask = (data >> 0) & 3; +} + +//TM +void PPU::mmio_w212c(uint8 data) { + sprite.regs.main_enable = data & 0x10; + bg4.regs.main_enable = data & 0x08; + bg3.regs.main_enable = data & 0x04; + bg2.regs.main_enable = data & 0x02; + bg1.regs.main_enable = data & 0x01; +} + +//TS +void PPU::mmio_w212d(uint8 data) { + sprite.regs.sub_enable = data & 0x10; + bg4.regs.sub_enable = data & 0x08; + bg3.regs.sub_enable = data & 0x04; + bg2.regs.sub_enable = data & 0x02; + bg1.regs.sub_enable = data & 0x01; +} + +//TMW +void PPU::mmio_w212e(uint8 data) { + window.regs.oam_main_enable = data & 0x10; + window.regs.bg4_main_enable = data & 0x08; + window.regs.bg3_main_enable = data & 0x04; + window.regs.bg2_main_enable = data & 0x02; + window.regs.bg1_main_enable = data & 0x01; +} + +//TSW +void PPU::mmio_w212f(uint8 data) { + window.regs.oam_sub_enable = data & 0x10; + window.regs.bg4_sub_enable = data & 0x08; + window.regs.bg3_sub_enable = data & 0x04; + window.regs.bg2_sub_enable = data & 0x02; + window.regs.bg1_sub_enable = data & 0x01; +} + +//CGWSEL +void PPU::mmio_w2130(uint8 data) { + window.regs.col_main_mask = (data >> 6) & 3; + window.regs.col_sub_mask = (data >> 4) & 3; + screen.regs.addsub_mode = data & 0x02; + screen.regs.direct_color = data & 0x01; +} + +//CGADDSUB +void PPU::mmio_w2131(uint8 data) { + screen.regs.color_mode = data & 0x80; + screen.regs.color_halve = data & 0x40; + screen.regs.back_color_enable = data & 0x20; + screen.regs.oam_color_enable = data & 0x10; + screen.regs.bg4_color_enable = data & 0x08; + screen.regs.bg3_color_enable = data & 0x04; + screen.regs.bg2_color_enable = data & 0x02; + screen.regs.bg1_color_enable = data & 0x01; +} + +//COLDATA +void PPU::mmio_w2132(uint8 data) { + if(data & 0x80) screen.regs.color_b = data & 0x1f; + if(data & 0x40) screen.regs.color_g = data & 0x1f; + if(data & 0x20) screen.regs.color_r = data & 0x1f; +} + +//SETINI +void PPU::mmio_w2133(uint8 data) { + regs.mode7_extbg = data & 0x40; + regs.pseudo_hires = data & 0x08; + regs.overscan = data & 0x04; + sprite.regs.interlace = data & 0x02; + regs.interlace = data & 0x01; + mmio_update_video_mode(); +} + +//MPYL +uint8 PPU::mmio_r2134() { + unsigned result = ((int16)regs.m7a * (int8)(regs.m7b >> 8)); + regs.ppu1_mdr = (result >> 0); + return regs.ppu1_mdr; +} + +//MPYM +uint8 PPU::mmio_r2135() { + unsigned result = ((int16)regs.m7a * (int8)(regs.m7b >> 8)); + regs.ppu1_mdr = (result >> 8); + return regs.ppu1_mdr; +} + +//MPYH +uint8 PPU::mmio_r2136() { + unsigned result = ((int16)regs.m7a * (int8)(regs.m7b >> 8)); + regs.ppu1_mdr = (result >> 16); + return regs.ppu1_mdr; +} + +//SLHV +uint8 PPU::mmio_r2137() { + if(cpu.pio() & 0x80) latch_counters(); + return cpu.regs.mdr; +} + +//OAMDATAREAD +uint8 PPU::mmio_r2138() { + uint10 addr = regs.oam_addr++; + if(regs.display_disable == false && vcounter() < (!regs.overscan ? 225 : 240)) addr = regs.oam_iaddr; + if(addr & 0x0200) addr &= 0x021f; + + regs.ppu1_mdr = oam[addr]; + sprite.set_first_sprite(); + return regs.ppu1_mdr; +} + +//VMDATALREAD +uint8 PPU::mmio_r2139() { + uint16 addr = get_vram_address() + 0; + regs.ppu1_mdr = regs.vram_readbuffer >> 0; + if(regs.vram_incmode == 0) { + addr &= ~1; + regs.vram_readbuffer = vram_read(addr + 0) << 0; + regs.vram_readbuffer |= vram_read(addr + 1) << 8; + regs.vram_addr += regs.vram_incsize; + } + return regs.ppu1_mdr; +} + +//VMDATAHREAD +uint8 PPU::mmio_r213a() { + uint16 addr = get_vram_address() + 1; + regs.ppu1_mdr = regs.vram_readbuffer >> 8; + if(regs.vram_incmode == 1) { + addr &= ~1; + regs.vram_readbuffer = vram_read(addr + 0) << 0; + regs.vram_readbuffer |= vram_read(addr + 1) << 8; + regs.vram_addr += regs.vram_incsize; + } + return regs.ppu1_mdr; +} + +//CGDATAREAD +uint8 PPU::mmio_r213b() { + bool latch = regs.cgram_addr & 1; + uint9 addr = regs.cgram_addr++; + if(regs.display_disable == false + && vcounter() > 0 && vcounter() < (!regs.overscan ? 225 : 240) + && hcounter() >= 88 && hcounter() < 1096 + ) addr = regs.cgram_iaddr; + + if(latch == 0) { + regs.ppu2_mdr = cgram[addr]; + } else { + regs.ppu2_mdr &= 0x80; + regs.ppu2_mdr |= cgram[addr]; + } + return regs.ppu2_mdr; +} + +//OPHCT +uint8 PPU::mmio_r213c() { + if(regs.latch_hcounter == 0) { + regs.ppu2_mdr = (regs.hcounter >> 0); + } else { + regs.ppu2_mdr &= 0xfe; + regs.ppu2_mdr |= (regs.hcounter >> 8) & 1; + } + regs.latch_hcounter ^= 1; + return regs.ppu2_mdr; +} + +//OPVCT +uint8 PPU::mmio_r213d() { + if(regs.latch_vcounter == 0) { + regs.ppu2_mdr = (regs.vcounter >> 0); + } else { + regs.ppu2_mdr &= 0xfe; + regs.ppu2_mdr |= (regs.vcounter >> 8) & 1; + } + regs.latch_vcounter ^= 1; + return regs.ppu2_mdr; +} + +//STAT77 +uint8 PPU::mmio_r213e() { + regs.ppu1_mdr &= 0x10; + regs.ppu1_mdr |= sprite.regs.time_over << 7; + regs.ppu1_mdr |= sprite.regs.range_over << 6; + regs.ppu1_mdr |= ppu1_version & 0x0f; + return regs.ppu1_mdr; +} + +//STAT78 +uint8 PPU::mmio_r213f() { + regs.latch_hcounter = 0; + regs.latch_vcounter = 0; + + regs.ppu2_mdr &= 0x20; + regs.ppu2_mdr |= field() << 7; + if((cpu.pio() & 0x80) == 0) { + regs.ppu2_mdr |= 0x40; + } else if(regs.counters_latched) { + regs.ppu2_mdr |= 0x40; + regs.counters_latched = false; + } + regs.ppu2_mdr |= (system.region.i == System::Region::NTSC ? 0 : 1) << 4; + regs.ppu2_mdr |= ppu2_version & 0x0f; + return regs.ppu2_mdr; +} + +void PPU::mmio_reset() { + regs.ppu1_mdr = random(0xff); + regs.ppu2_mdr = random(0xff); + + regs.vram_readbuffer = random(0x0000); + regs.oam_latchdata = random(0x00); + regs.cgram_latchdata = random(0x00); + regs.bgofs_latchdata = random(0x00); + regs.mode7_latchdata = random(0x00); + regs.counters_latched = false; + regs.latch_hcounter = 0; + regs.latch_vcounter = 0; + + regs.oam_iaddr = 0x0000; + regs.cgram_iaddr = 0x00; + + //$2100 INIDISP + regs.display_disable = true; + regs.display_brightness = 0; + + //$2102 OAMADDL + //$2103 OAMADDH + regs.oam_baseaddr = random(0x0000); + regs.oam_addr = random(0x0000); + regs.oam_priority = random(false); + + //$2105 BGMODE + regs.bg3_priority = false; + regs.bgmode = 0; + + //$210d BG1HOFS + regs.mode7_hoffset = random(0x0000); + + //$210e BG1VOFS + regs.mode7_voffset = random(0x0000); + + //$2115 VMAIN + regs.vram_incmode = random(1); + regs.vram_mapping = random(0); + regs.vram_incsize = 1; + + //$2116 VMADDL + //$2117 VMADDH + regs.vram_addr = random(0x0000); + + //$211a M7SEL + regs.mode7_repeat = random(0); + regs.mode7_vflip = random(false); + regs.mode7_hflip = random(false); + + //$211b M7A + regs.m7a = random(0x0000); + + //$211c M7B + regs.m7b = random(0x0000); + + //$211d M7C + regs.m7c = random(0x0000); + + //$211e M7D + regs.m7d = random(0x0000); + + //$211f M7X + regs.m7x = random(0x0000); + + //$2120 M7Y + regs.m7y = random(0x0000); + + //$2121 CGADD + regs.cgram_addr = random(0x0000); + + //$2133 SETINI + regs.mode7_extbg = random(false); + regs.pseudo_hires = random(false); + regs.overscan = false; + regs.interlace = false; + + //$213c OPHCT + regs.hcounter = 0; + + //$213d OPVCT + regs.vcounter = 0; +} + +uint8 PPU::mmio_read(unsigned addr) { + cpu.synchronize_ppu(); + + switch(addr & 0xffff) { + case 0x2104: + case 0x2105: + case 0x2106: + case 0x2108: + case 0x2109: + case 0x210a: + case 0x2114: + case 0x2115: + case 0x2116: + case 0x2118: + case 0x2119: + case 0x211a: + case 0x2124: + case 0x2125: + case 0x2126: + case 0x2128: + case 0x2129: + case 0x212a: return regs.ppu1_mdr; + case 0x2134: return mmio_r2134(); //MPYL + case 0x2135: return mmio_r2135(); //MPYM + case 0x2136: return mmio_r2136(); //MYPH + case 0x2137: return mmio_r2137(); //SLHV + case 0x2138: return mmio_r2138(); //OAMDATAREAD + case 0x2139: return mmio_r2139(); //VMDATALREAD + case 0x213a: return mmio_r213a(); //VMDATAHREAD + case 0x213b: return mmio_r213b(); //CGDATAREAD + case 0x213c: return mmio_r213c(); //OPHCT + case 0x213d: return mmio_r213d(); //OPVCT + case 0x213e: return mmio_r213e(); //STAT77 + case 0x213f: return mmio_r213f(); //STAT78 + } + + return cpu.regs.mdr; +} + +void PPU::mmio_write(unsigned addr, uint8 data) { + cpu.synchronize_ppu(); + + switch(addr & 0xffff) { + case 0x2100: return mmio_w2100(data); //INIDISP + case 0x2101: return mmio_w2101(data); //OBSEL + case 0x2102: return mmio_w2102(data); //OAMADDL + case 0x2103: return mmio_w2103(data); //OAMADDH + case 0x2104: return mmio_w2104(data); //OAMDATA + case 0x2105: return mmio_w2105(data); //BGMODE + case 0x2106: return mmio_w2106(data); //MOSAIC + case 0x2107: return mmio_w2107(data); //BG1SC + case 0x2108: return mmio_w2108(data); //BG2SC + case 0x2109: return mmio_w2109(data); //BG3SC + case 0x210a: return mmio_w210a(data); //BG4SC + case 0x210b: return mmio_w210b(data); //BG12NBA + case 0x210c: return mmio_w210c(data); //BG34NBA + case 0x210d: return mmio_w210d(data); //BG1HOFS + case 0x210e: return mmio_w210e(data); //BG1VOFS + case 0x210f: return mmio_w210f(data); //BG2HOFS + case 0x2110: return mmio_w2110(data); //BG2VOFS + case 0x2111: return mmio_w2111(data); //BG3HOFS + case 0x2112: return mmio_w2112(data); //BG3VOFS + case 0x2113: return mmio_w2113(data); //BG4HOFS + case 0x2114: return mmio_w2114(data); //BG4VOFS + case 0x2115: return mmio_w2115(data); //VMAIN + case 0x2116: return mmio_w2116(data); //VMADDL + case 0x2117: return mmio_w2117(data); //VMADDH + case 0x2118: return mmio_w2118(data); //VMDATAL + case 0x2119: return mmio_w2119(data); //VMDATAH + case 0x211a: return mmio_w211a(data); //M7SEL + case 0x211b: return mmio_w211b(data); //M7A + case 0x211c: return mmio_w211c(data); //M7B + case 0x211d: return mmio_w211d(data); //M7C + case 0x211e: return mmio_w211e(data); //M7D + case 0x211f: return mmio_w211f(data); //M7X + case 0x2120: return mmio_w2120(data); //M7Y + case 0x2121: return mmio_w2121(data); //CGADD + case 0x2122: return mmio_w2122(data); //CGDATA + case 0x2123: return mmio_w2123(data); //W12SEL + case 0x2124: return mmio_w2124(data); //W34SEL + case 0x2125: return mmio_w2125(data); //WOBJSEL + case 0x2126: return mmio_w2126(data); //WH0 + case 0x2127: return mmio_w2127(data); //WH1 + case 0x2128: return mmio_w2128(data); //WH2 + case 0x2129: return mmio_w2129(data); //WH3 + case 0x212a: return mmio_w212a(data); //WBGLOG + case 0x212b: return mmio_w212b(data); //WOBJLOG + case 0x212c: return mmio_w212c(data); //TM + case 0x212d: return mmio_w212d(data); //TS + case 0x212e: return mmio_w212e(data); //TMW + case 0x212f: return mmio_w212f(data); //TSW + case 0x2130: return mmio_w2130(data); //CGWSEL + case 0x2131: return mmio_w2131(data); //CGADDSUB + case 0x2132: return mmio_w2132(data); //COLDATA + case 0x2133: return mmio_w2133(data); //SETINI + } +} + +#endif diff --git a/libsnes/snes/ppu/mmio/mmio.hpp b/libsnes/snes/ppu/mmio/mmio.hpp new file mode 100644 index 0000000..0c10562 --- /dev/null +++ b/libsnes/snes/ppu/mmio/mmio.hpp @@ -0,0 +1,162 @@ +public: + uint8 mmio_read(unsigned addr); + void mmio_write(unsigned addr, uint8 data); + +private: + +struct { + uint8 ppu1_mdr; + uint8 ppu2_mdr; + + uint16 vram_readbuffer; + uint8 oam_latchdata; + uint8 cgram_latchdata; + uint8 bgofs_latchdata; + uint8 mode7_latchdata; + bool counters_latched; + bool latch_hcounter; + bool latch_vcounter; + + uint10 oam_iaddr; + uint9 cgram_iaddr; + + //$2100 INIDISP + bool display_disable; + uint4 display_brightness; + + //$2102 OAMADDL + //$2103 OAMADDH + uint10 oam_baseaddr; + uint10 oam_addr; + bool oam_priority; + + //$2105 BGMODE + bool bg3_priority; + uint8 bgmode; + + //$210d BG1HOFS + uint16 mode7_hoffset; + + //$210e BG1VOFS + uint16 mode7_voffset; + + //$2115 VMAIN + bool vram_incmode; + uint2 vram_mapping; + uint8 vram_incsize; + + //$2116 VMADDL + //$2117 VMADDH + uint16 vram_addr; + + //$211a M7SEL + uint2 mode7_repeat; + bool mode7_vflip; + bool mode7_hflip; + + //$211b M7A + uint16 m7a; + + //$211c M7B + uint16 m7b; + + //$211d M7C + uint16 m7c; + + //$211e M7D + uint16 m7d; + + //$211f M7X + uint16 m7x; + + //$2120 M7Y + uint16 m7y; + + //$2121 CGADD + uint9 cgram_addr; + + //$2133 SETINI + bool mode7_extbg; + bool pseudo_hires; + bool overscan; + bool interlace; + + //$213c OPHCT + uint16 hcounter; + + //$213d OPVCT + uint16 vcounter; +} regs; + +uint16 get_vram_address(); +uint8 vram_read(unsigned addr); +void vram_write(unsigned addr, uint8 data); + +void mmio_update_video_mode(); + +void mmio_w2100(uint8); //INIDISP +void mmio_w2101(uint8); //OBSEL +void mmio_w2102(uint8); //OAMADDL +void mmio_w2103(uint8); //OAMADDH +void mmio_w2104(uint8); //OAMDATA +void mmio_w2105(uint8); //BGMODE +void mmio_w2106(uint8); //MOSAIC +void mmio_w2107(uint8); //BG1SC +void mmio_w2108(uint8); //BG2SC +void mmio_w2109(uint8); //BG3SC +void mmio_w210a(uint8); //BG4SC +void mmio_w210b(uint8); //BG12NBA +void mmio_w210c(uint8); //BG34NBA +void mmio_w210d(uint8); //BG1HOFS +void mmio_w210e(uint8); //BG1VOFS +void mmio_w210f(uint8); //BG2HOFS +void mmio_w2110(uint8); //BG2VOFS +void mmio_w2111(uint8); //BG3HOFS +void mmio_w2112(uint8); //BG3VOFS +void mmio_w2113(uint8); //BG4HOFS +void mmio_w2114(uint8); //BG4VOFS +void mmio_w2115(uint8); //VMAIN +void mmio_w2116(uint8); //VMADDL +void mmio_w2117(uint8); //VMADDH +void mmio_w2118(uint8); //VMDATAL +void mmio_w2119(uint8); //VMDATAH +void mmio_w211a(uint8); //M7SEL +void mmio_w211b(uint8); //M7A +void mmio_w211c(uint8); //M7B +void mmio_w211d(uint8); //M7C +void mmio_w211e(uint8); //M7D +void mmio_w211f(uint8); //M7X +void mmio_w2120(uint8); //M7Y +void mmio_w2121(uint8); //CGADD +void mmio_w2122(uint8); //CGDATA +void mmio_w2123(uint8); //W12SEL +void mmio_w2124(uint8); //W34SEL +void mmio_w2125(uint8); //WOBJSEL +void mmio_w2126(uint8); //WH0 +void mmio_w2127(uint8); //WH1 +void mmio_w2128(uint8); //WH2 +void mmio_w2129(uint8); //WH3 +void mmio_w212a(uint8); //WBGLOG +void mmio_w212b(uint8); //WOBJLOG +void mmio_w212c(uint8); //TM +void mmio_w212d(uint8); //TS +void mmio_w212e(uint8); //TMW +void mmio_w212f(uint8); //TSW +void mmio_w2130(uint8); //CGWSEL +void mmio_w2131(uint8); //CGADDSUB +void mmio_w2132(uint8); //COLDATA +void mmio_w2133(uint8); //SETINI +uint8 mmio_r2134(); //MPYL +uint8 mmio_r2135(); //MPYM +uint8 mmio_r2136(); //MPYH +uint8 mmio_r2137(); //SLHV +uint8 mmio_r2138(); //OAMDATAREAD +uint8 mmio_r2139(); //VMDATALREAD +uint8 mmio_r213a(); //VMDATAHREAD +uint8 mmio_r213b(); //CGDATAREAD +uint8 mmio_r213c(); //OPHCT +uint8 mmio_r213d(); //OPVCT +uint8 mmio_r213e(); //STAT77 +uint8 mmio_r213f(); //STAT78 + +void mmio_reset(); diff --git a/libsnes/snes/ppu/ppu.cpp b/libsnes/snes/ppu/ppu.cpp new file mode 100644 index 0000000..be301bf --- /dev/null +++ b/libsnes/snes/ppu/ppu.cpp @@ -0,0 +1,162 @@ +#include + +#define PPU_CPP +namespace SNES { + +#if defined(DEBUGGER) + #include "debugger/debugger.cpp" + PPUDebugger ppu; +#else + PPU ppu; +#endif + +#include "background/background.cpp" +#include "mmio/mmio.cpp" +#include "screen/screen.cpp" +#include "sprite/sprite.cpp" +#include "window/window.cpp" +#include "serialization.cpp" + +void PPU::step(unsigned clocks) { + clock += clocks; +} + +void PPU::synchronize_cpu() { + if(CPU::Threaded == true) { + if(clock >= 0 && scheduler.sync.i != Scheduler::SynchronizeMode::All) co_switch(cpu.thread); + } else { + while(clock >= 0) cpu.enter(); + } +} + +void PPU::Enter() { ppu.enter(); } + +void PPU::enter() { + while(true) { + if(scheduler.sync.i == Scheduler::SynchronizeMode::All) { + scheduler.exit(Scheduler::ExitReason::SynchronizeEvent); + } + + scanline(); + add_clocks(60); + bg1.begin(); + bg2.begin(); + bg3.begin(); + bg4.begin(); + + if(vcounter() <= 239) { + for(signed pixel = -7; pixel <= 255; pixel++) { + bg1.run(1); + bg2.run(1); + bg3.run(1); + bg4.run(1); + add_clocks(2); + + bg1.run(0); + bg2.run(0); + bg3.run(0); + bg4.run(0); + if(pixel >= 0) { + sprite.run(); + window.run(); + screen.run(); + } + add_clocks(2); + } + + add_clocks(22); + sprite.tilefetch(); + } else { + add_clocks(1052 + 22 + 136); + } + + add_clocks(lineclocks() - 60 - 1052 - 22 - 136); + } +} + +void PPU::add_clocks(unsigned clocks) { + clocks >>= 1; + while(clocks--) { + tick(2); + step(2); + synchronize_cpu(); + } +} + +void PPU::enable() { + function read(&PPU::mmio_read, (PPU*)&ppu); + function write(&PPU::mmio_write, (PPU*)&ppu); + + bus.map(Bus::MapMode::Direct, 0x00, 0x3f, 0x2100, 0x213f, read, write); + bus.map(Bus::MapMode::Direct, 0x80, 0xbf, 0x2100, 0x213f, read, write); +} + +void PPU::power() { + ppu1_version = config.ppu1.version; + ppu2_version = config.ppu2.version; + + foreach(n, vram) n = random(0x00); + foreach(n, oam) n = random(0x00); + foreach(n, cgram) n = random(0x00); +} + +void PPU::reset() { + create(Enter, system.cpu_frequency); + PPUcounter::reset(); + memset(surface, 0, 512 * 512 * sizeof(uint32)); + + mmio_reset(); + bg1.reset(); + bg2.reset(); + bg3.reset(); + bg4.reset(); + sprite.reset(); + window.reset(); + screen.reset(); + + frame(); +} + +void PPU::scanline() { + if(vcounter() == 0) { + frame(); + bg1.frame(); + bg2.frame(); + bg3.frame(); + bg4.frame(); + } + + bg1.scanline(); + bg2.scanline(); + bg3.scanline(); + bg4.scanline(); + sprite.scanline(); + window.scanline(); + screen.scanline(); +} + +void PPU::frame() { + system.frame(); + sprite.frame(); + + display.interlace = regs.interlace; + display.overscan = regs.overscan; +} + +PPU::PPU() : +bg1(*this, Background::ID::BG1), +bg2(*this, Background::ID::BG2), +bg3(*this, Background::ID::BG3), +bg4(*this, Background::ID::BG4), +sprite(*this), +window(*this), +screen(*this) { + surface = new uint32[512 * 512]; + output = surface + 16 * 512; +} + +PPU::~PPU() { + delete[] surface; +} + +} diff --git a/libsnes/snes/ppu/ppu.hpp b/libsnes/snes/ppu/ppu.hpp new file mode 100644 index 0000000..0b40cee --- /dev/null +++ b/libsnes/snes/ppu/ppu.hpp @@ -0,0 +1,68 @@ +struct PPU : public Processor, public PPUcounter { + uint8 vram[64 * 1024]; + uint8 oam[544]; + uint8 cgram[512]; + + enum{ Threaded = true }; + alwaysinline void step(unsigned clocks); + alwaysinline void synchronize_cpu(); + + void latch_counters(); + bool interlace() const; + bool overscan() const; + bool hires() const; + + void enter(); + void enable(); + void power(); + void reset(); + + void serialize(serializer&); + PPU(); + ~PPU(); + +private: + uint32 *surface; + uint32 *output; + + uint8 ppu1_version; + uint8 ppu2_version; + + struct { + bool interlace; + bool overscan; + } display; + + #include "background/background.hpp" + #include "mmio/mmio.hpp" + #include "screen/screen.hpp" + #include "sprite/sprite.hpp" + #include "window/window.hpp" + + Background bg1; + Background bg2; + Background bg3; + Background bg4; + Sprite sprite; + Window window; + Screen screen; + + static void Enter(); + void add_clocks(unsigned); + + void scanline(); + void frame(); + + friend class PPU::Background; + friend class PPU::Sprite; + friend class PPU::Window; + friend class PPU::Screen; + friend class Video; +}; + +#if defined(DEBUGGER) + #include "debugger/debugger.hpp" + extern PPUDebugger ppu; +#else + extern PPU ppu; +#endif diff --git a/libsnes/snes/ppu/screen/screen.cpp b/libsnes/snes/ppu/screen/screen.cpp new file mode 100644 index 0000000..64d2464 --- /dev/null +++ b/libsnes/snes/ppu/screen/screen.cpp @@ -0,0 +1,210 @@ +#ifdef PPU_CPP + +void PPU::Screen::scanline() { + output = self.output + self.vcounter() * 1024; + if(self.display.interlace && self.field()) output += 512; +} + +void PPU::Screen::run() { + if(ppu.vcounter() == 0) return; + + uint32 color; + if(self.regs.pseudo_hires == false && self.regs.bgmode != 5 && self.regs.bgmode != 6) { + color = get_pixel(0); + *output++ = color; + *output++ = color; + } else { + color = get_pixel(1); + *output++ = color; + color = get_pixel(0); + *output++ = color; + } +} + +uint32 PPU::Screen::get_pixel(bool swap) { + if(ppu.regs.overscan == false && ppu.vcounter() >= 225) return 0x0000; + + enum source_t { BG1, BG2, BG3, BG4, OAM, BACK }; + bool color_enable[] = { regs.bg1_color_enable, regs.bg2_color_enable, regs.bg3_color_enable, regs.bg4_color_enable, regs.oam_color_enable, regs.back_color_enable }; + + //=========== + //main screen + //=========== + + unsigned priority_main = 0; + unsigned color_main; + unsigned source_main; + + if(self.bg1.output.main.priority) { + priority_main = self.bg1.output.main.priority; + if(regs.direct_color && (self.regs.bgmode == 3 || self.regs.bgmode == 4 || self.regs.bgmode == 7)) { + color_main = get_direct_color(self.bg1.output.main.palette, self.bg1.output.main.tile); + } else { + color_main = get_color(self.bg1.output.main.palette); + } + source_main = BG1; + } + if(self.bg2.output.main.priority > priority_main) { + priority_main = self.bg2.output.main.priority; + color_main = get_color(self.bg2.output.main.palette); + source_main = BG2; + } + if(self.bg3.output.main.priority > priority_main) { + priority_main = self.bg3.output.main.priority; + color_main = get_color(self.bg3.output.main.palette); + source_main = BG3; + } + if(self.bg4.output.main.priority > priority_main) { + priority_main = self.bg4.output.main.priority; + color_main = get_color(self.bg4.output.main.palette); + source_main = BG4; + } + if(self.sprite.output.main.priority > priority_main) { + priority_main = self.sprite.output.main.priority; + color_main = get_color(self.sprite.output.main.palette); + source_main = OAM; + } + if(priority_main == 0) { + color_main = get_color(0); + source_main = BACK; + } + + //========== + //sub screen + //========== + + unsigned priority_sub = 0; + unsigned color_sub; + unsigned source_sub; + + if(self.bg1.output.sub.priority) { + priority_sub = self.bg1.output.sub.priority; + if(regs.direct_color && (self.regs.bgmode == 3 || self.regs.bgmode == 4 || self.regs.bgmode == 7)) { + color_sub = get_direct_color(self.bg1.output.sub.palette, self.bg1.output.sub.tile); + } else { + color_sub = get_color(self.bg1.output.sub.palette); + } + source_sub = BG1; + } + if(self.bg2.output.sub.priority > priority_sub) { + priority_sub = self.bg2.output.sub.priority; + color_sub = get_color(self.bg2.output.sub.palette); + source_sub = BG2; + } + if(self.bg3.output.sub.priority > priority_sub) { + priority_sub = self.bg3.output.sub.priority; + color_sub = get_color(self.bg3.output.sub.palette); + source_sub = BG3; + } + if(self.bg4.output.sub.priority > priority_sub) { + priority_sub = self.bg4.output.sub.priority; + color_sub = get_color(self.bg4.output.sub.palette); + source_sub = BG4; + } + if(self.sprite.output.sub.priority > priority_sub) { + priority_sub = self.sprite.output.sub.priority; + color_sub = get_color(self.sprite.output.sub.palette); + source_sub = OAM; + } + if(priority_sub == 0) { + if(self.regs.pseudo_hires == true || self.regs.bgmode == 5 || self.regs.bgmode == 6) { + color_sub = get_color(0); + } else { + color_sub = (regs.color_b << 10) + (regs.color_g << 5) + (regs.color_r << 0); + } + source_sub = BACK; + } + + if(swap == true) { + nall::swap(priority_main, priority_sub); + nall::swap(color_main, color_sub); + nall::swap(source_main, source_sub); + } + + uint16 output; + if(!regs.addsub_mode) { + source_sub = BACK; + color_sub = (regs.color_b << 10) + (regs.color_g << 5) + (regs.color_r << 0); + } + + if(self.window.output.main.color_enable == false) { + if(self.window.output.sub.color_enable == false) { + return 0x0000; + } + color_main = 0x0000; + } + + bool color_exempt = (source_main == OAM && self.sprite.output.main.palette < 192); + if(!color_exempt && color_enable[source_main] && self.window.output.sub.color_enable) { + bool halve = false; + if(regs.color_halve && self.window.output.main.color_enable) { + if(!regs.addsub_mode || source_sub != BACK) halve = true; + } + output = addsub(color_main, color_sub, halve); + } else { + output = color_main; + } + + //======== + //lighting + //======== + + if(self.regs.display_disable) return 0; + return (self.regs.display_brightness << 15) | output; +} + +uint16 PPU::Screen::addsub(unsigned x, unsigned y, bool halve) { + if(!regs.color_mode) { + if(!halve) { + unsigned sum = x + y; + unsigned carry = (sum - ((x ^ y) & 0x0421)) & 0x8420; + return (sum - carry) | (carry - (carry >> 5)); + } else { + return (x + y - ((x ^ y) & 0x0421)) >> 1; + } + } else { + unsigned diff = x - y + 0x8420; + unsigned borrow = (diff - ((x ^ y) & 0x8420)) & 0x8420; + if(!halve) { + return (diff - borrow) & (borrow - (borrow >> 5)); + } else { + return (((diff - borrow) & (borrow - (borrow >> 5))) & 0x7bde) >> 1; + } + } +} + +uint16 PPU::Screen::get_color(unsigned palette) { + palette <<= 1; + self.regs.cgram_iaddr = palette; + return ppu.cgram[palette + 0] + (ppu.cgram[palette + 1] << 8); +} + +uint16 PPU::Screen::get_direct_color(unsigned palette, unsigned tile) { + //palette = -------- BBGGGRRR + //tile = ---bgr-- -------- + //output = 0BBb00GG Gg0RRRr0 + return ((palette << 7) & 0x6000) + ((tile >> 0) & 0x1000) + + ((palette << 4) & 0x0380) + ((tile >> 5) & 0x0040) + + ((palette << 2) & 0x001c) + ((tile >> 9) & 0x0002); +} + +void PPU::Screen::reset() { + regs.addsub_mode = random(false); + regs.direct_color = random(false); + regs.color_mode = random(false); + regs.color_halve = random(false); + regs.bg1_color_enable = random(false); + regs.bg2_color_enable = random(false); + regs.bg3_color_enable = random(false); + regs.bg4_color_enable = random(false); + regs.oam_color_enable = random(false); + regs.back_color_enable = random(false); + regs.color_r = random(0); + regs.color_g = random(0); + regs.color_b = random(0); +} + +PPU::Screen::Screen(PPU &self) : self(self) { +} + +#endif diff --git a/libsnes/snes/ppu/screen/screen.hpp b/libsnes/snes/ppu/screen/screen.hpp new file mode 100644 index 0000000..1550233 --- /dev/null +++ b/libsnes/snes/ppu/screen/screen.hpp @@ -0,0 +1,36 @@ +class Screen { + uint32 *output; + + struct Regs { + bool addsub_mode; + bool direct_color; + + bool color_mode; + bool color_halve; + bool bg1_color_enable; + bool bg2_color_enable; + bool bg3_color_enable; + bool bg4_color_enable; + bool oam_color_enable; + bool back_color_enable; + + uint5 color_b; + uint5 color_g; + uint5 color_r; + } regs; + + void scanline(); + void run(); + void reset(); + + uint32 get_pixel(bool swap); + uint16 addsub(unsigned x, unsigned y, bool halve); + uint16 get_color(unsigned palette); + uint16 get_direct_color(unsigned palette, unsigned tile); + + void serialize(serializer&); + Screen(PPU &self); + + PPU &self; + friend class PPU; +}; diff --git a/libsnes/snes/ppu/serialization.cpp b/libsnes/snes/ppu/serialization.cpp new file mode 100644 index 0000000..edf90f1 --- /dev/null +++ b/libsnes/snes/ppu/serialization.cpp @@ -0,0 +1,284 @@ +#ifdef PPU_CPP + +void PPUcounter::serialize(serializer &s) { + s.integer(status.interlace); + s.integer(status.field); + s.integer(status.vcounter); + s.integer(status.hcounter); + + s.array(history.field); + s.array(history.vcounter); + s.array(history.hcounter); + s.integer(history.index); +} + +void PPU::serialize(serializer &s) { + Processor::serialize(s); + PPUcounter::serialize(s); + + s.array(vram); + s.array(oam); + s.array(cgram); + + s.integer(ppu1_version); + s.integer(ppu2_version); + + s.integer(display.interlace); + s.integer(display.overscan); + + s.integer(regs.ppu1_mdr); + s.integer(regs.ppu2_mdr); + + s.integer(regs.vram_readbuffer); + s.integer(regs.oam_latchdata); + s.integer(regs.cgram_latchdata); + s.integer(regs.bgofs_latchdata); + s.integer(regs.mode7_latchdata); + s.integer(regs.counters_latched); + s.integer(regs.latch_hcounter); + s.integer(regs.latch_vcounter); + + s.integer(regs.oam_iaddr); + s.integer(regs.cgram_iaddr); + + s.integer(regs.display_disable); + s.integer(regs.display_brightness); + + s.integer(regs.oam_baseaddr); + s.integer(regs.oam_addr); + s.integer(regs.oam_priority); + + s.integer(regs.bg3_priority); + s.integer(regs.bgmode); + + s.integer(regs.mode7_hoffset); + s.integer(regs.mode7_voffset); + + s.integer(regs.vram_incmode); + s.integer(regs.vram_mapping); + s.integer(regs.vram_incsize); + + s.integer(regs.vram_addr); + + s.integer(regs.mode7_repeat); + s.integer(regs.mode7_vflip); + s.integer(regs.mode7_hflip); + + s.integer(regs.m7a); + s.integer(regs.m7b); + s.integer(regs.m7c); + s.integer(regs.m7d); + s.integer(regs.m7x); + s.integer(regs.m7y); + + s.integer(regs.cgram_addr); + + s.integer(regs.mode7_extbg); + s.integer(regs.pseudo_hires); + s.integer(regs.overscan); + s.integer(regs.interlace); + + s.integer(regs.hcounter); + s.integer(regs.vcounter); + + bg1.serialize(s); + bg2.serialize(s); + bg3.serialize(s); + bg4.serialize(s); + sprite.serialize(s); + window.serialize(s); + screen.serialize(s); +} + +void PPU::Background::serialize(serializer &s) { + s.integer(id); + + s.integer(regs.tiledata_addr); + s.integer(regs.screen_addr); + s.integer(regs.screen_size); + s.integer(regs.mosaic); + s.integer(regs.tile_size); + + s.integer(regs.mode); + s.integer(regs.priority0); + s.integer(regs.priority1); + + s.integer(regs.main_enable); + s.integer(regs.sub_enable); + + s.integer(regs.hoffset); + s.integer(regs.voffset); + + s.integer(cache.hoffset); + s.integer(cache.voffset); + + s.integer(output.main.priority); + s.integer(output.main.palette); + s.integer(output.main.tile); + + s.integer(output.sub.priority); + s.integer(output.sub.palette); + s.integer(output.sub.tile); + + s.integer(x); + s.integer(y); + + s.integer(mosaic.priority); + s.integer(mosaic.palette); + s.integer(mosaic.tile); + + s.integer(mosaic.vcounter); + s.integer(mosaic.voffset); + s.integer(mosaic.hcounter); + s.integer(mosaic.hoffset); + + s.integer(tile_counter); + s.integer(tile); + s.integer(priority); + s.integer(palette_number); + s.integer(palette_index); + s.array(data); +} + +void PPU::Sprite::serialize(serializer &s) { + for(unsigned i = 0; i < 128; i++) { + s.integer(list[i].x); + s.integer(list[i].y); + s.integer(list[i].character); + s.integer(list[i].nameselect); + s.integer(list[i].vflip); + s.integer(list[i].hflip); + s.integer(list[i].priority); + s.integer(list[i].palette); + s.integer(list[i].size); + } + + s.integer(t.x); + s.integer(t.y); + + s.integer(t.item_count); + s.integer(t.tile_count); + + s.integer(t.active); + for(unsigned n = 0; n < 2; n++) { + s.array(t.item[n]); + for(unsigned i = 0; i < 34; i++) { + s.integer(t.tile[n][i].x); + s.integer(t.tile[n][i].priority); + s.integer(t.tile[n][i].palette); + s.integer(t.tile[n][i].hflip); + s.integer(t.tile[n][i].d0); + s.integer(t.tile[n][i].d1); + s.integer(t.tile[n][i].d2); + s.integer(t.tile[n][i].d3); + } + } + + s.integer(regs.main_enable); + s.integer(regs.sub_enable); + s.integer(regs.interlace); + + s.integer(regs.base_size); + s.integer(regs.nameselect); + s.integer(regs.tiledata_addr); + s.integer(regs.first_sprite); + + s.integer(regs.priority0); + s.integer(regs.priority1); + s.integer(regs.priority2); + s.integer(regs.priority3); + + s.integer(regs.time_over); + s.integer(regs.range_over); + + s.integer(output.main.priority); + s.integer(output.main.palette); + + s.integer(output.sub.priority); + s.integer(output.sub.palette); +} + +void PPU::Window::serialize(serializer &s) { + s.integer(regs.bg1_one_enable); + s.integer(regs.bg1_one_invert); + s.integer(regs.bg1_two_enable); + s.integer(regs.bg1_two_invert); + + s.integer(regs.bg2_one_enable); + s.integer(regs.bg2_one_invert); + s.integer(regs.bg2_two_enable); + s.integer(regs.bg2_two_invert); + + s.integer(regs.bg3_one_enable); + s.integer(regs.bg3_one_invert); + s.integer(regs.bg3_two_enable); + s.integer(regs.bg3_two_invert); + + s.integer(regs.bg4_one_enable); + s.integer(regs.bg4_one_invert); + s.integer(regs.bg4_two_enable); + s.integer(regs.bg4_two_invert); + + s.integer(regs.oam_one_enable); + s.integer(regs.oam_one_invert); + s.integer(regs.oam_two_enable); + s.integer(regs.oam_two_invert); + + s.integer(regs.col_one_enable); + s.integer(regs.col_one_invert); + s.integer(regs.col_two_enable); + s.integer(regs.col_two_invert); + + s.integer(regs.one_left); + s.integer(regs.one_right); + s.integer(regs.two_left); + s.integer(regs.two_right); + + s.integer(regs.bg1_mask); + s.integer(regs.bg2_mask); + s.integer(regs.bg3_mask); + s.integer(regs.bg4_mask); + s.integer(regs.oam_mask); + s.integer(regs.col_mask); + + s.integer(regs.bg1_main_enable); + s.integer(regs.bg1_sub_enable); + s.integer(regs.bg2_main_enable); + s.integer(regs.bg2_sub_enable); + s.integer(regs.bg3_main_enable); + s.integer(regs.bg3_sub_enable); + s.integer(regs.bg4_main_enable); + s.integer(regs.bg4_sub_enable); + s.integer(regs.oam_main_enable); + s.integer(regs.oam_sub_enable); + + s.integer(regs.col_main_mask); + s.integer(regs.col_sub_mask); + + s.integer(output.main.color_enable); + s.integer(output.sub.color_enable); + + s.integer(x); + s.integer(one); + s.integer(two); +} + +void PPU::Screen::serialize(serializer &s) { + s.integer(regs.addsub_mode); + s.integer(regs.direct_color); + + s.integer(regs.color_mode); + s.integer(regs.color_halve); + s.integer(regs.bg1_color_enable); + s.integer(regs.bg2_color_enable); + s.integer(regs.bg3_color_enable); + s.integer(regs.bg4_color_enable); + s.integer(regs.oam_color_enable); + s.integer(regs.back_color_enable); + + s.integer(regs.color_b); + s.integer(regs.color_g); + s.integer(regs.color_r); +} + +#endif diff --git a/libsnes/snes/ppu/sprite/list.cpp b/libsnes/snes/ppu/sprite/list.cpp new file mode 100644 index 0000000..dda3588 --- /dev/null +++ b/libsnes/snes/ppu/sprite/list.cpp @@ -0,0 +1,60 @@ +#ifdef PPU_CPP + +void PPU::Sprite::update(unsigned addr, uint8 data) { + ppu.oam[addr] = data; + + if(addr < 0x0200) { + unsigned n = addr >> 2; + addr &= 3; + if(addr == 0) { + list[n].x = (list[n].x & 0x100) | data; + } else if(addr == 1) { + list[n].y = data; + } else if(addr == 2) { + list[n].character = data; + } else { //(addr == 3) + list[n].vflip = data & 0x80; + list[n].hflip = data & 0x40; + list[n].priority = (data >> 4) & 3; + list[n].palette = (data >> 1) & 7; + list[n].nameselect = data & 1; + } + } else { + unsigned n = (addr & 0x1f) << 2; + list[n + 0].x = ((data & 0x01) << 8) | (list[n + 0].x & 0xff); + list[n + 0].size = data & 0x02; + list[n + 1].x = ((data & 0x04) << 6) | (list[n + 1].x & 0xff); + list[n + 1].size = data & 0x08; + list[n + 2].x = ((data & 0x10) << 4) | (list[n + 2].x & 0xff); + list[n + 2].size = data & 0x20; + list[n + 3].x = ((data & 0x40) << 2) | (list[n + 3].x & 0xff); + list[n + 3].size = data & 0x80; + } +} + +void PPU::Sprite::synchronize() { + for(unsigned n = 0; n < 544; n++) update(n, ppu.oam[n]); +} + +unsigned PPU::Sprite::SpriteItem::width() const { + if(size == 0) { + static unsigned width[] = { 8, 8, 8, 16, 16, 32, 16, 16 }; + return width[ppu.sprite.regs.base_size]; + } else { + static unsigned width[] = { 16, 32, 64, 32, 64, 64, 32, 32 }; + return width[ppu.sprite.regs.base_size]; + } +} + +unsigned PPU::Sprite::SpriteItem::height() const { + if(size == 0) { + if(ppu.sprite.regs.interlace && ppu.sprite.regs.base_size >= 6) return 16; + static unsigned height[] = { 8, 8, 8, 16, 16, 32, 32, 32 }; + return height[ppu.sprite.regs.base_size]; + } else { + static unsigned height[] = { 16, 32, 64, 32, 64, 64, 64, 32 }; + return height[ppu.sprite.regs.base_size]; + } +} + +#endif diff --git a/libsnes/snes/ppu/sprite/sprite.cpp b/libsnes/snes/ppu/sprite/sprite.cpp new file mode 100644 index 0000000..7ae5e06 --- /dev/null +++ b/libsnes/snes/ppu/sprite/sprite.cpp @@ -0,0 +1,223 @@ +#ifdef PPU_CPP + +#include "list.cpp" + +void PPU::Sprite::address_reset() { + self.regs.oam_addr = self.regs.oam_baseaddr; + set_first_sprite(); +} + +void PPU::Sprite::set_first_sprite() { + regs.first_sprite = (self.regs.oam_priority == false ? 0 : (self.regs.oam_addr >> 2) & 127); +} + +void PPU::Sprite::frame() { + regs.time_over = false; + regs.range_over = false; +} + +void PPU::Sprite::scanline() { + t.x = 0; + t.y = self.vcounter(); + + t.item_count = 0; + t.tile_count = 0; + + t.active = !t.active; + uint8 *oam_item = t.item[t.active]; + TileItem *oam_tile = t.tile[t.active]; + + if(t.y == (!self.regs.overscan ? 225 : 240) && self.regs.display_disable == false) address_reset(); + if(t.y >= (!self.regs.overscan ? 224 : 239)) return; + + memset(oam_item, 0xff, 32); //default to invalid + for(unsigned i = 0; i < 34; i++) oam_tile[i].x = 0xffff; //default to invalid + + for(unsigned i = 0; i < 128; i++) { + unsigned sprite = (regs.first_sprite + i) & 127; + if(on_scanline(list[sprite]) == false) continue; + if(t.item_count++ >= 32) break; + oam_item[t.item_count - 1] = sprite; + } + + if(t.item_count > 0 && oam_item[t.item_count - 1] != 0xff) { + ppu.regs.oam_iaddr = 0x0200 + (oam_item[t.item_count - 1] >> 2); + } +} + +bool PPU::Sprite::on_scanline(SpriteItem &sprite) { + if(sprite.x > 256 && (sprite.x + sprite.width() - 1) < 512) return false; + signed height = (regs.interlace == false ? sprite.height() : (sprite.height() >> 1)); + if(t.y >= sprite.y && t.y < (sprite.y + height)) return true; + if((sprite.y + height) >= 256 && t.y < ((sprite.y + height) & 255)) return true; + return false; +} + +void PPU::Sprite::run() { + output.main.priority = 0; + output.sub.priority = 0; + + TileItem* oam_tile = t.tile[!t.active]; + unsigned priority_table[] = { regs.priority0, regs.priority1, regs.priority2, regs.priority3 }; + unsigned x = t.x++; + + for(unsigned n = 0; n < 34; n++) { + const TileItem& tile = oam_tile[n]; + if(tile.x == 0xffff) break; + + int px = x - sclip<9>(tile.x); + if(px & ~7) continue; + + unsigned mask = 0x80 >> (tile.hflip == false ? px : 7 - px); + unsigned color; + color = ((bool)(tile.d0 & mask)) << 0; + color |= ((bool)(tile.d1 & mask)) << 1; + color |= ((bool)(tile.d2 & mask)) << 2; + color |= ((bool)(tile.d3 & mask)) << 3; + + if(color) { + if(regs.main_enable) { + output.main.palette = tile.palette + color; + output.main.priority = priority_table[tile.priority]; + } + + if(regs.sub_enable) { + output.sub.palette = tile.palette + color; + output.sub.priority = priority_table[tile.priority]; + } + } + } +} + +void PPU::Sprite::tilefetch() { + uint8 *oam_item = t.item[t.active]; + TileItem *oam_tile = t.tile[t.active]; + + for(signed i = 31; i >= 0; i--) { + if(oam_item[i] == 0xff) continue; + const SpriteItem& sprite = list[oam_item[i]]; + + unsigned tile_width = sprite.width() >> 3; + signed x = sprite.x; + signed y = (t.y - sprite.y) & 0xff; + if(regs.interlace) y <<= 1; + + if(sprite.vflip) { + if(sprite.width() == sprite.height()) { + y = (sprite.height() - 1) - y; + } else if(y < sprite.width()) { + y = (sprite.width() - 1) - y; + } else { + y = sprite.width() + ((sprite.width() - 1) - (y - sprite.width())); + } + } + + if(regs.interlace) { + y = (sprite.vflip == false ? y + self.field() : y - self.field()); + } + + x &= 511; + y &= 255; + + uint16 tiledata_addr = regs.tiledata_addr; + uint16 chrx = (sprite.character >> 0) & 15; + uint16 chry = (sprite.character >> 4) & 15; + if(sprite.nameselect) { + tiledata_addr += (256 * 32) + (regs.nameselect << 13); + } + chry += (y >> 3); + chry &= 15; + chry <<= 4; + + for(unsigned tx = 0; tx < tile_width; tx++) { + unsigned sx = (x + (tx << 3)) & 511; + if(x != 256 && sx >= 256 && (sx + 7) < 512) continue; + if(t.tile_count++ >= 34) break; + + unsigned n = t.tile_count - 1; + oam_tile[n].x = sx; + oam_tile[n].priority = sprite.priority; + oam_tile[n].palette = 128 + (sprite.palette << 4); + oam_tile[n].hflip = sprite.hflip; + + unsigned mx = (sprite.hflip == false) ? tx : ((tile_width - 1) - tx); + unsigned pos = tiledata_addr + ((chry + ((chrx + mx) & 15)) << 5); + uint16 addr = (pos & 0xffe0) + ((y & 7) * 2); + + oam_tile[n].d0 = ppu.vram[addr + 0]; + oam_tile[n].d1 = ppu.vram[addr + 1]; + self.add_clocks(2); + + oam_tile[n].d2 = ppu.vram[addr + 16]; + oam_tile[n].d3 = ppu.vram[addr + 17]; + self.add_clocks(2); + } + } + + if(t.tile_count < 34) self.add_clocks((34 - t.tile_count) * 4); + regs.time_over |= (t.tile_count > 34); + regs.range_over |= (t.item_count > 32); +} + +void PPU::Sprite::reset() { + for(unsigned i = 0; i < 128; i++) { + list[i].x = 0; + list[i].y = 0; + list[i].character = 0; + list[i].nameselect = 0; + list[i].vflip = 0; + list[i].hflip = 0; + list[i].priority = 0; + list[i].palette = 0; + list[i].size = 0; + } + synchronize(); + + t.x = 0; + t.y = 0; + + t.item_count = 0; + t.tile_count = 0; + + t.active = 0; + for(unsigned n = 0; n < 2; n++) { + memset(t.item[n], 0, 32); + for(unsigned i = 0; i < 34; i++) { + t.tile[n][i].x = 0; + t.tile[n][i].priority = 0; + t.tile[n][i].palette = 0; + t.tile[n][i].hflip = 0; + t.tile[n][i].d0 = 0; + t.tile[n][i].d1 = 0; + t.tile[n][i].d2 = 0; + t.tile[n][i].d3 = 0; + } + } + + regs.main_enable = random(false); + regs.sub_enable = random(false); + regs.interlace = random(false); + + regs.base_size = random(0); + regs.nameselect = random(0); + regs.tiledata_addr = random(0x0000); + regs.first_sprite = 0; + + regs.priority0 = 0; + regs.priority1 = 0; + regs.priority2 = 0; + regs.priority3 = 0; + + regs.time_over = false; + regs.range_over = false; + + output.main.palette = 0; + output.main.priority = 0; + output.sub.palette = 0; + output.sub.priority = 0; +} + +PPU::Sprite::Sprite(PPU &self) : self(self) { +} + +#endif diff --git a/libsnes/snes/ppu/sprite/sprite.hpp b/libsnes/snes/ppu/sprite/sprite.hpp new file mode 100644 index 0000000..06ab39e --- /dev/null +++ b/libsnes/snes/ppu/sprite/sprite.hpp @@ -0,0 +1,81 @@ +struct Sprite { + struct SpriteItem { + uint16 x; + uint16 y; + uint8 character; + bool nameselect; + bool vflip; + bool hflip; + uint8 priority; + uint8 palette; + bool size; + unsigned width() const; + unsigned height() const; + } list[128]; + + struct TileItem { + uint16 x; + uint16 priority; + uint16 palette; + bool hflip; + uint8 d0, d1, d2, d3; + }; + + struct State { + unsigned x; + unsigned y; + + unsigned item_count; + unsigned tile_count; + + bool active; + uint8 item[2][32]; + TileItem tile[2][34]; + } t; + + struct Regs { + bool main_enable; + bool sub_enable; + bool interlace; + + uint3 base_size; + uint2 nameselect; + uint16 tiledata_addr; + uint8 first_sprite; + + unsigned priority0; + unsigned priority1; + unsigned priority2; + unsigned priority3; + + bool time_over; + bool range_over; + } regs; + + struct Output { + struct Pixel { + unsigned priority; //0 = none (transparent) + uint8 palette; + } main, sub; + } output; + + //list.cpp + void update(unsigned addr, uint8 data); + void synchronize(); + + //sprite.cpp + void address_reset(); + void set_first_sprite(); + void frame(); + void scanline(); + void run(); + void tilefetch(); + void reset(); + + bool on_scanline(SpriteItem&); + + void serialize(serializer&); + Sprite(PPU &self); + + PPU &self; +}; diff --git a/libsnes/snes/ppu/window/window.cpp b/libsnes/snes/ppu/window/window.cpp new file mode 100644 index 0000000..dec0587 --- /dev/null +++ b/libsnes/snes/ppu/window/window.cpp @@ -0,0 +1,169 @@ +#ifdef PPU_CPP + +void PPU::Window::scanline() { + x = 0; +} + +void PPU::Window::run() { + bool main, sub; + one = (x >= regs.one_left && x <= regs.one_right); + two = (x >= regs.two_left && x <= regs.two_right); + x++; + + test( + main, sub, + regs.bg1_one_enable, regs.bg1_one_invert, + regs.bg1_two_enable, regs.bg1_two_invert, + regs.bg1_mask, regs.bg1_main_enable, regs.bg1_sub_enable + ); + if(main) self.bg1.output.main.priority = 0; + if(sub) self.bg1.output.sub.priority = 0; + + test( + main, sub, + regs.bg2_one_enable, regs.bg2_one_invert, + regs.bg2_two_enable, regs.bg2_two_invert, + regs.bg2_mask, regs.bg2_main_enable, regs.bg2_sub_enable + ); + if(main) self.bg2.output.main.priority = 0; + if(sub) self.bg2.output.sub.priority = 0; + + test( + main, sub, + regs.bg3_one_enable, regs.bg3_one_invert, + regs.bg3_two_enable, regs.bg3_two_invert, + regs.bg3_mask, regs.bg3_main_enable, regs.bg3_sub_enable + ); + if(main) self.bg3.output.main.priority = 0; + if(sub) self.bg3.output.sub.priority = 0; + + test( + main, sub, + regs.bg4_one_enable, regs.bg4_one_invert, + regs.bg4_two_enable, regs.bg4_two_invert, + regs.bg4_mask, regs.bg4_main_enable, regs.bg4_sub_enable + ); + if(main) self.bg4.output.main.priority = 0; + if(sub) self.bg4.output.sub.priority = 0; + + test( + main, sub, + regs.oam_one_enable, regs.oam_one_invert, + regs.oam_two_enable, regs.oam_two_invert, + regs.oam_mask, regs.oam_main_enable, regs.oam_sub_enable + ); + if(main) self.sprite.output.main.priority = 0; + if(sub) self.sprite.output.sub.priority = 0; + + test( + main, sub, + regs.col_one_enable, regs.col_one_invert, + regs.col_two_enable, regs.col_two_invert, + regs.col_mask, true, true + ); + + switch(regs.col_main_mask) { + case 0: main = true; break; + case 1: break; + case 2: main = !main; break; + case 3: main = false; break; + } + + switch(regs.col_sub_mask) { + case 0: sub = true; break; + case 1: break; + case 2: sub = !sub; break; + case 3: sub = false; break; + } + + output.main.color_enable = main; + output.sub.color_enable = sub; +} + +void PPU::Window::test( + bool &main, bool &sub, + bool one_enable, bool one_invert, + bool two_enable, bool two_invert, + uint8 mask, bool main_enable, bool sub_enable +) { + bool one = Window::one ^ one_invert; + bool two = Window::two ^ two_invert; + bool output; + + if(one_enable == false && two_enable == false) { + output = false; + } else if(one_enable == true && two_enable == false) { + output = one; + } else if(one_enable == false && two_enable == true) { + output = two; + } else switch(mask) { + case 0: output = (one | two) == 1; break; + case 1: output = (one & two) == 1; break; + case 2: output = (one ^ two) == 1; break; + case 3: output = (one ^ two) == 0; break; + } + + main = main_enable ? output : false; + sub = sub_enable ? output : false; +} + +void PPU::Window::reset() { + regs.bg1_one_enable = random(false); + regs.bg1_one_invert = random(false); + regs.bg1_two_enable = random(false); + regs.bg1_two_invert = random(false); + regs.bg2_one_enable = random(false); + regs.bg2_one_invert = random(false); + regs.bg2_two_enable = random(false); + regs.bg2_two_invert = random(false); + regs.bg3_one_enable = random(false); + regs.bg3_one_invert = random(false); + regs.bg3_two_enable = random(false); + regs.bg3_two_invert = random(false); + regs.bg4_one_enable = random(false); + regs.bg4_one_invert = random(false); + regs.bg4_two_enable = random(false); + regs.bg4_two_invert = random(false); + regs.oam_one_enable = random(false); + regs.oam_one_invert = random(false); + regs.oam_two_enable = random(false); + regs.oam_two_invert = random(false); + regs.col_one_enable = random(false); + regs.col_one_invert = random(false); + regs.col_two_enable = random(false); + regs.col_two_invert = random(false); + regs.one_left = random(0x00); + regs.one_right = random(0x00); + regs.two_left = random(0x00); + regs.two_right = random(0x00); + regs.bg1_mask = random(0); + regs.bg2_mask = random(0); + regs.bg3_mask = random(0); + regs.bg4_mask = random(0); + regs.oam_mask = random(0); + regs.col_mask = random(0); + regs.bg1_main_enable = random(false); + regs.bg1_sub_enable = random(false); + regs.bg2_main_enable = random(false); + regs.bg2_sub_enable = random(false); + regs.bg3_main_enable = random(false); + regs.bg3_sub_enable = random(false); + regs.bg4_main_enable = random(false); + regs.bg4_sub_enable = random(false); + regs.oam_main_enable = random(false); + regs.oam_sub_enable = random(false); + regs.col_main_mask = random(0); + regs.col_sub_mask = random(0); + + output.main.color_enable = 0; + output.sub.color_enable = 0; + + x = 0; + one = 0; + two = 0; +} + +PPU::Window::Window(PPU &self) : self(self) { +} + +#endif diff --git a/libsnes/snes/ppu/window/window.hpp b/libsnes/snes/ppu/window/window.hpp new file mode 100644 index 0000000..bd41061 --- /dev/null +++ b/libsnes/snes/ppu/window/window.hpp @@ -0,0 +1,87 @@ +struct Window { + struct { + bool bg1_one_enable; + bool bg1_one_invert; + bool bg1_two_enable; + bool bg1_two_invert; + + bool bg2_one_enable; + bool bg2_one_invert; + bool bg2_two_enable; + bool bg2_two_invert; + + bool bg3_one_enable; + bool bg3_one_invert; + bool bg3_two_enable; + bool bg3_two_invert; + + bool bg4_one_enable; + bool bg4_one_invert; + bool bg4_two_enable; + bool bg4_two_invert; + + bool oam_one_enable; + bool oam_one_invert; + bool oam_two_enable; + bool oam_two_invert; + + bool col_one_enable; + bool col_one_invert; + bool col_two_enable; + bool col_two_invert; + + uint8 one_left; + uint8 one_right; + uint8 two_left; + uint8 two_right; + + uint2 bg1_mask; + uint2 bg2_mask; + uint2 bg3_mask; + uint2 bg4_mask; + uint2 oam_mask; + uint2 col_mask; + + bool bg1_main_enable; + bool bg1_sub_enable; + bool bg2_main_enable; + bool bg2_sub_enable; + bool bg3_main_enable; + bool bg3_sub_enable; + bool bg4_main_enable; + bool bg4_sub_enable; + bool oam_main_enable; + bool oam_sub_enable; + + uint2 col_main_mask; + uint2 col_sub_mask; + } regs; + + struct Output { + struct Pixel { + bool color_enable; + } main, sub; + } output; + + struct { + unsigned x; + bool one; + bool two; + }; + + void scanline(); + void run(); + void reset(); + + void test( + bool &main, bool &sub, + bool one_enable, bool one_invert, + bool two_enable, bool two_invert, + uint8 mask, bool main_enable, bool sub_enable + ); + + void serialize(serializer&); + Window(PPU &self); + + PPU &self; +}; diff --git a/libsnes/snes/profile-accuracy.hpp b/libsnes/snes/profile-accuracy.hpp new file mode 100644 index 0000000..7b6dded --- /dev/null +++ b/libsnes/snes/profile-accuracy.hpp @@ -0,0 +1,8 @@ +namespace Info { + static const char Profile[] = "Accuracy"; +} + +#include +#include +#include +#include diff --git a/libsnes/snes/profile-compatibility.hpp b/libsnes/snes/profile-compatibility.hpp new file mode 100644 index 0000000..bbf3582 --- /dev/null +++ b/libsnes/snes/profile-compatibility.hpp @@ -0,0 +1,8 @@ +namespace Info { + static const char Profile[] = "Compatibility"; +} + +#include +#include +#include +#include diff --git a/libsnes/snes/profile-performance.hpp b/libsnes/snes/profile-performance.hpp new file mode 100644 index 0000000..1d379fd --- /dev/null +++ b/libsnes/snes/profile-performance.hpp @@ -0,0 +1,12 @@ +namespace Info { + static const char Profile[] = "Performance"; +} + +#if defined(DEBUGGER) + #error "bsnes: debugger not supported with performance profile." +#endif + +#include +#include +#include +#include diff --git a/libsnes/snes/random/random.cpp b/libsnes/snes/random/random.cpp new file mode 100644 index 0000000..a92a980 --- /dev/null +++ b/libsnes/snes/random/random.cpp @@ -0,0 +1,18 @@ +Random random; + +void Random::seed(unsigned seed_iter) { + iter = seed_iter; +} + +unsigned Random::operator()(unsigned result) { + if(config.random == false) return result; + return iter = (iter >> 1) ^ (((iter & 1) - 1) & 0xedb88320); +} + +void Random::serialize(serializer &s) { + s.integer(iter); +} + +Random::Random() { + iter = 0; +} diff --git a/libsnes/snes/random/random.hpp b/libsnes/snes/random/random.hpp new file mode 100644 index 0000000..927d717 --- /dev/null +++ b/libsnes/snes/random/random.hpp @@ -0,0 +1,11 @@ +struct Random { + void seed(unsigned seed); + unsigned operator()(unsigned result = 0); + void serialize(serializer&); + Random(); + +private: + unsigned iter; +}; + +extern Random random; diff --git a/libsnes/snes/scheduler/scheduler.cpp b/libsnes/snes/scheduler/scheduler.cpp new file mode 100755 index 0000000..e9baae9 --- /dev/null +++ b/libsnes/snes/scheduler/scheduler.cpp @@ -0,0 +1,28 @@ +#ifdef SYSTEM_CPP + +Scheduler scheduler; + +void Scheduler::enter() { + host_thread = co_active(); + co_switch(thread); +} + +void Scheduler::exit(ExitReason::e reason) { + exit_reason.i = reason; + thread = co_active(); + co_switch(host_thread); +} + +void Scheduler::init() { + host_thread = co_active(); + thread = cpu.thread; + sync.i = SynchronizeMode::None; +} + +Scheduler::Scheduler() { + host_thread = 0; + thread = 0; + exit_reason.i = ExitReason::UnknownEvent; +} + +#endif diff --git a/libsnes/snes/scheduler/scheduler.hpp b/libsnes/snes/scheduler/scheduler.hpp new file mode 100755 index 0000000..6428c26 --- /dev/null +++ b/libsnes/snes/scheduler/scheduler.hpp @@ -0,0 +1,31 @@ +struct Scheduler : property { + struct SynchronizeMode { + enum e { + None, + CPU, + All + } i; + } sync; + + struct ExitReason { + enum e { + UnknownEvent, + FrameEvent, + SynchronizeEvent, + DebuggerEvent + } i; + }; + + ExitReason exit_reason; + + cothread_t host_thread; //program thread (used to exit emulation) + cothread_t thread; //active emulation thread (used to enter emulation) + + void enter(); + void exit(ExitReason::e); + + void init(); + Scheduler(); +}; + +extern Scheduler scheduler; diff --git a/libsnes/snes/smp/core-old/algorithms.cpp b/libsnes/snes/smp/core-old/algorithms.cpp new file mode 100644 index 0000000..30a2107 --- /dev/null +++ b/libsnes/snes/smp/core-old/algorithms.cpp @@ -0,0 +1,126 @@ +#ifdef SMPCORE_CPP + +uint8 SMPcore::op_adc(uint8 x, uint8 y) { + int r = x + y + regs.p.c; + regs.p.n = r & 0x80; + regs.p.v = ~(x ^ y) & (x ^ r) & 0x80; + regs.p.h = (x ^ y ^ r) & 0x10; + regs.p.z = (uint8)r == 0; + regs.p.c = r > 0xff; + return r; +} + +uint16 SMPcore::op_addw(uint16 x, uint16 y) { + uint16 r; + regs.p.c = 0; + r = op_adc(x, y); + r |= op_adc(x >> 8, y >> 8) << 8; + regs.p.z = r == 0; + return r; +} + +uint8 SMPcore::op_and(uint8 x, uint8 y) { + x &= y; + regs.p.n = x & 0x80; + regs.p.z = x == 0; + return x; +} + +uint8 SMPcore::op_cmp(uint8 x, uint8 y) { + int r = x - y; + regs.p.n = r & 0x80; + regs.p.z = (uint8)r == 0; + regs.p.c = r >= 0; + return x; +} + +uint16 SMPcore::op_cmpw(uint16 x, uint16 y) { + int r = x - y; + regs.p.n = r & 0x8000; + regs.p.z = (uint16)r == 0; + regs.p.c = r >= 0; + return x; +} + +uint8 SMPcore::op_eor(uint8 x, uint8 y) { + x ^= y; + regs.p.n = x & 0x80; + regs.p.z = x == 0; + return x; +} + +uint8 SMPcore::op_or(uint8 x, uint8 y) { + x |= y; + regs.p.n = x & 0x80; + regs.p.z = x == 0; + return x; +} + +uint8 SMPcore::op_sbc(uint8 x, uint8 y) { + int r = x - y - !regs.p.c; + regs.p.n = r & 0x80; + regs.p.v = (x ^ y) & (x ^ r) & 0x80; + regs.p.h = !((x ^ y ^ r) & 0x10); + regs.p.z = (uint8)r == 0; + regs.p.c = r >= 0; + return r; +} + +uint16 SMPcore::op_subw(uint16 x, uint16 y) { + uint16 r; + regs.p.c = 1; + r = op_sbc(x, y); + r |= op_sbc(x >> 8, y >> 8) << 8; + regs.p.z = r == 0; + return r; +} + +uint8 SMPcore::op_inc(uint8 x) { + x++; + regs.p.n = x & 0x80; + regs.p.z = x == 0; + return x; +} + +uint8 SMPcore::op_dec(uint8 x) { + x--; + regs.p.n = x & 0x80; + regs.p.z = x == 0; + return x; +} + +uint8 SMPcore::op_asl(uint8 x) { + regs.p.c = x & 0x80; + x <<= 1; + regs.p.n = x & 0x80; + regs.p.z = x == 0; + return x; +} + +uint8 SMPcore::op_lsr(uint8 x) { + regs.p.c = x & 0x01; + x >>= 1; + regs.p.n = x & 0x80; + regs.p.z = x == 0; + return x; +} + +uint8 SMPcore::op_rol(uint8 x) { + unsigned carry = (unsigned)regs.p.c; + regs.p.c = x & 0x80; + x = (x << 1) | carry; + regs.p.n = x & 0x80; + regs.p.z = x == 0; + return x; +} + +uint8 SMPcore::op_ror(uint8 x) { + unsigned carry = (unsigned)regs.p.c << 7; + regs.p.c = x & 0x01; + x = carry | (x >> 1); + regs.p.n = x & 0x80; + regs.p.z = x == 0; + return x; +} + +#endif diff --git a/libsnes/snes/smp/core-old/core.cpp b/libsnes/snes/smp/core-old/core.cpp new file mode 100644 index 0000000..af6cd59 --- /dev/null +++ b/libsnes/snes/smp/core-old/core.cpp @@ -0,0 +1,35 @@ +#include + +#define SMPCORE_CPP +namespace SNES { + +#include "serialization.cpp" +#include "algorithms.cpp" +#include "disassembler/disassembler.cpp" + +#define A 0 +#define X 1 +#define Y 2 +#define SP 3 + +#include "opcode_mov.cpp" +#include "opcode_pc.cpp" +#include "opcode_read.cpp" +#include "opcode_rmw.cpp" +#include "opcode_misc.cpp" +#include "table.cpp" + +#undef A +#undef X +#undef Y +#undef SP + +void SMPcore::op_step() { + (this->*opcode_table[op_readpc()])(); +} + +SMPcore::SMPcore() { + initialize_opcode_table(); +} + +} diff --git a/libsnes/snes/smp/core-old/core.hpp b/libsnes/snes/smp/core-old/core.hpp new file mode 100644 index 0000000..ec6c0e3 --- /dev/null +++ b/libsnes/snes/smp/core-old/core.hpp @@ -0,0 +1,118 @@ +struct SMPcore { + #include "registers.hpp" + #include "memory.hpp" + #include "disassembler/disassembler.hpp" + + regs_t regs; + uint16 dp, sp, rd, wr, bit, ya; + + virtual void op_io() = 0; + virtual uint8 op_read(uint16 addr) = 0; + virtual void op_write(uint16 addr, uint8 data) = 0; + void op_step(); + + uint8 op_adc (uint8 x, uint8 y); + uint16 op_addw(uint16 x, uint16 y); + uint8 op_and (uint8 x, uint8 y); + uint8 op_cmp (uint8 x, uint8 y); + uint16 op_cmpw(uint16 x, uint16 y); + uint8 op_eor (uint8 x, uint8 y); + uint8 op_inc (uint8 x); + uint8 op_dec (uint8 x); + uint8 op_or (uint8 x, uint8 y); + uint8 op_sbc (uint8 x, uint8 y); + uint16 op_subw(uint16 x, uint16 y); + uint8 op_asl (uint8 x); + uint8 op_lsr (uint8 x); + uint8 op_rol (uint8 x); + uint8 op_ror (uint8 x); + + template void op_mov_reg_reg(); + void op_mov_sp_x(); + template void op_mov_reg_const(); + void op_mov_a_ix(); + void op_mov_a_ixinc(); + template void op_mov_reg_dp(); + template void op_mov_reg_dpr(); + template void op_mov_reg_addr(); + template void op_mov_a_addrr(); + void op_mov_a_idpx(); + void op_mov_a_idpy(); + void op_mov_dp_dp(); + void op_mov_dp_const(); + void op_mov_ix_a(); + void op_mov_ixinc_a(); + template void op_mov_dp_reg(); + template void op_mov_dpr_reg(); + template void op_mov_addr_reg(); + template void op_mov_addrr_a(); + void op_mov_idpx_a(); + void op_mov_idpy_a(); + void op_movw_ya_dp(); + void op_movw_dp_ya(); + void op_mov1_c_bit(); + void op_mov1_bit_c(); + + void op_bra(); + template void op_branch(); + template void op_bitbranch(); + void op_cbne_dp(); + void op_cbne_dpx(); + void op_dbnz_dp(); + void op_dbnz_y(); + void op_jmp_addr(); + void op_jmp_iaddrx(); + void op_call(); + void op_pcall(); + template void op_tcall(); + void op_brk(); + void op_ret(); + void op_reti(); + + template void op_read_reg_const(); + template void op_read_a_ix(); + template void op_read_reg_dp(); + template void op_read_a_dpx(); + template void op_read_reg_addr(); + template void op_read_a_addrr(); + template void op_read_a_idpx(); + template void op_read_a_idpy(); + template void op_read_ix_iy(); + template void op_read_dp_dp(); + template void op_read_dp_const(); + template void op_read_ya_dp(); + void op_cmpw_ya_dp(); + template void op_and1_bit(); + void op_eor1_bit(); + void op_not1_bit(); + template void op_or1_bit(); + + template void op_adjust_reg(); + template void op_adjust_dp(); + template void op_adjust_dpx(); + template void op_adjust_addr(); + template void op_adjust_addr_a(); + template void op_adjustw_dp(); + + void op_nop(); + void op_wait(); + void op_xcn(); + void op_daa(); + void op_das(); + template void op_setbit(); + void op_notc(); + template void op_seti(); + template void op_setbit_dp(); + template void op_push_reg(); + void op_push_p(); + template void op_pop_reg(); + void op_pop_p(); + void op_mul_ya(); + void op_div_ya_x(); + + void (SMPcore::*opcode_table[256])(); + void initialize_opcode_table(); + + void core_serialize(serializer&); + SMPcore(); +}; diff --git a/libsnes/snes/smp/core-old/memory.hpp b/libsnes/snes/smp/core-old/memory.hpp new file mode 100644 index 0000000..4b7afba --- /dev/null +++ b/libsnes/snes/smp/core-old/memory.hpp @@ -0,0 +1,19 @@ +alwaysinline uint8_t op_readpc() { + return op_read(regs.pc++); +} + +alwaysinline uint8_t op_readsp() { + return op_read(0x0100 | ++regs.sp); +} + +alwaysinline void op_writesp(uint8_t data) { + op_write(0x0100 | regs.sp--, data); +} + +alwaysinline uint8_t op_readdp(uint8_t addr) { + return op_read(((unsigned)regs.p.p << 8) + addr); +} + +alwaysinline void op_writedp(uint8_t addr, uint8_t data) { + op_write(((unsigned)regs.p.p << 8) + addr, data); +} diff --git a/libsnes/snes/smp/core-old/opcode_misc.cpp b/libsnes/snes/smp/core-old/opcode_misc.cpp new file mode 100644 index 0000000..533c8f9 --- /dev/null +++ b/libsnes/snes/smp/core-old/opcode_misc.cpp @@ -0,0 +1,148 @@ +#ifdef SMPCORE_CPP + +void SMPcore::op_nop() { + op_io(); +} + +void SMPcore::op_wait() { + while(true) { + op_io(); + op_io(); + } +} + +void SMPcore::op_xcn() { + op_io(); + op_io(); + op_io(); + op_io(); + regs.a = (regs.a >> 4) | (regs.a << 4); + regs.p.n = (regs.a & 0x80); + regs.p.z = (regs.a == 0); +} + +void SMPcore::op_daa() { + op_io(); + op_io(); + if(regs.p.c || (regs.a) > 0x99) { + regs.a += 0x60; + regs.p.c = 1; + } + if(regs.p.h || (regs.a & 15) > 0x09) { + regs.a += 0x06; + } + regs.p.n = (regs.a & 0x80); + regs.p.z = (regs.a == 0); +} + +void SMPcore::op_das() { + op_io(); + op_io(); + if(!regs.p.c || (regs.a) > 0x99) { + regs.a -= 0x60; + regs.p.c = 0; + } + if(!regs.p.h || (regs.a & 15) > 0x09) { + regs.a -= 0x06; + } + regs.p.n = (regs.a & 0x80); + regs.p.z = (regs.a == 0); +} + +template void SMPcore::op_setbit() { + op_io(); + regs.p = (regs.p & ~mask) | value; +} + +void SMPcore::op_notc() { + op_io(); + op_io(); + regs.p.c = !regs.p.c; +} + +template void SMPcore::op_seti() { + op_io(); + op_io(); + regs.p.i = value; +} + +template void SMPcore::op_setbit_dp() { + dp = op_readpc(); + rd = op_readdp(dp); + rd = (op ? rd | value : rd & ~value); + op_writedp(dp, rd); +} + +template void SMPcore::op_push_reg() { + op_io(); + op_io(); + op_writesp(regs.r[n]); +} + +void SMPcore::op_push_p() { + op_io(); + op_io(); + op_writesp(regs.p); +} + +template void SMPcore::op_pop_reg() { + op_io(); + op_io(); + regs.r[n] = op_readsp(); +} + +void SMPcore::op_pop_p() { + op_io(); + op_io(); + regs.p = op_readsp(); +} + +void SMPcore::op_mul_ya() { + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + ya = regs.y * regs.a; + regs.a = ya; + regs.y = ya >> 8; + //result is set based on y (high-byte) only + regs.p.n = (regs.y & 0x80); + regs.p.z = (regs.y == 0); +} + +void SMPcore::op_div_ya_x() { + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + ya = regs.ya; + //overflow set if quotient >= 256 + regs.p.v = (regs.y >= regs.x); + regs.p.h = ((regs.y & 15) >= (regs.x & 15)); + if(regs.y < (regs.x << 1)) { + //if quotient is <= 511 (will fit into 9-bit result) + regs.a = ya / regs.x; + regs.y = ya % regs.x; + } else { + //otherwise, the quotient won't fit into regs.p.v + regs.a + //this emulates the odd behavior of the S-SMP in this case + regs.a = 255 - (ya - (regs.x << 9)) / (256 - regs.x); + regs.y = regs.x + (ya - (regs.x << 9)) % (256 - regs.x); + } + //result is set based on a (quotient) only + regs.p.n = (regs.a & 0x80); + regs.p.z = (regs.a == 0); +} + +#endif diff --git a/libsnes/snes/smp/core-old/opcode_mov.cpp b/libsnes/snes/smp/core-old/opcode_mov.cpp new file mode 100644 index 0000000..f176785 --- /dev/null +++ b/libsnes/snes/smp/core-old/opcode_mov.cpp @@ -0,0 +1,200 @@ +#ifdef SMPCORE_CPP + +template void SMPcore::op_mov_reg_reg() { + op_io(); + regs.r[to] = regs.r[from]; + regs.p.n = (regs.r[to] & 0x80); + regs.p.z = (regs.r[to] == 0); +} + +void SMPcore::op_mov_sp_x() { + op_io(); + regs.sp = regs.x; +} + +template void SMPcore::op_mov_reg_const() { + regs.r[n] = op_readpc(); + regs.p.n = (regs.r[n] & 0x80); + regs.p.z = (regs.r[n] == 0); +} + +void SMPcore::op_mov_a_ix() { + op_io(); + regs.a = op_readdp(regs.x); + regs.p.n = (regs.a & 0x80); + regs.p.z = (regs.a == 0); +} + +void SMPcore::op_mov_a_ixinc() { + op_io(); + regs.a = op_readdp(regs.x++); + op_io(); + regs.p.n = (regs.a & 0x80); + regs.p.z = (regs.a == 0); +} + +template void SMPcore::op_mov_reg_dp() { + sp = op_readpc(); + regs.r[n] = op_readdp(sp); + regs.p.n = (regs.r[n] & 0x80); + regs.p.z = (regs.r[n] == 0); +} + +template void SMPcore::op_mov_reg_dpr() { + sp = op_readpc(); + op_io(); + regs.r[n] = op_readdp(sp + regs.r[i]); + regs.p.n = (regs.r[n] & 0x80); + regs.p.z = (regs.r[n] == 0); +} + +template void SMPcore::op_mov_reg_addr() { + sp = op_readpc() << 0; + sp |= op_readpc() << 8; + regs.r[n] = op_read(sp); + regs.p.n = (regs.r[n] & 0x80); + regs.p.z = (regs.r[n] == 0); +} + +template void SMPcore::op_mov_a_addrr() { + sp = op_readpc() << 0; + sp |= op_readpc() << 8; + op_io(); + regs.a = op_read(sp + regs.r[i]); + regs.p.n = (regs.a & 0x80); + regs.p.z = (regs.a == 0); +} + +void SMPcore::op_mov_a_idpx() { + dp = op_readpc() + regs.x; + op_io(); + sp = op_readdp(dp + 0) << 0; + sp |= op_readdp(dp + 1) << 8; + regs.a = op_read(sp); + regs.p.n = (regs.a & 0x80); + regs.p.z = (regs.a == 0); +} + +void SMPcore::op_mov_a_idpy() { + dp = op_readpc(); + op_io(); + sp = op_readdp(dp + 0) << 0; + sp |= op_readdp(dp + 1) << 8; + regs.a = op_read(sp + regs.y); + regs.p.n = (regs.a & 0x80); + regs.p.z = (regs.a == 0); +} + +void SMPcore::op_mov_dp_dp() { + sp = op_readpc(); + rd = op_readdp(sp); + dp = op_readpc(); + op_writedp(dp, rd); +} + +void SMPcore::op_mov_dp_const() { + rd = op_readpc(); + dp = op_readpc(); + op_readdp(dp); + op_writedp(dp, rd); +} + +void SMPcore::op_mov_ix_a() { + op_io(); + op_readdp(regs.x); + op_writedp(regs.x, regs.a); +} + +void SMPcore::op_mov_ixinc_a() { + op_io(); + op_io(); + op_writedp(regs.x++, regs.a); +} + +template void SMPcore::op_mov_dp_reg() { + dp = op_readpc(); + op_readdp(dp); + op_writedp(dp, regs.r[n]); +} + +template void SMPcore::op_mov_dpr_reg() { + dp = op_readpc(); + op_io(); + dp += regs.r[i]; + op_readdp(dp); + op_writedp(dp, regs.r[n]); +} + +template void SMPcore::op_mov_addr_reg() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + op_read(dp); + op_write(dp, regs.r[n]); +} + +template void SMPcore::op_mov_addrr_a() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + op_io(); + dp += regs.r[i]; + op_read(dp); + op_write(dp, regs.a); +} + +void SMPcore::op_mov_idpx_a() { + sp = op_readpc(); + op_io(); + sp += regs.x; + dp = op_readdp(sp + 0) << 0; + dp |= op_readdp(sp + 1) << 8; + op_read(dp); + op_write(dp, regs.a); +} + +void SMPcore::op_mov_idpy_a() { + sp = op_readpc(); + dp = op_readdp(sp + 0) << 0; + dp |= op_readdp(sp + 1) << 8; + op_io(); + dp += regs.y; + op_read(dp); + op_write(dp, regs.a); +} + +void SMPcore::op_movw_ya_dp() { + sp = op_readpc(); + regs.a = op_readdp(sp + 0); + op_io(); + regs.y = op_readdp(sp + 1); + regs.p.n = (regs.ya & 0x8000); + regs.p.z = (regs.ya == 0); +} + +void SMPcore::op_movw_dp_ya() { + dp = op_readpc(); + op_readdp(dp); + op_writedp(dp + 0, regs.a); + op_writedp(dp + 1, regs.y); +} + +void SMPcore::op_mov1_c_bit() { + sp = op_readpc() << 0; + sp |= op_readpc() << 8; + bit = sp >> 13; + sp &= 0x1fff; + rd = op_read(sp); + regs.p.c = (rd & (1 << bit)); +} + +void SMPcore::op_mov1_bit_c() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + bit = dp >> 13; + dp &= 0x1fff; + rd = op_read(dp); + (regs.p.c) ? rd |= (1 << bit) : rd &= ~(1 << bit); + op_io(); + op_write(dp, rd); +} + +#endif diff --git a/libsnes/snes/smp/core-old/opcode_pc.cpp b/libsnes/snes/smp/core-old/opcode_pc.cpp new file mode 100644 index 0000000..727522c --- /dev/null +++ b/libsnes/snes/smp/core-old/opcode_pc.cpp @@ -0,0 +1,152 @@ +#ifdef SMPCORE_CPP + +void SMPcore::op_bra() { + rd = op_readpc(); + op_io(); + op_io(); + regs.pc += (int8)rd; +} + +template void SMPcore::op_branch() { + rd = op_readpc(); + if((bool)(regs.p & flag) != value) return; + op_io(); + op_io(); + regs.pc += (int8)rd; +} + +template void SMPcore::op_bitbranch() { + dp = op_readpc(); + sp = op_readdp(dp); + rd = op_readpc(); + op_io(); + if((bool)(sp & mask) != value) return; + op_io(); + op_io(); + regs.pc += (int8)rd; +} + +void SMPcore::op_cbne_dp() { + dp = op_readpc(); + sp = op_readdp(dp); + rd = op_readpc(); + op_io(); + if(regs.a == sp) return; + op_io(); + op_io(); + regs.pc += (int8)rd; +} + +void SMPcore::op_cbne_dpx() { + dp = op_readpc(); + op_io(); + sp = op_readdp(dp + regs.x); + rd = op_readpc(); + op_io(); + if(regs.a == sp) return; + op_io(); + op_io(); + regs.pc += (int8)rd; +} + +void SMPcore::op_dbnz_dp() { + dp = op_readpc(); + wr = op_readdp(dp); + op_writedp(dp, --wr); + rd = op_readpc(); + if(wr == 0) return; + op_io(); + op_io(); + regs.pc += (int8)rd; +} + +void SMPcore::op_dbnz_y() { + rd = op_readpc(); + op_io(); + regs.y--; + op_io(); + if(regs.y == 0) return; + op_io(); + op_io(); + regs.pc += (int8)rd; +} + +void SMPcore::op_jmp_addr() { + rd = op_readpc() << 0; + rd |= op_readpc() << 8; + regs.pc = rd; +} + +void SMPcore::op_jmp_iaddrx() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + op_io(); + dp += regs.x; + rd = op_read(dp + 0) << 0; + rd |= op_read(dp + 1) << 8; + regs.pc = rd; +} + +void SMPcore::op_call() { + rd = op_readpc() << 0; + rd |= op_readpc() << 8; + op_io(); + op_io(); + op_io(); + op_writesp(regs.pc >> 8); + op_writesp(regs.pc >> 0); + regs.pc = rd; +} + +void SMPcore::op_pcall() { + rd = op_readpc(); + op_io(); + op_io(); + op_writesp(regs.pc >> 8); + op_writesp(regs.pc >> 0); + regs.pc = 0xff00 | rd; +} + +template void SMPcore::op_tcall() { + dp = 0xffde - (n << 1); + rd = op_read(dp + 0) << 0; + rd |= op_read(dp + 1) << 8; + op_io(); + op_io(); + op_io(); + op_writesp(regs.pc >> 8); + op_writesp(regs.pc >> 0); + regs.pc = rd; +} + +void SMPcore::op_brk() { + rd = op_read(0xffde) << 0; + rd |= op_read(0xffdf) << 8; + op_io(); + op_io(); + op_writesp(regs.pc >> 8); + op_writesp(regs.pc >> 0); + op_writesp(regs.p); + regs.pc = rd; + regs.p.b = 1; + regs.p.i = 0; +} + +void SMPcore::op_ret() { + rd = op_readsp() << 0; + rd |= op_readsp() << 8; + op_io(); + op_io(); + regs.pc = rd; +} + +void SMPcore::op_reti() { + regs.p = op_readsp(); + rd = op_readsp() << 0; + rd |= op_readsp() << 8; + op_io(); + op_io(); + regs.pc = rd; +} + +#endif diff --git a/libsnes/snes/smp/core-old/opcode_read.cpp b/libsnes/snes/smp/core-old/opcode_read.cpp new file mode 100644 index 0000000..25a5bc0 --- /dev/null +++ b/libsnes/snes/smp/core-old/opcode_read.cpp @@ -0,0 +1,154 @@ +#ifdef SMPCORE_CPP + +template +void SMPcore::op_read_reg_const() { + rd = op_readpc(); + regs.r[n] = (this->*op)(regs.r[n], rd); +} + +template +void SMPcore::op_read_a_ix() { + op_io(); + rd = op_readdp(regs.x); + regs.a = (this->*op)(regs.a, rd); +} + +template +void SMPcore::op_read_reg_dp() { + dp = op_readpc(); + rd = op_readdp(dp); + regs.r[n] = (this->*op)(regs.r[n], rd); +} + +template +void SMPcore::op_read_a_dpx() { + dp = op_readpc(); + op_io(); + rd = op_readdp(dp + regs.x); + regs.a = (this->*op)(regs.a, rd); +} + +template +void SMPcore::op_read_reg_addr() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + rd = op_read(dp); + regs.r[n] = (this->*op)(regs.r[n], rd); +} + +template +void SMPcore::op_read_a_addrr() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + op_io(); + rd = op_read(dp + regs.r[i]); + regs.a = (this->*op)(regs.a, rd); +} + +template +void SMPcore::op_read_a_idpx() { + dp = op_readpc() + regs.x; + op_io(); + sp = op_readdp(dp + 0) << 0; + sp |= op_readdp(dp + 1) << 8; + rd = op_read(sp); + regs.a = (this->*op)(regs.a, rd); +} + +template +void SMPcore::op_read_a_idpy() { + dp = op_readpc(); + op_io(); + sp = op_readdp(dp + 0) << 0; + sp |= op_readdp(dp + 1) << 8; + rd = op_read(sp + regs.y); + regs.a = (this->*op)(regs.a, rd); +} + +template +void SMPcore::op_read_ix_iy() { + op_io(); + rd = op_readdp(regs.y); + wr = op_readdp(regs.x); + wr = (this->*op)(wr, rd); + static uint8 (SMPcore::*cmp)(uint8, uint8) = &SMPcore::op_cmp; + (op != cmp) ? op_writedp(regs.x, wr) : op_io(); +} + +template +void SMPcore::op_read_dp_dp() { + sp = op_readpc(); + rd = op_readdp(sp); + dp = op_readpc(); + wr = op_readdp(dp); + wr = (this->*op)(wr, rd); + static uint8 (SMPcore::*cmp)(uint8, uint8) = &SMPcore::op_cmp; + (op != cmp) ? op_writedp(dp, wr) : op_io(); +} + +template +void SMPcore::op_read_dp_const() { + rd = op_readpc(); + dp = op_readpc(); + wr = op_readdp(dp); + wr = (this->*op)(wr, rd); + static uint8 (SMPcore::*cmp)(uint8, uint8) = &SMPcore::op_cmp; + (op != cmp) ? op_writedp(dp, wr) : op_io(); +} + +template +void SMPcore::op_read_ya_dp() { + dp = op_readpc(); + rd = op_readdp(dp + 0) << 0; + op_io(); + rd |= op_readdp(dp + 1) << 8; + regs.ya = (this->*op)(regs.ya, rd); +} + +void SMPcore::op_cmpw_ya_dp() { + dp = op_readpc(); + rd = op_readdp(dp + 0) << 0; + rd |= op_readdp(dp + 1) << 8; + op_cmpw(regs.ya, rd); +} + +template void SMPcore::op_and1_bit() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + bit = dp >> 13; + dp &= 0x1fff; + rd = op_read(dp); + regs.p.c = regs.p.c & ((bool)(rd & (1 << bit)) ^ op); +} + +void SMPcore::op_eor1_bit() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + bit = dp >> 13; + dp &= 0x1fff; + rd = op_read(dp); + op_io(); + regs.p.c = regs.p.c ^ (bool)(rd & (1 << bit)); +} + +void SMPcore::op_not1_bit() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + bit = dp >> 13; + dp &= 0x1fff; + rd = op_read(dp); + rd ^= 1 << bit; + op_write(dp, rd); +} + +template void SMPcore::op_or1_bit() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + bit = dp >> 13; + dp &= 0x1fff; + rd = op_read(dp); + op_io(); + regs.p.c = regs.p.c | ((bool)(rd & (1 << bit)) ^ op); +} + +#endif diff --git a/libsnes/snes/smp/core-old/opcode_rmw.cpp b/libsnes/snes/smp/core-old/opcode_rmw.cpp new file mode 100644 index 0000000..df59f49 --- /dev/null +++ b/libsnes/snes/smp/core-old/opcode_rmw.cpp @@ -0,0 +1,58 @@ +#ifdef SMPCORE_CPP + +template +void SMPcore::op_adjust_reg() { + op_io(); + regs.r[n] = (this->*op)(regs.r[n]); +} + +template +void SMPcore::op_adjust_dp() { + dp = op_readpc(); + rd = op_readdp(dp); + rd = (this->*op)(rd); + op_writedp(dp, rd); +} + +template +void SMPcore::op_adjust_dpx() { + dp = op_readpc(); + op_io(); + rd = op_readdp(dp + regs.x); + rd = (this->*op)(rd); + op_writedp(dp + regs.x, rd); +} + +template +void SMPcore::op_adjust_addr() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + rd = op_read(dp); + rd = (this->*op)(rd); + op_write(dp, rd); +} + +template +void SMPcore::op_adjust_addr_a() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + rd = op_read(dp); + regs.p.n = ((regs.a - rd) & 0x80); + regs.p.z = ((regs.a - rd) == 0); + op_read(dp); + op_write(dp, (op ? rd | regs.a : rd & ~regs.a)); +} + +template +void SMPcore::op_adjustw_dp() { + dp = op_readpc(); + rd = op_readdp(dp) << 0; + rd += adjust; + op_writedp(dp++, rd); + rd += op_readdp(dp) << 8; + op_writedp(dp, rd >> 8); + regs.p.n = (rd & 0x8000); + regs.p.z = (rd == 0); +} + +#endif diff --git a/libsnes/snes/smp/core-old/registers.hpp b/libsnes/snes/smp/core-old/registers.hpp new file mode 100644 index 0000000..261f19f --- /dev/null +++ b/libsnes/snes/smp/core-old/registers.hpp @@ -0,0 +1,44 @@ +struct regya_t { + uint8_t &hi, &lo; + + inline operator uint16_t() const { + return (hi << 8) + lo; + } + + inline regya_t& operator=(uint16_t data) { + hi = data >> 8; + lo = data; + return *this; + } + + regya_t(uint8_t &hi_, uint8_t &lo_) : hi(hi_), lo(lo_) {} +}; + +struct flag_t { + bool n, v, p, b, h, i, z, c; + + inline operator unsigned() const { + return (n << 7) + (v << 6) + (p << 5) + (b << 4) + + (h << 3) + (i << 2) + (z << 1) + (c << 0); + } + + inline unsigned operator=(uint8_t data) { + n = data & 0x80; v = data & 0x40; p = data & 0x20; b = data & 0x10; + h = 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), p(0), b(0), h(0), i(0), z(0), c(0) {} +}; + +struct regs_t { + uint16_t pc; + uint8_t r[4], &a, &x, &y, &sp, &s; + regya_t ya; + flag_t p; + regs_t() : a(r[0]), x(r[1]), y(r[2]), s(r[3]), sp(r[3]), ya(r[2], r[0]) {} +}; diff --git a/libsnes/snes/smp/core-old/serialization.cpp b/libsnes/snes/smp/core-old/serialization.cpp new file mode 100644 index 0000000..883d013 --- /dev/null +++ b/libsnes/snes/smp/core-old/serialization.cpp @@ -0,0 +1,26 @@ +#ifdef SMPCORE_CPP + +void SMPcore::core_serialize(serializer &s) { + s.integer(regs.pc); + s.integer(regs.a); + s.integer(regs.x); + s.integer(regs.y); + s.integer(regs.sp); + s.integer(regs.p.n); + s.integer(regs.p.v); + s.integer(regs.p.p); + s.integer(regs.p.b); + s.integer(regs.p.h); + s.integer(regs.p.i); + s.integer(regs.p.z); + s.integer(regs.p.c); + + s.integer(dp); + s.integer(sp); + s.integer(rd); + s.integer(wr); + s.integer(bit); + s.integer(ya); +} + +#endif diff --git a/libsnes/snes/smp/core-old/table.cpp b/libsnes/snes/smp/core-old/table.cpp new file mode 100644 index 0000000..dc0f633 --- /dev/null +++ b/libsnes/snes/smp/core-old/table.cpp @@ -0,0 +1,264 @@ +#ifdef SMPCORE_CPP + +void SMPcore::initialize_opcode_table() { + #define op opcode_table + op[0x00] = &SMPcore::op_nop; + op[0x01] = &SMPcore::op_tcall<0>; + op[0x02] = &SMPcore::op_setbit_dp<1, 0x01>; + op[0x03] = &SMPcore::op_bitbranch<0x01, true>; + op[0x04] = &SMPcore::op_read_reg_dp<&SMPcore::op_or, A>; + op[0x05] = &SMPcore::op_read_reg_addr<&SMPcore::op_or, A>; + op[0x06] = &SMPcore::op_read_a_ix<&SMPcore::op_or>; + op[0x07] = &SMPcore::op_read_a_idpx<&SMPcore::op_or>; + op[0x08] = &SMPcore::op_read_reg_const<&SMPcore::op_or, A>; + op[0x09] = &SMPcore::op_read_dp_dp<&SMPcore::op_or>; + op[0x0a] = &SMPcore::op_or1_bit<0>; + op[0x0b] = &SMPcore::op_adjust_dp<&SMPcore::op_asl>; + op[0x0c] = &SMPcore::op_adjust_addr<&SMPcore::op_asl>; + op[0x0d] = &SMPcore::op_push_p; + op[0x0e] = &SMPcore::op_adjust_addr_a<1>; + op[0x0f] = &SMPcore::op_brk; + op[0x10] = &SMPcore::op_branch<0x80, false>; + op[0x11] = &SMPcore::op_tcall<1>; + op[0x12] = &SMPcore::op_setbit_dp<0, 0x01>; + op[0x13] = &SMPcore::op_bitbranch<0x01, false>; + op[0x14] = &SMPcore::op_read_a_dpx<&SMPcore::op_or>; + op[0x15] = &SMPcore::op_read_a_addrr<&SMPcore::op_or, X>; + op[0x16] = &SMPcore::op_read_a_addrr<&SMPcore::op_or, Y>; + op[0x17] = &SMPcore::op_read_a_idpy<&SMPcore::op_or>; + op[0x18] = &SMPcore::op_read_dp_const<&SMPcore::op_or>; + op[0x19] = &SMPcore::op_read_ix_iy<&SMPcore::op_or>; + op[0x1a] = &SMPcore::op_adjustw_dp<-1>; + op[0x1b] = &SMPcore::op_adjust_dpx<&SMPcore::op_asl>; + op[0x1c] = &SMPcore::op_adjust_reg<&SMPcore::op_asl, A>; + op[0x1d] = &SMPcore::op_adjust_reg<&SMPcore::op_dec, X>; + op[0x1e] = &SMPcore::op_read_reg_addr<&SMPcore::op_cmp, X>; + op[0x1f] = &SMPcore::op_jmp_iaddrx; + op[0x20] = &SMPcore::op_setbit<0x20, 0x00>; + op[0x21] = &SMPcore::op_tcall<2>; + op[0x22] = &SMPcore::op_setbit_dp<1, 0x02>; + op[0x23] = &SMPcore::op_bitbranch<0x02, true>; + op[0x24] = &SMPcore::op_read_reg_dp<&SMPcore::op_and, A>; + op[0x25] = &SMPcore::op_read_reg_addr<&SMPcore::op_and, A>; + op[0x26] = &SMPcore::op_read_a_ix<&SMPcore::op_and>; + op[0x27] = &SMPcore::op_read_a_idpx<&SMPcore::op_and>; + op[0x28] = &SMPcore::op_read_reg_const<&SMPcore::op_and, A>; + op[0x29] = &SMPcore::op_read_dp_dp<&SMPcore::op_and>; + op[0x2a] = &SMPcore::op_or1_bit<1>; + op[0x2b] = &SMPcore::op_adjust_dp<&SMPcore::op_rol>; + op[0x2c] = &SMPcore::op_adjust_addr<&SMPcore::op_rol>; + op[0x2d] = &SMPcore::op_push_reg; + op[0x2e] = &SMPcore::op_cbne_dp; + op[0x2f] = &SMPcore::op_bra; + op[0x30] = &SMPcore::op_branch<0x80, true>; + op[0x31] = &SMPcore::op_tcall<3>; + op[0x32] = &SMPcore::op_setbit_dp<0, 0x02>; + op[0x33] = &SMPcore::op_bitbranch<0x02, false>; + op[0x34] = &SMPcore::op_read_a_dpx<&SMPcore::op_and>; + op[0x35] = &SMPcore::op_read_a_addrr<&SMPcore::op_and, X>; + op[0x36] = &SMPcore::op_read_a_addrr<&SMPcore::op_and, Y>; + op[0x37] = &SMPcore::op_read_a_idpy<&SMPcore::op_and>; + op[0x38] = &SMPcore::op_read_dp_const<&SMPcore::op_and>; + op[0x39] = &SMPcore::op_read_ix_iy<&SMPcore::op_and>; + op[0x3a] = &SMPcore::op_adjustw_dp<+1>; + op[0x3b] = &SMPcore::op_adjust_dpx<&SMPcore::op_rol>; + op[0x3c] = &SMPcore::op_adjust_reg<&SMPcore::op_rol, A>; + op[0x3d] = &SMPcore::op_adjust_reg<&SMPcore::op_inc, X>; + op[0x3e] = &SMPcore::op_read_reg_dp<&SMPcore::op_cmp, X>; + op[0x3f] = &SMPcore::op_call; + op[0x40] = &SMPcore::op_setbit<0x20, 0x20>; + op[0x41] = &SMPcore::op_tcall<4>; + op[0x42] = &SMPcore::op_setbit_dp<1, 0x04>; + op[0x43] = &SMPcore::op_bitbranch<0x04, true>; + op[0x44] = &SMPcore::op_read_reg_dp<&SMPcore::op_eor, A>; + op[0x45] = &SMPcore::op_read_reg_addr<&SMPcore::op_eor, A>; + op[0x46] = &SMPcore::op_read_a_ix<&SMPcore::op_eor>; + op[0x47] = &SMPcore::op_read_a_idpx<&SMPcore::op_eor>; + op[0x48] = &SMPcore::op_read_reg_const<&SMPcore::op_eor, A>; + op[0x49] = &SMPcore::op_read_dp_dp<&SMPcore::op_eor>; + op[0x4a] = &SMPcore::op_and1_bit<0>; + op[0x4b] = &SMPcore::op_adjust_dp<&SMPcore::op_lsr>; + op[0x4c] = &SMPcore::op_adjust_addr<&SMPcore::op_lsr>; + op[0x4d] = &SMPcore::op_push_reg; + op[0x4e] = &SMPcore::op_adjust_addr_a<0>; + op[0x4f] = &SMPcore::op_pcall; + op[0x50] = &SMPcore::op_branch<0x40, false>; + op[0x51] = &SMPcore::op_tcall<5>; + op[0x52] = &SMPcore::op_setbit_dp<0, 0x04>; + op[0x53] = &SMPcore::op_bitbranch<0x04, false>; + op[0x54] = &SMPcore::op_read_a_dpx<&SMPcore::op_eor>; + op[0x55] = &SMPcore::op_read_a_addrr<&SMPcore::op_eor, X>; + op[0x56] = &SMPcore::op_read_a_addrr<&SMPcore::op_eor, Y>; + op[0x57] = &SMPcore::op_read_a_idpy<&SMPcore::op_eor>; + op[0x58] = &SMPcore::op_read_dp_const<&SMPcore::op_eor>; + op[0x59] = &SMPcore::op_read_ix_iy<&SMPcore::op_eor>; + op[0x5a] = &SMPcore::op_cmpw_ya_dp; + op[0x5b] = &SMPcore::op_adjust_dpx<&SMPcore::op_lsr>; + op[0x5c] = &SMPcore::op_adjust_reg<&SMPcore::op_lsr, A>; + op[0x5d] = &SMPcore::op_mov_reg_reg; + op[0x5e] = &SMPcore::op_read_reg_addr<&SMPcore::op_cmp, Y>; + op[0x5f] = &SMPcore::op_jmp_addr; + op[0x60] = &SMPcore::op_setbit<0x01, 0x00>; + op[0x61] = &SMPcore::op_tcall<6>; + op[0x62] = &SMPcore::op_setbit_dp<1, 0x08>; + op[0x63] = &SMPcore::op_bitbranch<0x08, true>; + op[0x64] = &SMPcore::op_read_reg_dp<&SMPcore::op_cmp, A>; + op[0x65] = &SMPcore::op_read_reg_addr<&SMPcore::op_cmp, A>; + op[0x66] = &SMPcore::op_read_a_ix<&SMPcore::op_cmp>; + op[0x67] = &SMPcore::op_read_a_idpx<&SMPcore::op_cmp>; + op[0x68] = &SMPcore::op_read_reg_const<&SMPcore::op_cmp, A>; + op[0x69] = &SMPcore::op_read_dp_dp<&SMPcore::op_cmp>; + op[0x6a] = &SMPcore::op_and1_bit<1>; + op[0x6b] = &SMPcore::op_adjust_dp<&SMPcore::op_ror>; + op[0x6c] = &SMPcore::op_adjust_addr<&SMPcore::op_ror>; + op[0x6d] = &SMPcore::op_push_reg; + op[0x6e] = &SMPcore::op_dbnz_dp; + op[0x6f] = &SMPcore::op_ret; + op[0x70] = &SMPcore::op_branch<0x40, true>; + op[0x71] = &SMPcore::op_tcall<7>; + op[0x72] = &SMPcore::op_setbit_dp<0, 0x08>; + op[0x73] = &SMPcore::op_bitbranch<0x08, false>; + op[0x74] = &SMPcore::op_read_a_dpx<&SMPcore::op_cmp>; + op[0x75] = &SMPcore::op_read_a_addrr<&SMPcore::op_cmp, X>; + op[0x76] = &SMPcore::op_read_a_addrr<&SMPcore::op_cmp, Y>; + op[0x77] = &SMPcore::op_read_a_idpy<&SMPcore::op_cmp>; + op[0x78] = &SMPcore::op_read_dp_const<&SMPcore::op_cmp>; + op[0x79] = &SMPcore::op_read_ix_iy<&SMPcore::op_cmp>; + op[0x7a] = &SMPcore::op_read_ya_dp<&SMPcore::op_addw>; + op[0x7b] = &SMPcore::op_adjust_dpx<&SMPcore::op_ror>; + op[0x7c] = &SMPcore::op_adjust_reg<&SMPcore::op_ror, A>; + op[0x7d] = &SMPcore::op_mov_reg_reg; + op[0x7e] = &SMPcore::op_read_reg_dp<&SMPcore::op_cmp, Y>; + op[0x7f] = &SMPcore::op_reti; + op[0x80] = &SMPcore::op_setbit<0x01, 0x01>; + op[0x81] = &SMPcore::op_tcall<8>; + op[0x82] = &SMPcore::op_setbit_dp<1, 0x10>; + op[0x83] = &SMPcore::op_bitbranch<0x10, true>; + op[0x84] = &SMPcore::op_read_reg_dp<&SMPcore::op_adc, A>; + op[0x85] = &SMPcore::op_read_reg_addr<&SMPcore::op_adc, A>; + op[0x86] = &SMPcore::op_read_a_ix<&SMPcore::op_adc>; + op[0x87] = &SMPcore::op_read_a_idpx<&SMPcore::op_adc>; + op[0x88] = &SMPcore::op_read_reg_const<&SMPcore::op_adc, A>; + op[0x89] = &SMPcore::op_read_dp_dp<&SMPcore::op_adc>; + op[0x8a] = &SMPcore::op_eor1_bit; + op[0x8b] = &SMPcore::op_adjust_dp<&SMPcore::op_dec>; + op[0x8c] = &SMPcore::op_adjust_addr<&SMPcore::op_dec>; + op[0x8d] = &SMPcore::op_mov_reg_const; + op[0x8e] = &SMPcore::op_pop_p; + op[0x8f] = &SMPcore::op_mov_dp_const; + op[0x90] = &SMPcore::op_branch<0x01, false>; + op[0x91] = &SMPcore::op_tcall<9>; + op[0x92] = &SMPcore::op_setbit_dp<0, 0x10>; + op[0x93] = &SMPcore::op_bitbranch<0x10, false>; + op[0x94] = &SMPcore::op_read_a_dpx<&SMPcore::op_adc>; + op[0x95] = &SMPcore::op_read_a_addrr<&SMPcore::op_adc, X>; + op[0x96] = &SMPcore::op_read_a_addrr<&SMPcore::op_adc, Y>; + op[0x97] = &SMPcore::op_read_a_idpy<&SMPcore::op_adc>; + op[0x98] = &SMPcore::op_read_dp_const<&SMPcore::op_adc>; + op[0x99] = &SMPcore::op_read_ix_iy<&SMPcore::op_adc>; + op[0x9a] = &SMPcore::op_read_ya_dp<&SMPcore::op_subw>; + op[0x9b] = &SMPcore::op_adjust_dpx<&SMPcore::op_dec>; + op[0x9c] = &SMPcore::op_adjust_reg<&SMPcore::op_dec, A>; + op[0x9d] = &SMPcore::op_mov_reg_reg; + op[0x9e] = &SMPcore::op_div_ya_x; + op[0x9f] = &SMPcore::op_xcn; + op[0xa0] = &SMPcore::op_seti<1>; + op[0xa1] = &SMPcore::op_tcall<10>; + op[0xa2] = &SMPcore::op_setbit_dp<1, 0x20>; + op[0xa3] = &SMPcore::op_bitbranch<0x20, true>; + op[0xa4] = &SMPcore::op_read_reg_dp<&SMPcore::op_sbc, A>; + op[0xa5] = &SMPcore::op_read_reg_addr<&SMPcore::op_sbc, A>; + op[0xa6] = &SMPcore::op_read_a_ix<&SMPcore::op_sbc>; + op[0xa7] = &SMPcore::op_read_a_idpx<&SMPcore::op_sbc>; + op[0xa8] = &SMPcore::op_read_reg_const<&SMPcore::op_sbc, A>; + op[0xa9] = &SMPcore::op_read_dp_dp<&SMPcore::op_sbc>; + op[0xaa] = &SMPcore::op_mov1_c_bit; + op[0xab] = &SMPcore::op_adjust_dp<&SMPcore::op_inc>; + op[0xac] = &SMPcore::op_adjust_addr<&SMPcore::op_inc>; + op[0xad] = &SMPcore::op_read_reg_const<&SMPcore::op_cmp, Y>; + op[0xae] = &SMPcore::op_pop_reg; + op[0xaf] = &SMPcore::op_mov_ixinc_a; + op[0xb0] = &SMPcore::op_branch<0x01, true>; + op[0xb1] = &SMPcore::op_tcall<11>; + op[0xb2] = &SMPcore::op_setbit_dp<0, 0x20>; + op[0xb3] = &SMPcore::op_bitbranch<0x20, false>; + op[0xb4] = &SMPcore::op_read_a_dpx<&SMPcore::op_sbc>; + op[0xb5] = &SMPcore::op_read_a_addrr<&SMPcore::op_sbc, X>; + op[0xb6] = &SMPcore::op_read_a_addrr<&SMPcore::op_sbc, Y>; + op[0xb7] = &SMPcore::op_read_a_idpy<&SMPcore::op_sbc>; + op[0xb8] = &SMPcore::op_read_dp_const<&SMPcore::op_sbc>; + op[0xb9] = &SMPcore::op_read_ix_iy<&SMPcore::op_sbc>; + op[0xba] = &SMPcore::op_movw_ya_dp; + op[0xbb] = &SMPcore::op_adjust_dpx<&SMPcore::op_inc>; + op[0xbc] = &SMPcore::op_adjust_reg<&SMPcore::op_inc, A>; + op[0xbd] = &SMPcore::op_mov_sp_x; + op[0xbe] = &SMPcore::op_das; + op[0xbf] = &SMPcore::op_mov_a_ixinc; + op[0xc0] = &SMPcore::op_seti<0>; + op[0xc1] = &SMPcore::op_tcall<12>; + op[0xc2] = &SMPcore::op_setbit_dp<1, 0x40>; + op[0xc3] = &SMPcore::op_bitbranch<0x40, true>; + op[0xc4] = &SMPcore::op_mov_dp_reg; + op[0xc5] = &SMPcore::op_mov_addr_reg; + op[0xc6] = &SMPcore::op_mov_ix_a; + op[0xc7] = &SMPcore::op_mov_idpx_a; + op[0xc8] = &SMPcore::op_read_reg_const<&SMPcore::op_cmp, X>; + op[0xc9] = &SMPcore::op_mov_addr_reg; + op[0xca] = &SMPcore::op_mov1_bit_c; + op[0xcb] = &SMPcore::op_mov_dp_reg; + op[0xcc] = &SMPcore::op_mov_addr_reg; + op[0xcd] = &SMPcore::op_mov_reg_const; + op[0xce] = &SMPcore::op_pop_reg; + op[0xcf] = &SMPcore::op_mul_ya; + op[0xd0] = &SMPcore::op_branch<0x02, false>; + op[0xd1] = &SMPcore::op_tcall<13>; + op[0xd2] = &SMPcore::op_setbit_dp<0, 0x40>; + op[0xd3] = &SMPcore::op_bitbranch<0x40, false>; + op[0xd4] = &SMPcore::op_mov_dpr_reg; + op[0xd5] = &SMPcore::op_mov_addrr_a; + op[0xd6] = &SMPcore::op_mov_addrr_a; + op[0xd7] = &SMPcore::op_mov_idpy_a; + op[0xd8] = &SMPcore::op_mov_dp_reg; + op[0xd9] = &SMPcore::op_mov_dpr_reg; + op[0xda] = &SMPcore::op_movw_dp_ya; + op[0xdb] = &SMPcore::op_mov_dpr_reg; + op[0xdc] = &SMPcore::op_adjust_reg<&SMPcore::op_dec, Y>; + op[0xdd] = &SMPcore::op_mov_reg_reg; + op[0xde] = &SMPcore::op_cbne_dpx; + op[0xdf] = &SMPcore::op_daa; + op[0xe0] = &SMPcore::op_setbit<0x48, 0x00>; + op[0xe1] = &SMPcore::op_tcall<14>; + op[0xe2] = &SMPcore::op_setbit_dp<1, 0x80>; + op[0xe3] = &SMPcore::op_bitbranch<0x80, true>; + op[0xe4] = &SMPcore::op_mov_reg_dp; + op[0xe5] = &SMPcore::op_mov_reg_addr; + op[0xe6] = &SMPcore::op_mov_a_ix; + op[0xe7] = &SMPcore::op_mov_a_idpx; + op[0xe8] = &SMPcore::op_mov_reg_const; + op[0xe9] = &SMPcore::op_mov_reg_addr; + op[0xea] = &SMPcore::op_not1_bit; + op[0xeb] = &SMPcore::op_mov_reg_dp; + op[0xec] = &SMPcore::op_mov_reg_addr; + op[0xed] = &SMPcore::op_notc; + op[0xee] = &SMPcore::op_pop_reg; + op[0xef] = &SMPcore::op_wait; + op[0xf0] = &SMPcore::op_branch<0x02, true>; + op[0xf1] = &SMPcore::op_tcall<15>; + op[0xf2] = &SMPcore::op_setbit_dp<0, 0x80>; + op[0xf3] = &SMPcore::op_bitbranch<0x80, false>; + op[0xf4] = &SMPcore::op_mov_reg_dpr; + op[0xf5] = &SMPcore::op_mov_a_addrr; + op[0xf6] = &SMPcore::op_mov_a_addrr; + op[0xf7] = &SMPcore::op_mov_a_idpy; + op[0xf8] = &SMPcore::op_mov_reg_dp; + op[0xf9] = &SMPcore::op_mov_reg_dpr; + op[0xfa] = &SMPcore::op_mov_dp_dp; + op[0xfb] = &SMPcore::op_mov_reg_dpr; + op[0xfc] = &SMPcore::op_adjust_reg<&SMPcore::op_inc, Y>; + op[0xfd] = &SMPcore::op_mov_reg_reg; + op[0xfe] = &SMPcore::op_dbnz_y; + op[0xff] = &SMPcore::op_wait; + #undef op +} + +#endif diff --git a/libsnes/snes/smp/core/algorithms.cpp b/libsnes/snes/smp/core/algorithms.cpp new file mode 100755 index 0000000..69504cd --- /dev/null +++ b/libsnes/snes/smp/core/algorithms.cpp @@ -0,0 +1,138 @@ +#ifdef SMPCORE_CPP + +uint8 SMPcore::op_adc(uint8 x, uint8 y) { + int r = x + y + regs.p.c; + regs.p.n = r & 0x80; + regs.p.v = ~(x ^ y) & (x ^ r) & 0x80; + regs.p.h = (x ^ y ^ r) & 0x10; + regs.p.z = (uint8)r == 0; + regs.p.c = r > 0xff; + return r; +} + +uint8 SMPcore::op_and(uint8 x, uint8 y) { + x &= y; + regs.p.n = x & 0x80; + regs.p.z = x == 0; + return x; +} + +uint8 SMPcore::op_asl(uint8 x) { + regs.p.c = x & 0x80; + x <<= 1; + regs.p.n = x & 0x80; + regs.p.z = x == 0; + return x; +} + +uint8 SMPcore::op_cmp(uint8 x, uint8 y) { + int r = x - y; + regs.p.n = r & 0x80; + regs.p.z = (uint8)r == 0; + regs.p.c = r >= 0; + return x; +} + +uint8 SMPcore::op_dec(uint8 x) { + x--; + regs.p.n = x & 0x80; + regs.p.z = x == 0; + return x; +} + +uint8 SMPcore::op_eor(uint8 x, uint8 y) { + x ^= y; + regs.p.n = x & 0x80; + regs.p.z = x == 0; + return x; +} + +uint8 SMPcore::op_inc(uint8 x) { + x++; + regs.p.n = x & 0x80; + regs.p.z = x == 0; + return x; +} + +uint8 SMPcore::op_ld(uint8 x, uint8 y) { + regs.p.n = y & 0x80; + regs.p.z = y == 0; + return y; +} + +uint8 SMPcore::op_lsr(uint8 x) { + regs.p.c = x & 0x01; + x >>= 1; + regs.p.n = x & 0x80; + regs.p.z = x == 0; + return x; +} + +uint8 SMPcore::op_or(uint8 x, uint8 y) { + x |= y; + regs.p.n = x & 0x80; + regs.p.z = x == 0; + return x; +} + +uint8 SMPcore::op_rol(uint8 x) { + unsigned carry = regs.p.c << 0; + regs.p.c = x & 0x80; + x = (x << 1) | carry; + regs.p.n = x & 0x80; + regs.p.z = x == 0; + return x; +} + +uint8 SMPcore::op_ror(uint8 x) { + unsigned carry = regs.p.c << 7; + regs.p.c = x & 0x01; + x = carry | (x >> 1); + regs.p.n = x & 0x80; + regs.p.z = x == 0; + return x; +} + +uint8 SMPcore::op_sbc(uint8 x, uint8 y) { + return op_adc(x, ~y); +} + +uint8 SMPcore::op_st(uint8 x, uint8 y) { + return y; +} + +// + +uint16 SMPcore::op_adw(uint16 x, uint16 y) { + uint16 r; + regs.p.c = 0; + r = op_adc(x, y); + r |= op_adc(x >> 8, y >> 8) << 8; + regs.p.z = r == 0; + return r; +} + +uint16 SMPcore::op_cpw(uint16 x, uint16 y) { + int r = x - y; + regs.p.n = r & 0x8000; + regs.p.z = (uint16)r == 0; + regs.p.c = r >= 0; + return x; +} + +uint16 SMPcore::op_ldw(uint16 x, uint16 y) { + regs.p.n = y & 0x8000; + regs.p.z = y == 0; + return y; +} + +uint16 SMPcore::op_sbw(uint16 x, uint16 y) { + uint16 r; + regs.p.c = 1; + r = op_sbc(x, y); + r |= op_sbc(x >> 8, y >> 8) << 8; + regs.p.z = r == 0; + return r; +} + +#endif diff --git a/libsnes/snes/smp/core/core.hpp b/libsnes/snes/smp/core/core.hpp new file mode 100755 index 0000000..6adf6f6 --- /dev/null +++ b/libsnes/snes/smp/core/core.hpp @@ -0,0 +1,96 @@ +struct SMPcore { + virtual void op_io() = 0; + virtual uint8 op_read(uint16 addr) = 0; + virtual void op_write(uint16 addr, uint8 data) = 0; + void op_step(); + + #include "registers.hpp" + #include "memory.hpp" + + regs_t regs; + word_t dp, sp, rd, wr, bit, ya; + uint8 opcode; + + void core_serialize(serializer&); + string disassemble_opcode(uint16 addr); + +protected: + uint8 op_adc(uint8, uint8); + uint8 op_and(uint8, uint8); + uint8 op_asl(uint8); + uint8 op_cmp(uint8, uint8); + uint8 op_dec(uint8); + uint8 op_eor(uint8, uint8); + uint8 op_inc(uint8); + uint8 op_ld (uint8, uint8); + uint8 op_lsr(uint8); + uint8 op_or (uint8, uint8); + uint8 op_rol(uint8); + uint8 op_ror(uint8); + uint8 op_sbc(uint8, uint8); + uint8 op_st (uint8, uint8); + uint16 op_adw(uint16, uint16); + uint16 op_cpw(uint16, uint16); + uint16 op_ldw(uint16, uint16); + uint16 op_sbw(uint16, uint16); + + template void op_adjust(uint8&); + template void op_adjust_addr(); + template void op_adjust_dp(); + void op_adjust_dpw(signed); + template void op_adjust_dpx(); + void op_branch(bool); + void op_branch_bit(); + void op_pull(uint8&); + void op_push(uint8); + template void op_read_addr(uint8&); + template void op_read_addri(uint8&); + template void op_read_const(uint8&); + template void op_read_dp(uint8&); + template void op_read_dpi(uint8&, uint8&); + template void op_read_dpw(); + template void op_read_idpx(); + template void op_read_idpy(); + template void op_read_ix(); + void op_set_addr_bit(); + void op_set_bit(); + void op_set_flag(bool&, bool); + void op_test_addr(bool); + void op_transfer(uint8&, uint8&); + void op_write_addr(uint8&); + void op_write_addri(uint8&); + void op_write_dp(uint8&); + void op_write_dpi(uint8&, uint8&); + template void op_write_dp_const(); + template void op_write_dp_dp(); + template void op_write_ix_iy(); + + void op_bne_dp(); + void op_bne_dpdec(); + void op_bne_dpx(); + void op_bne_ydec(); + void op_brk(); + void op_clv(); + void op_cmc(); + void op_daa(); + void op_das(); + void op_div_ya_x(); + void op_jmp_addr(); + void op_jmp_iaddrx(); + void op_jsp_dp(); + void op_jsr_addr(); + void op_jst(); + void op_lda_ixinc(); + void op_mul_ya(); + void op_nop(); + void op_plp(); + void op_rti(); + void op_rts(); + void op_sta_idpx(); + void op_sta_idpy(); + void op_sta_ix(); + void op_sta_ixinc(); + void op_stw_dp(); + void op_wait(); + void op_xcn(); +}; diff --git a/libsnes/snes/smp/core/memory.hpp b/libsnes/snes/smp/core/memory.hpp new file mode 100755 index 0000000..c4b6d99 --- /dev/null +++ b/libsnes/snes/smp/core/memory.hpp @@ -0,0 +1,19 @@ +alwaysinline uint8 op_readpc() { + return op_read(regs.pc++); +} + +alwaysinline uint8 op_readsp() { + return op_read(0x0100 | ++regs.s); +} + +alwaysinline void op_writesp(uint8 data) { + return op_write(0x0100 | regs.s--, data); +} + +alwaysinline uint8 op_readdp(uint8 addr) { + return op_read((regs.p.p << 8) + addr); +} + +alwaysinline void op_writedp(uint8 addr, uint8 data) { + return op_write((regs.p.p << 8) + addr, data); +} diff --git a/libsnes/snes/smp/core/opcodes.cpp b/libsnes/snes/smp/core/opcodes.cpp new file mode 100644 index 0000000..95b9844 --- /dev/null +++ b/libsnes/snes/smp/core/opcodes.cpp @@ -0,0 +1,559 @@ +#ifdef SMPCORE_CPP +#define call (this->*op) + +template +void SMPcore::op_adjust(uint8 &r) { + op_io(); + r = call(r); +} + +template +void SMPcore::op_adjust_addr() { + dp.l = op_readpc(); + dp.h = op_readpc(); + rd = op_read(dp); + rd = call(rd); + op_write(dp, rd); +} + +template +void SMPcore::op_adjust_dp() { + dp = op_readpc(); + rd = op_readdp(dp); + rd = call(rd); + op_writedp(dp, rd); +} + +void SMPcore::op_adjust_dpw(signed n) { + dp = op_readpc(); + rd.w = op_readdp(dp) + n; + op_writedp(dp++, rd.l); + rd.h += op_readdp(dp); + op_writedp(dp++, rd.h); + regs.p.n = rd & 0x8000; + regs.p.z = rd == 0; +} + +template +void SMPcore::op_adjust_dpx() { + dp = op_readpc(); + op_io(); + rd = op_readdp(dp + regs.x); + rd = call(rd); + op_writedp(dp + regs.x, rd); +} + +void SMPcore::op_branch(bool condition) { + rd = op_readpc(); + if(condition == false) return; + op_io(); + op_io(); + regs.pc += (int8)rd; +} + +void SMPcore::op_branch_bit() { + dp = op_readpc(); + sp = op_readdp(dp); + rd = op_readpc(); + op_io(); + if((bool)(sp & (1 << (opcode >> 5))) == (bool)(opcode & 0x10)) return; + op_io(); + op_io(); + regs.pc += (int8)rd; +} + +void SMPcore::op_pull(uint8 &r) { + op_io(); + op_io(); + r = op_readsp(); +} + +void SMPcore::op_push(uint8 r) { + op_io(); + op_io(); + op_writesp(r); +} + +template +void SMPcore::op_read_addr(uint8 &r) { + dp.l = op_readpc(); + dp.h = op_readpc(); + rd = op_read(dp); + r = call(r, rd); +} + +template +void SMPcore::op_read_addri(uint8 &r) { + dp.l = op_readpc(); + dp.h = op_readpc(); + op_io(); + rd = op_read(dp + r); + regs.a = call(regs.a, rd); +} + +template +void SMPcore::op_read_const(uint8 &r) { + rd = op_readpc(); + r = call(r, rd); +} + +template +void SMPcore::op_read_dp(uint8 &r) { + dp = op_readpc(); + rd = op_readdp(dp); + r = call(r, rd); +} + +template +void SMPcore::op_read_dpi(uint8 &r, uint8 &i) { + dp = op_readpc(); + op_io(); + rd = op_readdp(dp + i); + r = call(r, rd); +} + +template +void SMPcore::op_read_dpw() { + dp = op_readpc(); + rd.l = op_readdp(dp++); + if(op != &SMPcore::op_cpw) op_io(); + rd.h = op_readdp(dp++); + regs.ya = call(regs.ya, rd); +} + +template +void SMPcore::op_read_idpx() { + dp = op_readpc() + regs.x; + op_io(); + sp.l = op_readdp(dp++); + sp.h = op_readdp(dp++); + rd = op_read(sp); + regs.a = call(regs.a, rd); +} + +template +void SMPcore::op_read_idpy() { + dp = op_readpc(); + op_io(); + sp.l = op_readdp(dp++); + sp.h = op_readdp(dp++); + rd = op_read(sp + regs.y); + regs.a = call(regs.a, rd); +} + +template +void SMPcore::op_read_ix() { + op_io(); + rd = op_readdp(regs.x); + regs.a = call(regs.a, rd); +} + +void SMPcore::op_set_addr_bit() { + dp.l = op_readpc(); + dp.h = op_readpc(); + bit = dp >> 13; + dp &= 0x1fff; + rd = op_read(dp); + switch(opcode >> 5) { + case 0: //orc addr:bit + case 1: //orc !addr:bit + op_io(); + regs.p.c |= (rd & (1 << bit)) ^ (bool)(opcode & 0x20); + break; + case 2: //and addr:bit + case 3: //and !addr:bit + regs.p.c &= (rd & (1 << bit)) ^ (bool)(opcode & 0x20); + break; + case 4: //eor addr:bit + op_io(); + regs.p.c ^= (bool)(rd & (1 << bit)); + break; + case 5: //ldc addr:bit + regs.p.c = (rd & (1 << bit)); + break; + case 6: //stc addr:bit + op_io(); + rd = (rd & ~(1 << bit)) | (regs.p.c << bit); + op_write(dp, rd); + break; + case 7: //not addr:bit + rd ^= 1 << bit; + op_write(dp, rd); + break; + } +} + +void SMPcore::op_set_bit() { + dp = op_readpc(); + rd = op_readdp(dp) & ~(1 << (opcode >> 5)); + op_writedp(dp, rd | (!(opcode & 0x10) << (opcode >> 5))); +} + +void SMPcore::op_set_flag(bool &flag, bool data) { + op_io(); + if(&flag == ®s.p.i) op_io(); + flag = data; +} + +void SMPcore::op_test_addr(bool set) { + dp.l = op_readpc(); + dp.h = op_readpc(); + rd = op_read(dp); + regs.p.n = (regs.a - rd) & 0x80; + regs.p.z = (regs.a - rd) == 0; + op_read(dp); + op_write(dp, set ? rd | regs.a : rd & ~regs.a); +} + +void SMPcore::op_transfer(uint8 &from, uint8 &to) { + op_io(); + to = from; + if(&to == ®s.s) return; + regs.p.n = (to & 0x80); + regs.p.z = (to == 0); +} + +void SMPcore::op_write_addr(uint8 &r) { + dp.l = op_readpc(); + dp.h = op_readpc(); + op_read(dp); + op_write(dp, r); +} + +void SMPcore::op_write_addri(uint8 &i) { + dp.l = op_readpc(); + dp.h = op_readpc(); + op_io(); + dp += i; + op_read(dp); + op_write(dp, regs.a); +} + +void SMPcore::op_write_dp(uint8 &r) { + dp = op_readpc(); + op_readdp(dp); + op_writedp(dp, r); +} + +void SMPcore::op_write_dpi(uint8 &r, uint8 &i) { + dp = op_readpc() + i; + op_io(); + op_readdp(dp); + op_writedp(dp, r); +} + +template +void SMPcore::op_write_dp_const() { + rd = op_readpc(); + dp = op_readpc(); + wr = op_readdp(dp); + wr = call(wr, rd); + op != &SMPcore::op_cmp ? op_writedp(dp, wr) : op_io(); +} + +template +void SMPcore::op_write_dp_dp() { + sp = op_readpc(); + rd = op_readdp(sp); + dp = op_readpc(); + if(op != &SMPcore::op_st) wr = op_readdp(dp); + wr = call(wr, rd); + op != &SMPcore::op_cmp ? op_writedp(dp, wr) : op_io(); +} + +template +void SMPcore::op_write_ix_iy() { + op_io(); + rd = op_readdp(regs.y); + wr = op_readdp(regs.x); + wr = call(wr, rd); + op != &SMPcore::op_cmp ? op_writedp(regs.x, wr) : op_io(); +} + +// + +void SMPcore::op_bne_dp() { + dp = op_readpc(); + sp = op_readdp(dp); + rd = op_readpc(); + op_io(); + if(regs.a == sp) return; + op_io(); + op_io(); + regs.pc += (int8)rd; +} + +void SMPcore::op_bne_dpdec() { + dp = op_readpc(); + wr = op_readdp(dp); + op_writedp(dp, --wr); + rd = op_readpc(); + if(wr == 0) return; + op_io(); + op_io(); + regs.pc += (int8)rd; +} + +void SMPcore::op_bne_dpx() { + dp = op_readpc(); + op_io(); + sp = op_readdp(dp + regs.x); + rd = op_readpc(); + op_io(); + if(regs.a == sp) return; + op_io(); + op_io(); + regs.pc += (int8)rd; +} + +void SMPcore::op_bne_ydec() { + rd = op_readpc(); + op_io(); + op_io(); + if(--regs.y == 0) return; + op_io(); + op_io(); + regs.pc += (int8)rd; +} + +void SMPcore::op_brk() { + rd.l = op_read(0xffde); + rd.h = op_read(0xffdf); + op_io(); + op_io(); + op_writesp(regs.pc.h); + op_writesp(regs.pc.l); + op_writesp(regs.p); + regs.pc = rd; + regs.p.b = 1; + regs.p.i = 0; +} + +void SMPcore::op_clv() { + op_io(); + regs.p.v = 0; + regs.p.h = 0; +} + +void SMPcore::op_cmc() { + op_io(); + op_io(); + regs.p.c = !regs.p.c; +} + +void SMPcore::op_daa() { + op_io(); + op_io(); + if(regs.p.c || (regs.a) > 0x99) { + regs.a += 0x60; + regs.p.c = 1; + } + if(regs.p.h || (regs.a & 15) > 0x09) { + regs.a += 0x06; + } + regs.p.n = (regs.a & 0x80); + regs.p.z = (regs.a == 0); +} + +void SMPcore::op_das() { + op_io(); + op_io(); + if(!regs.p.c || (regs.a) > 0x99) { + regs.a -= 0x60; + regs.p.c = 0; + } + if(!regs.p.h || (regs.a & 15) > 0x09) { + regs.a -= 0x06; + } + regs.p.n = (regs.a & 0x80); + regs.p.z = (regs.a == 0); +} + +void SMPcore::op_div_ya_x() { + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + ya = regs.ya; + //overflow set if quotient >= 256 + regs.p.v = (regs.y >= regs.x); + regs.p.h = ((regs.y & 15) >= (regs.x & 15)); + if(regs.y < (regs.x << 1)) { + //if quotient is <= 511 (will fit into 9-bit result) + regs.a = ya / regs.x; + regs.y = ya % regs.x; + } else { + //otherwise, the quotient won't fit into regs.p.v + regs.a + //this emulates the odd behavior of the S-SMP in this case + regs.a = 255 - (ya - (regs.x << 9)) / (256 - regs.x); + regs.y = regs.x + (ya - (regs.x << 9)) % (256 - regs.x); + } + //result is set based on a (quotient) only + regs.p.n = (regs.a & 0x80); + regs.p.z = (regs.a == 0); +} + +void SMPcore::op_jmp_addr() { + rd.l = op_readpc(); + rd.h = op_readpc(); + regs.pc = rd; +} + +void SMPcore::op_jmp_iaddrx() { + dp.l = op_readpc(); + dp.h = op_readpc(); + op_io(); + dp += regs.x; + rd.l = op_read(dp++); + rd.h = op_read(dp++); + regs.pc = rd; +} + +void SMPcore::op_jsp_dp() { + rd = op_readpc(); + op_io(); + op_io(); + op_writesp(regs.pc.h); + op_writesp(regs.pc.l); + regs.pc = 0xff00 | rd; +} + +void SMPcore::op_jsr_addr() { + rd.l = op_readpc(); + rd.h = op_readpc(); + op_io(); + op_io(); + op_io(); + op_writesp(regs.pc.h); + op_writesp(regs.pc.l); + regs.pc = rd; +} + +void SMPcore::op_jst() { + dp = 0xffde - ((opcode >> 4) << 1); + rd.l = op_read(dp++); + rd.h = op_read(dp++); + op_io(); + op_io(); + op_io(); + op_writesp(regs.pc.h); + op_writesp(regs.pc.l); + regs.pc = rd; +} + +void SMPcore::op_lda_ixinc() { + op_io(); + regs.a = op_readdp(regs.x++); + op_io(); + regs.p.n = regs.a & 0x80; + regs.p.z = regs.a == 0; +} + +void SMPcore::op_mul_ya() { + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + ya = regs.y * regs.a; + regs.a = ya; + regs.y = ya >> 8; + //result is set based on y (high-byte) only + regs.p.n = (regs.y & 0x80); + regs.p.z = (regs.y == 0); +} + +void SMPcore::op_nop() { + op_io(); +} + +void SMPcore::op_plp() { + op_io(); + op_io(); + regs.p = op_readsp(); +} + +void SMPcore::op_rti() { + regs.p = op_readsp(); + rd.l = op_readsp(); + rd.h = op_readsp(); + op_io(); + op_io(); + regs.pc = rd; +} + +void SMPcore::op_rts() { + rd.l = op_readsp(); + rd.h = op_readsp(); + op_io(); + op_io(); + regs.pc = rd; +} + +void SMPcore::op_sta_idpx() { + sp = op_readpc() + regs.x; + op_io(); + dp.l = op_readdp(sp++); + dp.h = op_readdp(sp++); + op_read(dp); + op_write(dp, regs.a); +} + +void SMPcore::op_sta_idpy() { + sp = op_readpc(); + dp.l = op_readdp(sp++); + dp.h = op_readdp(sp++); + op_io(); + dp += regs.y; + op_read(dp); + op_write(dp, regs.a); +} + +void SMPcore::op_sta_ix() { + op_io(); + op_readdp(regs.x); + op_writedp(regs.x, regs.a); +} + +void SMPcore::op_sta_ixinc() { + op_io(); + op_io(); + op_writedp(regs.x++, regs.a); +} + +void SMPcore::op_stw_dp() { + dp = op_readpc(); + op_readdp(dp); + op_writedp(dp++, regs.a); + op_writedp(dp++, regs.y); +} + +void SMPcore::op_wait() { + while(true) { + op_io(); + op_io(); + } +} + +void SMPcore::op_xcn() { + op_io(); + op_io(); + op_io(); + op_io(); + regs.a = (regs.a >> 4) | (regs.a << 4); + regs.p.n = regs.a & 0x80; + regs.p.z = regs.a == 0; +} + +#undef call +#endif diff --git a/libsnes/snes/smp/core/registers.hpp b/libsnes/snes/smp/core/registers.hpp new file mode 100755 index 0000000..b033c42 --- /dev/null +++ b/libsnes/snes/smp/core/registers.hpp @@ -0,0 +1,54 @@ +struct flag_t { + bool n, v, p, b, h, i, z, c; + + inline operator unsigned() const { + return (n << 7) | (v << 6) | (p << 5) | (b << 4) + | (h << 3) | (i << 2) | (z << 1) | (c << 0); + } + + inline unsigned operator=(uint8 data) { + n = data & 0x80; v = data & 0x40; p = data & 0x20; b = data & 0x10; + h = data & 0x08; i = data & 0x04; z = data & 0x02; c = data & 0x01; + return data; + } + + inline unsigned operator|=(uint8 data) { return operator=(operator unsigned() | data); } + inline unsigned operator^=(uint8 data) { return operator=(operator unsigned() ^ data); } + inline unsigned operator&=(uint8 data) { return operator=(operator unsigned() & data); } +}; + +struct word_t { + union { + uint16 w; + struct { uint8 order_lsb2(l, h); }; + }; + + word_t(uint16 data) : w(data) {} + word_t() : w(0) {} + + inline operator uint16() const { return w; } + inline unsigned operator=(unsigned data) { return w = data; } + + inline unsigned operator++() { return ++w; } + inline unsigned operator--() { return --w; } + + inline unsigned operator++(int) { unsigned data = w++; return data; } + inline unsigned operator--(int) { unsigned data = w--; return data; } + + inline unsigned operator+=(unsigned data) { return w += data;; } + inline unsigned operator-=(unsigned data) { return w -= data;; } + + inline unsigned operator|=(unsigned data) { return w |= data; } + inline unsigned operator^=(unsigned data) { return w ^= data; } + inline unsigned operator&=(unsigned data) { return w &= data; } +}; + +struct regs_t { + word_t pc; + union { + uint16 ya; + struct { uint8 order_lsb2(a, y); }; + }; + uint8 x, s; + flag_t p; +}; diff --git a/libsnes/snes/smp/core/serialization.cpp b/libsnes/snes/smp/core/serialization.cpp new file mode 100755 index 0000000..c25e229 --- /dev/null +++ b/libsnes/snes/smp/core/serialization.cpp @@ -0,0 +1,27 @@ +#ifdef SMPCORE_CPP + +void SMPcore::core_serialize(serializer &s) { + s.integer(regs.pc); + s.integer(regs.a); + s.integer(regs.x); + s.integer(regs.y); + s.integer(regs.s); + s.integer(regs.p.n); + s.integer(regs.p.v); + s.integer(regs.p.p); + s.integer(regs.p.b); + s.integer(regs.p.h); + s.integer(regs.p.i); + s.integer(regs.p.z); + s.integer(regs.p.c); + + s.integer(opcode); + s.integer(dp.w); + s.integer(sp.w); + s.integer(rd.w); + s.integer(wr.w); + s.integer(bit.w); + s.integer(ya.w); +} + +#endif diff --git a/libsnes/snes/smp/core/smp_core.cpp b/libsnes/snes/smp/core/smp_core.cpp new file mode 100644 index 0000000..18d616e --- /dev/null +++ b/libsnes/snes/smp/core/smp_core.cpp @@ -0,0 +1,271 @@ +#include + +#define SMPCORE_CPP +namespace SNES { + +#include "algorithms.cpp" +#include "opcodes.cpp" +#include "serialization.cpp" + +void SMPcore::op_step() { + switch(opcode = op_readpc()) { + case 0x00: return op_nop(); + case 0x01: return op_jst(); + case 0x02: return op_set_bit(); + case 0x03: return op_branch_bit(); + case 0x04: return op_read_dp<&SMPcore::op_or>(regs.a); + case 0x05: return op_read_addr<&SMPcore::op_or>(regs.a); + case 0x06: return op_read_ix<&SMPcore::op_or>(); + case 0x07: return op_read_idpx<&SMPcore::op_or>(); + case 0x08: return op_read_const<&SMPcore::op_or>(regs.a); + case 0x09: return op_write_dp_dp<&SMPcore::op_or>(); + case 0x0a: return op_set_addr_bit(); + case 0x0b: return op_adjust_dp<&SMPcore::op_asl>(); + case 0x0c: return op_adjust_addr<&SMPcore::op_asl>(); + case 0x0d: return op_push(regs.p); + case 0x0e: return op_test_addr(1); + case 0x0f: return op_brk(); + case 0x10: return op_branch(regs.p.n == 0); + case 0x11: return op_jst(); + case 0x12: return op_set_bit(); + case 0x13: return op_branch_bit(); + case 0x14: return op_read_dpi<&SMPcore::op_or>(regs.a, regs.x); + case 0x15: return op_read_addri<&SMPcore::op_or>(regs.x); + case 0x16: return op_read_addri<&SMPcore::op_or>(regs.y); + case 0x17: return op_read_idpy<&SMPcore::op_or>(); + case 0x18: return op_write_dp_const<&SMPcore::op_or>(); + case 0x19: return op_write_ix_iy<&SMPcore::op_or>(); + case 0x1a: return op_adjust_dpw(-1); + case 0x1b: return op_adjust_dpx<&SMPcore::op_asl>(); + case 0x1c: return op_adjust<&SMPcore::op_asl>(regs.a); + case 0x1d: return op_adjust<&SMPcore::op_dec>(regs.x); + case 0x1e: return op_read_addr<&SMPcore::op_cmp>(regs.x); + case 0x1f: return op_jmp_iaddrx(); + case 0x20: return op_set_flag(regs.p.p, 0); + case 0x21: return op_jst(); + case 0x22: return op_set_bit(); + case 0x23: return op_branch_bit(); + case 0x24: return op_read_dp<&SMPcore::op_and>(regs.a); + case 0x25: return op_read_addr<&SMPcore::op_and>(regs.a); + case 0x26: return op_read_ix<&SMPcore::op_and>(); + case 0x27: return op_read_idpx<&SMPcore::op_and>(); + case 0x28: return op_read_const<&SMPcore::op_and>(regs.a); + case 0x29: return op_write_dp_dp<&SMPcore::op_and>(); + case 0x2a: return op_set_addr_bit(); + case 0x2b: return op_adjust_dp<&SMPcore::op_rol>(); + case 0x2c: return op_adjust_addr<&SMPcore::op_rol>(); + case 0x2d: return op_push(regs.a); + case 0x2e: return op_bne_dp(); + case 0x2f: return op_branch(true); + case 0x30: return op_branch(regs.p.n == 1); + case 0x31: return op_jst(); + case 0x32: return op_set_bit(); + case 0x33: return op_branch_bit(); + case 0x34: return op_read_dpi<&SMPcore::op_and>(regs.a, regs.x); + case 0x35: return op_read_addri<&SMPcore::op_and>(regs.x); + case 0x36: return op_read_addri<&SMPcore::op_and>(regs.y); + case 0x37: return op_read_idpy<&SMPcore::op_and>(); + case 0x38: return op_write_dp_const<&SMPcore::op_and>(); + case 0x39: return op_write_ix_iy<&SMPcore::op_and>(); + case 0x3a: return op_adjust_dpw(+1); + case 0x3b: return op_adjust_dpx<&SMPcore::op_rol>(); + case 0x3c: return op_adjust<&SMPcore::op_rol>(regs.a); + case 0x3d: return op_adjust<&SMPcore::op_inc>(regs.x); + case 0x3e: return op_read_dp<&SMPcore::op_cmp>(regs.x); + case 0x3f: return op_jsr_addr(); + case 0x40: return op_set_flag(regs.p.p, 1); + case 0x41: return op_jst(); + case 0x42: return op_set_bit(); + case 0x43: return op_branch_bit(); + case 0x44: return op_read_dp<&SMPcore::op_eor>(regs.a); + case 0x45: return op_read_addr<&SMPcore::op_eor>(regs.a); + case 0x46: return op_read_ix<&SMPcore::op_eor>(); + case 0x47: return op_read_idpx<&SMPcore::op_eor>(); + case 0x48: return op_read_const<&SMPcore::op_eor>(regs.a); + case 0x49: return op_write_dp_dp<&SMPcore::op_eor>(); + case 0x4a: return op_set_addr_bit(); + case 0x4b: return op_adjust_dp<&SMPcore::op_lsr>(); + case 0x4c: return op_adjust_addr<&SMPcore::op_lsr>(); + case 0x4d: return op_push(regs.x); + case 0x4e: return op_test_addr(0); + case 0x4f: return op_jsp_dp(); + case 0x50: return op_branch(regs.p.v == 0); + case 0x51: return op_jst(); + case 0x52: return op_set_bit(); + case 0x53: return op_branch_bit(); + case 0x54: return op_read_dpi<&SMPcore::op_eor>(regs.a, regs.x); + case 0x55: return op_read_addri<&SMPcore::op_eor>(regs.x); + case 0x56: return op_read_addri<&SMPcore::op_eor>(regs.y); + case 0x57: return op_read_idpy<&SMPcore::op_eor>(); + case 0x58: return op_write_dp_const<&SMPcore::op_eor>(); + case 0x59: return op_write_ix_iy<&SMPcore::op_eor>(); + case 0x5a: return op_read_dpw<&SMPcore::op_cpw>(); + case 0x5b: return op_adjust_dpx<&SMPcore::op_lsr>(); + case 0x5c: return op_adjust<&SMPcore::op_lsr>(regs.a); + case 0x5d: return op_transfer(regs.a, regs.x); + case 0x5e: return op_read_addr<&SMPcore::op_cmp>(regs.y); + case 0x5f: return op_jmp_addr(); + case 0x60: return op_set_flag(regs.p.c, 0); + case 0x61: return op_jst(); + case 0x62: return op_set_bit(); + case 0x63: return op_branch_bit(); + case 0x64: return op_read_dp<&SMPcore::op_cmp>(regs.a); + case 0x65: return op_read_addr<&SMPcore::op_cmp>(regs.a); + case 0x66: return op_read_ix<&SMPcore::op_cmp>(); + case 0x67: return op_read_idpx<&SMPcore::op_cmp>(); + case 0x68: return op_read_const<&SMPcore::op_cmp>(regs.a); + case 0x69: return op_write_dp_dp<&SMPcore::op_cmp>(); + case 0x6a: return op_set_addr_bit(); + case 0x6b: return op_adjust_dp<&SMPcore::op_ror>(); + case 0x6c: return op_adjust_addr<&SMPcore::op_ror>(); + case 0x6d: return op_push(regs.y); + case 0x6e: return op_bne_dpdec(); + case 0x6f: return op_rts(); + case 0x70: return op_branch(regs.p.v == 1); + case 0x71: return op_jst(); + case 0x72: return op_set_bit(); + case 0x73: return op_branch_bit(); + case 0x74: return op_read_dpi<&SMPcore::op_cmp>(regs.a, regs.x); + case 0x75: return op_read_addri<&SMPcore::op_cmp>(regs.x); + case 0x76: return op_read_addri<&SMPcore::op_cmp>(regs.y); + case 0x77: return op_read_idpy<&SMPcore::op_cmp>(); + case 0x78: return op_write_dp_const<&SMPcore::op_cmp>(); + case 0x79: return op_write_ix_iy<&SMPcore::op_cmp>(); + case 0x7a: return op_read_dpw<&SMPcore::op_adw>(); + case 0x7b: return op_adjust_dpx<&SMPcore::op_ror>(); + case 0x7c: return op_adjust<&SMPcore::op_ror>(regs.a); + case 0x7d: return op_transfer(regs.x, regs.a); + case 0x7e: return op_read_dp<&SMPcore::op_cmp>(regs.y); + case 0x7f: return op_rti(); + case 0x80: return op_set_flag(regs.p.c, 1); + case 0x81: return op_jst(); + case 0x82: return op_set_bit(); + case 0x83: return op_branch_bit(); + case 0x84: return op_read_dp<&SMPcore::op_adc>(regs.a); + case 0x85: return op_read_addr<&SMPcore::op_adc>(regs.a); + case 0x86: return op_read_ix<&SMPcore::op_adc>(); + case 0x87: return op_read_idpx<&SMPcore::op_adc>(); + case 0x88: return op_read_const<&SMPcore::op_adc>(regs.a); + case 0x89: return op_write_dp_dp<&SMPcore::op_adc>(); + case 0x8a: return op_set_addr_bit(); + case 0x8b: return op_adjust_dp<&SMPcore::op_dec>(); + case 0x8c: return op_adjust_addr<&SMPcore::op_dec>(); + case 0x8d: return op_read_const<&SMPcore::op_ld>(regs.y); + case 0x8e: return op_plp(); + case 0x8f: return op_write_dp_const<&SMPcore::op_st>(); + case 0x90: return op_branch(regs.p.c == 0); + case 0x91: return op_jst(); + case 0x92: return op_set_bit(); + case 0x93: return op_branch_bit(); + case 0x94: return op_read_dpi<&SMPcore::op_adc>(regs.a, regs.x); + case 0x95: return op_read_addri<&SMPcore::op_adc>(regs.x); + case 0x96: return op_read_addri<&SMPcore::op_adc>(regs.y); + case 0x97: return op_read_idpy<&SMPcore::op_adc>(); + case 0x98: return op_write_dp_const<&SMPcore::op_adc>(); + case 0x99: return op_write_ix_iy<&SMPcore::op_adc>(); + case 0x9a: return op_read_dpw<&SMPcore::op_sbw>(); + case 0x9b: return op_adjust_dpx<&SMPcore::op_dec>(); + case 0x9c: return op_adjust<&SMPcore::op_dec>(regs.a); + case 0x9d: return op_transfer(regs.s, regs.x); + case 0x9e: return op_div_ya_x(); + case 0x9f: return op_xcn(); + case 0xa0: return op_set_flag(regs.p.i, 1); + case 0xa1: return op_jst(); + case 0xa2: return op_set_bit(); + case 0xa3: return op_branch_bit(); + case 0xa4: return op_read_dp<&SMPcore::op_sbc>(regs.a); + case 0xa5: return op_read_addr<&SMPcore::op_sbc>(regs.a); + case 0xa6: return op_read_ix<&SMPcore::op_sbc>(); + case 0xa7: return op_read_idpx<&SMPcore::op_sbc>(); + case 0xa8: return op_read_const<&SMPcore::op_sbc>(regs.a); + case 0xa9: return op_write_dp_dp<&SMPcore::op_sbc>(); + case 0xaa: return op_set_addr_bit(); + case 0xab: return op_adjust_dp<&SMPcore::op_inc>(); + case 0xac: return op_adjust_addr<&SMPcore::op_inc>(); + case 0xad: return op_read_const<&SMPcore::op_cmp>(regs.y); + case 0xae: return op_pull(regs.a); + case 0xaf: return op_sta_ixinc(); + case 0xb0: return op_branch(regs.p.c == 1); + case 0xb1: return op_jst(); + case 0xb2: return op_set_bit(); + case 0xb3: return op_branch_bit(); + case 0xb4: return op_read_dpi<&SMPcore::op_sbc>(regs.a, regs.x); + case 0xb5: return op_read_addri<&SMPcore::op_sbc>(regs.x); + case 0xb6: return op_read_addri<&SMPcore::op_sbc>(regs.y); + case 0xb7: return op_read_idpy<&SMPcore::op_sbc>(); + case 0xb8: return op_write_dp_const<&SMPcore::op_sbc>(); + case 0xb9: return op_write_ix_iy<&SMPcore::op_sbc>(); + case 0xba: return op_read_dpw<&SMPcore::op_ldw>(); + case 0xbb: return op_adjust_dpx<&SMPcore::op_inc>(); + case 0xbc: return op_adjust<&SMPcore::op_inc>(regs.a); + case 0xbd: return op_transfer(regs.x, regs.s); + case 0xbe: return op_das(); + case 0xbf: return op_lda_ixinc(); + case 0xc0: return op_set_flag(regs.p.i, 0); + case 0xc1: return op_jst(); + case 0xc2: return op_set_bit(); + case 0xc3: return op_branch_bit(); + case 0xc4: return op_write_dp(regs.a); + case 0xc5: return op_write_addr(regs.a); + case 0xc6: return op_sta_ix(); + case 0xc7: return op_sta_idpx(); + case 0xc8: return op_read_const<&SMPcore::op_cmp>(regs.x); + case 0xc9: return op_write_addr(regs.x); + case 0xca: return op_set_addr_bit(); + case 0xcb: return op_write_dp(regs.y); + case 0xcc: return op_write_addr(regs.y); + case 0xcd: return op_read_const<&SMPcore::op_ld>(regs.x); + case 0xce: return op_pull(regs.x); + case 0xcf: return op_mul_ya(); + case 0xd0: return op_branch(regs.p.z == 0); + case 0xd1: return op_jst(); + case 0xd2: return op_set_bit(); + case 0xd3: return op_branch_bit(); + case 0xd4: return op_write_dpi(regs.a, regs.x); + case 0xd5: return op_write_addri(regs.x); + case 0xd6: return op_write_addri(regs.y); + case 0xd7: return op_sta_idpy(); + case 0xd8: return op_write_dp(regs.x); + case 0xd9: return op_write_dpi(regs.x, regs.y); + case 0xda: return op_stw_dp(); + case 0xdb: return op_write_dpi(regs.y, regs.x); + case 0xdc: return op_adjust<&SMPcore::op_dec>(regs.y); + case 0xdd: return op_transfer(regs.y, regs.a); + case 0xde: return op_bne_dpx(); + case 0xdf: return op_daa(); + case 0xe0: return op_clv(); + case 0xe1: return op_jst(); + case 0xe2: return op_set_bit(); + case 0xe3: return op_branch_bit(); + case 0xe4: return op_read_dp<&SMPcore::op_ld>(regs.a); + case 0xe5: return op_read_addr<&SMPcore::op_ld>(regs.a); + case 0xe6: return op_read_ix<&SMPcore::op_ld>(); + case 0xe7: return op_read_idpx<&SMPcore::op_ld>(); + case 0xe8: return op_read_const<&SMPcore::op_ld>(regs.a); + case 0xe9: return op_read_addr<&SMPcore::op_ld>(regs.x); + case 0xea: return op_set_addr_bit(); + case 0xeb: return op_read_dp<&SMPcore::op_ld>(regs.y); + case 0xec: return op_read_addr<&SMPcore::op_ld>(regs.y); + case 0xed: return op_cmc(); + case 0xee: return op_pull(regs.y); + case 0xef: return op_wait(); + case 0xf0: return op_branch(regs.p.z == 1); + case 0xf1: return op_jst(); + case 0xf2: return op_set_bit(); + case 0xf3: return op_branch_bit(); + case 0xf4: return op_read_dpi<&SMPcore::op_ld>(regs.a, regs.x); + case 0xf5: return op_read_addri<&SMPcore::op_ld>(regs.x); + case 0xf6: return op_read_addri<&SMPcore::op_ld>(regs.y); + case 0xf7: return op_read_idpy<&SMPcore::op_ld>(); + case 0xf8: return op_read_dp<&SMPcore::op_ld>(regs.x); + case 0xf9: return op_read_dpi<&SMPcore::op_ld>(regs.x, regs.y); + case 0xfa: return op_write_dp_dp<&SMPcore::op_st>(); + case 0xfb: return op_read_dpi<&SMPcore::op_ld>(regs.y, regs.x); + case 0xfc: return op_adjust<&SMPcore::op_inc>(regs.y); + case 0xfd: return op_transfer(regs.a, regs.y); + case 0xfe: return op_bne_ydec(); + case 0xff: return op_wait(); + } +} + +} diff --git a/libsnes/snes/smp/debugger/debugger.cpp b/libsnes/snes/smp/debugger/debugger.cpp new file mode 100755 index 0000000..9546c11 --- /dev/null +++ b/libsnes/snes/smp/debugger/debugger.cpp @@ -0,0 +1,75 @@ +#ifdef SMP_CPP + +void SMPDebugger::op_step() { + bool break_event = false; + + usage[regs.pc] |= UsageExec; + opcode_pc = regs.pc; + + opcode_edge = true; + debugger.breakpoint_test(Debugger::Breakpoint::Source::APURAM, Debugger::Breakpoint::Mode::Exec, regs.pc, 0x00); + if(step_event && step_event() == true) { + debugger.break_event = Debugger::BreakEvent::SMPStep; + scheduler.exit(Scheduler::ExitReason::DebuggerEvent); + } + opcode_edge = false; + + SMP::op_step(); + synchronize_cpu(); +} + +uint8 SMPDebugger::op_read(uint16 addr) { + uint8 data = SMP::op_read(addr); + usage[addr] |= UsageRead; + debugger.breakpoint_test(Debugger::Breakpoint::Source::APURAM, Debugger::Breakpoint::Mode::Read, addr, data); + return data; +} + +void SMPDebugger::op_write(uint16 addr, uint8 data) { + SMP::op_write(addr, data); + usage[addr] |= UsageWrite; + usage[addr] &= ~UsageExec; + debugger.breakpoint_test(Debugger::Breakpoint::Source::APURAM, Debugger::Breakpoint::Mode::Write, addr, data); +} + +SMPDebugger::SMPDebugger() { + usage = new uint8[1 << 16](); + opcode_pc = 0xffc0; + opcode_edge = false; +} + +SMPDebugger::~SMPDebugger() { + delete[] usage; +} + +bool SMPDebugger::property(unsigned id, string &name, string &value) { + unsigned n = 0; + + #define item(name_, value_) \ + if(id == n++) { \ + name = name_; \ + value = value_; \ + return true; \ + } + + //$00f0 + item("$00f0", ""); + item("Clock Speed", (unsigned)status.clock_speed); + item("Timers Enable", status.timers_enable); + item("RAM Disable", status.ram_disable); + item("RAM Writable", status.ram_writable); + item("Timers Disable", status.timers_disable); + + //$00f1 + item("$00f1", ""); + item("IPLROM Enable", status.iplrom_enable); + + //$00f2 + item("$00f2", ""); + item("DSP Address", string("0x", hex<2>(status.dsp_addr))); + + #undef item + return false; +} + +#endif diff --git a/libsnes/snes/smp/debugger/debugger.hpp b/libsnes/snes/smp/debugger/debugger.hpp new file mode 100755 index 0000000..d5d28e5 --- /dev/null +++ b/libsnes/snes/smp/debugger/debugger.hpp @@ -0,0 +1,22 @@ +class SMPDebugger : public SMP, public ChipDebugger { +public: + bool property(unsigned id, string &name, string &value); + + function step_event; + + enum Usage { + UsageRead = 0x80, + UsageWrite = 0x40, + UsageExec = 0x20, + }; + uint8 *usage; + uint16 opcode_pc; + bool opcode_edge; + + void op_step(); + uint8 op_read(uint16 addr); + void op_write(uint16 addr, uint8 data); + + SMPDebugger(); + ~SMPDebugger(); +}; diff --git a/libsnes/snes/smp/iplrom.cpp b/libsnes/snes/smp/iplrom.cpp new file mode 100755 index 0000000..a2ade89 --- /dev/null +++ b/libsnes/snes/smp/iplrom.cpp @@ -0,0 +1,44 @@ +#ifdef SMP_CPP + +//this is the IPLROM for the S-SMP coprocessor. +//the S-SMP does not allow writing to the IPLROM. +//all writes are instead mapped to the extended +//RAM region, accessible when $f1.d7 is clear. + +const uint8 SMP::iplrom[64] = { +/*ffc0*/ 0xcd, 0xef, //mov x,#$ef +/*ffc2*/ 0xbd, //mov sp,x +/*ffc3*/ 0xe8, 0x00, //mov a,#$00 +/*ffc5*/ 0xc6, //mov (x),a +/*ffc6*/ 0x1d, //dec x +/*ffc7*/ 0xd0, 0xfc, //bne $ffc5 +/*ffc9*/ 0x8f, 0xaa, 0xf4, //mov $f4,#$aa +/*ffcc*/ 0x8f, 0xbb, 0xf5, //mov $f5,#$bb +/*ffcf*/ 0x78, 0xcc, 0xf4, //cmp $f4,#$cc +/*ffd2*/ 0xd0, 0xfb, //bne $ffcf +/*ffd4*/ 0x2f, 0x19, //bra $ffef +/*ffd6*/ 0xeb, 0xf4, //mov y,$f4 +/*ffd8*/ 0xd0, 0xfc, //bne $ffd6 +/*ffda*/ 0x7e, 0xf4, //cmp y,$f4 +/*ffdc*/ 0xd0, 0x0b, //bne $ffe9 +/*ffde*/ 0xe4, 0xf5, //mov a,$f5 +/*ffe0*/ 0xcb, 0xf4, //mov $f4,y +/*ffe2*/ 0xd7, 0x00, //mov ($00)+y,a +/*ffe4*/ 0xfc, //inc y +/*ffe5*/ 0xd0, 0xf3, //bne $ffda +/*ffe7*/ 0xab, 0x01, //inc $01 +/*ffe9*/ 0x10, 0xef, //bpl $ffda +/*ffeb*/ 0x7e, 0xf4, //cmp y,$f4 +/*ffed*/ 0x10, 0xeb, //bpl $ffda +/*ffef*/ 0xba, 0xf6, //movw ya,$f6 +/*fff1*/ 0xda, 0x00, //movw $00,ya +/*fff3*/ 0xba, 0xf4, //movw ya,$f4 +/*fff5*/ 0xc4, 0xf4, //mov $f4,a +/*fff7*/ 0xdd, //mov a,y +/*fff8*/ 0x5d, //mov x,a +/*fff9*/ 0xd0, 0xdb, //bne $ffd6 +/*fffb*/ 0x1f, 0x00, 0x00, //jmp ($0000+x) +/*fffe*/ 0xc0, 0xff //reset vector location ($ffc0) +}; + +#endif diff --git a/libsnes/snes/smp/memory/memory.cpp b/libsnes/snes/smp/memory/memory.cpp new file mode 100755 index 0000000..391324c --- /dev/null +++ b/libsnes/snes/smp/memory/memory.cpp @@ -0,0 +1,197 @@ +#ifdef SMP_CPP + +alwaysinline uint8 SMP::ram_read(uint16 addr) { + if(addr >= 0xffc0 && status.iplrom_enable) return iplrom[addr & 0x3f]; + if(status.ram_disable) return 0x5a; //0xff on mini-SNES + return apuram[addr]; +} + +alwaysinline void SMP::ram_write(uint16 addr, uint8 data) { + //writes to $ffc0-$ffff always go to apuram, even if the iplrom is enabled + if(status.ram_writable && !status.ram_disable) apuram[addr] = data; +} + +uint8 SMP::port_read(uint2 port) const { + return apuram[0xf4 + port]; +} + +void SMP::port_write(uint2 port, uint8 data) { + apuram[0xf4 + port] = data; +} + +alwaysinline uint8 SMP::op_busread(uint16 addr) { + unsigned result; + + switch(addr) { + case 0xf0: //TEST -- write-only register + return 0x00; + + case 0xf1: //CONTROL -- write-only register + return 0x00; + + case 0xf2: //DSPADDR + return status.dsp_addr; + + case 0xf3: //DSPDATA + //0x80-0xff are read-only mirrors of 0x00-0x7f + return dsp.read(status.dsp_addr & 0x7f); + + case 0xf4: //CPUIO0 + case 0xf5: //CPUIO1 + case 0xf6: //CPUIO2 + case 0xf7: //CPUIO3 + synchronize_cpu(); + return cpu.port_read(addr); + + case 0xf8: //RAM0 + return status.ram00f8; + + case 0xf9: //RAM1 + return status.ram00f9; + + case 0xfa: //T0TARGET + case 0xfb: //T1TARGET + case 0xfc: //T2TARGET -- write-only registers + return 0x00; + + case 0xfd: //T0OUT -- 4-bit counter value + result = timer0.stage3_ticks; + timer0.stage3_ticks = 0; + return result; + + case 0xfe: //T1OUT -- 4-bit counter value + result = timer1.stage3_ticks; + timer1.stage3_ticks = 0; + return result; + + case 0xff: //T2OUT -- 4-bit counter value + result = timer2.stage3_ticks; + timer2.stage3_ticks = 0; + return result; + } + + return ram_read(addr); +} + +alwaysinline void SMP::op_buswrite(uint16 addr, uint8 data) { + switch(addr) { + case 0xf0: //TEST + if(regs.p.p) break; //writes only valid when P flag is clear + + status.clock_speed = (data >> 6) & 3; + status.timer_speed = (data >> 4) & 3; + status.timers_enable = data & 0x08; + status.ram_disable = data & 0x04; + status.ram_writable = data & 0x02; + status.timers_disable = data & 0x01; + + status.timer_step = (1 << status.clock_speed) + (2 << status.timer_speed); + + timer0.synchronize_stage1(); + timer1.synchronize_stage1(); + timer2.synchronize_stage1(); + break; + + case 0xf1: //CONTROL + status.iplrom_enable = data & 0x80; + + if(data & 0x30) { + //one-time clearing of APU port read registers, + //emulated by simulating CPU writes of 0x00 + synchronize_cpu(); + if(data & 0x20) { + cpu.port_write(2, 0x00); + cpu.port_write(3, 0x00); + } + if(data & 0x10) { + cpu.port_write(0, 0x00); + cpu.port_write(1, 0x00); + } + } + + //0->1 transistion resets timers + if(timer2.enable == false && (data & 0x04)) { + timer2.stage2_ticks = 0; + timer2.stage3_ticks = 0; + } + timer2.enable = data & 0x04; + + if(timer1.enable == false && (data & 0x02)) { + timer1.stage2_ticks = 0; + timer1.stage3_ticks = 0; + } + timer1.enable = data & 0x02; + + if(timer0.enable == false && (data & 0x01)) { + timer0.stage2_ticks = 0; + timer0.stage3_ticks = 0; + } + timer0.enable = data & 0x01; + break; + + case 0xf2: //DSPADDR + status.dsp_addr = data; + break; + + case 0xf3: //DSPDATA + if(status.dsp_addr & 0x80) break; //0x80-0xff are read-only mirrors of 0x00-0x7f + dsp.write(status.dsp_addr & 0x7f, data); + break; + + case 0xf4: //CPUIO0 + case 0xf5: //CPUIO1 + case 0xf6: //CPUIO2 + case 0xf7: //CPUIO3 + synchronize_cpu(); + port_write(addr, data); + break; + + case 0xf8: //RAM0 + status.ram00f8 = data; + break; + + case 0xf9: //RAM1 + status.ram00f9 = data; + break; + + case 0xfa: //T0TARGET + timer0.target = data; + break; + + case 0xfb: //T1TARGET + timer1.target = data; + break; + + case 0xfc: //T2TARGET + timer2.target = data; + break; + + case 0xfd: //T0OUT + case 0xfe: //T1OUT + case 0xff: //T2OUT -- read-only registers + break; + } + + ram_write(addr, data); //all writes, even to MMIO registers, appear on bus +} + +void SMP::op_io() { + add_clocks(24); + cycle_edge(); +} + +uint8 SMP::op_read(uint16 addr) { + add_clocks(12); + uint8 r = op_busread(addr); + add_clocks(12); + cycle_edge(); + return r; +} + +void SMP::op_write(uint16 addr, uint8 data) { + add_clocks(24); + op_buswrite(addr, data); + cycle_edge(); +} + +#endif diff --git a/libsnes/snes/smp/memory/memory.hpp b/libsnes/snes/smp/memory/memory.hpp new file mode 100755 index 0000000..1a07445 --- /dev/null +++ b/libsnes/snes/smp/memory/memory.hpp @@ -0,0 +1,9 @@ +uint8 ram_read(uint16 addr); +void ram_write(uint16 addr, uint8 data); + +uint8 op_busread(uint16 addr); +void op_buswrite(uint16 addr, uint8 data); + +void op_io(); +debugvirtual uint8 op_read(uint16 addr); +debugvirtual void op_write(uint16 addr, uint8 data); diff --git a/libsnes/snes/smp/serialization.cpp b/libsnes/snes/smp/serialization.cpp new file mode 100755 index 0000000..c646ad6 --- /dev/null +++ b/libsnes/snes/smp/serialization.cpp @@ -0,0 +1,52 @@ +#ifdef SMP_CPP + +void SMP::serialize(serializer &s) { + Processor::serialize(s); + SMPcore::core_serialize(s); + + s.array(apuram); + + s.integer(status.clock_counter); + s.integer(status.dsp_counter); + s.integer(status.timer_step); + + s.integer(status.clock_speed); + s.integer(status.timer_speed); + s.integer(status.timers_enable); + s.integer(status.ram_disable); + s.integer(status.ram_writable); + s.integer(status.timers_disable); + + s.integer(status.iplrom_enable); + + s.integer(status.dsp_addr); + + s.integer(status.ram00f8); + s.integer(status.ram00f9); + + s.integer(timer0.stage0_ticks); + s.integer(timer0.stage1_ticks); + s.integer(timer0.stage2_ticks); + s.integer(timer0.stage3_ticks); + s.integer(timer0.current_line); + s.integer(timer0.enable); + s.integer(timer0.target); + + s.integer(timer1.stage0_ticks); + s.integer(timer1.stage1_ticks); + s.integer(timer1.stage2_ticks); + s.integer(timer1.stage3_ticks); + s.integer(timer1.current_line); + s.integer(timer1.enable); + s.integer(timer1.target); + + s.integer(timer2.stage0_ticks); + s.integer(timer2.stage1_ticks); + s.integer(timer2.stage2_ticks); + s.integer(timer2.stage3_ticks); + s.integer(timer2.current_line); + s.integer(timer2.enable); + s.integer(timer2.target); +} + +#endif diff --git a/libsnes/snes/smp/smp.cpp b/libsnes/snes/smp/smp.cpp new file mode 100755 index 0000000..0abb1aa --- /dev/null +++ b/libsnes/snes/smp/smp.cpp @@ -0,0 +1,127 @@ +#include + +#define SMP_CPP +namespace SNES { + +#if defined(DEBUGGER) + #include "debugger/debugger.cpp" + SMPDebugger smp; +#else + SMP smp; +#endif + +#include "serialization.cpp" +#include "iplrom.cpp" +#include "memory/memory.cpp" +#include "timing/timing.cpp" + +void SMP::step(unsigned clocks) { + clock += clocks * (uint64)cpu.frequency; + dsp.clock -= clocks; +} + +void SMP::synchronize_cpu() { + if(CPU::Threaded == true) { + if(clock >= 0 && scheduler.sync.i != Scheduler::SynchronizeMode::All) co_switch(cpu.thread); + } else { + while(clock >= 0) cpu.enter(); + } +} + +void SMP::synchronize_dsp() { + if(DSP::Threaded == true) { + if(dsp.clock < 0 && scheduler.sync.i != Scheduler::SynchronizeMode::All) co_switch(dsp.thread); + } else { + while(dsp.clock < 0) dsp.enter(); + } +} + +void SMP::Enter() { smp.enter(); } + +void SMP::enter() { + while(true) { + if(scheduler.sync.i == Scheduler::SynchronizeMode::All) { + scheduler.exit(Scheduler::ExitReason::SynchronizeEvent); + } + + op_step(); + } +} + +void SMP::power() { + //targets not initialized/changed upon reset + timer0.target = 0; + timer1.target = 0; + timer2.target = 0; +} + +void SMP::reset() { + create(Enter, system.apu_frequency); + + regs.pc = 0xffc0; + regs.a = 0x00; + regs.x = 0x00; + regs.y = 0x00; + regs.s = 0xef; + regs.p = 0x02; + + foreach(n, apuram) n = random(0x00); + apuram[0x00f4] = 0x00; + apuram[0x00f5] = 0x00; + apuram[0x00f6] = 0x00; + apuram[0x00f7] = 0x00; + + status.clock_counter = 0; + status.dsp_counter = 0; + status.timer_step = 3; + + //$00f0 + status.clock_speed = 0; + status.timer_speed = 0; + status.timers_enable = true; + status.ram_disable = false; + status.ram_writable = true; + status.timers_disable = false; + + //$00f1 + status.iplrom_enable = true; + + //$00f2 + status.dsp_addr = 0x00; + + //$00f8,$00f9 + status.ram00f8 = 0x00; + status.ram00f9 = 0x00; + + timer0.stage0_ticks = 0; + timer1.stage0_ticks = 0; + timer2.stage0_ticks = 0; + + timer0.stage1_ticks = 0; + timer1.stage1_ticks = 0; + timer2.stage1_ticks = 0; + + timer0.stage2_ticks = 0; + timer1.stage2_ticks = 0; + timer2.stage2_ticks = 0; + + timer0.stage3_ticks = 0; + timer1.stage3_ticks = 0; + timer2.stage3_ticks = 0; + + timer0.current_line = 0; + timer1.current_line = 0; + timer2.current_line = 0; + + timer0.enable = false; + timer1.enable = false; + timer2.enable = false; +} + +SMP::SMP() { +} + +SMP::~SMP() { +} + +} diff --git a/libsnes/snes/smp/smp.hpp b/libsnes/snes/smp/smp.hpp new file mode 100755 index 0000000..35c1410 --- /dev/null +++ b/libsnes/snes/smp/smp.hpp @@ -0,0 +1,61 @@ +struct SMP : public Processor, public SMPcore { + static const uint8 iplrom[64]; + uint8 apuram[64 * 1024]; + + enum { Threaded = true }; + alwaysinline void step(unsigned clocks); + alwaysinline void synchronize_cpu(); + alwaysinline void synchronize_dsp(); + + uint8 port_read(uint2 port) const; + void port_write(uint2 port, uint8 data); + + void enter(); + void power(); + void reset(); + + void serialize(serializer&); + SMP(); + ~SMP(); + +private: + #include "memory/memory.hpp" + #include "timing/timing.hpp" + + struct { + //timing + unsigned clock_counter; + unsigned dsp_counter; + unsigned timer_step; + + //$00f0 + uint8 clock_speed; + uint8 timer_speed; + bool timers_enable; + bool ram_disable; + bool ram_writable; + bool timers_disable; + + //$00f1 + bool iplrom_enable; + + //$00f2 + uint8 dsp_addr; + + //$00f8,$00f9 + uint8 ram00f8; + uint8 ram00f9; + } status; + + static void Enter(); + + friend class SMPcore; + friend class SMPDebugger; +}; + +#if defined(DEBUGGER) + #include "debugger/debugger.hpp" + extern SMPDebugger smp; +#else + extern SMP smp; +#endif diff --git a/libsnes/snes/smp/timing/timing.cpp b/libsnes/snes/smp/timing/timing.cpp new file mode 100755 index 0000000..40374d1 --- /dev/null +++ b/libsnes/snes/smp/timing/timing.cpp @@ -0,0 +1,58 @@ +#ifdef SMP_CPP + +void SMP::add_clocks(unsigned clocks) { + step(clocks); + synchronize_dsp(); + + //forcefully sync S-SMP to S-CPU in case chips are not communicating + //sync if S-SMP is more than 24 samples ahead of S-CPU + if(clock > +(768 * 24 * (int64)24000000)) synchronize_cpu(); +} + +void SMP::cycle_edge() { + timer0.tick(); + timer1.tick(); + timer2.tick(); + + //TEST register S-SMP speed control + //24 clocks have already been added for this cycle at this point + switch(status.clock_speed) { + case 0: break; //100% speed + case 1: add_clocks(24); break; // 50% speed + case 2: while(true) add_clocks(24); // 0% speed -- locks S-SMP + case 3: add_clocks(24 * 9); break; // 10% speed + } +} + +template +void SMP::Timer::tick() { + //stage 0 increment + stage0_ticks += smp.status.timer_step; + if(stage0_ticks < timer_frequency) return; + stage0_ticks -= timer_frequency; + + //stage 1 increment + stage1_ticks ^= 1; + synchronize_stage1(); +} + +template +void SMP::Timer::synchronize_stage1() { + bool new_line = stage1_ticks; + if(smp.status.timers_enable == false) new_line = false; + if(smp.status.timers_disable == true) new_line = false; + + bool old_line = current_line; + current_line = new_line; + if(old_line != 1 || new_line != 0) return; //only pulse on 1->0 transition + + //stage 2 increment + if(enable == false) return; + if(++stage2_ticks != target) return; + + //stage 3 increment + stage2_ticks = 0; + stage3_ticks++; +} + +#endif diff --git a/libsnes/snes/smp/timing/timing.hpp b/libsnes/snes/smp/timing/timing.hpp new file mode 100755 index 0000000..2c28245 --- /dev/null +++ b/libsnes/snes/smp/timing/timing.hpp @@ -0,0 +1,21 @@ +template +class Timer { +public: + uint8 stage0_ticks; + uint8 stage1_ticks; + uint8 stage2_ticks; + uint4 stage3_ticks; + bool current_line; + bool enable; + uint8 target; + + void tick(); + void synchronize_stage1(); +}; + +Timer<192> timer0; +Timer<192> timer1; +Timer< 24> timer2; + +alwaysinline void add_clocks(unsigned clocks); +alwaysinline void cycle_edge(); diff --git a/libsnes/snes/snes.hpp b/libsnes/snes/snes.hpp new file mode 100644 index 0000000..a478955 --- /dev/null +++ b/libsnes/snes/snes.hpp @@ -0,0 +1,157 @@ +#ifndef SNES_HPP +#define SNES_HPP + +namespace SNES { + namespace Info { + static const char Name[] = "bsnes"; + static const unsigned SerializerVersion = 23; + } +} + +/* + bsnes - SNES emulator + author: byuu + license: GPLv3 + project started: 2004-10-14 +*/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +using namespace nall; + +#include + +#ifdef DEBUGGER + #define debugvirtual virtual +#else + #define debugvirtual +#endif + +namespace SNES { + typedef int8_t int8; + typedef int16_t int16; + typedef int32_t int32; + typedef int64_t int64; + + typedef int_t<24> int24; + + typedef uint8_t uint8; + typedef uint16_t uint16; + typedef uint32_t uint32; + typedef uint64_t uint64; + + typedef uint_t< 1> uint1; + typedef uint_t< 2> uint2; + typedef uint_t< 3> uint3; + typedef uint_t< 4> uint4; + typedef uint_t< 5> uint5; + typedef uint_t< 6> uint6; + typedef uint_t< 7> uint7; + + typedef uint_t< 9> uint9; + typedef uint_t<10> uint10; + typedef uint_t<11> uint11; + typedef uint_t<12> uint12; + typedef uint_t<13> uint13; + typedef uint_t<14> uint14; + typedef uint_t<15> uint15; + + typedef uint_t<17> uint17; + typedef uint_t<18> uint18; + typedef uint_t<19> uint19; + typedef uint_t<20> uint20; + typedef uint_t<21> uint21; + typedef uint_t<22> uint22; + typedef uint_t<23> uint23; + typedef uint_t<24> uint24; + typedef uint_t<25> uint25; + typedef uint_t<26> uint26; + typedef uint_t<27> uint27; + typedef uint_t<28> uint28; + typedef uint_t<29> uint29; + typedef uint_t<30> uint30; + typedef uint_t<31> uint31; + + typedef varuint_t varuint; + + template + alwaysinline bool within(unsigned addr) { + static const unsigned lo = (banklo << 16) | addrlo; + static const unsigned hi = (bankhi << 16) | addrhi; + static const unsigned mask = ~(hi ^ lo); + return (addr & mask) == lo; + } + + struct Processor { + cothread_t thread; + unsigned frequency; + int64 clock; + + inline void create(void (*entrypoint)(), unsigned frequency) { + if(thread) co_delete(thread); + thread = co_create(65536 * sizeof(void*), entrypoint); + this->frequency = frequency; + clock = 0; + } + + inline void serialize(serializer &s) { + s.integer(frequency); + s.integer(clock); + } + + inline Processor() : thread(0) {} + inline ~Processor() { + if (thread) co_delete(thread); + } + }; + + struct ChipDebugger { + virtual bool property(unsigned id, string &name, string &value) = 0; + }; + + #include + #include + #include + #include + + #if defined(PROFILE_ACCURACY) + #include "profile-accuracy.hpp" + #elif defined(PROFILE_COMPATIBILITY) + #include "profile-compatibility.hpp" + #elif defined(PROFILE_PERFORMANCE) + #include "profile-performance.hpp" + #endif + + #include + #include + #include + #include + #include + #include + + #include + #include +} + +#undef debugvirtual + +#endif diff --git a/libsnes/snes/system/audio.cpp b/libsnes/snes/system/audio.cpp new file mode 100644 index 0000000..62380b2 --- /dev/null +++ b/libsnes/snes/system/audio.cpp @@ -0,0 +1,69 @@ +#ifdef SYSTEM_CPP + +Audio audio; + +void Audio::coprocessor_enable(bool state) { + coprocessor = state; + dspaudio.clear(); + + dsp_rdoffset = cop_rdoffset = 0; + dsp_wroffset = cop_wroffset = 0; + dsp_length = cop_length = 0; +} + +void Audio::coprocessor_frequency(double input_frequency) { + dspaudio.setFrequency(input_frequency); + dspaudio.setResampler(nall::DSP::ResampleEngine::Sinc); + dspaudio.setResamplerFrequency(system.apu_frequency / 768.0); +} + +void Audio::sample(int16 lsample, int16 rsample) { + if(coprocessor == false) return interface->audioSample(lsample, rsample); + + dsp_buffer[dsp_wroffset] = ((uint16)lsample << 0) + ((uint16)rsample << 16); + dsp_wroffset = (dsp_wroffset + 1) & buffer_mask; + dsp_length = (dsp_length + 1) & buffer_mask; + flush(); +} + +void Audio::coprocessor_sample(int16 lsample, int16 rsample) { + signed samples[] = { lsample, rsample }; + dspaudio.sample(samples); + while(dspaudio.pending()) { + dspaudio.read(samples); + + cop_buffer[cop_wroffset] = ((uint16)samples[0] << 0) + ((uint16)samples[1] << 16); + cop_wroffset = (cop_wroffset + 1) & buffer_mask; + cop_length = (cop_length + 1) & buffer_mask; + flush(); + } +} + +void Audio::init() { +} + +void Audio::flush() { + while(dsp_length > 0 && cop_length > 0) { + uint32 dsp_sample = dsp_buffer[dsp_rdoffset]; + uint32 cop_sample = cop_buffer[cop_rdoffset]; + + dsp_rdoffset = (dsp_rdoffset + 1) & buffer_mask; + cop_rdoffset = (cop_rdoffset + 1) & buffer_mask; + + dsp_length--; + cop_length--; + + signed dsp_left = (int16)(dsp_sample >> 0); + signed dsp_right = (int16)(dsp_sample >> 16); + + signed cop_left = (int16)(cop_sample >> 0); + signed cop_right = (int16)(cop_sample >> 16); + + interface->audioSample( + sclamp<16>((dsp_left + cop_left ) / 2), + sclamp<16>((dsp_right + cop_right) / 2) + ); + } +} + +#endif diff --git a/libsnes/snes/system/audio.hpp b/libsnes/snes/system/audio.hpp new file mode 100644 index 0000000..85807a1 --- /dev/null +++ b/libsnes/snes/system/audio.hpp @@ -0,0 +1,20 @@ +struct Audio { + void coprocessor_enable(bool state); + void coprocessor_frequency(double frequency); + void sample(int16 lsample, int16 rsample); + void coprocessor_sample(int16 lsample, int16 rsample); + void init(); + +private: + nall::DSP dspaudio; + bool coprocessor; + enum { buffer_size = 256, buffer_mask = buffer_size - 1 }; + uint32 dsp_buffer[buffer_size], cop_buffer[buffer_size]; + unsigned dsp_rdoffset, cop_rdoffset; + unsigned dsp_wroffset, cop_wroffset; + unsigned dsp_length, cop_length; + + void flush(); +}; + +extern Audio audio; diff --git a/libsnes/snes/system/input.cpp b/libsnes/snes/system/input.cpp new file mode 100644 index 0000000..96a01d8 --- /dev/null +++ b/libsnes/snes/system/input.cpp @@ -0,0 +1,39 @@ +#ifdef SYSTEM_CPP + +Input input; + +void Input::connect(bool port, Input::Device::e id) { + Controller *&controller = (port == Controller::Port1 ? port1 : port2); + if(controller) { + delete controller; + controller = 0; + } + + switch(id) { default: + case Device::None: controller = new Controller(port); break; + case Device::Joypad: controller = new Gamepad(port); break; + case Device::Multitap: controller = new Multitap(port); break; + case Device::Mouse: controller = new Mouse(port); break; + case Device::SuperScope: controller = new SuperScope(port); break; + case Device::Justifier: controller = new Justifier(port, false); break; + case Device::Justifiers: controller = new Justifier(port, true); break; + case Device::Serial: controller = new Serial(port); break; + } + + switch(port) { + case Controller::Port1: config.controller_port1.i = id; break; + case Controller::Port2: config.controller_port2.i = id; break; + } +} + +Input::Input() : port1(0), port2(0) { + connect(Controller::Port1, Input::Device::Joypad); + connect(Controller::Port2, Input::Device::Joypad); +} + +Input::~Input() { + if(port1) delete port1; + if(port2) delete port2; +} + +#endif diff --git a/libsnes/snes/system/input.hpp b/libsnes/snes/system/input.hpp new file mode 100644 index 0000000..cc286d7 --- /dev/null +++ b/libsnes/snes/system/input.hpp @@ -0,0 +1,49 @@ +struct Input { + struct Device { + enum e { + None, + Joypad, + Multitap, + Mouse, + SuperScope, + Justifier, + Justifiers, + Serial, + } i; + }; + + struct JoypadID { + enum { + B = 0, Y = 1, Select = 2, Start = 3, + Up = 4, Down = 5, Left = 6, Right = 7, + A = 8, X = 9, L = 10, R = 11, + } i; + }; + + struct MouseID { + enum e { + X = 0, Y = 1, Left = 2, Right = 3, + } i; + }; + + struct SuperScopeID { + enum e { + X = 0, Y = 1, Trigger = 2, Cursor = 3, Turbo = 4, Pause = 5, + } i; + }; + + struct JustifierID { + enum e { + X = 0, Y = 1, Trigger = 2, Start = 3, + } i; + }; + + Controller *port1; + Controller *port2; + + void connect(bool port, Input::Device::e id); + Input(); + ~Input(); +}; + +extern Input input; diff --git a/libsnes/snes/system/serialization.cpp b/libsnes/snes/system/serialization.cpp new file mode 100755 index 0000000..158197d --- /dev/null +++ b/libsnes/snes/system/serialization.cpp @@ -0,0 +1,96 @@ +#ifdef SYSTEM_CPP + +serializer System::serialize() { + serializer s(serialize_size); + + unsigned signature = 0x31545342, version = Info::SerializerVersion, crc32 = cartridge.crc32(); + char description[512], profile[16]; + memset(&description, 0, sizeof description); + memset(&profile, 0, sizeof profile); + strlcpy(profile, Info::Profile, sizeof profile); + + s.integer(signature); + s.integer(version); + s.integer(crc32); + s.array(description); + s.array(profile); + + serialize_all(s); + return s; +} + +bool System::unserialize(serializer &s) { + unsigned signature, version, crc32; + char description[512], profile[16]; + + s.integer(signature); + s.integer(version); + s.integer(crc32); + s.array(description); + s.array(profile); + + if(signature != 0x31545342) return false; + if(version != Info::SerializerVersion) return false; +//if(crc32 != cartridge.crc32()) return false; + if(strcmp(profile, Info::Profile)) return false; + + power(); + serialize_all(s); + return true; +} + +//======== +//internal +//======== + +void System::serialize(serializer &s) { + unsigned region_ = (unsigned)region.i; + unsigned expansion_ = (unsigned)expansion.i; + s.integer(region_); + s.integer(expansion_); + region.i = (Region::e)region_; + expansion.i = (ExpansionPortDevice::e)expansion_; +} + +void System::serialize_all(serializer &s) { + cartridge.serialize(s); + system.serialize(s); + random.serialize(s); + cpu.serialize(s); + smp.serialize(s); + ppu.serialize(s); + dsp.serialize(s); + + if(cartridge.mode.i == Cartridge::Mode::SufamiTurbo) sufamiturbo.serialize(s); + if(cartridge.mode.i == Cartridge::Mode::SuperGameBoy) icd2.serialize(s); + if(cartridge.has_superfx()) superfx.serialize(s); + if(cartridge.has_sa1()) sa1.serialize(s); + if(cartridge.has_necdsp()) necdsp.serialize(s); + if(cartridge.has_hitachidsp()) hitachidsp.serialize(s); + if(cartridge.has_srtc()) srtc.serialize(s); + if(cartridge.has_sdd1()) sdd1.serialize(s); + if(cartridge.has_spc7110()) spc7110.serialize(s); + if(cartridge.has_obc1()) obc1.serialize(s); + if(cartridge.has_msu1()) msu1.serialize(s); +} + +//perform dry-run state save: +//determines exactly how many bytes are needed to save state for this cartridge, +//as amount varies per game (eg different RAM sizes, special chips, etc.) +void System::serialize_init() { + serializer s; + + unsigned signature = 0, version = 0, crc32 = 0; + char profile[16], description[512]; + + s.integer(signature); + s.integer(version); + s.integer(crc32); + s.array(profile); + s.array(description); + + serialize_all(s); + serialize_size = s.size(); +} + +#endif diff --git a/libsnes/snes/system/system.cpp b/libsnes/snes/system/system.cpp new file mode 100755 index 0000000..f00a23c --- /dev/null +++ b/libsnes/snes/system/system.cpp @@ -0,0 +1,237 @@ +#include + +#define SYSTEM_CPP +namespace SNES { + +System system; + +#include +#include +#include +#include + +#include "video.cpp" +#include "audio.cpp" +#include "input.cpp" +#include "serialization.cpp" + +void System::run() { + scheduler.sync.i = Scheduler::SynchronizeMode::None; + + scheduler.enter(); + if(scheduler.exit_reason.i == Scheduler::ExitReason::FrameEvent) { + video.update(); + } +} + +void System::runtosave() { + if(CPU::Threaded == true) { + scheduler.sync.i = Scheduler::SynchronizeMode::CPU; + runthreadtosave(); + } + + if(SMP::Threaded == true) { + scheduler.thread = smp.thread; + runthreadtosave(); + } + + if(PPU::Threaded == true) { + scheduler.thread = ppu.thread; + runthreadtosave(); + } + + if(DSP::Threaded == true) { + scheduler.thread = dsp.thread; + runthreadtosave(); + } + + for(unsigned i = 0; i < cpu.coprocessors.size(); i++) { + Processor &chip = *cpu.coprocessors[i]; + scheduler.thread = chip.thread; + runthreadtosave(); + } +} + +void System::runthreadtosave() { + while(true) { + scheduler.enter(); + if(scheduler.exit_reason.i == Scheduler::ExitReason::SynchronizeEvent) break; + if(scheduler.exit_reason.i == Scheduler::ExitReason::FrameEvent) { + video.update(); + } + } +} + +void System::init() { + assert(interface != 0); + + icd2.init(); + nss.init(); + superfx.init(); + sa1.init(); + necdsp.init(); + hitachidsp.init(); + bsxsatellaview.init(); + bsxcartridge.init(); + bsxflash.init(); + srtc.init(); + sdd1.init(); + spc7110.init(); + obc1.init(); + st0018.init(); + msu1.init(); + link.init(); + + video.init(); + audio.init(); + + input.connect(0, config.controller_port1.i); + input.connect(1, config.controller_port2.i); +} + +void System::term() { +} + +void System::load() { + audio.coprocessor_enable(false); + + bus.map_reset(); + bus.map_xml(); + + cpu.enable(); + ppu.enable(); + + if(expansion.i == ExpansionPortDevice::BSX) bsxsatellaview.load(); + if(cartridge.mode.i == Cartridge::Mode::Bsx) bsxcartridge.load(); + if(cartridge.mode.i == Cartridge::Mode::SufamiTurbo) sufamiturbo.load(); + if(cartridge.mode.i == Cartridge::Mode::SuperGameBoy) icd2.load(); + + if(cartridge.has_bsx_slot()) bsxflash.load(); + if(cartridge.has_nss_dip()) nss.load(); + if(cartridge.has_superfx()) superfx.load(); + if(cartridge.has_sa1()) sa1.load(); + if(cartridge.has_necdsp()) necdsp.load(); + if(cartridge.has_hitachidsp()) hitachidsp.load(); + if(cartridge.has_srtc()) srtc.load(); + if(cartridge.has_sdd1()) sdd1.load(); + if(cartridge.has_spc7110()) spc7110.load(); + if(cartridge.has_obc1()) obc1.load(); + if(cartridge.has_st0018()) st0018.load(); + if(cartridge.has_msu1()) msu1.load(); + if(cartridge.has_link()) link.load(); + + serialize_init(); + cheat.init(); +} + +void System::unload() { + if(expansion.i == ExpansionPortDevice::BSX) bsxsatellaview.unload(); + if(cartridge.mode.i == Cartridge::Mode::Bsx) bsxcartridge.unload(); + if(cartridge.mode.i == Cartridge::Mode::SufamiTurbo) sufamiturbo.unload(); + if(cartridge.mode.i == Cartridge::Mode::SuperGameBoy) icd2.unload(); + + if(cartridge.has_bsx_slot()) bsxflash.unload(); + if(cartridge.has_nss_dip()) nss.unload(); + if(cartridge.has_superfx()) superfx.unload(); + if(cartridge.has_sa1()) sa1.unload(); + if(cartridge.has_necdsp()) necdsp.unload(); + if(cartridge.has_hitachidsp()) hitachidsp.unload(); + if(cartridge.has_srtc()) srtc.unload(); + if(cartridge.has_sdd1()) sdd1.unload(); + if(cartridge.has_spc7110()) spc7110.unload(); + if(cartridge.has_obc1()) obc1.unload(); + if(cartridge.has_st0018()) st0018.unload(); + if(cartridge.has_msu1()) msu1.unload(); + if(cartridge.has_link()) link.unload(); +} + +void System::power() { + random.seed((unsigned)interface->randomSeed()); + + region = config.region; + expansion = config.expansion_port; + if(region.i == Region::Autodetect) { + region.i = (cartridge.region.i == Cartridge::Region::NTSC ? Region::NTSC : Region::PAL); + } + + cpu_frequency = region.i == Region::NTSC ? config.cpu.ntsc_frequency : config.cpu.pal_frequency; + apu_frequency = region.i == Region::NTSC ? config.smp.ntsc_frequency : config.smp.pal_frequency; + + cpu.power(); + smp.power(); + dsp.power(); + ppu.power(); + + if(expansion.i == ExpansionPortDevice::BSX) bsxsatellaview.power(); + if(cartridge.mode.i == Cartridge::Mode::Bsx) bsxcartridge.power(); + if(cartridge.mode.i == Cartridge::Mode::SuperGameBoy) icd2.power(); + + if(cartridge.has_bsx_slot()) bsxflash.power(); + if(cartridge.has_nss_dip()) nss.power(); + if(cartridge.has_superfx()) superfx.power(); + if(cartridge.has_sa1()) sa1.power(); + if(cartridge.has_necdsp()) necdsp.power(); + if(cartridge.has_hitachidsp()) hitachidsp.power(); + if(cartridge.has_srtc()) srtc.power(); + if(cartridge.has_sdd1()) sdd1.power(); + if(cartridge.has_spc7110()) spc7110.power(); + if(cartridge.has_obc1()) obc1.power(); + if(cartridge.has_st0018()) st0018.power(); + if(cartridge.has_msu1()) msu1.power(); + if(cartridge.has_link()) link.power(); + + reset(); +} + +void System::reset() { + cpu.reset(); + smp.reset(); + dsp.reset(); + ppu.reset(); + + if(expansion.i == ExpansionPortDevice::BSX) bsxsatellaview.reset(); + + if(cartridge.mode.i == Cartridge::Mode::Bsx) bsxcartridge.reset(); + if(cartridge.mode.i == Cartridge::Mode::SuperGameBoy) icd2.reset(); + + if(cartridge.has_bsx_slot()) bsxflash.reset(); + if(cartridge.has_nss_dip()) nss.reset(); + if(cartridge.has_superfx()) superfx.reset(); + if(cartridge.has_sa1()) sa1.reset(); + if(cartridge.has_necdsp()) necdsp.reset(); + if(cartridge.has_hitachidsp()) hitachidsp.reset(); + if(cartridge.has_srtc()) srtc.reset(); + if(cartridge.has_sdd1()) sdd1.reset(); + if(cartridge.has_spc7110()) spc7110.reset(); + if(cartridge.has_obc1()) obc1.reset(); + if(cartridge.has_st0018()) st0018.reset(); + if(cartridge.has_msu1()) msu1.reset(); + if(cartridge.has_link()) link.reset(); + + if(cartridge.mode.i == Cartridge::Mode::SuperGameBoy) cpu.coprocessors.append(&icd2); + if(cartridge.has_superfx()) cpu.coprocessors.append(&superfx); + if(cartridge.has_sa1()) cpu.coprocessors.append(&sa1); + if(cartridge.has_necdsp()) cpu.coprocessors.append(&necdsp); + if(cartridge.has_hitachidsp()) cpu.coprocessors.append(&hitachidsp); + if(cartridge.has_msu1()) cpu.coprocessors.append(&msu1); + if(cartridge.has_link()) cpu.coprocessors.append(&link); + + scheduler.init(); + input.connect(0, config.controller_port1.i); + input.connect(1, config.controller_port2.i); +} + +void System::scanline() { + video.scanline(); + if(cpu.vcounter() == 241) scheduler.exit(Scheduler::ExitReason::FrameEvent); +} + +void System::frame() { +} + +System::System() { + region.i = Region::Autodetect; + expansion.i = ExpansionPortDevice::BSX; +} + +} diff --git a/libsnes/snes/system/system.hpp b/libsnes/snes/system/system.hpp new file mode 100755 index 0000000..96d72f5 --- /dev/null +++ b/libsnes/snes/system/system.hpp @@ -0,0 +1,66 @@ +struct Interface; + +struct System : property { + struct Region { + enum e { + NTSC = 0, + PAL = 1, + Autodetect = 2 + } i; + }; + + struct ExpansionPortDevice { + enum e { + None = 0, + BSX = 1 + } i; + }; + + void run(); + void runtosave(); + + void init(); + void term(); + void load(); + void unload(); + void power(); + void reset(); + + void frame(); + void scanline(); + + //return *active* system information (settings are cached upon power-on) + Region region; + ExpansionPortDevice expansion; + unsigned cpu_frequency; + unsigned apu_frequency; + unsigned serialize_size; + + serializer serialize(); + bool unserialize(serializer&); + + System(); + +private: + void runthreadtosave(); + + void serialize(serializer&); + void serialize_all(serializer&); + void serialize_init(); + + friend class Cartridge; + friend class Video; + friend class Audio; + friend class Input; +}; + +#include "video.hpp" +#include "audio.hpp" +#include "input.hpp" + +#include +#include +#include +#include + +extern System system; diff --git a/libsnes/snes/system/video.cpp b/libsnes/snes/system/video.cpp new file mode 100644 index 0000000..42c7880 --- /dev/null +++ b/libsnes/snes/system/video.cpp @@ -0,0 +1,150 @@ +#ifdef SYSTEM_CPP + +Video video; + +unsigned Video::palette30(unsigned color) { + unsigned l = (color >> 15) & 15; + unsigned b = (color >> 10) & 31; + unsigned g = (color >> 5) & 31; + unsigned r = (color >> 0) & 31; + + double L = (1.0 + l) / 16.0; + unsigned R = L * ((r << 5) + (r << 0)); + unsigned G = L * ((g << 5) + (g << 0)); + unsigned B = L * ((b << 5) + (b << 0)); + + return (R << 20) + (G << 10) + (B << 0); +} + +void Video::generate(Format::e format) { + for(unsigned n = 0; n < (1 << 19); n++) palette[n] = palette30(n); + + if(format == Format::RGB24) { + for(unsigned n = 0; n < (1 << 19); n++) { + unsigned color = palette[n]; + palette[n] = ((color >> 6) & 0xff0000) + ((color >> 4) & 0x00ff00) + ((color >> 2) & 0x0000ff); + } + } + + if(format == Format::RGB16) { + for(unsigned n = 0; n < (1 << 19); n++) { + unsigned color = palette[n]; + palette[n] = ((color >> 14) & 0xf800) + ((color >> 9) & 0x07e0) + ((color >> 5) & 0x001f); + } + } + + if(format == Format::RGB15) { + for(unsigned n = 0; n < (1 << 19); n++) { + unsigned color = palette[n]; + palette[n] = ((color >> 15) & 0x7c00) + ((color >> 10) & 0x03e0) + ((color >> 5) & 0x001f); + } + } +} + +Video::Video() { + palette = new unsigned[1 << 19]; +} + +Video::~Video() { + delete[] palette; +} + +//internal + +const uint8_t Video::cursor[15 * 15] = { + 0,0,0,0,0,0,1,1,1,0,0,0,0,0,0, + 0,0,0,0,1,1,2,2,2,1,1,0,0,0,0, + 0,0,0,1,2,2,1,2,1,2,2,1,0,0,0, + 0,0,1,2,1,1,0,1,0,1,1,2,1,0,0, + 0,1,2,1,0,0,0,1,0,0,0,1,2,1,0, + 0,1,2,1,0,0,1,2,1,0,0,1,2,1,0, + 1,2,1,0,0,1,1,2,1,1,0,0,1,2,1, + 1,2,2,1,1,2,2,2,2,2,1,1,2,2,1, + 1,2,1,0,0,1,1,2,1,1,0,0,1,2,1, + 0,1,2,1,0,0,1,2,1,0,0,1,2,1,0, + 0,1,2,1,0,0,0,1,0,0,0,1,2,1,0, + 0,0,1,2,1,1,0,1,0,1,1,2,1,0,0, + 0,0,0,1,2,2,1,2,1,2,2,1,0,0,0, + 0,0,0,0,1,1,2,2,2,1,1,0,0,0,0, + 0,0,0,0,0,0,1,1,1,0,0,0,0,0,0, +}; + +void Video::draw_cursor(uint16_t color, int x, int y) { + uint32_t *data = (uint32_t*)ppu.output; + if(ppu.interlace() && ppu.field()) data += 512; + + for(int cy = 0; cy < 15; cy++) { + int vy = y + cy - 7; + if(vy <= 0 || vy >= 240) continue; //do not draw offscreen + + bool hires = (line_width[vy] == 512); + for(int cx = 0; cx < 15; cx++) { + int vx = x + cx - 7; + if(vx < 0 || vx >= 256) continue; //do not draw offscreen + uint8_t pixel = cursor[cy * 15 + cx]; + if(pixel == 0) continue; + uint32_t pixelcolor = (15 << 15) | ((pixel == 1) ? 0 : color); + + if(hires == false) { + *((uint32_t*)data + vy * 1024 + vx) = pixelcolor; + } else { + *((uint32_t*)data + vy * 1024 + vx * 2 + 0) = pixelcolor; + *((uint32_t*)data + vy * 1024 + vx * 2 + 1) = pixelcolor; + } + } + } +} + +void Video::update() { + switch(config.controller_port2.i) { + case Input::Device::SuperScope: + if(dynamic_cast(input.port2)) { + SuperScope &device = (SuperScope&)*input.port2; + draw_cursor(0x7c00, device.x, device.y); + } + break; + case Input::Device::Justifier: + case Input::Device::Justifiers: + if(dynamic_cast(input.port2)) { + Justifier &device = (Justifier&)*input.port2; + draw_cursor(0x001f, device.player1.x, device.player1.y); + if(device.chained == false) break; + draw_cursor(0x02e0, device.player2.x, device.player2.y); + } + break; + } + + uint32_t *data = (uint32_t*)ppu.output; + if(ppu.interlace() && ppu.field()) data += 512; + + if(hires) { + //normalize line widths + for(unsigned y = 0; y < 240; y++) { + if(line_width[y] == 512) continue; + uint32_t *buffer = data + y * 1024; + for(signed x = 255; x >= 0; x--) { + buffer[(x * 2) + 0] = buffer[(x * 2) + 1] = buffer[x]; + } + } + } + + interface->videoRefresh(ppu.surface, hires, ppu.interlace(), ppu.overscan()); + + hires = false; +} + +void Video::scanline() { + unsigned y = cpu.vcounter(); + if(y >= 240) return; + + hires |= ppu.hires(); + unsigned width = (ppu.hires() == false ? 256 : 512); + line_width[y] = width; +} + +void Video::init() { + hires = false; + foreach(n, line_width) n = 256; +} + +#endif diff --git a/libsnes/snes/system/video.hpp b/libsnes/snes/system/video.hpp new file mode 100644 index 0000000..c1b0386 --- /dev/null +++ b/libsnes/snes/system/video.hpp @@ -0,0 +1,24 @@ +struct Video { + struct Format { enum e { RGB30, RGB24, RGB16, RGB15 } i; }; + unsigned *palette; + + unsigned palette30(unsigned color); + void generate(Format::e format); + Video(); + ~Video(); + +private: + bool hires; + unsigned line_width[240]; + + void update(); + void scanline(); + void init(); + + static const uint8_t cursor[15 * 15]; + void draw_cursor(uint16_t color, int x, int y); + + friend class System; +}; + +extern Video video;