From 165f1e74b5b136f42b219c7905a00438abd1f3e9 Mon Sep 17 00:00:00 2001 From: Tim Allen Date: Mon, 9 Aug 2010 23:28:56 +1000 Subject: [PATCH] First version split into asnes and bsnes. --- Makefile | 91 + asnes/Makefile | 77 + asnes/audio/audio.cpp | 85 + asnes/audio/audio.hpp | 22 + asnes/cartridge/cartridge.cpp | 144 + asnes/cartridge/cartridge.hpp | 118 + asnes/cartridge/serialization.cpp | 37 + asnes/cartridge/xml.cpp | 671 + asnes/cheat/cheat-inline.hpp | 2 + asnes/cheat/cheat.cpp | 194 + asnes/cheat/cheat.hpp | 35 + asnes/chip/bsx/bsx.cpp | 8 + asnes/chip/bsx/bsx.hpp | 71 + asnes/chip/bsx/bsx_base.cpp | 140 + asnes/chip/bsx/bsx_cart.cpp | 96 + asnes/chip/bsx/bsx_flash.cpp | 119 + asnes/chip/chip.hpp | 31 + asnes/chip/cx4/cx4.cpp | 208 + asnes/chip/cx4/cx4.hpp | 95 + asnes/chip/cx4/data.cpp | 187 + asnes/chip/cx4/functions.cpp | 251 + asnes/chip/cx4/oam.cpp | 228 + asnes/chip/cx4/opcodes.cpp | 227 + asnes/chip/cx4/serialization.cpp | 39 + asnes/chip/dsp1/dsp1.cpp | 33 + asnes/chip/dsp1/dsp1.hpp | 30 + asnes/chip/dsp1/dsp1emu.cpp | 1625 + asnes/chip/dsp1/dsp1emu.hpp | 129 + asnes/chip/dsp1/serialization.cpp | 56 + asnes/chip/dsp2/dsp2.cpp | 153 + asnes/chip/dsp2/dsp2.hpp | 57 + asnes/chip/dsp2/opcodes.cpp | 177 + asnes/chip/dsp2/serialization.cpp | 26 + asnes/chip/dsp3/dsp3.cpp | 40 + asnes/chip/dsp3/dsp3.hpp | 12 + asnes/chip/dsp3/dsp3emu.c | 1146 + asnes/chip/dsp4/dsp4.cpp | 60 + asnes/chip/dsp4/dsp4.hpp | 12 + asnes/chip/dsp4/dsp4emu.c | 2150 + asnes/chip/dsp4/dsp4emu.h | 108 + asnes/chip/msu1/msu1.cpp | 148 + asnes/chip/msu1/msu1.hpp | 39 + asnes/chip/msu1/serialization.cpp | 28 + asnes/chip/obc1/obc1.cpp | 82 + asnes/chip/obc1/obc1.hpp | 26 + asnes/chip/obc1/serialization.cpp | 9 + asnes/chip/sa1/bus/bus.cpp | 218 + asnes/chip/sa1/bus/bus.hpp | 55 + asnes/chip/sa1/dma/dma.cpp | 139 + asnes/chip/sa1/dma/dma.hpp | 11 + asnes/chip/sa1/memory/memory.cpp | 24 + asnes/chip/sa1/memory/memory.hpp | 5 + asnes/chip/sa1/mmio/mmio.cpp | 631 + asnes/chip/sa1/mmio/mmio.hpp | 256 + asnes/chip/sa1/sa1.cpp | 329 + asnes/chip/sa1/sa1.hpp | 39 + asnes/chip/sa1/serialization.cpp | 149 + asnes/chip/sdd1/sdd1.cpp | 154 + asnes/chip/sdd1/sdd1.hpp | 41 + asnes/chip/sdd1/sdd1emu.cpp | 452 + asnes/chip/sdd1/sdd1emu.hpp | 164 + asnes/chip/sdd1/serialization.cpp | 19 + asnes/chip/serial/serial.cpp | 97 + asnes/chip/serial/serial.hpp | 25 + asnes/chip/serial/serialization.cpp | 8 + asnes/chip/spc7110/decomp.cpp | 511 + asnes/chip/spc7110/decomp.hpp | 46 + asnes/chip/spc7110/serialization.cpp | 81 + asnes/chip/spc7110/spc7110.cpp | 682 + asnes/chip/spc7110/spc7110.hpp | 159 + asnes/chip/srtc/serialization.cpp | 8 + asnes/chip/srtc/srtc.cpp | 231 + asnes/chip/srtc/srtc.hpp | 24 + asnes/chip/st0010/data.hpp | 130 + asnes/chip/st0010/opcodes.cpp | 261 + asnes/chip/st0010/serialization.cpp | 7 + asnes/chip/st0010/st0010.cpp | 93 + asnes/chip/st0010/st0010.hpp | 44 + asnes/chip/st0011/st0011.cpp | 20 + asnes/chip/st0011/st0011.hpp | 9 + asnes/chip/st0018/st0018.cpp | 123 + asnes/chip/st0018/st0018.hpp | 51 + asnes/chip/superfx/bus/bus.cpp | 97 + asnes/chip/superfx/bus/bus.hpp | 34 + asnes/chip/superfx/core/core.cpp | 107 + asnes/chip/superfx/core/core.hpp | 92 + asnes/chip/superfx/core/opcode_table.cpp | 270 + asnes/chip/superfx/core/opcodes.cpp | 661 + asnes/chip/superfx/core/registers.hpp | 176 + asnes/chip/superfx/disasm/disasm.cpp | 279 + asnes/chip/superfx/disasm/disasm.hpp | 5 + asnes/chip/superfx/memory/memory.cpp | 71 + asnes/chip/superfx/memory/memory.hpp | 9 + asnes/chip/superfx/mmio/mmio.cpp | 118 + asnes/chip/superfx/mmio/mmio.hpp | 2 + asnes/chip/superfx/serialization.cpp | 96 + asnes/chip/superfx/superfx.cpp | 81 + asnes/chip/superfx/superfx.hpp | 25 + asnes/chip/superfx/timing/timing.cpp | 97 + asnes/chip/superfx/timing/timing.hpp | 19 + asnes/chip/supergameboy/serialization.cpp | 9 + asnes/chip/supergameboy/supergameboy.cpp | 142 + asnes/chip/supergameboy/supergameboy.hpp | 43 + asnes/config/config.cpp | 25 + asnes/config/config.hpp | 34 + asnes/cpu/core/algorithms.cpp | 331 + asnes/cpu/core/core.cpp | 74 + asnes/cpu/core/core.hpp | 217 + asnes/cpu/core/disassembler/disassembler.cpp | 483 + asnes/cpu/core/disassembler/disassembler.hpp | 30 + asnes/cpu/core/memory.hpp | 77 + asnes/cpu/core/opcode_misc.cpp | 338 + asnes/cpu/core/opcode_pc.cpp | 181 + asnes/cpu/core/opcode_read.cpp | 279 + asnes/cpu/core/opcode_rmw.cpp | 169 + asnes/cpu/core/opcode_write.cpp | 199 + asnes/cpu/core/registers.hpp | 81 + asnes/cpu/core/serialization.cpp | 36 + asnes/cpu/core/table.cpp | 312 + asnes/cpu/cpu.cpp | 141 + asnes/cpu/cpu.hpp | 136 + asnes/cpu/debugger/cpu-debugger.cpp | 99 + asnes/cpu/debugger/cpu-debugger.hpp | 76 + asnes/cpu/debugger/debugger.cpp | 134 + asnes/cpu/debugger/debugger.hpp | 98 + asnes/cpu/dma/dma.cpp | 292 + asnes/cpu/dma/dma.hpp | 79 + asnes/cpu/memory/memory.cpp | 38 + asnes/cpu/memory/memory.hpp | 16 + asnes/cpu/mmio/mmio.cpp | 541 + asnes/cpu/mmio/mmio.hpp | 71 + asnes/cpu/serialization.cpp | 118 + asnes/cpu/timing/irq.cpp | 106 + asnes/cpu/timing/joypad.cpp | 28 + asnes/cpu/timing/timing.cpp | 195 + asnes/cpu/timing/timing.hpp | 24 + asnes/debugger/debugger.cpp | 106 + asnes/debugger/debugger.hpp | 32 + asnes/dsp/brr.cpp | 62 + asnes/dsp/counter.cpp | 52 + asnes/dsp/debugger/debugger.cpp | 36 + asnes/dsp/debugger/debugger.hpp | 35 + asnes/dsp/debugger/dsp-debugger.cpp | 54 + asnes/dsp/debugger/dsp-debugger.hpp | 32 + asnes/dsp/dsp.cpp | 304 + asnes/dsp/dsp.hpp | 179 + asnes/dsp/echo.cpp | 135 + asnes/dsp/envelope.cpp | 62 + asnes/dsp/gaussian.cpp | 54 + asnes/dsp/misc.cpp | 35 + asnes/dsp/serialization.cpp | 66 + asnes/dsp/voice.cpp | 174 + asnes/input/input.cpp | 344 + asnes/input/input.hpp | 87 + asnes/interface/interface.hpp | 7 + asnes/libsnes/libsnes.cpp | 264 + asnes/libsnes/libsnes.hpp | 123 + asnes/memory/memory-inline.hpp | 69 + asnes/memory/memory.cpp | 158 + asnes/memory/memory.hpp | 111 + asnes/memory/serialization.cpp | 11 + asnes/ppu/background/background.cpp | 249 + asnes/ppu/background/background.hpp | 58 + asnes/ppu/background/mode7.cpp | 100 + asnes/ppu/counter/counter-inline.hpp | 85 + asnes/ppu/counter/counter.hpp | 49 + asnes/ppu/debugger/debugger.cpp | 3 + asnes/ppu/debugger/debugger.hpp | 8 + asnes/ppu/debugger/ppu-debugger.cpp | 305 + asnes/ppu/debugger/ppu-debugger.hpp | 243 + asnes/ppu/mmio/mmio.cpp | 871 + asnes/ppu/mmio/mmio.hpp | 170 + asnes/ppu/ppu.cpp | 155 + asnes/ppu/ppu.hpp | 59 + asnes/ppu/screen/screen.cpp | 219 + asnes/ppu/screen/screen.hpp | 37 + asnes/ppu/serialization.cpp | 264 + asnes/ppu/sprite/list.cpp | 54 + asnes/ppu/sprite/sprite.cpp | 218 + asnes/ppu/sprite/sprite.hpp | 81 + asnes/ppu/window/window.cpp | 167 + asnes/ppu/window/window.hpp | 87 + asnes/scheduler/scheduler.cpp | 28 + asnes/scheduler/scheduler.hpp | 16 + asnes/smp/core/algorithms.cpp | 126 + asnes/smp/core/core.cpp | 31 + asnes/smp/core/core.hpp | 118 + asnes/smp/core/disassembler/disassembler.cpp | 315 + asnes/smp/core/disassembler/disassembler.hpp | 3 + asnes/smp/core/memory.hpp | 27 + asnes/smp/core/opcode_misc.cpp | 148 + asnes/smp/core/opcode_mov.cpp | 200 + asnes/smp/core/opcode_pc.cpp | 152 + asnes/smp/core/opcode_read.cpp | 154 + asnes/smp/core/opcode_rmw.cpp | 58 + asnes/smp/core/registers.hpp | 44 + asnes/smp/core/serialization.cpp | 26 + asnes/smp/core/table.cpp | 264 + asnes/smp/debugger/debugger.cpp | 61 + asnes/smp/debugger/debugger.hpp | 36 + asnes/smp/debugger/smp-debugger.cpp | 25 + asnes/smp/debugger/smp-debugger.hpp | 16 + asnes/smp/iplrom.cpp | 44 + asnes/smp/memory/memory.cpp | 213 + asnes/smp/memory/memory.hpp | 12 + asnes/smp/serialization.cpp | 52 + asnes/smp/smp.cpp | 123 + asnes/smp/smp.hpp | 59 + asnes/smp/timing/timing.cpp | 60 + asnes/smp/timing/timing.hpp | 21 + asnes/snes.hpp | 94 + asnes/system/serialization.cpp | 88 + asnes/system/system.cpp | 236 + asnes/system/system.hpp | 58 + asnes/video/video.cpp | 98 + asnes/video/video.hpp | 16 + bsnes/Makefile | 101 + bsnes/audio/audio.cpp | 85 + bsnes/audio/audio.hpp | 22 + bsnes/cartridge/cartridge.cpp | 144 + bsnes/cartridge/cartridge.hpp | 118 + bsnes/cartridge/serialization.cpp | 37 + bsnes/cartridge/xml.cpp | 671 + bsnes/cheat/cheat-inline.hpp | 2 + bsnes/cheat/cheat.cpp | 194 + bsnes/cheat/cheat.hpp | 35 + bsnes/chip/bsx/bsx.cpp | 8 + bsnes/chip/bsx/bsx.hpp | 71 + bsnes/chip/bsx/bsx_base.cpp | 140 + bsnes/chip/bsx/bsx_cart.cpp | 96 + bsnes/chip/bsx/bsx_flash.cpp | 119 + bsnes/chip/chip.hpp | 31 + bsnes/chip/cx4/cx4.cpp | 208 + bsnes/chip/cx4/cx4.hpp | 95 + bsnes/chip/cx4/data.cpp | 187 + bsnes/chip/cx4/functions.cpp | 251 + bsnes/chip/cx4/oam.cpp | 228 + bsnes/chip/cx4/opcodes.cpp | 227 + bsnes/chip/cx4/serialization.cpp | 39 + bsnes/chip/dsp1/dsp1.cpp | 33 + bsnes/chip/dsp1/dsp1.hpp | 30 + bsnes/chip/dsp1/dsp1emu.cpp | 1625 + bsnes/chip/dsp1/dsp1emu.hpp | 129 + bsnes/chip/dsp1/serialization.cpp | 56 + bsnes/chip/dsp2/dsp2.cpp | 153 + bsnes/chip/dsp2/dsp2.hpp | 57 + bsnes/chip/dsp2/opcodes.cpp | 177 + bsnes/chip/dsp2/serialization.cpp | 26 + bsnes/chip/dsp3/dsp3.cpp | 40 + bsnes/chip/dsp3/dsp3.hpp | 12 + bsnes/chip/dsp3/dsp3emu.c | 1146 + bsnes/chip/dsp4/dsp4.cpp | 60 + bsnes/chip/dsp4/dsp4.hpp | 12 + bsnes/chip/dsp4/dsp4emu.c | 2150 + bsnes/chip/dsp4/dsp4emu.h | 108 + bsnes/chip/msu1/msu1.cpp | 148 + bsnes/chip/msu1/msu1.hpp | 39 + bsnes/chip/msu1/serialization.cpp | 28 + bsnes/chip/obc1/obc1.cpp | 82 + bsnes/chip/obc1/obc1.hpp | 26 + bsnes/chip/obc1/serialization.cpp | 9 + bsnes/chip/sa1/bus/bus.cpp | 218 + bsnes/chip/sa1/bus/bus.hpp | 55 + bsnes/chip/sa1/dma/dma.cpp | 139 + bsnes/chip/sa1/dma/dma.hpp | 11 + bsnes/chip/sa1/memory/memory.cpp | 24 + bsnes/chip/sa1/memory/memory.hpp | 5 + bsnes/chip/sa1/mmio/mmio.cpp | 631 + bsnes/chip/sa1/mmio/mmio.hpp | 256 + bsnes/chip/sa1/sa1.cpp | 329 + bsnes/chip/sa1/sa1.hpp | 39 + bsnes/chip/sa1/serialization.cpp | 149 + bsnes/chip/sdd1/sdd1.cpp | 154 + bsnes/chip/sdd1/sdd1.hpp | 41 + bsnes/chip/sdd1/sdd1emu.cpp | 452 + bsnes/chip/sdd1/sdd1emu.hpp | 164 + bsnes/chip/sdd1/serialization.cpp | 19 + bsnes/chip/serial/serial.cpp | 97 + bsnes/chip/serial/serial.hpp | 25 + bsnes/chip/serial/serialization.cpp | 8 + bsnes/chip/spc7110/decomp.cpp | 511 + bsnes/chip/spc7110/decomp.hpp | 46 + bsnes/chip/spc7110/serialization.cpp | 81 + bsnes/chip/spc7110/spc7110.cpp | 682 + bsnes/chip/spc7110/spc7110.hpp | 159 + bsnes/chip/srtc/serialization.cpp | 8 + bsnes/chip/srtc/srtc.cpp | 231 + bsnes/chip/srtc/srtc.hpp | 24 + bsnes/chip/st0010/data.hpp | 130 + bsnes/chip/st0010/opcodes.cpp | 261 + bsnes/chip/st0010/serialization.cpp | 7 + bsnes/chip/st0010/st0010.cpp | 93 + bsnes/chip/st0010/st0010.hpp | 44 + bsnes/chip/st0011/st0011.cpp | 20 + bsnes/chip/st0011/st0011.hpp | 9 + bsnes/chip/st0018/st0018.cpp | 123 + bsnes/chip/st0018/st0018.hpp | 51 + bsnes/chip/superfx/bus/bus.cpp | 97 + bsnes/chip/superfx/bus/bus.hpp | 34 + bsnes/chip/superfx/core/core.cpp | 107 + bsnes/chip/superfx/core/core.hpp | 92 + bsnes/chip/superfx/core/opcode_table.cpp | 270 + bsnes/chip/superfx/core/opcodes.cpp | 661 + bsnes/chip/superfx/core/registers.hpp | 176 + bsnes/chip/superfx/disasm/disasm.cpp | 279 + bsnes/chip/superfx/disasm/disasm.hpp | 5 + bsnes/chip/superfx/memory/memory.cpp | 71 + bsnes/chip/superfx/memory/memory.hpp | 9 + bsnes/chip/superfx/mmio/mmio.cpp | 118 + bsnes/chip/superfx/mmio/mmio.hpp | 2 + bsnes/chip/superfx/serialization.cpp | 96 + bsnes/chip/superfx/superfx.cpp | 81 + bsnes/chip/superfx/superfx.hpp | 25 + bsnes/chip/superfx/timing/timing.cpp | 97 + bsnes/chip/superfx/timing/timing.hpp | 19 + bsnes/chip/supergameboy/serialization.cpp | 9 + bsnes/chip/supergameboy/supergameboy.cpp | 142 + bsnes/chip/supergameboy/supergameboy.hpp | 43 + bsnes/config/config.cpp | 25 + bsnes/config/config.hpp | 34 + bsnes/cpu/core/algorithms.cpp | 331 + bsnes/cpu/core/core.cpp | 74 + bsnes/cpu/core/core.hpp | 217 + bsnes/cpu/core/disassembler/disassembler.cpp | 483 + bsnes/cpu/core/disassembler/disassembler.hpp | 30 + bsnes/cpu/core/memory.hpp | 77 + bsnes/cpu/core/opcode_misc.cpp | 338 + bsnes/cpu/core/opcode_pc.cpp | 181 + bsnes/cpu/core/opcode_read.cpp | 279 + bsnes/cpu/core/opcode_rmw.cpp | 169 + bsnes/cpu/core/opcode_write.cpp | 199 + bsnes/cpu/core/registers.hpp | 81 + bsnes/cpu/core/serialization.cpp | 36 + bsnes/cpu/core/table.cpp | 312 + bsnes/cpu/cpu-debugger.cpp | 99 + bsnes/cpu/cpu-debugger.hpp | 76 + bsnes/cpu/cpu.cpp | 38 + bsnes/cpu/cpu.hpp | 35 + bsnes/cpu/scpu/debugger/debugger.cpp | 134 + bsnes/cpu/scpu/debugger/debugger.hpp | 98 + bsnes/cpu/scpu/dma/dma.cpp | 292 + bsnes/cpu/scpu/dma/dma.hpp | 79 + bsnes/cpu/scpu/memory/memory.cpp | 38 + bsnes/cpu/scpu/memory/memory.hpp | 16 + bsnes/cpu/scpu/mmio/mmio.cpp | 541 + bsnes/cpu/scpu/mmio/mmio.hpp | 71 + bsnes/cpu/scpu/scpu.cpp | 113 + bsnes/cpu/scpu/scpu.hpp | 126 + bsnes/cpu/scpu/serialization.cpp | 116 + bsnes/cpu/scpu/timing/irq.cpp | 106 + bsnes/cpu/scpu/timing/joypad.cpp | 28 + bsnes/cpu/scpu/timing/timing.cpp | 195 + bsnes/cpu/scpu/timing/timing.hpp | 24 + bsnes/cpu/synchronization.hpp | 23 + bsnes/debugger/debugger.cpp | 106 + bsnes/debugger/debugger.hpp | 32 + bsnes/dsp/adsp/adsp.cpp | 613 + bsnes/dsp/adsp/adsp.hpp | 182 + bsnes/dsp/adsp/debugger/debugger.cpp | 3 + bsnes/dsp/adsp/debugger/debugger.hpp | 3 + bsnes/dsp/adsp/serialization.cpp | 71 + bsnes/dsp/adsp/tables.cpp | 77 + bsnes/dsp/dsp-debugger.cpp | 54 + bsnes/dsp/dsp-debugger.hpp | 32 + bsnes/dsp/dsp.cpp | 24 + bsnes/dsp/dsp.hpp | 21 + bsnes/dsp/sdsp/brr.cpp | 62 + bsnes/dsp/sdsp/counter.cpp | 52 + bsnes/dsp/sdsp/debugger/debugger.cpp | 36 + bsnes/dsp/sdsp/debugger/debugger.hpp | 35 + bsnes/dsp/sdsp/echo.cpp | 135 + bsnes/dsp/sdsp/envelope.cpp | 62 + bsnes/dsp/sdsp/gaussian.cpp | 54 + bsnes/dsp/sdsp/misc.cpp | 35 + bsnes/dsp/sdsp/sdsp.cpp | 342 + bsnes/dsp/sdsp/sdsp.hpp | 176 + bsnes/dsp/sdsp/serialization.cpp | 68 + bsnes/dsp/sdsp/voice.cpp | 174 + bsnes/dsp/synchronization.hpp | 7 + bsnes/input/input.cpp | 344 + bsnes/input/input.hpp | 87 + bsnes/interface/interface.hpp | 7 + bsnes/libsnes/libsnes.cpp | 264 + bsnes/libsnes/libsnes.hpp | 123 + bsnes/memory/memory-inline.hpp | 75 + bsnes/memory/memory.cpp | 112 + bsnes/memory/memory.hpp | 104 + bsnes/memory/smemory/serialization.cpp | 11 + bsnes/memory/smemory/smemory.cpp | 58 + bsnes/memory/smemory/smemory.hpp | 19 + bsnes/ppu/bppu/bppu.cpp | 378 + bsnes/ppu/bppu/bppu.hpp | 64 + bsnes/ppu/bppu/debugger/debugger.cpp | 290 + bsnes/ppu/bppu/debugger/debugger.hpp | 272 + bsnes/ppu/bppu/debugger/render.cpp | 194 + bsnes/ppu/bppu/memory/memory.cpp | 154 + bsnes/ppu/bppu/memory/memory.hpp | 10 + bsnes/ppu/bppu/mmio/mmio.cpp | 671 + bsnes/ppu/bppu/mmio/mmio.hpp | 202 + bsnes/ppu/bppu/render/addsub.cpp | 25 + bsnes/ppu/bppu/render/bg.cpp | 205 + bsnes/ppu/bppu/render/cache.cpp | 147 + bsnes/ppu/bppu/render/line.cpp | 116 + bsnes/ppu/bppu/render/mode7.cpp | 139 + bsnes/ppu/bppu/render/oam.cpp | 221 + bsnes/ppu/bppu/render/render.cpp | 129 + bsnes/ppu/bppu/render/render.hpp | 97 + bsnes/ppu/bppu/render/windows.cpp | 70 + bsnes/ppu/bppu/serialization.cpp | 186 + bsnes/ppu/ppu-debugger.cpp | 305 + bsnes/ppu/ppu-debugger.hpp | 243 + bsnes/ppu/ppu-inline.hpp | 85 + bsnes/ppu/ppu.cpp | 54 + bsnes/ppu/ppu.hpp | 88 + bsnes/ppu/serialization.cpp | 26 + bsnes/ppu/sppu/background/background.cpp | 249 + bsnes/ppu/sppu/background/background.hpp | 58 + bsnes/ppu/sppu/background/mode7.cpp | 100 + bsnes/ppu/sppu/debugger/debugger.cpp | 3 + bsnes/ppu/sppu/debugger/debugger.hpp | 8 + bsnes/ppu/sppu/mmio/mmio.cpp | 871 + bsnes/ppu/sppu/mmio/mmio.hpp | 170 + bsnes/ppu/sppu/screen/screen.cpp | 219 + bsnes/ppu/sppu/screen/screen.hpp | 37 + bsnes/ppu/sppu/serialization.cpp | 245 + bsnes/ppu/sppu/sppu.cpp | 120 + bsnes/ppu/sppu/sppu.hpp | 40 + bsnes/ppu/sppu/sprite/list.cpp | 54 + bsnes/ppu/sppu/sprite/sprite.cpp | 218 + bsnes/ppu/sppu/sprite/sprite.hpp | 81 + bsnes/ppu/sppu/window/window.cpp | 167 + bsnes/ppu/sppu/window/window.hpp | 87 + bsnes/ppu/synchronization.hpp | 7 + bsnes/scheduler/scheduler.cpp | 28 + bsnes/scheduler/scheduler.hpp | 16 + bsnes/smp/core/algorithms.cpp | 126 + bsnes/smp/core/core.cpp | 31 + bsnes/smp/core/core.hpp | 118 + bsnes/smp/core/disassembler/disassembler.cpp | 315 + bsnes/smp/core/disassembler/disassembler.hpp | 3 + bsnes/smp/core/memory.hpp | 27 + bsnes/smp/core/opcode_misc.cpp | 148 + bsnes/smp/core/opcode_mov.cpp | 200 + bsnes/smp/core/opcode_pc.cpp | 152 + bsnes/smp/core/opcode_read.cpp | 154 + bsnes/smp/core/opcode_rmw.cpp | 58 + bsnes/smp/core/registers.hpp | 44 + bsnes/smp/core/serialization.cpp | 26 + bsnes/smp/core/table.cpp | 264 + bsnes/smp/smp-debugger.cpp | 25 + bsnes/smp/smp-debugger.hpp | 16 + bsnes/smp/smp.cpp | 65 + bsnes/smp/smp.hpp | 30 + bsnes/smp/ssmp/debugger/debugger.cpp | 61 + bsnes/smp/ssmp/debugger/debugger.hpp | 36 + bsnes/smp/ssmp/memory/memory.cpp | 213 + bsnes/smp/ssmp/memory/memory.hpp | 12 + bsnes/smp/ssmp/serialization.cpp | 52 + bsnes/smp/ssmp/ssmp.cpp | 109 + bsnes/smp/ssmp/ssmp.hpp | 51 + bsnes/smp/ssmp/timing/timing.cpp | 64 + bsnes/smp/ssmp/timing/timing.hpp | 21 + bsnes/smp/synchronization.hpp | 12 + bsnes/snes.hpp | 137 + bsnes/system/serialization.cpp | 88 + bsnes/system/system.cpp | 236 + bsnes/system/system.hpp | 58 + bsnes/video/video.cpp | 98 + bsnes/video/video.hpp | 16 + libco/amd64.c | 104 + libco/fiber.c | 51 + libco/libco.c | 23 + libco/libco.h | 34 + libco/ppc-elf.c | 325 + libco/ppc.s | 478 + libco/ppc64.s | 513 + libco/sjlj.c | 102 + libco/ucontext.c | 67 + libco/x86.c | 93 + nall/Makefile | 107 + nall/algorithm.hpp | 17 + nall/any.hpp | 74 + nall/array.hpp | 141 + nall/base64.hpp | 90 + nall/bit.hpp | 51 + nall/concept.hpp | 34 + nall/config.hpp | 123 + nall/crc32.hpp | 66 + nall/detect.hpp | 30 + nall/dictionary.hpp | 75 + nall/dl.hpp | 96 + nall/endian.hpp | 38 + nall/file.hpp | 261 + nall/filemap.hpp | 190 + nall/foreach.hpp | 12 + nall/function.hpp | 90 + nall/input.hpp | 386 + nall/lzss.hpp | 81 + nall/moduloarray.hpp | 40 + nall/platform.hpp | 82 + nall/priorityqueue.hpp | 109 + nall/property.hpp | 91 + nall/qt/Makefile | 55 + nall/qt/check-action.moc.hpp | 41 + nall/qt/concept.hpp | 10 + nall/qt/file-dialog.moc.hpp | 392 + nall/qt/hex-editor.moc.hpp | 173 + nall/qt/radio-action.moc.hpp | 41 + nall/qt/window.moc.hpp | 105 + nall/random.hpp | 20 + nall/serial.hpp | 80 + nall/serializer.hpp | 145 + nall/sha256.hpp | 143 + nall/snes/info.hpp | 864 + nall/sort.hpp | 62 + nall/static.hpp | 20 + nall/stdint.hpp | 44 + nall/string.hpp | 30 + nall/string/base.hpp | 141 + nall/string/bsv.hpp | 75 + nall/string/cast.hpp | 32 + nall/string/compare.hpp | 72 + nall/string/convert.hpp | 157 + nall/string/core.hpp | 143 + nall/string/filename.hpp | 61 + nall/string/match.hpp | 76 + nall/string/math.hpp | 164 + nall/string/replace.hpp | 103 + nall/string/split.hpp | 56 + nall/string/strl.hpp | 52 + nall/string/strpos.hpp | 41 + nall/string/trim.hpp | 61 + nall/string/utility.hpp | 157 + nall/string/variadic.hpp | 12 + nall/string/xml.hpp | 265 + nall/ups.hpp | 190 + nall/utf8.hpp | 72 + nall/utility.hpp | 39 + nall/varint.hpp | 92 + nall/vector.hpp | 281 + qt/Makefile | 94 + qt/application/application.cpp | 182 + qt/application/application.moc.hpp | 57 + qt/application/init.cpp | 107 + qt/base/about.cpp | 34 + qt/base/about.moc.hpp | 14 + qt/base/base.cpp | 7 + qt/base/filebrowser.cpp | 201 + qt/base/filebrowser.moc.hpp | 38 + qt/base/htmlviewer.cpp | 23 + qt/base/htmlviewer.moc.hpp | 14 + qt/base/loader.cpp | 236 + qt/base/loader.moc.hpp | 47 + qt/base/main.cpp | 674 + qt/base/main.moc.hpp | 162 + qt/cartridge/cartridge.cpp | 400 + qt/cartridge/cartridge.hpp | 42 + qt/config.cpp | 146 + qt/config.hpp | 107 + qt/data/bsnes.Manifest | 9 + qt/data/bsnes.desktop | 8 + qt/data/bsnes.ico | Bin 0 -> 22071 bytes qt/data/bsnes.png | Bin 0 -> 1368 bytes qt/data/cheats.xml | 53521 ++++++++++++++++ qt/data/documentation.html | 81 + .../icons-16x16/accessories-text-editor.png | Bin 0 -> 574 bytes .../icons-16x16/applications-multimedia.png | Bin 0 -> 592 bytes qt/data/icons-16x16/appointment-new.png | Bin 0 -> 897 bytes qt/data/icons-16x16/audio-volume-high.png | Bin 0 -> 685 bytes qt/data/icons-16x16/document-open.png | Bin 0 -> 672 bytes qt/data/icons-16x16/folder-new.png | Bin 0 -> 635 bytes qt/data/icons-16x16/folder.png | Bin 0 -> 581 bytes qt/data/icons-16x16/go-up.png | Bin 0 -> 652 bytes qt/data/icons-16x16/help-browser.png | Bin 0 -> 932 bytes qt/data/icons-16x16/image-x-generic.png | Bin 0 -> 558 bytes qt/data/icons-16x16/input-gaming.png | Bin 0 -> 812 bytes qt/data/icons-16x16/item-check-off.png | Bin 0 -> 209 bytes qt/data/icons-16x16/item-check-on.png | Bin 0 -> 266 bytes qt/data/icons-16x16/item-radio-off.png | Bin 0 -> 565 bytes qt/data/icons-16x16/item-radio-on.png | Bin 0 -> 647 bytes qt/data/icons-16x16/media-playback-start.png | Bin 0 -> 660 bytes qt/data/icons-16x16/media-playback-stop.png | Bin 0 -> 429 bytes qt/data/icons-16x16/media-record.png | Bin 0 -> 653 bytes qt/data/icons-16x16/preferences-desktop.png | Bin 0 -> 440 bytes qt/data/icons-16x16/preferences-system.png | Bin 0 -> 611 bytes qt/data/icons-16x16/process-stop.png | Bin 0 -> 820 bytes qt/data/icons-16x16/system-file-manager.png | Bin 0 -> 378 bytes qt/data/icons-16x16/system-search.png | Bin 0 -> 935 bytes qt/data/icons-16x16/text-x-generic.png | Bin 0 -> 333 bytes qt/data/icons-16x16/utilities-terminal.png | Bin 0 -> 668 bytes qt/data/icons-16x16/video-display.png | Bin 0 -> 662 bytes qt/data/icons-16x16/view-refresh.png | Bin 0 -> 912 bytes qt/data/license.html | 77 + qt/data/logo.png | Bin 0 -> 16733 bytes qt/debugger/debugger.cpp | 249 + qt/debugger/debugger.moc.hpp | 51 + qt/debugger/misc/debugger-options.cpp | 29 + qt/debugger/misc/debugger-options.moc.hpp | 15 + qt/debugger/ppu/cgram-viewer.cpp | 126 + qt/debugger/ppu/cgram-viewer.moc.hpp | 29 + qt/debugger/ppu/layer-toggle.cpp | 107 + qt/debugger/ppu/layer-toggle.moc.hpp | 30 + qt/debugger/ppu/oam-viewer.cpp | 126 + qt/debugger/ppu/oam-viewer.moc.hpp | 24 + qt/debugger/ppu/vram-viewer.cpp | 177 + qt/debugger/ppu/vram-viewer.moc.hpp | 38 + qt/debugger/tools/breakpoint.cpp | 80 + qt/debugger/tools/breakpoint.moc.hpp | 30 + qt/debugger/tools/disassembler.cpp | 112 + qt/debugger/tools/disassembler.moc.hpp | 29 + qt/debugger/tools/memory.cpp | 165 + qt/debugger/tools/memory.moc.hpp | 36 + qt/debugger/tools/properties.cpp | 100 + qt/debugger/tools/properties.moc.hpp | 39 + qt/debugger/tracer.cpp | 74 + qt/debugger/tracer.moc.hpp | 26 + qt/input/controller.cpp | 385 + qt/input/controller.hpp | 112 + qt/input/input.cpp | 272 + qt/input/input.hpp | 90 + qt/input/userinterface-emulationspeed.cpp | 181 + qt/input/userinterface-general.cpp | 69 + qt/input/userinterface-states.cpp | 148 + qt/input/userinterface-system.cpp | 89 + qt/input/userinterface-videosettings.cpp | 148 + qt/input/userinterface.hpp | 5 + qt/interface.cpp | 81 + qt/interface.hpp | 13 + qt/link/filter.cpp | 239 + qt/link/filter.hpp | 54 + qt/link/reader.cpp | 44 + qt/link/reader.hpp | 15 + qt/main.cpp | 45 + qt/movie/movie.cpp | 114 + qt/movie/movie.hpp | 20 + qt/platform/platform_osx.cpp | 17 + qt/platform/platform_win.cpp | 53 + qt/platform/platform_x.cpp | 47 + qt/resource/resource.qrc | 39 + qt/resource/resource.rc | 2 + qt/settings/advanced.cpp | 192 + qt/settings/advanced.moc.hpp | 58 + qt/settings/audio.cpp | 130 + qt/settings/audio.moc.hpp | 29 + qt/settings/input.cpp | 354 + qt/settings/input.moc.hpp | 44 + qt/settings/paths.cpp | 86 + qt/settings/paths.moc.hpp | 40 + qt/settings/settings.cpp | 62 + qt/settings/settings.moc.hpp | 18 + qt/settings/video.cpp | 343 + qt/settings/video.moc.hpp | 74 + qt/state/state.cpp | 110 + qt/state/state.hpp | 24 + qt/tools/cheateditor.cpp | 390 + qt/tools/cheateditor.moc.hpp | 65 + qt/tools/cheatfinder.cpp | 200 + qt/tools/cheatfinder.moc.hpp | 41 + qt/tools/statemanager.cpp | 280 + qt/tools/statemanager.moc.hpp | 40 + qt/tools/tools.cpp | 46 + qt/tools/tools.moc.hpp | 16 + qt/ui-base.hpp | 91 + qt/utility/system-state.cpp | 102 + qt/utility/utility.cpp | 138 + qt/utility/utility.hpp | 38 + qt/utility/window.cpp | 192 + ruby/audio.hpp | 23 + ruby/audio/alsa.cpp | 240 + ruby/audio/ao.cpp | 94 + ruby/audio/directsound.cpp | 212 + ruby/audio/openal.cpp | 210 + ruby/audio/oss.cpp | 113 + ruby/audio/pulseaudio.cpp | 177 + ruby/audio/pulseaudiosimple.cpp | 115 + ruby/input.hpp | 22 + ruby/input/carbon.cpp | 157 + ruby/input/directinput.cpp | 387 + ruby/input/rawinput.cpp | 797 + ruby/input/sdl.cpp | 230 + ruby/input/x.cpp | 50 + ruby/input/xlibkeys.hpp | 264 + ruby/ruby.cpp | 370 + ruby/ruby.hpp | 109 + ruby/ruby_audio.cpp | 133 + ruby/ruby_impl.cpp | 178 + ruby/video.hpp | 28 + ruby/video/direct3d.cpp | 453 + ruby/video/directdraw.cpp | 186 + ruby/video/gdi.cpp | 100 + ruby/video/glx.cpp | 231 + ruby/video/opengl.hpp | 225 + ruby/video/qtopengl.cpp | 174 + ruby/video/qtraster.cpp | 126 + ruby/video/sdl.cpp | 140 + ruby/video/wgl.cpp | 154 + ruby/video/xv.cpp | 498 + sync.sh | 10 + 698 files changed, 145483 insertions(+) create mode 100755 Makefile create mode 100755 asnes/Makefile create mode 100755 asnes/audio/audio.cpp create mode 100755 asnes/audio/audio.hpp create mode 100755 asnes/cartridge/cartridge.cpp create mode 100755 asnes/cartridge/cartridge.hpp create mode 100755 asnes/cartridge/serialization.cpp create mode 100755 asnes/cartridge/xml.cpp create mode 100755 asnes/cheat/cheat-inline.hpp create mode 100755 asnes/cheat/cheat.cpp create mode 100755 asnes/cheat/cheat.hpp create mode 100755 asnes/chip/bsx/bsx.cpp create mode 100755 asnes/chip/bsx/bsx.hpp create mode 100755 asnes/chip/bsx/bsx_base.cpp create mode 100755 asnes/chip/bsx/bsx_cart.cpp create mode 100755 asnes/chip/bsx/bsx_flash.cpp create mode 100755 asnes/chip/chip.hpp create mode 100755 asnes/chip/cx4/cx4.cpp create mode 100755 asnes/chip/cx4/cx4.hpp create mode 100755 asnes/chip/cx4/data.cpp create mode 100755 asnes/chip/cx4/functions.cpp create mode 100755 asnes/chip/cx4/oam.cpp create mode 100755 asnes/chip/cx4/opcodes.cpp create mode 100755 asnes/chip/cx4/serialization.cpp create mode 100755 asnes/chip/dsp1/dsp1.cpp create mode 100755 asnes/chip/dsp1/dsp1.hpp create mode 100755 asnes/chip/dsp1/dsp1emu.cpp create mode 100755 asnes/chip/dsp1/dsp1emu.hpp create mode 100755 asnes/chip/dsp1/serialization.cpp create mode 100755 asnes/chip/dsp2/dsp2.cpp create mode 100755 asnes/chip/dsp2/dsp2.hpp create mode 100755 asnes/chip/dsp2/opcodes.cpp create mode 100755 asnes/chip/dsp2/serialization.cpp create mode 100755 asnes/chip/dsp3/dsp3.cpp create mode 100755 asnes/chip/dsp3/dsp3.hpp create mode 100755 asnes/chip/dsp3/dsp3emu.c create mode 100755 asnes/chip/dsp4/dsp4.cpp create mode 100755 asnes/chip/dsp4/dsp4.hpp create mode 100755 asnes/chip/dsp4/dsp4emu.c create mode 100755 asnes/chip/dsp4/dsp4emu.h create mode 100755 asnes/chip/msu1/msu1.cpp create mode 100755 asnes/chip/msu1/msu1.hpp create mode 100755 asnes/chip/msu1/serialization.cpp create mode 100755 asnes/chip/obc1/obc1.cpp create mode 100755 asnes/chip/obc1/obc1.hpp create mode 100755 asnes/chip/obc1/serialization.cpp create mode 100755 asnes/chip/sa1/bus/bus.cpp create mode 100755 asnes/chip/sa1/bus/bus.hpp create mode 100755 asnes/chip/sa1/dma/dma.cpp create mode 100755 asnes/chip/sa1/dma/dma.hpp create mode 100755 asnes/chip/sa1/memory/memory.cpp create mode 100755 asnes/chip/sa1/memory/memory.hpp create mode 100755 asnes/chip/sa1/mmio/mmio.cpp create mode 100755 asnes/chip/sa1/mmio/mmio.hpp create mode 100755 asnes/chip/sa1/sa1.cpp create mode 100755 asnes/chip/sa1/sa1.hpp create mode 100755 asnes/chip/sa1/serialization.cpp create mode 100755 asnes/chip/sdd1/sdd1.cpp create mode 100755 asnes/chip/sdd1/sdd1.hpp create mode 100755 asnes/chip/sdd1/sdd1emu.cpp create mode 100755 asnes/chip/sdd1/sdd1emu.hpp create mode 100755 asnes/chip/sdd1/serialization.cpp create mode 100755 asnes/chip/serial/serial.cpp create mode 100755 asnes/chip/serial/serial.hpp create mode 100755 asnes/chip/serial/serialization.cpp create mode 100755 asnes/chip/spc7110/decomp.cpp create mode 100755 asnes/chip/spc7110/decomp.hpp create mode 100755 asnes/chip/spc7110/serialization.cpp create mode 100755 asnes/chip/spc7110/spc7110.cpp create mode 100755 asnes/chip/spc7110/spc7110.hpp create mode 100755 asnes/chip/srtc/serialization.cpp create mode 100755 asnes/chip/srtc/srtc.cpp create mode 100755 asnes/chip/srtc/srtc.hpp create mode 100755 asnes/chip/st0010/data.hpp create mode 100755 asnes/chip/st0010/opcodes.cpp create mode 100755 asnes/chip/st0010/serialization.cpp create mode 100755 asnes/chip/st0010/st0010.cpp create mode 100755 asnes/chip/st0010/st0010.hpp create mode 100755 asnes/chip/st0011/st0011.cpp create mode 100755 asnes/chip/st0011/st0011.hpp create mode 100755 asnes/chip/st0018/st0018.cpp create mode 100755 asnes/chip/st0018/st0018.hpp create mode 100755 asnes/chip/superfx/bus/bus.cpp create mode 100755 asnes/chip/superfx/bus/bus.hpp create mode 100755 asnes/chip/superfx/core/core.cpp create mode 100755 asnes/chip/superfx/core/core.hpp create mode 100755 asnes/chip/superfx/core/opcode_table.cpp create mode 100755 asnes/chip/superfx/core/opcodes.cpp create mode 100755 asnes/chip/superfx/core/registers.hpp create mode 100755 asnes/chip/superfx/disasm/disasm.cpp create mode 100755 asnes/chip/superfx/disasm/disasm.hpp create mode 100755 asnes/chip/superfx/memory/memory.cpp create mode 100755 asnes/chip/superfx/memory/memory.hpp create mode 100755 asnes/chip/superfx/mmio/mmio.cpp create mode 100755 asnes/chip/superfx/mmio/mmio.hpp create mode 100755 asnes/chip/superfx/serialization.cpp create mode 100755 asnes/chip/superfx/superfx.cpp create mode 100755 asnes/chip/superfx/superfx.hpp create mode 100755 asnes/chip/superfx/timing/timing.cpp create mode 100755 asnes/chip/superfx/timing/timing.hpp create mode 100755 asnes/chip/supergameboy/serialization.cpp create mode 100755 asnes/chip/supergameboy/supergameboy.cpp create mode 100755 asnes/chip/supergameboy/supergameboy.hpp create mode 100755 asnes/config/config.cpp create mode 100755 asnes/config/config.hpp create mode 100755 asnes/cpu/core/algorithms.cpp create mode 100755 asnes/cpu/core/core.cpp create mode 100755 asnes/cpu/core/core.hpp create mode 100755 asnes/cpu/core/disassembler/disassembler.cpp create mode 100755 asnes/cpu/core/disassembler/disassembler.hpp create mode 100755 asnes/cpu/core/memory.hpp create mode 100755 asnes/cpu/core/opcode_misc.cpp create mode 100755 asnes/cpu/core/opcode_pc.cpp create mode 100755 asnes/cpu/core/opcode_read.cpp create mode 100755 asnes/cpu/core/opcode_rmw.cpp create mode 100755 asnes/cpu/core/opcode_write.cpp create mode 100755 asnes/cpu/core/registers.hpp create mode 100755 asnes/cpu/core/serialization.cpp create mode 100755 asnes/cpu/core/table.cpp create mode 100755 asnes/cpu/cpu.cpp create mode 100755 asnes/cpu/cpu.hpp create mode 100755 asnes/cpu/debugger/cpu-debugger.cpp create mode 100755 asnes/cpu/debugger/cpu-debugger.hpp create mode 100755 asnes/cpu/debugger/debugger.cpp create mode 100755 asnes/cpu/debugger/debugger.hpp create mode 100755 asnes/cpu/dma/dma.cpp create mode 100755 asnes/cpu/dma/dma.hpp create mode 100755 asnes/cpu/memory/memory.cpp create mode 100755 asnes/cpu/memory/memory.hpp create mode 100755 asnes/cpu/mmio/mmio.cpp create mode 100755 asnes/cpu/mmio/mmio.hpp create mode 100755 asnes/cpu/serialization.cpp create mode 100755 asnes/cpu/timing/irq.cpp create mode 100755 asnes/cpu/timing/joypad.cpp create mode 100755 asnes/cpu/timing/timing.cpp create mode 100755 asnes/cpu/timing/timing.hpp create mode 100755 asnes/debugger/debugger.cpp create mode 100755 asnes/debugger/debugger.hpp create mode 100755 asnes/dsp/brr.cpp create mode 100755 asnes/dsp/counter.cpp create mode 100755 asnes/dsp/debugger/debugger.cpp create mode 100755 asnes/dsp/debugger/debugger.hpp create mode 100755 asnes/dsp/debugger/dsp-debugger.cpp create mode 100755 asnes/dsp/debugger/dsp-debugger.hpp create mode 100755 asnes/dsp/dsp.cpp create mode 100755 asnes/dsp/dsp.hpp create mode 100755 asnes/dsp/echo.cpp create mode 100755 asnes/dsp/envelope.cpp create mode 100755 asnes/dsp/gaussian.cpp create mode 100755 asnes/dsp/misc.cpp create mode 100755 asnes/dsp/serialization.cpp create mode 100755 asnes/dsp/voice.cpp create mode 100755 asnes/input/input.cpp create mode 100755 asnes/input/input.hpp create mode 100755 asnes/interface/interface.hpp create mode 100755 asnes/libsnes/libsnes.cpp create mode 100755 asnes/libsnes/libsnes.hpp create mode 100755 asnes/memory/memory-inline.hpp create mode 100755 asnes/memory/memory.cpp create mode 100755 asnes/memory/memory.hpp create mode 100755 asnes/memory/serialization.cpp create mode 100755 asnes/ppu/background/background.cpp create mode 100755 asnes/ppu/background/background.hpp create mode 100755 asnes/ppu/background/mode7.cpp create mode 100755 asnes/ppu/counter/counter-inline.hpp create mode 100755 asnes/ppu/counter/counter.hpp create mode 100755 asnes/ppu/debugger/debugger.cpp create mode 100755 asnes/ppu/debugger/debugger.hpp create mode 100755 asnes/ppu/debugger/ppu-debugger.cpp create mode 100755 asnes/ppu/debugger/ppu-debugger.hpp create mode 100755 asnes/ppu/mmio/mmio.cpp create mode 100755 asnes/ppu/mmio/mmio.hpp create mode 100755 asnes/ppu/ppu.cpp create mode 100755 asnes/ppu/ppu.hpp create mode 100755 asnes/ppu/screen/screen.cpp create mode 100755 asnes/ppu/screen/screen.hpp create mode 100755 asnes/ppu/serialization.cpp create mode 100755 asnes/ppu/sprite/list.cpp create mode 100755 asnes/ppu/sprite/sprite.cpp create mode 100755 asnes/ppu/sprite/sprite.hpp create mode 100755 asnes/ppu/window/window.cpp create mode 100755 asnes/ppu/window/window.hpp create mode 100755 asnes/scheduler/scheduler.cpp create mode 100755 asnes/scheduler/scheduler.hpp create mode 100755 asnes/smp/core/algorithms.cpp create mode 100755 asnes/smp/core/core.cpp create mode 100755 asnes/smp/core/core.hpp create mode 100755 asnes/smp/core/disassembler/disassembler.cpp create mode 100755 asnes/smp/core/disassembler/disassembler.hpp create mode 100755 asnes/smp/core/memory.hpp create mode 100755 asnes/smp/core/opcode_misc.cpp create mode 100755 asnes/smp/core/opcode_mov.cpp create mode 100755 asnes/smp/core/opcode_pc.cpp create mode 100755 asnes/smp/core/opcode_read.cpp create mode 100755 asnes/smp/core/opcode_rmw.cpp create mode 100755 asnes/smp/core/registers.hpp create mode 100755 asnes/smp/core/serialization.cpp create mode 100755 asnes/smp/core/table.cpp create mode 100755 asnes/smp/debugger/debugger.cpp create mode 100755 asnes/smp/debugger/debugger.hpp create mode 100755 asnes/smp/debugger/smp-debugger.cpp create mode 100755 asnes/smp/debugger/smp-debugger.hpp create mode 100755 asnes/smp/iplrom.cpp create mode 100755 asnes/smp/memory/memory.cpp create mode 100755 asnes/smp/memory/memory.hpp create mode 100755 asnes/smp/serialization.cpp create mode 100755 asnes/smp/smp.cpp create mode 100755 asnes/smp/smp.hpp create mode 100755 asnes/smp/timing/timing.cpp create mode 100755 asnes/smp/timing/timing.hpp create mode 100755 asnes/snes.hpp create mode 100755 asnes/system/serialization.cpp create mode 100755 asnes/system/system.cpp create mode 100755 asnes/system/system.hpp create mode 100755 asnes/video/video.cpp create mode 100755 asnes/video/video.hpp create mode 100755 bsnes/Makefile create mode 100755 bsnes/audio/audio.cpp create mode 100755 bsnes/audio/audio.hpp create mode 100755 bsnes/cartridge/cartridge.cpp create mode 100755 bsnes/cartridge/cartridge.hpp create mode 100755 bsnes/cartridge/serialization.cpp create mode 100755 bsnes/cartridge/xml.cpp create mode 100755 bsnes/cheat/cheat-inline.hpp create mode 100755 bsnes/cheat/cheat.cpp create mode 100755 bsnes/cheat/cheat.hpp create mode 100755 bsnes/chip/bsx/bsx.cpp create mode 100755 bsnes/chip/bsx/bsx.hpp create mode 100755 bsnes/chip/bsx/bsx_base.cpp create mode 100755 bsnes/chip/bsx/bsx_cart.cpp create mode 100755 bsnes/chip/bsx/bsx_flash.cpp create mode 100755 bsnes/chip/chip.hpp create mode 100755 bsnes/chip/cx4/cx4.cpp create mode 100755 bsnes/chip/cx4/cx4.hpp create mode 100755 bsnes/chip/cx4/data.cpp create mode 100755 bsnes/chip/cx4/functions.cpp create mode 100755 bsnes/chip/cx4/oam.cpp create mode 100755 bsnes/chip/cx4/opcodes.cpp create mode 100755 bsnes/chip/cx4/serialization.cpp create mode 100755 bsnes/chip/dsp1/dsp1.cpp create mode 100755 bsnes/chip/dsp1/dsp1.hpp create mode 100755 bsnes/chip/dsp1/dsp1emu.cpp create mode 100755 bsnes/chip/dsp1/dsp1emu.hpp create mode 100755 bsnes/chip/dsp1/serialization.cpp create mode 100755 bsnes/chip/dsp2/dsp2.cpp create mode 100755 bsnes/chip/dsp2/dsp2.hpp create mode 100755 bsnes/chip/dsp2/opcodes.cpp create mode 100755 bsnes/chip/dsp2/serialization.cpp create mode 100755 bsnes/chip/dsp3/dsp3.cpp create mode 100755 bsnes/chip/dsp3/dsp3.hpp create mode 100755 bsnes/chip/dsp3/dsp3emu.c create mode 100755 bsnes/chip/dsp4/dsp4.cpp create mode 100755 bsnes/chip/dsp4/dsp4.hpp create mode 100755 bsnes/chip/dsp4/dsp4emu.c create mode 100755 bsnes/chip/dsp4/dsp4emu.h create mode 100755 bsnes/chip/msu1/msu1.cpp create mode 100755 bsnes/chip/msu1/msu1.hpp create mode 100755 bsnes/chip/msu1/serialization.cpp create mode 100755 bsnes/chip/obc1/obc1.cpp create mode 100755 bsnes/chip/obc1/obc1.hpp create mode 100755 bsnes/chip/obc1/serialization.cpp create mode 100755 bsnes/chip/sa1/bus/bus.cpp create mode 100755 bsnes/chip/sa1/bus/bus.hpp create mode 100755 bsnes/chip/sa1/dma/dma.cpp create mode 100755 bsnes/chip/sa1/dma/dma.hpp create mode 100755 bsnes/chip/sa1/memory/memory.cpp create mode 100755 bsnes/chip/sa1/memory/memory.hpp create mode 100755 bsnes/chip/sa1/mmio/mmio.cpp create mode 100755 bsnes/chip/sa1/mmio/mmio.hpp create mode 100755 bsnes/chip/sa1/sa1.cpp create mode 100755 bsnes/chip/sa1/sa1.hpp create mode 100755 bsnes/chip/sa1/serialization.cpp create mode 100755 bsnes/chip/sdd1/sdd1.cpp create mode 100755 bsnes/chip/sdd1/sdd1.hpp create mode 100755 bsnes/chip/sdd1/sdd1emu.cpp create mode 100755 bsnes/chip/sdd1/sdd1emu.hpp create mode 100755 bsnes/chip/sdd1/serialization.cpp create mode 100755 bsnes/chip/serial/serial.cpp create mode 100755 bsnes/chip/serial/serial.hpp create mode 100755 bsnes/chip/serial/serialization.cpp create mode 100755 bsnes/chip/spc7110/decomp.cpp create mode 100755 bsnes/chip/spc7110/decomp.hpp create mode 100755 bsnes/chip/spc7110/serialization.cpp create mode 100755 bsnes/chip/spc7110/spc7110.cpp create mode 100755 bsnes/chip/spc7110/spc7110.hpp create mode 100755 bsnes/chip/srtc/serialization.cpp create mode 100755 bsnes/chip/srtc/srtc.cpp create mode 100755 bsnes/chip/srtc/srtc.hpp create mode 100755 bsnes/chip/st0010/data.hpp create mode 100755 bsnes/chip/st0010/opcodes.cpp create mode 100755 bsnes/chip/st0010/serialization.cpp create mode 100755 bsnes/chip/st0010/st0010.cpp create mode 100755 bsnes/chip/st0010/st0010.hpp create mode 100755 bsnes/chip/st0011/st0011.cpp create mode 100755 bsnes/chip/st0011/st0011.hpp create mode 100755 bsnes/chip/st0018/st0018.cpp create mode 100755 bsnes/chip/st0018/st0018.hpp create mode 100755 bsnes/chip/superfx/bus/bus.cpp create mode 100755 bsnes/chip/superfx/bus/bus.hpp create mode 100755 bsnes/chip/superfx/core/core.cpp create mode 100755 bsnes/chip/superfx/core/core.hpp create mode 100755 bsnes/chip/superfx/core/opcode_table.cpp create mode 100755 bsnes/chip/superfx/core/opcodes.cpp create mode 100755 bsnes/chip/superfx/core/registers.hpp create mode 100755 bsnes/chip/superfx/disasm/disasm.cpp create mode 100755 bsnes/chip/superfx/disasm/disasm.hpp create mode 100755 bsnes/chip/superfx/memory/memory.cpp create mode 100755 bsnes/chip/superfx/memory/memory.hpp create mode 100755 bsnes/chip/superfx/mmio/mmio.cpp create mode 100755 bsnes/chip/superfx/mmio/mmio.hpp create mode 100755 bsnes/chip/superfx/serialization.cpp create mode 100755 bsnes/chip/superfx/superfx.cpp create mode 100755 bsnes/chip/superfx/superfx.hpp create mode 100755 bsnes/chip/superfx/timing/timing.cpp create mode 100755 bsnes/chip/superfx/timing/timing.hpp create mode 100755 bsnes/chip/supergameboy/serialization.cpp create mode 100755 bsnes/chip/supergameboy/supergameboy.cpp create mode 100755 bsnes/chip/supergameboy/supergameboy.hpp create mode 100755 bsnes/config/config.cpp create mode 100755 bsnes/config/config.hpp create mode 100755 bsnes/cpu/core/algorithms.cpp create mode 100755 bsnes/cpu/core/core.cpp create mode 100755 bsnes/cpu/core/core.hpp create mode 100755 bsnes/cpu/core/disassembler/disassembler.cpp create mode 100755 bsnes/cpu/core/disassembler/disassembler.hpp create mode 100755 bsnes/cpu/core/memory.hpp create mode 100755 bsnes/cpu/core/opcode_misc.cpp create mode 100755 bsnes/cpu/core/opcode_pc.cpp create mode 100755 bsnes/cpu/core/opcode_read.cpp create mode 100755 bsnes/cpu/core/opcode_rmw.cpp create mode 100755 bsnes/cpu/core/opcode_write.cpp create mode 100755 bsnes/cpu/core/registers.hpp create mode 100755 bsnes/cpu/core/serialization.cpp create mode 100755 bsnes/cpu/core/table.cpp create mode 100755 bsnes/cpu/cpu-debugger.cpp create mode 100755 bsnes/cpu/cpu-debugger.hpp create mode 100755 bsnes/cpu/cpu.cpp create mode 100755 bsnes/cpu/cpu.hpp create mode 100755 bsnes/cpu/scpu/debugger/debugger.cpp create mode 100755 bsnes/cpu/scpu/debugger/debugger.hpp create mode 100755 bsnes/cpu/scpu/dma/dma.cpp create mode 100755 bsnes/cpu/scpu/dma/dma.hpp create mode 100755 bsnes/cpu/scpu/memory/memory.cpp create mode 100755 bsnes/cpu/scpu/memory/memory.hpp create mode 100755 bsnes/cpu/scpu/mmio/mmio.cpp create mode 100755 bsnes/cpu/scpu/mmio/mmio.hpp create mode 100755 bsnes/cpu/scpu/scpu.cpp create mode 100755 bsnes/cpu/scpu/scpu.hpp create mode 100755 bsnes/cpu/scpu/serialization.cpp create mode 100755 bsnes/cpu/scpu/timing/irq.cpp create mode 100755 bsnes/cpu/scpu/timing/joypad.cpp create mode 100755 bsnes/cpu/scpu/timing/timing.cpp create mode 100755 bsnes/cpu/scpu/timing/timing.hpp create mode 100755 bsnes/cpu/synchronization.hpp create mode 100755 bsnes/debugger/debugger.cpp create mode 100755 bsnes/debugger/debugger.hpp create mode 100755 bsnes/dsp/adsp/adsp.cpp create mode 100755 bsnes/dsp/adsp/adsp.hpp create mode 100755 bsnes/dsp/adsp/debugger/debugger.cpp create mode 100755 bsnes/dsp/adsp/debugger/debugger.hpp create mode 100755 bsnes/dsp/adsp/serialization.cpp create mode 100755 bsnes/dsp/adsp/tables.cpp create mode 100755 bsnes/dsp/dsp-debugger.cpp create mode 100755 bsnes/dsp/dsp-debugger.hpp create mode 100755 bsnes/dsp/dsp.cpp create mode 100755 bsnes/dsp/dsp.hpp create mode 100755 bsnes/dsp/sdsp/brr.cpp create mode 100755 bsnes/dsp/sdsp/counter.cpp create mode 100755 bsnes/dsp/sdsp/debugger/debugger.cpp create mode 100755 bsnes/dsp/sdsp/debugger/debugger.hpp create mode 100755 bsnes/dsp/sdsp/echo.cpp create mode 100755 bsnes/dsp/sdsp/envelope.cpp create mode 100755 bsnes/dsp/sdsp/gaussian.cpp create mode 100755 bsnes/dsp/sdsp/misc.cpp create mode 100755 bsnes/dsp/sdsp/sdsp.cpp create mode 100755 bsnes/dsp/sdsp/sdsp.hpp create mode 100755 bsnes/dsp/sdsp/serialization.cpp create mode 100755 bsnes/dsp/sdsp/voice.cpp create mode 100755 bsnes/dsp/synchronization.hpp create mode 100755 bsnes/input/input.cpp create mode 100755 bsnes/input/input.hpp create mode 100755 bsnes/interface/interface.hpp create mode 100755 bsnes/libsnes/libsnes.cpp create mode 100755 bsnes/libsnes/libsnes.hpp create mode 100755 bsnes/memory/memory-inline.hpp create mode 100755 bsnes/memory/memory.cpp create mode 100755 bsnes/memory/memory.hpp create mode 100755 bsnes/memory/smemory/serialization.cpp create mode 100755 bsnes/memory/smemory/smemory.cpp create mode 100755 bsnes/memory/smemory/smemory.hpp create mode 100755 bsnes/ppu/bppu/bppu.cpp create mode 100755 bsnes/ppu/bppu/bppu.hpp create mode 100755 bsnes/ppu/bppu/debugger/debugger.cpp create mode 100755 bsnes/ppu/bppu/debugger/debugger.hpp create mode 100755 bsnes/ppu/bppu/debugger/render.cpp create mode 100755 bsnes/ppu/bppu/memory/memory.cpp create mode 100755 bsnes/ppu/bppu/memory/memory.hpp create mode 100755 bsnes/ppu/bppu/mmio/mmio.cpp create mode 100755 bsnes/ppu/bppu/mmio/mmio.hpp create mode 100755 bsnes/ppu/bppu/render/addsub.cpp create mode 100755 bsnes/ppu/bppu/render/bg.cpp create mode 100755 bsnes/ppu/bppu/render/cache.cpp create mode 100755 bsnes/ppu/bppu/render/line.cpp create mode 100755 bsnes/ppu/bppu/render/mode7.cpp create mode 100755 bsnes/ppu/bppu/render/oam.cpp create mode 100755 bsnes/ppu/bppu/render/render.cpp create mode 100755 bsnes/ppu/bppu/render/render.hpp create mode 100755 bsnes/ppu/bppu/render/windows.cpp create mode 100755 bsnes/ppu/bppu/serialization.cpp create mode 100755 bsnes/ppu/ppu-debugger.cpp create mode 100755 bsnes/ppu/ppu-debugger.hpp create mode 100755 bsnes/ppu/ppu-inline.hpp create mode 100755 bsnes/ppu/ppu.cpp create mode 100755 bsnes/ppu/ppu.hpp create mode 100755 bsnes/ppu/serialization.cpp create mode 100755 bsnes/ppu/sppu/background/background.cpp create mode 100755 bsnes/ppu/sppu/background/background.hpp create mode 100755 bsnes/ppu/sppu/background/mode7.cpp create mode 100755 bsnes/ppu/sppu/debugger/debugger.cpp create mode 100755 bsnes/ppu/sppu/debugger/debugger.hpp create mode 100755 bsnes/ppu/sppu/mmio/mmio.cpp create mode 100755 bsnes/ppu/sppu/mmio/mmio.hpp create mode 100755 bsnes/ppu/sppu/screen/screen.cpp create mode 100755 bsnes/ppu/sppu/screen/screen.hpp create mode 100755 bsnes/ppu/sppu/serialization.cpp create mode 100755 bsnes/ppu/sppu/sppu.cpp create mode 100755 bsnes/ppu/sppu/sppu.hpp create mode 100755 bsnes/ppu/sppu/sprite/list.cpp create mode 100755 bsnes/ppu/sppu/sprite/sprite.cpp create mode 100755 bsnes/ppu/sppu/sprite/sprite.hpp create mode 100755 bsnes/ppu/sppu/window/window.cpp create mode 100755 bsnes/ppu/sppu/window/window.hpp create mode 100755 bsnes/ppu/synchronization.hpp create mode 100755 bsnes/scheduler/scheduler.cpp create mode 100755 bsnes/scheduler/scheduler.hpp create mode 100755 bsnes/smp/core/algorithms.cpp create mode 100755 bsnes/smp/core/core.cpp create mode 100755 bsnes/smp/core/core.hpp create mode 100755 bsnes/smp/core/disassembler/disassembler.cpp create mode 100755 bsnes/smp/core/disassembler/disassembler.hpp create mode 100755 bsnes/smp/core/memory.hpp create mode 100755 bsnes/smp/core/opcode_misc.cpp create mode 100755 bsnes/smp/core/opcode_mov.cpp create mode 100755 bsnes/smp/core/opcode_pc.cpp create mode 100755 bsnes/smp/core/opcode_read.cpp create mode 100755 bsnes/smp/core/opcode_rmw.cpp create mode 100755 bsnes/smp/core/registers.hpp create mode 100755 bsnes/smp/core/serialization.cpp create mode 100755 bsnes/smp/core/table.cpp create mode 100755 bsnes/smp/smp-debugger.cpp create mode 100755 bsnes/smp/smp-debugger.hpp create mode 100755 bsnes/smp/smp.cpp create mode 100755 bsnes/smp/smp.hpp create mode 100755 bsnes/smp/ssmp/debugger/debugger.cpp create mode 100755 bsnes/smp/ssmp/debugger/debugger.hpp create mode 100755 bsnes/smp/ssmp/memory/memory.cpp create mode 100755 bsnes/smp/ssmp/memory/memory.hpp create mode 100755 bsnes/smp/ssmp/serialization.cpp create mode 100755 bsnes/smp/ssmp/ssmp.cpp create mode 100755 bsnes/smp/ssmp/ssmp.hpp create mode 100755 bsnes/smp/ssmp/timing/timing.cpp create mode 100755 bsnes/smp/ssmp/timing/timing.hpp create mode 100755 bsnes/smp/synchronization.hpp create mode 100755 bsnes/snes.hpp create mode 100755 bsnes/system/serialization.cpp create mode 100755 bsnes/system/system.cpp create mode 100755 bsnes/system/system.hpp create mode 100755 bsnes/video/video.cpp create mode 100755 bsnes/video/video.hpp create mode 100755 libco/amd64.c create mode 100755 libco/fiber.c create mode 100755 libco/libco.c create mode 100755 libco/libco.h create mode 100755 libco/ppc-elf.c create mode 100755 libco/ppc.s create mode 100755 libco/ppc64.s create mode 100755 libco/sjlj.c create mode 100755 libco/ucontext.c create mode 100755 libco/x86.c create mode 100755 nall/Makefile create mode 100755 nall/algorithm.hpp create mode 100755 nall/any.hpp create mode 100755 nall/array.hpp create mode 100755 nall/base64.hpp create mode 100755 nall/bit.hpp create mode 100755 nall/concept.hpp create mode 100755 nall/config.hpp create mode 100755 nall/crc32.hpp create mode 100755 nall/detect.hpp create mode 100755 nall/dictionary.hpp create mode 100755 nall/dl.hpp create mode 100755 nall/endian.hpp create mode 100755 nall/file.hpp create mode 100755 nall/filemap.hpp create mode 100755 nall/foreach.hpp create mode 100755 nall/function.hpp create mode 100755 nall/input.hpp create mode 100755 nall/lzss.hpp create mode 100755 nall/moduloarray.hpp create mode 100755 nall/platform.hpp create mode 100755 nall/priorityqueue.hpp create mode 100755 nall/property.hpp create mode 100755 nall/qt/Makefile create mode 100755 nall/qt/check-action.moc.hpp create mode 100755 nall/qt/concept.hpp create mode 100755 nall/qt/file-dialog.moc.hpp create mode 100755 nall/qt/hex-editor.moc.hpp create mode 100755 nall/qt/radio-action.moc.hpp create mode 100755 nall/qt/window.moc.hpp create mode 100755 nall/random.hpp create mode 100755 nall/serial.hpp create mode 100755 nall/serializer.hpp create mode 100755 nall/sha256.hpp create mode 100755 nall/snes/info.hpp create mode 100755 nall/sort.hpp create mode 100755 nall/static.hpp create mode 100755 nall/stdint.hpp create mode 100755 nall/string.hpp create mode 100755 nall/string/base.hpp create mode 100755 nall/string/bsv.hpp create mode 100755 nall/string/cast.hpp create mode 100755 nall/string/compare.hpp create mode 100755 nall/string/convert.hpp create mode 100755 nall/string/core.hpp create mode 100755 nall/string/filename.hpp create mode 100755 nall/string/match.hpp create mode 100755 nall/string/math.hpp create mode 100755 nall/string/replace.hpp create mode 100755 nall/string/split.hpp create mode 100755 nall/string/strl.hpp create mode 100755 nall/string/strpos.hpp create mode 100755 nall/string/trim.hpp create mode 100755 nall/string/utility.hpp create mode 100755 nall/string/variadic.hpp create mode 100755 nall/string/xml.hpp create mode 100755 nall/ups.hpp create mode 100755 nall/utf8.hpp create mode 100755 nall/utility.hpp create mode 100755 nall/varint.hpp create mode 100755 nall/vector.hpp create mode 100755 qt/Makefile create mode 100755 qt/application/application.cpp create mode 100755 qt/application/application.moc.hpp create mode 100755 qt/application/init.cpp create mode 100755 qt/base/about.cpp create mode 100755 qt/base/about.moc.hpp create mode 100755 qt/base/base.cpp create mode 100755 qt/base/filebrowser.cpp create mode 100755 qt/base/filebrowser.moc.hpp create mode 100755 qt/base/htmlviewer.cpp create mode 100755 qt/base/htmlviewer.moc.hpp create mode 100755 qt/base/loader.cpp create mode 100755 qt/base/loader.moc.hpp create mode 100755 qt/base/main.cpp create mode 100755 qt/base/main.moc.hpp create mode 100755 qt/cartridge/cartridge.cpp create mode 100755 qt/cartridge/cartridge.hpp create mode 100755 qt/config.cpp create mode 100755 qt/config.hpp create mode 100755 qt/data/bsnes.Manifest create mode 100755 qt/data/bsnes.desktop create mode 100755 qt/data/bsnes.ico create mode 100755 qt/data/bsnes.png create mode 100755 qt/data/cheats.xml create mode 100755 qt/data/documentation.html create mode 100755 qt/data/icons-16x16/accessories-text-editor.png create mode 100755 qt/data/icons-16x16/applications-multimedia.png create mode 100755 qt/data/icons-16x16/appointment-new.png create mode 100755 qt/data/icons-16x16/audio-volume-high.png create mode 100755 qt/data/icons-16x16/document-open.png create mode 100755 qt/data/icons-16x16/folder-new.png create mode 100755 qt/data/icons-16x16/folder.png create mode 100755 qt/data/icons-16x16/go-up.png create mode 100755 qt/data/icons-16x16/help-browser.png create mode 100755 qt/data/icons-16x16/image-x-generic.png create mode 100755 qt/data/icons-16x16/input-gaming.png create mode 100755 qt/data/icons-16x16/item-check-off.png create mode 100755 qt/data/icons-16x16/item-check-on.png create mode 100755 qt/data/icons-16x16/item-radio-off.png create mode 100755 qt/data/icons-16x16/item-radio-on.png create mode 100755 qt/data/icons-16x16/media-playback-start.png create mode 100755 qt/data/icons-16x16/media-playback-stop.png create mode 100755 qt/data/icons-16x16/media-record.png create mode 100755 qt/data/icons-16x16/preferences-desktop.png create mode 100755 qt/data/icons-16x16/preferences-system.png create mode 100755 qt/data/icons-16x16/process-stop.png create mode 100755 qt/data/icons-16x16/system-file-manager.png create mode 100755 qt/data/icons-16x16/system-search.png create mode 100755 qt/data/icons-16x16/text-x-generic.png create mode 100755 qt/data/icons-16x16/utilities-terminal.png create mode 100755 qt/data/icons-16x16/video-display.png create mode 100755 qt/data/icons-16x16/view-refresh.png create mode 100755 qt/data/license.html create mode 100755 qt/data/logo.png create mode 100755 qt/debugger/debugger.cpp create mode 100755 qt/debugger/debugger.moc.hpp create mode 100755 qt/debugger/misc/debugger-options.cpp create mode 100755 qt/debugger/misc/debugger-options.moc.hpp create mode 100755 qt/debugger/ppu/cgram-viewer.cpp create mode 100755 qt/debugger/ppu/cgram-viewer.moc.hpp create mode 100755 qt/debugger/ppu/layer-toggle.cpp create mode 100755 qt/debugger/ppu/layer-toggle.moc.hpp create mode 100755 qt/debugger/ppu/oam-viewer.cpp create mode 100755 qt/debugger/ppu/oam-viewer.moc.hpp create mode 100755 qt/debugger/ppu/vram-viewer.cpp create mode 100755 qt/debugger/ppu/vram-viewer.moc.hpp create mode 100755 qt/debugger/tools/breakpoint.cpp create mode 100755 qt/debugger/tools/breakpoint.moc.hpp create mode 100755 qt/debugger/tools/disassembler.cpp create mode 100755 qt/debugger/tools/disassembler.moc.hpp create mode 100755 qt/debugger/tools/memory.cpp create mode 100755 qt/debugger/tools/memory.moc.hpp create mode 100755 qt/debugger/tools/properties.cpp create mode 100755 qt/debugger/tools/properties.moc.hpp create mode 100755 qt/debugger/tracer.cpp create mode 100755 qt/debugger/tracer.moc.hpp create mode 100755 qt/input/controller.cpp create mode 100755 qt/input/controller.hpp create mode 100755 qt/input/input.cpp create mode 100755 qt/input/input.hpp create mode 100755 qt/input/userinterface-emulationspeed.cpp create mode 100755 qt/input/userinterface-general.cpp create mode 100755 qt/input/userinterface-states.cpp create mode 100755 qt/input/userinterface-system.cpp create mode 100755 qt/input/userinterface-videosettings.cpp create mode 100755 qt/input/userinterface.hpp create mode 100755 qt/interface.cpp create mode 100755 qt/interface.hpp create mode 100755 qt/link/filter.cpp create mode 100755 qt/link/filter.hpp create mode 100755 qt/link/reader.cpp create mode 100755 qt/link/reader.hpp create mode 100755 qt/main.cpp create mode 100755 qt/movie/movie.cpp create mode 100755 qt/movie/movie.hpp create mode 100755 qt/platform/platform_osx.cpp create mode 100755 qt/platform/platform_win.cpp create mode 100755 qt/platform/platform_x.cpp create mode 100755 qt/resource/resource.qrc create mode 100755 qt/resource/resource.rc create mode 100755 qt/settings/advanced.cpp create mode 100755 qt/settings/advanced.moc.hpp create mode 100755 qt/settings/audio.cpp create mode 100755 qt/settings/audio.moc.hpp create mode 100755 qt/settings/input.cpp create mode 100755 qt/settings/input.moc.hpp create mode 100755 qt/settings/paths.cpp create mode 100755 qt/settings/paths.moc.hpp create mode 100755 qt/settings/settings.cpp create mode 100755 qt/settings/settings.moc.hpp create mode 100755 qt/settings/video.cpp create mode 100755 qt/settings/video.moc.hpp create mode 100755 qt/state/state.cpp create mode 100755 qt/state/state.hpp create mode 100755 qt/tools/cheateditor.cpp create mode 100755 qt/tools/cheateditor.moc.hpp create mode 100755 qt/tools/cheatfinder.cpp create mode 100755 qt/tools/cheatfinder.moc.hpp create mode 100755 qt/tools/statemanager.cpp create mode 100755 qt/tools/statemanager.moc.hpp create mode 100755 qt/tools/tools.cpp create mode 100755 qt/tools/tools.moc.hpp create mode 100755 qt/ui-base.hpp create mode 100755 qt/utility/system-state.cpp create mode 100755 qt/utility/utility.cpp create mode 100755 qt/utility/utility.hpp create mode 100755 qt/utility/window.cpp create mode 100755 ruby/audio.hpp create mode 100755 ruby/audio/alsa.cpp create mode 100755 ruby/audio/ao.cpp create mode 100755 ruby/audio/directsound.cpp create mode 100755 ruby/audio/openal.cpp create mode 100755 ruby/audio/oss.cpp create mode 100755 ruby/audio/pulseaudio.cpp create mode 100755 ruby/audio/pulseaudiosimple.cpp create mode 100755 ruby/input.hpp create mode 100755 ruby/input/carbon.cpp create mode 100755 ruby/input/directinput.cpp create mode 100755 ruby/input/rawinput.cpp create mode 100755 ruby/input/sdl.cpp create mode 100755 ruby/input/x.cpp create mode 100755 ruby/input/xlibkeys.hpp create mode 100755 ruby/ruby.cpp create mode 100755 ruby/ruby.hpp create mode 100755 ruby/ruby_audio.cpp create mode 100755 ruby/ruby_impl.cpp create mode 100755 ruby/video.hpp create mode 100755 ruby/video/direct3d.cpp create mode 100755 ruby/video/directdraw.cpp create mode 100755 ruby/video/gdi.cpp create mode 100755 ruby/video/glx.cpp create mode 100755 ruby/video/opengl.hpp create mode 100755 ruby/video/qtopengl.cpp create mode 100755 ruby/video/qtraster.cpp create mode 100755 ruby/video/sdl.cpp create mode 100755 ruby/video/wgl.cpp create mode 100755 ruby/video/xv.cpp create mode 100755 sync.sh diff --git a/Makefile b/Makefile new file mode 100755 index 00000000..c6ce4d07 --- /dev/null +++ b/Makefile @@ -0,0 +1,91 @@ +include nall/Makefile +snes := asnes +ui := qt + +# compiler +c := $(compiler) -std=gnu99 +cpp := $(subst cc,++,$(compiler)) -std=gnu++0x +flags := -O3 -fomit-frame-pointer -I. -I$(snes) +link := +objects := + +# profile-guided instrumentation +# flags += -fprofile-generate +# link += -lgcov + +# profile-guided optimization +# flags += -fprofile-use + +# platform +ifeq ($(platform),x) + link += -s -ldl -lX11 -lXext +else ifeq ($(platform),osx) +else ifeq ($(platform),win) + link += -mwindows +# link += -mconsole + link += -mthreads -s -luuid -lkernel32 -luser32 -lgdi32 -lshell32 + link += -enable-stdcall-fixup -Wl,-enable-auto-import -Wl,-enable-runtime-pseudo-reloc +else + unknown_platform: help; +endif + +# implicit rules +compile = \ + $(strip \ + $(if $(filter %.c,$<), \ + $(c) $(flags) $1 -c $< -o $@, \ + $(if $(filter %.cpp,$<), \ + $(cpp) $(flags) $1 -c $< -o $@ \ + ) \ + ) \ + ) + +%.o: $<; $(call compile) + +all: build; + +include $(snes)/Makefile +include $(ui)/Makefile + +objects := $(patsubst %,obj/%.o,$(objects)) + +# targets +build: ui_build $(objects) +ifeq ($(platform),osx) + test -d ../bsnes.app || mkdir -p ../bsnes.app/Contents/MacOS + $(strip $(cpp) -o ../bsnes.app/Contents/MacOS/bsnes $(objects) $(link)) +else + $(strip $(cpp) -o out/$(snes) $(objects) $(link)) +endif + +install: +ifeq ($(platform),x) + install -D -m 755 ../bsnes $(DESTDIR)$(prefix)/bin/bsnes + install -D -m 644 data/bsnes.png $(DESTDIR)$(prefix)/share/pixmaps/bsnes.png + install -D -m 644 data/bsnes.desktop $(DESTDIR)$(prefix)/share/applications/bsnes.desktop +endif + +uninstall: +ifeq ($(platform),x) + rm $(DESTDIR)$(prefix)/bin/bsnes + rm $(DESTDIR)$(prefix)/share/pixmaps/bsnes.png + rm $(DESTDIR)$(prefix)/share/applications/bsnes.desktop +endif + +clean: ui_clean + -@$(call delete,obj/*.o) + -@$(call delete,obj/*.a) + -@$(call delete,obj/*.so) + -@$(call delete,obj/*.dylib) + -@$(call delete,obj/*.dll) + -@$(call delete,*.res) + -@$(call delete,*.pgd) + -@$(call delete,*.pgc) + -@$(call delete,*.ilk) + -@$(call delete,*.pdb) + -@$(call delete,*.manifest) + +archive-all: + tar -cjf snes-`date +%Y%m%d`.tar.bz2 asnes bsnes libco nall obj out qt ruby Makefile sync.sh + +help:; diff --git a/asnes/Makefile b/asnes/Makefile new file mode 100755 index 00000000..b45ed35b --- /dev/null +++ b/asnes/Makefile @@ -0,0 +1,77 @@ +snes_objects := libco +snes_objects += snes-system +snes_objects += snes-cartridge snes-cheat +snes_objects += snes-memory snes-cpucore snes-cpu snes-smpcore snes-smp snes-dsp snes-ppu +snes_objects += snes-supergameboy snes-superfx snes-sa1 +snes_objects += snes-bsx snes-srtc snes-sdd1 snes-spc7110 +snes_objects += snes-cx4 snes-dsp1 snes-dsp2 snes-dsp3 snes-dsp4 +snes_objects += snes-obc1 snes-st0010 snes-st0011 snes-st0018 +snes_objects += snes-msu1 snes-serial +objects += $(snes_objects) + +obj/libco.o : libco/libco.c libco/* +obj/libsnes.o: $(snes)/libsnes/libsnes.cpp $(snes)/libsnes/* + +obj/snes-system.o : $(snes)/system/system.cpp $(call rwildcard,$(snes)/system/) $(call rwildcard,$(snes)/video/) +obj/snes-memory.o : $(snes)/memory/memory.cpp $(snes)/memory/* +obj/snes-cpucore.o : $(snes)/cpu/core/core.cpp $(call rwildcard,$(snes)/cpu/core/) +obj/snes-cpu.o : $(snes)/cpu/cpu.cpp $(snes)/cpu/* +obj/snes-smpcore.o : $(snes)/smp/core/core.cpp $(call rwildcard,$(snes)/smp/core/) +obj/snes-smp.o : $(snes)/smp/smp.cpp $(snes)/smp/* +obj/snes-dsp.o : $(snes)/dsp/dsp.cpp $(snes)/dsp/* +obj/snes-ppu.o : $(snes)/ppu/ppu.cpp $(snes)/ppu/* +obj/snes-cartridge.o: $(snes)/cartridge/cartridge.cpp $(snes)/cartridge/* +obj/snes-cheat.o : $(snes)/cheat/cheat.cpp $(snes)/cheat/* + +obj/snes-supergameboy.o: $(snes)/chip/supergameboy/supergameboy.cpp $(call rwildcard,$(snes)/chip/supergameboy/) +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-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-cx4.o : $(snes)/chip/cx4/cx4.cpp $(snes)/chip/cx4/* +obj/snes-dsp1.o : $(snes)/chip/dsp1/dsp1.cpp $(snes)/chip/dsp1/* +obj/snes-dsp2.o : $(snes)/chip/dsp2/dsp2.cpp $(snes)/chip/dsp2/* +obj/snes-dsp3.o : $(snes)/chip/dsp3/dsp3.cpp $(snes)/chip/dsp3/* +obj/snes-dsp4.o : $(snes)/chip/dsp4/dsp4.cpp $(snes)/chip/dsp4/* +obj/snes-obc1.o : $(snes)/chip/obc1/obc1.cpp $(snes)/chip/obc1/* +obj/snes-st0010.o : $(snes)/chip/st0010/st0010.cpp $(snes)/chip/st0010/* +obj/snes-st0011.o : $(snes)/chip/st0011/st0011.cpp $(snes)/chip/st0011/* +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-serial.o : $(snes)/chip/serial/serial.cpp $(snes)/chip/serial/* + +########### +# library # +########### + +snes_objects := $(patsubst %,obj/%.o,$(snes_objects)) + +library: $(snes_objects) obj/libsnes.o +ifeq ($(platform),x) + ar rcs obj/libsnes.a $(snes_objects) obj/libsnes.o + $(cpp) -o obj/libsnes.so -shared -Wl,-soname,libsnes.so.1 $(snes_objects) obj/libsnes.o +else ifeq ($(platform),osx) + ar rcs obj/libsnes.a $(snes_objects) obj/libsnes.o + $(cpp) -o obj/libsnes.dylib -install_name @executable_path/../Libraries/libsnes.dylib -shared -dynamiclib $(snes_objects) obj/libsnes.o +else ifeq ($(platform),win) + $(cpp) -o obj/snes.dll -shared -Wl,--out-implib,libsnes.a $(snes_objects) obj/libsnes.o +endif + +library-install: +ifeq ($(platform),x) + install -D -m 755 obj/libsnes.a $(DESTDIR)$(prefix)/lib/libsnes.a + install -D -m 755 obj/libsnes.so $(DESTDIR)$(prefix)/lib/libsnes.so + ldconfig -n $(DESTDIR)$(prefix)/lib +else ifeq ($(platform),osx) + cp obj/libsnes.dylib /usr/local/lib/libsnes.dylib +endif + +library-uninstall: +ifeq ($(platform),x) + rm $(DESTDIR)$(prefix)/lib/libsnes.a + rm $(DESTDIR)$(prefix)/lib/libsnes.so +else ifeq ($(platform),osx) + rm /usr/local/lib/libsnes.dylib +endif diff --git a/asnes/audio/audio.cpp b/asnes/audio/audio.cpp new file mode 100755 index 00000000..4b2f6861 --- /dev/null +++ b/asnes/audio/audio.cpp @@ -0,0 +1,85 @@ +#ifdef SYSTEM_CPP + +Audio audio; + +void Audio::coprocessor_enable(bool state) { + coprocessor = state; + + dsp_rdoffset = cop_rdoffset = 0; + dsp_wroffset = cop_wroffset = 0; + dsp_length = cop_length = 0; + + r_sum_l = r_sum_r = 0; +} + +void Audio::coprocessor_frequency(double input_frequency) { + double output_frequency; + output_frequency = system.apu_frequency() / 768.0; + r_step = input_frequency / output_frequency; + r_frac = 0; +} + +void Audio::sample(int16 left, int16 right) { + if(coprocessor == false) { + system.interface->audio_sample(left, right); + } else { + dsp_buffer[dsp_wroffset] = ((uint16)left << 0) + ((uint16)right << 16); + dsp_wroffset = (dsp_wroffset + 1) & 32767; + dsp_length = (dsp_length + 1) & 32767; + flush(); + } +} + +void Audio::coprocessor_sample(int16 left, int16 right) { + if(r_frac >= 1.0) { + r_frac -= 1.0; + r_sum_l += left; + r_sum_r += right; + return; + } + + r_sum_l += left * r_frac; + r_sum_r += right * r_frac; + + uint16 output_left = sclamp<16>(int(r_sum_l / r_step)); + uint16 output_right = sclamp<16>(int(r_sum_r / r_step)); + + double first = 1.0 - r_frac; + r_sum_l = left * first; + r_sum_r = right * first; + r_frac = r_step - first; + + cop_buffer[cop_wroffset] = (output_left << 0) + (output_right << 16); + cop_wroffset = (cop_wroffset + 1) & 32767; + cop_length = (cop_length + 1) & 32767; + 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) & 32767; + cop_rdoffset = (cop_rdoffset + 1) & 32767; + + dsp_length--; + cop_length--; + + int dsp_left = (int16)(dsp_sample >> 0); + int dsp_right = (int16)(dsp_sample >> 16); + + int cop_left = (int16)(cop_sample >> 0); + int cop_right = (int16)(cop_sample >> 16); + + system.interface->audio_sample( + sclamp<16>((dsp_left + cop_left ) / 2), + sclamp<16>((dsp_right + cop_right) / 2) + ); + } +} + +#endif diff --git a/asnes/audio/audio.hpp b/asnes/audio/audio.hpp new file mode 100755 index 00000000..3cd522fa --- /dev/null +++ b/asnes/audio/audio.hpp @@ -0,0 +1,22 @@ +class Audio { +public: + void coprocessor_enable(bool state); + void coprocessor_frequency(double frequency); + void sample(int16 left, int16 right); + void coprocessor_sample(int16 left, int16 right); + void init(); + +private: + bool coprocessor; + uint32 dsp_buffer[32768], cop_buffer[32768]; + unsigned dsp_rdoffset, cop_rdoffset; + unsigned dsp_wroffset, cop_wroffset; + unsigned dsp_length, cop_length; + + double r_step, r_frac; + int r_sum_l, r_sum_r; + + void flush(); +}; + +extern Audio audio; diff --git a/asnes/cartridge/cartridge.cpp b/asnes/cartridge/cartridge.cpp new file mode 100755 index 00000000..c1a41e4f --- /dev/null +++ b/asnes/cartridge/cartridge.cpp @@ -0,0 +1,144 @@ +#include + +#include +#include + +#define CARTRIDGE_CPP +namespace SNES { + +#include "xml.cpp" +#include "serialization.cpp" + +namespace memory { + MappedRAM cartrom, cartram, cartrtc; + MappedRAM bsxflash, bsxram, bsxpram; + MappedRAM stArom, stAram; + MappedRAM stBrom, stBram; + MappedRAM gbrom, gbram, gbrtc; +}; + +Cartridge cartridge; + +void Cartridge::load(Mode cartridge_mode, const lstring &xml_list) { + mode = cartridge_mode; + region = Region::NTSC; + ram_size = 0; + spc7110_data_rom_offset = 0x100000; + supergameboy_version = SuperGameBoyVersion::Version1; + supergameboy_ram_size = 0; + supergameboy_rtc_size = 0; + serial_baud_rate = 57600; + + 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_st0010 = false; + has_st0011 = false; + has_st0018 = false; + has_msu1 = false; + has_serial = false; + + parse_xml(xml_list); + + if(ram_size > 0) { + memory::cartram.map(allocate(ram_size, 0xff), ram_size); + } + + if(has_srtc || has_spc7110rtc) { + memory::cartrtc.map(allocate(20, 0xff), 20); + } + + if(mode == Mode::Bsx) { + memory::bsxram.map (allocate( 32 * 1024, 0xff), 32 * 1024); + memory::bsxpram.map(allocate(512 * 1024, 0xff), 512 * 1024); + } + + if(mode == Mode::SufamiTurbo) { + if(memory::stArom.data()) memory::stAram.map(allocate(128 * 1024, 0xff), 128 * 1024); + if(memory::stBrom.data()) memory::stBram.map(allocate(128 * 1024, 0xff), 128 * 1024); + } + + if(mode == Mode::SuperGameBoy) { + if(memory::gbrom.data()) { + if(supergameboy_ram_size) memory::gbram.map(allocate(supergameboy_ram_size, 0xff), supergameboy_ram_size); + if(supergameboy_rtc_size) memory::gbrtc.map(allocate(supergameboy_rtc_size, 0x00), supergameboy_rtc_size); + } + } + + memory::cartrom.write_protect(true); + memory::cartram.write_protect(false); + memory::cartrtc.write_protect(false); + memory::bsxflash.write_protect(true); + memory::bsxram.write_protect(false); + memory::bsxpram.write_protect(false); + memory::stArom.write_protect(true); + memory::stAram.write_protect(false); + memory::stBrom.write_protect(true); + memory::stBram.write_protect(false); + memory::gbrom.write_protect(true); + memory::gbram.write_protect(false); + memory::gbrtc.write_protect(false); + + unsigned checksum = ~0; foreach(n, memory::cartrom ) checksum = crc32_adjust(checksum, n); + if(memory::bsxflash.size() != 0 && memory::bsxflash.size() != ~0) foreach(n, memory::bsxflash) checksum = crc32_adjust(checksum, n); + if(memory::stArom.size() != 0 && memory::stArom.size() != ~0) foreach(n, memory::stArom ) checksum = crc32_adjust(checksum, n); + if(memory::stBrom.size() != 0 && memory::stBrom.size() != ~0) foreach(n, memory::stBrom ) checksum = crc32_adjust(checksum, n); + if(memory::gbrom.size() != 0 && memory::gbrom.size() != ~0) foreach(n, memory::gbrom ) checksum = crc32_adjust(checksum, n); + crc32 = ~checksum; + + sha256_ctx sha; + uint8_t shahash[32]; + sha256_init(&sha); + sha256_chunk(&sha, memory::cartrom.data(), memory::cartrom.size()); + sha256_final(&sha); + sha256_hash(&sha, shahash); + + string hash; + foreach(n, shahash) hash << strhex<2>(n); + sha256 = hash; + + bus.load_cart(); + system.serialize_init(); + loaded = true; +} + +void Cartridge::unload() { + memory::cartrom.reset(); + memory::cartram.reset(); + memory::cartrtc.reset(); + memory::bsxflash.reset(); + memory::bsxram.reset(); + memory::bsxpram.reset(); + memory::stArom.reset(); + memory::stAram.reset(); + memory::stBrom.reset(); + memory::stBram.reset(); + memory::gbrom.reset(); + memory::gbram.reset(); + memory::gbrtc.reset(); + + if(loaded == false) return; + bus.unload_cart(); + loaded = false; +} + +Cartridge::Cartridge() { + loaded = false; + unload(); +} + +Cartridge::~Cartridge() { + unload(); +} + +} diff --git a/asnes/cartridge/cartridge.hpp b/asnes/cartridge/cartridge.hpp new file mode 100755 index 00000000..8c849a33 --- /dev/null +++ b/asnes/cartridge/cartridge.hpp @@ -0,0 +1,118 @@ +class Cartridge : property { +public: + enum class Mode : unsigned { + Normal, + BsxSlotted, + Bsx, + SufamiTurbo, + SuperGameBoy, + }; + + enum class Region : unsigned { + NTSC, + PAL, + }; + + enum class SuperGameBoyVersion : unsigned { + Version1, + Version2, + }; + + //assigned externally to point to file-system datafiles (msu1 and serial) + //example: "/path/to/filename.sfc" would set this to "/path/to/filename" + readwrite basename; + + readonly loaded; + readonly crc32; + readonly sha256; + + readonly mode; + readonly region; + readonly ram_size; + readonly spc7110_data_rom_offset; + readonly supergameboy_version; + readonly supergameboy_ram_size; + readonly supergameboy_rtc_size; + readonly serial_baud_rate; + + readonly has_bsx_slot; + readonly has_superfx; + readonly has_sa1; + readonly has_srtc; + readonly has_sdd1; + readonly has_spc7110; + readonly has_spc7110rtc; + readonly has_cx4; + readonly has_dsp1; + readonly has_dsp2; + readonly has_dsp3; + readonly has_dsp4; + readonly has_obc1; + readonly has_st0010; + readonly has_st0011; + readonly has_st0018; + readonly has_msu1; + readonly has_serial; + + struct Mapping { + Memory *memory; + MMIO *mmio; + Bus::MapMode mode; + unsigned banklo; + unsigned bankhi; + unsigned addrlo; + unsigned addrhi; + unsigned offset; + unsigned size; + + Mapping(); + Mapping(Memory&); + Mapping(MMIO&); + }; + array mapping; + + void load(Mode, const lstring&); + void unload(); + + void serialize(serializer&); + Cartridge(); + ~Cartridge(); + +private: + void parse_xml(const lstring&); + void parse_xml_cartridge(const char*); + void parse_xml_bsx(const char*); + void parse_xml_sufami_turbo(const char*, bool); + void parse_xml_gameboy(const char*); + + void xml_parse_rom(xml_element&); + void xml_parse_ram(xml_element&); + void xml_parse_superfx(xml_element&); + void xml_parse_sa1(xml_element&); + void xml_parse_bsx(xml_element&); + void xml_parse_sufamiturbo(xml_element&); + void xml_parse_supergameboy(xml_element&); + void xml_parse_srtc(xml_element&); + void xml_parse_sdd1(xml_element&); + void xml_parse_spc7110(xml_element&); + void xml_parse_cx4(xml_element&); + void xml_parse_necdsp(xml_element&); + void xml_parse_obc1(xml_element&); + void xml_parse_setadsp(xml_element&); + void xml_parse_setarisc(xml_element&); + void xml_parse_msu1(xml_element&); + void xml_parse_serial(xml_element&); + + void xml_parse_address(Mapping&, const string&); + void xml_parse_mode(Mapping&, const string&); +}; + +namespace memory { + extern MappedRAM cartrom, cartram, cartrtc; + extern MappedRAM bsxflash, bsxram, bsxpram; + extern MappedRAM stArom, stAram; + extern MappedRAM stBrom, stBram; + extern MappedRAM gbrom, gbram, gbrtc; +}; + +extern Cartridge cartridge; diff --git a/asnes/cartridge/serialization.cpp b/asnes/cartridge/serialization.cpp new file mode 100755 index 00000000..847b2354 --- /dev/null +++ b/asnes/cartridge/serialization.cpp @@ -0,0 +1,37 @@ +#ifdef CARTRIDGE_CPP + +void Cartridge::serialize(serializer &s) { + if(memory::cartram.size() != 0 && memory::cartram.size() != ~0) { + s.array(memory::cartram.data(), memory::cartram.size()); + } + + if(memory::cartrtc.size() != 0 && memory::cartrtc.size() != ~0) { + s.array(memory::cartrtc.data(), memory::cartrtc.size()); + } + + if(memory::bsxram.size() != 0 && memory::bsxram.size() != ~0) { + s.array(memory::bsxram.data(), memory::bsxram.size()); + } + + if(memory::bsxpram.size() != 0 && memory::bsxpram.size() != ~0) { + s.array(memory::bsxpram.data(), memory::bsxpram.size()); + } + + if(memory::stAram.size() != 0 && memory::stAram.size() != ~0) { + s.array(memory::stAram.data(), memory::stAram.size()); + } + + if(memory::stBram.size() != 0 && memory::stBram.size() != ~0) { + s.array(memory::stBram.data(), memory::stBram.size()); + } + + if(memory::gbram.size() != 0 && memory::gbram.size() != ~0) { + s.array(memory::gbram.data(), memory::gbram.size()); + } + + if(memory::gbrtc.size() != 0 && memory::gbrtc.size() != ~0) { + s.array(memory::gbrtc.data(), memory::gbrtc.size()); + } +} + +#endif diff --git a/asnes/cartridge/xml.cpp b/asnes/cartridge/xml.cpp new file mode 100755 index 00000000..553183a7 --- /dev/null +++ b/asnes/cartridge/xml.cpp @@ -0,0 +1,671 @@ +#ifdef CARTRIDGE_CPP + +void Cartridge::parse_xml(const lstring &list) { + mapping.reset(); + parse_xml_cartridge(list[0]); + + if(mode == Mode::BsxSlotted) { + parse_xml_bsx(list[1]); + } else if(mode == Mode::Bsx) { + parse_xml_bsx(list[1]); + } else if(mode == Mode::SufamiTurbo) { + parse_xml_sufami_turbo(list[1], 0); + parse_xml_sufami_turbo(list[2], 1); + } else if(mode == Mode::SuperGameBoy) { + parse_xml_gameboy(list[1]); + } +} + +void Cartridge::parse_xml_cartridge(const char *data) { + xml_element document = xml_parse(data); + if(document.element.size() == 0) return; + + foreach(head, document.element) { + if(head.name == "cartridge") { + foreach(attr, head.attribute) { + if(attr.name == "region") { + if(attr.content == "NTSC") region = Region::NTSC; + if(attr.content == "PAL") region = Region::PAL; + } + } + + foreach(node, head.element) { + if(node.name == "rom") xml_parse_rom(node); + if(node.name == "ram") xml_parse_ram(node); + if(node.name == "superfx") xml_parse_superfx(node); + if(node.name == "sa1") xml_parse_sa1(node); + if(node.name == "bsx") xml_parse_bsx(node); + if(node.name == "sufamiturbo") xml_parse_sufamiturbo(node); + if(node.name == "supergameboy") xml_parse_supergameboy(node); + if(node.name == "srtc") xml_parse_srtc(node); + if(node.name == "sdd1") xml_parse_sdd1(node); + if(node.name == "spc7110") xml_parse_spc7110(node); + if(node.name == "cx4") xml_parse_cx4(node); + if(node.name == "necdsp") xml_parse_necdsp(node); + if(node.name == "obc1") xml_parse_obc1(node); + if(node.name == "setadsp") xml_parse_setadsp(node); + if(node.name == "setarisc") xml_parse_setarisc(node); + if(node.name == "msu1") xml_parse_msu1(node); + if(node.name == "serial") xml_parse_serial(node); + } + } + } +} + +void Cartridge::parse_xml_bsx(const char *data) { +} + +void Cartridge::parse_xml_sufami_turbo(const char *data, bool slot) { +} + +void Cartridge::parse_xml_gameboy(const char *data) { + xml_element document = xml_parse(data); + if(document.element.size() == 0) return; + + foreach(head, document.element) { + if(head.name == "cartridge") { + foreach(attr, head.attribute) { + if(attr.name == "rtc") { + supergameboy_rtc_size = (attr.content == "true") ? 4 : 0; + } + } + + foreach(leaf, head.element) { + if(leaf.name == "ram") { + foreach(attr, leaf.attribute) { + if(attr.name == "size") { + supergameboy_ram_size = strhex(attr.content); + } + } + } + } + } + } +} + +void Cartridge::xml_parse_rom(xml_element &root) { + foreach(leaf, root.element) { + if(leaf.name == "map") { + Mapping m(memory::cartrom); + foreach(attr, leaf.attribute) { + if(attr.name == "address") xml_parse_address(m, attr.content); + if(attr.name == "mode") xml_parse_mode(m, attr.content); + if(attr.name == "offset") m.offset = strhex(attr.content); + if(attr.name == "size") m.size = strhex(attr.content); + } + mapping.append(m); + } + } +} + +void Cartridge::xml_parse_ram(xml_element &root) { + foreach(attr, root.attribute) { + if(attr.name == "size") ram_size = strhex(attr.content); + } + + foreach(leaf, root.element) { + if(leaf.name == "map") { + Mapping m(memory::cartram); + foreach(attr, leaf.attribute) { + if(attr.name == "address") xml_parse_address(m, attr.content); + if(attr.name == "mode") xml_parse_mode(m, attr.content); + if(attr.name == "offset") m.offset = strhex(attr.content); + if(attr.name == "size") m.size = strhex(attr.content); + } + mapping.append(m); + } + } +} + +void Cartridge::xml_parse_superfx(xml_element &root) { + has_superfx = true; + + foreach(node, root.element) { + if(node.name == "rom") { + foreach(leaf, node.element) { + if(leaf.name == "map") { + Mapping m(memory::fxrom); + foreach(attr, leaf.attribute) { + if(attr.name == "address") xml_parse_address(m, attr.content); + if(attr.name == "mode") xml_parse_mode(m, attr.content); + if(attr.name == "offset") m.offset = strhex(attr.content); + if(attr.name == "size") m.size = strhex(attr.content); + } + mapping.append(m); + } + } + } else if(node.name == "ram") { + foreach(attr, node.attribute) { + if(attr.name == "size") ram_size = strhex(attr.content); + } + + foreach(leaf, node.element) { + if(leaf.name == "map") { + Mapping m(memory::fxram); + foreach(attr, leaf.attribute) { + if(attr.name == "address") xml_parse_address(m, attr.content); + if(attr.name == "mode") xml_parse_mode(m, attr.content); + if(attr.name == "offset") m.offset = strhex(attr.content); + if(attr.name == "size") m.size = strhex(attr.content); + } + mapping.append(m); + } + } + } else if(node.name == "mmio") { + foreach(leaf, node.element) { + if(leaf.name == "map") { + Mapping m(superfx); + foreach(attr, leaf.attribute) { + if(attr.name == "address") xml_parse_address(m, attr.content); + } + mapping.append(m); + } + } + } + } +} + +void Cartridge::xml_parse_sa1(xml_element &root) { + has_sa1 = true; + + foreach(node, root.element) { + if(node.name == "rom") { + foreach(leaf, node.element) { + if(leaf.name == "map") { + Mapping m(memory::vsprom); + foreach(attr, leaf.attribute) { + if(attr.name == "address") xml_parse_address(m, attr.content); + if(attr.name == "mode") xml_parse_mode(m, attr.content); + if(attr.name == "offset") m.offset = strhex(attr.content); + if(attr.name == "size") m.size = strhex(attr.content); + } + mapping.append(m); + } + } + } else if(node.name == "iram") { + foreach(leaf, node.element) { + if(leaf.name == "map") { + Mapping m(memory::cpuiram); + foreach(attr, leaf.attribute) { + if(attr.name == "address") xml_parse_address(m, attr.content); + if(attr.name == "mode") xml_parse_mode(m, attr.content); + if(attr.name == "offset") m.offset = strhex(attr.content); + if(attr.name == "size") m.size = strhex(attr.content); + } + mapping.append(m); + } + } + } else if(node.name == "bwram") { + foreach(attr, node.attribute) { + if(attr.name == "size") ram_size = strhex(attr.content); + } + + foreach(leaf, node.element) { + if(leaf.name == "map") { + Mapping m(memory::cc1bwram); + foreach(attr, leaf.attribute) { + if(attr.name == "address") xml_parse_address(m, attr.content); + if(attr.name == "mode") xml_parse_mode(m, attr.content); + if(attr.name == "offset") m.offset = strhex(attr.content); + if(attr.name == "size") m.size = strhex(attr.content); + } + mapping.append(m); + } + } + } else if(node.name == "mmio") { + foreach(leaf, node.element) { + if(leaf.name == "map") { + Mapping m(sa1); + foreach(attr, leaf.attribute) { + if(attr.name == "address") xml_parse_address(m, attr.content); + } + mapping.append(m); + } + } + } + } +} + +void Cartridge::xml_parse_bsx(xml_element &root) { + if(mode != Mode::BsxSlotted && mode != Mode::Bsx) return; + + foreach(node, root.element) { + if(node.name == "slot") { + foreach(leaf, node.element) { + if(leaf.name == "map") { + Mapping m(memory::bsxflash); + foreach(attr, leaf.attribute) { + if(attr.name == "address") xml_parse_address(m, attr.content); + if(attr.name == "mode") xml_parse_mode(m, attr.content); + if(attr.name == "offset") m.offset = strhex(attr.content); + if(attr.name == "size") m.size = strhex(attr.content); + } + mapping.append(m); + } + } + } else if(node.name == "mmio") { + foreach(leaf, node.element) { + if(leaf.name == "map") { + Mapping m(bsxcart); + foreach(attr, leaf.attribute) { + if(attr.name == "address") xml_parse_address(m, attr.content); + } + mapping.append(m); + } + } + } + } +} + +void Cartridge::xml_parse_sufamiturbo(xml_element &root) { + if(mode != Mode::SufamiTurbo) return; + + foreach(node, root.element) { + if(node.name == "slot") { + bool slotid = 0; + foreach(attr, node.attribute) { + if(attr.name == "id") { + if(attr.content == "A") slotid = 0; + if(attr.content == "B") slotid = 1; + } + } + + foreach(slot, node.element) { + if(slot.name == "rom") { + foreach(leaf, slot.element) { + if(leaf.name == "map") { + Mapping m(slotid == 0 ? memory::stArom : memory::stBrom); + foreach(attr, leaf.attribute) { + if(attr.name == "address") xml_parse_address(m, attr.content); + if(attr.name == "mode") xml_parse_mode(m, attr.content); + if(attr.name == "offset") m.offset = strhex(attr.content); + if(attr.name == "size") m.size = strhex(attr.content); + } + if(m.memory->size() > 0) mapping.append(m); + } + } + } else if(slot.name == "ram") { + foreach(leaf, slot.element) { + if(leaf.name == "map") { + Mapping m(slotid == 0 ? memory::stAram : memory::stBram); + foreach(attr, leaf.attribute) { + if(attr.name == "address") xml_parse_address(m, attr.content); + if(attr.name == "mode") xml_parse_mode(m, attr.content); + if(attr.name == "offset") m.offset = strhex(attr.content); + if(attr.name == "size") m.size = strhex(attr.content); + } + if(m.memory->size() > 0) mapping.append(m); + } + } + } + } + } + } +} + +void Cartridge::xml_parse_supergameboy(xml_element &root) { + if(mode != Mode::SuperGameBoy) return; + + foreach(attr, root.attribute) { + if(attr.name == "revision") { + if(attr.content == "1") supergameboy_version = SuperGameBoyVersion::Version1; + if(attr.content == "2") supergameboy_version = SuperGameBoyVersion::Version2; + } + } + + foreach(node, root.element) { + if(node.name == "mmio") { + foreach(leaf, node.element) { + if(leaf.name == "map") { + Mapping m((Memory&)supergameboy); + foreach(attr, leaf.attribute) { + if(attr.name == "address") xml_parse_address(m, attr.content); + } + mapping.append(m); + } + } + } + } +} + +void Cartridge::xml_parse_srtc(xml_element &root) { + has_srtc = true; + + foreach(node, root.element) { + if(node.name == "mmio") { + foreach(leaf, node.element) { + if(leaf.name == "map") { + Mapping m(srtc); + foreach(attr, leaf.attribute) { + if(attr.name == "address") xml_parse_address(m, attr.content); + } + mapping.append(m); + } + } + } + } +} + +void Cartridge::xml_parse_sdd1(xml_element &root) { + has_sdd1 = true; + + foreach(node, root.element) { + if(node.name == "mcu") { + foreach(leaf, node.element) { + if(leaf.name == "map") { + Mapping m((Memory&)sdd1); + foreach(attr, leaf.attribute) { + if(attr.name == "address") xml_parse_address(m, attr.content); + } + mapping.append(m); + } + } + } else if(node.name == "mmio") { + foreach(leaf, node.element) { + if(leaf.name == "map") { + Mapping m((MMIO&)sdd1); + foreach(attr, leaf.attribute) { + if(attr.name == "address") xml_parse_address(m, attr.content); + } + mapping.append(m); + } + } + } + } +} + +void Cartridge::xml_parse_spc7110(xml_element &root) { + has_spc7110 = true; + + foreach(node, root.element) { + if(node.name == "dcu") { + foreach(leaf, node.element) { + if(leaf.name == "map") { + Mapping m(spc7110dcu); + foreach(attr, leaf.attribute) { + if(attr.name == "address") xml_parse_address(m, attr.content); + } + mapping.append(m); + } + } + } else if(node.name == "mcu") { + foreach(leaf, node.element) { + if(leaf.name == "map") { + Mapping m(spc7110mcu); + foreach(attr, leaf.attribute) { + if(attr.name == "address") xml_parse_address(m, attr.content); + if(attr.name == "offset") spc7110_data_rom_offset = strhex(attr.content); + } + mapping.append(m); + } + } + } else if(node.name == "mmio") { + foreach(leaf, node.element) { + if(leaf.name == "map") { + Mapping m(spc7110); + foreach(attr, leaf.attribute) { + if(attr.name == "address") xml_parse_address(m, attr.content); + } + mapping.append(m); + } + } + } else if(node.name == "ram") { + foreach(attr, node.attribute) { + if(attr.name == "size") ram_size = strhex(attr.content); + } + + foreach(leaf, node.element) { + if(leaf.name == "map") { + Mapping m(spc7110ram); + foreach(attr, leaf.attribute) { + if(attr.name == "address") xml_parse_address(m, attr.content); + if(attr.name == "mode") xml_parse_mode(m, attr.content); + if(attr.name == "offset") m.offset = strhex(attr.content); + if(attr.name == "size") m.size = strhex(attr.content); + } + mapping.append(m); + } + } + } else if(node.name == "rtc") { + has_spc7110rtc = true; + + foreach(leaf, node.element) { + if(leaf.name == "map") { + Mapping m(spc7110); + foreach(attr, leaf.attribute) { + if(attr.name == "address") xml_parse_address(m, attr.content); + } + mapping.append(m); + } + } + } + } +} + +void Cartridge::xml_parse_cx4(xml_element &root) { + has_cx4 = true; + + foreach(node, root.element) { + if(node.name == "mmio") { + foreach(leaf, node.element) { + if(leaf.name == "map") { + Mapping m(cx4); + foreach(attr, leaf.attribute) { + if(attr.name == "address") xml_parse_address(m, attr.content); + } + mapping.append(m); + } + } + } + } +} + +void Cartridge::xml_parse_necdsp(xml_element &root) { + unsigned program = 0; + + foreach(attr, root.attribute) { + if(attr.name == "program") { + if(attr.content == "DSP-1" || attr.content == "DSP-1A" || attr.content == "DSP-1B") { + program = 1; + has_dsp1 = true; + } else if(attr.content == "DSP-2") { + program = 2; + has_dsp2 = true; + } else if(attr.content == "DSP-3") { + program = 3; + has_dsp3 = true; + } else if(attr.content == "DSP-4") { + program = 4; + has_dsp4 = true; + } + } + } + + Memory *dr[5] = { 0, &dsp1dr, &dsp2dr, &dsp3, &dsp4 }; + Memory *sr[5] = { 0, &dsp1sr, &dsp2sr, &dsp3, &dsp4 }; + + foreach(node, root.element) { + if(node.name == "dr" && dr[program]) { + foreach(leaf, node.element) { + if(leaf.name == "map") { + Mapping m(*dr[program]); + foreach(attr, leaf.attribute) { + if(attr.name == "address") xml_parse_address(m, attr.content); + } + mapping.append(m); + } + } + } else if(node.name == "sr" && sr[program]) { + foreach(leaf, node.element) { + if(leaf.name == "map") { + Mapping m(*sr[program]); + foreach(attr, leaf.attribute) { + if(attr.name == "address") xml_parse_address(m, attr.content); + } + mapping.append(m); + } + } + } + } +} + +void Cartridge::xml_parse_obc1(xml_element &root) { + has_obc1 = true; + + foreach(node, root.element) { + if(node.name == "mmio") { + foreach(leaf, node.element) { + if(leaf.name == "map") { + Mapping m(obc1); + foreach(attr, leaf.attribute) { + if(attr.name == "address") xml_parse_address(m, attr.content); + } + mapping.append(m); + } + } + } + } +} + +void Cartridge::xml_parse_setadsp(xml_element &root) { + unsigned program = 0; + + foreach(attr, root.attribute) { + if(attr.name == "program") { + if(attr.content == "ST-0010") { + program = 1; + has_st0010 = true; + } else if(attr.content == "ST-0011") { + program = 2; + has_st0011 = true; + } + } + } + + Memory *map[3] = { 0, &st0010, 0 }; + + foreach(node, root.element) { + if(node.name == "mmio" && map[program]) { + foreach(leaf, node.element) { + if(leaf.name == "map") { + Mapping m(*map[program]); + foreach(attr, leaf.attribute) { + if(attr.name == "address") xml_parse_address(m, attr.content); + } + mapping.append(m); + } + } + } + } +} + +void Cartridge::xml_parse_setarisc(xml_element &root) { + unsigned program = 0; + + foreach(attr, root.attribute) { + if(attr.name == "program") { + if(attr.content == "ST-0018") { + program = 1; + has_st0018 = true; + } + } + } + + MMIO *map[2] = { 0, &st0018 }; + + foreach(node, root.element) { + if(node.name == "mmio" && map[program]) { + foreach(leaf, node.element) { + if(leaf.name == "map") { + Mapping m(*map[program]); + foreach(attr, leaf.attribute) { + if(attr.name == "address") xml_parse_address(m, attr.content); + } + mapping.append(m); + } + } + } + } +} + +void Cartridge::xml_parse_msu1(xml_element &root) { + has_msu1 = true; + + foreach(node, root.element) { + if(node.name == "mmio") { + foreach(leaf, node.element) { + if(leaf.name == "map") { + Mapping m(msu1); + foreach(attr, leaf.attribute) { + if(attr.name == "address") xml_parse_address(m, attr.content); + } + mapping.append(m); + } + } + } + } +} + +void Cartridge::xml_parse_serial(xml_element &root) { + has_serial = true; + + foreach(attr, root.attribute) { + if(attr.name == "baud") { + serial_baud_rate = strunsigned(attr.content); + } + } +} + +void Cartridge::xml_parse_address(Mapping &m, const string &data) { + lstring part; + part.split(":", data); + if(part.size() != 2) return; + + lstring subpart; + subpart.split("-", part[0]); + if(subpart.size() == 1) { + m.banklo = strhex(subpart[0]); + m.bankhi = m.banklo; + } else if(subpart.size() == 2) { + m.banklo = strhex(subpart[0]); + m.bankhi = strhex(subpart[1]); + } + + subpart.split("-", part[1]); + if(subpart.size() == 1) { + m.addrlo = strhex(subpart[0]); + m.addrhi = m.addrlo; + } else if(subpart.size() == 2) { + m.addrlo = strhex(subpart[0]); + m.addrhi = strhex(subpart[1]); + } +} + +void Cartridge::xml_parse_mode(Mapping &m, const string& data) { + if(data == "direct") m.mode = Bus::MapMode::Direct; + else if(data == "linear") m.mode = Bus::MapMode::Linear; + else if(data == "shadow") m.mode = Bus::MapMode::Shadow; +} + +Cartridge::Mapping::Mapping() { + memory = 0; + mmio = 0; + mode = Bus::MapMode::Direct; + banklo = bankhi = addrlo = addrhi = offset = size = 0; +} + +Cartridge::Mapping::Mapping(Memory &memory_) { + memory = &memory_; + mmio = 0; + mode = Bus::MapMode::Direct; + banklo = bankhi = addrlo = addrhi = offset = size = 0; +} + +Cartridge::Mapping::Mapping(MMIO &mmio_) { + memory = 0; + mmio = &mmio_; + mode = Bus::MapMode::Direct; + banklo = bankhi = addrlo = addrhi = offset = size = 0; +} + +#endif diff --git a/asnes/cheat/cheat-inline.hpp b/asnes/cheat/cheat-inline.hpp new file mode 100755 index 00000000..a80f47f3 --- /dev/null +++ b/asnes/cheat/cheat-inline.hpp @@ -0,0 +1,2 @@ +bool Cheat::active() const { return cheat_enabled; } +bool Cheat::exists(unsigned addr) const { return bitmask[addr >> 3] & 1 << (addr & 7); } diff --git a/asnes/cheat/cheat.cpp b/asnes/cheat/cheat.cpp new file mode 100755 index 00000000..c97e9a96 --- /dev/null +++ b/asnes/cheat/cheat.cpp @@ -0,0 +1,194 @@ +#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(bitmask, 0x00, sizeof bitmask); + code_enabled = false; + + for(unsigned i = 0; i < size(); i++) { + const CheatCode &code = operator[](i); + if(code.enabled == false) continue; + + for(unsigned n = 0; n < code.addr.size(); n++) { + code_enabled = true; + + unsigned addr = mirror(code.addr[n]); + bitmask[addr >> 3] |= 1 << (addr & 7); + 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); + bitmask[mirroraddr >> 3] |= 1 << (mirroraddr & 7); + + mirroraddr = ((0x80 + x) << 16) + (addr & 0x1fff); + bitmask[mirroraddr >> 3] |= 1 << (mirroraddr & 7); + } + } + } + } + + cheat_enabled = system_enabled && code_enabled; +} + +bool Cheat::read(unsigned addr, uint8 &data) const { + addr = mirror(addr); + + for(unsigned i = 0; i < size(); i++) { + const CheatCode &code = operator[](i); + if(code.enabled == false) continue; + + for(unsigned n = 0; n < code.addr.size(); n++) { + if(addr == mirror(code.addr[n])) { + data = code.data[n]; + return true; + } + } + } + + return false; +} + +Cheat::Cheat() { + system_enabled = true; + synchronize(); +} + +//=============== +//encode / decode +//=============== + +bool Cheat::decode(const char *s, unsigned &addr, uint8 &data, Type &type) { + string t = s; + t.lower(); + + #define ischr(n) ((n >= '0' && n <= '9') || (n >= 'a' && n <= 'f')) + + if(strlen(t) == 8 || (strlen(t) == 9 && t[6] == ':')) { + //strip ':' + if(strlen(t) == 9 && t[6] == ':') t = string() << substr(t, 0, 6) << substr(t, 7); + //validate input + for(unsigned i = 0; i < 8; i++) if(!ischr(t[i])) return false; + + type = Type::ProActionReplay; + unsigned r = strhex((const char*)t); + addr = r >> 8; + data = r & 0xff; + return true; + } else if(strlen(t) == 9 && t[4] == '-') { + //strip '-' + t = string() << substr(t, 0, 4) << substr(t, 5); + //validate input + for(unsigned i = 0; i < 8; i++) if(!ischr(t[i])) return false; + + type = Type::GameGenie; + t.transform("df4709156bc8a23e", "0123456789abcdef"); + unsigned r = strhex((const char*)t); + //8421 8421 8421 8421 8421 8421 + //abcd efgh ijkl mnop qrst uvwx + //ijkl qrst opab cduv wxef ghmn + addr = (!!(r & 0x002000) << 23) | (!!(r & 0x001000) << 22) + | (!!(r & 0x000800) << 21) | (!!(r & 0x000400) << 20) + | (!!(r & 0x000020) << 19) | (!!(r & 0x000010) << 18) + | (!!(r & 0x000008) << 17) | (!!(r & 0x000004) << 16) + | (!!(r & 0x800000) << 15) | (!!(r & 0x400000) << 14) + | (!!(r & 0x200000) << 13) | (!!(r & 0x100000) << 12) + | (!!(r & 0x000002) << 11) | (!!(r & 0x000001) << 10) + | (!!(r & 0x008000) << 9) | (!!(r & 0x004000) << 8) + | (!!(r & 0x080000) << 7) | (!!(r & 0x040000) << 6) + | (!!(r & 0x020000) << 5) | (!!(r & 0x010000) << 4) + | (!!(r & 0x000200) << 3) | (!!(r & 0x000100) << 2) + | (!!(r & 0x000080) << 1) | (!!(r & 0x000040) << 0); + data = r >> 24; + return true; + } else { + return false; + } + + #undef ischr +} + +bool Cheat::encode(string &s, unsigned addr, uint8 data, Type type) { + char t[16]; + + if(type == Type::ProActionReplay) { + s = string(strhex<6>(addr), strhex<2>(data)); + return true; + } else if(type == Type::GameGenie) { + unsigned r = addr; + addr = (!!(r & 0x008000) << 23) | (!!(r & 0x004000) << 22) + | (!!(r & 0x002000) << 21) | (!!(r & 0x001000) << 20) + | (!!(r & 0x000080) << 19) | (!!(r & 0x000040) << 18) + | (!!(r & 0x000020) << 17) | (!!(r & 0x000010) << 16) + | (!!(r & 0x000200) << 15) | (!!(r & 0x000100) << 14) + | (!!(r & 0x800000) << 13) | (!!(r & 0x400000) << 12) + | (!!(r & 0x200000) << 11) | (!!(r & 0x100000) << 10) + | (!!(r & 0x000008) << 9) | (!!(r & 0x000004) << 8) + | (!!(r & 0x000002) << 7) | (!!(r & 0x000001) << 6) + | (!!(r & 0x080000) << 5) | (!!(r & 0x040000) << 4) + | (!!(r & 0x020000) << 3) | (!!(r & 0x010000) << 2) + | (!!(r & 0x000800) << 1) | (!!(r & 0x000400) << 0); + s = string(strhex<2>(data), strhex<2>(addr >> 16), "-", strhex<4>(addr & 0xffff)); + s.transform("0123456789abcdef", "df4709156bc8a23e"); + return true; + } else { + return false; + } +} + +//======== +//internal +//======== + +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; +} + +//========= +//CheatCode +//========= + +bool CheatCode::operator=(string s) { + addr.reset(); + data.reset(); + + lstring list; + list.split("+", s.replace(" ", "")); + + for(unsigned i = 0; i < list.size(); i++) { + unsigned addr_; + uint8 data_; + Cheat::Type type_; + if(Cheat::decode(list[i], addr_, data_, type_) == false) { + addr.reset(); + data.reset(); + return false; + } + + addr.append(addr_); + data.append(data_); + } + + return true; +} + +CheatCode::CheatCode() { + enabled = false; +} + +} diff --git a/asnes/cheat/cheat.hpp b/asnes/cheat/cheat.hpp new file mode 100755 index 00000000..ac4b43ec --- /dev/null +++ b/asnes/cheat/cheat.hpp @@ -0,0 +1,35 @@ +struct CheatCode { + bool enabled; + array addr; + array data; + + bool operator=(string); + CheatCode(); +}; + +class Cheat : public linear_vector { +public: + enum class Type : unsigned { ProActionReplay, GameGenie }; + + bool enabled() const; + void enable(bool); + void synchronize(); + bool read(unsigned, uint8&) const; + + inline bool active() const; + inline bool exists(unsigned addr) const; + + Cheat(); + + static bool decode(const char*, unsigned&, uint8&, Type&); + static bool encode(string&, unsigned, uint8, Type); + +private: + uint8 bitmask[0x200000]; + bool system_enabled; + bool code_enabled; + bool cheat_enabled; + unsigned mirror(unsigned) const; +}; + +extern Cheat cheat; diff --git a/asnes/chip/bsx/bsx.cpp b/asnes/chip/bsx/bsx.cpp new file mode 100755 index 00000000..8a083103 --- /dev/null +++ b/asnes/chip/bsx/bsx.cpp @@ -0,0 +1,8 @@ +#include + +#define BSX_CPP +namespace SNES { + #include "bsx_base.cpp" + #include "bsx_cart.cpp" + #include "bsx_flash.cpp" +} diff --git a/asnes/chip/bsx/bsx.hpp b/asnes/chip/bsx/bsx.hpp new file mode 100755 index 00000000..4e06b64c --- /dev/null +++ b/asnes/chip/bsx/bsx.hpp @@ -0,0 +1,71 @@ +class BSXBase : public MMIO { +public: + void init(); + void enable(); + 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; +}; + +class BSXCart : public MMIO { +public: + void init(); + void enable(); + void power(); + void reset(); + + uint8 mmio_read(unsigned addr); + void mmio_write(unsigned addr, uint8 data); + + BSXCart(); + ~BSXCart(); + +private: + struct { + uint8 r[16]; + } regs; + + void update_memory_map(); +}; + +class BSXFlash : public Memory { +public: + void init(); + void enable(); + 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 BSXBase bsxbase; +extern BSXCart bsxcart; +extern BSXFlash bsxflash; diff --git a/asnes/chip/bsx/bsx_base.cpp b/asnes/chip/bsx/bsx_base.cpp new file mode 100755 index 00000000..2272c174 --- /dev/null +++ b/asnes/chip/bsx/bsx_base.cpp @@ -0,0 +1,140 @@ +#ifdef BSX_CPP + +BSXBase bsxbase; + +void BSXBase::init() { +} + +void BSXBase::enable() { + for(uint16 i = 0x2188; i <= 0x219f; i++) memory::mmio.map(i, *this); +} + +void BSXBase::power() { + reset(); +} + +void BSXBase::reset() { + memset(®s, 0x00, sizeof regs); +} + +uint8 BSXBase::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; + time(&rawtime); + 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 BSXBase::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/asnes/chip/bsx/bsx_cart.cpp b/asnes/chip/bsx/bsx_cart.cpp new file mode 100755 index 00000000..6bd67269 --- /dev/null +++ b/asnes/chip/bsx/bsx_cart.cpp @@ -0,0 +1,96 @@ +#ifdef BSX_CPP + +BSXCart bsxcart; + +void BSXCart::init() { +} + +void BSXCart::enable() { +} + +void BSXCart::power() { + reset(); +} + +void BSXCart::reset() { + for(unsigned i = 0; i < 16; i++) regs.r[i] = 0x00; + regs.r[0x07] = 0x80; + regs.r[0x08] = 0x80; + + update_memory_map(); +} + +void BSXCart::update_memory_map() { + Memory &cart = (regs.r[0x01] & 0x80) == 0x00 ? (Memory&)bsxflash : (Memory&)memory::bsxpram; + + if((regs.r[0x02] & 0x80) == 0x00) { + //LoROM mapping + bus.map(Bus::MapMode::Linear, 0x00, 0x7d, 0x8000, 0xffff, cart); + bus.map(Bus::MapMode::Linear, 0x80, 0xff, 0x8000, 0xffff, cart); + } else { + //HiROM mapping + bus.map(Bus::MapMode::Shadow, 0x00, 0x3f, 0x8000, 0xffff, cart); + bus.map(Bus::MapMode::Linear, 0x40, 0x7d, 0x0000, 0xffff, cart); + bus.map(Bus::MapMode::Shadow, 0x80, 0xbf, 0x8000, 0xffff, cart); + bus.map(Bus::MapMode::Linear, 0xc0, 0xff, 0x0000, 0xffff, cart); + } + + if(regs.r[0x03] & 0x80) { + bus.map(Bus::MapMode::Linear, 0x60, 0x6f, 0x0000, 0xffff, memory::bsxpram); + //bus.map(Bus::MapMode::Linear, 0x70, 0x77, 0x0000, 0xffff, memory::bsxpram); + } + + if((regs.r[0x05] & 0x80) == 0x00) { + bus.map(Bus::MapMode::Linear, 0x40, 0x4f, 0x0000, 0xffff, memory::bsxpram); + } + + if((regs.r[0x06] & 0x80) == 0x00) { + bus.map(Bus::MapMode::Linear, 0x50, 0x5f, 0x0000, 0xffff, memory::bsxpram); + } + + if(regs.r[0x07] & 0x80) { + bus.map(Bus::MapMode::Linear, 0x00, 0x1f, 0x8000, 0xffff, memory::cartrom); + } + + if(regs.r[0x08] & 0x80) { + bus.map(Bus::MapMode::Linear, 0x80, 0x9f, 0x8000, 0xffff, memory::cartrom); + } + + bus.map(Bus::MapMode::Shadow, 0x20, 0x3f, 0x6000, 0x7fff, memory::bsxpram); + bus.map(Bus::MapMode::Linear, 0x70, 0x77, 0x0000, 0xffff, memory::bsxpram); +} + +uint8 BSXCart::mmio_read(unsigned addr) { + if((addr & 0xf0ffff) == 0x005000) { //$[00-0f]:5000 MMIO + uint8 n = (addr >> 16) & 15; + return regs.r[n]; + } + + if((addr & 0xf8f000) == 0x105000) { //$[10-17]:[5000-5fff] SRAM + return memory::bsxram.read(((addr >> 16) & 7) * 0x1000 + (addr & 0xfff)); + } + + return 0x00; +} + +void BSXCart::mmio_write(unsigned addr, uint8 data) { + if((addr & 0xf0ffff) == 0x005000) { //$[00-0f]:5000 MMIO + uint8 n = (addr >> 16) & 15; + regs.r[n] = data; + if(n == 0x0e && data & 0x80) update_memory_map(); + return; + } + + if((addr & 0xf8f000) == 0x105000) { //$[10-17]:[5000-5fff] SRAM + return memory::bsxram.write(((addr >> 16) & 7) * 0x1000 + (addr & 0xfff), data); + } +} + +BSXCart::BSXCart() { +} + +BSXCart::~BSXCart() { +} + +#endif + diff --git a/asnes/chip/bsx/bsx_flash.cpp b/asnes/chip/bsx/bsx_flash.cpp new file mode 100755 index 00000000..a4297a2b --- /dev/null +++ b/asnes/chip/bsx/bsx_flash.cpp @@ -0,0 +1,119 @@ +#ifdef BSX_CPP + +BSXFlash bsxflash; + +void BSXFlash::init() {} +void BSXFlash::enable() {} + +void BSXFlash::power() { + reset(); +} + +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::bsxflash.write_protect(!regs.write_enable); +} + +unsigned BSXFlash::size() const { + return memory::bsxflash.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::bsxflash.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::bsxflash.write(addr, data); + } + } else { + if(regs.write_enable) { + return memory::bsxflash.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::bsxflash.write_protect(!regs.write_enable); + } +} + +#endif + diff --git a/asnes/chip/chip.hpp b/asnes/chip/chip.hpp new file mode 100755 index 00000000..6893952f --- /dev/null +++ b/asnes/chip/chip.hpp @@ -0,0 +1,31 @@ +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 +#include +#include +#include + +void Coprocessor::step(unsigned clocks) { + clock += clocks * (uint64)cpu.frequency; +} + +void Coprocessor::synchronize_cpu() { + if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(cpu.thread); +} diff --git a/asnes/chip/cx4/cx4.cpp b/asnes/chip/cx4/cx4.cpp new file mode 100755 index 00000000..f6362e7c --- /dev/null +++ b/asnes/chip/cx4/cx4.cpp @@ -0,0 +1,208 @@ +//============= +//Cx4 emulation +//============= +//Used in Rockman X2/X3 (Megaman X2/X3) +//Portions (c) anomie, Overload, zsKnight, Nach, byuu + +#include + +#define CX4_CPP +namespace SNES { + +Cx4 cx4; + +#include "serialization.cpp" +#include "data.cpp" +#include "functions.cpp" +#include "oam.cpp" +#include "opcodes.cpp" + +void Cx4::init() { +} + +void Cx4::enable() { +} + +uint32 Cx4::ldr(uint8 r) { + uint16 addr = 0x0080 + (r * 3); + return (reg[addr + 0] << 0) + | (reg[addr + 1] << 8) + | (reg[addr + 2] << 16); +} + +void Cx4::str(uint8 r, uint32 data) { + uint16 addr = 0x0080 + (r * 3); + reg[addr + 0] = (data >> 0); + reg[addr + 1] = (data >> 8); + reg[addr + 2] = (data >> 16); +} + +void Cx4::mul(uint32 x, uint32 y, uint32 &rl, uint32 &rh) { + int64 rx = x & 0xffffff; + int64 ry = y & 0xffffff; + if(rx & 0x800000)rx |= ~0x7fffff; + if(ry & 0x800000)ry |= ~0x7fffff; + + rx *= ry; + + rl = (rx) & 0xffffff; + rh = (rx >> 24) & 0xffffff; +} + +uint32 Cx4::sin(uint32 rx) { + r0 = rx & 0x1ff; + if(r0 & 0x100)r0 ^= 0x1ff; + if(r0 & 0x080)r0 ^= 0x0ff; + if(rx & 0x100) { + return sin_table[r0 + 0x80]; + } else { + return sin_table[r0]; + } +} + +uint32 Cx4::cos(uint32 rx) { + return sin(rx + 0x080); +} + +void Cx4::immediate_reg(uint32 start) { + r0 = ldr(0); + for(uint32 i = start; i < 48; i++) { + if((r0 & 0x0fff) < 0x0c00) { + ram[r0 & 0x0fff] = immediate_data[i]; + } + r0++; + } + str(0, r0); +} + +void Cx4::transfer_data() { + uint32 src; + uint16 dest, count; + + src = (reg[0x40]) | (reg[0x41] << 8) | (reg[0x42] << 16); + count = (reg[0x43]) | (reg[0x44] << 8); + dest = (reg[0x45]) | (reg[0x46] << 8); + + for(uint32 i=0;i> 2; + return; + } + + switch(data) { + case 0x00: op00(); break; + case 0x01: op01(); break; + case 0x05: op05(); break; + case 0x0d: op0d(); break; + case 0x10: op10(); break; + case 0x13: op13(); break; + case 0x15: op15(); break; + case 0x1f: op1f(); break; + case 0x22: op22(); break; + case 0x25: op25(); break; + case 0x2d: op2d(); break; + case 0x40: op40(); break; + case 0x54: op54(); break; + case 0x5c: op5c(); break; + case 0x5e: op5e(); break; + case 0x60: op60(); break; + case 0x62: op62(); break; + case 0x64: op64(); break; + case 0x66: op66(); break; + case 0x68: op68(); break; + case 0x6a: op6a(); break; + case 0x6c: op6c(); break; + case 0x6e: op6e(); break; + case 0x70: op70(); break; + case 0x72: op72(); break; + case 0x74: op74(); break; + case 0x76: op76(); break; + case 0x78: op78(); break; + case 0x7a: op7a(); break; + case 0x7c: op7c(); break; + case 0x89: op89(); break; + } + } +} + +void Cx4::writeb(uint16 addr, uint8 data) { + write(addr, data); +} + +void Cx4::writew(uint16 addr, uint16 data) { + write(addr + 0, data >> 0); + write(addr + 1, data >> 8); +} + +void Cx4::writel(uint16 addr, uint32 data) { + write(addr + 0, data >> 0); + write(addr + 1, data >> 8); + write(addr + 2, data >> 16); +} + +uint8 Cx4::read(unsigned addr) { + addr &= 0x1fff; + + if(addr < 0x0c00) { + return ram[addr]; + } + + if(addr >= 0x1f00) { + return reg[addr & 0xff]; + } + + return cpu.regs.mdr; +} + +uint8 Cx4::readb(uint16 addr) { + return read(addr); +} + +uint16 Cx4::readw(uint16 addr) { + return read(addr) | (read(addr + 1) << 8); +} + +uint32 Cx4::readl(uint16 addr) { + return read(addr) | (read(addr + 1) << 8) + (read(addr + 2) << 16); +} + +void Cx4::power() { + reset(); +} + +void Cx4::reset() { + memset(ram, 0, 0x0c00); + memset(reg, 0, 0x0100); +} + +}; diff --git a/asnes/chip/cx4/cx4.hpp b/asnes/chip/cx4/cx4.hpp new file mode 100755 index 00000000..f971f44d --- /dev/null +++ b/asnes/chip/cx4/cx4.hpp @@ -0,0 +1,95 @@ +class Cx4 : public Memory { +public: + void init(); + void enable(); + void power(); + void reset(); + + uint8 read(unsigned addr); + void write(unsigned addr, uint8 data); + + void serialize(serializer&); + +private: + uint8 ram[0x0c00]; + uint8 reg[0x0100]; + uint32 r0, r1, r2, r3, r4, r5, r6, r7, + r8, r9, r10, r11, r12, r13, r14, r15; + + static const uint8 immediate_data[48]; + static const uint16 wave_data[40]; + static const uint32 sin_table[256]; + + static const int16 SinTable[512]; + static const int16 CosTable[512]; + + int16 C4WFXVal, C4WFYVal, C4WFZVal, C4WFX2Val, C4WFY2Val, C4WFDist, C4WFScale; + int16 C41FXVal, C41FYVal, C41FAngleRes, C41FDist, C41FDistVal; + + void C4TransfWireFrame(); + void C4TransfWireFrame2(); + void C4CalcWireFrame(); + void C4DrawLine(int32 X1, int32 Y1, int16 Z1, int32 X2, int32 Y2, int16 Z2, uint8 Color); + void C4DrawWireFrame(); + void C4DoScaleRotate(int row_padding); + +public: + uint32 ldr(uint8 r); + void str(uint8 r, uint32 data); + void mul(uint32 x, uint32 y, uint32 &rl, uint32 &rh); + uint32 sin(uint32 rx); + uint32 cos(uint32 rx); + + void transfer_data(); + void immediate_reg(uint32 num); + + void op00_00(); + void op00_03(); + void op00_05(); + void op00_07(); + void op00_08(); + void op00_0b(); + void op00_0c(); + + void op00(); + void op01(); + void op05(); + void op0d(); + void op10(); + void op13(); + void op15(); + void op1f(); + void op22(); + void op25(); + void op2d(); + void op40(); + void op54(); + void op5c(); + void op5e(); + void op60(); + void op62(); + void op64(); + void op66(); + void op68(); + void op6a(); + void op6c(); + void op6e(); + void op70(); + void op72(); + void op74(); + void op76(); + void op78(); + void op7a(); + void op7c(); + void op89(); + + uint8 readb(uint16 addr); + uint16 readw(uint16 addr); + uint32 readl(uint16 addr); + + void writeb(uint16 addr, uint8 data); + void writew(uint16 addr, uint16 data); + void writel(uint16 addr, uint32 data); +}; + +extern Cx4 cx4; diff --git a/asnes/chip/cx4/data.cpp b/asnes/chip/cx4/data.cpp new file mode 100755 index 00000000..8538f602 --- /dev/null +++ b/asnes/chip/cx4/data.cpp @@ -0,0 +1,187 @@ +#ifdef CX4_CPP + +const uint8 Cx4::immediate_data[48] = { + 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x80, 0xff, 0xff, 0x7f, + 0x00, 0x80, 0x00, 0xff, 0x7f, 0x00, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0xff, + 0x00, 0x00, 0x01, 0xff, 0xff, 0xfe, 0x00, 0x01, 0x00, 0xff, 0xfe, 0x00 +}; + +const uint16 Cx4::wave_data[40] = { + 0x0000, 0x0002, 0x0004, 0x0006, 0x0008, 0x000a, 0x000c, 0x000e, + 0x0200, 0x0202, 0x0204, 0x0206, 0x0208, 0x020a, 0x020c, 0x020e, + 0x0400, 0x0402, 0x0404, 0x0406, 0x0408, 0x040a, 0x040c, 0x040e, + 0x0600, 0x0602, 0x0604, 0x0606, 0x0608, 0x060a, 0x060c, 0x060e, + 0x0800, 0x0802, 0x0804, 0x0806, 0x0808, 0x080a, 0x080c, 0x080e +}; + +const uint32 Cx4::sin_table[256] = { + 0x000000, 0x000324, 0x000648, 0x00096c, 0x000c8f, 0x000fb2, 0x0012d5, 0x0015f6, + 0x001917, 0x001c37, 0x001f56, 0x002273, 0x002590, 0x0028aa, 0x002bc4, 0x002edb, + 0x0031f1, 0x003505, 0x003817, 0x003b26, 0x003e33, 0x00413e, 0x004447, 0x00474d, + 0x004a50, 0x004d50, 0x00504d, 0x005347, 0x00563e, 0x005931, 0x005c22, 0x005f0e, + 0x0061f7, 0x0064dc, 0x0067bd, 0x006a9b, 0x006d74, 0x007049, 0x007319, 0x0075e5, + 0x0078ad, 0x007b70, 0x007e2e, 0x0080e7, 0x00839c, 0x00864b, 0x0088f5, 0x008b9a, + 0x008e39, 0x0090d3, 0x009368, 0x0095f6, 0x00987f, 0x009b02, 0x009d7f, 0x009ff6, + 0x00a267, 0x00a4d2, 0x00a736, 0x00a994, 0x00abeb, 0x00ae3b, 0x00b085, 0x00b2c8, + 0x00b504, 0x00b73a, 0x00b968, 0x00bb8f, 0x00bdae, 0x00bfc7, 0x00c1d8, 0x00c3e2, + 0x00c5e4, 0x00c7de, 0x00c9d1, 0x00cbbb, 0x00cd9f, 0x00cf7a, 0x00d14d, 0x00d318, + 0x00d4db, 0x00d695, 0x00d848, 0x00d9f2, 0x00db94, 0x00dd2d, 0x00debe, 0x00e046, + 0x00e1c5, 0x00e33c, 0x00e4aa, 0x00e60f, 0x00e76b, 0x00e8bf, 0x00ea09, 0x00eb4b, + 0x00ec83, 0x00edb2, 0x00eed8, 0x00eff5, 0x00f109, 0x00f213, 0x00f314, 0x00f40b, + 0x00f4fa, 0x00f5de, 0x00f6ba, 0x00f78b, 0x00f853, 0x00f912, 0x00f9c7, 0x00fa73, + 0x00fb14, 0x00fbac, 0x00fc3b, 0x00fcbf, 0x00fd3a, 0x00fdab, 0x00fe13, 0x00fe70, + 0x00fec4, 0x00ff0e, 0x00ff4e, 0x00ff84, 0x00ffb1, 0x00ffd3, 0x00ffec, 0x00fffb, + 0x000000, 0xfffcdb, 0xfff9b7, 0xfff693, 0xfff370, 0xfff04d, 0xffed2a, 0xffea09, + 0xffe6e8, 0xffe3c8, 0xffe0a9, 0xffdd8c, 0xffda6f, 0xffd755, 0xffd43b, 0xffd124, + 0xffce0e, 0xffcafa, 0xffc7e8, 0xffc4d9, 0xffc1cc, 0xffbec1, 0xffbbb8, 0xffb8b2, + 0xffb5af, 0xffb2af, 0xffafb2, 0xffacb8, 0xffa9c1, 0xffa6ce, 0xffa3dd, 0xffa0f1, + 0xff9e08, 0xff9b23, 0xff9842, 0xff9564, 0xff928b, 0xff8fb6, 0xff8ce6, 0xff8a1a, + 0xff8752, 0xff848f, 0xff81d1, 0xff7f18, 0xff7c63, 0xff79b4, 0xff770a, 0xff7465, + 0xff71c6, 0xff6f2c, 0xff6c97, 0xff6a09, 0xff6780, 0xff64fd, 0xff6280, 0xff6009, + 0xff5d98, 0xff5b2d, 0xff58c9, 0xff566b, 0xff5414, 0xff51c4, 0xff4f7a, 0xff4d37, + 0xff4afb, 0xff48c5, 0xff4697, 0xff4470, 0xff4251, 0xff4038, 0xff3e27, 0xff3c1e, + 0xff3a1b, 0xff3821, 0xff362e, 0xff3444, 0xff3260, 0xff3085, 0xff2eb2, 0xff2ce7, + 0xff2b24, 0xff296a, 0xff27b7, 0xff260d, 0xff246b, 0xff22d2, 0xff2141, 0xff1fb9, + 0xff1e3a, 0xff1cc3, 0xff1b55, 0xff19f0, 0xff1894, 0xff1740, 0xff15f6, 0xff14b4, + 0xff137c, 0xff124d, 0xff1127, 0xff100a, 0xff0ef6, 0xff0dec, 0xff0ceb, 0xff0bf4, + 0xff0b05, 0xff0a21, 0xff0945, 0xff0874, 0xff07ac, 0xff06ed, 0xff0638, 0xff058d, + 0xff04eb, 0xff0453, 0xff03c4, 0xff0340, 0xff02c5, 0xff0254, 0xff01ec, 0xff018f, + 0xff013b, 0xff00f1, 0xff00b1, 0xff007b, 0xff004e, 0xff002c, 0xff0013, 0xff0004 +}; + +const int16 Cx4::SinTable[512] = { + 0, 402, 804, 1206, 1607, 2009, 2410, 2811, + 3211, 3611, 4011, 4409, 4808, 5205, 5602, 5997, + 6392, 6786, 7179, 7571, 7961, 8351, 8739, 9126, + 9512, 9896, 10278, 10659, 11039, 11416, 11793, 12167, + 12539, 12910, 13278, 13645, 14010, 14372, 14732, 15090, + 15446, 15800, 16151, 16499, 16846, 17189, 17530, 17869, + 18204, 18537, 18868, 19195, 19519, 19841, 20159, 20475, + 20787, 21097, 21403, 21706, 22005, 22301, 22594, 22884, + 23170, 23453, 23732, 24007, 24279, 24547, 24812, 25073, + 25330, 25583, 25832, 26077, 26319, 26557, 26790, 27020, + 27245, 27466, 27684, 27897, 28106, 28310, 28511, 28707, + 28898, 29086, 29269, 29447, 29621, 29791, 29956, 30117, + 30273, 30425, 30572, 30714, 30852, 30985, 31114, 31237, + 31357, 31471, 31581, 31685, 31785, 31881, 31971, 32057, + 32138, 32214, 32285, 32351, 32413, 32469, 32521, 32568, + 32610, 32647, 32679, 32706, 32728, 32745, 32758, 32765, + 32767, 32765, 32758, 32745, 32728, 32706, 32679, 32647, + 32610, 32568, 32521, 32469, 32413, 32351, 32285, 32214, + 32138, 32057, 31971, 31881, 31785, 31685, 31581, 31471, + 31357, 31237, 31114, 30985, 30852, 30714, 30572, 30425, + 30273, 30117, 29956, 29791, 29621, 29447, 29269, 29086, + 28898, 28707, 28511, 28310, 28106, 27897, 27684, 27466, + 27245, 27020, 26790, 26557, 26319, 26077, 25832, 25583, + 25330, 25073, 24812, 24547, 24279, 24007, 23732, 23453, + 23170, 22884, 22594, 22301, 22005, 21706, 21403, 21097, + 20787, 20475, 20159, 19841, 19519, 19195, 18868, 18537, + 18204, 17869, 17530, 17189, 16846, 16499, 16151, 15800, + 15446, 15090, 14732, 14372, 14010, 13645, 13278, 12910, + 12539, 12167, 11793, 11416, 11039, 10659, 10278, 9896, + 9512, 9126, 8739, 8351, 7961, 7571, 7179, 6786, + 6392, 5997, 5602, 5205, 4808, 4409, 4011, 3611, + 3211, 2811, 2410, 2009, 1607, 1206, 804, 402, + 0, -402, -804, -1206, -1607, -2009, -2410, -2811, + -3211, -3611, -4011, -4409, -4808, -5205, -5602, -5997, + -6392, -6786, -7179, -7571, -7961, -8351, -8739, -9126, + -9512, -9896, -10278, -10659, -11039, -11416, -11793, -12167, + -12539, -12910, -13278, -13645, -14010, -14372, -14732, -15090, + -15446, -15800, -16151, -16499, -16846, -17189, -17530, -17869, + -18204, -18537, -18868, -19195, -19519, -19841, -20159, -20475, + -20787, -21097, -21403, -21706, -22005, -22301, -22594, -22884, + -23170, -23453, -23732, -24007, -24279, -24547, -24812, -25073, + -25330, -25583, -25832, -26077, -26319, -26557, -26790, -27020, + -27245, -27466, -27684, -27897, -28106, -28310, -28511, -28707, + -28898, -29086, -29269, -29447, -29621, -29791, -29956, -30117, + -30273, -30425, -30572, -30714, -30852, -30985, -31114, -31237, + -31357, -31471, -31581, -31685, -31785, -31881, -31971, -32057, + -32138, -32214, -32285, -32351, -32413, -32469, -32521, -32568, + -32610, -32647, -32679, -32706, -32728, -32745, -32758, -32765, + -32767, -32765, -32758, -32745, -32728, -32706, -32679, -32647, + -32610, -32568, -32521, -32469, -32413, -32351, -32285, -32214, + -32138, -32057, -31971, -31881, -31785, -31685, -31581, -31471, + -31357, -31237, -31114, -30985, -30852, -30714, -30572, -30425, + -30273, -30117, -29956, -29791, -29621, -29447, -29269, -29086, + -28898, -28707, -28511, -28310, -28106, -27897, -27684, -27466, + -27245, -27020, -26790, -26557, -26319, -26077, -25832, -25583, + -25330, -25073, -24812, -24547, -24279, -24007, -23732, -23453, + -23170, -22884, -22594, -22301, -22005, -21706, -21403, -21097, + -20787, -20475, -20159, -19841, -19519, -19195, -18868, -18537, + -18204, -17869, -17530, -17189, -16846, -16499, -16151, -15800, + -15446, -15090, -14732, -14372, -14010, -13645, -13278, -12910, + -12539, -12167, -11793, -11416, -11039, -10659, -10278, -9896, + -9512, -9126, -8739, -8351, -7961, -7571, -7179, -6786, + -6392, -5997, -5602, -5205, -4808, -4409, -4011, -3611, + -3211, -2811, -2410, -2009, -1607, -1206, -804, -402 +}; + +const int16 Cx4::CosTable[512] = { + 32767, 32765, 32758, 32745, 32728, 32706, 32679, 32647, + 32610, 32568, 32521, 32469, 32413, 32351, 32285, 32214, + 32138, 32057, 31971, 31881, 31785, 31685, 31581, 31471, + 31357, 31237, 31114, 30985, 30852, 30714, 30572, 30425, + 30273, 30117, 29956, 29791, 29621, 29447, 29269, 29086, + 28898, 28707, 28511, 28310, 28106, 27897, 27684, 27466, + 27245, 27020, 26790, 26557, 26319, 26077, 25832, 25583, + 25330, 25073, 24812, 24547, 24279, 24007, 23732, 23453, + 23170, 22884, 22594, 22301, 22005, 21706, 21403, 21097, + 20787, 20475, 20159, 19841, 19519, 19195, 18868, 18537, + 18204, 17869, 17530, 17189, 16846, 16499, 16151, 15800, + 15446, 15090, 14732, 14372, 14010, 13645, 13278, 12910, + 12539, 12167, 11793, 11416, 11039, 10659, 10278, 9896, + 9512, 9126, 8739, 8351, 7961, 7571, 7179, 6786, + 6392, 5997, 5602, 5205, 4808, 4409, 4011, 3611, + 3211, 2811, 2410, 2009, 1607, 1206, 804, 402, + 0, -402, -804, -1206, -1607, -2009, -2410, -2811, + -3211, -3611, -4011, -4409, -4808, -5205, -5602, -5997, + -6392, -6786, -7179, -7571, -7961, -8351, -8739, -9126, + -9512, -9896, -10278, -10659, -11039, -11416, -11793, -12167, + -12539, -12910, -13278, -13645, -14010, -14372, -14732, -15090, + -15446, -15800, -16151, -16499, -16846, -17189, -17530, -17869, + -18204, -18537, -18868, -19195, -19519, -19841, -20159, -20475, + -20787, -21097, -21403, -21706, -22005, -22301, -22594, -22884, + -23170, -23453, -23732, -24007, -24279, -24547, -24812, -25073, + -25330, -25583, -25832, -26077, -26319, -26557, -26790, -27020, + -27245, -27466, -27684, -27897, -28106, -28310, -28511, -28707, + -28898, -29086, -29269, -29447, -29621, -29791, -29956, -30117, + -30273, -30425, -30572, -30714, -30852, -30985, -31114, -31237, + -31357, -31471, -31581, -31685, -31785, -31881, -31971, -32057, + -32138, -32214, -32285, -32351, -32413, -32469, -32521, -32568, + -32610, -32647, -32679, -32706, -32728, -32745, -32758, -32765, + -32767, -32765, -32758, -32745, -32728, -32706, -32679, -32647, + -32610, -32568, -32521, -32469, -32413, -32351, -32285, -32214, + -32138, -32057, -31971, -31881, -31785, -31685, -31581, -31471, + -31357, -31237, -31114, -30985, -30852, -30714, -30572, -30425, + -30273, -30117, -29956, -29791, -29621, -29447, -29269, -29086, + -28898, -28707, -28511, -28310, -28106, -27897, -27684, -27466, + -27245, -27020, -26790, -26557, -26319, -26077, -25832, -25583, + -25330, -25073, -24812, -24547, -24279, -24007, -23732, -23453, + -23170, -22884, -22594, -22301, -22005, -21706, -21403, -21097, + -20787, -20475, -20159, -19841, -19519, -19195, -18868, -18537, + -18204, -17869, -17530, -17189, -16846, -16499, -16151, -15800, + -15446, -15090, -14732, -14372, -14010, -13645, -13278, -12910, + -12539, -12167, -11793, -11416, -11039, -10659, -10278, -9896, + -9512, -9126, -8739, -8351, -7961, -7571, -7179, -6786, + -6392, -5997, -5602, -5205, -4808, -4409, -4011, -3611, + -3211, -2811, -2410, -2009, -1607, -1206, -804, -402, + 0, 402, 804, 1206, 1607, 2009, 2410, 2811, + 3211, 3611, 4011, 4409, 4808, 5205, 5602, 5997, + 6392, 6786, 7179, 7571, 7961, 8351, 8739, 9126, + 9512, 9896, 10278, 10659, 11039, 11416, 11793, 12167, + 12539, 12910, 13278, 13645, 14010, 14372, 14732, 15090, + 15446, 15800, 16151, 16499, 16846, 17189, 17530, 17869, + 18204, 18537, 18868, 19195, 19519, 19841, 20159, 20475, + 20787, 21097, 21403, 21706, 22005, 22301, 22594, 22884, + 23170, 23453, 23732, 24007, 24279, 24547, 24812, 25073, + 25330, 25583, 25832, 26077, 26319, 26557, 26790, 27020, + 27245, 27466, 27684, 27897, 28106, 28310, 28511, 28707, + 28898, 29086, 29269, 29447, 29621, 29791, 29956, 30117, + 30273, 30425, 30572, 30714, 30852, 30985, 31114, 31237, + 31357, 31471, 31581, 31685, 31785, 31881, 31971, 32057, + 32138, 32214, 32285, 32351, 32413, 32469, 32521, 32568, + 32610, 32647, 32679, 32706, 32728, 32745, 32758, 32765 +}; + +#endif diff --git a/asnes/chip/cx4/functions.cpp b/asnes/chip/cx4/functions.cpp new file mode 100755 index 00000000..7466ffb8 --- /dev/null +++ b/asnes/chip/cx4/functions.cpp @@ -0,0 +1,251 @@ +#ifdef CX4_CPP + +#include +#define Tan(a) (CosTable[a] ? ((((int32)SinTable[a]) << 16) / CosTable[a]) : 0x80000000) +#define sar(b, n) ((b) >> (n)) +#ifdef PI +#undef PI +#endif +#define PI 3.1415926535897932384626433832795 + +//Wireframe Helpers +void Cx4::C4TransfWireFrame() { + double c4x = (double)C4WFXVal; + double c4y = (double)C4WFYVal; + double c4z = (double)C4WFZVal - 0x95; + double tanval, c4x2, c4y2, c4z2; + + //Rotate X + tanval = -(double)C4WFX2Val * PI * 2 / 128; + c4y2 = c4y * ::cos(tanval) - c4z * ::sin(tanval); + c4z2 = c4y * ::sin(tanval) + c4z * ::cos(tanval); + + //Rotate Y + tanval = -(double)C4WFY2Val * PI * 2 / 128; + c4x2 = c4x * ::cos(tanval) + c4z2 * ::sin(tanval); + c4z = c4x * -::sin(tanval) + c4z2 * ::cos(tanval); + + //Rotate Z + tanval = -(double)C4WFDist * PI * 2 / 128; + c4x = c4x2 * ::cos(tanval) - c4y2 * ::sin(tanval); + c4y = c4x2 * ::sin(tanval) + c4y2 * ::cos(tanval); + + //Scale + C4WFXVal = (int16)(c4x * C4WFScale / (0x90 * (c4z + 0x95)) * 0x95); + C4WFYVal = (int16)(c4y * C4WFScale / (0x90 * (c4z + 0x95)) * 0x95); +} + +void Cx4::C4CalcWireFrame() { + C4WFXVal = C4WFX2Val - C4WFXVal; + C4WFYVal = C4WFY2Val - C4WFYVal; + + if(abs(C4WFXVal) > abs(C4WFYVal)) { + C4WFDist = abs(C4WFXVal) + 1; + C4WFYVal = (256 * (long)C4WFYVal) / abs(C4WFXVal); + C4WFXVal = (C4WFXVal < 0) ? -256 : 256; + } else if(C4WFYVal != 0) { + C4WFDist = abs(C4WFYVal) + 1; + C4WFXVal = (256 * (long)C4WFXVal) / abs(C4WFYVal); + C4WFYVal = (C4WFYVal < 0) ? -256 : 256; + } else { + C4WFDist = 0; + } +} + +void Cx4::C4TransfWireFrame2() { + double c4x = (double)C4WFXVal; + double c4y = (double)C4WFYVal; + double c4z = (double)C4WFZVal; + double tanval, c4x2, c4y2, c4z2; + + //Rotate X + tanval = -(double)C4WFX2Val * PI * 2 / 128; + c4y2 = c4y * ::cos(tanval) - c4z * ::sin(tanval); + c4z2 = c4y * ::sin(tanval) + c4z * ::cos(tanval); + + //Rotate Y + tanval = -(double)C4WFY2Val * PI * 2 / 128; + c4x2 = c4x * ::cos(tanval) + c4z2 * ::sin(tanval); + c4z = c4x * -::sin(tanval) + c4z2 * ::cos(tanval); + + //Rotate Z + tanval = -(double)C4WFDist * PI * 2 / 128; + c4x = c4x2 * ::cos(tanval) - c4y2 * ::sin(tanval); + c4y = c4x2 * ::sin(tanval) + c4y2 * ::cos(tanval); + + //Scale + C4WFXVal = (int16)(c4x * C4WFScale / 0x100); + C4WFYVal = (int16)(c4y * C4WFScale / 0x100); +} + +void Cx4::C4DrawWireFrame() { + uint32 line = readl(0x1f80); + uint32 point1, point2; + int16 X1, Y1, Z1; + int16 X2, Y2, Z2; + uint8 Color; + + for(int32 i = ram[0x0295]; i > 0; i--, line += 5) { + if(bus.read(line) == 0xff && bus.read(line + 1) == 0xff) { + int32 tmp = line - 5; + while(bus.read(tmp + 2) == 0xff && bus.read(tmp + 3) == 0xff && (tmp + 2) >= 0) { tmp -= 5; } + point1 = (read(0x1f82) << 16) | (bus.read(tmp + 2) << 8) | bus.read(tmp + 3); + } else { + point1 = (read(0x1f82) << 16) | (bus.read(line) << 8) | bus.read(line + 1); + } + point2 = (read(0x1f82) << 16) | (bus.read(line + 2) << 8) | bus.read(line + 3); + + X1=(bus.read(point1 + 0) << 8) | bus.read(point1 + 1); + Y1=(bus.read(point1 + 2) << 8) | bus.read(point1 + 3); + Z1=(bus.read(point1 + 4) << 8) | bus.read(point1 + 5); + X2=(bus.read(point2 + 0) << 8) | bus.read(point2 + 1); + Y2=(bus.read(point2 + 2) << 8) | bus.read(point2 + 3); + Z2=(bus.read(point2 + 4) << 8) | bus.read(point2 + 5); + Color = bus.read(line + 4); + C4DrawLine(X1, Y1, Z1, X2, Y2, Z2, Color); + } +} + +void Cx4::C4DrawLine(int32 X1, int32 Y1, int16 Z1, int32 X2, int32 Y2, int16 Z2, uint8 Color) { + //Transform coordinates + C4WFXVal = (int16)X1; + C4WFYVal = (int16)Y1; + C4WFZVal = Z1; + C4WFScale = read(0x1f90); + C4WFX2Val = read(0x1f86); + C4WFY2Val = read(0x1f87); + C4WFDist = read(0x1f88); + C4TransfWireFrame2(); + X1 = (C4WFXVal + 48) << 8; + Y1 = (C4WFYVal + 48) << 8; + + C4WFXVal = (int16)X2; + C4WFYVal = (int16)Y2; + C4WFZVal = Z2; + C4TransfWireFrame2(); + X2 = (C4WFXVal + 48) << 8; + Y2 = (C4WFYVal + 48) << 8; + + //Get line info + C4WFXVal = (int16)(X1 >> 8); + C4WFYVal = (int16)(Y1 >> 8); + C4WFX2Val = (int16)(X2 >> 8); + C4WFY2Val = (int16)(Y2 >> 8); + C4CalcWireFrame(); + X2 = (int16)C4WFXVal; + Y2 = (int16)C4WFYVal; + + //Render line + for(int32 i = C4WFDist ? C4WFDist : 1; i > 0; i--) { + if(X1 > 0xff && Y1 > 0xff && X1 < 0x6000 && Y1 < 0x6000) { + uint16 addr = (((Y1 >> 8) >> 3) << 8) - (((Y1 >> 8) >> 3) << 6) + (((X1 >> 8) >> 3) << 4) + ((Y1 >> 8) & 7) * 2; + uint8 bit = 0x80 >> ((X1 >> 8) & 7); + ram[addr + 0x300] &= ~bit; + ram[addr + 0x301] &= ~bit; + if(Color & 1) ram[addr + 0x300] |= bit; + if(Color & 2) ram[addr + 0x301] |= bit; + } + X1 += X2; + Y1 += Y2; + } +} + +void Cx4::C4DoScaleRotate(int row_padding) { + int16 A, B, C, D; + + //Calculate matrix + int32 XScale = readw(0x1f8f); + int32 YScale = readw(0x1f92); + + if(XScale & 0x8000)XScale = 0x7fff; + if(YScale & 0x8000)YScale = 0x7fff; + + if(readw(0x1f80) == 0) { //no rotation + A = (int16)XScale; + B = 0; + C = 0; + D = (int16)YScale; + } else if(readw(0x1f80) == 128) { //90 degree rotation + A = 0; + B = (int16)(-YScale); + C = (int16)XScale; + D = 0; + } else if(readw(0x1f80) == 256) { //180 degree rotation + A = (int16)(-XScale); + B = 0; + C = 0; + D = (int16)(-YScale); + } else if(readw(0x1f80) == 384) { //270 degree rotation + A = 0; + B = (int16)YScale; + C = (int16)(-XScale); + D = 0; + } else { + A = (int16) sar(CosTable[readw(0x1f80) & 0x1ff] * XScale, 15); + B = (int16)(-sar(SinTable[readw(0x1f80) & 0x1ff] * YScale, 15)); + C = (int16) sar(SinTable[readw(0x1f80) & 0x1ff] * XScale, 15); + D = (int16) sar(CosTable[readw(0x1f80) & 0x1ff] * YScale, 15); + } + + //Calculate Pixel Resolution + uint8 w = read(0x1f89) & ~7; + uint8 h = read(0x1f8c) & ~7; + + //Clear the output RAM + memset(ram, 0, (w + row_padding / 4) * h / 2); + + int32 Cx = (int16)readw(0x1f83); + int32 Cy = (int16)readw(0x1f86); + + //Calculate start position (i.e. (Ox, Oy) = (0, 0)) + //The low 12 bits are fractional, so (Cx<<12) gives us the Cx we want in + //the function. We do Cx*A etc normally because the matrix parameters + //already have the fractional parts. + int32 LineX = (Cx << 12) - Cx * A - Cx * B; + int32 LineY = (Cy << 12) - Cy * C - Cy * D; + + //Start loop + uint32 X, Y; + uint8 byte; + int32 outidx = 0; + uint8 bit = 0x80; + + for(int32 y = 0; y < h; y++) { + X = LineX; + Y = LineY; + for(int32 x = 0; x < w; x++) { + if((X >> 12) >= w || (Y >> 12) >= h) { + byte = 0; + } else { + uint32 addr = (Y >> 12) * w + (X >> 12); + byte = read(0x600 + (addr >> 1)); + if(addr & 1) { byte >>= 4; } + } + + //De-bitplanify + if(byte & 1) ram[outidx ] |= bit; + if(byte & 2) ram[outidx + 1] |= bit; + if(byte & 4) ram[outidx + 16] |= bit; + if(byte & 8) ram[outidx + 17] |= bit; + + bit >>= 1; + if(!bit) { + bit = 0x80; + outidx += 32; + } + + X += A; //Add 1 to output x => add an A and a C + Y += C; + } + outidx += 2 + row_padding; + if(outidx & 0x10) { + outidx &= ~0x10; + } else { + outidx -= w * 4 + row_padding; + } + LineX += B; //Add 1 to output y => add a B and a D + LineY += D; + } +} + +#endif diff --git a/asnes/chip/cx4/oam.cpp b/asnes/chip/cx4/oam.cpp new file mode 100755 index 00000000..dcda69e4 --- /dev/null +++ b/asnes/chip/cx4/oam.cpp @@ -0,0 +1,228 @@ +#ifdef CX4_CPP + +//Build OAM +void Cx4::op00_00() { + uint32 oamptr = ram[0x626] << 2; + for(int32 i = 0x1fd; i > oamptr && i >= 0; i -= 4) { + //clear oam-to-be + if(i >= 0) ram[i] = 0xe0; + } + + uint16 globalx, globaly; + uint32 oamptr2; + int16 sprx, spry; + uint8 sprname, sprattr; + uint8 sprcount; + + globalx = readw(0x621); + globaly = readw(0x623); + oamptr2 = 0x200 + (ram[0x626] >> 2); + + if(!ram[0x620]) return; + + sprcount = 128 - ram[0x626]; + uint8 offset = (ram[0x626] & 3) * 2; + uint32 srcptr = 0x220; + + for(int i = ram[0x620]; i > 0 && sprcount > 0; i--, srcptr += 16) { + sprx = readw(srcptr) - globalx; + spry = readw(srcptr + 2) - globaly; + sprname = ram[srcptr + 5]; + sprattr = ram[srcptr + 4] | ram[srcptr + 6]; + + uint32 spraddr = readl(srcptr + 7); + if(bus.read(spraddr)) { + int16 x, y; + for(int sprcnt = bus.read(spraddr++); sprcnt > 0 && sprcount > 0; sprcnt--, spraddr += 4) { + x = (int8)bus.read(spraddr + 1); + if(sprattr & 0x40) { + x = -x - ((bus.read(spraddr) & 0x20) ? 16 : 8); + } + x += sprx; + if(x >= -16 && x <= 272) { + y = (int8)bus.read(spraddr + 2); + if(sprattr & 0x80) { + y = -y - ((bus.read(spraddr) & 0x20) ? 16 : 8); + } + y += spry; + if(y >= -16 && y <= 224) { + ram[oamptr ] = (uint8)x; + ram[oamptr + 1] = (uint8)y; + ram[oamptr + 2] = sprname + bus.read(spraddr + 3); + ram[oamptr + 3] = sprattr ^ (bus.read(spraddr) & 0xc0); + ram[oamptr2] &= ~(3 << offset); + if(x & 0x100) ram[oamptr2] |= 1 << offset; + if(bus.read(spraddr) & 0x20) ram[oamptr2] |= 2 << offset; + oamptr += 4; + sprcount--; + offset = (offset + 2) & 6; + if(!offset)oamptr2++; + } + } + } + } else if(sprcount > 0) { + ram[oamptr ] = (uint8)sprx; + ram[oamptr + 1] = (uint8)spry; + ram[oamptr + 2] = sprname; + ram[oamptr + 3] = sprattr; + ram[oamptr2] &= ~(3 << offset); + if(sprx & 0x100) ram[oamptr2] |= 3 << offset; + else ram[oamptr2] |= 2 << offset; + oamptr += 4; + sprcount--; + offset = (offset + 2) & 6; + if(!offset) oamptr2++; + } + } +} + +//Scale and Rotate +void Cx4::op00_03() { + C4DoScaleRotate(0); +} + +//Transform Lines +void Cx4::op00_05() { + C4WFX2Val = read(0x1f83); + C4WFY2Val = read(0x1f86); + C4WFDist = read(0x1f89); + C4WFScale = read(0x1f8c); + +//Transform Vertices +uint32 ptr = 0; + for(int32 i = readw(0x1f80); i > 0; i--, ptr += 0x10) { + C4WFXVal = readw(ptr + 1); + C4WFYVal = readw(ptr + 5); + C4WFZVal = readw(ptr + 9); + C4TransfWireFrame(); + + //Displace + writew(ptr + 1, C4WFXVal + 0x80); + writew(ptr + 5, C4WFYVal + 0x50); + } + + writew(0x600, 23); + writew(0x602, 0x60); + writew(0x605, 0x40); + writew(0x600 + 8, 23); + writew(0x602 + 8, 0x60); + writew(0x605 + 8, 0x40); + + ptr = 0xb02; + uint32 ptr2 = 0; + + for(int32 i = readw(0xb00); i > 0; i--, ptr += 2, ptr2 += 8) { + C4WFXVal = readw((read(ptr + 0) << 4) + 1); + C4WFYVal = readw((read(ptr + 0) << 4) + 5); + C4WFX2Val = readw((read(ptr + 1) << 4) + 1); + C4WFY2Val = readw((read(ptr + 1) << 4) + 5); + C4CalcWireFrame(); + writew(ptr2 + 0x600, C4WFDist ? C4WFDist : 1); + writew(ptr2 + 0x602, C4WFXVal); + writew(ptr2 + 0x605, C4WFYVal); + } +} + +//Scale and Rotate +void Cx4::op00_07() { + C4DoScaleRotate(64); +} + +//Draw Wireframe +void Cx4::op00_08() { + C4DrawWireFrame(); +} + +//Disintegrate +void Cx4::op00_0b() { + uint8 width, height; + uint32 startx, starty; + uint32 srcptr; + uint32 x, y; + int32 scalex, scaley; + int32 cx, cy; + int32 i, j; + + width = read(0x1f89); + height = read(0x1f8c); + cx = readw(0x1f80); + cy = readw(0x1f83); + + scalex = (int16)readw(0x1f86); + scaley = (int16)readw(0x1f8f); + startx = -cx * scalex + (cx << 8); + starty = -cy * scaley + (cy << 8); + srcptr = 0x600; + + for(i = 0; i < (width * height) >> 1; i++) { + write(i, 0); + } + + for(y = starty, i = 0;i < height; i++, y += scaley) { + for(x = startx, j = 0;j < width; j++, x += scalex) { + if((x >> 8) < width && (y >> 8) < height && (y >> 8) * width + (x >> 8) < 0x2000) { + uint8 pixel = (j & 1) ? (ram[srcptr] >> 4) : (ram[srcptr]); + int32 index = (y >> 11) * width * 4 + (x >> 11) * 32 + ((y >> 8) & 7) * 2; + uint8 mask = 0x80 >> ((x >> 8) & 7); + + if(pixel & 1) ram[index ] |= mask; + if(pixel & 2) ram[index + 1] |= mask; + if(pixel & 4) ram[index + 16] |= mask; + if(pixel & 8) ram[index + 17] |= mask; + } + if(j & 1) srcptr++; + } + } +} + +//Bitplane Wave +void Cx4::op00_0c() { + uint32 destptr = 0; + uint32 waveptr = read(0x1f83); + uint16 mask1 = 0xc0c0; + uint16 mask2 = 0x3f3f; + + for(int j = 0; j < 0x10; j++) { + do { + int16 height = -((int8)read(waveptr + 0xb00)) - 16; + for(int i = 0; i < 40; i++) { + uint16 temp = readw(destptr + wave_data[i]) & mask2; + if(height >= 0) { + if(height < 8) { + temp |= mask1 & readw(0xa00 + height * 2); + } else { + temp |= mask1 & 0xff00; + } + } + writew(destptr + wave_data[i], temp); + height++; + } + waveptr = (waveptr + 1) & 0x7f; + mask1 = (mask1 >> 2) | (mask1 << 6); + mask2 = (mask2 >> 2) | (mask2 << 6); + } while(mask1 != 0xc0c0); + destptr += 16; + + do { + int16 height = -((int8)read(waveptr + 0xb00)) - 16; + for(int i = 0; i < 40; i++) { + uint16 temp = readw(destptr + wave_data[i]) & mask2; + if(height >= 0) { + if(height < 8) { + temp |= mask1 & readw(0xa10 + height * 2); + } else { + temp |= mask1 & 0xff00; + } + } + writew(destptr + wave_data[i], temp); + height++; + } + waveptr = (waveptr + 1) & 0x7f; + mask1 = (mask1 >> 2) | (mask1 << 6); + mask2 = (mask2 >> 2) | (mask2 << 6); + } while(mask1 != 0xc0c0); + destptr += 16; + } +} + +#endif diff --git a/asnes/chip/cx4/opcodes.cpp b/asnes/chip/cx4/opcodes.cpp new file mode 100755 index 00000000..28f62c1d --- /dev/null +++ b/asnes/chip/cx4/opcodes.cpp @@ -0,0 +1,227 @@ +#ifdef CX4_CPP + +//Sprite Functions +void Cx4::op00() { + switch(reg[0x4d]) { + case 0x00: op00_00(); break; + case 0x03: op00_03(); break; + case 0x05: op00_05(); break; + case 0x07: op00_07(); break; + case 0x08: op00_08(); break; + case 0x0b: op00_0b(); break; + case 0x0c: op00_0c(); break; + } +} + +//Draw Wireframe +void Cx4::op01() { + memset(ram + 0x300, 0, 2304); + C4DrawWireFrame(); +} + +//Propulsion +void Cx4::op05() { + int32 temp = 0x10000; + if(readw(0x1f83)) { + temp = sar((temp / readw(0x1f83)) * readw(0x1f81), 8); + } + writew(0x1f80, temp); +} + +//Set Vector length +void Cx4::op0d() { + C41FXVal = readw(0x1f80); + C41FYVal = readw(0x1f83); + C41FDistVal = readw(0x1f86); + double tanval = sqrt(((double)C41FYVal) * ((double)C41FYVal) + ((double)C41FXVal) * ((double)C41FXVal)); + tanval = (double)C41FDistVal / tanval; + C41FYVal = (int16)(((double)C41FYVal * tanval) * 0.99); + C41FXVal = (int16)(((double)C41FXVal * tanval) * 0.98); + writew(0x1f89, C41FXVal); + writew(0x1f8c, C41FYVal); +} + +//Triangle +void Cx4::op10() { + r0 = ldr(0); + r1 = ldr(1); + + r4 = r0 & 0x1ff; + if(r1 & 0x8000)r1 |= ~0x7fff; + + mul(cos(r4), r1, r5, r2); + r5 = (r5 >> 16) & 0xff; + r2 = (r2 << 8) + r5; + + mul(sin(r4), r1, r5, r3); + r5 = (r5 >> 16) & 0xff; + r3 = (r3 << 8) + r5; + + str(0, r0); + str(1, r1); + str(2, r2); + str(3, r3); + str(4, r4); + str(5, r5); +} + +//Triangle +void Cx4::op13() { + r0 = ldr(0); + r1 = ldr(1); + + r4 = r0 & 0x1ff; + + mul(cos(r4), r1, r5, r2); + r5 = (r5 >> 8) & 0xffff; + r2 = (r2 << 16) + r5; + + mul(sin(r4), r1, r5, r3); + r5 = (r5 >> 8) & 0xffff; + r3 = (r3 << 16) + r5; + + str(0, r0); + str(1, r1); + str(2, r2); + str(3, r3); + str(4, r4); + str(5, r5); +} + +//Pythagorean +void Cx4::op15() { + C41FXVal = readw(0x1f80); + C41FYVal = readw(0x1f83); + C41FDist = (int16)sqrt((double)C41FXVal * (double)C41FXVal + (double)C41FYVal * (double)C41FYVal); + writew(0x1f80, C41FDist); +} + +//Calculate distance +void Cx4::op1f() { + C41FXVal = readw(0x1f80); + C41FYVal = readw(0x1f83); + if(!C41FXVal) { + C41FAngleRes = (C41FYVal > 0) ? 0x080 : 0x180; + } else { + double tanval = ((double)C41FYVal) / ((double)C41FXVal); + C41FAngleRes = (short)(atan(tanval) / (PI * 2) * 512); + C41FAngleRes = C41FAngleRes; + if(C41FXVal < 0) { + C41FAngleRes += 0x100; + } + C41FAngleRes &= 0x1ff; + } + writew(0x1f86, C41FAngleRes); +} + +//Trapezoid +void Cx4::op22() { + int16 angle1 = readw(0x1f8c) & 0x1ff; + int16 angle2 = readw(0x1f8f) & 0x1ff; + int32 tan1 = Tan(angle1); + int32 tan2 = Tan(angle2); + int16 y = readw(0x1f83) - readw(0x1f89); + int16 left, right; + + for(int32 j = 0; j < 225; j++, y++) { + if(y >= 0) { + left = sar((int32)tan1 * y, 16) - readw(0x1f80) + readw(0x1f86); + right = sar((int32)tan2 * y, 16) - readw(0x1f80) + readw(0x1f86) + readw(0x1f93); + + if(left < 0 && right < 0) { + left = 1; + right = 0; + } else if(left < 0) { + left = 0; + } else if(right < 0) { + right = 0; + } + + if(left > 255 && right > 255) { + left = 255; + right = 254; + } else if(left > 255) { + left = 255; + } else if(right > 255) { + right = 255; + } + } else { + left = 1; + right = 0; + } + ram[j + 0x800] = (uint8)left; + ram[j + 0x900] = (uint8)right; + } +} + +//Multiply +void Cx4::op25() { + r0 = ldr(0); + r1 = ldr(1); + mul(r0, r1, r0, r1); + str(0, r0); + str(1, r1); +} + +//Transform Coords +void Cx4::op2d() { + C4WFXVal = readw(0x1f81); + C4WFYVal = readw(0x1f84); + C4WFZVal = readw(0x1f87); + C4WFX2Val = read (0x1f89); + C4WFY2Val = read (0x1f8a); + C4WFDist = read (0x1f8b); + C4WFScale = readw(0x1f90); + C4TransfWireFrame2(); + writew(0x1f80, C4WFXVal); + writew(0x1f83, C4WFYVal); +} + +//Sum +void Cx4::op40() { + r0 = 0; + for(uint32 i=0;i<0x800;i++) { + r0 += ram[i]; + } + str(0, r0); +} + +//Square +void Cx4::op54() { + r0 = ldr(0); + mul(r0, r0, r1, r2); + str(1, r1); + str(2, r2); +} + +//Immediate Register +void Cx4::op5c() { + str(0, 0x000000); + immediate_reg(0); +} + +//Immediate Register (Multiple) +void Cx4::op5e() { immediate_reg( 0); } +void Cx4::op60() { immediate_reg( 3); } +void Cx4::op62() { immediate_reg( 6); } +void Cx4::op64() { immediate_reg( 9); } +void Cx4::op66() { immediate_reg(12); } +void Cx4::op68() { immediate_reg(15); } +void Cx4::op6a() { immediate_reg(18); } +void Cx4::op6c() { immediate_reg(21); } +void Cx4::op6e() { immediate_reg(24); } +void Cx4::op70() { immediate_reg(27); } +void Cx4::op72() { immediate_reg(30); } +void Cx4::op74() { immediate_reg(33); } +void Cx4::op76() { immediate_reg(36); } +void Cx4::op78() { immediate_reg(39); } +void Cx4::op7a() { immediate_reg(42); } +void Cx4::op7c() { immediate_reg(45); } + +//Immediate ROM +void Cx4::op89() { + str(0, 0x054336); + str(1, 0xffffff); +} + +#endif diff --git a/asnes/chip/cx4/serialization.cpp b/asnes/chip/cx4/serialization.cpp new file mode 100755 index 00000000..1b36a4eb --- /dev/null +++ b/asnes/chip/cx4/serialization.cpp @@ -0,0 +1,39 @@ +#ifdef CX4_CPP + +void Cx4::serialize(serializer &s) { + s.array(ram); + s.array(reg); + + s.integer(r0); + s.integer(r1); + s.integer(r2); + s.integer(r3); + s.integer(r4); + s.integer(r5); + s.integer(r6); + s.integer(r7); + s.integer(r8); + s.integer(r9); + s.integer(r10); + s.integer(r11); + s.integer(r12); + s.integer(r13); + s.integer(r14); + s.integer(r15); + + s.integer(C4WFXVal); + s.integer(C4WFYVal); + s.integer(C4WFZVal); + s.integer(C4WFX2Val); + s.integer(C4WFY2Val); + s.integer(C4WFDist); + s.integer(C4WFScale); + + s.integer(C41FXVal); + s.integer(C41FYVal); + s.integer(C41FAngleRes); + s.integer(C41FDist); + s.integer(C41FDistVal); +} + +#endif diff --git a/asnes/chip/dsp1/dsp1.cpp b/asnes/chip/dsp1/dsp1.cpp new file mode 100755 index 00000000..ae71d3bf --- /dev/null +++ b/asnes/chip/dsp1/dsp1.cpp @@ -0,0 +1,33 @@ +#include + +#define DSP1_CPP +namespace SNES { + +DSP1 dsp1; +DSP1DR dsp1dr; +DSP1SR dsp1sr; + +#include "serialization.cpp" +#include "dsp1emu.cpp" + +void DSP1::init() { +} + +void DSP1::enable() { +} + +void DSP1::power() { + reset(); +} + +void DSP1::reset() { + dsp1.reset(); +} + +uint8 DSP1DR::read(unsigned addr) { return dsp1.dsp1.getDr(); } +void DSP1DR::write(unsigned addr, uint8 data) { dsp1.dsp1.setDr(data); } + +uint8 DSP1SR::read(unsigned addr) { return dsp1.dsp1.getSr(); } +void DSP1SR::write(unsigned addr, uint8 data) {} + +} diff --git a/asnes/chip/dsp1/dsp1.hpp b/asnes/chip/dsp1/dsp1.hpp new file mode 100755 index 00000000..5e373be2 --- /dev/null +++ b/asnes/chip/dsp1/dsp1.hpp @@ -0,0 +1,30 @@ +#include "dsp1emu.hpp" + +class DSP1 { +public: + void init(); + void enable(); + void power(); + void reset(); + + void serialize(serializer&); + +private: + Dsp1 dsp1; + friend class DSP1DR; + friend class DSP1SR; +}; + +class DSP1DR : public Memory { + uint8 read(unsigned addr); + void write(unsigned addr, uint8 data); +}; + +class DSP1SR : public Memory { + uint8 read(unsigned addr); + void write(unsigned addr, uint8 data); +}; + +extern DSP1 dsp1; +extern DSP1DR dsp1dr; +extern DSP1SR dsp1sr; diff --git a/asnes/chip/dsp1/dsp1emu.cpp b/asnes/chip/dsp1/dsp1emu.cpp new file mode 100755 index 00000000..d933b11c --- /dev/null +++ b/asnes/chip/dsp1/dsp1emu.cpp @@ -0,0 +1,1625 @@ +#ifdef DSP1_CPP + +// DSP-1's emulation code +// +// Based on research by Overload, The Dumper, Neviksti and Andreas Naive +// Date: June 2006 + +////////////////////////////////////////////////////////////////// + +Dsp1::Dsp1() +{ + reset(); +} + +////////////////////////////////////////////////////////////////// + +uint8 Dsp1::getSr() +{ + mSrLowByteAccess = ~mSrLowByteAccess; + if (mSrLowByteAccess) + return 0; + else + return mSr; +} + +////////////////////////////////////////////////////////////////// + +uint8 Dsp1::getDr() +{ + uint8 oDr; + + fsmStep(true, oDr); + return oDr; +} + +////////////////////////////////////////////////////////////////// + +void Dsp1::setDr(uint8 iDr) +{ + fsmStep(false, iDr); +} + +////////////////////////////////////////////////////////////////// + +void Dsp1::reset() +{ + mSr = DRC|RQM; + mSrLowByteAccess = false; + mDr = 0x0080; // Only a supposition. Is this correct? + mFreeze = false; + mFsmMajorState = WAIT_COMMAND; + memset(&shared, 0, sizeof(SharedData)); // another supposition +} + +////////////////////////////////////////////////////////////////// + +// Though the DSP-1 is unaware of the type of operation (read or write) +// we need to know what is being done by the program, as the class +// is responsible for maintaining the binding between the +// "external" and "internal" representations of the DR (data register). + +void Dsp1::fsmStep(bool read, uint8 &data) +{ + if (0 == (mSr&RQM)) return; + // Now RQM would be cleared; however, as this code is not to be used in + // a multithread environment, we will simply fake RQM operation. + // (The only exception would be Op1A's freeze.) + + // binding + if (read) + { + if (mSr&DRS) + data = static_cast(mDr>>8); + else + data = static_cast(mDr); + } + else + { + if (mSr&DRS) + { + mDr &= 0x00ff; + mDr |= data<<8; + } + else + { + mDr &= 0xff00; + mDr |= data; + } + } + + + switch (mFsmMajorState) + { + case WAIT_COMMAND: + mCommand = static_cast(mDr); + if (!(mCommand & 0xc0)) // valid command? + { + switch(mCommand) + { + // freeze cases + case 0x1a: + case 0x2a: + case 0x3a: + mFreeze = true; + break; + // normal cases + default: + mDataCounter=0; + mFsmMajorState = READ_DATA; + mSr &= ~DRC; + break; + } + } + break; + case READ_DATA: + mSr ^= DRS; + if (!(mSr&DRS)) + { + mReadBuffer[mDataCounter++] = static_cast(mDr); + if (mDataCounter >= mCommandTable[mCommand].reads) + { + (this->*mCommandTable[mCommand].callback)(mReadBuffer, mWriteBuffer); + if (0 != mCommandTable[mCommand].writes) // any output? + { + mDataCounter = 0; + mDr = static_cast(mWriteBuffer[mDataCounter]); + mFsmMajorState = WRITE_DATA; + } + else + { + mDr = 0x0080; // valid command completion + mFsmMajorState = WAIT_COMMAND; + mSr |= DRC; + } + } + } + break; + case WRITE_DATA: + mSr ^= DRS; + if (!(mSr&DRS)) + { + ++mDataCounter; + if (mDataCounter >= mCommandTable[mCommand].writes) + { + if ((mCommand == 0x0a)&&(mDr != 0x8000)) + { + // works in continuous mode + mReadBuffer[0]++; // next raster line + (this->*mCommandTable[mCommand].callback)(mReadBuffer, mWriteBuffer); + mDataCounter = 0; + mDr = static_cast(mWriteBuffer[mDataCounter]); + } + else + { + mDr = 0x0080; // valid command completion + mFsmMajorState = WAIT_COMMAND; + mSr |= DRC; + } + } + else + { + mDr = static_cast(mWriteBuffer[mDataCounter]); + } + } + break; + } + + + + // Now RQM would be set (except when executing Op1A -command equals 0x1a, 0x2a or 0x3a-). + if (mFreeze) + mSr &= ~RQM; +} + +////////////////////////////////////////////////////////////////// + +// The info on this table follows Overload's docs. + +const Dsp1::Command Dsp1::mCommandTable[0x40] = { + {&Dsp1::multiply, 2, 1}, //0x00 + {&Dsp1::attitudeA, 4, 0}, //0x01 + {&Dsp1::parameter, 7, 4}, //0x02 + {&Dsp1::subjectiveA, 3, 3}, //0x03 + {&Dsp1::triangle, 2, 2}, //0x04 + {&Dsp1::attitudeA, 4, 0}, //0x01 + {&Dsp1::project, 3, 3}, //0x06 + {&Dsp1::memoryTest, 1, 1}, //0x0f + {&Dsp1::radius, 3, 2}, //0x08 + {&Dsp1::objectiveA, 3, 3}, //0x0d + {&Dsp1::raster, 1, 4}, // 0x0a. This will normally work in continuous mode + {&Dsp1::scalarA, 3, 1}, //0x0b + {&Dsp1::rotate, 3, 2}, //0x0c + {&Dsp1::objectiveA, 3, 3}, //0x0d + {&Dsp1::target, 2, 2}, //0x0e + {&Dsp1::memoryTest, 1, 1}, //0x0f + + {&Dsp1::inverse, 2, 2}, //0x10 + {&Dsp1::attitudeB, 4, 0}, //0x11 + {&Dsp1::parameter, 7, 4}, //0x02 + {&Dsp1::subjectiveB, 3, 3}, //0x13 + {&Dsp1::gyrate, 6, 3}, //0x14 + {&Dsp1::attitudeB, 4, 0}, //0x11 + {&Dsp1::project, 3, 3}, //0x06 + {&Dsp1::memoryDump, 1, 1024}, //0x1f + {&Dsp1::range, 4, 1}, //0x18 + {&Dsp1::objectiveB, 3, 3}, //0x1d + {0, 0, 0}, // 0x1a; the chip freezes + {&Dsp1::scalarB, 3, 1}, //0x1b + {&Dsp1::polar, 6, 3}, //0x1c + {&Dsp1::objectiveB, 3, 3}, //0x1d + {&Dsp1::target, 2, 2}, //0x0e + {&Dsp1::memoryDump, 1, 1024}, //0x1f + + {&Dsp1::multiply2, 2, 1}, //0x20 + {&Dsp1::attitudeC, 4, 0}, //0x21 + {&Dsp1::parameter, 7, 4}, //0x02 + {&Dsp1::subjectiveC, 3, 3}, //0x23 + {&Dsp1::triangle, 2, 2}, //0x04 + {&Dsp1::attitudeC, 4, 0}, //0x21 + {&Dsp1::project, 3, 3}, //0x06 + {&Dsp1::memorySize, 1, 1}, //0x2f + {&Dsp1::distance, 3, 1}, //0x28 + {&Dsp1::objectiveC, 3, 3}, //0x2d + {0, 0, 0}, // 0x1a; the chip freezes + {&Dsp1::scalarC, 3, 1}, //0x2b + {&Dsp1::rotate, 3, 2}, //0x0c + {&Dsp1::objectiveC, 3, 3}, //0x2d + {&Dsp1::target, 2, 2}, //0x0e + {&Dsp1::memorySize, 1, 1}, //0x2f + + {&Dsp1::inverse, 2, 2}, //0x10 + {&Dsp1::attitudeA, 4, 0}, //0x01 + {&Dsp1::parameter, 7, 4}, //0x02 + {&Dsp1::subjectiveA, 3, 3}, //0x03 + {&Dsp1::gyrate, 6, 3}, //0x14 + {&Dsp1::attitudeA, 4, 0}, //0x01 + {&Dsp1::project, 3, 3}, //0x06 + {&Dsp1::memoryDump, 1, 1024}, //0x1f + {&Dsp1::range2, 4, 1}, //0x38 + {&Dsp1::objectiveA, 3, 3}, //0x0d + {0, 0, 0}, // 0x1a; the chip freezes + {&Dsp1::scalarA, 3, 1}, //0x0b + {&Dsp1::polar, 6, 3}, //0x1c + {&Dsp1::objectiveA, 3, 3}, //0x0d + {&Dsp1::target, 2, 2}, //0x0e + {&Dsp1::memoryDump, 1, 1024}, //0x1f +}; + +////////////////////////////////////////////////////////////////// + +void Dsp1::memoryTest(int16 *input, int16 *output) +{ + int16& Size = input[0]; + int16& Result = output[0]; + + Result = 0x0000; +} + +////////////////////////////////////////////////////////////////// + +void Dsp1::memoryDump(int16 *input, int16 *output) +{ + memcpy(output, DataRom, 1024); +} + +////////////////////////////////////////////////////////////////// + +void Dsp1::memorySize(int16 *input, int16 *output) +{ + int16& Size = output[0]; + + Size = 0x0100; +} + +////////////////////////////////////////////////////////////////// + +// 16-bit multiplication + +void Dsp1::multiply(int16 *input, int16 *output) +{ + int16& Multiplicand = input[0]; + int16& Multiplier = input[1]; + int16& Product = output[0]; + + Product = Multiplicand * Multiplier >> 15; +} + +////////////////////////////////////////////////////////////////// + +// 16-bit multiplication. 'Alternative' method. Can anyone check this carefully? + +void Dsp1::multiply2(int16 *input, int16 *output) +{ + int16& Multiplicand = input[0]; + int16& Multiplier = input[1]; + int16& Product = output[0]; + + Product = (Multiplicand * Multiplier >> 15)+1; +} + +////////////////////////////////////////////////////////////////// + +// This command determines the inverse of a floating point decimal number. + +void Dsp1::inverse(int16 *input, int16 *output) +{ + int16& Coefficient = input[0]; + int16& Exponent = input[1]; + int16& iCoefficient = output[0]; + int16& iExponent = output[1]; + + inverse(Coefficient, Exponent, iCoefficient, iExponent); +} + +////////////////////////////////////////////////////////////////// + +// Vector component calculation. Determines the X and Y components for a +// two-dimensional vector whose size and direction is known. +// Y = Radius * sin(Angle) +// X = Radius * cos(Angle) + +void Dsp1::triangle(int16 *input, int16 *output) +{ + int16& Angle = input[0]; + int16& Radius = input[1]; + int16& Y = output[0]; + int16& X = output[1]; + + Y = sin(Angle) * Radius >> 15; + X = cos(Angle) * Radius >> 15; +} + +////////////////////////////////////////////////////////////////// + +// Determines the squared norm of a vector (X,Y,Z) +// The output is Radius = X^2+Y^2+Z^2 (double integer) + +void Dsp1::radius(int16 *input, int16 *output) +{ + int16& X = input[0]; + int16& Y = input[1]; + int16& Z = input[2]; + int16& RadiusLow = output[0]; + int16& RadiusHigh = output[1]; + + int32 Radius; + + Radius = (X * X + Y * Y + Z * Z) << 1; + RadiusLow = static_cast(Radius); + RadiusHigh = static_cast(Radius>>16); +} + +////////////////////////////////////////////////////////////////// + +// Vector size comparison. This command compares the size of the vector (X,Y,Z) and the distance (R) +// from a particular point, and so may be used to determine if a point is within the sphere or radius R. +// The output is D = X^2+Y^2+Z^2-R^2 + +void Dsp1::range(int16 *input, int16 *output) +{ + int16& X = input[0]; + int16& Y = input[1]; + int16& Z = input[2]; + int16& Radius = input[3]; + int16& Range = output[0]; + + Range = (X * X + Y * Y + Z * Z - Radius * Radius) >> 15; +} + +////////////////////////////////////////////////////////////////// + +// Vector size comparison. 'Alternative' method. + +void Dsp1::range2(int16 *input, int16 *output) +{ + int16& X = input[0]; + int16& Y = input[1]; + int16& Z = input[2]; + int16& Radius = input[3]; + int16& Range = output[0]; + + Range = ((X * X + Y * Y + Z * Z - Radius * Radius) >> 15) + 1; +} + +////////////////////////////////////////////////////////////////// + +// This command calculates the norm of a (X,Y,Z) vector, or the distance from +// the point (X,Y,Z) to (0,0,0), as you prefer to see it. +// Distance = sqrt(X^2+Y^2+Z^2) +// The square root of a number 'a' is calculated by doing this: you +// write 'a' as b*2^2n, with 'b' between 1/4 and 1; then, you calculate +// c=sqrt(b) by using lineal interpolation between points of a +// look-up table and, finally, you output the result as c*2^n. + +void Dsp1::distance(int16 *input, int16 *output) +{ + int16& X = input[0]; + int16& Y = input[1]; + int16& Z = input[2]; + int16& Distance = output[0]; + + int32 Radius = X * X + Y * Y + Z * Z; + + if (Radius == 0) Distance = 0; + else + { + int16 C, E; + normalizeDouble(Radius, C, E); + if (E & 1) C = C * 0x4000 >> 15; + + int16 Pos = C * 0x0040 >> 15; + + int16 Node1 = DataRom[0x00d5 + Pos]; + int16 Node2 = DataRom[0x00d6 + Pos]; + + Distance = ((Node2 - Node1) * (C & 0x1ff) >> 9) + Node1; + +#if DSP1_VERSION < 0x0102 + if (Pos & 1) Distance -= (Node2 - Node1); +#endif + Distance >>= (E >> 1); + } +} + +////////////////////////////////////////////////////////////////// + +// Determines the (X2, Y2) coordinates obtained by rotating (X1, Y1) +// clockwise for an angle 'Angle'. The official documentation says +// 'counterclockwise', but it's obviously wrong (surprise! :P) +// +// In matrix notation: +// |X2| |cos(Angle) sin(Angle)| |X1| +// | | = | | | | +// |Y2| |-sin(Angle cos(Angle)| |Y1| + +void Dsp1::rotate(int16 *input, int16 *output) +{ + int16& Angle = input[0]; + int16& X1 = input[1]; + int16& Y1 = input[2]; + int16& X2 = output[0]; + int16& Y2 = output[1]; + + X2 = (Y1 * sin(Angle) >> 15) + (X1 * cos(Angle) >> 15); + Y2 = (Y1 * cos(Angle) >> 15) - (X1 * sin(Angle) >> 15); +} + +////////////////////////////////////////////////////////////////// + +// Calculate the coordinates (X2, Y2, Z2) obtained when rotating (X1, Y1, Z1) +// three-dimensionally. Rotation is done in the order of Az around the Z axis, +// Ay around the Y axis and Ax around the X axis. As occur with the "attitude" commands +// (see comments in the "gyrate" command), this doesn't match what explained in +// the official documentation, but it's coherent with what it is done in the "attitude" +// command (but not with the "gyrate" command). +// +// In matrix notation: +// |X2| |1 0 0 | |cosRy 0 -sinRy| | cosRz sinRz 0| |X1| +// |Y2| = |0 cosRx sinRx| | 0 1 0 | |-sinRz cosRz 0| |Y1| +// |Z2| |0 -sinRx cosRx| |sinRy 0 cosRy| | 0 0 1| |Z1| + +void Dsp1::polar(int16 *input, int16 *output) +{ + int16& Az = input[0]; + int16& Ay = input[1]; + int16& Ax = input[2]; + int16& X1 = input[3]; + int16& Y1 = input[4]; + int16& Z1 = input[5]; + int16& X2 = output[0]; + int16& Y2 = output[1]; + int16& Z2 = output[2]; + + int16 X, Y, Z; + + // Rotate Around Z + X = (Y1 * sin(Az) >> 15) + (X1 * cos(Az) >> 15); + Y = (Y1 * cos(Az) >> 15) - (X1 * sin(Az) >> 15); + X1 = X; Y1 = Y; + + // Rotate Around Y + Z = (X1 * sin(Ay) >> 15) + (Z1 * cos(Ay) >> 15); + X = (X1 * cos(Ay) >> 15) - (Z1 * sin(Ay) >> 15); + X2 = X; Z1 = Z; + + // Rotate Around X + Y = (Z1 * sin(Ax) >> 15) + (Y1 * cos(Ax) >> 15); + Z = (Z1 * cos(Ax) >> 15) - (Y1 * sin(Ax) >> 15); + Y2 = Y; Z2 = Z; +} + +////////////////////////////////////////////////////////////////// + +// Set up the elements of an "attitude matrix" (there are other ones): +// S | cosRz sinRz 0| |cosRy 0 -sinRy| |1 0 0 | +// MatrixA = - |-sinRz cosRz 0| | 0 1 0 | |0 cosRx sinRx| +// 2 | 0 0 1| |sinRy 0 cosRy| |0 -sinRx cosRx| +// This matrix is thought to be used within the following framework: +// let's suppose we define positive rotations around a system of orthogonal axes in this manner: +// a rotation of +90 degrees around axis3 converts axis2 into axis1 +// a rotation of +90 degrees around axis2 converts axis1 into axis3 +// a rotation of +90 degrees around axis1 converts axis3 into axis2 +// and let's suppose that we have defined a new orthonormal axes system (FLU) +// by doing the following operations about the standard one (XYZ): +// first rotating the XYZ system around Z by an angle Rz (obtaining X'Y'Z'), +// then rotating the resulting system around Y by an angle Ry (obtaining X''Y''Z'') +// and, finally, rotating the resulting system around X by an angle Rx (obtaining FLU) +// This FLU (forward/left/up) system represents an "attitude" and, then, the matrix here defined +// is the change of coordinates matrix that transform coordinates in the FLU +// system (the "object coordinates") into the standard XYZ system (the "global coordinates"), +// multiplied by a scale factor S/2, that is: +// |x| S |f| +// |y| * - = MatrixA * |l| +// |z| 2 |u| +// In a similar way, if we use the transpose of the matrix, we can transform global coordinates +// into object coordinates: +// |f| S |x| +// |l| * - = MatrixA_transposed * |y| +// |u| 2 |z| +// +// input[0]: S +// input[1]: Rz +// input[2]: Ry +// input[3]: Rx + +void Dsp1::attitudeA(int16 *input, int16 *output) +{ + int16& S = input[0]; + int16& Rz = input[1]; + int16& Ry = input[2]; + int16& Rx = input[3]; + + int16 SinRz = sin(Rz); + int16 CosRz = cos(Rz); + int16 SinRy = sin(Ry); + int16 CosRy = cos(Ry); + int16 SinRx = sin(Rx); + int16 CosRx = cos(Rx); + + S >>= 1; + + shared.MatrixA[0][0] = (S * CosRz >> 15) * CosRy >> 15; + shared.MatrixA[0][1] = ((S * SinRz >> 15) * CosRx >> 15) + (((S * CosRz >> 15) * SinRx >> 15) * SinRy >> 15); + shared.MatrixA[0][2] = ((S * SinRz >> 15) * SinRx >> 15) - (((S * CosRz >> 15) * CosRx >> 15) * SinRy >> 15); + + shared.MatrixA[1][0] = -((S * SinRz >> 15) * CosRy >> 15); + shared.MatrixA[1][1] = ((S * CosRz >> 15) * CosRx >> 15) - (((S * SinRz >> 15) * SinRx >> 15) * SinRy >> 15); + shared.MatrixA[1][2] = ((S * CosRz >> 15) * SinRx >> 15) + (((S * SinRz >> 15) * CosRx >> 15) * SinRy >> 15); + + shared.MatrixA[2][0] = S * SinRy >> 15; + shared.MatrixA[2][1] = -((S * SinRx >> 15) * CosRy >> 15); + shared.MatrixA[2][2] = (S * CosRx >> 15) * CosRy >> 15; +} + +////////////////////////////////////////////////////////////////// + +// Same than 'attitudeA', but with a difference attitude matrix (matrixB) + +void Dsp1::attitudeB(int16 *input, int16 *output) +{ + int16& S = input[0]; + int16& Rz = input[1]; + int16& Ry = input[2]; + int16& Rx = input[3]; + + int16 SinRz = sin(Rz); + int16 CosRz = cos(Rz); + int16 SinRy = sin(Ry); + int16 CosRy = cos(Ry); + int16 SinRx = sin(Rx); + int16 CosRx = cos(Rx); + + S >>= 1; + + shared.MatrixB[0][0] = (S * CosRz >> 15) * CosRy >> 15; + shared.MatrixB[0][1] = ((S * SinRz >> 15) * CosRx >> 15) + (((S * CosRz >> 15) * SinRx >> 15) * SinRy >> 15); + shared.MatrixB[0][2] = ((S * SinRz >> 15) * SinRx >> 15) - (((S * CosRz >> 15) * CosRx >> 15) * SinRy >> 15); + + shared.MatrixB[1][0] = -((S * SinRz >> 15) * CosRy >> 15); + shared.MatrixB[1][1] = ((S * CosRz >> 15) * CosRx >> 15) - (((S * SinRz >> 15) * SinRx >> 15) * SinRy >> 15); + shared.MatrixB[1][2] = ((S * CosRz >> 15) * SinRx >> 15) + (((S * SinRz >> 15) * CosRx >> 15) * SinRy >> 15); + + shared.MatrixB[2][0] = S * SinRy >> 15; + shared.MatrixB[2][1] = -((S * SinRx >> 15) * CosRy >> 15); + shared.MatrixB[2][2] = (S * CosRx >> 15) * CosRy >> 15; +} + +////////////////////////////////////////////////////////////////// + +// Same than 'attitudeA', but with a difference attitude matrix (matrixC) + +void Dsp1::attitudeC(int16 *input, int16 *output) +{ + int16& S = input[0]; + int16& Rz = input[1]; + int16& Ry = input[2]; + int16& Rx = input[3]; + + int16 SinRz = sin(Rz); + int16 CosRz = cos(Rz); + int16 SinRy = sin(Ry); + int16 CosRy = cos(Ry); + int16 SinRx = sin(Rx); + int16 CosRx = cos(Rx); + + S >>= 1; + + shared.MatrixC[0][0] = (S * CosRz >> 15) * CosRy >> 15; + shared.MatrixC[0][1] = ((S * SinRz >> 15) * CosRx >> 15) + (((S * CosRz >> 15) * SinRx >> 15) * SinRy >> 15); + shared.MatrixC[0][2] = ((S * SinRz >> 15) * SinRx >> 15) - (((S * CosRz >> 15) * CosRx >> 15) * SinRy >> 15); + + shared.MatrixC[1][0] = -((S * SinRz >> 15) * CosRy >> 15); + shared.MatrixC[1][1] = ((S * CosRz >> 15) * CosRx >> 15) - (((S * SinRz >> 15) * SinRx >> 15) * SinRy >> 15); + shared.MatrixC[1][2] = ((S * CosRz >> 15) * SinRx >> 15) + (((S * SinRz >> 15) * CosRx >> 15) * SinRy >> 15); + + shared.MatrixC[2][0] = S * SinRy >> 15; + shared.MatrixC[2][1] = -((S * SinRx >> 15) * CosRy >> 15); + shared.MatrixC[2][2] = (S * CosRx >> 15) * CosRy >> 15; +} + +////////////////////////////////////////////////////////////////// + +// Convert global coordinates (X,Y,Z) to object coordinates (F,L,U) +// See the comment in "attitudeA" for a explanation about the calculation. +// +// input[0]: X ; input[1]: Y ; input[2]: Z +// output[0]: F ; output[1]: L ; output[2]: U + +void Dsp1::objectiveA(int16 *input, int16 *output) +{ + int16& X = input[0]; + int16& Y = input[1]; + int16& Z = input[2]; + int16& F = output[0]; + int16& L = output[1]; + int16& U = output[2]; + + F = (shared.MatrixA[0][0] * X >> 15) + (shared.MatrixA[1][0] * Y >> 15) + (shared.MatrixA[2][0] * Z >> 15); + L = (shared.MatrixA[0][1] * X >> 15) + (shared.MatrixA[1][1] * Y >> 15) + (shared.MatrixA[2][1] * Z >> 15); + U = (shared.MatrixA[0][2] * X >> 15) + (shared.MatrixA[1][2] * Y >> 15) + (shared.MatrixA[2][2] * Z >> 15); +} + +////////////////////////////////////////////////////////////////// + +// Same than 'objectiveA', but for the 'B' attitude + +void Dsp1::objectiveB(int16 *input, int16 *output) +{ + int16& X = input[0]; + int16& Y = input[1]; + int16& Z = input[2]; + int16& F = output[0]; + int16& L = output[1]; + int16& U = output[2]; + + F = (shared.MatrixB[0][0] * X >> 15) + (shared.MatrixB[1][0] * Y >> 15) + (shared.MatrixB[2][0] * Z >> 15); + L = (shared.MatrixB[0][1] * X >> 15) + (shared.MatrixB[1][1] * Y >> 15) + (shared.MatrixB[2][1] * Z >> 15); + U = (shared.MatrixB[0][2] * X >> 15) + (shared.MatrixB[1][2] * Y >> 15) + (shared.MatrixB[2][2] * Z >> 15); +} + +////////////////////////////////////////////////////////////////// + +// Same than 'objectiveA', but for the 'C' attitude + +void Dsp1::objectiveC(int16 *input, int16 *output) +{ + int16& X = input[0]; + int16& Y = input[1]; + int16& Z = input[2]; + int16& F = output[0]; + int16& L = output[1]; + int16& U = output[2]; + + F = (shared.MatrixC[0][0] * X >> 15) + (shared.MatrixC[1][0] * Y >> 15) + (shared.MatrixC[2][0] * Z >> 15); + L = (shared.MatrixC[0][1] * X >> 15) + (shared.MatrixC[1][1] * Y >> 15) + (shared.MatrixC[2][1] * Z >> 15); + U = (shared.MatrixC[0][2] * X >> 15) + (shared.MatrixC[1][2] * Y >> 15) + (shared.MatrixC[2][2] * Z >> 15); +} + +////////////////////////////////////////////////////////////////// + +// Convert object coordinates (F,L,U) to object coordinates (X,Y,Z) +// See the comment in "attitudeA" for a explanation about the calculation. +// +// input[0]: F ; input[1]: L ; input[2]: U +// output[0]: X ; output[1]: Y ; output[2]: Z + +void Dsp1::subjectiveA(int16 *input, int16 *output) +{ + int16& F = input[0]; + int16& L = input[1]; + int16& U = input[2]; + int16& X = output[0]; + int16& Y = output[1]; + int16& Z = output[2]; + + X = (shared.MatrixA[0][0] * F >> 15) + (shared.MatrixA[0][1] * L >> 15) + (shared.MatrixA[0][2] * U >> 15); + Y = (shared.MatrixA[1][0] * F >> 15) + (shared.MatrixA[1][1] * L >> 15) + (shared.MatrixA[1][2] * U >> 15); + Z = (shared.MatrixA[2][0] * F >> 15) + (shared.MatrixA[2][1] * L >> 15) + (shared.MatrixA[2][2] * U >> 15); +} + +////////////////////////////////////////////////////////////////// + +// Same than 'subjectiveA', but for the 'B' attitude + +void Dsp1::subjectiveB(int16 *input, int16 *output) +{ + int16& F = input[0]; + int16& L = input[1]; + int16& U = input[2]; + int16& X = output[0]; + int16& Y = output[1]; + int16& Z = output[2]; + + X = (shared.MatrixB[0][0] * F >> 15) + (shared.MatrixB[0][1] * L >> 15) + (shared.MatrixB[0][2] * U >> 15); + Y = (shared.MatrixB[1][0] * F >> 15) + (shared.MatrixB[1][1] * L >> 15) + (shared.MatrixB[1][2] * U >> 15); + Z = (shared.MatrixB[2][0] * F >> 15) + (shared.MatrixB[2][1] * L >> 15) + (shared.MatrixB[2][2] * U >> 15); +} + +////////////////////////////////////////////////////////////////// + +// Same than 'subjectiveA', but for the 'C' attitude + +void Dsp1::subjectiveC(int16 *input, int16 *output) +{ + int16& F = input[0]; + int16& L = input[1]; + int16& U = input[2]; + int16& X = output[0]; + int16& Y = output[1]; + int16& Z = output[2]; + + X = (shared.MatrixC[0][0] * F >> 15) + (shared.MatrixC[0][1] * L >> 15) + (shared.MatrixC[0][2] * U >> 15); + Y = (shared.MatrixC[1][0] * F >> 15) + (shared.MatrixC[1][1] * L >> 15) + (shared.MatrixC[1][2] * U >> 15); + Z = (shared.MatrixC[2][0] * F >> 15) + (shared.MatrixC[2][1] * L >> 15) + (shared.MatrixC[2][2] * U >> 15); +} + +////////////////////////////////////////////////////////////////// + +// This command calculates the inner product (S) of a vector (X,Y,Z) and +// the first column of MatrixA. It should be noted that that first column +// represent the global coordinates of an unity vector in the forward +// direction in the object coordinate system (coordinates (1,0,0) in the FLU +// axes system). +// +// input[0]: X ; input[1]: Y ; input[2]: Z +// output[0]: S + +void Dsp1::scalarA(int16 *input, int16 *output) +{ + int16& X = input[0]; + int16& Y = input[1]; + int16& Z = input[2]; + int16& S = output[0]; + + S = (X * shared.MatrixA[0][0] + Y * shared.MatrixA[1][0] + Z * shared.MatrixA[2][0]) >> 15; +} + +////////////////////////////////////////////////////////////////// + +// Same than 'scalarA', but for the 'B' attitude + +void Dsp1::scalarB(int16 *input, int16 *output) +{ + int16& X = input[0]; + int16& Y = input[1]; + int16& Z = input[2]; + int16& S = output[0]; + + S = (X * shared.MatrixB[0][0] + Y * shared.MatrixB[1][0] + Z * shared.MatrixB[2][0]) >> 15; +} + +////////////////////////////////////////////////////////////////// + +// Same than 'scalarA', but for the 'C' attitude + +void Dsp1::scalarC(int16 *input, int16 *output) +{ + int16& X = input[0]; + int16& Y = input[1]; + int16& Z = input[2]; + int16& S = output[0]; + + S = (X * shared.MatrixC[0][0] + Y * shared.MatrixC[1][0] + Z * shared.MatrixC[2][0]) >> 15; +} + +////////////////////////////////////////////////////////////////// + +// This command determines the final attitude angles after the body with attitude angles (Ax, Ay, Az) with +// respect to the global coordinates is rotated by the minor angular displacements (DeltaF, DeltaL, DeltaU). +// It means that the XYZ axes are rotated by (Ax, Ay, Az) to obtain the FLU axes and, then, these +// are rotated by (DeltaF, DeltaL, DeltaU). The command calculates and return the new FLU angles respect to the +// XYZ system (Rx, Ry, Rz) +// The formulae are: +// Rx = Ax + (DeltaU*sin(Ay)+DeltaF*cos(Ay)) +// Ry = Ay + DeltaL - tan(Ax)*(DeltaU*cos(Ay)+DeltaF*sin(Ay)) +// Rz = Az + sec(Ax)*(DeltaU*cos(Ay)-DeltaF*sin(Ay)) +// +// Now the discussion: according to the official documentation, as described in various commands, you pass from +// XYZ to FLU by doing the rotations in the order Y, X, Z. In this command, the formulae are coherent with the +// fact that Y is the first axis to do a rotation around it. However, in the "attitude" command, while the official +// document describe it that way, we have discovered, when reverse engineering the command, that the calculated +// matrix do the rotation around Y in the second place. This incoherent behaviour of various commands is, in my +// opinion, a pretty severe implementation error. However, if you only use small "minor displacements", the error term +// introduced by that incoherence should be almost negligible. + +void Dsp1::gyrate(int16 *input, int16 *output) +{ + int16& Az = input[0]; + int16& Ax = input[1]; + int16& Ay = input[2]; + int16& U = input[3]; + int16& F = input[4]; + int16& L = input[5]; + int16& Rz = output[0]; + int16& Rx = output[1]; + int16& Ry = output[2]; + + int16 CSec, ESec, CSin, C, E; + int16 SinAy = sin(Ay); + int16 CosAy = cos(Ay); + + inverse(cos(Ax), 0, CSec, ESec); + + // Rotation Around Z + normalizeDouble(U * CosAy - F * SinAy, C, E); + + E = ESec - E; + + normalize(C * CSec >> 15, C, E); + + Rz = Az + denormalizeAndClip(C, E); + + // Rotation Around X + Rx = Ax + (U * SinAy >> 15) + (F * CosAy >> 15); + + // Rotation Around Y + normalizeDouble(U * CosAy + F * SinAy, C, E); + + E = ESec - E; + + normalize(sin(Ax), CSin, E); + + normalize(-(C * (CSec * CSin >> 15) >> 15), C, E); + + Ry = Ay + denormalizeAndClip(C, E) + L; +} + +////////////////////////////////////////////////////////////////// + +const int16 Dsp1::MaxAZS_Exp[16] = { + 0x38b4, 0x38b7, 0x38ba, 0x38be, 0x38c0, 0x38c4, 0x38c7, 0x38ca, + 0x38ce, 0x38d0, 0x38d4, 0x38d7, 0x38da, 0x38dd, 0x38e0, 0x38e4 +}; + +////////////////////////////////////////////////////////////////// + + +// Set-up the projection framework. Besides returning some values, it store in RAM some values that +// will be used by the other three projection commands (raster, target an project) +// Input: +// (Fx, Fy, Fz)-> coordinates of base point (global coordinates) +// Lfe-> distance between the base point and the viewpoint (center of projection) +// Les-> distance between the base point and the screen +// Aas-> azimuth angle (0 degrees is east; 90 degrees is north) +// Azs-> zenith angle (0 degrees is zenith) +// Output: +// Vof-> raster line of imaginary center (whatever it means ;) ) +// Vva-> raster line representing the horizon line +// (Cx, Cy)-> coordinates of the projection of the center of the screen over the ground (ground coordinates) + +void Dsp1::parameter(int16 *input, int16 *output) +{ + int16& Fx = input[0]; + int16& Fy = input[1]; + int16& Fz = input[2]; + int16& Lfe = input[3]; + int16& Les = input[4]; + int16& Aas = input[5]; + int16& Azs = input[6]; + int16& Vof = output[0]; + int16& Vva = output[1]; + int16& Cx = output[2]; + int16& Cy = output[3]; + + int16 CSec, C, E; + int16 LfeNx, LfeNy, LfeNz; + int16 LesNx, LesNy, LesNz; + + // Copy Zenith angle for clipping + int16 AZS = Azs; + + // Store Les and his coefficient and exponent when normalized + shared.Les = Les; + shared.E_Les=0; + normalize(Les, shared.C_Les, shared.E_Les); + + // Store Sine and Cosine of Azimuth and Zenith angle + shared.SinAas = sin(Aas); + shared.CosAas = cos(Aas); + shared.SinAzs = sin(Azs); + shared.CosAzs = cos(Azs); + + // normal vector to the screen (norm 1, points toward the center of projection) + shared.Nx = shared.SinAzs * -shared.SinAas >> 15; + shared.Ny = shared.SinAzs * shared.CosAas >> 15; + shared.Nz = shared.CosAzs * 0x7fff >> 15; + + // horizontal vector of the screen (Hz=0, norm 1, points toward the right of the screen) + shared.Hx = shared.CosAas*0x7fff>>15; + shared.Hy = shared.SinAas*0x7fff>>15; + + // vertical vector of the screen (norm 1, points toward the top of the screen) + shared.Vx = shared.CosAzs*-shared.SinAas>>15; + shared.Vy = shared.CosAzs*shared.CosAas>>15; + shared.Vz = -shared.SinAzs*0x7fff>>15; + + LfeNx = Lfe*shared.Nx>>15; + LfeNy = Lfe*shared.Ny>>15; + LfeNz = Lfe*shared.Nz>>15; + + // Center of Projection + shared.CentreX = Fx+LfeNx; + shared.CentreY = Fy+LfeNy; + shared.CentreZ = Fz+LfeNz; + + LesNx = Les*shared.Nx>>15; + LesNy = Les*shared.Ny>>15; + LesNz = Les*shared.Nz>>15; + + // center of the screen (global coordinates) + shared.Gx=shared.CentreX-LesNx; + shared.Gy=shared.CentreY-LesNy; + shared.Gz=shared.CentreZ-LesNz; + + + E = 0; + normalize(shared.CentreZ, C, E); + + shared.CentreZ_C = C; + shared.CentreZ_E = E; + + // Determine clip boundary and clip Zenith angle if necessary + // (Why to clip? Maybe to avoid the screen can only show sky with no ground? Only a guess...) + int16 MaxAZS = MaxAZS_Exp[-E]; + + if (AZS < 0) { + MaxAZS = -MaxAZS; + if (AZS < MaxAZS + 1) AZS = MaxAZS + 1; + } else { + if (AZS > MaxAZS) AZS = MaxAZS; + } + + // Store Sine and Cosine of clipped Zenith angle + shared.SinAZS = sin(AZS); + shared.CosAZS = cos(AZS); + + // calculate the separation of (cx, cy) from the projection of + // the 'centre of projection' over the ground... (CentreZ*tg(AZS)) + inverse(shared.CosAZS, 0, shared.SecAZS_C1, shared.SecAZS_E1); + normalize(C * shared.SecAZS_C1 >> 15, C, E); + E += shared.SecAZS_E1; + C = denormalizeAndClip(C, E) * shared.SinAZS >> 15; + + // ... and then take into account the position of the centre of + // projection and the azimuth angle + shared.CentreX += C * shared.SinAas >> 15; + shared.CentreY -= C * shared.CosAas >> 15; + + Cx = shared.CentreX; + Cy = shared.CentreY; + + // Raster number of imaginary center and horizontal line + Vof = 0; + + if ((Azs != AZS) || (Azs == MaxAZS)) + { + // correct vof and vva when Azs is outside the 'non-clipping interval' + // we have only some few Taylor coefficients, so we cannot guess which ones + // are the approximated functions and, what is worse, we don't know why + // the own clipping stuff (and, particularly, this correction) is done + if (Azs == -32768) Azs = -32767; + + C = Azs - MaxAZS; + if (C >= 0) C--; + int16 Aux = ~(C << 2); + + // Vof += x+(1/3)*x^3, where x ranges from 0 to PI/4 when Azs-MaxAZS goes from 0 to 0x2000 + C = Aux * DataRom[0x0328] >> 15; + C = (C * Aux >> 15) + DataRom[0x0327]; + Vof -= (C * Aux >> 15) * Les >> 15; + + // CosAZS *= 1+(1/2)*x^2+(5/24)*x^24, where x ranges from 0 to PI/4 when Azs-MaxAZS goes from 0 to 0x2000 + C = Aux * Aux >> 15; + Aux = (C * DataRom[0x0324] >> 15) + DataRom[0x0325]; + shared.CosAZS += (C * Aux >> 15) * shared.CosAZS >> 15; + } + + // vertical offset of the screen with regard to the horizontal plane + // containing the centre of projection + shared.VOffset = Les * shared.CosAZS >> 15; + + // The horizon line (the line in the screen that is crossed by the horizon plane + // -the horizontal plane containing the 'centre of projection'-), + // will be at distance Les*cotg(AZS) from the centre of the screen. This is difficult + // to explain but easily seen in a graph. To better see it, consider it in this way: + // Les*tg(AZS-90), draw some lines and apply basic trigonometry. ;) + inverse(shared.SinAZS, 0, CSec, E); + normalize(shared.VOffset, C, E); + normalize(C * CSec >> 15, C, E); + + if (C == -32768) { C >>= 1; E++; } + + Vva = denormalizeAndClip(-C, E); + + // Store Secant of clipped Zenith angle + inverse(shared.CosAZS, 0, shared.SecAZS_C2, shared.SecAZS_E2); +} + +////////////////////////////////////////////////////////////////// + +// Calculates the matrix which transform an object situated on a raster line (Vs) into +// his projection over the ground. The modified SecAZS is used here, so +// i don't understand the fine details, but, basically, it's done +// this way: The vertical offset between the point of projection and the +// raster line is calculated (Vs*SinAzs>>15)+VOffset, then the height of +// the center of projection is measured in that units (*CentreZ_C). If, now +// you consider the "reference case" (center of projection at an unit of height), +// the projection of a thin strip containing the raster line will have the same +// width (as the raster line would be on the ground in this case, but will suffer a +// change of scale in height (as the ground and the vertical axis would form an angle of 180-Azs degrees). +// This scale factor, when the angle 'center of screen-center of projection-raster line' is small, +// can be aproximated by the one of the center of the screen, 1/cos(Azs).(**) (Here is when it's used +// SecAZS). By last, you have to consider the effect of the azimuth angle Aas, and you are done. +// +// Using matrix notation: +// |A B| Centre_ZS | cos(Aas) -sin(Aas)| |1 0| +// ProjectionMatrix = | | = ----------- * | | * | | +// |C D| Vs*sin(Azs) |sin(Aas) cos(Aas)| |0 sec(Azs)| +// +// (**) +// If Les=1, the vertical offset between the center +// of projection and the center of the screen is Cos(Azs); then, if the vertical +// offset is 1, the ratio of the projection over the ground respect to the +// line on the screen is 1/cos(Azs). + +void Dsp1::raster(int16 *input, int16 *output) +{ + int16& Vs = input[0]; + int16& An = output[0]; + int16& Bn = output[1]; + int16& Cn = output[2]; + int16& Dn = output[3]; + + int16 C, E, C1, E1; + + inverse((Vs * shared.SinAzs >> 15) + shared.VOffset, 7, C, E); + + E += shared.CentreZ_E; + C1 = C * shared.CentreZ_C >> 15; + + E1 = E + shared.SecAZS_E2; + + normalize(C1, C, E); + C = denormalizeAndClip(C, E); + + An = C * shared.CosAas >> 15; + Cn = C * shared.SinAas >> 15; + + normalize(C1 * shared.SecAZS_C2 >> 15, C, E1); + C = denormalizeAndClip(C, E1); + + Bn = C * -shared.SinAas >> 15; + Dn = C * shared.CosAas >> 15; +} + +////////////////////////////////////////////////////////////////// + +// Calculate the projection over the ground of a selected point of screen +// It simply apply the projection matrix described in the "Raster" command +// to the vector (H,V) transposed, and add the result to the position of +// the centre of projection. +// The only special point to take into account is the directions on the screen: +// H is positive rightward, but V is positive downward; this is why +// the signs take that configuration + +void Dsp1::target(int16 *input, int16 *output) +{ + int16& H = input[0]; + int16& V = input[1]; + int16& X = output[0]; + int16& Y = output[1]; + + int16 C, E, C1, E1; + + inverse((V * shared.SinAzs >> 15) + shared.VOffset, 8, C, E); + + E += shared.CentreZ_E; + C1 = C * shared.CentreZ_C >> 15; + + E1 = E + shared.SecAZS_E1; + + H <<= 8; + normalize(C1, C, E); + C = denormalizeAndClip(C, E) * H >> 15; + + X = shared.CentreX + (C * shared.CosAas >> 15); + Y = shared.CentreY - (C * shared.SinAas >> 15); + + V <<= 8; + normalize(C1 * shared.SecAZS_C1 >> 15, C, E1); + C = denormalizeAndClip(C, E1) * V >> 15; + + X += C * -shared.SinAas >> 15; + Y += C * shared.CosAas >> 15; +} + +////////////////////////////////////////////////////////////////// + +// Calculation of the projection over the screen (H,V) of an object (X,Y,Z) and his +// 'enlargement ratio' (M). The positive directions on the screen are as described +// in the targe command. M is scaled down by 2^-7, that is, M==0x0100 means ratio 1:1 + + void Dsp1::project(int16 *input, int16 *output) +{ + int16& X = input[0]; + int16& Y = input[1]; + int16& Z = input[2]; + int16& H = output[0]; + int16& V = output[1]; + int16& M = output[2]; + + int32 aux, aux4; + int16 E, E2, E3, E4, E5, refE, E6, E7; + int16 C2, C4, C6, C8, C9, C10, C11, C12, C16, C17, C18, C19, C20, C21, C22, C23, C24, C25, C26; + int16 Px, Py, Pz; + + E4=E3=E2=E=E5=0; + + normalizeDouble(int32(X)-shared.Gx, Px, E4); + normalizeDouble(int32(Y)-shared.Gy, Py, E); + normalizeDouble(int32(Z)-shared.Gz, Pz, E3); + Px>>=1; E4--; // to avoid overflows when calculating the scalar products + Py>>=1; E--; + Pz>>=1; E3--; + + refE = (E>15); + C8=- (Py*shared.Ny>>15); + C9=- (Pz*shared.Nz>>15); + C12=C11+C8+C9; // this cannot overflow! + + aux4=C12; // de-normalization with 32-bits arithmetic + refE = 16-refE; // refE can be up to 3 + if (refE>=0) + aux4 <<=(refE); + else + aux4 >>=-(refE); + if (aux4==-1) aux4 = 0; // why? + aux4>>=1; + + aux = static_cast(shared.Les) + aux4; // Les - the scalar product of P with the normal vector of the screen + normalizeDouble(aux, C10, E2); + E2 = 15-E2; + + inverse(C10, 0, C4, E4); + C2=C4*shared.C_Les>>15; // scale factor + + + // H + E7=0; + C16= (Px*shared.Hx>>15); + C20= (Py*shared.Hy>>15); + C17=C16+C20; // scalar product of P with the normalized horizontal vector of the screen... + + C18=C17*C2>>15; // ... multiplied by the scale factor + normalize(C18, C19, E7); + H=denormalizeAndClip(C19, shared.E_Les-E2+refE+E7); + + // V + E6=0; + C21 = Px*shared.Vx>>15; + C22 = Py*shared.Vy>>15; + C23 = Pz*shared.Vz>>15; + C24=C21+C22+C23; // scalar product of P with the normalized vertical vector of the screen... + + C26=C24*C2>>15; // ... multiplied by the scale factor + normalize(C26, C25, E6); + V=denormalizeAndClip(C25, shared.E_Les-E2+refE+E6); + + // M + normalize(C2, C6, E4); + M=denormalizeAndClip(C6, E4+shared.E_Les-E2-7); // M is the scale factor divided by 2^7 +} + +////////////////////////////////////////////////////////////////// + +// Calculate the sine of the input parameter +// this is done by linear interpolation between +// the points of a look-up table + +int16 Dsp1::sin(int16 Angle) +{ + if (Angle < 0) { + if (Angle == -32768) return 0; + return -sin(-Angle); + } + int32 S = SinTable[Angle >> 8] + (MulTable[Angle & 0xff] * SinTable[0x40 + (Angle >> 8)] >> 15); + if (S > 32767) S = 32767; + return (int16) S; +} + +////////////////////////////////////////////////////////////////// + +// Calculate the cosine of the input parameter. +// It's used the same method than in sin(int16) + +int16 Dsp1::cos(int16 Angle) +{ + if (Angle < 0) { + if (Angle == -32768) return -32768; + Angle = -Angle; + } + int32 S = SinTable[0x40 + (Angle >> 8)] - (MulTable[Angle & 0xff] * SinTable[Angle >> 8] >> 15); + if (S < -32768) S = -32767; + return (int16) S; +} + +////////////////////////////////////////////////////////////////// + +// Determines the inverse of a floating point decimal number +// iCoefficient*2^iExponent = 1/(Coefficient*2^Exponent), with the output +// normalized (iCoefficient represents a number whose absolute value is between 1/2 and 1) +// To invert 'Coefficient' a first initial guess is taken from a look-up table +// and, then, two iterations of the Newton method (applied to the function +// f(x)=1/(2*x)-Coefficient) are done. This results in a close approximation (iCoefficient) to a number 'y' +// that verify Coefficient*y=1/2. This is why you have to correct the exponent by one +// unit at the end. + +void Dsp1::inverse(int16 Coefficient, int16 Exponent, int16 &iCoefficient, int16 &iExponent) +{ + // Step One: Division by Zero + if (Coefficient == 0x0000) + { + iCoefficient = 0x7fff; + iExponent = 0x002f; + } + else + { + int16 Sign = 1; + + // Step Two: Remove Sign + if (Coefficient < 0) + { + if (Coefficient < -32767) Coefficient = -32767; + Coefficient = -Coefficient; + Sign = -1; + } + + // Step Three: Normalize + while (Coefficient < 0x4000) + { + Coefficient <<= 1; + Exponent--; + } + + // Step Four: Special Case + if (Coefficient == 0x4000) + if (Sign == 1) iCoefficient = 0x7fff; + else { + iCoefficient = -0x4000; + Exponent--; + } + else { + // Step Five: Initial Guess + int16 i = DataRom[((Coefficient - 0x4000) >> 7) + 0x0065]; + + // Step Six: Iterate Newton's Method + i = (i + (-i * (Coefficient * i >> 15) >> 15)) << 1; + i = (i + (-i * (Coefficient * i >> 15) >> 15)) << 1; + + iCoefficient = i * Sign; + } + + iExponent = 1 - Exponent; + } +} + +////////////////////////////////////////////////////////////////// + +int16 Dsp1::denormalizeAndClip(int16 C, int16 E) +{ + if (E > 0) { + if (C > 0) return 32767; else if (C < 0) return -32767; + } else { + if (E < 0) return C * DataRom[0x0031 + E] >> 15; + } + return C; +} + +////////////////////////////////////////////////////////////////// + +// Normalize the input number (m), understood as ranging from -1 to 1, +// to the form: Coefficient*2^Exponent, +// where the absolute value of Coefficient is >= 1/2 +// (Coefficient>=0x4000 or Coefficient <= (int16)0xc001) + +void Dsp1::normalize(int16 m, int16 &Coefficient, int16 &Exponent) +{ + int16 i = 0x4000; + int16 e = 0; + + if (m < 0) + while ((m & i) && i) + { + i >>= 1; + e++; + } + else + while (!(m & i) && i) + { + i >>= 1; + e++; + } + + if (e > 0) + Coefficient = m * DataRom[0x21 + e] << 1; + else + Coefficient = m; + + Exponent -= e; +} + +////////////////////////////////////////////////////////////////// + +// Same than 'normalize' but with an int32 input + +void Dsp1::normalizeDouble(int32 Product, int16 &Coefficient, int16 &Exponent) +{ + int16 n = Product & 0x7fff; + int16 m = Product >> 15; + int16 i = 0x4000; + int16 e = 0; + + if (m < 0) + while ((m & i) && i) + { + i >>= 1; + e++; + } + else + while (!(m & i) && i) + { + i >>= 1; + e++; + } + + if (e > 0) + { + Coefficient = m * DataRom[0x0021 + e] << 1; + + if (e < 15) + Coefficient += n * DataRom[0x0040 - e] >> 15; + else + { + i = 0x4000; + + if (m < 0) + while ((n & i) && i) + { + i >>= 1; + e++; + } + else + while (!(n & i) && i) + { + i >>= 1; + e++; + } + + if (e > 15) + Coefficient = n * DataRom[0x0012 + e] << 1; + else + Coefficient += n; + } + } + else + Coefficient = m; + + Exponent = e; +} + +////////////////////////////////////////////////////////////////// + +// Shift to the right + +int16 Dsp1::shiftR(int16 C, int16 E) +{ + return (C * DataRom[0x0031 + E] >> 15); +} + +////////////////////////////////////////////////////////////////// + +// this is, indeed, only part of the Data ROM +const int16 Dsp1::SinTable[256] = { + 0x0000, 0x0324, 0x0647, 0x096a, 0x0c8b, 0x0fab, 0x12c8, 0x15e2, + 0x18f8, 0x1c0b, 0x1f19, 0x2223, 0x2528, 0x2826, 0x2b1f, 0x2e11, + 0x30fb, 0x33de, 0x36ba, 0x398c, 0x3c56, 0x3f17, 0x41ce, 0x447a, + 0x471c, 0x49b4, 0x4c3f, 0x4ebf, 0x5133, 0x539b, 0x55f5, 0x5842, + 0x5a82, 0x5cb4, 0x5ed7, 0x60ec, 0x62f2, 0x64e8, 0x66cf, 0x68a6, + 0x6a6d, 0x6c24, 0x6dca, 0x6f5f, 0x70e2, 0x7255, 0x73b5, 0x7504, + 0x7641, 0x776c, 0x7884, 0x798a, 0x7a7d, 0x7b5d, 0x7c29, 0x7ce3, + 0x7d8a, 0x7e1d, 0x7e9d, 0x7f09, 0x7f62, 0x7fa7, 0x7fd8, 0x7ff6, + 0x7fff, 0x7ff6, 0x7fd8, 0x7fa7, 0x7f62, 0x7f09, 0x7e9d, 0x7e1d, + 0x7d8a, 0x7ce3, 0x7c29, 0x7b5d, 0x7a7d, 0x798a, 0x7884, 0x776c, + 0x7641, 0x7504, 0x73b5, 0x7255, 0x70e2, 0x6f5f, 0x6dca, 0x6c24, + 0x6a6d, 0x68a6, 0x66cf, 0x64e8, 0x62f2, 0x60ec, 0x5ed7, 0x5cb4, + 0x5a82, 0x5842, 0x55f5, 0x539b, 0x5133, 0x4ebf, 0x4c3f, 0x49b4, + 0x471c, 0x447a, 0x41ce, 0x3f17, 0x3c56, 0x398c, 0x36ba, 0x33de, + 0x30fb, 0x2e11, 0x2b1f, 0x2826, 0x2528, 0x2223, 0x1f19, 0x1c0b, + 0x18f8, 0x15e2, 0x12c8, 0x0fab, 0x0c8b, 0x096a, 0x0647, 0x0324, + -0x0000, -0x0324, -0x0647, -0x096a, -0x0c8b, -0x0fab, -0x12c8, -0x15e2, + -0x18f8, -0x1c0b, -0x1f19, -0x2223, -0x2528, -0x2826, -0x2b1f, -0x2e11, + -0x30fb, -0x33de, -0x36ba, -0x398c, -0x3c56, -0x3f17, -0x41ce, -0x447a, + -0x471c, -0x49b4, -0x4c3f, -0x4ebf, -0x5133, -0x539b, -0x55f5, -0x5842, + -0x5a82, -0x5cb4, -0x5ed7, -0x60ec, -0x62f2, -0x64e8, -0x66cf, -0x68a6, + -0x6a6d, -0x6c24, -0x6dca, -0x6f5f, -0x70e2, -0x7255, -0x73b5, -0x7504, + -0x7641, -0x776c, -0x7884, -0x798a, -0x7a7d, -0x7b5d, -0x7c29, -0x7ce3, + -0x7d8a, -0x7e1d, -0x7e9d, -0x7f09, -0x7f62, -0x7fa7, -0x7fd8, -0x7ff6, + -0x7fff, -0x7ff6, -0x7fd8, -0x7fa7, -0x7f62, -0x7f09, -0x7e9d, -0x7e1d, + -0x7d8a, -0x7ce3, -0x7c29, -0x7b5d, -0x7a7d, -0x798a, -0x7884, -0x776c, + -0x7641, -0x7504, -0x73b5, -0x7255, -0x70e2, -0x6f5f, -0x6dca, -0x6c24, + -0x6a6d, -0x68a6, -0x66cf, -0x64e8, -0x62f2, -0x60ec, -0x5ed7, -0x5cb4, + -0x5a82, -0x5842, -0x55f5, -0x539b, -0x5133, -0x4ebf, -0x4c3f, -0x49b4, + -0x471c, -0x447a, -0x41ce, -0x3f17, -0x3c56, -0x398c, -0x36ba, -0x33de, + -0x30fb, -0x2e11, -0x2b1f, -0x2826, -0x2528, -0x2223, -0x1f19, -0x1c0b, + -0x18f8, -0x15e2, -0x12c8, -0x0fab, -0x0c8b, -0x096a, -0x0647, -0x0324}; + + ////////////////////////////////////////////////////////////////// + +// Optimised for Performance + const int16 Dsp1::MulTable[256] = { + 0x0000, 0x0003, 0x0006, 0x0009, 0x000c, 0x000f, 0x0012, 0x0015, + 0x0019, 0x001c, 0x001f, 0x0022, 0x0025, 0x0028, 0x002b, 0x002f, + 0x0032, 0x0035, 0x0038, 0x003b, 0x003e, 0x0041, 0x0045, 0x0048, + 0x004b, 0x004e, 0x0051, 0x0054, 0x0057, 0x005b, 0x005e, 0x0061, + 0x0064, 0x0067, 0x006a, 0x006d, 0x0071, 0x0074, 0x0077, 0x007a, + 0x007d, 0x0080, 0x0083, 0x0087, 0x008a, 0x008d, 0x0090, 0x0093, + 0x0096, 0x0099, 0x009d, 0x00a0, 0x00a3, 0x00a6, 0x00a9, 0x00ac, + 0x00af, 0x00b3, 0x00b6, 0x00b9, 0x00bc, 0x00bf, 0x00c2, 0x00c5, + 0x00c9, 0x00cc, 0x00cf, 0x00d2, 0x00d5, 0x00d8, 0x00db, 0x00df, + 0x00e2, 0x00e5, 0x00e8, 0x00eb, 0x00ee, 0x00f1, 0x00f5, 0x00f8, + 0x00fb, 0x00fe, 0x0101, 0x0104, 0x0107, 0x010b, 0x010e, 0x0111, + 0x0114, 0x0117, 0x011a, 0x011d, 0x0121, 0x0124, 0x0127, 0x012a, + 0x012d, 0x0130, 0x0133, 0x0137, 0x013a, 0x013d, 0x0140, 0x0143, + 0x0146, 0x0149, 0x014d, 0x0150, 0x0153, 0x0156, 0x0159, 0x015c, + 0x015f, 0x0163, 0x0166, 0x0169, 0x016c, 0x016f, 0x0172, 0x0175, + 0x0178, 0x017c, 0x017f, 0x0182, 0x0185, 0x0188, 0x018b, 0x018e, + 0x0192, 0x0195, 0x0198, 0x019b, 0x019e, 0x01a1, 0x01a4, 0x01a8, + 0x01ab, 0x01ae, 0x01b1, 0x01b4, 0x01b7, 0x01ba, 0x01be, 0x01c1, + 0x01c4, 0x01c7, 0x01ca, 0x01cd, 0x01d0, 0x01d4, 0x01d7, 0x01da, + 0x01dd, 0x01e0, 0x01e3, 0x01e6, 0x01ea, 0x01ed, 0x01f0, 0x01f3, + 0x01f6, 0x01f9, 0x01fc, 0x0200, 0x0203, 0x0206, 0x0209, 0x020c, + 0x020f, 0x0212, 0x0216, 0x0219, 0x021c, 0x021f, 0x0222, 0x0225, + 0x0228, 0x022c, 0x022f, 0x0232, 0x0235, 0x0238, 0x023b, 0x023e, + 0x0242, 0x0245, 0x0248, 0x024b, 0x024e, 0x0251, 0x0254, 0x0258, + 0x025b, 0x025e, 0x0261, 0x0264, 0x0267, 0x026a, 0x026e, 0x0271, + 0x0274, 0x0277, 0x027a, 0x027d, 0x0280, 0x0284, 0x0287, 0x028a, + 0x028d, 0x0290, 0x0293, 0x0296, 0x029a, 0x029d, 0x02a0, 0x02a3, + 0x02a6, 0x02a9, 0x02ac, 0x02b0, 0x02b3, 0x02b6, 0x02b9, 0x02bc, + 0x02bf, 0x02c2, 0x02c6, 0x02c9, 0x02cc, 0x02cf, 0x02d2, 0x02d5, + 0x02d8, 0x02db, 0x02df, 0x02e2, 0x02e5, 0x02e8, 0x02eb, 0x02ee, + 0x02f1, 0x02f5, 0x02f8, 0x02fb, 0x02fe, 0x0301, 0x0304, 0x0307, + 0x030b, 0x030e, 0x0311, 0x0314, 0x0317, 0x031a, 0x031d, 0x0321}; + +////////////////////////////////////////////////////////////////// + +// Data ROM, as logged from a DSP-1B with the 0x1f command; +// it contains the tables and constants used by the commands. +// The tables used are: two shift tables (0x022-0x031 and 0x031-0x040 -this last one +// with an error in 0x03c which has survived to all the DSP-1 revisions-); a inverse +// table (used as initial guess) at 0x065-0x0e4; a square root table (used also +// as initial guess) at 0x0e5-0x115; two sin and cos tables (used as nodes to construct +// a interpolation curve) at, respectively, 0x116-0x197 and 0x196-0x215. +// As a curiosity, in the positions 0x21c-0x31c it's contained a +// 257-points arccos table that, apparently, have been not used anywhere +// (maybe for the MaxAZS_Exp table?). + const uint16 Dsp1::DataRom[1024] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, + 0x0040, 0x0080, 0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, + 0x4000, 0x7fff, 0x4000, 0x2000, 0x1000, 0x0800, 0x0400, 0x0200, + 0x0100, 0x0080, 0x0040, 0x0020, 0x0001, 0x0008, 0x0004, 0x0002, + 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x8000, 0xffe5, 0x0100, 0x7fff, 0x7f02, 0x7e08, + 0x7d12, 0x7c1f, 0x7b30, 0x7a45, 0x795d, 0x7878, 0x7797, 0x76ba, + 0x75df, 0x7507, 0x7433, 0x7361, 0x7293, 0x71c7, 0x70fe, 0x7038, + 0x6f75, 0x6eb4, 0x6df6, 0x6d3a, 0x6c81, 0x6bca, 0x6b16, 0x6a64, + 0x69b4, 0x6907, 0x685b, 0x67b2, 0x670b, 0x6666, 0x65c4, 0x6523, + 0x6484, 0x63e7, 0x634c, 0x62b3, 0x621c, 0x6186, 0x60f2, 0x6060, + 0x5fd0, 0x5f41, 0x5eb5, 0x5e29, 0x5d9f, 0x5d17, 0x5c91, 0x5c0c, + 0x5b88, 0x5b06, 0x5a85, 0x5a06, 0x5988, 0x590b, 0x5890, 0x5816, + 0x579d, 0x5726, 0x56b0, 0x563b, 0x55c8, 0x5555, 0x54e4, 0x5474, + 0x5405, 0x5398, 0x532b, 0x52bf, 0x5255, 0x51ec, 0x5183, 0x511c, + 0x50b6, 0x5050, 0x4fec, 0x4f89, 0x4f26, 0x4ec5, 0x4e64, 0x4e05, + 0x4da6, 0x4d48, 0x4cec, 0x4c90, 0x4c34, 0x4bda, 0x4b81, 0x4b28, + 0x4ad0, 0x4a79, 0x4a23, 0x49cd, 0x4979, 0x4925, 0x48d1, 0x487f, + 0x482d, 0x47dc, 0x478c, 0x473c, 0x46ed, 0x469f, 0x4651, 0x4604, + 0x45b8, 0x456c, 0x4521, 0x44d7, 0x448d, 0x4444, 0x43fc, 0x43b4, + 0x436d, 0x4326, 0x42e0, 0x429a, 0x4255, 0x4211, 0x41cd, 0x4189, + 0x4146, 0x4104, 0x40c2, 0x4081, 0x4040, 0x3fff, 0x41f7, 0x43e1, + 0x45bd, 0x478d, 0x4951, 0x4b0b, 0x4cbb, 0x4e61, 0x4fff, 0x5194, + 0x5322, 0x54a9, 0x5628, 0x57a2, 0x5914, 0x5a81, 0x5be9, 0x5d4a, + 0x5ea7, 0x5fff, 0x6152, 0x62a0, 0x63ea, 0x6530, 0x6672, 0x67b0, + 0x68ea, 0x6a20, 0x6b53, 0x6c83, 0x6daf, 0x6ed9, 0x6fff, 0x7122, + 0x7242, 0x735f, 0x747a, 0x7592, 0x76a7, 0x77ba, 0x78cb, 0x79d9, + 0x7ae5, 0x7bee, 0x7cf5, 0x7dfa, 0x7efe, 0x7fff, 0x0000, 0x0324, + 0x0647, 0x096a, 0x0c8b, 0x0fab, 0x12c8, 0x15e2, 0x18f8, 0x1c0b, + 0x1f19, 0x2223, 0x2528, 0x2826, 0x2b1f, 0x2e11, 0x30fb, 0x33de, + 0x36ba, 0x398c, 0x3c56, 0x3f17, 0x41ce, 0x447a, 0x471c, 0x49b4, + 0x4c3f, 0x4ebf, 0x5133, 0x539b, 0x55f5, 0x5842, 0x5a82, 0x5cb4, + 0x5ed7, 0x60ec, 0x62f2, 0x64e8, 0x66cf, 0x68a6, 0x6a6d, 0x6c24, + 0x6dca, 0x6f5f, 0x70e2, 0x7255, 0x73b5, 0x7504, 0x7641, 0x776c, + 0x7884, 0x798a, 0x7a7d, 0x7b5d, 0x7c29, 0x7ce3, 0x7d8a, 0x7e1d, + 0x7e9d, 0x7f09, 0x7f62, 0x7fa7, 0x7fd8, 0x7ff6, 0x7fff, 0x7ff6, + 0x7fd8, 0x7fa7, 0x7f62, 0x7f09, 0x7e9d, 0x7e1d, 0x7d8a, 0x7ce3, + 0x7c29, 0x7b5d, 0x7a7d, 0x798a, 0x7884, 0x776c, 0x7641, 0x7504, + 0x73b5, 0x7255, 0x70e2, 0x6f5f, 0x6dca, 0x6c24, 0x6a6d, 0x68a6, + 0x66cf, 0x64e8, 0x62f2, 0x60ec, 0x5ed7, 0x5cb4, 0x5a82, 0x5842, + 0x55f5, 0x539b, 0x5133, 0x4ebf, 0x4c3f, 0x49b4, 0x471c, 0x447a, + 0x41ce, 0x3f17, 0x3c56, 0x398c, 0x36ba, 0x33de, 0x30fb, 0x2e11, + 0x2b1f, 0x2826, 0x2528, 0x2223, 0x1f19, 0x1c0b, 0x18f8, 0x15e2, + 0x12c8, 0x0fab, 0x0c8b, 0x096a, 0x0647, 0x0324, 0x7fff, 0x7ff6, + 0x7fd8, 0x7fa7, 0x7f62, 0x7f09, 0x7e9d, 0x7e1d, 0x7d8a, 0x7ce3, + 0x7c29, 0x7b5d, 0x7a7d, 0x798a, 0x7884, 0x776c, 0x7641, 0x7504, + 0x73b5, 0x7255, 0x70e2, 0x6f5f, 0x6dca, 0x6c24, 0x6a6d, 0x68a6, + 0x66cf, 0x64e8, 0x62f2, 0x60ec, 0x5ed7, 0x5cb4, 0x5a82, 0x5842, + 0x55f5, 0x539b, 0x5133, 0x4ebf, 0x4c3f, 0x49b4, 0x471c, 0x447a, + 0x41ce, 0x3f17, 0x3c56, 0x398c, 0x36ba, 0x33de, 0x30fb, 0x2e11, + 0x2b1f, 0x2826, 0x2528, 0x2223, 0x1f19, 0x1c0b, 0x18f8, 0x15e2, + 0x12c8, 0x0fab, 0x0c8b, 0x096a, 0x0647, 0x0324, 0x0000, 0xfcdc, + 0xf9b9, 0xf696, 0xf375, 0xf055, 0xed38, 0xea1e, 0xe708, 0xe3f5, + 0xe0e7, 0xdddd, 0xdad8, 0xd7da, 0xd4e1, 0xd1ef, 0xcf05, 0xcc22, + 0xc946, 0xc674, 0xc3aa, 0xc0e9, 0xbe32, 0xbb86, 0xb8e4, 0xb64c, + 0xb3c1, 0xb141, 0xaecd, 0xac65, 0xaa0b, 0xa7be, 0xa57e, 0xa34c, + 0xa129, 0x9f14, 0x9d0e, 0x9b18, 0x9931, 0x975a, 0x9593, 0x93dc, + 0x9236, 0x90a1, 0x8f1e, 0x8dab, 0x8c4b, 0x8afc, 0x89bf, 0x8894, + 0x877c, 0x8676, 0x8583, 0x84a3, 0x83d7, 0x831d, 0x8276, 0x81e3, + 0x8163, 0x80f7, 0x809e, 0x8059, 0x8028, 0x800a, 0x6488, 0x0080, + 0x03ff, 0x0116, 0x0002, 0x0080, 0x4000, 0x3fd7, 0x3faf, 0x3f86, + 0x3f5d, 0x3f34, 0x3f0c, 0x3ee3, 0x3eba, 0x3e91, 0x3e68, 0x3e40, + 0x3e17, 0x3dee, 0x3dc5, 0x3d9c, 0x3d74, 0x3d4b, 0x3d22, 0x3cf9, + 0x3cd0, 0x3ca7, 0x3c7f, 0x3c56, 0x3c2d, 0x3c04, 0x3bdb, 0x3bb2, + 0x3b89, 0x3b60, 0x3b37, 0x3b0e, 0x3ae5, 0x3abc, 0x3a93, 0x3a69, + 0x3a40, 0x3a17, 0x39ee, 0x39c5, 0x399c, 0x3972, 0x3949, 0x3920, + 0x38f6, 0x38cd, 0x38a4, 0x387a, 0x3851, 0x3827, 0x37fe, 0x37d4, + 0x37aa, 0x3781, 0x3757, 0x372d, 0x3704, 0x36da, 0x36b0, 0x3686, + 0x365c, 0x3632, 0x3609, 0x35df, 0x35b4, 0x358a, 0x3560, 0x3536, + 0x350c, 0x34e1, 0x34b7, 0x348d, 0x3462, 0x3438, 0x340d, 0x33e3, + 0x33b8, 0x338d, 0x3363, 0x3338, 0x330d, 0x32e2, 0x32b7, 0x328c, + 0x3261, 0x3236, 0x320b, 0x31df, 0x31b4, 0x3188, 0x315d, 0x3131, + 0x3106, 0x30da, 0x30ae, 0x3083, 0x3057, 0x302b, 0x2fff, 0x2fd2, + 0x2fa6, 0x2f7a, 0x2f4d, 0x2f21, 0x2ef4, 0x2ec8, 0x2e9b, 0x2e6e, + 0x2e41, 0x2e14, 0x2de7, 0x2dba, 0x2d8d, 0x2d60, 0x2d32, 0x2d05, + 0x2cd7, 0x2ca9, 0x2c7b, 0x2c4d, 0x2c1f, 0x2bf1, 0x2bc3, 0x2b94, + 0x2b66, 0x2b37, 0x2b09, 0x2ada, 0x2aab, 0x2a7c, 0x2a4c, 0x2a1d, + 0x29ed, 0x29be, 0x298e, 0x295e, 0x292e, 0x28fe, 0x28ce, 0x289d, + 0x286d, 0x283c, 0x280b, 0x27da, 0x27a9, 0x2777, 0x2746, 0x2714, + 0x26e2, 0x26b0, 0x267e, 0x264c, 0x2619, 0x25e7, 0x25b4, 0x2581, + 0x254d, 0x251a, 0x24e6, 0x24b2, 0x247e, 0x244a, 0x2415, 0x23e1, + 0x23ac, 0x2376, 0x2341, 0x230b, 0x22d6, 0x229f, 0x2269, 0x2232, + 0x21fc, 0x21c4, 0x218d, 0x2155, 0x211d, 0x20e5, 0x20ad, 0x2074, + 0x203b, 0x2001, 0x1fc7, 0x1f8d, 0x1f53, 0x1f18, 0x1edd, 0x1ea1, + 0x1e66, 0x1e29, 0x1ded, 0x1db0, 0x1d72, 0x1d35, 0x1cf6, 0x1cb8, + 0x1c79, 0x1c39, 0x1bf9, 0x1bb8, 0x1b77, 0x1b36, 0x1af4, 0x1ab1, + 0x1a6e, 0x1a2a, 0x19e6, 0x19a1, 0x195c, 0x1915, 0x18ce, 0x1887, + 0x183f, 0x17f5, 0x17ac, 0x1761, 0x1715, 0x16c9, 0x167c, 0x162e, + 0x15df, 0x158e, 0x153d, 0x14eb, 0x1497, 0x1442, 0x13ec, 0x1395, + 0x133c, 0x12e2, 0x1286, 0x1228, 0x11c9, 0x1167, 0x1104, 0x109e, + 0x1036, 0x0fcc, 0x0f5f, 0x0eef, 0x0e7b, 0x0e04, 0x0d89, 0x0d0a, + 0x0c86, 0x0bfd, 0x0b6d, 0x0ad6, 0x0a36, 0x098d, 0x08d7, 0x0811, + 0x0736, 0x063e, 0x0519, 0x039a, 0x0000, 0x7fff, 0x0100, 0x0080, + 0x021d, 0x00c8, 0x00ce, 0x0048, 0x0a26, 0x277a, 0x00ce, 0x6488, + 0x14ac, 0x0001, 0x00f9, 0x00fc, 0x00ff, 0x00fc, 0x00f9, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff}; + +////////////////////////////////////////////////////////////////// + +#endif diff --git a/asnes/chip/dsp1/dsp1emu.hpp b/asnes/chip/dsp1/dsp1emu.hpp new file mode 100755 index 00000000..9ae313ac --- /dev/null +++ b/asnes/chip/dsp1/dsp1emu.hpp @@ -0,0 +1,129 @@ +// DSP-1's emulation code +// +// Based on research by Overload, The Dumper, Neviksti and Andreas Naive +// Date: June 2006 + +#ifndef __DSP1EMUL_H +#define __DSP1EMUL_H + +#define DSP1_VERSION 0x0102 + +class Dsp1 +{ + public: + // The DSP-1 status register has 16 bits, but only + // the upper 8 bits can be accessed from an external device, so all these + // positions are referred to the upper byte (bits D8 to D15) + enum SrFlags {DRC=0x04, DRS=0x10, RQM=0x80}; + + // According to Overload's docs, these are the meanings of the flags: + // DRC: The Data Register Control (DRC) bit specifies the data transfer length to and from the host CPU. + // 0: Data transfer to and from the DSP-1 is 16 bits. + // 1: Data transfer to and from the DSP-1 is 8 bits. + // DRS: The Data Register Status (DRS) bit indicates the data transfer status in the case of transfering 16-bit data. + // 0: Data transfer has terminated. + // 1: Data transfer in progress. + // RQM: The Request for Master (RQM) indicates that the DSP1 is requesting host CPU for data read/write. + // 0: Internal Data Register Transfer. + // 1: External Data Register Transfer. + + Dsp1(); + uint8 getSr(); // return the status register's high byte + uint8 getDr(); + void setDr(uint8 iDr); + void reset(); + + void serialize(serializer&); + + private: + enum FsmMajorState {WAIT_COMMAND, READ_DATA, WRITE_DATA}; + enum MaxDataAccesses {MAX_READS=7, MAX_WRITES=1024}; + + struct Command { + void (Dsp1::*callback)(int16 *, int16 *); + unsigned int reads; + unsigned int writes; + }; + + static const Command mCommandTable[]; + static const int16 MaxAZS_Exp[16]; + static const int16 SinTable[]; + static const int16 MulTable[]; + static const uint16 DataRom[]; + + struct SharedData { // some RAM variables shared between commands + int16 MatrixA[3][3]; // attitude matrix A + int16 MatrixB[3][3]; + int16 MatrixC[3][3]; + int16 CentreX, CentreY, CentreZ; // center of projection + int16 CentreZ_C, CentreZ_E; + int16 VOffset; // vertical offset of the screen with regard to the centre of projection + int16 Les, C_Les, E_Les; + int16 SinAas, CosAas; + int16 SinAzs, CosAzs; + int16 SinAZS, CosAZS; + int16 SecAZS_C1, SecAZS_E1; + int16 SecAZS_C2, SecAZS_E2; + int16 Nx, Ny, Nz; // normal vector to the screen (norm 1, points toward the center of projection) + int16 Gx, Gy, Gz; // center of the screen (global coordinates) + int16 Hx, Hy; // horizontal vector of the screen (Hz=0, norm 1, points toward the right of the screen) + int16 Vx, Vy, Vz; // vertical vector of the screen (norm 1, points toward the top of the screen) + + } shared; + + uint8 mSr; // status register + int mSrLowByteAccess; + uint16 mDr; // "internal" representation of the data register + unsigned mFsmMajorState; // current major state of the FSM + uint8 mCommand; // current command processed by the FSM + uint8 mDataCounter; // #uint16 read/writes counter used by the FSM + int16 mReadBuffer[MAX_READS]; + int16 mWriteBuffer[MAX_WRITES]; + bool mFreeze; // need explanation? ;) + + void fsmStep(bool read, uint8 &data); // FSM logic + + // commands + void memoryTest(int16 *input, int16 *output); + void memoryDump(int16 *input, int16 *output); + void memorySize(int16 *input, int16 *output); + void multiply(int16* input, int16* output); + void multiply2(int16* input, int16* output); + void inverse(int16 *input, int16 *output); + void triangle(int16 *input, int16 *output); + void radius(int16 *input, int16 *output); + void range(int16 *input, int16 *output); + void range2(int16 *input, int16 *output); + void distance(int16 *input, int16 *output); + void rotate(int16 *input, int16 *output); + void polar(int16 *input, int16 *output); + void attitudeA(int16 *input, int16 *output); + void attitudeB(int16 *input, int16 *output); + void attitudeC(int16 *input, int16 *output); + void objectiveA(int16 *input, int16 *output); + void objectiveB(int16 *input, int16 *output); + void objectiveC(int16 *input, int16 *output); + void subjectiveA(int16 *input, int16 *output); + void subjectiveB(int16 *input, int16 *output); + void subjectiveC(int16 *input, int16 *output); + void scalarA(int16 *input, int16 *output); + void scalarB(int16 *input, int16 *output); + void scalarC(int16 *input, int16 *output); + void gyrate(int16 *input, int16 *output); + void parameter(int16 *input, int16 *output); + void raster(int16 *input, int16 *output); + void target(int16 *input, int16 *output); + void project(int16 *input, int16 *output); + + // auxiliar functions + int16 sin(int16 Angle); + int16 cos(int16 Angle); + void inverse(int16 Coefficient, int16 Exponent, int16 &iCoefficient, int16 &iExponent); + int16 denormalizeAndClip(int16 C, int16 E); + void normalize(int16 m, int16 &Coefficient, int16 &Exponent); + void normalizeDouble(int32 Product, int16 &Coefficient, int16 &Exponent); + int16 shiftR(int16 C, int16 E); +}; + +#endif + diff --git a/asnes/chip/dsp1/serialization.cpp b/asnes/chip/dsp1/serialization.cpp new file mode 100755 index 00000000..f7edb381 --- /dev/null +++ b/asnes/chip/dsp1/serialization.cpp @@ -0,0 +1,56 @@ +#ifdef DSP1_CPP + +void DSP1::serialize(serializer &s) { + dsp1.serialize(s); +} + +void Dsp1::serialize(serializer &s) { + for(unsigned i = 0; i < 3; i++) { + s.array(shared.MatrixA[i]); + s.array(shared.MatrixB[i]); + s.array(shared.MatrixC[i]); + } + + s.integer(shared.CentreX); + s.integer(shared.CentreY); + s.integer(shared.CentreZ); + s.integer(shared.CentreZ_C); + s.integer(shared.CentreZ_E); + s.integer(shared.VOffset); + s.integer(shared.Les); + s.integer(shared.C_Les); + s.integer(shared.E_Les); + s.integer(shared.SinAas); + s.integer(shared.CosAas); + s.integer(shared.SinAzs); + s.integer(shared.CosAzs); + s.integer(shared.SinAZS); + s.integer(shared.CosAZS); + s.integer(shared.SecAZS_C1); + s.integer(shared.SecAZS_E1); + s.integer(shared.SecAZS_C2); + s.integer(shared.SecAZS_E2); + s.integer(shared.Nx); + s.integer(shared.Ny); + s.integer(shared.Nz); + s.integer(shared.Gx); + s.integer(shared.Gy); + s.integer(shared.Gz); + s.integer(shared.Hx); + s.integer(shared.Hy); + s.integer(shared.Vx); + s.integer(shared.Vy); + s.integer(shared.Vz); + + s.integer(mSr); + s.integer(mSrLowByteAccess); + s.integer(mDr); + s.integer(mFsmMajorState); + s.integer(mCommand); + s.integer(mDataCounter); + s.array(mReadBuffer); + s.array(mWriteBuffer); + s.integer(mFreeze); +} + +#endif diff --git a/asnes/chip/dsp2/dsp2.cpp b/asnes/chip/dsp2/dsp2.cpp new file mode 100755 index 00000000..bb33ba35 --- /dev/null +++ b/asnes/chip/dsp2/dsp2.cpp @@ -0,0 +1,153 @@ +#include + +#define DSP2_CPP +namespace SNES { + +DSP2 dsp2; +DSP2DR dsp2dr; +DSP2SR dsp2sr; + +#include "serialization.cpp" +#include "opcodes.cpp" + +void DSP2::init() { +} + +void DSP2::enable() { +} + +void DSP2::power() { + reset(); +} + +void DSP2::reset() { + status.waiting_for_command = true; + status.in_count = 0; + status.in_index = 0; + status.out_count = 0; + status.out_index = 0; + + status.op05transparent = 0; + status.op05haslen = false; + status.op05len = 0; + status.op06haslen = false; + status.op06len = 0; + status.op09word1 = 0; + status.op09word2 = 0; + status.op0dhaslen = false; + status.op0doutlen = 0; + status.op0dinlen = 0; +} + +uint8 DSP2::read(unsigned addr) { + uint8 r = 0xff; + if(status.out_count) { + r = status.output[status.out_index++]; + status.out_index &= 511; + if(status.out_count == status.out_index) { + status.out_count = 0; + } + } + return r; +} + +void DSP2::write(unsigned addr, uint8 data) { + if(status.waiting_for_command) { + status.command = data; + status.in_index = 0; + status.waiting_for_command = false; + + switch(data) { + case 0x01: status.in_count = 32; break; + case 0x03: status.in_count = 1; break; + case 0x05: status.in_count = 1; break; + case 0x06: status.in_count = 1; break; + case 0x07: break; + case 0x08: break; + case 0x09: status.in_count = 4; break; + case 0x0d: status.in_count = 2; break; + case 0x0f: status.in_count = 0; break; + } + } else { + status.parameters[status.in_index++] = data; + status.in_index &= 511; + } + + if(status.in_count == status.in_index) { + status.waiting_for_command = true; + status.out_index = 0; + switch(status.command) { + case 0x01: { + status.out_count = 32; + op01(); + } break; + + case 0x03: { + op03(); + } break; + + case 0x05: { + if(status.op05haslen) { + status.op05haslen = false; + status.out_count = status.op05len; + op05(); + } else { + status.op05len = status.parameters[0]; + status.in_index = 0; + status.in_count = status.op05len * 2; + status.op05haslen = true; + if(data)status.waiting_for_command = false; + } + } break; + + case 0x06: { + if(status.op06haslen) { + status.op06haslen = false; + status.out_count = status.op06len; + op06(); + } else { + status.op06len = status.parameters[0]; + status.in_index = 0; + status.in_count = status.op06len; + status.op06haslen = true; + if(data)status.waiting_for_command = false; + } + } break; + + case 0x07: break; + case 0x08: break; + + case 0x09: { + op09(); + } break; + + case 0x0d: { + if(status.op0dhaslen) { + status.op0dhaslen = false; + status.out_count = status.op0doutlen; + op0d(); + } else { + status.op0dinlen = status.parameters[0]; + status.op0doutlen = status.parameters[1]; + status.in_index = 0; + status.in_count = (status.op0dinlen + 1) >> 1; + status.op0dhaslen = true; + if(data)status.waiting_for_command = false; + } + } break; + + case 0x0f: break; + } + } +} + +DSP2::DSP2() {} +DSP2::~DSP2() {} + +uint8 DSP2DR::read(unsigned addr) { return dsp2.read(addr); } +void DSP2DR::write(unsigned addr, uint8 data) { dsp2.write(addr, data); } + +uint8 DSP2SR::read(unsigned addr) { return 0x00; } +void DSP2SR::write(unsigned addr, uint8 data) {} + +} diff --git a/asnes/chip/dsp2/dsp2.hpp b/asnes/chip/dsp2/dsp2.hpp new file mode 100755 index 00000000..bc14a5bf --- /dev/null +++ b/asnes/chip/dsp2/dsp2.hpp @@ -0,0 +1,57 @@ +class DSP2 { +public: + struct { + bool waiting_for_command; + unsigned command; + unsigned in_count, in_index; + unsigned out_count, out_index; + + uint8 parameters[512]; + uint8 output[512]; + + uint8 op05transparent; + bool op05haslen; + int op05len; + bool op06haslen; + int op06len; + uint16 op09word1; + uint16 op09word2; + bool op0dhaslen; + int op0doutlen; + int op0dinlen; + } status; + + void init(); + void enable(); + void power(); + void reset(); + + uint8 read(unsigned addr); + void write(unsigned addr, uint8 data); + + void serialize(serializer&); + DSP2(); + ~DSP2(); + +protected: + void op01(); + void op03(); + void op05(); + void op06(); + void op09(); + void op0d(); +}; + +class DSP2DR : public Memory { + uint8 read(unsigned addr); + void write(unsigned addr, uint8 data); +}; + +class DSP2SR : public Memory { + uint8 read(unsigned addr); + void write(unsigned addr, uint8 data); +}; + +extern DSP2 dsp2; +extern DSP2DR dsp2dr; +extern DSP2SR dsp2sr; diff --git a/asnes/chip/dsp2/opcodes.cpp b/asnes/chip/dsp2/opcodes.cpp new file mode 100755 index 00000000..f015ac32 --- /dev/null +++ b/asnes/chip/dsp2/opcodes.cpp @@ -0,0 +1,177 @@ +#ifdef DSP2_CPP + +//convert bitmap to bitplane tile +void DSP2::op01() { +//op01 size is always 32 bytes input and output +//the hardware does strange things if you vary the size + +unsigned char c0, c1, c2, c3; +unsigned char *p1 = status.parameters; +unsigned char *p2a = status.output; +unsigned char *p2b = status.output + 16; //halfway + +//process 8 blocks of 4 bytes each + for(int j = 0; j < 8; j++) { + c0 = *p1++; + c1 = *p1++; + c2 = *p1++; + c3 = *p1++; + + *p2a++ = (c0 & 0x10) << 3 | + (c0 & 0x01) << 6 | + (c1 & 0x10) << 1 | + (c1 & 0x01) << 4 | + (c2 & 0x10) >> 1 | + (c2 & 0x01) << 2 | + (c3 & 0x10) >> 3 | + (c3 & 0x01); + + *p2a++ = (c0 & 0x20) << 2 | + (c0 & 0x02) << 5 | + (c1 & 0x20) | + (c1 & 0x02) << 3 | + (c2 & 0x20) >> 2 | + (c2 & 0x02) << 1 | + (c3 & 0x20) >> 4 | + (c3 & 0x02) >> 1; + + *p2b++ = (c0 & 0x40) << 1 | + (c0 & 0x04) << 4 | + (c1 & 0x40) >> 1 | + (c1 & 0x04) << 2 | + (c2 & 0x40) >> 3 | + (c2 & 0x04) | + (c3 & 0x40) >> 5 | + (c3 & 0x04) >> 2; + + *p2b++ = (c0 & 0x80) | + (c0 & 0x08) << 3 | + (c1 & 0x80) >> 2 | + (c1 & 0x08) << 1 | + (c2 & 0x80) >> 4 | + (c2 & 0x08) >> 1 | + (c3 & 0x80) >> 6 | + (c3 & 0x08) >> 3; + } +} + +//set transparent color +void DSP2::op03() { + status.op05transparent = status.parameters[0]; +} + +//replace bitmap using transparent color +void DSP2::op05() { +uint8 color; +// Overlay bitmap with transparency. +// Input: +// +// Bitmap 1: i[0] <=> i[size-1] +// Bitmap 2: i[size] <=> i[2*size-1] +// +// Output: +// +// Bitmap 3: o[0] <=> o[size-1] +// +// Processing: +// +// Process all 4-bit pixels (nibbles) in the bitmap +// +// if ( BM2_pixel == transparent_color ) +// pixelout = BM1_pixel +// else +// pixelout = BM2_pixel + +// The max size bitmap is limited to 255 because the size parameter is a byte +// I think size=0 is an error. The behavior of the chip on size=0 is to +// return the last value written to DR if you read DR on Op05 with +// size = 0. I don't think it's worth implementing this quirk unless it's +// proven necessary. + +unsigned char c1, c2; +unsigned char *p1 = status.parameters; +unsigned char *p2 = status.parameters + status.op05len; +unsigned char *p3 = status.output; + + color = status.op05transparent & 0x0f; + + for(int n = 0; n < status.op05len; n++) { + c1 = *p1++; + c2 = *p2++; + *p3++ = ( ((c2 >> 4) == color ) ? c1 & 0xf0 : c2 & 0xf0 ) | + ( ((c2 & 0x0f) == color ) ? c1 & 0x0f : c2 & 0x0f ); + } +} + +//reverse bitmap +void DSP2::op06() { +// Input: +// size +// bitmap + +int i, j; + for(i = 0, j = status.op06len - 1; i < status.op06len; i++, j--) { + status.output[j] = (status.parameters[i] << 4) | (status.parameters[i] >> 4); + } +} + +//multiply +void DSP2::op09() { + status.out_count = 4; + + status.op09word1 = status.parameters[0] | (status.parameters[1] << 8); + status.op09word2 = status.parameters[2] | (status.parameters[3] << 8); + +uint32 r; + r = status.op09word1 * status.op09word2; + status.output[0] = r; + status.output[1] = r >> 8; + status.output[2] = r >> 16; + status.output[3] = r >> 24; +} + +//scale bitmap +void DSP2::op0d() { +// Bit accurate hardware algorithm - uses fixed point math +// This should match the DSP2 Op0D output exactly +// I wouldn't recommend using this unless you're doing hardware debug. +// In some situations it has small visual artifacts that +// are not readily apparent on a TV screen but show up clearly +// on a monitor. Use Overload's scaling instead. +// This is for hardware verification testing. +// +// One note: the HW can do odd byte scaling but since we divide +// by two to get the count of bytes this won't work well for +// odd byte scaling (in any of the current algorithm implementations). +// So far I haven't seen Dungeon Master use it. +// If it does we can adjust the parameters and code to work with it + +uint32 multiplier; // Any size int >= 32-bits +uint32 pixloc; // match size of multiplier +int i, j; +uint8 pixelarray[512]; + if(status.op0dinlen <= status.op0doutlen) { + multiplier = 0x10000; // In our self defined fixed point 0x10000 == 1 + } else { + multiplier = (status.op0dinlen << 17) / ((status.op0doutlen << 1) + 1); + } + + pixloc = 0; + for(i = 0; i < status.op0doutlen * 2; i++) { + j = pixloc >> 16; + + if(j & 1) { + pixelarray[i] = (status.parameters[j >> 1] & 0x0f); + } else { + pixelarray[i] = (status.parameters[j >> 1] & 0xf0) >> 4; + } + + pixloc += multiplier; + } + + for(i = 0; i < status.op0doutlen; i++) { + status.output[i] = (pixelarray[i << 1] << 4) | pixelarray[(i << 1) + 1]; + } +} + +#endif diff --git a/asnes/chip/dsp2/serialization.cpp b/asnes/chip/dsp2/serialization.cpp new file mode 100755 index 00000000..d089b0fe --- /dev/null +++ b/asnes/chip/dsp2/serialization.cpp @@ -0,0 +1,26 @@ +#ifdef DSP2_CPP + +void DSP2::serialize(serializer &s) { + s.integer(status.waiting_for_command); + s.integer(status.command); + s.integer(status.in_count); + s.integer(status.in_index); + s.integer(status.out_count); + s.integer(status.out_index); + + s.array(status.parameters); + s.array(status.output); + + s.integer(status.op05transparent); + s.integer(status.op05haslen); + s.integer(status.op05len); + s.integer(status.op06haslen); + s.integer(status.op06len); + s.integer(status.op09word1); + s.integer(status.op09word2); + s.integer(status.op0dhaslen); + s.integer(status.op0doutlen); + s.integer(status.op0dinlen); +} + +#endif diff --git a/asnes/chip/dsp3/dsp3.cpp b/asnes/chip/dsp3/dsp3.cpp new file mode 100755 index 00000000..bf60c578 --- /dev/null +++ b/asnes/chip/dsp3/dsp3.cpp @@ -0,0 +1,40 @@ +#include + +#define DSP3_CPP +namespace SNES { + +DSP3 dsp3; + +namespace DSP3i { + #define bool8 uint8 + #include "dsp3emu.c" + #undef bool8 +}; + +void DSP3::init() { +} + +void DSP3::enable() { +} + +void DSP3::power() { + reset(); +} + +void DSP3::reset() { + DSP3i::DSP3_Reset(); +} + +uint8 DSP3::read(unsigned addr) { + DSP3i::dsp3_address = addr & 0xffff; + DSP3i::DSP3GetByte(); + return DSP3i::dsp3_byte; +} + +void DSP3::write(unsigned addr, uint8 data) { + DSP3i::dsp3_address = addr & 0xffff; + DSP3i::dsp3_byte = data; + DSP3i::DSP3SetByte(); +} + +} diff --git a/asnes/chip/dsp3/dsp3.hpp b/asnes/chip/dsp3/dsp3.hpp new file mode 100755 index 00000000..b3353e33 --- /dev/null +++ b/asnes/chip/dsp3/dsp3.hpp @@ -0,0 +1,12 @@ +class DSP3 : public Memory { +public: + void init(); + void enable(); + void power(); + void reset(); + + uint8 read (unsigned addr); + void write(unsigned addr, uint8 data); +}; + +extern DSP3 dsp3; diff --git a/asnes/chip/dsp3/dsp3emu.c b/asnes/chip/dsp3/dsp3emu.c new file mode 100755 index 00000000..9e656776 --- /dev/null +++ b/asnes/chip/dsp3/dsp3emu.c @@ -0,0 +1,1146 @@ +#ifdef DSP3_CPP + +//DSP-3 emulator code +//Copyright (c) 2003-2006 John Weidman, Kris Bleakley, Lancer, z80 gaiden + +uint16 DSP3_DataROM[1024] = { + 0x8000, 0x4000, 0x2000, 0x1000, 0x0800, 0x0400, 0x0200, 0x0100, + 0x0080, 0x0040, 0x0020, 0x0010, 0x0008, 0x0004, 0x0002, 0x0001, + 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080, 0x0100, + 0x0000, 0x000f, 0x0400, 0x0200, 0x0140, 0x0400, 0x0200, 0x0040, + 0x007d, 0x007e, 0x007e, 0x007b, 0x007c, 0x007d, 0x007b, 0x007c, + 0x0002, 0x0020, 0x0030, 0x0000, 0x000d, 0x0019, 0x0026, 0x0032, + 0x003e, 0x004a, 0x0056, 0x0062, 0x006d, 0x0079, 0x0084, 0x008e, + 0x0098, 0x00a2, 0x00ac, 0x00b5, 0x00be, 0x00c6, 0x00ce, 0x00d5, + 0x00dc, 0x00e2, 0x00e7, 0x00ec, 0x00f1, 0x00f5, 0x00f8, 0x00fb, + 0x00fd, 0x00ff, 0x0100, 0x0100, 0x0100, 0x00ff, 0x00fd, 0x00fb, + 0x00f8, 0x00f5, 0x00f1, 0x00ed, 0x00e7, 0x00e2, 0x00dc, 0x00d5, + 0x00ce, 0x00c6, 0x00be, 0x00b5, 0x00ac, 0x00a2, 0x0099, 0x008e, + 0x0084, 0x0079, 0x006e, 0x0062, 0x0056, 0x004a, 0x003e, 0x0032, + 0x0026, 0x0019, 0x000d, 0x0000, 0xfff3, 0xffe7, 0xffdb, 0xffce, + 0xffc2, 0xffb6, 0xffaa, 0xff9e, 0xff93, 0xff87, 0xff7d, 0xff72, + 0xff68, 0xff5e, 0xff54, 0xff4b, 0xff42, 0xff3a, 0xff32, 0xff2b, + 0xff25, 0xff1e, 0xff19, 0xff14, 0xff0f, 0xff0b, 0xff08, 0xff05, + 0xff03, 0xff01, 0xff00, 0xff00, 0xff00, 0xff01, 0xff03, 0xff05, + 0xff08, 0xff0b, 0xff0f, 0xff13, 0xff18, 0xff1e, 0xff24, 0xff2b, + 0xff32, 0xff3a, 0xff42, 0xff4b, 0xff54, 0xff5d, 0xff67, 0xff72, + 0xff7c, 0xff87, 0xff92, 0xff9e, 0xffa9, 0xffb5, 0xffc2, 0xffce, + 0xffda, 0xffe7, 0xfff3, 0x002b, 0x007f, 0x0020, 0x00ff, 0xff00, + 0xffbe, 0x0000, 0x0044, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0xffc1, 0x0001, 0x0002, 0x0045, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0xffc5, 0x0003, 0x0004, 0x0005, 0x0047, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0xffca, 0x0006, 0x0007, 0x0008, + 0x0009, 0x004a, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0xffd0, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x004e, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0xffd7, 0x000f, 0x0010, 0x0011, + 0x0012, 0x0013, 0x0014, 0x0053, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0xffdf, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, + 0x0059, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0xffe8, 0x001c, 0x001d, 0x001e, + 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0060, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0xfff2, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, + 0x002b, 0x002c, 0x0068, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0xfffd, 0x002d, 0x002e, 0x002f, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0071, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0xffc7, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, + 0x003e, 0x003f, 0x0040, 0x0041, 0x007b, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0xffd4, 0x0000, 0x0001, 0x0002, + 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, + 0x000b, 0x0044, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0xffe2, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, + 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0050, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0xfff1, 0x0019, 0x001a, 0x001b, + 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, + 0x0024, 0x0025, 0x0026, 0x005d, 0x0000, 0x0000, 0x0000, 0x0000, + 0xffcb, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, + 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, + 0x006b, 0x0000, 0x0000, 0x0000, 0xffdc, 0x0000, 0x0001, 0x0002, + 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, + 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0044, 0x0000, 0x0000, + 0xffee, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, + 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, + 0x001f, 0x0020, 0x0054, 0x0000, 0xffee, 0x0021, 0x0022, 0x0023, + 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, + 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0065, + 0xffbe, 0x0000, 0xfeac, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0xffc1, 0x0001, 0x0002, 0xfead, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0xffc5, 0x0003, 0x0004, 0x0005, 0xfeaf, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0xffca, 0x0006, 0x0007, 0x0008, + 0x0009, 0xfeb2, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0xffd0, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0xfeb6, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0xffd7, 0x000f, 0x0010, 0x0011, + 0x0012, 0x0013, 0x0014, 0xfebb, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0xffdf, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, + 0xfec1, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0xffe8, 0x001c, 0x001d, 0x001e, + 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0xfec8, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0xfff2, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, + 0x002b, 0x002c, 0xfed0, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0xfffd, 0x002d, 0x002e, 0x002f, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0xfed9, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0xffc7, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, + 0x003e, 0x003f, 0x0040, 0x0041, 0xfee3, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0xffd4, 0x0000, 0x0001, 0x0002, + 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, + 0x000b, 0xfeac, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0xffe2, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, + 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0xfeb8, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0xfff1, 0x0019, 0x001a, 0x001b, + 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, + 0x0024, 0x0025, 0x0026, 0xfec5, 0x0000, 0x0000, 0x0000, 0x0000, + 0xffcb, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, + 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, + 0xfed3, 0x0000, 0x0000, 0x0000, 0xffdc, 0x0000, 0x0001, 0x0002, + 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, + 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0xfeac, 0x0000, 0x0000, + 0xffee, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, + 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, + 0x001f, 0x0020, 0xfebc, 0x0000, 0xffee, 0x0021, 0x0022, 0x0023, + 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, + 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0xfecd, + 0x0154, 0x0218, 0x0110, 0x00b0, 0x00cc, 0x00b0, 0x0088, 0x00b0, + 0x0044, 0x00b0, 0x0000, 0x00b0, 0x00fe, 0xff07, 0x0002, 0x00ff, + 0x00f8, 0x0007, 0x00fe, 0x00ee, 0x07ff, 0x0200, 0x00ef, 0xf800, + 0x0700, 0x00ee, 0xffff, 0xffff, 0xffff, 0x0000, 0x0000, 0x0001, + 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, 0xffff, 0xffff, 0xffff, + 0xffff, 0x0000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, + 0x0000, 0xffff, 0xffff, 0x0000, 0xffff, 0x0001, 0x0000, 0x0001, + 0x0001, 0x0000, 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0x0000, + 0xffff, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0xffff, + 0xffff, 0xffff, 0x0000, 0x0000, 0x0000, 0x0044, 0x0088, 0x00cc, + 0x0110, 0x0154, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, + 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff +}; + +void (*SetDSP3)(); +void DSP3_Command(); + +uint16 DSP3_DR; +uint16 DSP3_SR; +uint16 DSP3_MemoryIndex; + +void DSP3_Reset() +{ + DSP3_DR = 0x0080; + DSP3_SR = 0x0084; + SetDSP3 = &DSP3_Command; +} + +void DSP3_MemorySize() +{ + DSP3_DR = 0x0300; + SetDSP3 = &DSP3_Reset; +} + +void DSP3_TestMemory() +{ + DSP3_DR = 0x0000; + SetDSP3 = &DSP3_Reset; +} + +void DSP3_DumpDataROM() +{ + DSP3_DR = DSP3_DataROM[DSP3_MemoryIndex++]; + if (DSP3_MemoryIndex == 1024) + SetDSP3 = &DSP3_Reset; +} + +void DSP3_MemoryDump() +{ + DSP3_MemoryIndex = 0; + SetDSP3 = &DSP3_DumpDataROM; + DSP3_DumpDataROM(); +} + +int16 DSP3_WinLo; +int16 DSP3_WinHi; + +void DSP3_OP06() +{ + DSP3_WinLo = (uint8)(DSP3_DR); + DSP3_WinHi = (uint8)(DSP3_DR >> 8); + DSP3_Reset(); +} + +void DSP3_OP03() +{ + int16 Lo = (uint8)(DSP3_DR); + int16 Hi = (uint8)(DSP3_DR >> 8); + int16 Ofs = (DSP3_WinLo * Hi << 1) + (Lo << 1); + DSP3_DR = Ofs >> 1; + SetDSP3 = &DSP3_Reset; +} + +int16 DSP3_AddLo; +int16 DSP3_AddHi; + +void DSP3_OP07_B() +{ + int16 Ofs = (DSP3_WinLo * DSP3_AddHi << 1) + (DSP3_AddLo << 1); + DSP3_DR = Ofs >> 1; + SetDSP3 = &DSP3_Reset; +} + +void DSP3_OP07_A() +{ + int16 Lo = (uint8)(DSP3_DR); + int16 Hi = (uint8)(DSP3_DR >> 8); + + if (Lo & 1) Hi += (DSP3_AddLo & 1); + + DSP3_AddLo += Lo; + DSP3_AddHi += Hi; + + if (DSP3_AddLo < 0) + DSP3_AddLo += DSP3_WinLo; + else + if (DSP3_AddLo >= DSP3_WinLo) + DSP3_AddLo -= DSP3_WinLo; + + if (DSP3_AddHi < 0) + DSP3_AddHi += DSP3_WinHi; + else + if (DSP3_AddHi >= DSP3_WinHi) + DSP3_AddHi -= DSP3_WinHi; + + DSP3_DR = DSP3_AddLo | (DSP3_AddHi << 8) | ((DSP3_AddHi >> 8) & 0xff); + SetDSP3 = &DSP3_OP07_B; +} + +void DSP3_OP07() +{ + uint32 dataOfs = ((DSP3_DR << 1) + 0x03b2) & 0x03ff; + + DSP3_AddHi = DSP3_DataROM[dataOfs]; + DSP3_AddLo = DSP3_DataROM[dataOfs + 1]; + + SetDSP3 = &DSP3_OP07_A; + DSP3_SR = 0x0080; +} + +uint16 DSP3_Codewords; +uint16 DSP3_Outwords; +uint16 DSP3_Symbol; +uint16 DSP3_BitCount; +uint16 DSP3_Index; +uint16 DSP3_Codes[512]; +uint16 DSP3_BitsLeft; +uint16 DSP3_ReqBits; +uint16 DSP3_ReqData; +uint16 DSP3_BitCommand; +uint8 DSP3_BaseLength; +uint16 DSP3_BaseCodes; +uint16 DSP3_BaseCode; +uint8 DSP3_CodeLengths[8]; +uint16 DSP3_CodeOffsets[8]; +uint16 DSP3_LZCode; +uint8 DSP3_LZLength; + +uint16 DSP3_X; +uint16 DSP3_Y; + +void DSP3_Coordinate() +{ + DSP3_Index++; + + switch (DSP3_Index) + { + case 3: + { + if (DSP3_DR == 0xffff) + DSP3_Reset(); + break; + } + case 4: + { + DSP3_X = DSP3_DR; + break; + } + case 5: + { + DSP3_Y = DSP3_DR; + DSP3_DR = 1; + break; + } + case 6: + { + DSP3_DR = DSP3_X; + break; + } + case 7: + { + DSP3_DR = DSP3_Y; + DSP3_Index = 0; + break; + } + } +} + +uint8 DSP3_Bitmap[8]; +uint8 DSP3_Bitplane[8]; +uint16 DSP3_BMIndex; +uint16 DSP3_BPIndex; +uint16 DSP3_Count; + +void DSP3_Convert_A() +{ + if (DSP3_BMIndex < 8) + { + DSP3_Bitmap[DSP3_BMIndex++] = (uint8) (DSP3_DR); + DSP3_Bitmap[DSP3_BMIndex++] = (uint8) (DSP3_DR >> 8); + + if (DSP3_BMIndex == 8) + { + short i, j; + for (i=0; i < 8; i++) + for (j=0; j < 8; j++) + { + DSP3_Bitplane[j] <<= 1; + DSP3_Bitplane[j] |= (DSP3_Bitmap[i] >> j) & 1; + } + + DSP3_BPIndex = 0; + DSP3_Count--; + } + } + + if (DSP3_BMIndex == 8) + { + if (DSP3_BPIndex == 8) + { + if (!DSP3_Count) DSP3_Reset(); + DSP3_BMIndex = 0; + } + else + { + DSP3_DR = DSP3_Bitplane[DSP3_BPIndex++]; + DSP3_DR |= DSP3_Bitplane[DSP3_BPIndex++] << 8; + } + } +} + +void DSP3_Convert() +{ + DSP3_Count = DSP3_DR; + DSP3_BMIndex = 0; + SetDSP3 = &DSP3_Convert_A; +} + +bool DSP3_GetBits(uint8 Count) +{ + if (!DSP3_BitsLeft) + { + DSP3_BitsLeft = Count; + DSP3_ReqBits = 0; + } + + do { + if (!DSP3_BitCount) + { + DSP3_SR = 0xC0; + return false; + } + + DSP3_ReqBits <<= 1; + if (DSP3_ReqData & 0x8000) DSP3_ReqBits++; + DSP3_ReqData <<= 1; + + DSP3_BitCount--; + DSP3_BitsLeft--; + + } while (DSP3_BitsLeft); + + return true; +} + +void DSP3_Decode_Data() +{ + if (!DSP3_BitCount) + { + if (DSP3_SR & 0x40) + { + DSP3_ReqData = DSP3_DR; + DSP3_BitCount += 16; + } + else + { + DSP3_SR = 0xC0; + return; + } + } + + if (DSP3_LZCode == 1) + { + if (!DSP3_GetBits(1)) + return; + + if (DSP3_ReqBits) + DSP3_LZLength = 12; + else + DSP3_LZLength = 8; + + DSP3_LZCode++; + } + + if (DSP3_LZCode == 2) + { + if (!DSP3_GetBits(DSP3_LZLength)) + return; + + DSP3_LZCode = 0; + DSP3_Outwords--; + if (!DSP3_Outwords) SetDSP3 = &DSP3_Reset; + + DSP3_SR = 0x80; + DSP3_DR = DSP3_ReqBits; + return; + } + + if (DSP3_BaseCode == 0xffff) + { + if (!DSP3_GetBits(DSP3_BaseLength)) + return; + + DSP3_BaseCode = DSP3_ReqBits; + } + + if (!DSP3_GetBits(DSP3_CodeLengths[DSP3_BaseCode])) + return; + + DSP3_Symbol = DSP3_Codes[DSP3_CodeOffsets[DSP3_BaseCode] + DSP3_ReqBits]; + DSP3_BaseCode = 0xffff; + + if (DSP3_Symbol & 0xff00) + { + DSP3_Symbol += 0x7f02; + DSP3_LZCode++; + } + else + { + DSP3_Outwords--; + if (!DSP3_Outwords) + SetDSP3 = &DSP3_Reset; + } + + DSP3_SR = 0x80; + DSP3_DR = DSP3_Symbol; +} + +void DSP3_Decode_Tree() +{ + if (!DSP3_BitCount) + { + DSP3_ReqData = DSP3_DR; + DSP3_BitCount += 16; + } + + if (!DSP3_BaseCodes) + { + DSP3_GetBits(1); + if (DSP3_ReqBits) + { + DSP3_BaseLength = 3; + DSP3_BaseCodes = 8; + } + else + { + DSP3_BaseLength = 2; + DSP3_BaseCodes = 4; + } + } + + while (DSP3_BaseCodes) + { + if (!DSP3_GetBits(3)) + return; + + DSP3_ReqBits++; + + DSP3_CodeLengths[DSP3_Index] = (uint8) DSP3_ReqBits; + DSP3_CodeOffsets[DSP3_Index] = DSP3_Symbol; + DSP3_Index++; + + DSP3_Symbol += 1 << DSP3_ReqBits; + DSP3_BaseCodes--; + } + + DSP3_BaseCode = 0xffff; + DSP3_LZCode = 0; + + SetDSP3 = &DSP3_Decode_Data; + if (DSP3_BitCount) DSP3_Decode_Data(); +} + +void DSP3_Decode_Symbols() +{ + DSP3_ReqData = DSP3_DR; + DSP3_BitCount += 16; + + do { + + if (DSP3_BitCommand == 0xffff) + { + if (!DSP3_GetBits(2)) return; + DSP3_BitCommand = DSP3_ReqBits; + } + + switch (DSP3_BitCommand) + { + case 0: + { + if (!DSP3_GetBits(9)) return; + DSP3_Symbol = DSP3_ReqBits; + break; + } + case 1: + { + DSP3_Symbol++; + break; + } + case 2: + { + if (!DSP3_GetBits(1)) return; + DSP3_Symbol += 2 + DSP3_ReqBits; + break; + } + case 3: + { + if (!DSP3_GetBits(4)) return; + DSP3_Symbol += 4 + DSP3_ReqBits; + break; + } + } + + DSP3_BitCommand = 0xffff; + + DSP3_Codes[DSP3_Index++] = DSP3_Symbol; + DSP3_Codewords--; + + } while (DSP3_Codewords); + + DSP3_Index = 0; + DSP3_Symbol = 0; + DSP3_BaseCodes = 0; + + SetDSP3 = &DSP3_Decode_Tree; + if (DSP3_BitCount) DSP3_Decode_Tree(); +} + +void DSP3_Decode_A() +{ + DSP3_Outwords = DSP3_DR; + SetDSP3 = &DSP3_Decode_Symbols; + DSP3_BitCount = 0; + DSP3_BitsLeft = 0; + DSP3_Symbol = 0; + DSP3_Index = 0; + DSP3_BitCommand = 0xffff; + DSP3_SR = 0xC0; +} + +void DSP3_Decode() +{ + DSP3_Codewords = DSP3_DR; + SetDSP3 = &DSP3_Decode_A; +} + + +// Opcodes 1E/3E bit-perfect to 'dsp3-intro' log +// src: adapted from SD Gundam X/G-Next + +int16 op3e_x; +int16 op3e_y; + +int16 op1e_terrain[0x2000]; +int16 op1e_cost[0x2000]; +int16 op1e_weight[0x2000]; + +int16 op1e_cell; +int16 op1e_turn; +int16 op1e_search; + +int16 op1e_x; +int16 op1e_y; + +int16 op1e_min_radius; +int16 op1e_max_radius; + +int16 op1e_max_search_radius; +int16 op1e_max_path_radius; + +int16 op1e_lcv_radius; +int16 op1e_lcv_steps; +int16 op1e_lcv_turns; + +void DSP3_OP3E() +{ + op3e_x = (uint8)(DSP3_DR & 0x00ff); + op3e_y = (uint8)((DSP3_DR & 0xff00)>>8); + + DSP3_OP03(); + + op1e_terrain[ DSP3_DR ] = 0x00; + op1e_cost[ DSP3_DR ] = 0xff; + op1e_weight[ DSP3_DR ] = 0; + + op1e_max_search_radius = 0; + op1e_max_path_radius = 0; +} + +void DSP3_OP1E_A(); +void DSP3_OP1E_A1(); +void DSP3_OP1E_A2(); +void DSP3_OP1E_A3(); + +void DSP3_OP1E_B(); +void DSP3_OP1E_B1(); +void DSP3_OP1E_B2(); + +void DSP3_OP1E_C(); +void DSP3_OP1E_C1(); +void DSP3_OP1E_C2(); + +void DSP3_OP1E_D( int16, int16 *, int16 * ); +void DSP3_OP1E_D1( int16 move, int16 *lo, int16 *hi ); + +void DSP3_OP1E() +{ + int lcv; + + op1e_min_radius = (uint8)(DSP3_DR & 0x00ff); + op1e_max_radius = (uint8)((DSP3_DR & 0xff00)>>8); + + if( op1e_min_radius == 0 ) + op1e_min_radius++; + + if( op1e_max_search_radius >= op1e_min_radius ) + op1e_min_radius = op1e_max_search_radius+1; + + if( op1e_max_radius > op1e_max_search_radius ) + op1e_max_search_radius = op1e_max_radius; + + op1e_lcv_radius = op1e_min_radius; + op1e_lcv_steps = op1e_min_radius; + + op1e_lcv_turns = 6; + op1e_turn = 0; + + op1e_x = op3e_x; + op1e_y = op3e_y; + + for( lcv = 0; lcv < op1e_min_radius; lcv++ ) + DSP3_OP1E_D( op1e_turn, &op1e_x, &op1e_y ); + + DSP3_OP1E_A(); +} + +void DSP3_OP1E_A() +{ + int lcv; + + if( op1e_lcv_steps == 0 ) { + op1e_lcv_radius++; + + op1e_lcv_steps = op1e_lcv_radius; + + op1e_x = op3e_x; + op1e_y = op3e_y; + + for( lcv = 0; lcv < op1e_lcv_radius; lcv++ ) + DSP3_OP1E_D( op1e_turn, &op1e_x, &op1e_y ); + } + + if( op1e_lcv_radius > op1e_max_radius ) { + op1e_turn++; + op1e_lcv_turns--; + + op1e_lcv_radius = op1e_min_radius; + op1e_lcv_steps = op1e_min_radius; + + op1e_x = op3e_x; + op1e_y = op3e_y; + + for( lcv = 0; lcv < op1e_min_radius; lcv++ ) + DSP3_OP1E_D( op1e_turn, &op1e_x, &op1e_y ); + } + + if( op1e_lcv_turns == 0 ) { + DSP3_DR = 0xffff; + DSP3_SR = 0x0080; + SetDSP3 = &DSP3_OP1E_B; + return; + } + + DSP3_DR = (uint8)(op1e_x) | ((uint8)(op1e_y)<<8); + DSP3_OP03(); + + op1e_cell = DSP3_DR; + + DSP3_SR = 0x0080; + SetDSP3 = &DSP3_OP1E_A1; +} + +void DSP3_OP1E_A1() +{ + DSP3_SR = 0x0084; + SetDSP3 = &DSP3_OP1E_A2; +} + +void DSP3_OP1E_A2() +{ + op1e_terrain[ op1e_cell ] = (uint8)(DSP3_DR & 0x00ff); + + DSP3_SR = 0x0084; + SetDSP3 = &DSP3_OP1E_A3; +} + +void DSP3_OP1E_A3() +{ + op1e_cost[ op1e_cell ] = (uint8)(DSP3_DR & 0x00ff); + + if( op1e_lcv_radius == 1 ) { + if( op1e_terrain[ op1e_cell ] & 1 ) { + op1e_weight[ op1e_cell ] = 0xff; + } else { + op1e_weight[ op1e_cell ] = op1e_cost[ op1e_cell ]; + } + } + else { + op1e_weight[ op1e_cell ] = 0xff; + } + + DSP3_OP1E_D( (int16)(op1e_turn+2), &op1e_x, &op1e_y ); + op1e_lcv_steps--; + + DSP3_SR = 0x0080; + DSP3_OP1E_A(); +} + + +void DSP3_OP1E_B() +{ + op1e_x = op3e_x; + op1e_y = op3e_y; + op1e_lcv_radius = 1; + + op1e_search = 0; + + DSP3_OP1E_B1(); + + SetDSP3 = &DSP3_OP1E_C; +} + + +void DSP3_OP1E_B1() +{ + while( op1e_lcv_radius < op1e_max_radius ) { + op1e_y--; + + op1e_lcv_turns = 6; + op1e_turn = 5; + + while( op1e_lcv_turns ) { + op1e_lcv_steps = op1e_lcv_radius; + + while( op1e_lcv_steps ) { + DSP3_OP1E_D1( op1e_turn, &op1e_x, &op1e_y ); + + if( 0 <= op1e_y && op1e_y < DSP3_WinHi && + 0 <= op1e_x && op1e_x < DSP3_WinLo ) { + DSP3_DR = (uint8)(op1e_x) | ((uint8)(op1e_y)<<8); + DSP3_OP03(); + + op1e_cell = DSP3_DR; + if( op1e_cost[ op1e_cell ] < 0x80 && + op1e_terrain[ op1e_cell ] < 0x40 ) { + DSP3_OP1E_B2(); + } // end cell perimeter + } + + op1e_lcv_steps--; + } // end search line + + op1e_turn--; + if( op1e_turn == 0 ) op1e_turn = 6; + + op1e_lcv_turns--; + } // end circle search + + op1e_lcv_radius++; + } // end radius search +} + + +void DSP3_OP1E_B2() +{ + int16 cell; + int16 path; + int16 x,y; + int16 lcv_turns; + + path = 0xff; + lcv_turns = 6; + + while( lcv_turns ) { + x = op1e_x; + y = op1e_y; + + DSP3_OP1E_D1( lcv_turns, &x, &y ); + + DSP3_DR = (uint8)(x) | ((uint8)(y)<<8); + DSP3_OP03(); + + cell = DSP3_DR; + + if( 0 <= y && y < DSP3_WinHi && + 0 <= x && x < DSP3_WinLo ) { + + if( op1e_terrain[ cell ] < 0x80 || op1e_weight[ cell ] == 0 ) { + if( op1e_weight[ cell ] < path ) { + path = op1e_weight[ cell ]; + } + } + } // end step travel + + lcv_turns--; + } // end while turns + + if( path != 0xff ) { + op1e_weight[ op1e_cell ] = path + op1e_cost[ op1e_cell ]; + } +} + + +void DSP3_OP1E_C() +{ + int lcv; + + op1e_min_radius = (uint8)(DSP3_DR & 0x00ff); + op1e_max_radius = (uint8)((DSP3_DR & 0xff00)>>8); + + if( op1e_min_radius == 0 ) + op1e_min_radius++; + + if( op1e_max_path_radius >= op1e_min_radius ) + op1e_min_radius = op1e_max_path_radius+1; + + if( op1e_max_radius > op1e_max_path_radius ) + op1e_max_path_radius = op1e_max_radius; + + op1e_lcv_radius = op1e_min_radius; + op1e_lcv_steps = op1e_min_radius; + + op1e_lcv_turns = 6; + op1e_turn = 0; + + op1e_x = op3e_x; + op1e_y = op3e_y; + + for( lcv = 0; lcv < op1e_min_radius; lcv++ ) + DSP3_OP1E_D( op1e_turn, &op1e_x, &op1e_y ); + + DSP3_OP1E_C1(); +} + + +void DSP3_OP1E_C1() +{ + int lcv; + + if( op1e_lcv_steps == 0 ) { + op1e_lcv_radius++; + + op1e_lcv_steps = op1e_lcv_radius; + + op1e_x = op3e_x; + op1e_y = op3e_y; + + for( lcv = 0; lcv < op1e_lcv_radius; lcv++ ) + DSP3_OP1E_D( op1e_turn, &op1e_x, &op1e_y ); + } + + if( op1e_lcv_radius > op1e_max_radius ) { + op1e_turn++; + op1e_lcv_turns--; + + op1e_lcv_radius = op1e_min_radius; + op1e_lcv_steps = op1e_min_radius; + + op1e_x = op3e_x; + op1e_y = op3e_y; + + for( lcv = 0; lcv < op1e_min_radius; lcv++ ) + DSP3_OP1E_D( op1e_turn, &op1e_x, &op1e_y ); + } + + if( op1e_lcv_turns == 0 ) { + DSP3_DR = 0xffff; + DSP3_SR = 0x0080; + SetDSP3 = &DSP3_Reset; + return; + } + + DSP3_DR = (uint8)(op1e_x) | ((uint8)(op1e_y)<<8); + DSP3_OP03(); + + op1e_cell = DSP3_DR; + + DSP3_SR = 0x0080; + SetDSP3 = &DSP3_OP1E_C2; +} + + +void DSP3_OP1E_C2() +{ + DSP3_DR = op1e_weight[ op1e_cell ]; + + DSP3_OP1E_D( (int16)(op1e_turn+2), &op1e_x, &op1e_y ); + op1e_lcv_steps--; + + DSP3_SR = 0x0084; + SetDSP3 = &DSP3_OP1E_C1; +} + + +void DSP3_OP1E_D( int16 move, int16 *lo, int16 *hi ) +{ + uint32 dataOfs = ((move << 1) + 0x03b2) & 0x03ff; + int16 Lo; + int16 Hi; + + DSP3_AddHi = DSP3_DataROM[dataOfs]; + DSP3_AddLo = DSP3_DataROM[dataOfs + 1]; + + Lo = (uint8)(*lo); + Hi = (uint8)(*hi); + + if (Lo & 1) Hi += (DSP3_AddLo & 1); + + DSP3_AddLo += Lo; + DSP3_AddHi += Hi; + + if (DSP3_AddLo < 0) + DSP3_AddLo += DSP3_WinLo; + else + if (DSP3_AddLo >= DSP3_WinLo) + DSP3_AddLo -= DSP3_WinLo; + + if (DSP3_AddHi < 0) + DSP3_AddHi += DSP3_WinHi; + else + if (DSP3_AddHi >= DSP3_WinHi) + DSP3_AddHi -= DSP3_WinHi; + + *lo = DSP3_AddLo; + *hi = DSP3_AddHi; +} + + +void DSP3_OP1E_D1( int16 move, int16 *lo, int16 *hi ) +{ + //uint32 dataOfs = ((move << 1) + 0x03b2) & 0x03ff; + int16 Lo; + int16 Hi; + + const unsigned short HiAdd[] = { + 0x00, 0xFF, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, + 0x00, 0xFF, 0xFF, 0x00, 0x01, 0x00, 0xFF, 0x00 + }; + const unsigned short LoAdd[] = { + 0x00, 0x00, 0x01, 0x01, 0x00, 0xFF, 0xFF, 0x00 + }; + + if( (*lo) & 1 ) + DSP3_AddHi = HiAdd[ move + 8 ]; + else + DSP3_AddHi = HiAdd[ move + 0 ]; + DSP3_AddLo = LoAdd[ move ]; + + Lo = (uint8)(*lo); + Hi = (uint8)(*hi); + + if (Lo & 1) Hi += (DSP3_AddLo & 1); + + DSP3_AddLo += Lo; + DSP3_AddHi += Hi; + + *lo = DSP3_AddLo; + *hi = DSP3_AddHi; +} + + +void DSP3_OP10() +{ + if( DSP3_DR == 0xffff ) { + DSP3_Reset(); + } else { + // absorb 2 bytes + DSP3_DR = DSP3_DR; + } +} + + +void DSP3_OP0C_A() +{ + // absorb 2 bytes + + DSP3_DR = 0; + SetDSP3 = &DSP3_Reset; +} + + +void DSP3_OP0C() +{ + // absorb 2 bytes + + DSP3_DR = 0; + //SetDSP3 = &DSP3_OP0C_A; + SetDSP3 = &DSP3_Reset; +} + + +void DSP3_OP1C_C() +{ + // return 2 bytes + DSP3_DR = 0; + SetDSP3 = &DSP3_Reset; +} + + +void DSP3_OP1C_B() +{ + // absorb 2 bytes + + // return 2 bytes + DSP3_DR = 0; + SetDSP3 = &DSP3_OP1C_C; +} + + +void DSP3_OP1C_A() +{ + // absorb 2 bytes + + SetDSP3 = &DSP3_OP1C_B; +} + + +void DSP3_OP1C() +{ + // absorb 2 bytes + + SetDSP3 = &DSP3_OP1C_A; +} + + +void DSP3_Command() +{ + if (DSP3_DR < 0x40) + { + switch (DSP3_DR) + { + case 0x02: SetDSP3 = &DSP3_Coordinate; break; + case 0x03: SetDSP3 = &DSP3_OP03; break; + case 0x06: SetDSP3 = &DSP3_OP06; break; + case 0x07: SetDSP3 = &DSP3_OP07; return; + case 0x0c: SetDSP3 = &DSP3_OP0C; break; + case 0x0f: SetDSP3 = &DSP3_TestMemory; break; + case 0x10: SetDSP3 = &DSP3_OP10; break; + case 0x18: SetDSP3 = &DSP3_Convert; break; + case 0x1c: SetDSP3 = &DSP3_OP1C; break; + case 0x1e: SetDSP3 = &DSP3_OP1E; break; + case 0x1f: SetDSP3 = &DSP3_MemoryDump; break; + case 0x38: SetDSP3 = &DSP3_Decode; break; + case 0x3e: SetDSP3 = &DSP3_OP3E; break; + default: + return; + } + DSP3_SR = 0x0080; + DSP3_Index = 0; + } +} + +uint8 dsp3_byte; +uint16 dsp3_address; + +void DSP3SetByte() +{ + if (dsp3_address < 0xC000) + { + if (DSP3_SR & 0x04) + { + DSP3_DR = (DSP3_DR & 0xff00) + dsp3_byte; + (*SetDSP3)(); + } + else + { + DSP3_SR ^= 0x10; + + if (DSP3_SR & 0x10) + DSP3_DR = (DSP3_DR & 0xff00) + dsp3_byte; + else + { + DSP3_DR = (DSP3_DR & 0x00ff) + (dsp3_byte << 8); + (*SetDSP3)(); + } + } + } +} + +void DSP3GetByte() +{ + if (dsp3_address < 0xC000) + { + if (DSP3_SR & 0x04) + { + dsp3_byte = (uint8) DSP3_DR; + (*SetDSP3)(); + } + else + { + DSP3_SR ^= 0x10; + + if (DSP3_SR & 0x10) + dsp3_byte = (uint8) (DSP3_DR); + else + { + dsp3_byte = (uint8) (DSP3_DR >> 8); + (*SetDSP3)(); + } + } + + } + else + { + dsp3_byte = (uint8) DSP3_SR; + } +} + +void InitDSP3() +{ + DSP3_Reset(); +} + +#endif diff --git a/asnes/chip/dsp4/dsp4.cpp b/asnes/chip/dsp4/dsp4.cpp new file mode 100755 index 00000000..c568c728 --- /dev/null +++ b/asnes/chip/dsp4/dsp4.cpp @@ -0,0 +1,60 @@ +#include + +#define DSP4_CPP +namespace SNES { + +DSP4 dsp4; + +void DSP4::init() { +} + +void DSP4::enable() { +} + +namespace DSP4i { + inline uint16 READ_WORD(uint8 *addr) { + return (addr[0]) + (addr[1] << 8); + } + + inline uint32 READ_DWORD(uint8 *addr) { + return (addr[0]) + (addr[1] << 8) + (addr[2] << 16) + (addr[3] << 24); + } + + inline void WRITE_WORD(uint8 *addr, uint16 data) { + addr[0] = data; + addr[1] = data >> 8; + } + + #define bool8 uint8 + #include "dsp4emu.c" + #undef bool8 +}; + +void DSP4::power() { + reset(); +} + +void DSP4::reset() { + DSP4i::InitDSP4(); +} + +uint8 DSP4::read(unsigned addr) { + addr &= 0xffff; + if(addr < 0xc000) { + DSP4i::dsp4_address = addr; + DSP4i::DSP4GetByte(); + return DSP4i::dsp4_byte; + } + return 0x80; +} + +void DSP4::write(unsigned addr, uint8 data) { + addr &= 0xffff; + if(addr < 0xc000) { + DSP4i::dsp4_address = addr; + DSP4i::dsp4_byte = data; + DSP4i::DSP4SetByte(); + } +} + +} diff --git a/asnes/chip/dsp4/dsp4.hpp b/asnes/chip/dsp4/dsp4.hpp new file mode 100755 index 00000000..7a261192 --- /dev/null +++ b/asnes/chip/dsp4/dsp4.hpp @@ -0,0 +1,12 @@ +class DSP4 : public Memory { +public: + void init(); + void enable(); + void power(); + void reset(); + + uint8 read (unsigned addr); + void write(unsigned addr, uint8 data); +}; + +extern DSP4 dsp4; diff --git a/asnes/chip/dsp4/dsp4emu.c b/asnes/chip/dsp4/dsp4emu.c new file mode 100755 index 00000000..73c1ec3d --- /dev/null +++ b/asnes/chip/dsp4/dsp4emu.c @@ -0,0 +1,2150 @@ +#ifdef DSP4_CPP + +//DSP-4 emulator code +//Copyright (c) 2004-2006 Dreamer Nom, John Weidman, Kris Bleakley, Nach, z80 gaiden + +/* +Due recognition and credit are given on Overload's DSP website. +Thank those contributors for their hard work on this chip. + + +Fixed-point math reminder: + +[sign, integer, fraction] +1.15.00 * 1.15.00 = 2.30.00 -> 1.30.00 (DSP) -> 1.31.00 (LSB is '0') +1.15.00 * 1.00.15 = 2.15.15 -> 1.15.15 (DSP) -> 1.15.16 (LSB is '0') +*/ + +#include "dsp4emu.h" + +struct DSP4_t DSP4; +struct DSP4_vars_t DSP4_vars; + +////////////////////////////////////////////////////////////// + +// input protocol + +static int16 DSP4_READ_WORD() +{ + int16 out; + + out = READ_WORD(DSP4.parameters + DSP4.in_index); + DSP4.in_index += 2; + + return out; +} + +static int32 DSP4_READ_DWORD() +{ + int32 out; + + out = READ_DWORD(DSP4.parameters + DSP4.in_index); + DSP4.in_index += 4; + + return out; +} + + +////////////////////////////////////////////////////////////// + +// output protocol + +#define DSP4_CLEAR_OUT() \ +{ DSP4.out_count = 0; DSP4.out_index = 0; } + +#define DSP4_WRITE_BYTE( d ) \ +{ WRITE_WORD( DSP4.output + DSP4.out_count, ( d ) ); DSP4.out_count++; } + +#define DSP4_WRITE_WORD( d ) \ +{ WRITE_WORD( DSP4.output + DSP4.out_count, ( d ) ); DSP4.out_count += 2; } + +#ifndef MSB_FIRST +#define DSP4_WRITE_16_WORD( d ) \ +{ memcpy(DSP4.output + DSP4.out_count, ( d ), 32); DSP4.out_count += 32; } +#else +#define DSP4_WRITE_16_WORD( d ) \ +{ int16 *p = ( d ), *end = ( d )+16; \ + for (; p != end; p++) \ + { \ + WRITE_WORD( DSP4.output + DSP4.out_count, *p ); \ + } \ + DSP4.out_count += 32; \ +} +#endif + +#ifdef PRINT_OP +#define DSP4_WRITE_DEBUG( x, d ) \ + WRITE_WORD( nop + x, d ); +#endif + +#ifdef DEBUG_DSP +#define DSP4_WRITE_DEBUG( x, d ) \ + WRITE_WORD( nop + x, d ); +#endif + +////////////////////////////////////////////////////////////// + +// used to wait for dsp i/o + +#define DSP4_WAIT( x ) \ + DSP4.in_index = 0; DSP4_vars.DSP4_Logic = x; return; + +////////////////////////////////////////////////////////////// + +// 1.7.8 -> 1.15.16 +#define SEX78( a ) ( ( (int32) ( (int16) (a) ) ) << 8 ) + +// 1.15.0 -> 1.15.16 +#define SEX16( a ) ( ( (int32) ( (int16) (a) ) ) << 16 ) + +#ifdef PRINT_OP +#define U16( a ) ( (uint16) ( a ) ) +#endif + +#ifdef DEBUG_DSP +#define U16( a ) ( (uint16) ( a ) ) +#endif + +////////////////////////////////////////////////////////////// + +// Attention: This lookup table is not verified +static const uint16 div_lut[64] = { 0x0000, 0x8000, 0x4000, 0x2aaa, 0x2000, 0x1999, 0x1555, 0x1249, 0x1000, 0x0e38, + 0x0ccc, 0x0ba2, 0x0aaa, 0x09d8, 0x0924, 0x0888, 0x0800, 0x0787, 0x071c, 0x06bc, + 0x0666, 0x0618, 0x05d1, 0x0590, 0x0555, 0x051e, 0x04ec, 0x04bd, 0x0492, 0x0469, + 0x0444, 0x0421, 0x0400, 0x03e0, 0x03c3, 0x03a8, 0x038e, 0x0375, 0x035e, 0x0348, + 0x0333, 0x031f, 0x030c, 0x02fa, 0x02e8, 0x02d8, 0x02c8, 0x02b9, 0x02aa, 0x029c, + 0x028f, 0x0282, 0x0276, 0x026a, 0x025e, 0x0253, 0x0249, 0x023e, 0x0234, 0x022b, + 0x0222, 0x0219, 0x0210, 0x0208, }; +int16 DSP4_Inverse(int16 value) +{ + // saturate bounds + if (value < 0) + { + value = 0; + } + if (value > 63) + { + value = 63; + } + + return div_lut[value]; +} + +////////////////////////////////////////////////////////////// + +// Prototype +void DSP4_OP0B(bool8 *draw, int16 sp_x, int16 sp_y, int16 sp_attr, bool8 size, bool8 stop); + +////////////////////////////////////////////////////////////// + +// OP00 +void DSP4_Multiply(int16 Multiplicand, int16 Multiplier, int32 *Product) +{ + *Product = (Multiplicand * Multiplier << 1) >> 1; +} + +////////////////////////////////////////////////////////////// + + +void DSP4_OP01() +{ + DSP4.waiting4command = FALSE; + + // op flow control + switch (DSP4_vars.DSP4_Logic) + { + case 1: + goto resume1; break; + case 2: + goto resume2; break; + case 3: + goto resume3; break; + } + + //////////////////////////////////////////////////// + // process initial inputs + + // sort inputs + DSP4_vars.world_y = DSP4_READ_DWORD(); + DSP4_vars.poly_bottom[0][0] = DSP4_READ_WORD(); + DSP4_vars.poly_top[0][0] = DSP4_READ_WORD(); + DSP4_vars.poly_cx[1][0] = DSP4_READ_WORD(); + DSP4_vars.viewport_bottom = DSP4_READ_WORD(); + DSP4_vars.world_x = DSP4_READ_DWORD(); + DSP4_vars.poly_cx[0][0] = DSP4_READ_WORD(); + DSP4_vars.poly_ptr[0][0] = DSP4_READ_WORD(); + DSP4_vars.world_yofs = DSP4_READ_WORD(); + DSP4_vars.world_dy = DSP4_READ_DWORD(); + DSP4_vars.world_dx = DSP4_READ_DWORD(); + DSP4_vars.distance = DSP4_READ_WORD(); + DSP4_READ_WORD(); // 0x0000 + DSP4_vars.world_xenv = DSP4_READ_DWORD(); + DSP4_vars.world_ddy = DSP4_READ_WORD(); + DSP4_vars.world_ddx = DSP4_READ_WORD(); + DSP4_vars.view_yofsenv = DSP4_READ_WORD(); + + // initial (x,y,offset) at starting DSP4_vars.raster line + DSP4_vars.view_x1 = (int16)((DSP4_vars.world_x + DSP4_vars.world_xenv) >> 16); + DSP4_vars.view_y1 = (int16)(DSP4_vars.world_y >> 16); + DSP4_vars.view_xofs1 = (int16)(DSP4_vars.world_x >> 16); + DSP4_vars.view_yofs1 = DSP4_vars.world_yofs; + DSP4_vars.view_turnoff_x = 0; + DSP4_vars.view_turnoff_dx = 0; + + // first DSP4_vars.raster line + DSP4_vars.poly_raster[0][0] = DSP4_vars.poly_bottom[0][0]; + + do + { + //////////////////////////////////////////////////// + // process one iteration of projection + + // perspective projection of world (x,y,scroll) points + // based on the current projection lines + DSP4_vars.view_x2 = (int16)(( ( ( DSP4_vars.world_x + DSP4_vars.world_xenv ) >> 16 ) * DSP4_vars.distance >> 15 ) + ( DSP4_vars.view_turnoff_x * DSP4_vars.distance >> 15 )); + DSP4_vars.view_y2 = (int16)((DSP4_vars.world_y >> 16) * DSP4_vars.distance >> 15); + DSP4_vars.view_xofs2 = DSP4_vars.view_x2; + DSP4_vars.view_yofs2 = (DSP4_vars.world_yofs * DSP4_vars.distance >> 15) + DSP4_vars.poly_bottom[0][0] - DSP4_vars.view_y2; + + + // 1. World x-location before transformation + // 2. Viewer x-position at the next + // 3. World y-location before perspective projection + // 4. Viewer y-position below the horizon + // 5. Number of DSP4_vars.raster lines drawn in this iteration + + DSP4_CLEAR_OUT(); + DSP4_WRITE_WORD((uint16)((DSP4_vars.world_x + DSP4_vars.world_xenv) >> 16)); + DSP4_WRITE_WORD(DSP4_vars.view_x2); + DSP4_WRITE_WORD((uint16)(DSP4_vars.world_y >> 16)); + DSP4_WRITE_WORD(DSP4_vars.view_y2); + + ////////////////////////////////////////////////////// + + // SR = 0x00 + + // determine # of DSP4_vars.raster lines used + DSP4_vars.segments = DSP4_vars.poly_raster[0][0] - DSP4_vars.view_y2; + + // prevent overdraw + if (DSP4_vars.view_y2 >= DSP4_vars.poly_raster[0][0]) + DSP4_vars.segments = 0; + else + DSP4_vars.poly_raster[0][0] = DSP4_vars.view_y2; + + // don't draw outside the window + if (DSP4_vars.view_y2 < DSP4_vars.poly_top[0][0]) + { + DSP4_vars.segments = 0; + + // flush remaining DSP4_vars.raster lines + if (DSP4_vars.view_y1 >= DSP4_vars.poly_top[0][0]) + DSP4_vars.segments = DSP4_vars.view_y1 - DSP4_vars.poly_top[0][0]; + } + + // SR = 0x80 + + DSP4_WRITE_WORD(DSP4_vars.segments); + + ////////////////////////////////////////////////////// + + // scan next command if no SR check needed + if (DSP4_vars.segments) + { + int32 px_dx, py_dy; + int32 x_scroll, y_scroll; + + // SR = 0x00 + + // linear interpolation (lerp) between projected points + px_dx = (DSP4_vars.view_xofs2 - DSP4_vars.view_xofs1) * DSP4_Inverse(DSP4_vars.segments) << 1; + py_dy = (DSP4_vars.view_yofs2 - DSP4_vars.view_yofs1) * DSP4_Inverse(DSP4_vars.segments) << 1; + + // starting step values + x_scroll = SEX16(DSP4_vars.poly_cx[0][0] + DSP4_vars.view_xofs1); + y_scroll = SEX16(-DSP4_vars.viewport_bottom + DSP4_vars.view_yofs1 + DSP4_vars.view_yofsenv + DSP4_vars.poly_cx[1][0] - DSP4_vars.world_yofs); + + // SR = 0x80 + + // rasterize line + for (DSP4_vars.lcv = 0; DSP4_vars.lcv < DSP4_vars.segments; DSP4_vars.lcv++) + { + // 1. HDMA memory pointer (bg1) + // 2. vertical scroll offset ($210E) + // 3. horizontal scroll offset ($210D) + + DSP4_WRITE_WORD(DSP4_vars.poly_ptr[0][0]); + DSP4_WRITE_WORD((uint16)((y_scroll + 0x8000) >> 16)); + DSP4_WRITE_WORD((uint16)((x_scroll + 0x8000) >> 16)); + + + // update memory address + DSP4_vars.poly_ptr[0][0] -= 4; + + // update screen values + x_scroll += px_dx; + y_scroll += py_dy; + } + } + + //////////////////////////////////////////////////// + // Post-update + + // update new viewer (x,y,scroll) to last DSP4_vars.raster line drawn + DSP4_vars.view_x1 = DSP4_vars.view_x2; + DSP4_vars.view_y1 = DSP4_vars.view_y2; + DSP4_vars.view_xofs1 = DSP4_vars.view_xofs2; + DSP4_vars.view_yofs1 = DSP4_vars.view_yofs2; + + // add deltas for projection lines + DSP4_vars.world_dx += SEX78(DSP4_vars.world_ddx); + DSP4_vars.world_dy += SEX78(DSP4_vars.world_ddy); + + // update projection lines + DSP4_vars.world_x += (DSP4_vars.world_dx + DSP4_vars.world_xenv); + DSP4_vars.world_y += DSP4_vars.world_dy; + + // update road turnoff position + DSP4_vars.view_turnoff_x += DSP4_vars.view_turnoff_dx; + + //////////////////////////////////////////////////// + // command check + + // scan next command + DSP4.in_count = 2; + DSP4_WAIT(1) resume1 : + + // check for termination + DSP4_vars.distance = DSP4_READ_WORD(); + if (DSP4_vars.distance == -0x8000) + break; + + // road turnoff + if( (uint16) DSP4_vars.distance == 0x8001 ) + { + DSP4.in_count = 6; + DSP4_WAIT(2) resume2: + + DSP4_vars.distance = DSP4_READ_WORD(); + DSP4_vars.view_turnoff_x = DSP4_READ_WORD(); + DSP4_vars.view_turnoff_dx = DSP4_READ_WORD(); + + // factor in new changes + DSP4_vars.view_x1 += ( DSP4_vars.view_turnoff_x * DSP4_vars.distance >> 15 ); + DSP4_vars.view_xofs1 += ( DSP4_vars.view_turnoff_x * DSP4_vars.distance >> 15 ); + + // update stepping values + DSP4_vars.view_turnoff_x += DSP4_vars.view_turnoff_dx; + + DSP4.in_count = 2; + DSP4_WAIT(1) + } + + // already have 2 bytes read + DSP4.in_count = 6; + DSP4_WAIT(3) resume3 : + + // inspect inputs + DSP4_vars.world_ddy = DSP4_READ_WORD(); + DSP4_vars.world_ddx = DSP4_READ_WORD(); + DSP4_vars.view_yofsenv = DSP4_READ_WORD(); + + // no envelope here + DSP4_vars.world_xenv = 0; + } + while (1); + + // terminate op + DSP4.waiting4command = TRUE; +} + +////////////////////////////////////////////////////////////// + + +void DSP4_OP03() +{ + DSP4_vars.OAM_RowMax = 33; + memset(DSP4_vars.OAM_Row, 0, 64); +} + + +////////////////////////////////////////////////////////////// + + +void DSP4_OP05() +{ + DSP4_vars.OAM_index = 0; + DSP4_vars.OAM_bits = 0; + memset(DSP4_vars.OAM_attr, 0, 32); + DSP4_vars.sprite_count = 0; +} + + +////////////////////////////////////////////////////////////// + +void DSP4_OP06() +{ + DSP4_CLEAR_OUT(); + DSP4_WRITE_16_WORD(DSP4_vars.OAM_attr); +} + +////////////////////////////////////////////////////////////// + + +void DSP4_OP07() +{ + DSP4.waiting4command = FALSE; + + // op flow control + switch (DSP4_vars.DSP4_Logic) + { + case 1: + goto resume1; break; + case 2: + goto resume2; break; + } + + //////////////////////////////////////////////////// + // sort inputs + + DSP4_vars.world_y = DSP4_READ_DWORD(); + DSP4_vars.poly_bottom[0][0] = DSP4_READ_WORD(); + DSP4_vars.poly_top[0][0] = DSP4_READ_WORD(); + DSP4_vars.poly_cx[1][0] = DSP4_READ_WORD(); + DSP4_vars.viewport_bottom = DSP4_READ_WORD(); + DSP4_vars.world_x = DSP4_READ_DWORD(); + DSP4_vars.poly_cx[0][0] = DSP4_READ_WORD(); + DSP4_vars.poly_ptr[0][0] = DSP4_READ_WORD(); + DSP4_vars.world_yofs = DSP4_READ_WORD(); + DSP4_vars.distance = DSP4_READ_WORD(); + DSP4_vars.view_y2 = DSP4_READ_WORD(); + DSP4_vars.view_dy = DSP4_READ_WORD() * DSP4_vars.distance >> 15; + DSP4_vars.view_x2 = DSP4_READ_WORD(); + DSP4_vars.view_dx = DSP4_READ_WORD() * DSP4_vars.distance >> 15; + DSP4_vars.view_yofsenv = DSP4_READ_WORD(); + + // initial (x,y,offset) at starting DSP4_vars.raster line + DSP4_vars.view_x1 = (int16)(DSP4_vars.world_x >> 16); + DSP4_vars.view_y1 = (int16)(DSP4_vars.world_y >> 16); + DSP4_vars.view_xofs1 = DSP4_vars.view_x1; + DSP4_vars.view_yofs1 = DSP4_vars.world_yofs; + + // first DSP4_vars.raster line + DSP4_vars.poly_raster[0][0] = DSP4_vars.poly_bottom[0][0]; + + + do + { + //////////////////////////////////////////////////// + // process one iteration of projection + + // add shaping + DSP4_vars.view_x2 += DSP4_vars.view_dx; + DSP4_vars.view_y2 += DSP4_vars.view_dy; + + // vertical scroll calculation + DSP4_vars.view_xofs2 = DSP4_vars.view_x2; + DSP4_vars.view_yofs2 = (DSP4_vars.world_yofs * DSP4_vars.distance >> 15) + DSP4_vars.poly_bottom[0][0] - DSP4_vars.view_y2; + + // 1. Viewer x-position at the next + // 2. Viewer y-position below the horizon + // 3. Number of DSP4_vars.raster lines drawn in this iteration + + DSP4_CLEAR_OUT(); + DSP4_WRITE_WORD(DSP4_vars.view_x2); + DSP4_WRITE_WORD(DSP4_vars.view_y2); + + ////////////////////////////////////////////////////// + + // SR = 0x00 + + // determine # of DSP4_vars.raster lines used + DSP4_vars.segments = DSP4_vars.view_y1 - DSP4_vars.view_y2; + + // prevent overdraw + if (DSP4_vars.view_y2 >= DSP4_vars.poly_raster[0][0]) + DSP4_vars.segments = 0; + else + DSP4_vars.poly_raster[0][0] = DSP4_vars.view_y2; + + // don't draw outside the window + if (DSP4_vars.view_y2 < DSP4_vars.poly_top[0][0]) + { + DSP4_vars.segments = 0; + + // flush remaining DSP4_vars.raster lines + if (DSP4_vars.view_y1 >= DSP4_vars.poly_top[0][0]) + DSP4_vars.segments = DSP4_vars.view_y1 - DSP4_vars.poly_top[0][0]; + } + + // SR = 0x80 + + DSP4_WRITE_WORD(DSP4_vars.segments); + + ////////////////////////////////////////////////////// + + // scan next command if no SR check needed + if (DSP4_vars.segments) + { + int32 px_dx, py_dy; + int32 x_scroll, y_scroll; + + // SR = 0x00 + + // linear interpolation (lerp) between projected points + px_dx = (DSP4_vars.view_xofs2 - DSP4_vars.view_xofs1) * DSP4_Inverse(DSP4_vars.segments) << 1; + py_dy = (DSP4_vars.view_yofs2 - DSP4_vars.view_yofs1) * DSP4_Inverse(DSP4_vars.segments) << 1; + + // starting step values + x_scroll = SEX16(DSP4_vars.poly_cx[0][0] + DSP4_vars.view_xofs1); + y_scroll = SEX16(-DSP4_vars.viewport_bottom + DSP4_vars.view_yofs1 + DSP4_vars.view_yofsenv + DSP4_vars.poly_cx[1][0] - DSP4_vars.world_yofs); + + // SR = 0x80 + + // rasterize line + for (DSP4_vars.lcv = 0; DSP4_vars.lcv < DSP4_vars.segments; DSP4_vars.lcv++) + { + // 1. HDMA memory pointer (bg2) + // 2. vertical scroll offset ($2110) + // 3. horizontal scroll offset ($210F) + + DSP4_WRITE_WORD(DSP4_vars.poly_ptr[0][0]); + DSP4_WRITE_WORD((uint16)((y_scroll + 0x8000) >> 16)); + DSP4_WRITE_WORD((uint16)((x_scroll + 0x8000) >> 16)); + + // update memory address + DSP4_vars.poly_ptr[0][0] -= 4; + + // update screen values + x_scroll += px_dx; + y_scroll += py_dy; + } + } + + ///////////////////////////////////////////////////// + // Post-update + + // update new viewer (x,y,scroll) to last DSP4_vars.raster line drawn + DSP4_vars.view_x1 = DSP4_vars.view_x2; + DSP4_vars.view_y1 = DSP4_vars.view_y2; + DSP4_vars.view_xofs1 = DSP4_vars.view_xofs2; + DSP4_vars.view_yofs1 = DSP4_vars.view_yofs2; + + //////////////////////////////////////////////////// + // command check + + // scan next command + DSP4.in_count = 2; + DSP4_WAIT(1) resume1 : + + // check for opcode termination + DSP4_vars.distance = DSP4_READ_WORD(); + if (DSP4_vars.distance == -0x8000) + break; + + // already have 2 bytes in queue + DSP4.in_count = 10; + DSP4_WAIT(2) resume2 : + + // inspect inputs + DSP4_vars.view_y2 = DSP4_READ_WORD(); + DSP4_vars.view_dy = DSP4_READ_WORD() * DSP4_vars.distance >> 15; + DSP4_vars.view_x2 = DSP4_READ_WORD(); + DSP4_vars.view_dx = DSP4_READ_WORD() * DSP4_vars.distance >> 15; + DSP4_vars.view_yofsenv = DSP4_READ_WORD(); + } + while (1); + + DSP4.waiting4command = TRUE; +} + +////////////////////////////////////////////////////////////// + +void DSP4_OP08() +{ + int16 win_left, win_right; + int16 view_x[2], view_y[2]; + int16 envelope[2][2]; + + DSP4.waiting4command = FALSE; + + // op flow control + switch (DSP4_vars.DSP4_Logic) + { + case 1: + goto resume1; break; + case 2: + goto resume2; break; + } + + //////////////////////////////////////////////////// + // process initial inputs for two polygons + + // clip values + DSP4_vars.poly_clipRt[0][0] = DSP4_READ_WORD(); + DSP4_vars.poly_clipRt[0][1] = DSP4_READ_WORD(); + DSP4_vars.poly_clipRt[1][0] = DSP4_READ_WORD(); + DSP4_vars.poly_clipRt[1][1] = DSP4_READ_WORD(); + + DSP4_vars.poly_clipLf[0][0] = DSP4_READ_WORD(); + DSP4_vars.poly_clipLf[0][1] = DSP4_READ_WORD(); + DSP4_vars.poly_clipLf[1][0] = DSP4_READ_WORD(); + DSP4_vars.poly_clipLf[1][1] = DSP4_READ_WORD(); + + // unknown (constant) (ex. 1P/2P = $00A6, $00A6, $00A6, $00A6) + DSP4_READ_WORD(); + DSP4_READ_WORD(); + DSP4_READ_WORD(); + DSP4_READ_WORD(); + + // unknown (constant) (ex. 1P/2P = $00A5, $00A5, $00A7, $00A7) + DSP4_READ_WORD(); + DSP4_READ_WORD(); + DSP4_READ_WORD(); + DSP4_READ_WORD(); + + // polygon centering (left,right) + DSP4_vars.poly_cx[0][0] = DSP4_READ_WORD(); + DSP4_vars.poly_cx[0][1] = DSP4_READ_WORD(); + DSP4_vars.poly_cx[1][0] = DSP4_READ_WORD(); + DSP4_vars.poly_cx[1][1] = DSP4_READ_WORD(); + + // HDMA pointer locations + DSP4_vars.poly_ptr[0][0] = DSP4_READ_WORD(); + DSP4_vars.poly_ptr[0][1] = DSP4_READ_WORD(); + DSP4_vars.poly_ptr[1][0] = DSP4_READ_WORD(); + DSP4_vars.poly_ptr[1][1] = DSP4_READ_WORD(); + + // starting DSP4_vars.raster line below the horizon + DSP4_vars.poly_bottom[0][0] = DSP4_READ_WORD(); + DSP4_vars.poly_bottom[0][1] = DSP4_READ_WORD(); + DSP4_vars.poly_bottom[1][0] = DSP4_READ_WORD(); + DSP4_vars.poly_bottom[1][1] = DSP4_READ_WORD(); + + // top boundary line to clip + DSP4_vars.poly_top[0][0] = DSP4_READ_WORD(); + DSP4_vars.poly_top[0][1] = DSP4_READ_WORD(); + DSP4_vars.poly_top[1][0] = DSP4_READ_WORD(); + DSP4_vars.poly_top[1][1] = DSP4_READ_WORD(); + + // unknown + // (ex. 1P = $2FC8, $0034, $FF5C, $0035) + // + // (ex. 2P = $3178, $0034, $FFCC, $0035) + // (ex. 2P = $2FC8, $0034, $FFCC, $0035) + + DSP4_READ_WORD(); + DSP4_READ_WORD(); + DSP4_READ_WORD(); + DSP4_READ_WORD(); + + // look at guidelines for both polygon shapes + DSP4_vars.distance = DSP4_READ_WORD(); + view_x[0] = DSP4_READ_WORD(); + view_y[0] = DSP4_READ_WORD(); + view_x[1] = DSP4_READ_WORD(); + view_y[1] = DSP4_READ_WORD(); + + // envelope shaping guidelines (one frame only) + envelope[0][0] = DSP4_READ_WORD(); + envelope[0][1] = DSP4_READ_WORD(); + envelope[1][0] = DSP4_READ_WORD(); + envelope[1][1] = DSP4_READ_WORD(); + + // starting base values to project from + DSP4_vars.poly_start[0] = view_x[0]; + DSP4_vars.poly_start[1] = view_x[1]; + + // starting DSP4_vars.raster lines to begin drawing + DSP4_vars.poly_raster[0][0] = view_y[0]; + DSP4_vars.poly_raster[0][1] = view_y[0]; + DSP4_vars.poly_raster[1][0] = view_y[1]; + DSP4_vars.poly_raster[1][1] = view_y[1]; + + // starting distances + DSP4_vars.poly_plane[0] = DSP4_vars.distance; + DSP4_vars.poly_plane[1] = DSP4_vars.distance; + + // SR = 0x00 + + // re-center coordinates + win_left = DSP4_vars.poly_cx[0][0] - view_x[0] + envelope[0][0]; + win_right = DSP4_vars.poly_cx[0][1] - view_x[0] + envelope[0][1]; + + // saturate offscreen data for polygon #1 + if (win_left < DSP4_vars.poly_clipLf[0][0]) + { + win_left = DSP4_vars.poly_clipLf[0][0]; + } + if (win_left > DSP4_vars.poly_clipRt[0][0]) + { + win_left = DSP4_vars.poly_clipRt[0][0]; + } + if (win_right < DSP4_vars.poly_clipLf[0][1]) + { + win_right = DSP4_vars.poly_clipLf[0][1]; + } + if (win_right > DSP4_vars.poly_clipRt[0][1]) + { + win_right = DSP4_vars.poly_clipRt[0][1]; + } + + // SR = 0x80 + + // initial output for polygon #1 + DSP4_CLEAR_OUT(); + DSP4_WRITE_BYTE(win_left & 0xff); + DSP4_WRITE_BYTE(win_right & 0xff); + + + do + { + int16 polygon; + //////////////////////////////////////////////////// + // command check + + // scan next command + DSP4.in_count = 2; + DSP4_WAIT(1) resume1 : + + // terminate op + DSP4_vars.distance = DSP4_READ_WORD(); + if (DSP4_vars.distance == -0x8000) + break; + + // already have 2 bytes in queue + DSP4.in_count = 16; + + DSP4_WAIT(2) resume2 : + + // look at guidelines for both polygon shapes + view_x[0] = DSP4_READ_WORD(); + view_y[0] = DSP4_READ_WORD(); + view_x[1] = DSP4_READ_WORD(); + view_y[1] = DSP4_READ_WORD(); + + // envelope shaping guidelines (one frame only) + envelope[0][0] = DSP4_READ_WORD(); + envelope[0][1] = DSP4_READ_WORD(); + envelope[1][0] = DSP4_READ_WORD(); + envelope[1][1] = DSP4_READ_WORD(); + + //////////////////////////////////////////////////// + // projection begins + + // init + DSP4_CLEAR_OUT(); + + + ////////////////////////////////////////////// + // solid polygon renderer - 2 shapes + + for (polygon = 0; polygon < 2; polygon++) + { + int32 left_inc, right_inc; + int16 x1_final, x2_final; + int16 env[2][2]; + int16 poly; + + // SR = 0x00 + + // # DSP4_vars.raster lines to draw + DSP4_vars.segments = DSP4_vars.poly_raster[polygon][0] - view_y[polygon]; + + // prevent overdraw + if (DSP4_vars.segments > 0) + { + // bump drawing cursor + DSP4_vars.poly_raster[polygon][0] = view_y[polygon]; + DSP4_vars.poly_raster[polygon][1] = view_y[polygon]; + } + else + DSP4_vars.segments = 0; + + // don't draw outside the window + if (view_y[polygon] < DSP4_vars.poly_top[polygon][0]) + { + DSP4_vars.segments = 0; + + // flush remaining DSP4_vars.raster lines + if (view_y[polygon] >= DSP4_vars.poly_top[polygon][0]) + DSP4_vars.segments = view_y[polygon] - DSP4_vars.poly_top[polygon][0]; + } + + // SR = 0x80 + + // tell user how many DSP4_vars.raster structures to read in + DSP4_WRITE_WORD(DSP4_vars.segments); + + // normal parameters + poly = polygon; + + ///////////////////////////////////////////////////// + + // scan next command if no SR check needed + if (DSP4_vars.segments) + { + int32 win_left, win_right; + + // road turnoff selection + if( (uint16) envelope[ polygon ][ 0 ] == (uint16) 0xc001 ) + poly = 1; + else if( envelope[ polygon ][ 1 ] == 0x3fff ) + poly = 1; + + /////////////////////////////////////////////// + // left side of polygon + + // perspective correction on additional shaping parameters + env[0][0] = envelope[polygon][0] * DSP4_vars.poly_plane[poly] >> 15; + env[0][1] = envelope[polygon][0] * DSP4_vars.distance >> 15; + + // project new shapes (left side) + x1_final = view_x[poly] + env[0][0]; + x2_final = DSP4_vars.poly_start[poly] + env[0][1]; + + // interpolate between projected points with shaping + left_inc = (x2_final - x1_final) * DSP4_Inverse(DSP4_vars.segments) << 1; + if (DSP4_vars.segments == 1) + left_inc = -left_inc; + + /////////////////////////////////////////////// + // right side of polygon + + // perspective correction on additional shaping parameters + env[1][0] = envelope[polygon][1] * DSP4_vars.poly_plane[poly] >> 15;; + env[1][1] = envelope[polygon][1] * DSP4_vars.distance >> 15; + + // project new shapes (right side) + x1_final = view_x[poly] + env[1][0]; + x2_final = DSP4_vars.poly_start[poly] + env[1][1]; + + + // interpolate between projected points with shaping + right_inc = (x2_final - x1_final) * DSP4_Inverse(DSP4_vars.segments) << 1; + if (DSP4_vars.segments == 1) + right_inc = -right_inc; + + /////////////////////////////////////////////// + // update each point on the line + + win_left = SEX16(DSP4_vars.poly_cx[polygon][0] - DSP4_vars.poly_start[poly] + env[0][0]); + win_right = SEX16(DSP4_vars.poly_cx[polygon][1] - DSP4_vars.poly_start[poly] + env[1][0]); + + // update DSP4_vars.distance drawn into world + DSP4_vars.poly_plane[polygon] = DSP4_vars.distance; + + // rasterize line + for (DSP4_vars.lcv = 0; DSP4_vars.lcv < DSP4_vars.segments; DSP4_vars.lcv++) + { + int16 x_left, x_right; + + // project new coordinates + win_left += left_inc; + win_right += right_inc; + + // grab integer portion, drop fraction (no rounding) + x_left = (int16)(win_left >> 16); + x_right = (int16)(win_right >> 16); + + // saturate offscreen data + if (x_left < DSP4_vars.poly_clipLf[polygon][0]) + x_left = DSP4_vars.poly_clipLf[polygon][0]; + if (x_left > DSP4_vars.poly_clipRt[polygon][0]) + x_left = DSP4_vars.poly_clipRt[polygon][0]; + if (x_right < DSP4_vars.poly_clipLf[polygon][1]) + x_right = DSP4_vars.poly_clipLf[polygon][1]; + if (x_right > DSP4_vars.poly_clipRt[polygon][1]) + x_right = DSP4_vars.poly_clipRt[polygon][1]; + + // 1. HDMA memory pointer + // 2. Left window position ($2126/$2128) + // 3. Right window position ($2127/$2129) + + DSP4_WRITE_WORD(DSP4_vars.poly_ptr[polygon][0]); + DSP4_WRITE_BYTE(x_left & 0xff); + DSP4_WRITE_BYTE(x_right & 0xff); + + + // update memory pointers + DSP4_vars.poly_ptr[polygon][0] -= 4; + DSP4_vars.poly_ptr[polygon][1] -= 4; + } // end rasterize line + } + + //////////////////////////////////////////////// + // Post-update + + // new projection spot to continue rasterizing from + DSP4_vars.poly_start[polygon] = view_x[poly]; + } // end polygon rasterizer + } + while (1); + + // unknown output + DSP4_CLEAR_OUT(); + DSP4_WRITE_WORD(0); + + + DSP4.waiting4command = TRUE; +} + +////////////////////////////////////////////////////////////// + +void DSP4_OP09() +{ + DSP4.waiting4command = FALSE; + + // op flow control + switch (DSP4_vars.DSP4_Logic) + { + case 1: + goto resume1; break; + case 2: + goto resume2; break; + case 3: + goto resume3; break; + case 4: + goto resume4; break; + case 5: + goto resume5; break; + case 6: + goto resume6; break; + } + + //////////////////////////////////////////////////// + // process initial inputs + + // grab screen information + DSP4_vars.viewport_cx = DSP4_READ_WORD(); + DSP4_vars.viewport_cy = DSP4_READ_WORD(); + DSP4_READ_WORD(); // 0x0000 + DSP4_vars.viewport_left = DSP4_READ_WORD(); + DSP4_vars.viewport_right = DSP4_READ_WORD(); + DSP4_vars.viewport_top = DSP4_READ_WORD(); + DSP4_vars.viewport_bottom = DSP4_READ_WORD(); + + // starting DSP4_vars.raster line below the horizon + DSP4_vars.poly_bottom[0][0] = DSP4_vars.viewport_bottom - DSP4_vars.viewport_cy; + DSP4_vars.poly_raster[0][0] = 0x100; + + do + { + //////////////////////////////////////////////////// + // check for new sprites + + DSP4.in_count = 4; + DSP4_WAIT(1) resume1 : + + //////////////////////////////////////////////// + // DSP4_vars.raster overdraw check + + DSP4_vars.raster = DSP4_READ_WORD(); + + // continue updating the DSP4_vars.raster line where overdraw begins + if (DSP4_vars.raster < DSP4_vars.poly_raster[0][0]) + { + DSP4_vars.sprite_clipy = DSP4_vars.viewport_bottom - (DSP4_vars.poly_bottom[0][0] - DSP4_vars.raster); + DSP4_vars.poly_raster[0][0] = DSP4_vars.raster; + } + + ///////////////////////////////////////////////// + // identify sprite + + // op termination + DSP4_vars.distance = DSP4_READ_WORD(); + if (DSP4_vars.distance == -0x8000) + goto terminate; + + + // no sprite + if (DSP4_vars.distance == 0x0000) + { + continue; + } + + //////////////////////////////////////////////////// + // process projection information + + // vehicle sprite + if ((uint16) DSP4_vars.distance == 0x9000) + { + int16 car_left, car_right, car_back; + int16 impact_left, impact_back; + int16 world_spx, world_spy; + int16 view_spx, view_spy; + uint16 energy; + + // we already have 4 bytes we want + DSP4.in_count = 14; + DSP4_WAIT(2) resume2 : + + // filter inputs + energy = DSP4_READ_WORD(); + impact_back = DSP4_READ_WORD(); + car_back = DSP4_READ_WORD(); + impact_left = DSP4_READ_WORD(); + car_left = DSP4_READ_WORD(); + DSP4_vars.distance = DSP4_READ_WORD(); + car_right = DSP4_READ_WORD(); + + // calculate car's world (x,y) values + world_spx = car_right - car_left; + world_spy = car_back; + + // add in collision vector [needs bit-twiddling] + world_spx -= energy * (impact_left - car_left) >> 16; + world_spy -= energy * (car_back - impact_back) >> 16; + + // perspective correction for world (x,y) + view_spx = world_spx * DSP4_vars.distance >> 15; + view_spy = world_spy * DSP4_vars.distance >> 15; + + // convert to screen values + DSP4_vars.sprite_x = DSP4_vars.viewport_cx + view_spx; + DSP4_vars.sprite_y = DSP4_vars.viewport_bottom - (DSP4_vars.poly_bottom[0][0] - view_spy); + + // make the car's (x)-coordinate available + DSP4_CLEAR_OUT(); + DSP4_WRITE_WORD(world_spx); + + // grab a few remaining vehicle values + DSP4.in_count = 4; + DSP4_WAIT(3) resume3 : + + // add vertical lift factor + DSP4_vars.sprite_y += DSP4_READ_WORD(); + } + // terrain sprite + else + { + int16 world_spx, world_spy; + int16 view_spx, view_spy; + + // we already have 4 bytes we want + DSP4.in_count = 10; + DSP4_WAIT(4) resume4 : + + // sort loop inputs + DSP4_vars.poly_cx[0][0] = DSP4_READ_WORD(); + DSP4_vars.poly_raster[0][1] = DSP4_READ_WORD(); + world_spx = DSP4_READ_WORD(); + world_spy = DSP4_READ_WORD(); + + // compute base DSP4_vars.raster line from the bottom + DSP4_vars.segments = DSP4_vars.poly_bottom[0][0] - DSP4_vars.raster; + + // perspective correction for world (x,y) + view_spx = world_spx * DSP4_vars.distance >> 15; + view_spy = world_spy * DSP4_vars.distance >> 15; + + // convert to screen values + DSP4_vars.sprite_x = DSP4_vars.viewport_cx + view_spx - DSP4_vars.poly_cx[0][0]; + DSP4_vars.sprite_y = DSP4_vars.viewport_bottom - DSP4_vars.segments + view_spy; + } + + // default sprite size: 16x16 + DSP4_vars.sprite_size = 1; + DSP4_vars.sprite_attr = DSP4_READ_WORD(); + + //////////////////////////////////////////////////// + // convert tile data to SNES OAM format + + do + { + uint16 header; + + int16 sp_x, sp_y, sp_attr, sp_dattr; + int16 sp_dx, sp_dy; + int16 pixels; + + bool8 draw; + + DSP4.in_count = 2; + DSP4_WAIT(5) resume5 : + + draw = TRUE; + + // opcode termination + DSP4_vars.raster = DSP4_READ_WORD(); + if (DSP4_vars.raster == -0x8000) + goto terminate; + + // stop code + if (DSP4_vars.raster == 0x0000 && !DSP4_vars.sprite_size) + break; + + // toggle sprite size + if (DSP4_vars.raster == 0x0000) + { + DSP4_vars.sprite_size = !DSP4_vars.sprite_size; + continue; + } + + // check for valid sprite header + header = DSP4_vars.raster; + header >>= 8; + if (header != 0x20 && + header != 0x2e && //This is for attractor sprite + header != 0x40 && + header != 0x60 && + header != 0xa0 && + header != 0xc0 && + header != 0xe0) + break; + + // read in rest of sprite data + DSP4.in_count = 4; + DSP4_WAIT(6) resume6 : + + draw = TRUE; + + ///////////////////////////////////// + // process tile data + + // sprite deltas + sp_dattr = DSP4_vars.raster; + sp_dy = DSP4_READ_WORD(); + sp_dx = DSP4_READ_WORD(); + + // update coordinates to screen space + sp_x = DSP4_vars.sprite_x + sp_dx; + sp_y = DSP4_vars.sprite_y + sp_dy; + + // update sprite nametable/attribute information + sp_attr = DSP4_vars.sprite_attr + sp_dattr; + + // allow partially visibile tiles + pixels = DSP4_vars.sprite_size ? 15 : 7; + + DSP4_CLEAR_OUT(); + + // transparent tile to clip off parts of a sprite (overdraw) + if (DSP4_vars.sprite_clipy - pixels <= sp_y && + sp_y <= DSP4_vars.sprite_clipy && + sp_x >= DSP4_vars.viewport_left - pixels && + sp_x <= DSP4_vars.viewport_right && + DSP4_vars.sprite_clipy >= DSP4_vars.viewport_top - pixels && + DSP4_vars.sprite_clipy <= DSP4_vars.viewport_bottom) + { + DSP4_OP0B(&draw, sp_x, DSP4_vars.sprite_clipy, 0x00EE, DSP4_vars.sprite_size, 0); + } + + + // normal sprite tile + if (sp_x >= DSP4_vars.viewport_left - pixels && + sp_x <= DSP4_vars.viewport_right && + sp_y >= DSP4_vars.viewport_top - pixels && + sp_y <= DSP4_vars.viewport_bottom && + sp_y <= DSP4_vars.sprite_clipy) + { + DSP4_OP0B(&draw, sp_x, sp_y, sp_attr, DSP4_vars.sprite_size, 0); + } + + + // no following OAM data + DSP4_OP0B(&draw, 0, 0x0100, 0, 0, 1); + } + while (1); + } + while (1); + + terminate : DSP4.waiting4command = TRUE; +} + +////////////////////////////////////////////////////////////// + +const uint16 OP0A_Values[16] = { 0x0000, 0x0030, 0x0060, 0x0090, 0x00c0, 0x00f0, 0x0120, 0x0150, 0xfe80, + 0xfeb0, 0xfee0, 0xff10, 0xff40, 0xff70, 0xffa0, 0xffd0 }; + +void DSP4_OP0A(int16 n2, int16 *o1, int16 *o2, int16 *o3, int16 *o4) +{ + *o4 = OP0A_Values[(n2 & 0x000f)]; + *o3 = OP0A_Values[(n2 & 0x00f0) >> 4]; + *o2 = OP0A_Values[(n2 & 0x0f00) >> 8]; + *o1 = OP0A_Values[(n2 & 0xf000) >> 12]; +} + +////////////////////////////////////////////////////////////// + +void DSP4_OP0B(bool8 *draw, int16 sp_x, int16 sp_y, int16 sp_attr, bool8 size, bool8 stop) +{ + int16 Row1, Row2; + + // SR = 0x00 + + // align to nearest 8-pixel row + Row1 = (sp_y >> 3) & 0x1f; + Row2 = (Row1 + 1) & 0x1f; + + // check boundaries + if (!((sp_y < 0) || ((sp_y & 0x01ff) < 0x00eb))) + { + *draw = 0; + } + if (size) + { + if (DSP4_vars.OAM_Row[Row1] + 1 >= DSP4_vars.OAM_RowMax) + *draw = 0; + if (DSP4_vars.OAM_Row[Row2] + 1 >= DSP4_vars.OAM_RowMax) + *draw = 0; + } + else + { + if (DSP4_vars.OAM_Row[Row1] >= DSP4_vars.OAM_RowMax) + { + *draw = 0; + } + } + + // emulator fail-safe (unknown if this really exists) + if (DSP4_vars.sprite_count >= 128) + { + *draw = 0; + } + + // SR = 0x80 + + if (*draw) + { + // Row tiles + if (size) + { + DSP4_vars.OAM_Row[Row1] += 2; + DSP4_vars.OAM_Row[Row2] += 2; + } + else + { + DSP4_vars.OAM_Row[Row1]++; + } + + // yield OAM output + DSP4_WRITE_WORD(1); + + // pack OAM data: x,y,name,attr + DSP4_WRITE_BYTE(sp_x & 0xff); + DSP4_WRITE_BYTE(sp_y & 0xff); + DSP4_WRITE_WORD(sp_attr); + + DSP4_vars.sprite_count++; + + // OAM: size,msb data + // save post-oam table data for future retrieval + DSP4_vars.OAM_attr[DSP4_vars.OAM_index] |= ((sp_x <0 || sp_x> 255) << DSP4_vars.OAM_bits); + DSP4_vars.OAM_bits++; + + DSP4_vars.OAM_attr[DSP4_vars.OAM_index] |= (size << DSP4_vars.OAM_bits); + DSP4_vars.OAM_bits++; + + // move to next byte in buffer + if (DSP4_vars.OAM_bits == 16) + { + DSP4_vars.OAM_bits = 0; + DSP4_vars.OAM_index++; + } + } + else if (stop) + { + // yield no OAM output + DSP4_WRITE_WORD(0); + } +} + +////////////////////////////////////////////////////////////// + +void DSP4_OP0D() +{ + DSP4.waiting4command = FALSE; + + // op flow control + switch (DSP4_vars.DSP4_Logic) + { + case 1: + goto resume1; break; + case 2: + goto resume2; break; + } + + //////////////////////////////////////////////////// + // process initial inputs + + // sort inputs + DSP4_vars.world_y = DSP4_READ_DWORD(); + DSP4_vars.poly_bottom[0][0] = DSP4_READ_WORD(); + DSP4_vars.poly_top[0][0] = DSP4_READ_WORD(); + DSP4_vars.poly_cx[1][0] = DSP4_READ_WORD(); + DSP4_vars.viewport_bottom = DSP4_READ_WORD(); + DSP4_vars.world_x = DSP4_READ_DWORD(); + DSP4_vars.poly_cx[0][0] = DSP4_READ_WORD(); + DSP4_vars.poly_ptr[0][0] = DSP4_READ_WORD(); + DSP4_vars.world_yofs = DSP4_READ_WORD(); + DSP4_vars.world_dy = DSP4_READ_DWORD(); + DSP4_vars.world_dx = DSP4_READ_DWORD(); + DSP4_vars.distance = DSP4_READ_WORD(); + DSP4_READ_WORD(); // 0x0000 + DSP4_vars.world_xenv = SEX78(DSP4_READ_WORD()); + DSP4_vars.world_ddy = DSP4_READ_WORD(); + DSP4_vars.world_ddx = DSP4_READ_WORD(); + DSP4_vars.view_yofsenv = DSP4_READ_WORD(); + + // initial (x,y,offset) at starting DSP4_vars.raster line + DSP4_vars.view_x1 = (int16)((DSP4_vars.world_x + DSP4_vars.world_xenv) >> 16); + DSP4_vars.view_y1 = (int16)(DSP4_vars.world_y >> 16); + DSP4_vars.view_xofs1 = (int16)(DSP4_vars.world_x >> 16); + DSP4_vars.view_yofs1 = DSP4_vars.world_yofs; + + // first DSP4_vars.raster line + DSP4_vars.poly_raster[0][0] = DSP4_vars.poly_bottom[0][0]; + + + do + { + //////////////////////////////////////////////////// + // process one iteration of projection + + // perspective projection of world (x,y,scroll) points + // based on the current projection lines + DSP4_vars.view_x2 = (int16)(( ( ( DSP4_vars.world_x + DSP4_vars.world_xenv ) >> 16 ) * DSP4_vars.distance >> 15 ) + ( DSP4_vars.view_turnoff_x * DSP4_vars.distance >> 15 )); + DSP4_vars.view_y2 = (int16)((DSP4_vars.world_y >> 16) * DSP4_vars.distance >> 15); + DSP4_vars.view_xofs2 = DSP4_vars.view_x2; + DSP4_vars.view_yofs2 = (DSP4_vars.world_yofs * DSP4_vars.distance >> 15) + DSP4_vars.poly_bottom[0][0] - DSP4_vars.view_y2; + + // 1. World x-location before transformation + // 2. Viewer x-position at the current + // 3. World y-location before perspective projection + // 4. Viewer y-position below the horizon + // 5. Number of DSP4_vars.raster lines drawn in this iteration + + DSP4_CLEAR_OUT(); + DSP4_WRITE_WORD((uint16)((DSP4_vars.world_x + DSP4_vars.world_xenv) >> 16)); + DSP4_WRITE_WORD(DSP4_vars.view_x2); + DSP4_WRITE_WORD((uint16)(DSP4_vars.world_y >> 16)); + DSP4_WRITE_WORD(DSP4_vars.view_y2); + + ////////////////////////////////////////////////////////// + + // SR = 0x00 + + // determine # of DSP4_vars.raster lines used + DSP4_vars.segments = DSP4_vars.view_y1 - DSP4_vars.view_y2; + + // prevent overdraw + if (DSP4_vars.view_y2 >= DSP4_vars.poly_raster[0][0]) + DSP4_vars.segments = 0; + else + DSP4_vars.poly_raster[0][0] = DSP4_vars.view_y2; + + // don't draw outside the window + if (DSP4_vars.view_y2 < DSP4_vars.poly_top[0][0]) + { + DSP4_vars.segments = 0; + + // flush remaining DSP4_vars.raster lines + if (DSP4_vars.view_y1 >= DSP4_vars.poly_top[0][0]) + DSP4_vars.segments = DSP4_vars.view_y1 - DSP4_vars.poly_top[0][0]; + } + + // SR = 0x80 + + DSP4_WRITE_WORD(DSP4_vars.segments); + + ////////////////////////////////////////////////////////// + + // scan next command if no SR check needed + if (DSP4_vars.segments) + { + int32 px_dx, py_dy; + int32 x_scroll, y_scroll; + + // SR = 0x00 + + // linear interpolation (lerp) between projected points + px_dx = (DSP4_vars.view_xofs2 - DSP4_vars.view_xofs1) * DSP4_Inverse(DSP4_vars.segments) << 1; + py_dy = (DSP4_vars.view_yofs2 - DSP4_vars.view_yofs1) * DSP4_Inverse(DSP4_vars.segments) << 1; + + // starting step values + x_scroll = SEX16(DSP4_vars.poly_cx[0][0] + DSP4_vars.view_xofs1); + y_scroll = SEX16(-DSP4_vars.viewport_bottom + DSP4_vars.view_yofs1 + DSP4_vars.view_yofsenv + DSP4_vars.poly_cx[1][0] - DSP4_vars.world_yofs); + + // SR = 0x80 + + // rasterize line + for (DSP4_vars.lcv = 0; DSP4_vars.lcv < DSP4_vars.segments; DSP4_vars.lcv++) + { + // 1. HDMA memory pointer (bg1) + // 2. vertical scroll offset ($210E) + // 3. horizontal scroll offset ($210D) + + DSP4_WRITE_WORD(DSP4_vars.poly_ptr[0][0]); + DSP4_WRITE_WORD((uint16)((y_scroll + 0x8000) >> 16)); + DSP4_WRITE_WORD((uint16)((x_scroll + 0x8000) >> 16)); + + + // update memory address + DSP4_vars.poly_ptr[0][0] -= 4; + + // update screen values + x_scroll += px_dx; + y_scroll += py_dy; + } + } + + ///////////////////////////////////////////////////// + // Post-update + + // update new viewer (x,y,scroll) to last DSP4_vars.raster line drawn + DSP4_vars.view_x1 = DSP4_vars.view_x2; + DSP4_vars.view_y1 = DSP4_vars.view_y2; + DSP4_vars.view_xofs1 = DSP4_vars.view_xofs2; + DSP4_vars.view_yofs1 = DSP4_vars.view_yofs2; + + // add deltas for projection lines + DSP4_vars.world_dx += SEX78(DSP4_vars.world_ddx); + DSP4_vars.world_dy += SEX78(DSP4_vars.world_ddy); + + // update projection lines + DSP4_vars.world_x += (DSP4_vars.world_dx + DSP4_vars.world_xenv); + DSP4_vars.world_y += DSP4_vars.world_dy; + + //////////////////////////////////////////////////// + // command check + + // scan next command + DSP4.in_count = 2; + DSP4_WAIT(1) resume1 : + + // inspect input + DSP4_vars.distance = DSP4_READ_WORD(); + + // terminate op + if (DSP4_vars.distance == -0x8000) + break; + + // already have 2 bytes in queue + DSP4.in_count = 6; + DSP4_WAIT(2) resume2: + + // inspect inputs + DSP4_vars.world_ddy = DSP4_READ_WORD(); + DSP4_vars.world_ddx = DSP4_READ_WORD(); + DSP4_vars.view_yofsenv = DSP4_READ_WORD(); + + // no envelope here + DSP4_vars.world_xenv = 0; + } + while (1); + + DSP4.waiting4command = TRUE; +} + +////////////////////////////////////////////////////////////// + + +void DSP4_OP0E() +{ + DSP4_vars.OAM_RowMax = 16; + memset(DSP4_vars.OAM_Row, 0, 64); +} + + +////////////////////////////////////////////////////////////// + +void DSP4_OP0F() +{ + DSP4.waiting4command = FALSE; + + // op flow control + switch (DSP4_vars.DSP4_Logic) + { + case 1: + goto resume1; break; + case 2: + goto resume2; break; + case 3: + goto resume3; break; + case 4: + goto resume4; break; + } + + //////////////////////////////////////////////////// + // process initial inputs + + // sort inputs + DSP4_READ_WORD(); // 0x0000 + DSP4_vars.world_y = DSP4_READ_DWORD(); + DSP4_vars.poly_bottom[0][0] = DSP4_READ_WORD(); + DSP4_vars.poly_top[0][0] = DSP4_READ_WORD(); + DSP4_vars.poly_cx[1][0] = DSP4_READ_WORD(); + DSP4_vars.viewport_bottom = DSP4_READ_WORD(); + DSP4_vars.world_x = DSP4_READ_DWORD(); + DSP4_vars.poly_cx[0][0] = DSP4_READ_WORD(); + DSP4_vars.poly_ptr[0][0] = DSP4_READ_WORD(); + DSP4_vars.world_yofs = DSP4_READ_WORD(); + DSP4_vars.world_dy = DSP4_READ_DWORD(); + DSP4_vars.world_dx = DSP4_READ_DWORD(); + DSP4_vars.distance = DSP4_READ_WORD(); + DSP4_READ_WORD(); // 0x0000 + DSP4_vars.world_xenv = DSP4_READ_DWORD(); + DSP4_vars.world_ddy = DSP4_READ_WORD(); + DSP4_vars.world_ddx = DSP4_READ_WORD(); + DSP4_vars.view_yofsenv = DSP4_READ_WORD(); + + // initial (x,y,offset) at starting DSP4_vars.raster line + DSP4_vars.view_x1 = (int16)((DSP4_vars.world_x + DSP4_vars.world_xenv) >> 16); + DSP4_vars.view_y1 = (int16)(DSP4_vars.world_y >> 16); + DSP4_vars.view_xofs1 = (int16)(DSP4_vars.world_x >> 16); + DSP4_vars.view_yofs1 = DSP4_vars.world_yofs; + DSP4_vars.view_turnoff_x = 0; + DSP4_vars.view_turnoff_dx = 0; + + // first DSP4_vars.raster line + DSP4_vars.poly_raster[0][0] = DSP4_vars.poly_bottom[0][0]; + + + do + { + //////////////////////////////////////////////////// + // process one iteration of projection + + // perspective projection of world (x,y,scroll) points + // based on the current projection lines + DSP4_vars.view_x2 = (int16)(((DSP4_vars.world_x + DSP4_vars.world_xenv) >> 16) * DSP4_vars.distance >> 15); + DSP4_vars.view_y2 = (int16)((DSP4_vars.world_y >> 16) * DSP4_vars.distance >> 15); + DSP4_vars.view_xofs2 = DSP4_vars.view_x2; + DSP4_vars.view_yofs2 = (DSP4_vars.world_yofs * DSP4_vars.distance >> 15) + DSP4_vars.poly_bottom[0][0] - DSP4_vars.view_y2; + + // 1. World x-location before transformation + // 2. Viewer x-position at the next + // 3. World y-location before perspective projection + // 4. Viewer y-position below the horizon + // 5. Number of DSP4_vars.raster lines drawn in this iteration + + DSP4_CLEAR_OUT(); + DSP4_WRITE_WORD((uint16)((DSP4_vars.world_x + DSP4_vars.world_xenv) >> 16)); + DSP4_WRITE_WORD(DSP4_vars.view_x2); + DSP4_WRITE_WORD((uint16)(DSP4_vars.world_y >> 16)); + DSP4_WRITE_WORD(DSP4_vars.view_y2); + + ////////////////////////////////////////////////////// + + // SR = 0x00 + + // determine # of DSP4_vars.raster lines used + DSP4_vars.segments = DSP4_vars.poly_raster[0][0] - DSP4_vars.view_y2; + + // prevent overdraw + if (DSP4_vars.view_y2 >= DSP4_vars.poly_raster[0][0]) + DSP4_vars.segments = 0; + else + DSP4_vars.poly_raster[0][0] = DSP4_vars.view_y2; + + // don't draw outside the window + if (DSP4_vars.view_y2 < DSP4_vars.poly_top[0][0]) + { + DSP4_vars.segments = 0; + + // flush remaining DSP4_vars.raster lines + if (DSP4_vars.view_y1 >= DSP4_vars.poly_top[0][0]) + DSP4_vars.segments = DSP4_vars.view_y1 - DSP4_vars.poly_top[0][0]; + } + + // SR = 0x80 + + DSP4_WRITE_WORD(DSP4_vars.segments); + + ////////////////////////////////////////////////////// + + // scan next command if no SR check needed + if (DSP4_vars.segments) + { + int32 px_dx, py_dy; + int32 x_scroll, y_scroll; + + for (DSP4_vars.lcv = 0; DSP4_vars.lcv < 4; DSP4_vars.lcv++) + { + // grab inputs + DSP4.in_count = 4; + DSP4_WAIT(1); + resume1 : + for (;;) + { + int16 distance; + int16 color, red, green, blue; + + distance = DSP4_READ_WORD(); + color = DSP4_READ_WORD(); + + // U1+B5+G5+R5 + red = color & 0x1f; + green = (color >> 5) & 0x1f; + blue = (color >> 10) & 0x1f; + + // dynamic lighting + red = (red * distance >> 15) & 0x1f; + green = (green * distance >> 15) & 0x1f; + blue = (blue * distance >> 15) & 0x1f; + color = red | (green << 5) | (blue << 10); + + DSP4_CLEAR_OUT(); + DSP4_WRITE_WORD(color); + break; + } + } + + ////////////////////////////////////////////////////// + + // SR = 0x00 + + // linear interpolation (lerp) between projected points + px_dx = (DSP4_vars.view_xofs2 - DSP4_vars.view_xofs1) * DSP4_Inverse(DSP4_vars.segments) << 1; + py_dy = (DSP4_vars.view_yofs2 - DSP4_vars.view_yofs1) * DSP4_Inverse(DSP4_vars.segments) << 1; + + + // starting step values + x_scroll = SEX16(DSP4_vars.poly_cx[0][0] + DSP4_vars.view_xofs1); + y_scroll = SEX16(-DSP4_vars.viewport_bottom + DSP4_vars.view_yofs1 + DSP4_vars.view_yofsenv + DSP4_vars.poly_cx[1][0] - DSP4_vars.world_yofs); + + // SR = 0x80 + + // rasterize line + for (DSP4_vars.lcv = 0; DSP4_vars.lcv < DSP4_vars.segments; DSP4_vars.lcv++) + { + // 1. HDMA memory pointer + // 2. vertical scroll offset ($210E) + // 3. horizontal scroll offset ($210D) + + DSP4_WRITE_WORD(DSP4_vars.poly_ptr[0][0]); + DSP4_WRITE_WORD((uint16)((y_scroll + 0x8000) >> 16)); + DSP4_WRITE_WORD((uint16)((x_scroll + 0x8000) >> 16)); + + // update memory address + DSP4_vars.poly_ptr[0][0] -= 4; + + // update screen values + x_scroll += px_dx; + y_scroll += py_dy; + } + } + + //////////////////////////////////////////////////// + // Post-update + + // update new viewer (x,y,scroll) to last DSP4_vars.raster line drawn + DSP4_vars.view_x1 = DSP4_vars.view_x2; + DSP4_vars.view_y1 = DSP4_vars.view_y2; + DSP4_vars.view_xofs1 = DSP4_vars.view_xofs2; + DSP4_vars.view_yofs1 = DSP4_vars.view_yofs2; + + // add deltas for projection lines + DSP4_vars.world_dx += SEX78(DSP4_vars.world_ddx); + DSP4_vars.world_dy += SEX78(DSP4_vars.world_ddy); + + // update projection lines + DSP4_vars.world_x += (DSP4_vars.world_dx + DSP4_vars.world_xenv); + DSP4_vars.world_y += DSP4_vars.world_dy; + + // update road turnoff position + DSP4_vars.view_turnoff_x += DSP4_vars.view_turnoff_dx; + + //////////////////////////////////////////////////// + // command check + + // scan next command + DSP4.in_count = 2; + DSP4_WAIT(2) resume2: + + // check for termination + DSP4_vars.distance = DSP4_READ_WORD(); + if (DSP4_vars.distance == -0x8000) + break; + + // road splice + if( (uint16) DSP4_vars.distance == 0x8001 ) + { + DSP4.in_count = 6; + DSP4_WAIT(3) resume3: + + DSP4_vars.distance = DSP4_READ_WORD(); + DSP4_vars.view_turnoff_x = DSP4_READ_WORD(); + DSP4_vars.view_turnoff_dx = DSP4_READ_WORD(); + + // factor in new changes + DSP4_vars.view_x1 += ( DSP4_vars.view_turnoff_x * DSP4_vars.distance >> 15 ); + DSP4_vars.view_xofs1 += ( DSP4_vars.view_turnoff_x * DSP4_vars.distance >> 15 ); + + // update stepping values + DSP4_vars.view_turnoff_x += DSP4_vars.view_turnoff_dx; + + DSP4.in_count = 2; + DSP4_WAIT(2) + } + + // already have 2 bytes in queue + DSP4.in_count = 6; + DSP4_WAIT(4) resume4 : + + // inspect inputs + DSP4_vars.world_ddy = DSP4_READ_WORD(); + DSP4_vars.world_ddx = DSP4_READ_WORD(); + DSP4_vars.view_yofsenv = DSP4_READ_WORD(); + + // no envelope here + DSP4_vars.world_xenv = 0; + } + while (1); + + // terminate op + DSP4.waiting4command = TRUE; +} + +////////////////////////////////////////////////////////////// + + +void DSP4_OP10() +{ + DSP4.waiting4command = FALSE; + + // op flow control + switch (DSP4_vars.DSP4_Logic) + { + case 1: + goto resume1; break; + case 2: + goto resume2; break; + case 3: + goto resume3; break; + } + + //////////////////////////////////////////////////// + // sort inputs + + DSP4_READ_WORD(); // 0x0000 + DSP4_vars.world_y = DSP4_READ_DWORD(); + DSP4_vars.poly_bottom[0][0] = DSP4_READ_WORD(); + DSP4_vars.poly_top[0][0] = DSP4_READ_WORD(); + DSP4_vars.poly_cx[1][0] = DSP4_READ_WORD(); + DSP4_vars.viewport_bottom = DSP4_READ_WORD(); + DSP4_vars.world_x = DSP4_READ_DWORD(); + DSP4_vars.poly_cx[0][0] = DSP4_READ_WORD(); + DSP4_vars.poly_ptr[0][0] = DSP4_READ_WORD(); + DSP4_vars.world_yofs = DSP4_READ_WORD(); + DSP4_vars.distance = DSP4_READ_WORD(); + DSP4_vars.view_y2 = DSP4_READ_WORD(); + DSP4_vars.view_dy = DSP4_READ_WORD() * DSP4_vars.distance >> 15; + DSP4_vars.view_x2 = DSP4_READ_WORD(); + DSP4_vars.view_dx = DSP4_READ_WORD() * DSP4_vars.distance >> 15; + DSP4_vars.view_yofsenv = DSP4_READ_WORD(); + + // initial (x,y,offset) at starting DSP4_vars.raster line + DSP4_vars.view_x1 = (int16)(DSP4_vars.world_x >> 16); + DSP4_vars.view_y1 = (int16)(DSP4_vars.world_y >> 16); + DSP4_vars.view_xofs1 = DSP4_vars.view_x1; + DSP4_vars.view_yofs1 = DSP4_vars.world_yofs; + + // first DSP4_vars.raster line + DSP4_vars.poly_raster[0][0] = DSP4_vars.poly_bottom[0][0]; + + do + { + //////////////////////////////////////////////////// + // process one iteration of projection + + // add shaping + DSP4_vars.view_x2 += DSP4_vars.view_dx; + DSP4_vars.view_y2 += DSP4_vars.view_dy; + + // vertical scroll calculation + DSP4_vars.view_xofs2 = DSP4_vars.view_x2; + DSP4_vars.view_yofs2 = (DSP4_vars.world_yofs * DSP4_vars.distance >> 15) + DSP4_vars.poly_bottom[0][0] - DSP4_vars.view_y2; + + // 1. Viewer x-position at the next + // 2. Viewer y-position below the horizon + // 3. Number of DSP4_vars.raster lines drawn in this iteration + + DSP4_CLEAR_OUT(); + DSP4_WRITE_WORD(DSP4_vars.view_x2); + DSP4_WRITE_WORD(DSP4_vars.view_y2); + + ////////////////////////////////////////////////////// + + // SR = 0x00 + + // determine # of DSP4_vars.raster lines used + DSP4_vars.segments = DSP4_vars.view_y1 - DSP4_vars.view_y2; + + // prevent overdraw + if (DSP4_vars.view_y2 >= DSP4_vars.poly_raster[0][0]) + DSP4_vars.segments = 0; + else + DSP4_vars.poly_raster[0][0] = DSP4_vars.view_y2; + + // don't draw outside the window + if (DSP4_vars.view_y2 < DSP4_vars.poly_top[0][0]) + { + DSP4_vars.segments = 0; + + // flush remaining DSP4_vars.raster lines + if (DSP4_vars.view_y1 >= DSP4_vars.poly_top[0][0]) + DSP4_vars.segments = DSP4_vars.view_y1 - DSP4_vars.poly_top[0][0]; + } + + // SR = 0x80 + + DSP4_WRITE_WORD(DSP4_vars.segments); + + ////////////////////////////////////////////////////// + + // scan next command if no SR check needed + if (DSP4_vars.segments) + { + for (DSP4_vars.lcv = 0; DSP4_vars.lcv < 4; DSP4_vars.lcv++) + { + // grab inputs + DSP4.in_count = 4; + DSP4_WAIT(1); + resume1 : + for (;;) + { + int16 distance; + int16 color, red, green, blue; + + distance = DSP4_READ_WORD(); + color = DSP4_READ_WORD(); + + // U1+B5+G5+R5 + red = color & 0x1f; + green = (color >> 5) & 0x1f; + blue = (color >> 10) & 0x1f; + + // dynamic lighting + red = (red * distance >> 15) & 0x1f; + green = (green * distance >> 15) & 0x1f; + blue = (blue * distance >> 15) & 0x1f; + color = red | (green << 5) | (blue << 10); + + DSP4_CLEAR_OUT(); + DSP4_WRITE_WORD(color); + break; + } + } + } + + ////////////////////////////////////////////////////// + + // scan next command if no SR check needed + if (DSP4_vars.segments) + { + int32 px_dx, py_dy; + int32 x_scroll, y_scroll; + + // SR = 0x00 + + // linear interpolation (lerp) between projected points + px_dx = (DSP4_vars.view_xofs2 - DSP4_vars.view_xofs1) * DSP4_Inverse(DSP4_vars.segments) << 1; + py_dy = (DSP4_vars.view_yofs2 - DSP4_vars.view_yofs1) * DSP4_Inverse(DSP4_vars.segments) << 1; + + // starting step values + x_scroll = SEX16(DSP4_vars.poly_cx[0][0] + DSP4_vars.view_xofs1); + y_scroll = SEX16(-DSP4_vars.viewport_bottom + DSP4_vars.view_yofs1 + DSP4_vars.view_yofsenv + DSP4_vars.poly_cx[1][0] - DSP4_vars.world_yofs); + + // SR = 0x80 + + // rasterize line + for (DSP4_vars.lcv = 0; DSP4_vars.lcv < DSP4_vars.segments; DSP4_vars.lcv++) + { + // 1. HDMA memory pointer (bg2) + // 2. vertical scroll offset ($2110) + // 3. horizontal scroll offset ($210F) + + DSP4_WRITE_WORD(DSP4_vars.poly_ptr[0][0]); + DSP4_WRITE_WORD((uint16)((y_scroll + 0x8000) >> 16)); + DSP4_WRITE_WORD((uint16)((x_scroll + 0x8000) >> 16)); + + // update memory address + DSP4_vars.poly_ptr[0][0] -= 4; + + // update screen values + x_scroll += px_dx; + y_scroll += py_dy; + } + } + + ///////////////////////////////////////////////////// + // Post-update + + // update new viewer (x,y,scroll) to last DSP4_vars.raster line drawn + DSP4_vars.view_x1 = DSP4_vars.view_x2; + DSP4_vars.view_y1 = DSP4_vars.view_y2; + DSP4_vars.view_xofs1 = DSP4_vars.view_xofs2; + DSP4_vars.view_yofs1 = DSP4_vars.view_yofs2; + + //////////////////////////////////////////////////// + // command check + + // scan next command + DSP4.in_count = 2; + DSP4_WAIT(2) resume2 : + + // check for opcode termination + DSP4_vars.distance = DSP4_READ_WORD(); + if (DSP4_vars.distance == -0x8000) + break; + + // already have 2 bytes in queue + DSP4.in_count = 10; + DSP4_WAIT(3) resume3 : + + + // inspect inputs + DSP4_vars.view_y2 = DSP4_READ_WORD(); + DSP4_vars.view_dy = DSP4_READ_WORD() * DSP4_vars.distance >> 15; + DSP4_vars.view_x2 = DSP4_READ_WORD(); + DSP4_vars.view_dx = DSP4_READ_WORD() * DSP4_vars.distance >> 15; + } + while (1); + + DSP4.waiting4command = TRUE; +} + +////////////////////////////////////////////////////////////// + +void DSP4_OP11(int16 A, int16 B, int16 C, int16 D, int16 *M) +{ + // 0x155 = 341 = Horizontal Width of the Screen + *M = ((A * 0x0155 >> 2) & 0xf000) | + ((B * 0x0155 >> 6) & 0x0f00) | + ((C * 0x0155 >> 10) & 0x00f0) | + ((D * 0x0155 >> 14) & 0x000f); +} + + + + + +///////////////////////////////////////////////////////////// +//Processing Code +///////////////////////////////////////////////////////////// +uint8 dsp4_byte; +uint16 dsp4_address; + +void InitDSP4() +{ + memset(&DSP4, 0, sizeof(DSP4)); + DSP4.waiting4command = TRUE; +} + +void DSP4SetByte() +{ + // clear pending read + if (DSP4.out_index < DSP4.out_count) + { + DSP4.out_index++; + return; + } + + if (DSP4.waiting4command) + { + if (DSP4.half_command) + { + DSP4.command |= (dsp4_byte << 8); + DSP4.in_index = 0; + DSP4.waiting4command = FALSE; + DSP4.half_command = FALSE; + DSP4.out_count = 0; + DSP4.out_index = 0; + + DSP4_vars.DSP4_Logic = 0; + + + switch (DSP4.command) + { + case 0x0000: + DSP4.in_count = 4; break; + case 0x0001: + DSP4.in_count = 44; break; + case 0x0003: + DSP4.in_count = 0; break; + case 0x0005: + DSP4.in_count = 0; break; + case 0x0006: + DSP4.in_count = 0; break; + case 0x0007: + DSP4.in_count = 34; break; + case 0x0008: + DSP4.in_count = 90; break; + case 0x0009: + DSP4.in_count = 14; break; + case 0x000a: + DSP4.in_count = 6; break; + case 0x000b: + DSP4.in_count = 6; break; + case 0x000d: + DSP4.in_count = 42; break; + case 0x000e: + DSP4.in_count = 0; break; + case 0x000f: + DSP4.in_count = 46; break; + case 0x0010: + DSP4.in_count = 36; break; + case 0x0011: + DSP4.in_count = 8; break; + default: + DSP4.waiting4command = TRUE; + break; + } + } + else + { + DSP4.command = dsp4_byte; + DSP4.half_command = TRUE; + } + } + else + { + DSP4.parameters[DSP4.in_index] = dsp4_byte; + DSP4.in_index++; + } + + if (!DSP4.waiting4command && DSP4.in_count == DSP4.in_index) + { + // Actually execute the command + DSP4.waiting4command = TRUE; + DSP4.out_index = 0; + DSP4.in_index = 0; + + switch (DSP4.command) + { + // 16-bit multiplication + case 0x0000: + { + int16 multiplier, multiplicand; + int32 product; + + multiplier = DSP4_READ_WORD(); + multiplicand = DSP4_READ_WORD(); + + DSP4_Multiply(multiplicand, multiplier, &product); + + DSP4_CLEAR_OUT(); + DSP4_WRITE_WORD((uint16)(product)); + DSP4_WRITE_WORD((uint16)(product >> 16)); + } + break; + + // single-player track projection + case 0x0001: + DSP4_OP01(); break; + + // single-player selection + case 0x0003: + DSP4_OP03(); break; + + // clear OAM + case 0x0005: + DSP4_OP05(); break; + + // transfer OAM + case 0x0006: + DSP4_OP06(); break; + + // single-player track turnoff projection + case 0x0007: + DSP4_OP07(); break; + + // solid polygon projection + case 0x0008: + DSP4_OP08(); break; + + // sprite projection + case 0x0009: + DSP4_OP09(); break; + + // unknown + case 0x000A: + { + int16 in1a = DSP4_READ_WORD(); + int16 in2a = DSP4_READ_WORD(); + int16 in3a = DSP4_READ_WORD(); + int16 out1a, out2a, out3a, out4a; + + DSP4_OP0A(in2a, &out2a, &out1a, &out4a, &out3a); + + DSP4_CLEAR_OUT(); + DSP4_WRITE_WORD(out1a); + DSP4_WRITE_WORD(out2a); + DSP4_WRITE_WORD(out3a); + DSP4_WRITE_WORD(out4a); + } + break; + + // set OAM + case 0x000B: + { + int16 sp_x = DSP4_READ_WORD(); + int16 sp_y = DSP4_READ_WORD(); + int16 sp_attr = DSP4_READ_WORD(); + bool8 draw = 1; + + DSP4_CLEAR_OUT(); + + DSP4_OP0B(&draw, sp_x, sp_y, sp_attr, 0, 1); + } + break; + + // multi-player track projection + case 0x000D: + DSP4_OP0D(); break; + + // multi-player selection + case 0x000E: + DSP4_OP0E(); break; + + // single-player track projection with lighting + case 0x000F: + DSP4_OP0F(); break; + + // single-player track turnoff projection with lighting + case 0x0010: + DSP4_OP10(); break; + + // unknown: horizontal mapping command + case 0x0011: + { + int16 a, b, c, d, m; + + + d = DSP4_READ_WORD(); + c = DSP4_READ_WORD(); + b = DSP4_READ_WORD(); + a = DSP4_READ_WORD(); + + DSP4_OP11(a, b, c, d, &m); + + DSP4_CLEAR_OUT(); + DSP4_WRITE_WORD(m); + + break; + } + + default: + break; + } + } +} + +void DSP4GetByte() +{ + if (DSP4.out_count) + { + dsp4_byte = (uint8) DSP4.output[DSP4.out_index&0x1FF]; + DSP4.out_index++; + if (DSP4.out_count == DSP4.out_index) + DSP4.out_count = 0; + } + else + { + dsp4_byte = 0xff; + } +} + +#endif diff --git a/asnes/chip/dsp4/dsp4emu.h b/asnes/chip/dsp4/dsp4emu.h new file mode 100755 index 00000000..834b33de --- /dev/null +++ b/asnes/chip/dsp4/dsp4emu.h @@ -0,0 +1,108 @@ +//DSP-4 emulator code +//Copyright (c) 2004-2006 Dreamer Nom, John Weidman, Kris Bleakley, Nach, z80 gaiden + +#ifndef DSP4EMU_H +#define DSP4EMU_H + +#undef TRUE +#undef FALSE +#define TRUE true +#define FALSE false + +struct DSP4_t +{ + bool8 waiting4command; + bool8 half_command; + uint16 command; + uint32 in_count; + uint32 in_index; + uint32 out_count; + uint32 out_index; + uint8 parameters[512]; + uint8 output[512]; +}; + +extern struct DSP4_t DSP4; + +struct DSP4_vars_t +{ + // op control + int8 DSP4_Logic; // controls op flow + + + // projection format + int16 lcv; // loop-control variable + int16 distance; // z-position into virtual world + int16 raster; // current raster line + int16 segments; // number of raster lines drawn + + // 1.15.16 or 1.15.0 [sign, integer, fraction] + int32 world_x; // line of x-projection in world + int32 world_y; // line of y-projection in world + int32 world_dx; // projection line x-delta + int32 world_dy; // projection line y-delta + int16 world_ddx; // x-delta increment + int16 world_ddy; // y-delta increment + int32 world_xenv; // world x-shaping factor + int16 world_yofs; // world y-vertical scroll + + int16 view_x1; // current viewer-x + int16 view_y1; // current viewer-y + int16 view_x2; // future viewer-x + int16 view_y2; // future viewer-y + int16 view_dx; // view x-delta factor + int16 view_dy; // view y-delta factor + int16 view_xofs1; // current viewer x-vertical scroll + int16 view_yofs1; // current viewer y-vertical scroll + int16 view_xofs2; // future viewer x-vertical scroll + int16 view_yofs2; // future viewer y-vertical scroll + int16 view_yofsenv; // y-scroll shaping factor + int16 view_turnoff_x; // road turnoff data + int16 view_turnoff_dx; // road turnoff delta factor + + + // drawing area + + int16 viewport_cx; // x-center of viewport window + int16 viewport_cy; // y-center of render window + int16 viewport_left; // x-left of viewport + int16 viewport_right; // x-right of viewport + int16 viewport_top; // y-top of viewport + int16 viewport_bottom; // y-bottom of viewport + + + // sprite structure + + int16 sprite_x; // projected x-pos of sprite + int16 sprite_y; // projected y-pos of sprite + int16 sprite_attr; // obj attributes + bool8 sprite_size; // sprite size: 8x8 or 16x16 + int16 sprite_clipy; // visible line to clip pixels off + int16 sprite_count; + + // generic projection variables designed for + // two solid polygons + two polygon sides + + int16 poly_clipLf[2][2]; // left clip boundary + int16 poly_clipRt[2][2]; // right clip boundary + int16 poly_ptr[2][2]; // HDMA structure pointers + int16 poly_raster[2][2]; // current raster line below horizon + int16 poly_top[2][2]; // top clip boundary + int16 poly_bottom[2][2]; // bottom clip boundary + int16 poly_cx[2][2]; // center for left/right points + int16 poly_start[2]; // current projection points + int16 poly_plane[2]; // previous z-plane distance + + + // OAM + int16 OAM_attr[16]; // OAM (size,MSB) data + int16 OAM_index; // index into OAM table + int16 OAM_bits; // offset into OAM table + + int16 OAM_RowMax; // maximum number of tiles per 8 aligned pixels (row) + int16 OAM_Row[32]; // current number of tiles per row +}; + +extern struct DSP4_vars_t DSP4_vars; + +#endif diff --git a/asnes/chip/msu1/msu1.cpp b/asnes/chip/msu1/msu1.cpp new file mode 100755 index 00000000..76ac88d6 --- /dev/null +++ b/asnes/chip/msu1/msu1.cpp @@ -0,0 +1,148 @@ +#include + +#define MSU1_CPP +namespace SNES { + +MSU1 msu1; + +#include "serialization.cpp" + +void MSU1::Enter() { msu1.enter(); } + +void MSU1::enter() { + while(true) { + if(scheduler.sync == 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 = 58); + } else { + mmio.audio_offset += 4; + left = audiofile.readl(2); + right = audiofile.readl(2); + } + } else { + mmio.audio_play = false; + } + } + + left = sclamp<16>((double)left * (double)mmio.audio_volume / 255.0); + right = sclamp<16>((double)right * (double)mmio.audio_volume / 255.0); + + audio.coprocessor_sample(left, right); + step(1); + synchronize_cpu(); + } +} + +void MSU1::init() { +} + +void MSU1::enable() { + audio.coprocessor_enable(true); + audio.coprocessor_frequency(44100.0); + + if(datafile.open()) datafile.close(); + datafile.open(string() << cartridge.basename() << ".msu", file::mode_read); +} + +void MSU1::power() { + reset(); +} + +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) { + addr &= 0xffff; + + if(addr == 0x2000) { + return (mmio.data_busy << 7) + | (mmio.audio_busy << 6) + | (mmio.audio_repeat << 5) + | (mmio.audio_play << 4) + | (Revision << 0); + } + + if(addr == 0x2001) { + if(mmio.data_busy) return 0x00; + mmio.data_offset++; + if(datafile.open()) return datafile.read(); + return 0x00; + } + + if(addr == 0x2002) return 'S'; + if(addr == 0x2003) return '-'; + if(addr == 0x2004) return 'M'; + if(addr == 0x2005) return 'S'; + if(addr == 0x2006) return 'U'; + if(addr == 0x2007) return '0' + Revision; + + return 0x00; +} + +void MSU1::mmio_write(unsigned addr, uint8 data) { + addr &= 0xffff; + + if(addr == 0x2000) { + mmio.data_offset = (mmio.data_offset & 0xffffff00) | (data << 0); + } + + if(addr == 0x2001) { + mmio.data_offset = (mmio.data_offset & 0xffff00ff) | (data << 8); + } + + if(addr == 0x2002) { + mmio.data_offset = (mmio.data_offset & 0xff00ffff) | (data << 16); + } + + if(addr == 0x2003) { + mmio.data_offset = (mmio.data_offset & 0x00ffffff) | (data << 24); + if(datafile.open()) datafile.seek(mmio.data_offset); + mmio.data_busy = false; + } + + if(addr == 0x2004) { + mmio.audio_track = (mmio.audio_track & 0xff00) | (data << 0); + } + + if(addr == 0x2005) { + mmio.audio_track = (mmio.audio_track & 0x00ff) | (data << 8); + if(audiofile.open()) audiofile.close(); + char track[16]; + sprintf(track, "-%u", mmio.audio_track); + if(audiofile.open(string() << cartridge.basename() << track << ".wav", file::mode_read)) { + audiofile.seek(mmio.audio_offset = 58); //skip WAV header + } + mmio.audio_busy = false; + mmio.audio_repeat = false; + mmio.audio_play = false; + } + + if(addr == 0x2006) { + mmio.audio_volume = data; + } + + if(addr == 0x2007) { + mmio.audio_repeat = data & 2; + mmio.audio_play = data & 1; + } +} + +} diff --git a/asnes/chip/msu1/msu1.hpp b/asnes/chip/msu1/msu1.hpp new file mode 100755 index 00000000..473b75da --- /dev/null +++ b/asnes/chip/msu1/msu1.hpp @@ -0,0 +1,39 @@ +class MSU1 : public Coprocessor, public MMIO { +public: + static void Enter(); + void enter(); + void init(); + void enable(); + 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; + 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/asnes/chip/msu1/serialization.cpp b/asnes/chip/msu1/serialization.cpp new file mode 100755 index 00000000..cf0d639b --- /dev/null +++ b/asnes/chip/msu1/serialization.cpp @@ -0,0 +1,28 @@ +#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_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(string() << cartridge.basename() << ".msu", file::mode_read)) { + datafile.seek(mmio.data_offset); + } + + if(audiofile.open()) audiofile.close(); + char track[16]; + sprintf(track, "-%u", mmio.audio_track); + if(audiofile.open(string() << cartridge.basename() << track << ".wav", file::mode_read)) { + audiofile.seek(mmio.audio_offset); + } +} + +#endif diff --git a/asnes/chip/obc1/obc1.cpp b/asnes/chip/obc1/obc1.cpp new file mode 100755 index 00000000..4b79a4c2 --- /dev/null +++ b/asnes/chip/obc1/obc1.cpp @@ -0,0 +1,82 @@ +#include + +#define OBC1_CPP +namespace SNES { + +OBC1 obc1; + +#include "serialization.cpp" + +void OBC1::init() { +} + +void OBC1::enable() { +} + +void OBC1::power() { + reset(); +} + +void OBC1::reset() { + for(unsigned i = 0x0000; i <= 0x1fff; i++) ram_write(i, 0xff); + + 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; + if((addr & 0x1ff8) != 0x1ff0) return ram_read(addr); + + switch(addr) { default: //never used, avoids compiler warning + 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); + case 0x1ff5: case 0x1ff6: case 0x1ff7: return ram_read(addr); + } +} + +void OBC1::write(unsigned addr, uint8 data) { + addr &= 0x1fff; + if((addr & 0x1ff8) != 0x1ff0) return ram_write(addr, data); + + switch(addr) { + case 0x1ff0: ram_write(status.baseptr + (status.address << 2) + 0, data); break; + case 0x1ff1: ram_write(status.baseptr + (status.address << 2) + 1, data); break; + case 0x1ff2: ram_write(status.baseptr + (status.address << 2) + 2, data); break; + case 0x1ff3: ram_write(status.baseptr + (status.address << 2) + 3, data); break; + 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); + } break; + case 0x1ff5: { + status.baseptr = (data & 1) ? 0x1800 : 0x1c00; + ram_write(addr, data); + } break; + case 0x1ff6: { + status.address = (data & 0x7f); + status.shift = (data & 3) << 1; + ram_write(addr, data); + } break; + case 0x1ff7: { + ram_write(addr, data); + } break; + } +} + +uint8 OBC1::ram_read(unsigned addr) { + return memory::cartram.read(addr & 0x1fff); +} + +void OBC1::ram_write(unsigned addr, uint8 data) { + memory::cartram.write(addr & 0x1fff, data); +} + +OBC1::OBC1() {} +OBC1::~OBC1() {} + +} diff --git a/asnes/chip/obc1/obc1.hpp b/asnes/chip/obc1/obc1.hpp new file mode 100755 index 00000000..4b5d4408 --- /dev/null +++ b/asnes/chip/obc1/obc1.hpp @@ -0,0 +1,26 @@ +class OBC1 : public Memory { +public: + void init(); + void enable(); + void power(); + void reset(); + + uint8 read(unsigned addr); + void write(unsigned addr, uint8 data); + + void serialize(serializer&); + OBC1(); + ~OBC1(); + +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/asnes/chip/obc1/serialization.cpp b/asnes/chip/obc1/serialization.cpp new file mode 100755 index 00000000..3d61aafc --- /dev/null +++ b/asnes/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/asnes/chip/sa1/bus/bus.cpp b/asnes/chip/sa1/bus/bus.cpp new file mode 100755 index 00000000..4eb60e9b --- /dev/null +++ b/asnes/chip/sa1/bus/bus.cpp @@ -0,0 +1,218 @@ +#ifdef SA1_CPP + +VBRBus vbrbus; +SA1Bus sa1bus; + +namespace memory { + StaticRAM iram(2048); + //accessed by: + VSPROM vsprom; //S-CPU + SA-1 + CPUIRAM cpuiram; //S-CPU + SA1IRAM sa1iram; //SA-1 + SA1BWRAM sa1bwram; //SA-1 + CC1BWRAM cc1bwram; //S-CPU + BitmapRAM bitmapram; //SA-1 +} + +//$230c (VDPL), $230d (VDPH) use this bus to read variable-length data. +//this is used both to avoid 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. +//(* eg, memory::cartram is used directly, as memory::sa1bwram syncs to the S-CPU) +void VBRBus::init() { + map(MapMode::Direct, 0x00, 0xff, 0x0000, 0xffff, memory::memory_unmapped); + + map(MapMode::Linear, 0x00, 0x3f, 0x0000, 0x07ff, memory::iram); + map(MapMode::Linear, 0x00, 0x3f, 0x3000, 0x37ff, memory::iram); + map(MapMode::Linear, 0x00, 0x3f, 0x6000, 0x7fff, memory::cartram); + map(MapMode::Linear, 0x00, 0x3f, 0x8000, 0xffff, memory::vsprom); + map(MapMode::Linear, 0x40, 0x4f, 0x0000, 0xffff, memory::cartram); + map(MapMode::Linear, 0x80, 0xbf, 0x0000, 0x07ff, memory::iram); + map(MapMode::Linear, 0x80, 0xbf, 0x3000, 0x37ff, memory::iram); + map(MapMode::Linear, 0x80, 0xbf, 0x6000, 0x7fff, memory::cartram); + map(MapMode::Linear, 0x80, 0xbf, 0x8000, 0xffff, memory::vsprom); + map(MapMode::Linear, 0xc0, 0xff, 0x0000, 0xffff, memory::vsprom); +} + +void SA1Bus::init() { + map(MapMode::Direct, 0x00, 0xff, 0x0000, 0xffff, memory::memory_unmapped); + + map(MapMode::Linear, 0x00, 0x3f, 0x0000, 0x07ff, memory::sa1iram); + map(MapMode::Direct, 0x00, 0x3f, 0x2200, 0x23ff, memory::mmio); + map(MapMode::Linear, 0x00, 0x3f, 0x3000, 0x37ff, memory::sa1iram); + map(MapMode::Linear, 0x00, 0x3f, 0x6000, 0x7fff, memory::sa1bwram); + map(MapMode::Linear, 0x00, 0x3f, 0x8000, 0xffff, memory::vsprom); + map(MapMode::Linear, 0x40, 0x4f, 0x0000, 0xffff, memory::sa1bwram); + map(MapMode::Linear, 0x60, 0x6f, 0x0000, 0xffff, memory::bitmapram); + map(MapMode::Linear, 0x80, 0xbf, 0x0000, 0x07ff, memory::sa1iram); + map(MapMode::Direct, 0x80, 0xbf, 0x2200, 0x23ff, memory::mmio); + map(MapMode::Linear, 0x80, 0xbf, 0x3000, 0x37ff, memory::sa1iram); + map(MapMode::Linear, 0x80, 0xbf, 0x6000, 0x7fff, memory::sa1bwram); + map(MapMode::Linear, 0x80, 0xbf, 0x8000, 0xffff, memory::vsprom); + map(MapMode::Linear, 0xc0, 0xff, 0x0000, 0xffff, memory::vsprom); +} + +//====== +//VSPROM +//====== + +//this class maps $00:[ff00-ffff] for the purpose of supporting: +//$2209.d6 IVSW (S-CPU IRQ vector selection) (0 = cart, 1 = SA-1) +//$2209.d4 NVSW (S-CPU NMI vector selection) (0 = cart, 1 = SA-1) +//when set, vector addresses are over-ridden with SA-1 register settings: +//SIV = S-CPU IRQ vector address override +//SNV = S-CPU NMI vector address override +// +//$00:[ffea-ffeb|ffee-ffef] are special cased on read; +//all other addresses return original mapped data. + +unsigned VSPROM::size() const { + return memory::cartrom.size(); +} + +uint8 VSPROM::read(unsigned addr) { + //use $7fex instead of $ffex due to linear mapping of 32k granularity ROM data + if((addr & 0xffffe0) == 0x007fe0) { + if(addr == 0x7fea && sa1.mmio.cpu_nvsw) return sa1.mmio.snv >> 0; + if(addr == 0x7feb && sa1.mmio.cpu_nvsw) return sa1.mmio.snv >> 8; + if(addr == 0x7fee && sa1.mmio.cpu_ivsw) return sa1.mmio.siv >> 0; + if(addr == 0x7fef && sa1.mmio.cpu_ivsw) return sa1.mmio.siv >> 8; + } + return memory::cartrom.read(addr); +} + +void VSPROM::write(unsigned addr, uint8 data) { +} + +//======= +//SA1IRAM +//======= + +unsigned SA1IRAM::size() const { + return memory::iram.size(); +} + +uint8 SA1IRAM::read(unsigned addr) { + sa1.synchronize_cpu(); + return memory::iram.read(addr); +} + +void SA1IRAM::write(unsigned addr, uint8 data) { + sa1.synchronize_cpu(); + memory::iram.write(addr, data); +} + +//======= +//CPUIRAM +//======= + +unsigned CPUIRAM::size() const { + return memory::iram.size(); +} + +uint8 CPUIRAM::read(unsigned addr) { + cpu.synchronize_coprocessor(); + return memory::iram.read(addr); +} + +void CPUIRAM::write(unsigned addr, uint8 data) { + cpu.synchronize_coprocessor(); + memory::iram.write(addr, data); +} + +//======== +//SA1BWRAM +//======== + +unsigned SA1BWRAM::size() const { + return memory::cartram.size(); +} + +uint8 SA1BWRAM::read(unsigned addr) { + sa1.synchronize_cpu(); + return memory::cartram.read(addr); +} + +void SA1BWRAM::write(unsigned addr, uint8 data) { + sa1.synchronize_cpu(); + memory::cartram.write(addr, data); +} + +//======== +//CC1BWRAM +//======== + +unsigned CC1BWRAM::size() const { + return memory::cartram.size(); +} + +uint8 CC1BWRAM::read(unsigned addr) { + cpu.synchronize_coprocessor(); + if(dma) return sa1.dma_cc1_read(addr); + return memory::cartram.read(addr); +} + +void CC1BWRAM::write(unsigned addr, uint8 data) { + cpu.synchronize_coprocessor(); + memory::cartram.write(addr, data); +} + +//========= +//BitmapRAM +//========= + +unsigned BitmapRAM::size() const { + return 0x100000; +} + +uint8 BitmapRAM::read(unsigned addr) { + sa1.synchronize_cpu(); + + if(sa1.mmio.bbf == 0) { + //4bpp + unsigned shift = addr & 1; + addr = (addr >> 1) & (memory::cartram.size() - 1); + switch(shift) { default: + case 0: return (memory::cartram.read(addr) >> 0) & 15; + case 1: return (memory::cartram.read(addr) >> 4) & 15; + } + } else { + //2bpp + unsigned shift = addr & 3; + addr = (addr >> 2) & (memory::cartram.size() - 1); + switch(shift) { default: + case 0: return (memory::cartram.read(addr) >> 0) & 3; + case 1: return (memory::cartram.read(addr) >> 2) & 3; + case 2: return (memory::cartram.read(addr) >> 4) & 3; + case 3: return (memory::cartram.read(addr) >> 6) & 3; + } + } +} + +void BitmapRAM::write(unsigned addr, uint8 data) { + sa1.synchronize_cpu(); + + if(sa1.mmio.bbf == 0) { + //4bpp + unsigned shift = addr & 1; + addr = (addr >> 1) & (memory::cartram.size() - 1); + switch(shift) { default: + case 0: data = (memory::cartram.read(addr) & 0xf0) | ((data & 15) << 0); break; + case 1: data = (memory::cartram.read(addr) & 0x0f) | ((data & 15) << 4); break; + } + } else { + //2bpp + unsigned shift = addr & 3; + addr = (addr >> 2) & (memory::cartram.size() - 1); + switch(shift) { default: + case 0: data = (memory::cartram.read(addr) & 0xfc) | ((data & 3) << 0); break; + case 1: data = (memory::cartram.read(addr) & 0xf3) | ((data & 3) << 2); break; + case 2: data = (memory::cartram.read(addr) & 0xcf) | ((data & 3) << 4); break; + case 3: data = (memory::cartram.read(addr) & 0x3f) | ((data & 3) << 6); break; + } + } + + memory::cartram.write(addr, data); +} + +#endif diff --git a/asnes/chip/sa1/bus/bus.hpp b/asnes/chip/sa1/bus/bus.hpp new file mode 100755 index 00000000..2aeb11de --- /dev/null +++ b/asnes/chip/sa1/bus/bus.hpp @@ -0,0 +1,55 @@ +struct VBRBus : Bus { + void init(); +}; + +struct SA1Bus : Bus { + void init(); +}; + +struct VSPROM : Memory { + unsigned size() const; + alwaysinline uint8 read(unsigned); + alwaysinline void write(unsigned, uint8); +}; + +struct CPUIRAM : Memory { + unsigned size() const; + alwaysinline uint8 read(unsigned); + alwaysinline void write(unsigned, uint8); +}; + +struct SA1IRAM : Memory { + unsigned size() const; + alwaysinline uint8 read(unsigned); + alwaysinline void write(unsigned, uint8); +}; + +struct SA1BWRAM : Memory { + unsigned size() const; + alwaysinline uint8 read(unsigned); + alwaysinline void write(unsigned, uint8); +}; + +struct CC1BWRAM : Memory { + unsigned size() const; + alwaysinline uint8 read(unsigned); + alwaysinline void write(unsigned, uint8); + bool dma; +}; + +struct BitmapRAM : Memory { + unsigned size() const; + alwaysinline uint8 read(unsigned); + alwaysinline void write(unsigned, uint8); +}; + +namespace memory { + extern StaticRAM iram; + + extern VSPROM vsprom; + extern CPUIRAM cpuiram; + extern SA1IRAM sa1iram; + extern SA1BWRAM sa1bwram; + extern CC1BWRAM cc1bwram; + extern BitmapRAM bitmapram; +}; diff --git a/asnes/chip/sa1/dma/dma.cpp b/asnes/chip/sa1/dma/dma.cpp new file mode 100755 index 00000000..aecb7358 --- /dev/null +++ b/asnes/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 = sa1bus.read(dsa); + } + } break; + + case DMA::SourceBWRAM: { + if((dsa & 0x40e000) == 0x006000 || (dsa & 0xf00000) == 0x400000) { + data = sa1bus.read(dsa); + } + } break; + + case DMA::SourceIRAM: { + data = memory::iram.read(dsa & 0x07ff); + } break; + } + + switch(mmio.dd) { + case DMA::DestBWRAM: { + if((dda & 0x40e000) == 0x006000 || (dda & 0xf00000) == 0x400000) { + sa1bus.write(dda, data); + } + } break; + + case DMA::DestIRAM: { + memory::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() { + memory::cc1bwram.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 = memory::cartram.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)memory::cartram.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); + memory::iram.write(p & 0x07ff, out[byte]); + } + } + } + + return memory::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); + } + memory::iram.write(addr + ((byte & 6) << 3) + (byte & 1), output); + } + + dma.line = (dma.line + 1) & 15; +} + +#endif diff --git a/asnes/chip/sa1/dma/dma.hpp b/asnes/chip/sa1/dma/dma.hpp new file mode 100755 index 00000000..4d3cff7f --- /dev/null +++ b/asnes/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/asnes/chip/sa1/memory/memory.cpp b/asnes/chip/sa1/memory/memory.cpp new file mode 100755 index 00000000..b2779a35 --- /dev/null +++ b/asnes/chip/sa1/memory/memory.cpp @@ -0,0 +1,24 @@ +#ifdef SA1_CPP + +//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 sa1bus.read(addr); +} + +void SA1::op_write(unsigned addr, uint8 data) { + tick(); + if(((addr & 0x40e000) == 0x006000) || ((addr & 0xd00000) == 0x400000)) tick(); + sa1bus.write(addr, data); +} + +#endif diff --git a/asnes/chip/sa1/memory/memory.hpp b/asnes/chip/sa1/memory/memory.hpp new file mode 100755 index 00000000..d466c416 --- /dev/null +++ b/asnes/chip/sa1/memory/memory.hpp @@ -0,0 +1,5 @@ +alwaysinline void op_io(); +alwaysinline uint8 op_read(unsigned addr); +alwaysinline void op_write(unsigned addr, uint8 data); + +uint8_t vbr_read(unsigned addr); diff --git a/asnes/chip/sa1/mmio/mmio.cpp b/asnes/chip/sa1/mmio/mmio.cpp new file mode 100755 index 00000000..c36d019b --- /dev/null +++ b/asnes/chip/sa1/mmio/mmio.cpp @@ -0,0 +1,631 @@ +#ifdef SA1_CPP + +//BS-X flash carts, when present, are mapped to 0x400000+ +Memory& SA1::mmio_access(unsigned &addr) { + if(!memory::bsxflash.data()) return memory::vsprom; + if(addr < 0x400000) return memory::vsprom; + addr &= 0x3fffff; + return bsxflash; +} + +//(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); + + unsigned addr = mmio.cb << 20; + Memory &access = mmio_access(addr); + + if(mmio.cbmode == 0) { + bus.map(Bus::MapMode::Linear, 0x00, 0x1f, 0x8000, 0xffff, memory::vsprom, 0x000000); + sa1bus.map(Bus::MapMode::Linear, 0x00, 0x1f, 0x8000, 0xffff, memory::vsprom, 0x000000); + } else { + bus.map(Bus::MapMode::Linear, 0x00, 0x1f, 0x8000, 0xffff, access, addr); + sa1bus.map(Bus::MapMode::Linear, 0x00, 0x1f, 0x8000, 0xffff, access, addr); + } + + bus.map(Bus::MapMode::Linear, 0xc0, 0xcf, 0x0000, 0xffff, access, addr); + sa1bus.map(Bus::MapMode::Linear, 0xc0, 0xcf, 0x0000, 0xffff, access, addr); +} + +//(DXB) Super MMC bank D +void SA1::mmio_w2221(uint8 data) { + mmio.dbmode = (data & 0x80); + mmio.db = (data & 0x07); + + unsigned addr = mmio.db << 20; + Memory &access = mmio_access(addr); + + if(mmio.dbmode == 0) { + bus.map(Bus::MapMode::Linear, 0x20, 0x3f, 0x8000, 0xffff, memory::vsprom, 0x100000); + sa1bus.map(Bus::MapMode::Linear, 0x20, 0x3f, 0x8000, 0xffff, memory::vsprom, 0x100000); + } else { + bus.map(Bus::MapMode::Linear, 0x20, 0x3f, 0x8000, 0xffff, access, addr); + sa1bus.map(Bus::MapMode::Linear, 0x20, 0x3f, 0x8000, 0xffff, access, addr); + } + + bus.map(Bus::MapMode::Linear, 0xd0, 0xdf, 0x0000, 0xffff, access, addr); + sa1bus.map(Bus::MapMode::Linear, 0xd0, 0xdf, 0x0000, 0xffff, access, addr); +} + +//(EXB) Super MMC bank E +void SA1::mmio_w2222(uint8 data) { + mmio.ebmode = (data & 0x80); + mmio.eb = (data & 0x07); + + unsigned addr = mmio.eb << 20; + Memory &access = mmio_access(addr); + + if(mmio.ebmode == 0) { + bus.map(Bus::MapMode::Linear, 0x80, 0x9f, 0x8000, 0xffff, memory::vsprom, 0x200000); + sa1bus.map(Bus::MapMode::Linear, 0x80, 0x9f, 0x8000, 0xffff, memory::vsprom, 0x200000); + } else { + bus.map(Bus::MapMode::Linear, 0x80, 0x9f, 0x8000, 0xffff, access, addr); + sa1bus.map(Bus::MapMode::Linear, 0x80, 0x9f, 0x8000, 0xffff, access, addr); + } + + bus.map(Bus::MapMode::Linear, 0xe0, 0xef, 0x0000, 0xffff, access, addr); + sa1bus.map(Bus::MapMode::Linear, 0xe0, 0xef, 0x0000, 0xffff, access, addr); +} + +//(FXB) Super MMC bank F +void SA1::mmio_w2223(uint8 data) { + mmio.fbmode = (data & 0x80); + mmio.fb = (data & 0x07); + + unsigned addr = mmio.fb << 20; + Memory &access = mmio_access(addr); + + if(mmio.fbmode == 0) { + bus.map(Bus::MapMode::Linear, 0xa0, 0xbf, 0x8000, 0xffff, memory::vsprom, 0x300000); + sa1bus.map(Bus::MapMode::Linear, 0xa0, 0xbf, 0x8000, 0xffff, memory::vsprom, 0x300000); + } else { + bus.map(Bus::MapMode::Linear, 0xa0, 0xbf, 0x8000, 0xffff, access, addr); + sa1bus.map(Bus::MapMode::Linear, 0xa0, 0xbf, 0x8000, 0xffff, access, addr); + } + + bus.map(Bus::MapMode::Linear, 0xf0, 0xff, 0x0000, 0xffff, access, addr); + sa1bus.map(Bus::MapMode::Linear, 0xf0, 0xff, 0x0000, 0xffff, access, addr); +} + +//(BMAPS) S-CPU BW-RAM address mapping +void SA1::mmio_w2224(uint8 data) { + mmio.sbm = (data & 0x1f); + + bus.map(Bus::MapMode::Linear, 0x00, 0x3f, 0x6000, 0x7fff, memory::cc1bwram, mmio.sbm * 0x2000, 0x2000); + bus.map(Bus::MapMode::Linear, 0x80, 0xbf, 0x6000, 0x7fff, memory::cc1bwram, mmio.sbm * 0x2000, 0x2000); +} + +//(BMAP) SA-1 BW-RAM address mapping +void SA1::mmio_w2225(uint8 data) { + mmio.sw46 = (data & 0x80); + mmio.cbm = (data & 0x7f); + + if(mmio.sw46 == 0) { + //$[40-43]:[0000-ffff] x 32 projection + sa1bus.map(Bus::MapMode::Linear, 0x00, 0x3f, 0x6000, 0x7fff, memory::sa1bwram, (mmio.cbm & 0x1f) * 0x2000, 0x2000); + sa1bus.map(Bus::MapMode::Linear, 0x80, 0xbf, 0x6000, 0x7fff, memory::sa1bwram, (mmio.cbm & 0x1f) * 0x2000, 0x2000); + } else { + //$[60-6f]:[0000-ffff] x 128 projection + sa1bus.map(Bus::MapMode::Linear, 0x00, 0x3f, 0x6000, 0x7fff, memory::bitmapram, mmio.cbm * 0x2000, 0x2000); + sa1bus.map(Bus::MapMode::Linear, 0x80, 0xbf, 0x6000, 0x7fff, memory::bitmapram, mmio.cbm * 0x2000, 0x2000); + } +} + +//(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) memory::cc1bwram.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 = (vbrbus.read(mmio.va + 0) << 0) + | (vbrbus.read(mmio.va + 1) << 8) + | (vbrbus.read(mmio.va + 2) << 16); + data >>= mmio.vbit; + return data >> 0; +} + +//(VDPH) variable-length data read port high +uint8 SA1::mmio_r230d() { + uint32 data = (vbrbus.read(mmio.va + 0) << 0) + | (vbrbus.read(mmio.va + 1) << 8) + | (vbrbus.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_coprocessor() : 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_coprocessor() : 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/asnes/chip/sa1/mmio/mmio.hpp b/asnes/chip/sa1/mmio/mmio.hpp new file mode 100755 index 00000000..e63ad1fa --- /dev/null +++ b/asnes/chip/sa1/mmio/mmio.hpp @@ -0,0 +1,256 @@ +uint8 mmio_read(unsigned addr); +void mmio_write(unsigned addr, uint8 data); +Memory& mmio_access(unsigned &addr); + +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; + uint8 cb; + + //$2221 DXB + bool dbmode; + uint8 db; + + //$2222 EXB + bool ebmode; + uint8 eb; + + //$2223 FXB + bool fbmode; + uint8 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/asnes/chip/sa1/sa1.cpp b/asnes/chip/sa1/sa1.cpp new file mode 100755 index 00000000..5c24a1f7 --- /dev/null +++ b/asnes/chip/sa1/sa1.cpp @@ -0,0 +1,329 @@ +#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 == 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; + interrupt(status.interrupt_vector); + } + + (this->*opcode_table[op_readpc()])(); + } +} + +void SA1::last_cycle() { + if(mmio.sa1_nmi && !mmio.sa1_nmicl) { + status.interrupt_pending = true; + status.interrupt_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; + status.interrupt_vector = mmio.civ; + mmio.timer_irqfl = true; + regs.wai = false; + } else if(mmio.dma_irqen && !mmio.dma_irqcl) { + status.interrupt_pending = true; + status.interrupt_vector = mmio.civ; + mmio.dma_irqfl = true; + regs.wai = false; + } else if(mmio.sa1_irq && !mmio.sa1_irqcl) { + status.interrupt_pending = true; + status.interrupt_vector = mmio.civ; + mmio.sa1_irqfl = true; + regs.wai = false; + } + } +} + +void SA1::interrupt(uint16 vector) { + SA1::op_read(regs.pc.d); + SA1::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 = vector; + regs.pc.b = 0x00; + regs.p.i = 1; + regs.p.d = 0; +} + +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::enable() { +} + +void SA1::power() { + regs.a = regs.x = regs.y = 0x0000; + regs.s = 0x01ff; + vbrbus.init(); + sa1bus.init(); + reset(); +} + +void SA1::reset() { + create(SA1::Enter, system.cpu_frequency()); + + memory::cc1bwram.dma = false; + for(unsigned addr = 0; addr < memory::iram.size(); addr++) { + memory::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; + CPUcore::update_table(); + + status.tick_counter = 0; + + status.interrupt_pending = false; + status.interrupt_vector = 0x0000; + + status.scanlines = (system.region() == 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 = 0; + mmio.dbmode = 0; + mmio.ebmode = 0; + mmio.fbmode = 0; + + mmio.cb = 0x00; + mmio.db = 0x01; + mmio.eb = 0x02; + mmio.fb = 0x03; + + //$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() { +} + +} diff --git a/asnes/chip/sa1/sa1.hpp b/asnes/chip/sa1/sa1.hpp new file mode 100755 index 00000000..be0597a9 --- /dev/null +++ b/asnes/chip/sa1/sa1.hpp @@ -0,0 +1,39 @@ +#include "bus/bus.hpp" + +class SA1 : public Coprocessor, public CPUcore, public MMIO { +public: + #include "dma/dma.hpp" + #include "memory/memory.hpp" + #include "mmio/mmio.hpp" + + struct Status { + uint8 tick_counter; + + bool interrupt_pending; + uint16 interrupt_vector; + + uint16 scanlines; + uint16 vcounter; + uint16 hcounter; + } status; + + static void Enter(); + void enter(); + void interrupt(uint16 vector); + void tick(); + + alwaysinline void trigger_irq(); + alwaysinline void last_cycle(); + alwaysinline bool interrupt_pending(); + + void init(); + void enable(); + void power(); + void reset(); + + void serialize(serializer&); + SA1(); +}; + +extern SA1 sa1; +extern SA1Bus sa1bus; diff --git a/asnes/chip/sa1/serialization.cpp b/asnes/chip/sa1/serialization.cpp new file mode 100755 index 00000000..032be0bd --- /dev/null +++ b/asnes/chip/sa1/serialization.cpp @@ -0,0 +1,149 @@ +#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.interrupt_vector); + + s.integer(status.scanlines); + s.integer(status.vcounter); + s.integer(status.hcounter); + + //bus/bus.hpp + s.array(memory::iram.data(), memory::iram.size()); + + s.integer(memory::cc1bwram.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/asnes/chip/sdd1/sdd1.cpp b/asnes/chip/sdd1/sdd1.cpp new file mode 100755 index 00000000..4a2eabfe --- /dev/null +++ b/asnes/chip/sdd1/sdd1.cpp @@ -0,0 +1,154 @@ +#include + +#define SDD1_CPP +namespace SNES { + +SDD1 sdd1; + +#include "serialization.cpp" +#include "sdd1emu.cpp" + +void SDD1::init() {} + +void SDD1::enable() { + //hook S-CPU DMA MMIO registers to gather information for struct dma[]; + //buffer address and transfer size information for use in SDD1::read() + for(unsigned i = 0x4300; i <= 0x437f; i++) { + cpu_mmio[i & 0x7f] = memory::mmio.mmio[i - 0x2000]; + memory::mmio.map(i, *this); + } +} + +void SDD1::power() { + reset(); +} + +void SDD1::reset() { + sdd1_enable = 0x00; + xfer_enable = 0x00; + + 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; + } + + buffer.ready = false; +} + +uint8 SDD1::mmio_read(unsigned addr) { + addr &= 0xffff; + + if((addr & 0x4380) == 0x4300) { + return cpu_mmio[addr & 0x7f]->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[addr & 0x7f]->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; + } +} + +//SDD1::read() is mapped to $[c0-ff]:[0000-ffff] +//the design is meant to be as close to the hardware design as possible, thus this code +//avoids adding S-DD1 hooks inside S-CPU::DMA emulation. +// +//the real S-DD1 cannot see $420b (DMA enable) writes, as they are not placed on the bus. +//however, $43x0-$43xf writes (DMAx channel settings) most likely do appear on the bus. +//the S-DD1 also requires fixed addresses for transfers, which wouldn't be necessary if +//it could see $420b writes (eg it would know when the transfer should begin.) +// +//the hardware needs a way to distinguish program code after $4801 writes from DMA +//decompression that follows soon after. +// +//the only plausible design for hardware would be for the S-DD1 to spy on DMAx settings, +//and begin spooling decompression on writes to $4801 that activate a channel. after that, +//it feeds decompressed data only when the ROM read address matches the DMA channel address. +// +//the actual S-DD1 transfer can occur on any channel, but it is most likely limited to +//one transfer per $420b write (for spooling purposes). however, this is not known for certain. +uint8 SDD1::read(unsigned addr) { + if(sdd1_enable & xfer_enable) { + //at least one channel has S-DD1 decompression enabled ... + for(unsigned i = 0; i < 8; i++) { + if(sdd1_enable & xfer_enable & (1 << i)) { + //S-DD1 always uses fixed transfer mode, so address will not change during transfer + if(addr == dma[i].addr) { + if(!buffer.ready) { + //first byte read for channel performs full decompression. + //this really should stream byte-by-byte, but it's not necessary since the size is known + buffer.offset = 0; + buffer.size = dma[i].size ? dma[i].size : 65536; + + //sdd1emu calls this function; it needs to access uncompressed data; + //so temporarily disable decompression mode for decompress() call. + uint8 temp = sdd1_enable; + sdd1_enable = false; + sdd1emu.decompress(addr, buffer.size, buffer.data); + sdd1_enable = temp; + + buffer.ready = true; + } + + //fetch a decompressed byte; once buffer is depleted, disable channel and invalidate buffer + uint8 data = buffer.data[(uint16)buffer.offset++]; + if(buffer.offset >= buffer.size) { + buffer.ready = false; + xfer_enable &= ~(1 << i); + } + + return data; + } //address matched + } //channel enabled + } //channel loop + } //S-DD1 decompressor enabled + + //S-DD1 decompression mode inactive; return ROM data + return memory::cartrom.read(mmc[(addr >> 20) & 3] + (addr & 0x0fffff)); +} + +void SDD1::write(unsigned addr, uint8 data) { +} + +SDD1::SDD1() { +} + +SDD1::~SDD1() { +} + +} diff --git a/asnes/chip/sdd1/sdd1.hpp b/asnes/chip/sdd1/sdd1.hpp new file mode 100755 index 00000000..fd95f6b9 --- /dev/null +++ b/asnes/chip/sdd1/sdd1.hpp @@ -0,0 +1,41 @@ +#include "sdd1emu.hpp" + +class SDD1 : public MMIO, public Memory { +public: + void init(); + void enable(); + void power(); + void reset(); + + uint8 mmio_read(unsigned addr); + void mmio_write(unsigned addr, uint8 data); + + uint8 read(unsigned addr); + void write(unsigned addr, uint8 data); + + void serialize(serializer&); + SDD1(); + ~SDD1(); + +private: + MMIO *cpu_mmio[0x80]; //bus spying hooks to glean information for struct dma[] + + uint8 sdd1_enable; //channel bit-mask + uint8 xfer_enable; //channel bit-mask + unsigned mmc[4]; //memory map controller ROM indices + + struct { + unsigned addr; //$43x2-$43x4 -- DMA transfer address + uint16 size; //$43x5-$43x6 -- DMA transfer size + } dma[8]; + + SDD1emu sdd1emu; + struct { + uint8 data[65536]; //pointer to decompressed S-DD1 data + uint16 offset; //read index into S-DD1 decompression buffer + unsigned size; //length of data buffer; reads decrement counter, set ready to false at 0 + bool ready; //true when data[] is valid; false to invoke sdd1emu.decompress() + } buffer; +}; + +extern SDD1 sdd1; diff --git a/asnes/chip/sdd1/sdd1emu.cpp b/asnes/chip/sdd1/sdd1emu.cpp new file mode 100755 index 00000000..a7eb3646 --- /dev/null +++ b/asnes/chip/sdd1/sdd1emu.cpp @@ -0,0 +1,452 @@ +#ifdef SDD1_CPP + +/************************************************************************ + +S-DD1'algorithm emulation code +------------------------------ + +Author: Andreas Naive +Date: August 2003 +Last update: October 2004 + +This code is Public Domain. There is no copyright holded by the author. +Said this, the author wish to explicitly emphasize his inalienable moral rights +over this piece of intelectual work and the previous research that made it +possible, as recognized by most of the copyright laws around the world. + +This code is provided 'as-is', with no warranty, expressed or implied. +No responsability is assumed by the author in connection with it. + +The author is greatly indebted with The Dumper, without whose help and +patience providing him with real S-DD1 data the research would have never been +possible. He also wish to note that in the very beggining of his research, +Neviksti had done some steps in the right direction. By last, the author is +indirectly indebted to all the people that worked and contributed in the +S-DD1 issue in the past. + +An algorithm's documentation is available as a separate document. +The implementation is obvious when the algorithm is +understood. + +************************************************************************/ + +typedef uint8 bool8; +#define SDD1_read(__addr) (sdd1.read(__addr)) + +//////////////////////////////////////////////////// + + +void SDD1_IM::prepareDecomp(uint32 in_buf) { + + byte_ptr=in_buf; + bit_count=4; + +} + +//////////////////////////////////////////////////// + + +uint8 SDD1_IM::getCodeword(uint8 code_len) { + + uint8 codeword; + uint8 comp_count; + + codeword = (SDD1_read(byte_ptr))<>(9-bit_count); + bit_count+=code_len; + } + + if (bit_count & 0x08) { + byte_ptr++; + bit_count&=0x07; + } + + return codeword; + +} + +////////////////////////////////////////////////////// + + +SDD1_GCD::SDD1_GCD(SDD1_IM *associatedIM) : + IM(associatedIM) +{ + +} + +////////////////////////////////////////////////////// + + +void SDD1_GCD::getRunCount(uint8 code_num, uint8 *MPScount, bool8 *LPSind) { + + const uint8 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, + }; + + uint8 codeword=IM->getCodeword(code_num); + + if (codeword & 0x80) { + *LPSind=1; + *MPScount=run_count[codeword>>(code_num^0x07)]; + } + else { + *MPScount=(1<getRunCount(code_num, &MPScount, &LPSind); + + if (MPScount) { + bit=0; + MPScount--; + } + else { + bit=1; + LPSind=0; + } + + if (MPScount || LPSind) (*endOfRun)=0; + else (*endOfRun)=1; + + return bit; + +} + +///////////////////////////////////////////////// + + +SDD1_PEM::SDD1_PEM(SDD1_BG *associatedBG0, SDD1_BG *associatedBG1, + SDD1_BG *associatedBG2, SDD1_BG *associatedBG3, + SDD1_BG *associatedBG4, SDD1_BG *associatedBG5, + SDD1_BG *associatedBG6, SDD1_BG *associatedBG7) { + + BG[0]=associatedBG0; + BG[1]=associatedBG1; + BG[2]=associatedBG2; + BG[3]=associatedBG3; + BG[4]=associatedBG4; + BG[5]=associatedBG5; + BG[6]=associatedBG6; + BG[7]=associatedBG7; + +} + +///////////////////////////////////////////////////////// + + +const SDD1_PEM::state SDD1_PEM::evolution_table[]={ + { 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_PEM::prepareDecomp(void) { + + for (uint8 i=0; i<32; i++) { + contextInfo[i].status=0; + contextInfo[i].MPS=0; + } + +} + +///////////////////////////////////////////////////////// + + +uint8 SDD1_PEM::getBit(uint8 context) { + + bool8 endOfRun; + uint8 bit; + + SDD1_ContextInfo *pContInfo=&contextInfo[context]; + uint8 currStatus = pContInfo->status; + const state *pState=&SDD1_PEM::evolution_table[currStatus]; + uint8 currentMPS=pContInfo->MPS; + + bit=(BG[pState->code_num])->getBit(&endOfRun); + + if (endOfRun) + if (bit) { + if (!(currStatus & 0xfe)) (pContInfo->MPS)^=0x01; + (pContInfo->status)=pState->nextIfLPS; + } + else + (pContInfo->status)=pState->nextIfMPS; + + return bit^currentMPS; + +} + +////////////////////////////////////////////////////////////// + + +SDD1_CM::SDD1_CM(SDD1_PEM *associatedPEM) : + PEM(associatedPEM) +{ + +} + +////////////////////////////////////////////////////////////// + + +void SDD1_CM::prepareDecomp(uint32 first_byte) { + + bitplanesInfo = SDD1_read(first_byte) & 0xc0; + contextBitsInfo = SDD1_read(first_byte) & 0x30; + bit_number=0; + for (int i=0; i<8; i++) prevBitplaneBits[i]=0; + switch (bitplanesInfo) { + case 0x00: + currBitplane = 1; + break; + case 0x40: + currBitplane = 7; + break; + case 0x80: + currBitplane = 3; + } + +} + +///////////////////////////////////////////////////////////// + + +uint8 SDD1_CM::getBit(void) { + + uint8 currContext; + uint16 *context_bits; + + switch (bitplanesInfo) { + case 0x00: + currBitplane ^= 0x01; + break; + case 0x40: + currBitplane ^= 0x01; + if (!(bit_number & 0x7f)) currBitplane = ((currBitplane+2) & 0x07); + break; + case 0x80: + currBitplane ^= 0x01; + if (!(bit_number & 0x7f)) currBitplane ^= 0x02; + break; + case 0xc0: + currBitplane = bit_number & 0x07; + } + + context_bits = &prevBitplaneBits[currBitplane]; + + currContext=(currBitplane & 0x01)<<4; + switch (contextBitsInfo) { + case 0x00: + currContext|=((*context_bits & 0x01c0)>>5)|(*context_bits & 0x0001); + break; + case 0x10: + currContext|=((*context_bits & 0x0180)>>5)|(*context_bits & 0x0001); + break; + case 0x20: + currContext|=((*context_bits & 0x00c0)>>5)|(*context_bits & 0x0001); + break; + case 0x30: + currContext|=((*context_bits & 0x0180)>>5)|(*context_bits & 0x0003); + } + + uint8 bit=PEM->getBit(currContext); + + *context_bits <<= 1; + *context_bits |= bit; + + bit_number++; + + return bit; + +} + +////////////////////////////////////////////////// + + +SDD1_OL::SDD1_OL(SDD1_CM *associatedCM) : + CM(associatedCM) +{ + +} + +/////////////////////////////////////////////////// + + +void SDD1_OL::prepareDecomp(uint32 first_byte, uint16 out_len, uint8 *out_buf) { + + bitplanesInfo = SDD1_read(first_byte) & 0xc0; + length=out_len; + buffer=out_buf; + +} + +/////////////////////////////////////////////////// + + +void SDD1_OL::launch(void) { + + uint8 i; + uint8 register1, register2; + + switch (bitplanesInfo) { + case 0x00: + case 0x40: + case 0x80: + i=1; + do { //if length==0, we output 2^16 bytes + if (!i) { + *(buffer++)=register2; + i=~i; + } + else { + for (register1=register2=0, i=0x80; i; i>>=1) { + if (CM->getBit()) register1 |= i; + if (CM->getBit()) register2 |= i; + } + *(buffer++)=register1; + } + } while (--length); + break; + case 0xc0: + do { + for (register1=0, i=0x01; i; i<<=1) { + if (CM->getBit()) register1 |= i; + } + *(buffer++)=register1; + } while (--length); + } + +} + +/////////////////////////////////////////////////////// + + +void SDD1emu::decompress(uint32 in_buf, uint16 out_len, uint8 *out_buf) { + + IM.prepareDecomp(in_buf); + BG0.prepareDecomp(); + BG1.prepareDecomp(); + BG2.prepareDecomp(); + BG3.prepareDecomp(); + BG4.prepareDecomp(); + BG5.prepareDecomp(); + BG6.prepareDecomp(); + BG7.prepareDecomp(); + PEM.prepareDecomp(); + CM.prepareDecomp(in_buf); + OL.prepareDecomp(in_buf, out_len, out_buf); + + OL.launch(); + +} + +//////////////////////////////////////////////////////////// + + +SDD1emu::SDD1emu() : + GCD(&IM), + BG0(&GCD, 0), BG1(&GCD, 1), BG2(&GCD, 2), BG3(&GCD, 3), + BG4(&GCD, 4), BG5(&GCD, 5), BG6(&GCD, 6), BG7(&GCD, 7), + PEM(&BG0, &BG1, &BG2, &BG3, &BG4, &BG5, &BG6, &BG7), + CM(&PEM), + OL(&CM) +{ + +} + +/////////////////////////////////////////////////////////// + +#endif diff --git a/asnes/chip/sdd1/sdd1emu.hpp b/asnes/chip/sdd1/sdd1emu.hpp new file mode 100755 index 00000000..04f82bf5 --- /dev/null +++ b/asnes/chip/sdd1/sdd1emu.hpp @@ -0,0 +1,164 @@ +/************************************************************************ + +S-DD1'algorithm emulation code +------------------------------ + +Author: Andreas Naive +Date: August 2003 +Last update: October 2004 + +This code is Public Domain. There is no copyright holded by the author. +Said this, the author wish to explicitly emphasize his inalienable moral rights +over this piece of intelectual work and the previous research that made it +possible, as recognized by most of the copyright laws around the world. + +This code is provided 'as-is', with no warranty, expressed or implied. +No responsability is assumed by the author in connection with it. + +The author is greatly indebted with The Dumper, without whose help and +patience providing him with real S-DD1 data the research would have never been +possible. He also wish to note that in the very beggining of his research, +Neviksti had done some steps in the right direction. By last, the author is +indirectly indebted to all the people that worked and contributed in the +S-DD1 issue in the past. + +An algorithm's documentation is available as a separate document. +The implementation is obvious when the algorithm is +understood. + +************************************************************************/ + +#define bool8 uint8 + +class SDD1_IM { //Input Manager + + public: + SDD1_IM(void) {} + void prepareDecomp(uint32 in_buf); + uint8 getCodeword(const uint8 code_len); + + private: + uint32 byte_ptr; + uint8 bit_count; + +}; + +//////////////////////////////////////////////////// + + +class SDD1_GCD { //Golomb-Code Decoder + + public: + SDD1_GCD(SDD1_IM *associatedIM); + void getRunCount(uint8 code_num, uint8 *MPScount, bool8 *LPSind); + + private: + SDD1_IM *const IM; + +}; + +////////////////////////////////////////////////////// + + +class SDD1_BG { // Bits Generator + + public: + SDD1_BG(SDD1_GCD *associatedGCD, uint8 code); + void prepareDecomp(void); + uint8 getBit(bool8 *endOfRun); + + private: + const uint8 code_num; + uint8 MPScount; + bool8 LPSind; + SDD1_GCD *const GCD; + +}; + +//////////////////////////////////////////////// + + +class SDD1_PEM { //Probability Estimation Module + + public: + SDD1_PEM(SDD1_BG *associatedBG0, SDD1_BG *associatedBG1, + SDD1_BG *associatedBG2, SDD1_BG *associatedBG3, + SDD1_BG *associatedBG4, SDD1_BG *associatedBG5, + SDD1_BG *associatedBG6, SDD1_BG *associatedBG7); + void prepareDecomp(void); + uint8 getBit(uint8 context); + + private: + struct state { + uint8 code_num; + uint8 nextIfMPS; + uint8 nextIfLPS; + }; + static const state evolution_table[]; + struct SDD1_ContextInfo { + uint8 status; + uint8 MPS; + } contextInfo[32]; + SDD1_BG * BG[8]; + +}; + +/////////////////////////////////////////////////// + + +class SDD1_CM { //Context Model + + public: + SDD1_CM(SDD1_PEM *associatedPEM); + void prepareDecomp(uint32 first_byte); + uint8 getBit(void); + + private: + uint8 bitplanesInfo; + uint8 contextBitsInfo; + uint8 bit_number; + uint8 currBitplane; + uint16 prevBitplaneBits[8]; + SDD1_PEM *const PEM; + +}; + +/////////////////////////////////////////////////// + + +class SDD1_OL { //Output Logic + + public: + SDD1_OL(SDD1_CM *associatedCM); + void prepareDecomp(uint32 first_byte, uint16 out_len, uint8 *out_buf); + void launch(void); + + private: + uint8 bitplanesInfo; + uint16 length; + uint8 *buffer; + SDD1_CM *const CM; + +}; + +///////////////////////////////////////////////////////// + + +class SDD1emu { + + public: + SDD1emu(void); + void decompress(uint32 in_buf, uint16 out_len, uint8 *out_buf); + + private: + SDD1_IM IM; + SDD1_GCD GCD; + SDD1_BG BG0; SDD1_BG BG1; SDD1_BG BG2; SDD1_BG BG3; + SDD1_BG BG4; SDD1_BG BG5; SDD1_BG BG6; SDD1_BG BG7; + SDD1_PEM PEM; + SDD1_CM CM; + SDD1_OL OL; + +}; + +#undef bool8 diff --git a/asnes/chip/sdd1/serialization.cpp b/asnes/chip/sdd1/serialization.cpp new file mode 100755 index 00000000..87da13f3 --- /dev/null +++ b/asnes/chip/sdd1/serialization.cpp @@ -0,0 +1,19 @@ +#ifdef SDD1_CPP + +void SDD1::serialize(serializer &s) { + s.integer(sdd1_enable); + s.integer(xfer_enable); + s.array(mmc); + + for(unsigned n = 0; n < 8; n++) { + s.integer(dma[n].addr); + s.integer(dma[n].size); + } + + s.array(buffer.data); + s.integer(buffer.offset); + s.integer(buffer.size); + s.integer(buffer.ready); +} + +#endif diff --git a/asnes/chip/serial/serial.cpp b/asnes/chip/serial/serial.cpp new file mode 100755 index 00000000..b9404adb --- /dev/null +++ b/asnes/chip/serial/serial.cpp @@ -0,0 +1,97 @@ +#include + +#define SERIAL_CPP +namespace SNES { + +Serial serial; + +#include "serialization.cpp" + +static void snesserial_tick(unsigned clocks) { serial.add_clocks(clocks * 8); } +static uint8 snesserial_read() { return serial.read(); } +static void snesserial_write(uint8 data) { serial.write(data); } + +void Serial::Enter() { serial.enter(); } + +void Serial::enter() { + latch = 0; + add_clocks(256 * 8); //warm-up + if(snesserial_main) snesserial_main(snesserial_tick, snesserial_read, snesserial_write); + while(true) add_clocks(frequency); //snesserial_main() fallback +} + +void Serial::add_clocks(unsigned clocks) { + step(clocks); + synchronize_cpu(); +} + +uint8 Serial::read() { + while(cpu.joylatch() == 0) add_clocks(1); + while(cpu.joylatch() == 1) add_clocks(1); + add_clocks(4); + + uint8 data = 0; + for(unsigned i = 0; i < 8; i++) { + add_clocks(8); + data = (cpu.joylatch() << 7) | (data >> 1); + } + + return data; +} + +void Serial::write(uint8 data) { + latch = 1; + add_clocks(8); + + for(unsigned i = 0; i < 8; i++) { + latch = (data & 1) ^ 1; + data >>= 1; + add_clocks(8); + } + + latch = 0; + add_clocks(8); +} + +uint8 Serial::mmio_read(unsigned addr) { + cpu.synchronize_coprocessor(); + switch(addr & 1) { default: + case 0: return r4016->mmio_read(addr); + case 1: return r4017->mmio_read(addr); + } +} + +void Serial::mmio_write(unsigned addr, uint8 data) { + cpu.synchronize_coprocessor(); + switch(addr & 1) { default: + case 0: r4016->mmio_write(addr, data); break; + case 1: r4017->mmio_write(addr, data); break; + } +} + +void Serial::init() { +} + +void Serial::enable() { + r4016 = memory::mmio.mmio[0x4016 - 0x2000]; + r4017 = memory::mmio.mmio[0x4017 - 0x2000]; + memory::mmio.mmio[0x4016 - 0x2000] = this; + memory::mmio.mmio[0x4017 - 0x2000] = this; + + if(opened()) close(); + string name = notdir(cartridge.basename()); + string path = dir(cartridge.basename()); + if(open(name, path)) { + snesserial_main = sym("snesserial_main"); + } +} + +void Serial::power() { + reset(); +} + +void Serial::reset() { + create(Serial::Enter, cartridge.serial_baud_rate() * 8); +} + +} diff --git a/asnes/chip/serial/serial.hpp b/asnes/chip/serial/serial.hpp new file mode 100755 index 00000000..adb499ed --- /dev/null +++ b/asnes/chip/serial/serial.hpp @@ -0,0 +1,25 @@ +class Serial : public Coprocessor, public MMIO, public library, public property { +public: + static void Enter(); + void enter(); + void init(); + void enable(); + void power(); + void reset(); + void serialize(serializer&); + + readonly latch; + + void add_clocks(unsigned clocks); + uint8 read(); + void write(uint8 data); + + uint8 mmio_read(unsigned addr); + void mmio_write(unsigned addr, uint8 data); + +private: + MMIO *r4016, *r4017; + function snesserial_main; +}; + +extern Serial serial; diff --git a/asnes/chip/serial/serialization.cpp b/asnes/chip/serial/serialization.cpp new file mode 100755 index 00000000..8b2a318a --- /dev/null +++ b/asnes/chip/serial/serialization.cpp @@ -0,0 +1,8 @@ +#ifdef SERIAL_CPP + +void Serial::serialize(serializer &s) { + Processor::serialize(s); + s.integer((bool&)latch); +} + +#endif diff --git a/asnes/chip/spc7110/decomp.cpp b/asnes/chip/spc7110/decomp.cpp new file mode 100755 index 00000000..212fb0af --- /dev/null +++ b/asnes/chip/spc7110/decomp.cpp @@ -0,0 +1,511 @@ +#ifdef SPC7110_CPP + +uint8 SPC7110Decomp::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 SPC7110Decomp::write(uint8 data) { + decomp_buffer[decomp_buffer_wroffset++] = data; + decomp_buffer_wroffset &= decomp_buffer_size - 1; + decomp_buffer_length++; +} + +uint8 SPC7110Decomp::dataread() { + unsigned size = memory::cartrom.size() - cartridge.spc7110_data_rom_offset(); + while(decomp_offset >= size) decomp_offset -= size; + return memory::cartrom.read(cartridge.spc7110_data_rom_offset() + decomp_offset++); +} + +void SPC7110Decomp::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 SPC7110Decomp::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 SPC7110Decomp::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 = morton_2x8(out); + write(data >> 8); + write(data >> 0); + } +} + +void SPC7110Decomp::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 = morton_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 SPC7110Decomp::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 SPC7110Decomp::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 SPC7110Decomp::probability (unsigned n) { return evolution_table[context[n].index][0]; } +uint8 SPC7110Decomp::next_lps (unsigned n) { return evolution_table[context[n].index][1]; } +uint8 SPC7110Decomp::next_mps (unsigned n) { return evolution_table[context[n].index][2]; } +bool SPC7110Decomp::toggle_invert(unsigned n) { return evolution_table[context[n].index][3]; } + +unsigned SPC7110Decomp::morton_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 + return morton16[0][(data >> 0) & 255] + morton16[1][(data >> 8) & 255]; +} + +unsigned SPC7110Decomp::morton_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 + return morton32[0][(data >> 0) & 255] + morton32[1][(data >> 8) & 255] + + morton32[2][(data >> 16) & 255] + morton32[3][(data >> 24) & 255]; +} + +// + +void SPC7110Decomp::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; +} + +SPC7110Decomp::SPC7110Decomp() { + decomp_buffer = new uint8_t[decomp_buffer_size]; + reset(); + + //initialize reverse morton lookup tables + for(unsigned i = 0; i < 256; i++) { + #define map(x, y) (((i >> x) & 1) << y) + //2x8-bit + morton16[1][i] = map(7, 15) + map(6, 7) + map(5, 14) + map(4, 6) + + map(3, 13) + map(2, 5) + map(1, 12) + map(0, 4); + morton16[0][i] = map(7, 11) + map(6, 3) + map(5, 10) + map(4, 2) + + map(3, 9) + map(2, 1) + map(1, 8) + map(0, 0); + //4x8-bit + morton32[3][i] = map(7, 31) + map(6, 23) + map(5, 15) + map(4, 7) + + map(3, 30) + map(2, 22) + map(1, 14) + map(0, 6); + morton32[2][i] = map(7, 29) + map(6, 21) + map(5, 13) + map(4, 5) + + map(3, 28) + map(2, 20) + map(1, 12) + map(0, 4); + morton32[1][i] = map(7, 27) + map(6, 19) + map(5, 11) + map(4, 3) + + map(3, 26) + map(2, 18) + map(1, 10) + map(0, 2); + morton32[0][i] = map(7, 25) + map(6, 17) + map(5, 9) + map(4, 1) + + map(3, 24) + map(2, 16) + map(1, 8) + map(0, 0); + #undef map + } +} + +SPC7110Decomp::~SPC7110Decomp() { + delete[] decomp_buffer; +} + +#endif diff --git a/asnes/chip/spc7110/decomp.hpp b/asnes/chip/spc7110/decomp.hpp new file mode 100755 index 00000000..2b67605f --- /dev/null +++ b/asnes/chip/spc7110/decomp.hpp @@ -0,0 +1,46 @@ +class SPC7110Decomp { +public: + uint8 read(); + void init(unsigned mode, unsigned offset, unsigned index); + void reset(); + + void serialize(serializer&); + SPC7110Decomp(); + ~SPC7110Decomp(); + +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 morton16[2][256]; + unsigned morton32[4][256]; + unsigned morton_2x8(unsigned data); + unsigned morton_4x8(unsigned data); +}; diff --git a/asnes/chip/spc7110/serialization.cpp b/asnes/chip/spc7110/serialization.cpp new file mode 100755 index 00000000..2e77d524 --- /dev/null +++ b/asnes/chip/spc7110/serialization.cpp @@ -0,0 +1,81 @@ +#ifdef SPC7110_CPP + +void SPC7110Decomp::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.integer(rtc_state); + s.integer(rtc_mode); + s.integer(rtc_index); +} + +#endif diff --git a/asnes/chip/spc7110/spc7110.cpp b/asnes/chip/spc7110/spc7110.cpp new file mode 100755 index 00000000..3559a631 --- /dev/null +++ b/asnes/chip/spc7110/spc7110.cpp @@ -0,0 +1,682 @@ +#include + +#define SPC7110_CPP +namespace SNES { + +SPC7110 spc7110; +SPC7110MCU spc7110mcu; +SPC7110DCU spc7110dcu; +SPC7110RAM spc7110ram; + +#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::enable() {} + +void SPC7110::power() { + reset(); +} + +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 = memory::cartrom.size() - cartridge.spc7110_data_rom_offset(); + while(addr >= size) addr -= size; + return cartridge.spc7110_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 + = (memory::cartrtc.read(16) << 0) + | (memory::cartrtc.read(17) << 8) + | (memory::cartrtc.read(18) << 16) + | (memory::cartrtc.read(19) << 24); + time_t current_time = time(0) - offset; + + //sizeof(time_t) is platform-dependent; though memory::cartrtc 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 + //memory::cartrtc 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) + : (std::numeric_limits::max() - rtc_time + current_time + 1); //compensate for overflow + if(diff > std::numeric_limits::max() / 2) diff = 0; //compensate for underflow + + bool update = true; + if(memory::cartrtc.read(13) & 1) update = false; //do not update if CR0 timer disable flag is set + if(memory::cartrtc.read(15) & 3) update = false; //do not update if CR2 timer disable flags are set + + if(diff > 0 && update == true) { + unsigned second = memory::cartrtc.read( 0) + memory::cartrtc.read( 1) * 10; + unsigned minute = memory::cartrtc.read( 2) + memory::cartrtc.read( 3) * 10; + unsigned hour = memory::cartrtc.read( 4) + memory::cartrtc.read( 5) * 10; + unsigned day = memory::cartrtc.read( 6) + memory::cartrtc.read( 7) * 10; + unsigned month = memory::cartrtc.read( 8) + memory::cartrtc.read( 9) * 10; + unsigned year = memory::cartrtc.read(10) + memory::cartrtc.read(11) * 10; + unsigned weekday = memory::cartrtc.read(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; + + memory::cartrtc.write( 0, second % 10); + memory::cartrtc.write( 1, second / 10); + memory::cartrtc.write( 2, minute % 10); + memory::cartrtc.write( 3, minute / 10); + memory::cartrtc.write( 4, hour % 10); + memory::cartrtc.write( 5, hour / 10); + memory::cartrtc.write( 6, day % 10); + memory::cartrtc.write( 7, day / 10); + memory::cartrtc.write( 8, month % 10); + memory::cartrtc.write( 9, month / 10); + memory::cartrtc.write(10, year % 10); + memory::cartrtc.write(11, (year / 10) % 10); + memory::cartrtc.write(12, weekday % 7); + } + + memory::cartrtc.write(16, current_time >> 0); + memory::cartrtc.write(17, current_time >> 8); + memory::cartrtc.write(18, current_time >> 16); + memory::cartrtc.write(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 = memory::cartrom.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 = memory::cartrom.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 = memory::cartrtc.read(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 = (memory::cartrom.read(addr + 0)); + unsigned offset = (memory::cartrom.read(addr + 1) << 16) + + (memory::cartrom.read(addr + 2) << 8) + + (memory::cartrom.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 = memory::cartrtc.read( 0) + memory::cartrtc.read( 1) * 10; + //clear seconds + memory::cartrtc.write(0, 0); + memory::cartrtc.write(1, 0); + + if(second >= 30) update_time(+60); + } + } + + //control register 2 + if(rtc_index == 15) { + //disable timer and clear second counter + if((data & 1) && !(memory::cartrtc.read(15) & 1)) { + update_time(); + + //clear seconds + memory::cartrtc.write(0, 0); + memory::cartrtc.write(1, 0); + } + + //disable timer + if((data & 2) && !(memory::cartrtc.read(15) & 2)) { + update_time(); + } + } + + memory::cartrtc.write(rtc_index, data & 15); + rtc_index = (rtc_index + 1) & 15; + } break; + } //switch(rtc_state) + } break; + } +} + +SPC7110::SPC7110() { +} + +//========== +//SPC7110MCU +//========== + +unsigned SPC7110MCU::size() const { + return 0x300000; +} + +uint8 SPC7110MCU::read(unsigned addr) { + if(addr <= 0xdfffff) return memory::cartrom.read(spc7110.dx_offset + (addr & 0x0fffff)); + if(addr <= 0xefffff) return memory::cartrom.read(spc7110.ex_offset + (addr & 0x0fffff)); + if(addr <= 0xffffff) return memory::cartrom.read(spc7110.fx_offset + (addr & 0x0fffff)); + return cpu.regs.mdr; +} + +void SPC7110MCU::write(unsigned addr, uint8 data) { +} + +//========== +//SPC7110DCU +//========== + +uint8 SPC7110DCU::read(unsigned) { + return spc7110.mmio_read(0x4800); +} + +void SPC7110DCU::write(unsigned, uint8) { +} + +//========== +//SPC7110RAM +//========== + +unsigned SPC7110RAM::size() const { + return 0x2000; +} + +uint8 SPC7110RAM::read(unsigned addr) { + return memory::cartram.read(addr & 0x1fff); +} + +void SPC7110RAM::write(unsigned addr, uint8 data) { + if(spc7110.r4830 & 0x80) memory::cartram.write(addr & 0x1fff, data); +} + +} diff --git a/asnes/chip/spc7110/spc7110.hpp b/asnes/chip/spc7110/spc7110.hpp new file mode 100755 index 00000000..4d8b43c3 --- /dev/null +++ b/asnes/chip/spc7110/spc7110.hpp @@ -0,0 +1,159 @@ +/***** + * SPC7110 emulator - version 0.04 (2010-02-14) + * Copyright (c) 2008-2010, byuu and neviksti + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * The software is provided "as is" and the author disclaims all warranties + * with regard to this software including all implied warranties of + * merchantibility and fitness, in no event shall the author be liable for + * any special, direct, indirect, or consequential damages or any damages + * whatsoever resulting from loss of use, data or profits, whether in an + * action of contract, negligence or other tortious action, arising out of + * or in connection with the use or performance of this software. + *****/ + +#include "decomp.hpp" + +class SPC7110 : public MMIO { +public: + void init(); + void enable(); + 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); + + //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 + + SPC7110Decomp 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]; + friend class SPC7110MCU; + friend class SPC7110DCU; + friend class SPC7110RAM; +}; + +class SPC7110MCU : public Memory { +public: + unsigned size() const; + uint8 read(unsigned addr); + void write(unsigned addr, uint8 data); +}; + +class SPC7110DCU : public Memory { +public: + uint8 read(unsigned addr); + void write(unsigned addr, uint8 data); +}; + +class SPC7110RAM : public Memory { +public: + unsigned size() const; + uint8 read(unsigned addr); + void write(unsigned addr, uint8 data); +}; + +extern SPC7110 spc7110; +extern SPC7110MCU spc7110mcu; +extern SPC7110DCU spc7110dcu; +extern SPC7110RAM spc7110ram; diff --git a/asnes/chip/srtc/serialization.cpp b/asnes/chip/srtc/serialization.cpp new file mode 100755 index 00000000..538a1bd8 --- /dev/null +++ b/asnes/chip/srtc/serialization.cpp @@ -0,0 +1,8 @@ +#ifdef SRTC_CPP + +void SRTC::serialize(serializer &s) { + s.integer(rtc_mode); + s.integer(rtc_index); +} + +#endif diff --git a/asnes/chip/srtc/srtc.cpp b/asnes/chip/srtc/srtc.cpp new file mode 100755 index 00000000..965a0ef0 --- /dev/null +++ b/asnes/chip/srtc/srtc.cpp @@ -0,0 +1,231 @@ +#include + +#define SRTC_CPP +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::enable() { +} + +void SRTC::power() { + reset(); +} + +void SRTC::reset() { + rtc_mode = RtcRead; + rtc_index = -1; + update_time(); +} + +void SRTC::update_time() { + time_t rtc_time + = (memory::cartrtc.read(16) << 0) + | (memory::cartrtc.read(17) << 8) + | (memory::cartrtc.read(18) << 16) + | (memory::cartrtc.read(19) << 24); + time_t current_time = time(0); + + //sizeof(time_t) is platform-dependent; though memory::cartrtc 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 + //memory::cartrtc 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) + : (std::numeric_limits::max() - rtc_time + current_time + 1); //compensate for overflow + if(diff > std::numeric_limits::max() / 2) diff = 0; //compensate for underflow + + if(diff > 0) { + unsigned second = memory::cartrtc.read( 0) + memory::cartrtc.read( 1) * 10; + unsigned minute = memory::cartrtc.read( 2) + memory::cartrtc.read( 3) * 10; + unsigned hour = memory::cartrtc.read( 4) + memory::cartrtc.read( 5) * 10; + unsigned day = memory::cartrtc.read( 6) + memory::cartrtc.read( 7) * 10; + unsigned month = memory::cartrtc.read( 8); + unsigned year = memory::cartrtc.read( 9) + memory::cartrtc.read(10) * 10 + memory::cartrtc.read(11) * 100; + unsigned weekday = memory::cartrtc.read(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; + + memory::cartrtc.write( 0, second % 10); + memory::cartrtc.write( 1, second / 10); + memory::cartrtc.write( 2, minute % 10); + memory::cartrtc.write( 3, minute / 10); + memory::cartrtc.write( 4, hour % 10); + memory::cartrtc.write( 5, hour / 10); + memory::cartrtc.write( 6, day % 10); + memory::cartrtc.write( 7, day / 10); + memory::cartrtc.write( 8, month); + memory::cartrtc.write( 9, year % 10); + memory::cartrtc.write(10, (year / 10) % 10); + memory::cartrtc.write(11, year / 100); + memory::cartrtc.write(12, weekday % 7); + } + + memory::cartrtc.write(16, current_time >> 0); + memory::cartrtc.write(17, current_time >> 8); + memory::cartrtc.write(18, current_time >> 16); + memory::cartrtc.write(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::mmio_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 memory::cartrtc.read(rtc_index++); + } + } + + return cpu.regs.mdr; +} + +void SRTC::mmio_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) { + memory::cartrtc.write(rtc_index++, data); + + if(rtc_index == 12) { + //day of week is automatically calculated and written + unsigned day = memory::cartrtc.read( 6) + memory::cartrtc.read( 7) * 10; + unsigned month = memory::cartrtc.read( 8); + unsigned year = memory::cartrtc.read( 9) + memory::cartrtc.read(10) * 10 + memory::cartrtc.read(11) * 100; + year += 1000; + + memory::cartrtc.write(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++) memory::cartrtc.write(i, 0); + } else { + //unknown behavior + rtc_mode = RtcReady; + } + } + } +} + +SRTC::SRTC() { +} + +} diff --git a/asnes/chip/srtc/srtc.hpp b/asnes/chip/srtc/srtc.hpp new file mode 100755 index 00000000..7bbe7f47 --- /dev/null +++ b/asnes/chip/srtc/srtc.hpp @@ -0,0 +1,24 @@ +class SRTC : public MMIO { +public: + void init(); + void enable(); + void power(); + void reset(); + + uint8 mmio_read(unsigned addr); + void mmio_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/asnes/chip/st0010/data.hpp b/asnes/chip/st0010/data.hpp new file mode 100755 index 00000000..52b251f9 --- /dev/null +++ b/asnes/chip/st0010/data.hpp @@ -0,0 +1,130 @@ +#ifdef ST0010_CPP + +const int16 ST0010::sin_table[256] = { + 0x0000, 0x0324, 0x0648, 0x096a, 0x0c8c, 0x0fab, 0x12c8, 0x15e2, + 0x18f9, 0x1c0b, 0x1f1a, 0x2223, 0x2528, 0x2826, 0x2b1f, 0x2e11, + 0x30fb, 0x33df, 0x36ba, 0x398c, 0x3c56, 0x3f17, 0x41ce, 0x447a, + 0x471c, 0x49b4, 0x4c3f, 0x4ebf, 0x5133, 0x539b, 0x55f5, 0x5842, + 0x5a82, 0x5cb3, 0x5ed7, 0x60eb, 0x62f1, 0x64e8, 0x66cf, 0x68a6, + 0x6a6d, 0x6c23, 0x6dc9, 0x6f5e, 0x70e2, 0x7254, 0x73b5, 0x7504, + 0x7641, 0x776b, 0x7884, 0x7989, 0x7a7c, 0x7b5c, 0x7c29, 0x7ce3, + 0x7d89, 0x7e1d, 0x7e9c, 0x7f09, 0x7f61, 0x7fa6, 0x7fd8, 0x7ff5, + 0x7fff, 0x7ff5, 0x7fd8, 0x7fa6, 0x7f61, 0x7f09, 0x7e9c, 0x7e1d, + 0x7d89, 0x7ce3, 0x7c29, 0x7b5c, 0x7a7c, 0x7989, 0x7884, 0x776b, + 0x7641, 0x7504, 0x73b5, 0x7254, 0x70e2, 0x6f5e, 0x6dc9, 0x6c23, + 0x6a6d, 0x68a6, 0x66cf, 0x64e8, 0x62f1, 0x60eb, 0x5ed7, 0x5cb3, + 0x5a82, 0x5842, 0x55f5, 0x539b, 0x5133, 0x4ebf, 0x4c3f, 0x49b4, + 0x471c, 0x447a, 0x41ce, 0x3f17, 0x3c56, 0x398c, 0x36ba, 0x33df, + 0x30fb, 0x2e11, 0x2b1f, 0x2826, 0x2528, 0x2223, 0x1f1a, 0x1c0b, + 0x18f8, 0x15e2, 0x12c8, 0x0fab, 0x0c8c, 0x096a, 0x0648, 0x0324, + 0x0000, -0x0324, -0x0648, -0x096b, -0x0c8c, -0x0fab, -0x12c8, -0x15e2, + -0x18f9, -0x1c0b, -0x1f1a, -0x2223, -0x2528, -0x2826, -0x2b1f, -0x2e11, + -0x30fb, -0x33df, -0x36ba, -0x398d, -0x3c56, -0x3f17, -0x41ce, -0x447a, + -0x471c, -0x49b4, -0x4c3f, -0x4ebf, -0x5133, -0x539b, -0x55f5, -0x5842, + -0x5a82, -0x5cb3, -0x5ed7, -0x60ec, -0x62f1, -0x64e8, -0x66cf, -0x68a6, + -0x6a6d, -0x6c23, -0x6dc9, -0x6f5e, -0x70e2, -0x7254, -0x73b5, -0x7504, + -0x7641, -0x776b, -0x7884, -0x7989, -0x7a7c, -0x7b5c, -0x7c29, -0x7ce3, + -0x7d89, -0x7e1d, -0x7e9c, -0x7f09, -0x7f61, -0x7fa6, -0x7fd8, -0x7ff5, + -0x7fff, -0x7ff5, -0x7fd8, -0x7fa6, -0x7f61, -0x7f09, -0x7e9c, -0x7e1d, + -0x7d89, -0x7ce3, -0x7c29, -0x7b5c, -0x7a7c, -0x7989, -0x7883, -0x776b, + -0x7641, -0x7504, -0x73b5, -0x7254, -0x70e2, -0x6f5e, -0x6dc9, -0x6c23, + -0x6a6d, -0x68a6, -0x66cf, -0x64e8, -0x62f1, -0x60eb, -0x5ed7, -0x5cb3, + -0x5a82, -0x5842, -0x55f5, -0x539a, -0x5133, -0x4ebf, -0x4c3f, -0x49b3, + -0x471c, -0x447a, -0x41cd, -0x3f17, -0x3c56, -0x398c, -0x36b9, -0x33de, + -0x30fb, -0x2e10, -0x2b1f, -0x2826, -0x2527, -0x2223, -0x1f19, -0x1c0b, + -0x18f8, -0x15e2, -0x12c8, -0x0fab, -0x0c8b, -0x096a, -0x0647, -0x0324 +}; + +const int16 ST0010::mode7_scale[176] = { + 0x0380, 0x0325, 0x02da, 0x029c, 0x0268, 0x023b, 0x0215, 0x01f3, + 0x01d5, 0x01bb, 0x01a3, 0x018e, 0x017b, 0x016a, 0x015a, 0x014b, + 0x013e, 0x0132, 0x0126, 0x011c, 0x0112, 0x0109, 0x0100, 0x00f8, + 0x00f0, 0x00e9, 0x00e3, 0x00dc, 0x00d6, 0x00d1, 0x00cb, 0x00c6, + 0x00c1, 0x00bd, 0x00b8, 0x00b4, 0x00b0, 0x00ac, 0x00a8, 0x00a5, + 0x00a2, 0x009e, 0x009b, 0x0098, 0x0095, 0x0093, 0x0090, 0x008d, + 0x008b, 0x0088, 0x0086, 0x0084, 0x0082, 0x0080, 0x007e, 0x007c, + 0x007a, 0x0078, 0x0076, 0x0074, 0x0073, 0x0071, 0x006f, 0x006e, + 0x006c, 0x006b, 0x0069, 0x0068, 0x0067, 0x0065, 0x0064, 0x0063, + 0x0062, 0x0060, 0x005f, 0x005e, 0x005d, 0x005c, 0x005b, 0x005a, + 0x0059, 0x0058, 0x0057, 0x0056, 0x0055, 0x0054, 0x0053, 0x0052, + 0x0051, 0x0051, 0x0050, 0x004f, 0x004e, 0x004d, 0x004d, 0x004c, + 0x004b, 0x004b, 0x004a, 0x0049, 0x0048, 0x0048, 0x0047, 0x0047, + 0x0046, 0x0045, 0x0045, 0x0044, 0x0044, 0x0043, 0x0042, 0x0042, + 0x0041, 0x0041, 0x0040, 0x0040, 0x003f, 0x003f, 0x003e, 0x003e, + 0x003d, 0x003d, 0x003c, 0x003c, 0x003b, 0x003b, 0x003a, 0x003a, + 0x003a, 0x0039, 0x0039, 0x0038, 0x0038, 0x0038, 0x0037, 0x0037, + 0x0036, 0x0036, 0x0036, 0x0035, 0x0035, 0x0035, 0x0034, 0x0034, + 0x0034, 0x0033, 0x0033, 0x0033, 0x0032, 0x0032, 0x0032, 0x0031, + 0x0031, 0x0031, 0x0030, 0x0030, 0x0030, 0x0030, 0x002f, 0x002f, + 0x002f, 0x002e, 0x002e, 0x002e, 0x002e, 0x002d, 0x002d, 0x002d, + 0x002d, 0x002c, 0x002c, 0x002c, 0x002c, 0x002b, 0x002b, 0x002b +}; + +const uint8 ST0010::arctan[32][32] = { + { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 }, + { 0x80, 0xa0, 0xad, 0xb3, 0xb6, 0xb8, 0xb9, 0xba, 0xbb, 0xbb, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd, + 0xbd, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf, 0xbf }, + { 0x80, 0x93, 0xa0, 0xa8, 0xad, 0xb0, 0xb3, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xb9, 0xba, 0xba, 0xbb, + 0xbb, 0xbb, 0xbb, 0xbc, 0xbc, 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd, 0xbd }, + { 0x80, 0x8d, 0x98, 0xa0, 0xa6, 0xaa, 0xad, 0xb0, 0xb1, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb7, 0xb8, + 0xb8, 0xb9, 0xb9, 0xba, 0xba, 0xba, 0xba, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbc, 0xbc, 0xbc, 0xbc }, + { 0x80, 0x8a, 0x93, 0x9a, 0xa0, 0xa5, 0xa8, 0xab, 0xad, 0xaf, 0xb0, 0xb2, 0xb3, 0xb4, 0xb5, 0xb5, + 0xb6, 0xb7, 0xb7, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xba, 0xba, 0xba, 0xba, 0xba, 0xbb, 0xbb }, + { 0x80, 0x88, 0x90, 0x96, 0x9b, 0xa0, 0xa4, 0xa7, 0xa9, 0xab, 0xad, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, + 0xb4, 0xb4, 0xb5, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xb9, 0xb9 }, + { 0x80, 0x87, 0x8d, 0x93, 0x98, 0x9c, 0xa0, 0xa3, 0xa6, 0xa8, 0xaa, 0xac, 0xad, 0xae, 0xb0, 0xb0, + 0xb1, 0xb2, 0xb3, 0xb4, 0xb4, 0xb5, 0xb5, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7, 0xb7, 0xb8, 0xb8, 0xb8 }, + { 0x80, 0x86, 0x8b, 0x90, 0x95, 0x99, 0x9d, 0xa0, 0xa3, 0xa5, 0xa7, 0xa9, 0xaa, 0xac, 0xad, 0xae, + 0xaf, 0xb0, 0xb1, 0xb2, 0xb2, 0xb3, 0xb3, 0xb4, 0xb4, 0xb5, 0xb5, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7 }, + { 0x80, 0x85, 0x8a, 0x8f, 0x93, 0x97, 0x9a, 0x9d, 0xa0, 0xa2, 0xa5, 0xa6, 0xa8, 0xaa, 0xab, 0xac, + 0xad, 0xae, 0xaf, 0xb0, 0xb0, 0xb1, 0xb2, 0xb2, 0xb3, 0xb3, 0xb4, 0xb4, 0xb5, 0xb5, 0xb5, 0xb5 }, + { 0x80, 0x85, 0x89, 0x8d, 0x91, 0x95, 0x98, 0x9b, 0x9e, 0xa0, 0xa0, 0xa4, 0xa6, 0xa7, 0xa9, 0xaa, + 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb0, 0xb1, 0xb1, 0xb2, 0xb2, 0xb3, 0xb3, 0xb4, 0xb4, 0xb4 }, + { 0x80, 0x84, 0x88, 0x8c, 0x90, 0x93, 0x96, 0x99, 0x9b, 0x9e, 0xa0, 0xa2, 0xa4, 0xa5, 0xa7, 0xa8, + 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xaf, 0xb0, 0xb0, 0xb1, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3 }, + { 0x80, 0x84, 0x87, 0x8b, 0x8e, 0x91, 0x94, 0x97, 0x9a, 0x9c, 0x9e, 0xa0, 0xa2, 0xa3, 0xa5, 0xa6, + 0xa7, 0xa9, 0xaa, 0xab, 0xac, 0xac, 0xad, 0xae, 0xae, 0xaf, 0xb0, 0xb0, 0xb1, 0xb1, 0xb2, 0xb2 }, + { 0x80, 0x83, 0x87, 0x8a, 0x8d, 0x90, 0x93, 0x96, 0x98, 0x9a, 0x9c, 0x9e, 0xa0, 0xa2, 0xa3, 0xa5, + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xac, 0xad, 0xae, 0xae, 0xaf, 0xb0, 0xb0, 0xb0, 0xb1 }, + { 0x80, 0x83, 0x86, 0x89, 0x8c, 0x8f, 0x92, 0x94, 0x96, 0x99, 0x9b, 0x9d, 0x9e, 0xa0, 0xa2, 0xa3, + 0xa4, 0xa5, 0xa7, 0xa8, 0xa9, 0xa9, 0xaa, 0xab, 0xac, 0xac, 0xad, 0xae, 0xae, 0xaf, 0xaf, 0xb0 }, + { 0x80, 0x83, 0x86, 0x89, 0x8b, 0x8e, 0x90, 0x93, 0x95, 0x97, 0x99, 0x9b, 0x9d, 0x9e, 0xa0, 0xa1, + 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xaa, 0xab, 0xac, 0xad, 0xad, 0xae, 0xae, 0xaf }, + { 0x80, 0x83, 0x85, 0x88, 0x8b, 0x8d, 0x90, 0x92, 0x94, 0x96, 0x98, 0x9a, 0x9b, 0x9d, 0x9f, 0xa0, + 0xa1, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa8, 0xa9, 0xaa, 0xab, 0xab, 0xac, 0xad, 0xad, 0xae }, + { 0x80, 0x83, 0x85, 0x88, 0x8a, 0x8c, 0x8f, 0x91, 0x93, 0x95, 0x97, 0x99, 0x9a, 0x9c, 0x9d, 0x9f, + 0xa0, 0xa1, 0xa2, 0xa3, 0xa5, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xaa, 0xab, 0xab, 0xac, 0xad }, + { 0x80, 0x82, 0x85, 0x87, 0x89, 0x8c, 0x8e, 0x90, 0x92, 0x94, 0x96, 0x97, 0x99, 0x9b, 0x9c, 0x9d, + 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa8, 0xa9, 0xaa, 0xaa, 0xab, 0xac }, + { 0x80, 0x82, 0x85, 0x87, 0x89, 0x8b, 0x8d, 0x8f, 0x91, 0x93, 0x95, 0x96, 0x98, 0x99, 0x9b, 0x9c, + 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa7, 0xa8, 0xa9, 0xa9, 0xaa, 0xab }, + { 0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c, 0x8e, 0x90, 0x92, 0x94, 0x95, 0x97, 0x98, 0x9a, 0x9b, + 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa6, 0xa7, 0xa8, 0xa8, 0xa9, 0xaa }, + { 0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c, 0x8e, 0x90, 0x91, 0x93, 0x94, 0x96, 0x97, 0x99, 0x9a, + 0x9b, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa5, 0xa6, 0xa7, 0xa7, 0xa8, 0xa9 }, + { 0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8b, 0x8d, 0x8f, 0x90, 0x92, 0x94, 0x95, 0x97, 0x98, 0x99, + 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa4, 0xa5, 0xa6, 0xa6, 0xa7, 0xa8 }, + { 0x80, 0x82, 0x84, 0x86, 0x87, 0x89, 0x8b, 0x8d, 0x8e, 0x90, 0x91, 0x93, 0x94, 0x96, 0x97, 0x98, + 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa3, 0xa4, 0xa5, 0xa6, 0xa6, 0xa7 }, + { 0x80, 0x82, 0x84, 0x85, 0x87, 0x89, 0x8a, 0x8c, 0x8e, 0x8f, 0x91, 0x92, 0x94, 0x95, 0x96, 0x98, + 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa2, 0xa3, 0xa4, 0xa5, 0xa5, 0xa6 }, + { 0x80, 0x82, 0x83, 0x85, 0x87, 0x88, 0x8a, 0x8c, 0x8d, 0x8f, 0x90, 0x92, 0x93, 0x94, 0x96, 0x97, + 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa2, 0xa3, 0xa4, 0xa5, 0xa5 }, + { 0x80, 0x82, 0x83, 0x85, 0x86, 0x88, 0x8a, 0x8b, 0x8d, 0x8e, 0x90, 0x91, 0x92, 0x94, 0x95, 0x96, + 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa2, 0xa3, 0xa4, 0xa4 }, + { 0x80, 0x82, 0x83, 0x85, 0x86, 0x88, 0x89, 0x8b, 0x8c, 0x8e, 0x8f, 0x90, 0x92, 0x93, 0x94, 0x95, + 0x96, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa2, 0xa3, 0xa4 }, + { 0x80, 0x82, 0x83, 0x85, 0x86, 0x87, 0x89, 0x8a, 0x8c, 0x8d, 0x8e, 0x90, 0x91, 0x92, 0x93, 0x95, + 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9e, 0x9f, 0xa0, 0xa1, 0xa1, 0xa2, 0xa3 }, + { 0x80, 0x81, 0x83, 0x84, 0x86, 0x87, 0x89, 0x8a, 0x8b, 0x8d, 0x8e, 0x8f, 0x90, 0x92, 0x93, 0x94, + 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9e, 0x9f, 0xa0, 0xa1, 0xa1, 0xa2 }, + { 0x80, 0x81, 0x83, 0x84, 0x86, 0x87, 0x88, 0x8a, 0x8b, 0x8c, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, + 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0x9f, 0xa0, 0xa1, 0xa1 }, + { 0x80, 0x81, 0x83, 0x84, 0x85, 0x87, 0x88, 0x89, 0x8b, 0x8c, 0x8d, 0x8e, 0x90, 0x91, 0x92, 0x93, + 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0x9f, 0xa0, 0xa1 }, + { 0x80, 0x81, 0x83, 0x84, 0x85, 0x87, 0x88, 0x89, 0x8a, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, + 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9c, 0x9d, 0x9e, 0x9f, 0x9f, 0xa0 } +}; + +#endif diff --git a/asnes/chip/st0010/opcodes.cpp b/asnes/chip/st0010/opcodes.cpp new file mode 100755 index 00000000..e6aa5419 --- /dev/null +++ b/asnes/chip/st0010/opcodes.cpp @@ -0,0 +1,261 @@ +#ifdef ST0010_CPP + +//ST-0010 emulation code - Copyright (C) 2003 The Dumper, Matthew Kendora, Overload, Feather +//bsnes port - Copyright (C) 2007 byuu + +void ST0010::op_01(int16 x0, int16 y0, int16 &x1, int16 &y1, int16 &quadrant, int16 &theta) { + if((x0 < 0) && (y0 < 0)) { + x1 = -x0; + y1 = -y0; + quadrant = -0x8000; + } else if(x0 < 0) { + x1 = y0; + y1 = -x0; + quadrant = -0x4000; + } else if(y0 < 0) { + x1 = -y0; + y1 = x0; + quadrant = 0x4000; + } else { + x1 = x0; + y1 = y0; + quadrant = 0x0000; + } + + while((x1 > 0x1f) || (y1 > 0x1f)) { + if(x1 > 1) { x1 >>= 1; } + if(y1 > 1) { y1 >>= 1; } + } + + if(y1 == 0) { quadrant += 0x4000; } + + theta = (arctan[y1][x1] << 8) ^ quadrant; +} + +// + +void ST0010::op_01() { + int16 x0 = readw(0x0000); + int16 y0 = readw(0x0002); + int16 x1, y1, quadrant, theta; + + op_01(x0, y0, x1, y1, quadrant, theta); + + writew(0x0000, x1); + writew(0x0002, y1); + writew(0x0004, quadrant); +//writew(0x0006, y0); //Overload's docs note this write occurs, SNES9x disagrees + writew(0x0010, theta); +} + +void ST0010::op_02() { + int16 positions = readw(0x0024); + uint16 *places = (uint16*)(ram + 0x0040); + uint16 *drivers = (uint16*)(ram + 0x0080); + + bool sorted; + uint16 temp; + if(positions > 1) { + do { + sorted = true; + for(int i = 0; i < positions - 1; i++) { + if(places[i] < places[i + 1]) { + temp = places[i + 1]; + places[i + 1] = places[i]; + places[i] = temp; + + temp = drivers[i + 1]; + drivers[i + 1] = drivers[i]; + drivers[i] = temp; + + sorted = false; + } + } + positions--; + } while(!sorted); + } +} + +void ST0010::op_03() { + int16 x0 = readw(0x0000); + int16 y0 = readw(0x0002); + int16 multiplier = readw(0x0004); + int32 x1, y1; + + x1 = x0 * multiplier << 1; + y1 = y0 * multiplier << 1; + + writed(0x0010, x1); + writed(0x0014, y1); +} + +void ST0010::op_04() { + int16 x = readw(0x0000); + int16 y = readw(0x0002); + int16 square; + //calculate the vector length of (x,y) + square = (int16)sqrt((double)(y * y + x * x)); + + writew(0x0010, square); +} + +void ST0010::op_05() { + int32 dx, dy; + int16 a1, b1, c1; + uint16 o1; + bool wrap = false; + + //target (x,y) coordinates + int16 ypos_max = readw(0x00c0); + int16 xpos_max = readw(0x00c2); + + //current coordinates and direction + int32 ypos = readd(0x00c4); + int32 xpos = readd(0x00c8); + uint16 rot = readw(0x00cc); + + //physics + uint16 speed = readw(0x00d4); + uint16 accel = readw(0x00d6); + uint16 speed_max = readw(0x00d8); + + //special condition acknowledgement + int16 system = readw(0x00da); + int16 flags = readw(0x00dc); + + //new target coordinates + int16 ypos_new = readw(0x00de); + int16 xpos_new = readw(0x00e0); + + //mask upper bit + xpos_new &= 0x7fff; + + //get the current distance + dx = xpos_max - (xpos >> 16); + dy = ypos_max - (ypos >> 16); + + //quirk: clear and move in9 + writew(0x00d2, 0xffff); + writew(0x00da, 0x0000); + + //grab the target angle + op_01(dy, dx, a1, b1, c1, (int16&)o1); + + //check for wrapping + if(abs(o1 - rot) > 0x8000) { + o1 += 0x8000; + rot += 0x8000; + wrap = true; + } + + uint16 old_speed = speed; + + //special case + if(abs(o1 - rot) == 0x8000) { + speed = 0x100; + } + + //slow down for sharp curves + else if(abs(o1 - rot) >= 0x1000) { + uint32 slow = abs(o1 - rot); + slow >>= 4; //scaling + speed -= slow; + } + + //otherwise accelerate + else { + speed += accel; + if(speed > speed_max) { + speed = speed_max; //clip speed + } + } + + //prevent negative/positive overflow + if(abs(old_speed - speed) > 0x8000) { + if(old_speed < speed) { speed = 0; } + else speed = 0xff00; + } + + //adjust direction by so many degrees + //be careful of negative adjustments + if((o1 > rot && (o1 - rot) > 0x80) || (o1 < rot && (rot - o1) >= 0x80)) { + if(o1 < rot) { rot -= 0x280; } + else if(o1 > rot) { rot += 0x280; } + } + + //turn off wrapping + if(wrap) { rot -= 0x8000; } + + //now check the distances (store for later) + dx = (xpos_max << 16) - xpos; + dy = (ypos_max << 16) - ypos; + dx >>= 16; + dy >>= 16; + + //if we're in so many units of the target, signal it + if((system && (dy <= 6 && dy >= -8) && (dx <= 126 && dx >= -128)) || (!system && (dx <= 6 && dx >= -8) && (dy <= 126 && dy >= -128))) { + //announce our new destination and flag it + xpos_max = xpos_new & 0x7fff; + ypos_max = ypos_new; + flags |= 0x08; + } + + //update position + xpos -= (cos(rot) * 0x400 >> 15) * (speed >> 8) << 1; + ypos -= (sin(rot) * 0x400 >> 15) * (speed >> 8) << 1; + + //quirk: mask upper byte + xpos &= 0x1fffffff; + ypos &= 0x1fffffff; + + writew(0x00c0, ypos_max); + writew(0x00c2, xpos_max); + writed(0x00c4, ypos); + writed(0x00c8, xpos); + writew(0x00cc, rot); + writew(0x00d4, speed); + writew(0x00dc, flags); +} + +void ST0010::op_06() { + int16 multiplicand = readw(0x0000); + int16 multiplier = readw(0x0002); + int32 product; + + product = multiplicand * multiplier << 1; + + writed(0x0010, product); +} + +void ST0010::op_07() { + int16 theta = readw(0x0000); + + int16 data; + for(int i = 0, offset = 0; i < 176; i++) { + data = mode7_scale[i] * cos(theta) >> 15; + writew(0x00f0 + offset, data); + writew(0x0510 + offset, data); + + data = mode7_scale[i] * sin(theta) >> 15; + writew(0x0250 + offset, data); + if(data) { data = ~data; } + writew(0x03b0 + offset, data); + + offset += 2; + } +} + +void ST0010::op_08() { + int16 x0 = readw(0x0000); + int16 y0 = readw(0x0002); + int16 theta = readw(0x0004); + int16 x1, y1; + + x1 = (y0 * sin(theta) >> 15) + (x0 * cos(theta) >> 15); + y1 = (y0 * cos(theta) >> 15) - (x0 * sin(theta) >> 15); + + writew(0x0010, x1); + writew(0x0012, y1); +} + +#endif diff --git a/asnes/chip/st0010/serialization.cpp b/asnes/chip/st0010/serialization.cpp new file mode 100755 index 00000000..5c21f644 --- /dev/null +++ b/asnes/chip/st0010/serialization.cpp @@ -0,0 +1,7 @@ +#ifdef ST0010_CPP + +void ST0010::serialize(serializer &s) { + s.array(ram); +} + +#endif diff --git a/asnes/chip/st0010/st0010.cpp b/asnes/chip/st0010/st0010.cpp new file mode 100755 index 00000000..295d0f5a --- /dev/null +++ b/asnes/chip/st0010/st0010.cpp @@ -0,0 +1,93 @@ +#include + +#define ST0010_CPP +namespace SNES { + +ST0010 st0010; + +#include "data.hpp" +#include "opcodes.cpp" +#include "serialization.cpp" + +void ST0010::init() { +} + +void ST0010::enable() { +} + +int16 ST0010::sin(int16 theta) { + return sin_table[(theta >> 8) & 0xff]; +} + +int16 ST0010::cos(int16 theta) { + return sin_table[((theta + 0x4000) >> 8) & 0xff]; +} + +uint8 ST0010::readb(uint16 addr) { + return ram[addr & 0xfff]; +} + +uint16 ST0010::readw(uint16 addr) { + return (readb(addr + 0) << 0) | + (readb(addr + 1) << 8); +} + +uint32 ST0010::readd(uint16 addr) { + return (readb(addr + 0) << 0) | + (readb(addr + 1) << 8) | + (readb(addr + 2) << 16) | + (readb(addr + 3) << 24); +} + +void ST0010::writeb(uint16 addr, uint8 data) { + ram[addr & 0xfff] = data; +} + +void ST0010::writew(uint16 addr, uint16 data) { + writeb(addr + 0, data >> 0); + writeb(addr + 1, data >> 8); +} + +void ST0010::writed(uint16 addr, uint32 data) { + writeb(addr + 0, data >> 0); + writeb(addr + 1, data >> 8); + writeb(addr + 2, data >> 16); + writeb(addr + 3, data >> 24); +} + +// + +void ST0010::power() { + reset(); +} + +void ST0010::reset() { + memset(ram, 0x00, sizeof ram); +} + +// + +uint8 ST0010::read(unsigned addr) { + return readb(addr); +} + +void ST0010::write(unsigned addr, uint8 data) { + writeb(addr, data); + + if((addr & 0xfff) == 0x0021 && (data & 0x80)) { + switch(ram[0x0020]) { + case 0x01: op_01(); break; + case 0x02: op_02(); break; + case 0x03: op_03(); break; + case 0x04: op_04(); break; + case 0x05: op_05(); break; + case 0x06: op_06(); break; + case 0x07: op_07(); break; + case 0x08: op_08(); break; + } + + ram[0x0021] &= ~0x80; + } +} + +} diff --git a/asnes/chip/st0010/st0010.hpp b/asnes/chip/st0010/st0010.hpp new file mode 100755 index 00000000..cceb0666 --- /dev/null +++ b/asnes/chip/st0010/st0010.hpp @@ -0,0 +1,44 @@ +class ST0010 : public Memory { +public: + void init(); + void enable(); + void power(); + void reset(); + + uint8 read(unsigned addr); + void write(unsigned addr, uint8 data); + + void serialize(serializer&); + +private: + uint8 ram[0x1000]; + static const int16 sin_table[256]; + static const int16 mode7_scale[176]; + static const uint8 arctan[32][32]; + + //interfaces to sin table + int16 sin(int16 theta); + int16 cos(int16 theta); + + //interfaces to ram buffer + uint8 readb (uint16 addr); + uint16 readw (uint16 addr); + uint32 readd (uint16 addr); + void writeb(uint16 addr, uint8 data); + void writew(uint16 addr, uint16 data); + void writed(uint16 addr, uint32 data); + + //opcodes + void op_01(); + void op_02(); + void op_03(); + void op_04(); + void op_05(); + void op_06(); + void op_07(); + void op_08(); + + void op_01(int16 x0, int16 y0, int16 &x1, int16 &y1, int16 &quadrant, int16 &theta); +}; + +extern ST0010 st0010; diff --git a/asnes/chip/st0011/st0011.cpp b/asnes/chip/st0011/st0011.cpp new file mode 100755 index 00000000..fe8e9c23 --- /dev/null +++ b/asnes/chip/st0011/st0011.cpp @@ -0,0 +1,20 @@ +#include + +#define ST0011_CPP +namespace SNES { + +ST0011 st0011; + +void ST0011::init() { +} + +void ST0011::enable() { +} + +void ST0011::power() { +} + +void ST0011::reset() { +} + +} diff --git a/asnes/chip/st0011/st0011.hpp b/asnes/chip/st0011/st0011.hpp new file mode 100755 index 00000000..c605c075 --- /dev/null +++ b/asnes/chip/st0011/st0011.hpp @@ -0,0 +1,9 @@ +class ST0011 { +public: + void init(); + void enable(); + void power(); + void reset(); +}; + +extern ST0011 st0011; diff --git a/asnes/chip/st0018/st0018.cpp b/asnes/chip/st0018/st0018.cpp new file mode 100755 index 00000000..03ef4d0f --- /dev/null +++ b/asnes/chip/st0018/st0018.cpp @@ -0,0 +1,123 @@ +#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::enable() { +} + +void ST0018::power() { + reset(); +} + +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/asnes/chip/st0018/st0018.hpp b/asnes/chip/st0018/st0018.hpp new file mode 100755 index 00000000..8a92e934 --- /dev/null +++ b/asnes/chip/st0018/st0018.hpp @@ -0,0 +1,51 @@ +class ST0018 : public MMIO { +public: + void init(); + void enable(); + 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/asnes/chip/superfx/bus/bus.cpp b/asnes/chip/superfx/bus/bus.cpp new file mode 100755 index 00000000..8e040308 --- /dev/null +++ b/asnes/chip/superfx/bus/bus.cpp @@ -0,0 +1,97 @@ +#ifdef SUPERFX_CPP + +SuperFXBus superfxbus; + +namespace memory { + SuperFXGSUROM gsurom; + SuperFXGSURAM gsuram; + SuperFXCPUROM fxrom; + SuperFXCPURAM fxram; +} + +void SuperFXBus::init() { + map(MapMode::Direct, 0x00, 0xff, 0x0000, 0xffff, memory::memory_unmapped); + + map(MapMode::Linear, 0x00, 0x3f, 0x0000, 0x7fff, memory::gsurom); + map(MapMode::Linear, 0x00, 0x3f, 0x8000, 0xffff, memory::gsurom); + map(MapMode::Linear, 0x40, 0x5f, 0x0000, 0xffff, memory::gsurom); + map(MapMode::Linear, 0x60, 0x7f, 0x0000, 0xffff, memory::gsuram); +} + +//ROM / RAM access from the SuperFX CPU + +unsigned SuperFXGSUROM::size() const { + return memory::cartrom.size(); +} + +uint8 SuperFXGSUROM::read(unsigned addr) { + while(!superfx.regs.scmr.ron && scheduler.sync != Scheduler::SynchronizeMode::All) { + superfx.add_clocks(6); + superfx.synchronize_cpu(); + } + return memory::cartrom.read(addr); +} + +void SuperFXGSUROM::write(unsigned addr, uint8 data) { + while(!superfx.regs.scmr.ron && scheduler.sync != Scheduler::SynchronizeMode::All) { + superfx.add_clocks(6); + superfx.synchronize_cpu(); + } + memory::cartrom.write(addr, data); +} + +unsigned SuperFXGSURAM::size() const { + return memory::cartram.size(); +} + +uint8 SuperFXGSURAM::read(unsigned addr) { + while(!superfx.regs.scmr.ran && scheduler.sync != Scheduler::SynchronizeMode::All) { + superfx.add_clocks(6); + superfx.synchronize_cpu(); + } + return memory::cartram.read(addr); +} + +void SuperFXGSURAM::write(unsigned addr, uint8 data) { + while(!superfx.regs.scmr.ran && scheduler.sync != Scheduler::SynchronizeMode::All) { + superfx.add_clocks(6); + superfx.synchronize_cpu(); + } + memory::cartram.write(addr, data); +} + +//ROM / RAM access from the S-CPU + +unsigned SuperFXCPUROM::size() const { + return memory::cartrom.size(); +} + +uint8 SuperFXCPUROM::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 memory::cartrom.read(addr); +} + +void SuperFXCPUROM::write(unsigned addr, uint8 data) { + memory::cartrom.write(addr, data); +} + +unsigned SuperFXCPURAM::size() const { + return memory::cartram.size(); +} + +uint8 SuperFXCPURAM::read(unsigned addr) { + if(superfx.regs.sfr.g && superfx.regs.scmr.ran) return cpu.regs.mdr; + return memory::cartram.read(addr); +} + +void SuperFXCPURAM::write(unsigned addr, uint8 data) { + memory::cartram.write(addr, data); +} + +#endif diff --git a/asnes/chip/superfx/bus/bus.hpp b/asnes/chip/superfx/bus/bus.hpp new file mode 100755 index 00000000..dd615c65 --- /dev/null +++ b/asnes/chip/superfx/bus/bus.hpp @@ -0,0 +1,34 @@ +struct SuperFXBus : Bus { + void init(); +}; + +struct SuperFXGSUROM : Memory { + unsigned size() const; + uint8 read(unsigned); + void write(unsigned, uint8); +}; + +struct SuperFXGSURAM : Memory { + unsigned size() const; + uint8 read(unsigned); + void write(unsigned, uint8); +}; + +struct SuperFXCPUROM : Memory { + unsigned size() const; + uint8 read(unsigned); + void write(unsigned, uint8); +}; + +struct SuperFXCPURAM : Memory { + unsigned size() const; + uint8 read(unsigned); + void write(unsigned, uint8); +}; + +namespace memory { + extern SuperFXGSUROM gsurom; + extern SuperFXGSURAM gsuram; + extern SuperFXCPUROM fxrom; + extern SuperFXCPURAM fxram; +} diff --git a/asnes/chip/superfx/core/core.cpp b/asnes/chip/superfx/core/core.cpp new file mode 100755 index 00000000..229ad24e --- /dev/null +++ b/asnes/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 |= ((superfxbus.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 |= superfxbus.read(addr + byte) & ~cache.bitpend; + } + add_clocks(memory_access_speed); + superfxbus.write(addr + byte, data); + } + + cache.bitpend = 0x00; +} + +#endif diff --git a/asnes/chip/superfx/core/core.hpp b/asnes/chip/superfx/core/core.hpp new file mode 100755 index 00000000..1c13f1ce --- /dev/null +++ b/asnes/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/asnes/chip/superfx/core/opcode_table.cpp b/asnes/chip/superfx/core/opcode_table.cpp new file mode 100755 index 00000000..79082531 --- /dev/null +++ b/asnes/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/asnes/chip/superfx/core/opcodes.cpp b/asnes/chip/superfx/core/opcodes.cpp new file mode 100755 index 00000000..7d2f13a2 --- /dev/null +++ b/asnes/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/asnes/chip/superfx/core/registers.hpp b/asnes/chip/superfx/core/registers.hpp new file mode 100755 index 00000000..ac193a8a --- /dev/null +++ b/asnes/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&) = delete; +}; + +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/asnes/chip/superfx/disasm/disasm.cpp b/asnes/chip/superfx/disasm/disasm.cpp new file mode 100755 index 00000000..c2d3e387 --- /dev/null +++ b/asnes/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 superfxbus.read((regs.pbr << 16) + regs.r[15] + 0) +#define op2 superfxbus.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/asnes/chip/superfx/disasm/disasm.hpp b/asnes/chip/superfx/disasm/disasm.hpp new file mode 100755 index 00000000..903615eb --- /dev/null +++ b/asnes/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/asnes/chip/superfx/memory/memory.cpp b/asnes/chip/superfx/memory/memory.cpp new file mode 100755 index 00000000..9ae6091c --- /dev/null +++ b/asnes/chip/superfx/memory/memory.cpp @@ -0,0 +1,71 @@ +#ifdef SUPERFX_CPP + +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++] = superfxbus.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 superfxbus.read((regs.pbr << 16) + addr); + } else { + //$[60-7f]:[0000-ffff] RAM + rambuffer_sync(); + add_clocks(memory_access_speed); + return superfxbus.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() { + 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/asnes/chip/superfx/memory/memory.hpp b/asnes/chip/superfx/memory/memory.hpp new file mode 100755 index 00000000..3b11a3f9 --- /dev/null +++ b/asnes/chip/superfx/memory/memory.hpp @@ -0,0 +1,9 @@ +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/asnes/chip/superfx/mmio/mmio.cpp b/asnes/chip/superfx/mmio/mmio.cpp new file mode 100755 index 00000000..7bf43b1d --- /dev/null +++ b/asnes/chip/superfx/mmio/mmio.cpp @@ -0,0 +1,118 @@ +#ifdef SUPERFX_CPP + +uint8 SuperFX::mmio_read(unsigned addr) { + cpu.synchronize_coprocessor(); + 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_coprocessor(); + 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/asnes/chip/superfx/mmio/mmio.hpp b/asnes/chip/superfx/mmio/mmio.hpp new file mode 100755 index 00000000..08cc85a9 --- /dev/null +++ b/asnes/chip/superfx/mmio/mmio.hpp @@ -0,0 +1,2 @@ +uint8 mmio_read(unsigned addr); +void mmio_write(unsigned addr, uint8 data); diff --git a/asnes/chip/superfx/serialization.cpp b/asnes/chip/superfx/serialization.cpp new file mode 100755 index 00000000..67e5385b --- /dev/null +++ b/asnes/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/asnes/chip/superfx/superfx.cpp b/asnes/chip/superfx/superfx.cpp new file mode 100755 index 00000000..89eaf18b --- /dev/null +++ b/asnes/chip/superfx/superfx.cpp @@ -0,0 +1,81 @@ +#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 == 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 = { &SuperFX::r14_modify, this }; + regs.r[15].on_modify = { &SuperFX::r15_modify, this }; +} + +void SuperFX::enable() { +} + +void SuperFX::power() { + clockmode = config.superfx.speed; + reset(); +} + +void SuperFX::reset() { + create(SuperFX::Enter, system.cpu_frequency()); + superfxbus.init(); + 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/asnes/chip/superfx/superfx.hpp b/asnes/chip/superfx/superfx.hpp new file mode 100755 index 00000000..ac2059fb --- /dev/null +++ b/asnes/chip/superfx/superfx.hpp @@ -0,0 +1,25 @@ +#include "bus/bus.hpp" + +class SuperFX : public Coprocessor, public MMIO { +public: + #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 enable(); + void power(); + void reset(); + void serialize(serializer&); + +private: + unsigned clockmode; + unsigned instruction_counter; +}; + +extern SuperFX superfx; +extern SuperFXBus superfxbus; diff --git a/asnes/chip/superfx/timing/timing.cpp b/asnes/chip/superfx/timing/timing.cpp new file mode 100755 index 00000000..aae7820b --- /dev/null +++ b/asnes/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 = superfxbus.read((regs.rombr << 16) + regs.r[14]); + } + } + + if(regs.ramcl) { + regs.ramcl -= min(clocks, regs.ramcl); + if(regs.ramcl == 0) { + superfxbus.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 superfxbus.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/asnes/chip/superfx/timing/timing.hpp b/asnes/chip/superfx/timing/timing.hpp new file mode 100755 index 00000000..9ae7e8d4 --- /dev/null +++ b/asnes/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/asnes/chip/supergameboy/serialization.cpp b/asnes/chip/supergameboy/serialization.cpp new file mode 100755 index 00000000..727bdff4 --- /dev/null +++ b/asnes/chip/supergameboy/serialization.cpp @@ -0,0 +1,9 @@ +#ifdef SUPERGAMEBOY_CPP + +void SuperGameBoy::serialize(serializer &s) { + Processor::serialize(s); + s.integer(row); + if(sgb_serialize) sgb_serialize(s); +} + +#endif diff --git a/asnes/chip/supergameboy/supergameboy.cpp b/asnes/chip/supergameboy/supergameboy.cpp new file mode 100755 index 00000000..eee2c8c9 --- /dev/null +++ b/asnes/chip/supergameboy/supergameboy.cpp @@ -0,0 +1,142 @@ +#include + +#define SUPERGAMEBOY_CPP +namespace SNES { + +SuperGameBoy supergameboy; + +#include "serialization.cpp" + +void SuperGameBoy::Enter() { supergameboy.enter(); } + +void SuperGameBoy::enter() { + if(!sgb_run) while(true) { + if(scheduler.sync == Scheduler::SynchronizeMode::All) { + scheduler.exit(Scheduler::ExitReason::SynchronizeEvent); + } + + audio.coprocessor_sample(0, 0); + step(1); + synchronize_cpu(); + } + + while(true) { + if(scheduler.sync == Scheduler::SynchronizeMode::All) { + scheduler.exit(Scheduler::ExitReason::SynchronizeEvent); + } + + unsigned samples = sgb_run(samplebuffer, 16); + for(unsigned i = 0; i < samples; i++) { + int16 left = samplebuffer[i] >> 0; + int16 right = samplebuffer[i] >> 16; + + //SNES audio is notoriously quiet; lower Game Boy samples to match SGB sound effects + audio.coprocessor_sample(left / 3, right / 3); + } + + step(samples); + synchronize_cpu(); + } +} + +void SuperGameBoy::save() { + if(sgb_save) sgb_save(); +} + +uint8 SuperGameBoy::mmio_read(unsigned addr) { + addr &= 0xffff; + + if(addr == 0x2181) return mmio[0]->mmio_read(addr); + if(addr == 0x2182) return mmio[1]->mmio_read(addr); + if(addr == 0x420b) return mmio[2]->mmio_read(addr); + + return 0x00; +} + +void SuperGameBoy::mmio_write(unsigned addr, uint8 data) { + addr &= 0xffff; + + if(addr == 0x2181) { + row = (row & 0xff00) | (data << 0); + mmio[0]->mmio_write(addr, data); + } + + if(addr == 0x2182) { + row = (row & 0x00ff) | (data << 8); + mmio[1]->mmio_write(addr, data); + } + + if(addr == 0x420b) { + if(data == 0x10 && sgb_row) { + if(row >= 0x5000 && row <= 0x6540) sgb_row((row - 0x5000) / 320); + if(row >= 0x6800 && row <= 0x7d40) sgb_row((row - 0x6800) / 320); + } + mmio[2]->mmio_write(addr, data); + } +} + +uint8 SuperGameBoy::read(unsigned addr) { + if(sgb_read) return sgb_read(addr); + return 0x00; +} + +void SuperGameBoy::write(unsigned addr, uint8 data) { + if(sgb_write) sgb_write(addr, data); +} + +void SuperGameBoy::init() { + if(open("supergameboy")) { + sgb_rom = sym("sgb_rom"); + sgb_ram = sym("sgb_ram"); + sgb_rtc = sym("sgb_rtc"); + sgb_init = sym("sgb_init"); + sgb_term = sym("sgb_term"); + sgb_power = sym("sgb_power"); + sgb_reset = sym("sgb_reset"); + sgb_row = sym("sgb_row"); + sgb_read = sym("sgb_read"); + sgb_write = sym("sgb_write"); + sgb_run = sym("sgb_run"); + sgb_save = sym("sgb_save"); + sgb_serialize = sym("sgb_serialize"); + } +} + +void SuperGameBoy::enable() { + mmio[0] = memory::mmio.mmio[0x2181 - 0x2000]; + mmio[1] = memory::mmio.mmio[0x2182 - 0x2000]; + mmio[2] = memory::mmio.mmio[0x420b - 0x2000]; + + memory::mmio.map(0x2181, *this); + memory::mmio.map(0x2182, *this); + memory::mmio.map(0x420b, *this); +} + +void SuperGameBoy::power() { + unsigned frequency = (cartridge.supergameboy_version() == Cartridge::SuperGameBoyVersion::Version1 ? system.cpu_frequency() / 10 : 2097152); + create(SuperGameBoy::Enter, frequency); + + audio.coprocessor_enable(true); + audio.coprocessor_frequency(cartridge.supergameboy_version() == Cartridge::SuperGameBoyVersion::Version1 ? 2147727.0 : 2097152.0); + + sgb_rom(memory::gbrom.data(), memory::gbrom.size() == -1U ? 0 : memory::gbrom.size()); + sgb_ram(memory::gbram.data(), memory::gbram.size() == -1U ? 0 : memory::gbram.size()); + sgb_rtc(memory::gbrtc.data(), memory::gbrtc.size() == -1U ? 0 : memory::gbrtc.size()); + + bool version = (cartridge.supergameboy_version() == Cartridge::SuperGameBoyVersion::Version1) ? 0 : 1; + if(sgb_init) sgb_init(version); + if(sgb_power) sgb_power(); +} + +void SuperGameBoy::reset() { + unsigned frequency = (cartridge.supergameboy_version() == Cartridge::SuperGameBoyVersion::Version1 ? system.cpu_frequency() / 10 : 2097152); + create(SuperGameBoy::Enter, frequency); + + if(sgb_reset) sgb_reset(); +} + +void SuperGameBoy::unload() { + if(sgb_term) sgb_term(); +} + +} diff --git a/asnes/chip/supergameboy/supergameboy.hpp b/asnes/chip/supergameboy/supergameboy.hpp new file mode 100755 index 00000000..bf392b11 --- /dev/null +++ b/asnes/chip/supergameboy/supergameboy.hpp @@ -0,0 +1,43 @@ +class SuperGameBoy : public Coprocessor, public MMIO, public Memory, public library { +public: + static void Enter(); + void enter(); + void save(); + + MMIO *mmio[3]; + uint8 mmio_read(unsigned addr); + void mmio_write(unsigned addr, uint8 data); + + uint8 read(unsigned addr); + void write(unsigned addr, uint8 data); + + void init(); + void enable(); + void power(); + void reset(); + void unload(); + + void serialize(serializer&); + +private: + uint32_t samplebuffer[4096]; + unsigned row; + + function sgb_rom; + function sgb_ram; + function sgb_rtc; + function sgb_init; + function sgb_term; + function sgb_power; + function sgb_reset; + function sgb_row; + function sgb_read; + function sgb_write; + function sgb_run; + function sgb_save; + function sgb_serialize; + + friend class Cartridge; +}; + +extern SuperGameBoy supergameboy; diff --git a/asnes/config/config.cpp b/asnes/config/config.cpp new file mode 100755 index 00000000..404e20b9 --- /dev/null +++ b/asnes/config/config.cpp @@ -0,0 +1,25 @@ +#ifdef SYSTEM_CPP + +Configuration config; + +Configuration::Configuration() { + controller_port1 = Input::Device::Joypad; + controller_port2 = Input::Device::Joypad; + expansion_port = System::ExpansionPortDevice::BSX; + region = System::Region::Autodetect; + + cpu.version = 2; + cpu.ntsc_frequency = 21477272; + 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/asnes/config/config.hpp b/asnes/config/config.hpp new file mode 100755 index 00000000..7188d209 --- /dev/null +++ b/asnes/config/config.hpp @@ -0,0 +1,34 @@ +struct Configuration { + Input::Device controller_port1; + Input::Device controller_port2; + System::ExpansionPortDevice expansion_port; + System::Region region; + + 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/asnes/cpu/core/algorithms.cpp b/asnes/cpu/core/algorithms.cpp new file mode 100755 index 00000000..9b6ba032 --- /dev/null +++ b/asnes/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/asnes/cpu/core/core.cpp b/asnes/cpu/core/core.cpp new file mode 100755 index 00000000..aae4ba67 --- /dev/null +++ b/asnes/cpu/core/core.cpp @@ -0,0 +1,74 @@ +#include + +#define CPUCORE_CPP +namespace SNES { + +#include "serialization.cpp" +#include "algorithms.cpp" +#include "disassembler/disassembler.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(); + } +} + +CPUcore::CPUcore() { + initialize_opcode_table(); +} + +} diff --git a/asnes/cpu/core/core.hpp b/asnes/cpu/core/core.hpp new file mode 100755 index 00000000..6d1314aa --- /dev/null +++ b/asnes/cpu/core/core.hpp @@ -0,0 +1,217 @@ +class CPUcore { +public: + #include "registers.hpp" + #include "memory.hpp" + #include "disassembler/disassembler.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; + + 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/asnes/cpu/core/disassembler/disassembler.cpp b/asnes/cpu/core/disassembler/disassembler.cpp new file mode 100755 index 00000000..030b3ab5 --- /dev/null +++ b/asnes/cpu/core/disassembler/disassembler.cpp @@ -0,0 +1,483 @@ +#ifdef CPUCORE_CPP + +uint8 CPUcore::dreadb(uint32 addr) { + if((addr & 0x40ffff) >= 0x2000 && (addr & 0x40ffff) <= 0x5fff) { + //$[00-3f|80-bf]:[2000-5fff] + //do not read MMIO registers within debugger + return 0x00; + } + return bus.read(addr); +} + +uint16 CPUcore::dreadw(uint32 addr) { + uint16 r; + r = dreadb((addr + 0) & 0xffffff) << 0; + r |= dreadb((addr + 1) & 0xffffff) << 8; + return r; +} + +uint32 CPUcore::dreadl(uint32 addr) { + uint32 r; + r = dreadb((addr + 0) & 0xffffff) << 0; + r |= dreadb((addr + 1) & 0xffffff) << 8; + r |= dreadb((addr + 2) & 0xffffff) << 16; + return r; +} + +uint32 CPUcore::decode(uint8 offset_type, uint32 addr) { + uint32 r = 0; + + switch(offset_type) { + case OPTYPE_DP: + r = (regs.d + (addr & 0xffff)) & 0xffff; + break; + case OPTYPE_DPX: + r = (regs.d + regs.x + (addr & 0xffff)) & 0xffff; + break; + case OPTYPE_DPY: + r = (regs.d + regs.y + (addr & 0xffff)) & 0xffff; + break; + case OPTYPE_IDP: + addr = (regs.d + (addr & 0xffff)) & 0xffff; + r = (regs.db << 16) + dreadw(addr); + break; + case OPTYPE_IDPX: + addr = (regs.d + regs.x + (addr & 0xffff)) & 0xffff; + r = (regs.db << 16) + dreadw(addr); + break; + case OPTYPE_IDPY: + addr = (regs.d + (addr & 0xffff)) & 0xffff; + r = (regs.db << 16) + dreadw(addr) + regs.y; + break; + case OPTYPE_ILDP: + addr = (regs.d + (addr & 0xffff)) & 0xffff; + r = dreadl(addr); + break; + case OPTYPE_ILDPY: + addr = (regs.d + (addr & 0xffff)) & 0xffff; + r = dreadl(addr) + regs.y; + break; + case OPTYPE_ADDR: + r = (regs.db << 16) + (addr & 0xffff); + break; + case OPTYPE_ADDR_PC: + r = (regs.pc.b << 16) + (addr & 0xffff); + break; + case OPTYPE_ADDRX: + r = (regs.db << 16) + (addr & 0xffff) + regs.x; + break; + case OPTYPE_ADDRY: + r = (regs.db << 16) + (addr & 0xffff) + regs.y; + break; + case OPTYPE_IADDR_PC: + r = (regs.pc.b << 16) + (addr & 0xffff); + break; + case OPTYPE_IADDRX: + r = (regs.pc.b << 16) + ((addr + regs.x) & 0xffff); + break; + case OPTYPE_ILADDR: + r = addr; + break; + case OPTYPE_LONG: + r = addr; + break; + case OPTYPE_LONGX: + r = (addr + regs.x); + break; + case OPTYPE_SR: + r = (regs.s + (addr & 0xff)) & 0xffff; + break; + case OPTYPE_ISRY: + addr = (regs.s + (addr & 0xff)) & 0xffff; + r = (regs.db << 16) + dreadw(addr) + regs.y; + break; + case OPTYPE_RELB: + r = (regs.pc.b << 16) + ((regs.pc.w + 2) & 0xffff); + r += int8(addr); + break; + case OPTYPE_RELW: + r = (regs.pc.b << 16) + ((regs.pc.w + 3) & 0xffff); + r += int16(addr); + break; + } + + return(r & 0xffffff); +} + +void CPUcore::disassemble_opcode(char *output, uint32 addr) { + static reg24_t pc; + char t[256]; + char *s = output; + + if(false /* in_opcode() == true */) { + strcpy(s, "?????? "); + return; + } + + pc.d = addr; + sprintf(s, "%.6x ", (uint32)pc.d); + + uint8 op = dreadb(pc.d); pc.w++; + uint8 op0 = dreadb(pc.d); pc.w++; + uint8 op1 = dreadb(pc.d); pc.w++; + uint8 op2 = dreadb(pc.d); + + #define op8 ((op0)) + #define op16 ((op0) | (op1 << 8)) + #define op24 ((op0) | (op1 << 8) | (op2 << 16)) + #define a8 (regs.e || regs.p.m) + #define x8 (regs.e || regs.p.x) + + switch(op) { + case 0x00: sprintf(t, "brk #$%.2x ", op8); break; + case 0x01: sprintf(t, "ora ($%.2x,x) [%.6x]", op8, decode(OPTYPE_IDPX, op8)); break; + case 0x02: sprintf(t, "cop #$%.2x ", op8); break; + case 0x03: sprintf(t, "ora $%.2x,s [%.6x]", op8, decode(OPTYPE_SR, op8)); break; + case 0x04: sprintf(t, "tsb $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break; + case 0x05: sprintf(t, "ora $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break; + case 0x06: sprintf(t, "asl $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break; + case 0x07: sprintf(t, "ora [$%.2x] [%.6x]", op8, decode(OPTYPE_ILDP, op8)); break; + case 0x08: sprintf(t, "php "); break; + case 0x09: if(a8)sprintf(t, "ora #$%.2x ", op8); + else sprintf(t, "ora #$%.4x ", op16); break; + case 0x0a: sprintf(t, "asl a "); break; + case 0x0b: sprintf(t, "phd "); break; + case 0x0c: sprintf(t, "tsb $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break; + case 0x0d: sprintf(t, "ora $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break; + case 0x0e: sprintf(t, "asl $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break; + case 0x0f: sprintf(t, "ora $%.6x [%.6x]", op24, decode(OPTYPE_LONG, op24)); break; + case 0x10: sprintf(t, "bpl $%.4x [%.6x]", uint16(decode(OPTYPE_RELB, op8)), decode(OPTYPE_RELB, op8)); break; + case 0x11: sprintf(t, "ora ($%.2x),y [%.6x]", op8, decode(OPTYPE_IDPY, op8)); break; + case 0x12: sprintf(t, "ora ($%.2x) [%.6x]", op8, decode(OPTYPE_IDP, op8)); break; + case 0x13: sprintf(t, "ora ($%.2x,s),y [%.6x]", op8, decode(OPTYPE_ISRY, op8)); break; + case 0x14: sprintf(t, "trb $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break; + case 0x15: sprintf(t, "ora $%.2x,x [%.6x]", op8, decode(OPTYPE_DPX, op8)); break; + case 0x16: sprintf(t, "asl $%.2x,x [%.6x]", op8, decode(OPTYPE_DPX, op8)); break; + case 0x17: sprintf(t, "ora [$%.2x],y [%.6x]", op8, decode(OPTYPE_ILDPY, op8)); break; + case 0x18: sprintf(t, "clc "); break; + case 0x19: sprintf(t, "ora $%.4x,y [%.6x]", op16, decode(OPTYPE_ADDRY, op16)); break; + case 0x1a: sprintf(t, "inc "); break; + case 0x1b: sprintf(t, "tcs "); break; + case 0x1c: sprintf(t, "trb $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break; + case 0x1d: sprintf(t, "ora $%.4x,x [%.6x]", op16, decode(OPTYPE_ADDRX, op16)); break; + case 0x1e: sprintf(t, "asl $%.4x,x [%.6x]", op16, decode(OPTYPE_ADDRX, op16)); break; + case 0x1f: sprintf(t, "ora $%.6x,x [%.6x]", op24, decode(OPTYPE_LONGX, op24)); break; + case 0x20: sprintf(t, "jsr $%.4x [%.6x]", op16, decode(OPTYPE_ADDR_PC, op16)); break; + case 0x21: sprintf(t, "and ($%.2x,x) [%.6x]", op8, decode(OPTYPE_IDPX, op8)); break; + case 0x22: sprintf(t, "jsl $%.6x [%.6x]", op24, decode(OPTYPE_LONG, op24)); break; + case 0x23: sprintf(t, "and $%.2x,s [%.6x]", op8, decode(OPTYPE_SR, op8)); break; + case 0x24: sprintf(t, "bit $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break; + case 0x25: sprintf(t, "and $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break; + case 0x26: sprintf(t, "rol $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break; + case 0x27: sprintf(t, "and [$%.2x] [%.6x]", op8, decode(OPTYPE_ILDP, op8)); break; + case 0x28: sprintf(t, "plp "); break; + case 0x29: if(a8)sprintf(t, "and #$%.2x ", op8); + else sprintf(t, "and #$%.4x ", op16); break; + case 0x2a: sprintf(t, "rol a "); break; + case 0x2b: sprintf(t, "pld "); break; + case 0x2c: sprintf(t, "bit $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break; + case 0x2d: sprintf(t, "and $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break; + case 0x2e: sprintf(t, "rol $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break; + case 0x2f: sprintf(t, "and $%.6x [%.6x]", op24, decode(OPTYPE_LONG, op24)); break; + case 0x30: sprintf(t, "bmi $%.4x [%.6x]", uint16(decode(OPTYPE_RELB, op8)), decode(OPTYPE_RELB, op8)); break; + case 0x31: sprintf(t, "and ($%.2x),y [%.6x]", op8, decode(OPTYPE_IDPY, op8)); break; + case 0x32: sprintf(t, "and ($%.2x) [%.6x]", op8, decode(OPTYPE_IDP, op8)); break; + case 0x33: sprintf(t, "and ($%.2x,s),y [%.6x]", op8, decode(OPTYPE_ISRY, op8)); break; + case 0x34: sprintf(t, "bit $%.2x,x [%.6x]", op8, decode(OPTYPE_DPX, op8)); break; + case 0x35: sprintf(t, "and $%.2x,x [%.6x]", op8, decode(OPTYPE_DPX, op8)); break; + case 0x36: sprintf(t, "rol $%.2x,x [%.6x]", op8, decode(OPTYPE_DPX, op8)); break; + case 0x37: sprintf(t, "and [$%.2x],y [%.6x]", op8, decode(OPTYPE_ILDPY, op8)); break; + case 0x38: sprintf(t, "sec "); break; + case 0x39: sprintf(t, "and $%.4x,y [%.6x]", op16, decode(OPTYPE_ADDRY, op16)); break; + case 0x3a: sprintf(t, "dec "); break; + case 0x3b: sprintf(t, "tsc "); break; + case 0x3c: sprintf(t, "bit $%.4x,x [%.6x]", op16, decode(OPTYPE_ADDRX, op16)); break; + case 0x3d: sprintf(t, "and $%.4x,x [%.6x]", op16, decode(OPTYPE_ADDRX, op16)); break; + case 0x3e: sprintf(t, "rol $%.4x,x [%.6x]", op16, decode(OPTYPE_ADDRX, op16)); break; + case 0x3f: sprintf(t, "and $%.6x,x [%.6x]", op24, decode(OPTYPE_LONGX, op24)); break; + case 0x40: sprintf(t, "rti "); break; + case 0x41: sprintf(t, "eor ($%.2x,x) [%.6x]", op8, decode(OPTYPE_IDPX, op8)); break; + case 0x42: sprintf(t, "wdm "); break; + case 0x43: sprintf(t, "eor $%.2x,s [%.6x]", op8, decode(OPTYPE_SR, op8)); break; + case 0x44: sprintf(t, "mvp $%.2x,$%.2x ", op1, op8); break; + case 0x45: sprintf(t, "eor $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break; + case 0x46: sprintf(t, "lsr $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break; + case 0x47: sprintf(t, "eor [$%.2x] [%.6x]", op8, decode(OPTYPE_ILDP, op8)); break; + case 0x48: sprintf(t, "pha "); break; + case 0x49: if(a8)sprintf(t, "eor #$%.2x ", op8); + else sprintf(t, "eor #$%.4x ", op16); break; + case 0x4a: sprintf(t, "lsr a "); break; + case 0x4b: sprintf(t, "phk "); break; + case 0x4c: sprintf(t, "jmp $%.4x [%.6x]", op16, decode(OPTYPE_ADDR_PC, op16)); break; + case 0x4d: sprintf(t, "eor $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break; + case 0x4e: sprintf(t, "lsr $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break; + case 0x4f: sprintf(t, "eor $%.6x [%.6x]", op24, decode(OPTYPE_LONG, op24)); break; + case 0x50: sprintf(t, "bvc $%.4x [%.6x]", uint16(decode(OPTYPE_RELB, op8)), decode(OPTYPE_RELB, op8)); break; + case 0x51: sprintf(t, "eor ($%.2x),y [%.6x]", op8, decode(OPTYPE_IDPY, op8)); break; + case 0x52: sprintf(t, "eor ($%.2x) [%.6x]", op8, decode(OPTYPE_IDP, op8)); break; + case 0x53: sprintf(t, "eor ($%.2x,s),y [%.6x]", op8, decode(OPTYPE_ISRY, op8)); break; + case 0x54: sprintf(t, "mvn $%.2x,$%.2x ", op1, op8); break; + case 0x55: sprintf(t, "eor $%.2x,x [%.6x]", op8, decode(OPTYPE_DPX, op8)); break; + case 0x56: sprintf(t, "lsr $%.2x,x [%.6x]", op8, decode(OPTYPE_DPX, op8)); break; + case 0x57: sprintf(t, "eor [$%.2x],y [%.6x]", op8, decode(OPTYPE_ILDPY, op8)); break; + case 0x58: sprintf(t, "cli "); break; + case 0x59: sprintf(t, "eor $%.4x,y [%.6x]", op16, decode(OPTYPE_ADDRY, op16)); break; + case 0x5a: sprintf(t, "phy "); break; + case 0x5b: sprintf(t, "tcd "); break; + case 0x5c: sprintf(t, "jml $%.6x [%.6x]", op24, decode(OPTYPE_LONG, op24)); break; + case 0x5d: sprintf(t, "eor $%.4x,x [%.6x]", op16, decode(OPTYPE_ADDRX, op16)); break; + case 0x5e: sprintf(t, "lsr $%.4x,x [%.6x]", op16, decode(OPTYPE_ADDRX, op16)); break; + case 0x5f: sprintf(t, "eor $%.6x,x [%.6x]", op24, decode(OPTYPE_LONGX, op24)); break; + case 0x60: sprintf(t, "rts "); break; + case 0x61: sprintf(t, "adc ($%.2x,x) [%.6x]", op8, decode(OPTYPE_IDPX, op8)); break; + case 0x62: sprintf(t, "per $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break; + case 0x63: sprintf(t, "adc $%.2x,s [%.6x]", op8, decode(OPTYPE_SR, op8)); break; + case 0x64: sprintf(t, "stz $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break; + case 0x65: sprintf(t, "adc $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break; + case 0x66: sprintf(t, "ror $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break; + case 0x67: sprintf(t, "adc [$%.2x] [%.6x]", op8, decode(OPTYPE_ILDP, op8)); break; + case 0x68: sprintf(t, "pla "); break; + case 0x69: if(a8)sprintf(t, "adc #$%.2x ", op8); + else sprintf(t, "adc #$%.4x ", op16); break; + case 0x6a: sprintf(t, "ror a "); break; + case 0x6b: sprintf(t, "rtl "); break; + case 0x6c: sprintf(t, "jmp ($%.4x) [%.6x]", op16, decode(OPTYPE_IADDR_PC, op16)); break; + case 0x6d: sprintf(t, "adc $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break; + case 0x6e: sprintf(t, "ror $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break; + case 0x6f: sprintf(t, "adc $%.6x [%.6x]", op24, decode(OPTYPE_LONG, op24)); break; + case 0x70: sprintf(t, "bvs $%.4x [%.6x]", uint16(decode(OPTYPE_RELB, op8)), decode(OPTYPE_RELB, op8)); break; + case 0x71: sprintf(t, "adc ($%.2x),y [%.6x]", op8, decode(OPTYPE_IDPY, op8)); break; + case 0x72: sprintf(t, "adc ($%.2x) [%.6x]", op8, decode(OPTYPE_IDP, op8)); break; + case 0x73: sprintf(t, "adc ($%.2x,s),y [%.6x]", op8, decode(OPTYPE_ISRY, op8)); break; + case 0x74: sprintf(t, "stz $%.2x,x [%.6x]", op8, decode(OPTYPE_DPX, op8)); break; + case 0x75: sprintf(t, "adc $%.2x,x [%.6x]", op8, decode(OPTYPE_DPX, op8)); break; + case 0x76: sprintf(t, "ror $%.2x,x [%.6x]", op8, decode(OPTYPE_DPX, op8)); break; + case 0x77: sprintf(t, "adc [$%.2x],y [%.6x]", op8, decode(OPTYPE_ILDPY, op8)); break; + case 0x78: sprintf(t, "sei "); break; + case 0x79: sprintf(t, "adc $%.4x,y [%.6x]", op16, decode(OPTYPE_ADDRY, op16)); break; + case 0x7a: sprintf(t, "ply "); break; + case 0x7b: sprintf(t, "tdc "); break; + case 0x7c: sprintf(t, "jmp ($%.4x,x) [%.6x]", op16, decode(OPTYPE_IADDRX, op16)); break; + case 0x7d: sprintf(t, "adc $%.4x,x [%.6x]", op16, decode(OPTYPE_ADDRX, op16)); break; + case 0x7e: sprintf(t, "ror $%.4x,x [%.6x]", op16, decode(OPTYPE_ADDRX, op16)); break; + case 0x7f: sprintf(t, "adc $%.6x,x [%.6x]", op24, decode(OPTYPE_LONGX, op24)); break; + case 0x80: sprintf(t, "bra $%.4x [%.6x]", uint16(decode(OPTYPE_RELB, op8)), decode(OPTYPE_RELB, op8)); break; + case 0x81: sprintf(t, "sta ($%.2x,x) [%.6x]", op8, decode(OPTYPE_IDPX, op8)); break; + case 0x82: sprintf(t, "brl $%.4x [%.6x]", uint16(decode(OPTYPE_RELW, op16)), decode(OPTYPE_RELW, op16)); break; + case 0x83: sprintf(t, "sta $%.2x,s [%.6x]", op8, decode(OPTYPE_SR, op8)); break; + case 0x84: sprintf(t, "sty $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break; + case 0x85: sprintf(t, "sta $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break; + case 0x86: sprintf(t, "stx $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break; + case 0x87: sprintf(t, "sta [$%.2x] [%.6x]", op8, decode(OPTYPE_ILDP, op8)); break; + case 0x88: sprintf(t, "dey "); break; + case 0x89: if(a8)sprintf(t, "bit #$%.2x ", op8); + else sprintf(t, "bit #$%.4x ", op16); break; + case 0x8a: sprintf(t, "txa "); break; + case 0x8b: sprintf(t, "phb "); break; + case 0x8c: sprintf(t, "sty $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break; + case 0x8d: sprintf(t, "sta $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break; + case 0x8e: sprintf(t, "stx $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break; + case 0x8f: sprintf(t, "sta $%.6x [%.6x]", op24, decode(OPTYPE_LONG, op24)); break; + case 0x90: sprintf(t, "bcc $%.4x [%.6x]", uint16(decode(OPTYPE_RELB, op8)), decode(OPTYPE_RELB, op8)); break; + case 0x91: sprintf(t, "sta ($%.2x),y [%.6x]", op8, decode(OPTYPE_IDPY, op8)); break; + case 0x92: sprintf(t, "sta ($%.2x) [%.6x]", op8, decode(OPTYPE_IDP, op8)); break; + case 0x93: sprintf(t, "sta ($%.2x,s),y [%.6x]", op8, decode(OPTYPE_ISRY, op8)); break; + case 0x94: sprintf(t, "sty $%.2x,x [%.6x]", op8, decode(OPTYPE_DPX, op8)); break; + case 0x95: sprintf(t, "sta $%.2x,x [%.6x]", op8, decode(OPTYPE_DPX, op8)); break; + case 0x96: sprintf(t, "stx $%.2x,y [%.6x]", op8, decode(OPTYPE_DPY, op8)); break; + case 0x97: sprintf(t, "sta [$%.2x],y [%.6x]", op8, decode(OPTYPE_ILDPY, op8)); break; + case 0x98: sprintf(t, "tya "); break; + case 0x99: sprintf(t, "sta $%.4x,y [%.6x]", op16, decode(OPTYPE_ADDRY, op16)); break; + case 0x9a: sprintf(t, "txs "); break; + case 0x9b: sprintf(t, "txy "); break; + case 0x9c: sprintf(t, "stz $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break; + case 0x9d: sprintf(t, "sta $%.4x,x [%.6x]", op16, decode(OPTYPE_ADDRX, op16)); break; + case 0x9e: sprintf(t, "stz $%.4x,x [%.6x]", op16, decode(OPTYPE_ADDRX, op16)); break; + case 0x9f: sprintf(t, "sta $%.6x,x [%.6x]", op24, decode(OPTYPE_LONGX, op24)); break; + case 0xa0: if(x8)sprintf(t, "ldy #$%.2x ", op8); + else sprintf(t, "ldy #$%.4x ", op16); break; + case 0xa1: sprintf(t, "lda ($%.2x,x) [%.6x]", op8, decode(OPTYPE_IDPX, op8)); break; + case 0xa2: if(x8)sprintf(t, "ldx #$%.2x ", op8); + else sprintf(t, "ldx #$%.4x ", op16); break; + case 0xa3: sprintf(t, "lda $%.2x,s [%.6x]", op8, decode(OPTYPE_SR, op8)); break; + case 0xa4: sprintf(t, "ldy $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break; + case 0xa5: sprintf(t, "lda $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break; + case 0xa6: sprintf(t, "ldx $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break; + case 0xa7: sprintf(t, "lda [$%.2x] [%.6x]", op8, decode(OPTYPE_ILDP, op8)); break; + case 0xa8: sprintf(t, "tay "); break; + case 0xa9: if(a8)sprintf(t, "lda #$%.2x ", op8); + else sprintf(t, "lda #$%.4x ", op16); break; + case 0xaa: sprintf(t, "tax "); break; + case 0xab: sprintf(t, "plb "); break; + case 0xac: sprintf(t, "ldy $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break; + case 0xad: sprintf(t, "lda $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break; + case 0xae: sprintf(t, "ldx $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break; + case 0xaf: sprintf(t, "lda $%.6x [%.6x]", op24, decode(OPTYPE_LONG, op24)); break; + case 0xb0: sprintf(t, "bcs $%.4x [%.6x]", uint16(decode(OPTYPE_RELB, op8)), decode(OPTYPE_RELB, op8)); break; + case 0xb1: sprintf(t, "lda ($%.2x),y [%.6x]", op8, decode(OPTYPE_IDPY, op8)); break; + case 0xb2: sprintf(t, "lda ($%.2x) [%.6x]", op8, decode(OPTYPE_IDP, op8)); break; + case 0xb3: sprintf(t, "lda ($%.2x,s),y [%.6x]", op8, decode(OPTYPE_ISRY, op8)); break; + case 0xb4: sprintf(t, "ldy $%.2x,x [%.6x]", op8, decode(OPTYPE_DPX, op8)); break; + case 0xb5: sprintf(t, "lda $%.2x,x [%.6x]", op8, decode(OPTYPE_DPX, op8)); break; + case 0xb6: sprintf(t, "ldx $%.2x,y [%.6x]", op8, decode(OPTYPE_DPY, op8)); break; + case 0xb7: sprintf(t, "lda [$%.2x],y [%.6x]", op8, decode(OPTYPE_ILDPY, op8)); break; + case 0xb8: sprintf(t, "clv "); break; + case 0xb9: sprintf(t, "lda $%.4x,y [%.6x]", op16, decode(OPTYPE_ADDRY, op16)); break; + case 0xba: sprintf(t, "tsx "); break; + case 0xbb: sprintf(t, "tyx "); break; + case 0xbc: sprintf(t, "ldy $%.4x,x [%.6x]", op16, decode(OPTYPE_ADDRX, op16)); break; + case 0xbd: sprintf(t, "lda $%.4x,x [%.6x]", op16, decode(OPTYPE_ADDRX, op16)); break; + case 0xbe: sprintf(t, "ldx $%.4x,y [%.6x]", op16, decode(OPTYPE_ADDRY, op16)); break; + case 0xbf: sprintf(t, "lda $%.6x,x [%.6x]", op24, decode(OPTYPE_LONGX, op24)); break; + case 0xc0: if(x8)sprintf(t, "cpy #$%.2x ", op8); + else sprintf(t, "cpy #$%.4x ", op16); break; + case 0xc1: sprintf(t, "cmp ($%.2x,x) [%.6x]", op8, decode(OPTYPE_IDPX, op8)); break; + case 0xc2: sprintf(t, "rep #$%.2x ", op8); break; + case 0xc3: sprintf(t, "cmp $%.2x,s [%.6x]", op8, decode(OPTYPE_SR, op8)); break; + case 0xc4: sprintf(t, "cpy $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break; + case 0xc5: sprintf(t, "cmp $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break; + case 0xc6: sprintf(t, "dec $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break; + case 0xc7: sprintf(t, "cmp [$%.2x] [%.6x]", op8, decode(OPTYPE_ILDP, op8)); break; + case 0xc8: sprintf(t, "iny "); break; + case 0xc9: if(a8)sprintf(t, "cmp #$%.2x ", op8); + else sprintf(t, "cmp #$%.4x ", op16); break; + case 0xca: sprintf(t, "dex "); break; + case 0xcb: sprintf(t, "wai "); break; + case 0xcc: sprintf(t, "cpy $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break; + case 0xcd: sprintf(t, "cmp $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break; + case 0xce: sprintf(t, "dec $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break; + case 0xcf: sprintf(t, "cmp $%.6x [%.6x]", op24, decode(OPTYPE_LONG, op24)); break; + case 0xd0: sprintf(t, "bne $%.4x [%.6x]", uint16(decode(OPTYPE_RELB, op8)), decode(OPTYPE_RELB, op8)); break; + case 0xd1: sprintf(t, "cmp ($%.2x),y [%.6x]", op8, decode(OPTYPE_IDPY, op8)); break; + case 0xd2: sprintf(t, "cmp ($%.2x) [%.6x]", op8, decode(OPTYPE_IDP, op8)); break; + case 0xd3: sprintf(t, "cmp ($%.2x,s),y [%.6x]", op8, decode(OPTYPE_ISRY, op8)); break; + case 0xd4: sprintf(t, "pei ($%.2x) [%.6x]", op8, decode(OPTYPE_IDP, op8)); break; + case 0xd5: sprintf(t, "cmp $%.2x,x [%.6x]", op8, decode(OPTYPE_DPX, op8)); break; + case 0xd6: sprintf(t, "dec $%.2x,x [%.6x]", op8, decode(OPTYPE_DPX, op8)); break; + case 0xd7: sprintf(t, "cmp [$%.2x],y [%.6x]", op8, decode(OPTYPE_ILDPY, op8)); break; + case 0xd8: sprintf(t, "cld "); break; + case 0xd9: sprintf(t, "cmp $%.4x,y [%.6x]", op16, decode(OPTYPE_ADDRY, op16)); break; + case 0xda: sprintf(t, "phx "); break; + case 0xdb: sprintf(t, "stp "); break; + case 0xdc: sprintf(t, "jmp [$%.4x] [%.6x]", op16, decode(OPTYPE_ILADDR, op16)); break; + case 0xdd: sprintf(t, "cmp $%.4x,x [%.6x]", op16, decode(OPTYPE_ADDRX, op16)); break; + case 0xde: sprintf(t, "dec $%.4x,x [%.6x]", op16, decode(OPTYPE_ADDRX, op16)); break; + case 0xdf: sprintf(t, "cmp $%.6x,x [%.6x]", op24, decode(OPTYPE_LONGX, op24)); break; + case 0xe0: if(x8)sprintf(t, "cpx #$%.2x ", op8); + else sprintf(t, "cpx #$%.4x ", op16); break; + case 0xe1: sprintf(t, "sbc ($%.2x,x) [%.6x]", op8, decode(OPTYPE_IDPX, op8)); break; + case 0xe2: sprintf(t, "sep #$%.2x ", op8); break; + case 0xe3: sprintf(t, "sbc $%.2x,s [%.6x]", op8, decode(OPTYPE_SR, op8)); break; + case 0xe4: sprintf(t, "cpx $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break; + case 0xe5: sprintf(t, "sbc $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break; + case 0xe6: sprintf(t, "inc $%.2x [%.6x]", op8, decode(OPTYPE_DP, op8)); break; + case 0xe7: sprintf(t, "sbc [$%.2x] [%.6x]", op8, decode(OPTYPE_ILDP, op8)); break; + case 0xe8: sprintf(t, "inx "); break; + case 0xe9: if(a8)sprintf(t, "sbc #$%.2x ", op8); + else sprintf(t, "sbc #$%.4x ", op16); break; + case 0xea: sprintf(t, "nop "); break; + case 0xeb: sprintf(t, "xba "); break; + case 0xec: sprintf(t, "cpx $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break; + case 0xed: sprintf(t, "sbc $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break; + case 0xee: sprintf(t, "inc $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break; + case 0xef: sprintf(t, "sbc $%.6x [%.6x]", op24, decode(OPTYPE_LONG, op24)); break; + case 0xf0: sprintf(t, "beq $%.4x [%.6x]", uint16(decode(OPTYPE_RELB, op8)), decode(OPTYPE_RELB, op8)); break; + case 0xf1: sprintf(t, "sbc ($%.2x),y [%.6x]", op8, decode(OPTYPE_IDPY, op8)); break; + case 0xf2: sprintf(t, "sbc ($%.2x) [%.6x]", op8, decode(OPTYPE_IDP, op8)); break; + case 0xf3: sprintf(t, "sbc ($%.2x,s),y [%.6x]", op8, decode(OPTYPE_ISRY, op8)); break; + case 0xf4: sprintf(t, "pea $%.4x [%.6x]", op16, decode(OPTYPE_ADDR, op16)); break; + case 0xf5: sprintf(t, "sbc $%.2x,x [%.6x]", op8, decode(OPTYPE_DPX, op8)); break; + case 0xf6: sprintf(t, "inc $%.2x,x [%.6x]", op8, decode(OPTYPE_DPX, op8)); break; + case 0xf7: sprintf(t, "sbc [$%.2x],y [%.6x]", op8, decode(OPTYPE_ILDPY, op8)); break; + case 0xf8: sprintf(t, "sed "); break; + case 0xf9: sprintf(t, "sbc $%.4x,y [%.6x]", op16, decode(OPTYPE_ADDRY, op16)); break; + case 0xfa: sprintf(t, "plx "); break; + case 0xfb: sprintf(t, "xce "); break; + case 0xfc: sprintf(t, "jsr ($%.4x,x) [%.6x]", op16, decode(OPTYPE_IADDRX, op16)); break; + case 0xfd: sprintf(t, "sbc $%.4x,x [%.6x]", op16, decode(OPTYPE_ADDRX, op16)); break; + case 0xfe: sprintf(t, "inc $%.4x,x [%.6x]", op16, decode(OPTYPE_ADDRX, op16)); break; + case 0xff: sprintf(t, "sbc $%.6x,x [%.6x]", op24, decode(OPTYPE_LONGX, op24)); break; + } + + #undef op8 + #undef op16 + #undef op24 + #undef a8 + #undef x8 + + strcat(s, t); + strcat(s, " "); + + sprintf(t, "A:%.4x X:%.4x Y:%.4x S:%.4x D:%.4x DB:%.2x ", + regs.a.w, regs.x.w, regs.y.w, regs.s.w, regs.d.w, regs.db); + strcat(s, t); + + if(regs.e) { + sprintf(t, "%c%c%c%c%c%c%c%c", + regs.p.n ? 'N' : 'n', regs.p.v ? 'V' : 'v', + regs.p.m ? '1' : '0', regs.p.x ? 'B' : 'b', + regs.p.d ? 'D' : 'd', regs.p.i ? 'I' : 'i', + regs.p.z ? 'Z' : 'z', regs.p.c ? 'C' : 'c'); + } else { + sprintf(t, "%c%c%c%c%c%c%c%c", + regs.p.n ? 'N' : 'n', regs.p.v ? 'V' : 'v', + regs.p.m ? 'M' : 'm', regs.p.x ? 'X' : 'x', + regs.p.d ? 'D' : 'd', regs.p.i ? 'I' : 'i', + regs.p.z ? 'Z' : 'z', regs.p.c ? 'C' : 'c'); + } + + strcat(s, t); + strcat(s, " "); + + sprintf(t, "V:%3d H:%4d", cpu.vcounter(), cpu.hcounter()); + strcat(s, t); +} + +//opcode_length() retrieves the length of the next opcode +//to be executed. It is used by the debugger to step over, +//disable and proceed cpu opcodes. +// +//5 and 6 are special cases, 5 is used for #consts based on +//the A register size, 6 for the X/Y register size. the +//rest are literal sizes. There's no need to test for +//emulation mode, as regs.p.m/regs.p.x should *always* be +//set in emulation mode. + +uint8 CPUcore::opcode_length() { + uint8 op, len; + static uint8 op_len_tbl[256] = { + //0,1,2,3, 4,5,6,7, 8,9,a,b, c,d,e,f + + 2,2,2,2, 2,2,2,2, 1,5,1,1, 3,3,3,4, //0x0n + 2,2,2,2, 2,2,2,2, 1,3,1,1, 3,3,3,4, //0x1n + 3,2,4,2, 2,2,2,2, 1,5,1,1, 3,3,3,4, //0x2n + 2,2,2,2, 2,2,2,2, 1,3,1,1, 3,3,3,4, //0x3n + + 1,2,2,2, 3,2,2,2, 1,5,1,1, 3,3,3,4, //0x4n + 2,2,2,2, 3,2,2,2, 1,3,1,1, 4,3,3,4, //0x5n + 1,2,3,2, 2,2,2,2, 1,5,1,1, 3,3,3,4, //0x6n + 2,2,2,2, 2,2,2,2, 1,3,1,1, 3,3,3,4, //0x7n + + 2,2,3,2, 2,2,2,2, 1,5,1,1, 3,3,3,4, //0x8n + 2,2,2,2, 2,2,2,2, 1,3,1,1, 3,3,3,4, //0x9n + 6,2,6,2, 2,2,2,2, 1,5,1,1, 3,3,3,4, //0xan + 2,2,2,2, 2,2,2,2, 1,3,1,1, 3,3,3,4, //0xbn + + 6,2,2,2, 2,2,2,2, 1,5,1,1, 3,3,3,4, //0xcn + 2,2,2,2, 2,2,2,2, 1,3,1,1, 3,3,3,4, //0xdn + 6,2,2,2, 2,2,2,2, 1,5,1,1, 3,3,3,4, //0xen + 2,2,2,2, 3,2,2,2, 1,3,1,1, 3,3,3,4 //0xfn + }; + + if(false /* in_opcode() == true */) { + return 0; + } + + op = dreadb(regs.pc.d); + len = op_len_tbl[op]; + if(len == 5) return (regs.e || regs.p.m) ? 2 : 3; + if(len == 6) return (regs.e || regs.p.x) ? 2 : 3; + return len; +} + +#endif diff --git a/asnes/cpu/core/disassembler/disassembler.hpp b/asnes/cpu/core/disassembler/disassembler.hpp new file mode 100755 index 00000000..b0ee6f04 --- /dev/null +++ b/asnes/cpu/core/disassembler/disassembler.hpp @@ -0,0 +1,30 @@ +enum { + OPTYPE_DP = 0, //dp + OPTYPE_DPX, //dp,x + OPTYPE_DPY, //dp,y + OPTYPE_IDP, //(dp) + OPTYPE_IDPX, //(dp,x) + OPTYPE_IDPY, //(dp),y + OPTYPE_ILDP, //[dp] + OPTYPE_ILDPY, //[dp],y + OPTYPE_ADDR, //addr + OPTYPE_ADDRX, //addr,x + OPTYPE_ADDRY, //addr,y + OPTYPE_IADDRX, //(addr,x) + OPTYPE_ILADDR, //[addr] + OPTYPE_LONG, //long + OPTYPE_LONGX, //long, x + OPTYPE_SR, //sr,s + OPTYPE_ISRY, //(sr,s),y + OPTYPE_ADDR_PC, //pbr:addr + OPTYPE_IADDR_PC, //pbr:(addr) + OPTYPE_RELB, //relb + OPTYPE_RELW, //relw +}; + +void disassemble_opcode(char *output, uint32 addr); +uint8 dreadb(uint32 addr); +uint16 dreadw(uint32 addr); +uint32 dreadl(uint32 addr); +uint32 decode(uint8 offset_type, uint32 addr); +uint8 opcode_length(); diff --git a/asnes/cpu/core/memory.hpp b/asnes/cpu/core/memory.hpp new file mode 100755 index 00000000..49926578 --- /dev/null +++ b/asnes/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/asnes/cpu/core/opcode_misc.cpp b/asnes/cpu/core/opcode_misc.cpp new file mode 100755 index 00000000..8087fe66 --- /dev/null +++ b/asnes/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/asnes/cpu/core/opcode_pc.cpp b/asnes/cpu/core/opcode_pc.cpp new file mode 100755 index 00000000..3b4543f3 --- /dev/null +++ b/asnes/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/asnes/cpu/core/opcode_read.cpp b/asnes/cpu/core/opcode_read.cpp new file mode 100755 index 00000000..61a7feb3 --- /dev/null +++ b/asnes/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/asnes/cpu/core/opcode_rmw.cpp b/asnes/cpu/core/opcode_rmw.cpp new file mode 100755 index 00000000..fed939e1 --- /dev/null +++ b/asnes/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/asnes/cpu/core/opcode_write.cpp b/asnes/cpu/core/opcode_write.cpp new file mode 100755 index 00000000..de85e672 --- /dev/null +++ b/asnes/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/asnes/cpu/core/registers.hpp b/asnes/cpu/core/registers.hpp new file mode 100755 index 00000000..1305d334 --- /dev/null +++ b/asnes/cpu/core/registers.hpp @@ -0,0 +1,81 @@ +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 + + 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) { + z = 0; + } +}; diff --git a/asnes/cpu/core/serialization.cpp b/asnes/cpu/core/serialization.cpp new file mode 100755 index 00000000..467765bd --- /dev/null +++ b/asnes/cpu/core/serialization.cpp @@ -0,0 +1,36 @@ +#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(aa.d); + s.integer(rd.d); + s.integer(sp); + s.integer(dp); + + update_table(); +} + +#endif diff --git a/asnes/cpu/core/table.cpp b/asnes/cpu/core/table.cpp new file mode 100755 index 00000000..6575a4db --- /dev/null +++ b/asnes/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/asnes/cpu/cpu.cpp b/asnes/cpu/cpu.cpp new file mode 100755 index 00000000..32bfd41d --- /dev/null +++ b/asnes/cpu/cpu.cpp @@ -0,0 +1,141 @@ +#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; + } +} + +void CPU::synchronize_smp() { + if(smp.clock < 0) co_switch(smp.thread); +} + +void CPU::synchronize_ppu() { + if(ppu.clock < 0) co_switch(ppu.thread); +} + +void CPU::synchronize_coprocessor() { + for(unsigned i = 0; i < coprocessors.size(); i++) { + Processor &chip = *coprocessors[i]; + if(chip.clock < 0) co_switch(chip.thread); + } +} + +void CPU::Enter() { cpu.enter(); } + +void CPU::enter() { + while(true) { + if(scheduler.sync == Scheduler::SynchronizeMode::CPU) { + scheduler.sync = Scheduler::SynchronizeMode::All; + scheduler.exit(Scheduler::ExitReason::SynchronizeEvent); + } + + if(status.interrupt_pending) { + status.interrupt_pending = false; + if(status.nmi_pending) { + status.nmi_pending = false; + status.interrupt_vector = (regs.e == false ? 0xffea : 0xfffa); + op_irq(); + } else if(status.irq_pending) { + status.irq_pending = false; + status.interrupt_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()])(); +} + +void CPU::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(status.interrupt_vector + 0); + regs.pc.b = 0x00; + regs.p.i = 1; + regs.p.d = 0; + rd.h = op_read(status.interrupt_vector + 1); + regs.pc.w = rd.w; +} + +void CPU::power() { + cpu_version = config.cpu.version; + + regs.a = regs.x = regs.y = 0x0000; + regs.s = 0x01ff; + + mmio_power(); + dma_power(); + timing_power(); + + reset(); +} + +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; + update_table(); + + mmio_reset(); + dma_reset(); + timing_reset(); + + apu_port[0] = 0x00; + apu_port[1] = 0x00; + apu_port[2] = 0x00; + apu_port[3] = 0x00; +} + +CPU::CPU() { + PPUCounter::scanline = { &CPU::scanline, this }; +} + +CPU::~CPU() { +} + +} diff --git a/asnes/cpu/cpu.hpp b/asnes/cpu/cpu.hpp new file mode 100755 index 00000000..4ad7c83b --- /dev/null +++ b/asnes/cpu/cpu.hpp @@ -0,0 +1,136 @@ +class CPU : public Processor, public PPUCounter, public MMIO, public CPUcore { +public: + //synchronization + array coprocessors; + alwaysinline void step(unsigned clocks); + alwaysinline void synchronize_smp(); + void synchronize_ppu(); + void synchronize_coprocessor(); + + static void Enter(); + void enter(); + debugvirtual void op_step(); + void op_irq(); + bool interrupt_pending() { return status.interrupt_pending; } + + uint8 cpu_version; + + #include "dma/dma.hpp" + #include "memory/memory.hpp" + #include "mmio/mmio.hpp" + #include "timing/timing.hpp" + + struct Status { + bool interrupt_pending; + uint16 interrupt_vector; + + 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 + + //==== + //MMIO + //==== + + //$2181-$2183 + uint32 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 + uint16 hirq_pos, virq_pos; + + //$420d + unsigned rom_speed; + + //$4214-$4217 + uint16 rddiv; + uint16 rdmpy; + + //$4218-$421f + uint8 joy1l, joy1h; + uint8 joy2l, joy2h; + uint8 joy3l, joy3h; + uint8 joy4l, joy4h; + } status; + + struct ALU { + unsigned mpyctr; + unsigned divctr; + unsigned shift; + } alu; + + void power(); + void reset(); + + void serialize(serializer&); + CPU(); + ~CPU(); + + friend class CPUDebugger; +}; + +#if defined(DEBUGGER) + #include "debugger/debugger.hpp" + extern CPUDebugger cpu; +#else + extern CPU cpu; +#endif diff --git a/asnes/cpu/debugger/cpu-debugger.cpp b/asnes/cpu/debugger/cpu-debugger.cpp new file mode 100755 index 00000000..6111a746 --- /dev/null +++ b/asnes/cpu/debugger/cpu-debugger.cpp @@ -0,0 +1,99 @@ +#ifdef CPU_CPP + +bool CPUDebugger::property(unsigned id, string &name, string &value) { + unsigned n = 0; + + //internal + if(id == n++) { name = "S-CPU MDR"; value = string("0x", strhex<2>(mdr())); return true; } + + //$2181-2183 + if(id == n++) { name = "$2181-$2183"; value = ""; return true; } + if(id == n++) { name = "WRAM Address"; value = string("0x", strhex<6>(wram_address())); return true; } + + //$4016 + if(id == n++) { name = "$4016"; value = ""; return true; } + if(id == n++) { name = "Joypad Strobe Latch"; value = joypad_strobe_latch(); return true; } + + //$4200 + if(id == n++) { name = "$4200"; value = ""; return true; } + if(id == n++) { name = "NMI Enable"; value = nmi_enable(); return true; } + if(id == n++) { name = "H-IRQ Enable"; value = hirq_enable(); return true; } + if(id == n++) { name = "V-IRQ Enable"; value = virq_enable(); return true; } + if(id == n++) { name = "Auto Joypad Poll"; value = auto_joypad_poll(); return true; } + + //$4201 + if(id == n++) { name = "$4201"; value = ""; return true; } + if(id == n++) { name = "PIO"; value = string("0x", strhex<2>(pio_bits())); return true; } + + //$4202 + if(id == n++) { name = "$4202"; value = ""; return true; } + if(id == n++) { name = "Multiplicand"; value = string("0x", strhex<2>(multiplicand())); return true; } + + //$4203 + if(id == n++) { name = "$4203"; value = ""; return true; } + if(id == n++) { name = "Multiplier"; value = string("0x", strhex<2>(multiplier())); return true; } + + //$4204-$4205 + if(id == n++) { name = "$4204-$4205"; value = ""; return true; } + if(id == n++) { name = "Dividend"; value = string("0x", strhex<4>(dividend())); return true; } + + //$4206 + if(id == n++) { name = "$4206"; value = ""; return true; } + if(id == n++) { name = "Divisor"; value = string("0x", strhex<2>(divisor())); return true; } + + //$4207-$4208 + if(id == n++) { name = "$4207-$4208"; value = ""; return true; } + if(id == n++) { name = "H-Time"; value = string("0x", strhex<4>(htime())); return true; } + + //$4209-$420a + if(id == n++) { name = "$4209-$420a"; value = ""; return true; } + if(id == n++) { name = "V-Time"; value = string("0x", strhex<4>(vtime())); return true; } + + //$420b + if(id == n++) { name = "$420b"; value = ""; return true; } + if(id == n++) { name = "DMA Enable"; value = string("0x", strhex<2>(dma_enable())); return true; } + + //$420c + if(id == n++) { name = "$420c"; value = ""; return true; } + if(id == n++) { name = "HDMA Enable"; value = string("0x", strhex<2>(hdma_enable())); return true; } + + //$420d + if(id == n++) { name = "$420d"; value = ""; return true; } + if(id == n++) { name = "FastROM Enable"; value = fastrom_enable(); return true; } + + for(unsigned i = 0; i < 8; i++) { + if(id == n++) { name = string() << "DMA Channel " << i; return true; } + + //$43x0 + if(id == n++) { name = "Direction"; value = dma_direction(i); return true; } + if(id == n++) { name = "Indirect"; value = dma_indirect(i); return true; } + if(id == n++) { name = "Reverse Transfer"; value = dma_reverse_transfer(i); return true; } + if(id == n++) { name = "Fixed Transfer"; value = dma_fixed_transfer(i); return true; } + if(id == n++) { name = "Transfer Mode"; value = dma_transfer_mode(i); return true; } + + //$43x1 + if(id == n++) { name = "B-Bus Address"; value = string("0x", strhex<4>(dma_bbus_address(i))); return true; } + + //$43x2-$43x3 + if(id == n++) { name = "A-Bus Address"; value = string("0x", strhex<4>(dma_abus_address(i))); return true; } + + //$43x4 + if(id == n++) { name = "A-Bus Bank"; value = string("0x", strhex<2>(dma_abus_bank(i))); return true; } + + //$43x5-$43x6 + if(id == n++) { name = "Transfer Size / Indirect Address"; value = string("0x", strhex<4>(dma_transfer_size(i))); return true; } + + //$43x7 + if(id == n++) { name = "Indirect Bank"; value = string("0x", strhex<2>(dma_indirect_bank(i))); return true; } + + //$43x8-$43x9 + if(id == n++) { name = "Table Address"; value = string("0x", strhex<4>(dma_table_address(i))); return true; } + + //$43xa + if(id == n++) { name = "Line Counter"; value = string("0x", strhex<2>(dma_line_counter(i))); return true; } + } + + return false; +} + +#endif diff --git a/asnes/cpu/debugger/cpu-debugger.hpp b/asnes/cpu/debugger/cpu-debugger.hpp new file mode 100755 index 00000000..31ddec59 --- /dev/null +++ b/asnes/cpu/debugger/cpu-debugger.hpp @@ -0,0 +1,76 @@ +struct CPUDebugger : ChipDebugger { + bool property(unsigned id, string &name, string &value); + + //internal + virtual unsigned mdr() { return 0; } + + //$2181-2183 + virtual unsigned wram_address() { return 0; } + + //$4016 + virtual bool joypad_strobe_latch() { return 0; } + + //$4200 + virtual bool nmi_enable() { return 0; } + virtual bool hirq_enable() { return 0; } + virtual bool virq_enable() { return 0; } + virtual bool auto_joypad_poll() { return 0; } + + //$4201 + virtual unsigned pio_bits() { return 0; } + + //$4202 + virtual unsigned multiplicand() { return 0; } + + //$4203 + virtual unsigned multiplier() { return 0; } + + //$4204-$4205 + virtual unsigned dividend() { return 0; } + + //$4206 + virtual unsigned divisor() { return 0; } + + //$4207-$4208 + virtual unsigned htime() { return 0; } + + //$4209-$420a + virtual unsigned vtime() { return 0; } + + //$420b + virtual unsigned dma_enable() { return 0; } + + //$420c + virtual unsigned hdma_enable() { return 0; } + + //$420d + virtual bool fastrom_enable() { return 0; } + + //$43x0 + virtual bool dma_direction(unsigned) { return 0; } + virtual bool dma_indirect(unsigned) { return 0; } + virtual bool dma_reverse_transfer(unsigned) { return 0; } + virtual bool dma_fixed_transfer(unsigned) { return 0; } + virtual unsigned dma_transfer_mode(unsigned) { return 0; } + + //$43x1 + virtual unsigned dma_bbus_address(unsigned) { return 0; } + + //$43x2-$43x3 + virtual unsigned dma_abus_address(unsigned) { return 0; } + + //$43x4 + virtual unsigned dma_abus_bank(unsigned) { return 0; } + + //$43x5-$43x6 + virtual unsigned dma_transfer_size(unsigned) { return 0; } + + //$43x7 + virtual unsigned dma_indirect_bank(unsigned) { return 0; } + + //$43x8-$43x9 + virtual unsigned dma_table_address(unsigned addr) { return 0; } + + //$43xa + virtual unsigned dma_line_counter(unsigned addr) { return 0; } +}; diff --git a/asnes/cpu/debugger/debugger.cpp b/asnes/cpu/debugger/debugger.cpp new file mode 100755 index 00000000..e22625ea --- /dev/null +++ b/asnes/cpu/debugger/debugger.cpp @@ -0,0 +1,134 @@ +#ifdef CPU_CPP + +void sCPUDebugger::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; + + if(debugger.step_cpu) { + debugger.break_event = Debugger::BreakEvent::CPUStep; + scheduler.exit(Scheduler::ExitReason::DebuggerEvent); + } else { + debugger.breakpoint_test(Debugger::Breakpoint::Source::CPUBus, Debugger::Breakpoint::Mode::Exec, regs.pc, 0x00); + } + + if(step_event) step_event(); + sCPU::op_step(); + synchronize_smp(); +} + +uint8 sCPUDebugger::op_read(uint32 addr) { + uint8 data = sCPU::op_read(addr); + usage[addr] |= UsageRead; + debugger.breakpoint_test(Debugger::Breakpoint::Source::CPUBus, Debugger::Breakpoint::Mode::Read, addr, data); + return data; +} + +void sCPUDebugger::op_write(uint32 addr, uint8 data) { + sCPU::op_write(addr, data); + usage[addr] |= UsageWrite; + usage[addr] &= ~UsageExec; + debugger.breakpoint_test(Debugger::Breakpoint::Source::CPUBus, Debugger::Breakpoint::Mode::Write, addr, data); +} + +sCPUDebugger::sCPUDebugger() { + usage = new uint8[1 << 24](); + opcode_pc = 0x8000; +} + +sCPUDebugger::~sCPUDebugger() { + delete[] usage; +} + +//=========== +//CPUDebugger +//=========== + +//internal +unsigned sCPUDebugger::mdr() { return regs.mdr; } + +//$2181-$2183 +unsigned sCPUDebugger::wram_address() { return status.wram_addr; } + +//$4016 +bool sCPUDebugger::joypad_strobe_latch() { return status.joypad_strobe_latch; } + +//$4200 +bool sCPUDebugger::nmi_enable() { return status.nmi_enabled; } +bool sCPUDebugger::hirq_enable() { return status.hirq_enabled; } +bool sCPUDebugger::virq_enable() { return status.virq_enabled; } +bool sCPUDebugger::auto_joypad_poll() { return status.auto_joypad_poll; } + +//$4201 +unsigned sCPUDebugger::pio_bits() { return status.pio; } + +//$4202 +unsigned sCPUDebugger::multiplicand() { return status.wrmpya; } + +//$4203 +unsigned sCPUDebugger::multiplier() { return status.wrmpyb; } + +//$4204-$4205 +unsigned sCPUDebugger::dividend() { return status.wrdiva; } + +//$4206 +unsigned sCPUDebugger::divisor() { return status.wrdivb; } + +//$4207-$4208 +unsigned sCPUDebugger::htime() { return status.hirq_pos; } + +//$4209-$420a +unsigned sCPUDebugger::vtime() { return status.virq_pos; } + +//$420b +unsigned sCPUDebugger::dma_enable() { + unsigned result = 0; + for(unsigned n = 0; n < 8; n++) { + result |= channel[n].dma_enabled << n; + } + return result; +} + +//$420c +unsigned sCPUDebugger::hdma_enable() { + unsigned result = 0; + for(unsigned n = 0; n < 8; n++) { + result |= channel[n].hdma_enabled << n; + } + return result; +} + +//$420d +bool sCPUDebugger::fastrom_enable() { return status.rom_speed; } + +//$43x0 +bool sCPUDebugger::dma_direction(unsigned n) { return channel[n].direction; } +bool sCPUDebugger::dma_indirect(unsigned n) { return channel[n].hdma_indirect; } +bool sCPUDebugger::dma_reverse_transfer(unsigned n) { return channel[n].reversexfer; } +bool sCPUDebugger::dma_fixed_transfer(unsigned n) { return channel[n].fixedxfer; } +unsigned sCPUDebugger::dma_transfer_mode(unsigned n) { return channel[n].xfermode; } + +//$43x1 +unsigned sCPUDebugger::dma_bbus_address(unsigned n) { return 0x2100 + channel[n].destaddr; } + +//$43x2-$43x3 +unsigned sCPUDebugger::dma_abus_address(unsigned n) { return channel[n].srcaddr; } + +//$43x4 +unsigned sCPUDebugger::dma_abus_bank(unsigned n) { return channel[n].srcbank; } + +//$43x5-$43x6 +unsigned sCPUDebugger::dma_transfer_size(unsigned n) { return channel[n].xfersize; } + +//$43x7 +unsigned sCPUDebugger::dma_indirect_bank(unsigned n) { return channel[n].hdma_ibank; } + +//$43x8-$43x9 +unsigned sCPUDebugger::dma_table_address(unsigned n) { return channel[n].hdma_addr; } + +//$43xa +unsigned sCPUDebugger::dma_line_counter(unsigned n) { return channel[n].hdma_line_counter; } + +#endif diff --git a/asnes/cpu/debugger/debugger.hpp b/asnes/cpu/debugger/debugger.hpp new file mode 100755 index 00000000..cff3c61f --- /dev/null +++ b/asnes/cpu/debugger/debugger.hpp @@ -0,0 +1,98 @@ +class sCPUDebugger : public sCPU, public CPUDebugger { +public: + function step_event; + + enum Usage { + UsageRead = 0x80, + UsageWrite = 0x40, + UsageExec = 0x20, + UsageFlagM = 0x02, + UsageFlagX = 0x01, + }; + uint8 *usage; + uint32 opcode_pc; //points to the current opcode, used to backtrace on read/write breakpoints + + void op_step(); + uint8 op_read(uint32 addr); + void op_write(uint32 addr, uint8 data); + + sCPUDebugger(); + ~sCPUDebugger(); + + //=========== + //CPUDebugger + //=========== + + //internal + unsigned mdr(); + + //$2181-$2183 + unsigned wram_address(); + + //$4016 + bool joypad_strobe_latch(); + + //$4200 + bool nmi_enable(); + bool hirq_enable(); + bool virq_enable(); + bool auto_joypad_poll(); + + //$4201 + unsigned pio_bits(); + + //$4202 + unsigned multiplicand(); + + //$4203 + unsigned multiplier(); + + //$4204-$4205 + unsigned dividend(); + + //$4206 + unsigned divisor(); + + //$4207-$4208 + unsigned htime(); + + //$4209-$420a + unsigned vtime(); + + //$420b + unsigned dma_enable(); + + //$420c + unsigned hdma_enable(); + + //$420d + bool fastrom_enable(); + + //$43x0 + bool dma_direction(unsigned); + bool dma_indirect(unsigned); + bool dma_reverse_transfer(unsigned); + bool dma_fixed_transfer(unsigned); + unsigned dma_transfer_mode(unsigned); + + //$43x1 + unsigned dma_bbus_address(unsigned); + + //$43x2-$43x3 + unsigned dma_abus_address(unsigned); + + //$43x4 + unsigned dma_abus_bank(unsigned); + + //$43x5-$43x6 + unsigned dma_transfer_size(unsigned); + + //$43x7 + unsigned dma_indirect_bank(unsigned); + + //$43x8-$43x9 + unsigned dma_table_address(unsigned); + + //$43xa + unsigned dma_line_counter(unsigned); +}; diff --git a/asnes/cpu/dma/dma.cpp b/asnes/cpu/dma/dma.cpp new file mode 100755 index 00000000..9aad43f2 --- /dev/null +++ b/asnes/cpu/dma/dma.cpp @@ -0,0 +1,292 @@ +#ifdef CPU_CPP + +void CPU::dma_add_clocks(unsigned clocks) { + status.dma_clocks += clocks; + add_clocks(clocks); + synchronize_ppu(); + synchronize_coprocessor(); +} + +//============= +//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].xfermode) { default: + case 0: return (channel[i].destaddr); //0 + case 1: return (channel[i].destaddr + (index & 1)); //0,1 + case 2: return (channel[i].destaddr); //0,0 + case 3: return (channel[i].destaddr + ((index >> 1) & 1)); //0,0,1,1 + case 4: return (channel[i].destaddr + (index & 3)); //0,1,2,3 + case 5: return (channel[i].destaddr + (index & 1)); //0,1,0,1 + case 6: return (channel[i].destaddr); //0,0 [2] + case 7: return (channel[i].destaddr + ((index >> 1) & 1)); //0,0,1,1 [3] + } +} + +inline uint32 CPU::dma_addr(unsigned i) { + uint32 r = (channel[i].srcbank << 16) | (channel[i].srcaddr); + + if(channel[i].fixedxfer == false) { + if(channel[i].reversexfer == false) { + channel[i].srcaddr++; + } else { + channel[i].srcaddr--; + } + } + + return r; +} + +inline uint32 CPU::hdma_addr(unsigned i) { + return (channel[i].srcbank << 16) | (channel[i].hdma_addr++); +} + +inline uint32 CPU::hdma_iaddr(unsigned i) { + return (channel[i].hdma_ibank << 16) | (channel[i].hdma_iaddr++); +} + +//============== +//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].xfersize); + + 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].srcbank << 16) | channel[i].hdma_addr); + dma_add_clocks(4); + dma_write(false); + + if((channel[i].hdma_line_counter & 0x7f) == 0) { + channel[i].hdma_line_counter = regs.mdr; + channel[i].hdma_addr++; + + channel[i].hdma_completed = (channel[i].hdma_line_counter == 0); + channel[i].hdma_do_transfer = !channel[i].hdma_completed; + + if(channel[i].hdma_indirect) { + dma_add_clocks(4); + regs.mdr = dma_read(hdma_addr(i)); + channel[i].hdma_iaddr = 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].hdma_iaddr >>= 8; + channel[i].hdma_iaddr |= 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].xfermode]; + for(unsigned index = 0; index < length; index++) { + unsigned addr = !channel[i].hdma_indirect ? 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].hdma_line_counter--; + channel[i].hdma_do_transfer = channel[i].hdma_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].srcaddr; + channel[i].hdma_line_counter = 0; + hdma_update(i); + } + + status.irq_lock = true; +} + +//============== +//initialization +//============== + +void CPU::dma_power() { + for(unsigned i = 0; i < 8; i++) { + channel[i].dmap = 0xff; + channel[i].direction = 1; + channel[i].hdma_indirect = true; + channel[i].reversexfer = true; + channel[i].fixedxfer = true; + channel[i].xfermode = 7; + + channel[i].destaddr = 0xff; + + channel[i].srcaddr = 0xffff; + channel[i].srcbank = 0xff; + + //channel[i]::union { xfersize, hdma_iaddr }; + channel[i].xfersize = 0xffff; + channel[i].hdma_ibank = 0xff; + + channel[i].hdma_addr = 0xffff; + channel[i].hdma_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/asnes/cpu/dma/dma.hpp b/asnes/cpu/dma/dma.hpp new file mode 100755 index 00000000..f82d44ec --- /dev/null +++ b/asnes/cpu/dma/dma.hpp @@ -0,0 +1,79 @@ +struct { + //$420b + bool dma_enabled; + + //$420c + bool hdma_enabled; + + //$43x0 + uint8 dmap; + bool direction; + bool hdma_indirect; + bool reversexfer; + bool fixedxfer; + uint8 xfermode; + + //$43x1 + uint8 destaddr; + + //$43x2-$43x3 + uint16 srcaddr; + + //$43x4 + uint8 srcbank; + + //$43x5-$43x6 + union { + uint16 xfersize; + uint16 hdma_iaddr; + }; + + //$43x7 + uint8 hdma_ibank; + + //$43x8-$43x9 + uint16 hdma_addr; + + //$43xa + uint8 hdma_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/asnes/cpu/memory/memory.cpp b/asnes/cpu/memory/memory.cpp new file mode 100755 index 00000000..bf48dee5 --- /dev/null +++ b/asnes/cpu/memory/memory.cpp @@ -0,0 +1,38 @@ +#ifdef CPU_CPP + +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/asnes/cpu/memory/memory.hpp b/asnes/cpu/memory/memory.hpp new file mode 100755 index 00000000..43d296d8 --- /dev/null +++ b/asnes/cpu/memory/memory.hpp @@ -0,0 +1,16 @@ +//============================ +//CPU<>APU communication ports +//============================ + +uint8 apu_port[4]; +uint8 port_read(uint8 port) { return apu_port[port & 3]; } +void port_write(uint8 port, uint8 data) { apu_port[port & 3] = data; } + +//====================== +//core CPU bus functions +//====================== + +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/asnes/cpu/mmio/mmio.cpp b/asnes/cpu/mmio/mmio.cpp new file mode 100755 index 00000000..10df666f --- /dev/null +++ b/asnes/cpu/mmio/mmio.cpp @@ -0,0 +1,541 @@ +#ifdef CPU_CPP + +uint8 CPU::pio() { return status.pio; } +bool CPU::joylatch() { return status.joypad_strobe_latch; } + +//WMDATA +uint8 CPU::mmio_r2180() { + uint8 r = bus.read(0x7e0000 | status.wram_addr); + status.wram_addr = (status.wram_addr + 1) & 0x01ffff; + return r; +} + +//WMDATA +void CPU::mmio_w2180(uint8 data) { + bus.write(0x7e0000 | status.wram_addr, data); + status.wram_addr = (status.wram_addr + 1) & 0x01ffff; +} + +//WMADDL +void CPU::mmio_w2181(uint8 data) { + status.wram_addr = (status.wram_addr & 0xffff00) | (data); + status.wram_addr &= 0x01ffff; +} + +//WMADDM +void CPU::mmio_w2182(uint8 data) { + status.wram_addr = (status.wram_addr & 0xff00ff) | (data << 8); + status.wram_addr &= 0x01ffff; +} + +//WMADDH +void CPU::mmio_w2183(uint8 data) { + status.wram_addr = (status.wram_addr & 0x00ffff) | (data << 16); + status.wram_addr &= 0x01ffff; +} + +//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) { + bool old_latch = status.joypad_strobe_latch; + bool new_latch = data & 1; + status.joypad_strobe_latch = new_latch; + + if(old_latch != new_latch) { + input.poll(); + } +} + +//JOYSER0 +//7-2 = MDR +//1-0 = Joypad serial data +uint8 CPU::mmio_r4016() { + uint8 r = regs.mdr & 0xfc; + r |= input.port_read(0) & 3; + 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.port_read(1) & 3; + return r; +} + +//NMITIMEN +void CPU::mmio_w4200(uint8 data) { + status.auto_joypad_poll = !!(data & 0x01); + 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); +} + +//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 & ~0xff) | (data); + status.hirq_pos &= 0x01ff; +} + +//HTIMEH +void CPU::mmio_w4208(uint8 data) { + status.hirq_pos = (status.hirq_pos & 0xff) | (data << 8); + status.hirq_pos &= 0x01ff; +} + +//VTIMEL +void CPU::mmio_w4209(uint8 data) { + status.virq_pos = (status.virq_pos & ~0xff) | (data); + status.virq_pos &= 0x01ff; +} + +//VTIMEH +void CPU::mmio_w420a(uint8 data) { + status.virq_pos = (status.virq_pos & 0xff) | (data << 8); + status.virq_pos &= 0x01ff; +} + +//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); + uint16 vs = ppu.overscan() == false ? 225 : 240; + + //auto joypad polling + if(vcounter() >= vs && vcounter() <= (vs + 2))r |= 0x01; + + //hblank + if(hcounter() <= 2 || hcounter() >= 1096)r |= 0x40; + + //vblank + if(vcounter() >= vs)r |= 0x80; + + return r; +} + +//RDIO +uint8 CPU::mmio_r4213() { + return status.pio; +} + +//RDDIVL +uint8 CPU::mmio_r4214() { + return status.rddiv; +} + +//RDDIVH +uint8 CPU::mmio_r4215() { + return status.rddiv >> 8; +} + +//RDMPYL +uint8 CPU::mmio_r4216() { + return status.rdmpy; +} + +//RDMPYH +uint8 CPU::mmio_r4217() { + return status.rdmpy >> 8; +} + +//TODO: handle reads during joypad polling (v=225-227) +uint8 CPU::mmio_r4218() { return status.joy1l; } //JOY1L +uint8 CPU::mmio_r4219() { return status.joy1h; } //JOY1H +uint8 CPU::mmio_r421a() { return status.joy2l; } //JOY2L +uint8 CPU::mmio_r421b() { return status.joy2h; } //JOY2H +uint8 CPU::mmio_r421c() { return status.joy3l; } //JOY3L +uint8 CPU::mmio_r421d() { return status.joy3h; } //JOY3H +uint8 CPU::mmio_r421e() { return status.joy4l; } //JOY4L +uint8 CPU::mmio_r421f() { return status.joy4h; } //JOY4H + +//DMAPx +uint8 CPU::mmio_r43x0(uint8 i) { + return channel[i].dmap; +} + +//BBADx +uint8 CPU::mmio_r43x1(uint8 i) { + return channel[i].destaddr; +} + +//A1TxL +uint8 CPU::mmio_r43x2(uint8 i) { + return channel[i].srcaddr; +} + +//A1TxH +uint8 CPU::mmio_r43x3(uint8 i) { + return channel[i].srcaddr >> 8; +} + +//A1Bx +uint8 CPU::mmio_r43x4(uint8 i) { + return channel[i].srcbank; +} + +//DASxL +//union { uint16 xfersize; uint16 hdma_iaddr; }; +uint8 CPU::mmio_r43x5(uint8 i) { + return channel[i].xfersize; +} + +//DASxH +//union { uint16 xfersize; uint16 hdma_iaddr; }; +uint8 CPU::mmio_r43x6(uint8 i) { + return channel[i].xfersize >> 8; +} + +//DASBx +uint8 CPU::mmio_r43x7(uint8 i) { + return channel[i].hdma_ibank; +} + +//A2AxL +uint8 CPU::mmio_r43x8(uint8 i) { + return channel[i].hdma_addr; +} + +//A2AxH +uint8 CPU::mmio_r43x9(uint8 i) { + return channel[i].hdma_addr >> 8; +} + +//NTRLx +uint8 CPU::mmio_r43xa(uint8 i) { + return channel[i].hdma_line_counter; +} + +//??? +uint8 CPU::mmio_r43xb(uint8 i) { + return channel[i].unknown; +} + +//DMAPx +void CPU::mmio_w43x0(uint8 i, uint8 data) { + channel[i].dmap = data; + channel[i].direction = data & 0x80; + channel[i].hdma_indirect = data & 0x40; + channel[i].reversexfer = data & 0x10; + channel[i].fixedxfer = data & 0x08; + channel[i].xfermode = data & 7; +} + +//DDBADx +void CPU::mmio_w43x1(uint8 i, uint8 data) { + channel[i].destaddr = data; +} + +//A1TxL +void CPU::mmio_w43x2(uint8 i, uint8 data) { + channel[i].srcaddr = (channel[i].srcaddr & 0xff00) | (data); +} + +//A1TxH +void CPU::mmio_w43x3(uint8 i, uint8 data) { + channel[i].srcaddr = (channel[i].srcaddr & 0x00ff) | (data << 8); +} + +//A1Bx +void CPU::mmio_w43x4(uint8 i, uint8 data) { + channel[i].srcbank = data; +} + +//DASxL +//union { uint16 xfersize; uint16 hdma_iaddr; }; +void CPU::mmio_w43x5(uint8 i, uint8 data) { + channel[i].xfersize = (channel[i].xfersize & 0xff00) | (data); +} + +//DASxH +//union { uint16 xfersize; uint16 hdma_iaddr; }; +void CPU::mmio_w43x6(uint8 i, uint8 data) { + channel[i].xfersize = (channel[i].xfersize & 0x00ff) | (data << 8); +} + +//DASBx +void CPU::mmio_w43x7(uint8 i, uint8 data) { + channel[i].hdma_ibank = data; +} + +//A2AxL +void CPU::mmio_w43x8(uint8 i, uint8 data) { + channel[i].hdma_addr = (channel[i].hdma_addr & 0xff00) | (data); +} + +//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].hdma_line_counter = data; +} + +//??? +void CPU::mmio_w43xb(uint8 i, uint8 data) { + channel[i].unknown = data; +} + +void CPU::mmio_power() { +} + +void CPU::mmio_reset() { + //$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.joy1l = 0x00; + status.joy1h = 0x00; + status.joy2l = 0x00; + status.joy2h = 0x00; + status.joy3l = 0x00; + status.joy3h = 0x00; + status.joy4l = 0x00; + status.joy4h = 0x00; + + //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 & 3); + } + + //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 & 3, 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/asnes/cpu/mmio/mmio.hpp b/asnes/cpu/mmio/mmio.hpp new file mode 100755 index 00000000..5f5d73fa --- /dev/null +++ b/asnes/cpu/mmio/mmio.hpp @@ -0,0 +1,71 @@ +void mmio_power(); +void mmio_reset(); +uint8 mmio_read(unsigned addr); +void mmio_write(unsigned addr, uint8 data); + +uint8 pio(); +bool joylatch(); + +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/asnes/cpu/serialization.cpp b/asnes/cpu/serialization.cpp new file mode 100755 index 00000000..cbf31efb --- /dev/null +++ b/asnes/cpu/serialization.cpp @@ -0,0 +1,118 @@ +#ifdef CPU_CPP + +void CPU::serialize(serializer &s) { + CPUcore::core_serialize(s); + Processor::serialize(s); + PPUCounter::serialize(s); + s.integer(cpu_version); + + s.integer(status.interrupt_pending); + s.integer(status.interrupt_vector); + + 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.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.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); + + 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].dmap); + s.integer(channel[i].direction); + s.integer(channel[i].hdma_indirect); + s.integer(channel[i].reversexfer); + s.integer(channel[i].fixedxfer); + s.integer(channel[i].xfermode); + s.integer(channel[i].destaddr); + s.integer(channel[i].srcaddr); + s.integer(channel[i].srcbank); + s.integer(channel[i].xfersize); + s.integer(channel[i].hdma_ibank); + s.integer(channel[i].hdma_addr); + s.integer(channel[i].hdma_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); + + s.integer(apu_port[0]); + s.integer(apu_port[1]); + s.integer(apu_port[2]); + s.integer(apu_port[3]); +} + +#endif diff --git a/asnes/cpu/timing/irq.cpp b/asnes/cpu/timing/irq.cpp new file mode 100755 index 00000000..506a435e --- /dev/null +++ b/asnes/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/asnes/cpu/timing/joypad.cpp b/asnes/cpu/timing/joypad.cpp new file mode 100755 index 00000000..d00cdccb --- /dev/null +++ b/asnes/cpu/timing/joypad.cpp @@ -0,0 +1,28 @@ +#ifdef CPU_CPP + +void CPU::run_auto_joypad_poll() { + uint16 joy1 = 0, joy2 = 0, joy3 = 0, joy4 = 0; + for(unsigned i = 0; i < 16; i++) { + uint8 port0 = input.port_read(0); + uint8 port1 = input.port_read(1); + + 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/asnes/cpu/timing/timing.cpp b/asnes/cpu/timing/timing.cpp new file mode 100755 index 00000000..39dd3a3a --- /dev/null +++ b/asnes/cpu/timing/timing.cpp @@ -0,0 +1,195 @@ +#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) { + input.tick(); + poll_interrupts(); + } + } + + step(clocks); + + 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_coprocessor(); + 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; + } + + //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; + } + + if(status.auto_joypad_poll == true && vcounter() == (ppu.overscan() == false ? 227 : 242)) { + input.poll(); + run_auto_joypad_poll(); + } +} + +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.interrupt_vector = 0xfffc; //reset vector address + + status.dma_active = false; + status.dma_counter = 0; + status.dma_clocks = 0; + status.dma_pending = false; + status.hdma_pending = false; + status.hdma_mode = 0; +} + +#endif diff --git a/asnes/cpu/timing/timing.hpp b/asnes/cpu/timing/timing.hpp new file mode 100755 index 00000000..c185ea4c --- /dev/null +++ b/asnes/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 run_auto_joypad_poll(); diff --git a/asnes/debugger/debugger.cpp b/asnes/debugger/debugger.cpp new file mode 100755 index 00000000..dc70dfec --- /dev/null +++ b/asnes/debugger/debugger.cpp @@ -0,0 +1,106 @@ +#ifdef SYSTEM_CPP + +Debugger debugger; + +void Debugger::breakpoint_test(Debugger::Breakpoint::Source source, Debugger::Breakpoint::Mode 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 != source) continue; + if(breakpoint[i].mode != mode) continue; + + breakpoint[i].counter++; + breakpoint_hit = i; + break_event = BreakEvent::BreakpointHit; + scheduler.exit(Scheduler::ExitReason::DebuggerEvent); + break; + } +} + +uint8 Debugger::read(Debugger::MemorySource 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::APURAM: { + return memory::apuram.read(addr & 0xffff); + } break; + + case MemorySource::VRAM: { + return memory::vram.read(addr & 0xffff); + } break; + + case MemorySource::OAM: { + if(addr & 0x0200) return memory::oam.read(0x0200 + (addr & 0x1f)); + return memory::oam.read(addr & 0x01ff); + } break; + + case MemorySource::CGRAM: { + return memory::cgram.read(addr & 0x01ff); + } break; + } + + return 0x00; +} + +void Debugger::write(Debugger::MemorySource 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 + memory::cartrom.write_protect(false); + bus.write(addr & 0xffffff, data); + memory::cartrom.write_protect(true); + } break; + + case MemorySource::APURAM: { + memory::apuram.write(addr & 0xffff, data); + } break; + + case MemorySource::VRAM: { + memory::vram.write(addr & 0xffff, data); + } break; + + case MemorySource::OAM: { + if(addr & 0x0200) memory::oam.write(0x0200 + (addr & 0x1f), data); + else memory::oam.write(addr & 0x01ff, data); + } break; + + case MemorySource::CGRAM: { + memory::cgram.write(addr & 0x01ff, data); + } break; + } +} + +Debugger::Debugger() { + break_event = BreakEvent::None; + + for(unsigned n = 0; n < Breakpoints; n++) { + breakpoint[n].enabled = false; + breakpoint[n].addr = 0; + breakpoint[n].data = -1; + breakpoint[n].mode = Breakpoint::Mode::Exec; + breakpoint[n].source = Breakpoint::Source::CPUBus; + breakpoint[n].counter = 0; + } + breakpoint_hit = 0; + + step_cpu = false; + step_smp = false; +} + +#endif diff --git a/asnes/debugger/debugger.hpp b/asnes/debugger/debugger.hpp new file mode 100755 index 00000000..28ee52eb --- /dev/null +++ b/asnes/debugger/debugger.hpp @@ -0,0 +1,32 @@ +class Debugger { +public: + enum class BreakEvent : unsigned { + None, + BreakpointHit, + CPUStep, + SMPStep, + } break_event; + + enum { Breakpoints = 8 }; + struct Breakpoint { + bool enabled; + unsigned addr; + signed data; //-1 = unused + enum class Mode : unsigned { Exec, Read, Write } mode; + enum class Source : unsigned { CPUBus, APURAM, VRAM, OAM, CGRAM } source; + unsigned counter; //number of times breakpoint has been hit since being set + } breakpoint[Breakpoints]; + unsigned breakpoint_hit; + void breakpoint_test(Breakpoint::Source source, Breakpoint::Mode mode, unsigned addr, uint8 data); + + bool step_cpu; + bool step_smp; + + enum class MemorySource : unsigned { CPUBus, APURAM, VRAM, OAM, CGRAM }; + uint8 read(MemorySource, unsigned addr); + void write(MemorySource, unsigned addr, uint8 data); + + Debugger(); +}; + +extern Debugger debugger; diff --git a/asnes/dsp/brr.cpp b/asnes/dsp/brr.cpp new file mode 100755 index 00000000..abdf2a0d --- /dev/null +++ b/asnes/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) + memory::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/asnes/dsp/counter.cpp b/asnes/dsp/counter.cpp new file mode 100755 index 00000000..f65fdd26 --- /dev/null +++ b/asnes/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/asnes/dsp/debugger/debugger.cpp b/asnes/dsp/debugger/debugger.cpp new file mode 100755 index 00000000..838a08d8 --- /dev/null +++ b/asnes/dsp/debugger/debugger.cpp @@ -0,0 +1,36 @@ +#ifdef DSP_CPP + +//=========== +//DSPDebugger +//=========== + +unsigned sDSPDebugger::main_volume_left() { return state.regs[0x0c]; } +unsigned sDSPDebugger::main_volume_right() { return state.regs[0x1c]; } +unsigned sDSPDebugger::echo_volume_left() { return state.regs[0x2c]; } +unsigned sDSPDebugger::echo_volume_right() { return state.regs[0x3c]; } +unsigned sDSPDebugger::key_on() { return state.regs[0x4c]; } +unsigned sDSPDebugger::key_off() { return state.regs[0x5c]; } +bool sDSPDebugger::flag_reset() { return state.regs[0x6c] & 0x80; } +bool sDSPDebugger::flag_mute() { return state.regs[0x6c] & 0x40; } +bool sDSPDebugger::flag_echo_disable() { return state.regs[0x6c] & 0x20; } +unsigned sDSPDebugger::flag_noise_clock() { return state.regs[0x6c] & 0x1f; } +unsigned sDSPDebugger::source_end_block() { return state.regs[0x7c]; } +unsigned sDSPDebugger::echo_feedback() { return state.regs[0x0d]; } +unsigned sDSPDebugger::pitch_modulation_enable() { return state.regs[0x2d]; } +unsigned sDSPDebugger::noise_enable() { return state.regs[0x3d]; } +unsigned sDSPDebugger::echo_enable() { return state.regs[0x4d]; } +unsigned sDSPDebugger::source_directory() { return state.regs[0x5d]; } +unsigned sDSPDebugger::echo_start_address() { return state.regs[0x6d]; } +unsigned sDSPDebugger::echo_directory() { return state.regs[0x7d]; } +unsigned sDSPDebugger::echo_filter_coefficient(unsigned n) { return state.regs[(n << 4) + 0x0f]; } +unsigned sDSPDebugger::voice_volume_left(unsigned n) { return state.regs[(n << 4) + 0x00]; } +unsigned sDSPDebugger::voice_volume_right(unsigned n) { return state.regs[(n << 4) + 0x01]; } +unsigned sDSPDebugger::voice_pitch_height(unsigned n) { return state.regs[(n << 4) + 0x02] + (state.regs[(n << 4) + 0x03] << 8); } +unsigned sDSPDebugger::voice_source_number(unsigned n) { return state.regs[(n << 4) + 0x04]; } +unsigned sDSPDebugger::voice_adsr1(unsigned n) { return state.regs[(n << 4) + 0x05]; } +unsigned sDSPDebugger::voice_adsr2(unsigned n) { return state.regs[(n << 4) + 0x06]; } +unsigned sDSPDebugger::voice_gain(unsigned n) { return state.regs[(n << 4) + 0x07]; } +unsigned sDSPDebugger::voice_envx(unsigned n) { return state.regs[(n << 4) + 0x08]; } +unsigned sDSPDebugger::voice_outx(unsigned n) { return state.regs[(n << 4) + 0x09]; } + +#endif diff --git a/asnes/dsp/debugger/debugger.hpp b/asnes/dsp/debugger/debugger.hpp new file mode 100755 index 00000000..5f8d6110 --- /dev/null +++ b/asnes/dsp/debugger/debugger.hpp @@ -0,0 +1,35 @@ +class sDSPDebugger : public sDSP, public DSPDebugger { +public: + //=========== + //DSPDebugger + //=========== + + unsigned main_volume_left(); + unsigned main_volume_right(); + unsigned echo_volume_left(); + unsigned echo_volume_right(); + unsigned key_on(); + unsigned key_off(); + bool flag_reset(); + bool flag_mute(); + bool flag_echo_disable(); + unsigned flag_noise_clock(); + unsigned source_end_block(); + unsigned echo_feedback(); + unsigned pitch_modulation_enable(); + unsigned noise_enable(); + unsigned echo_enable(); + unsigned source_directory(); + unsigned echo_start_address(); + unsigned echo_directory(); + unsigned echo_filter_coefficient(unsigned); + unsigned voice_volume_left(unsigned); + unsigned voice_volume_right(unsigned); + unsigned voice_pitch_height(unsigned); + unsigned voice_source_number(unsigned); + unsigned voice_adsr1(unsigned); + unsigned voice_adsr2(unsigned); + unsigned voice_gain(unsigned); + unsigned voice_envx(unsigned); + unsigned voice_outx(unsigned); +}; diff --git a/asnes/dsp/debugger/dsp-debugger.cpp b/asnes/dsp/debugger/dsp-debugger.cpp new file mode 100755 index 00000000..a361dc19 --- /dev/null +++ b/asnes/dsp/debugger/dsp-debugger.cpp @@ -0,0 +1,54 @@ +#ifdef DSP_CPP + +bool DSPDebugger::property(unsigned id, string &name, string &value) { + unsigned n = 0; + + if(id == n++) { name = "Main Volume - Left"; value = main_volume_left(); return true; } + if(id == n++) { name = "Main Volume - Right"; value = main_volume_right(); return true; } + if(id == n++) { name = "Echo Volume - Left"; value = echo_volume_left(); return true; } + if(id == n++) { name = "Echo Volume - Right"; value = echo_volume_right(); return true; } + if(id == n++) { name = "Key On"; value = string("0x", strhex<2>(key_on())); return true; } + if(id == n++) { name = "Key Off"; value = string("0x", strhex<2>(key_off())); return true; } + if(id == n++) { name = "Flag - Reset"; value = flag_reset(); return true; } + if(id == n++) { name = "Flag - Mute"; value = flag_mute(); return true; } + if(id == n++) { name = "Flag - Echo Disable"; value = flag_echo_disable(); return true; } + if(id == n++) { name = "Flag - Noise Clock"; value = flag_noise_clock(); return true; } + if(id == n++) { name = "Source End Block"; value = source_end_block(); return true; } + if(id == n++) { name = "Echo Feedback"; value = echo_feedback(); return true; } + if(id == n++) { name = "Pitch Modulation Enable"; value = string("0x", strhex<2>(pitch_modulation_enable())); return true; } + if(id == n++) { name = "Noise Enable"; value = string("0x", strhex<2>(noise_enable())); return true; } + if(id == n++) { name = "Echo Enable"; value = string("0x", strhex<2>(echo_enable())); return true; } + if(id == n++) { name = "Source Directory"; value = source_directory(); return true; } + if(id == n++) { name = "Echo Start Address"; value = echo_start_address(); return true; } + if(id == n++) { name = "Echo Directory"; value = echo_directory(); return true; } + + for(unsigned i = 0; i < 8; i++) { + if(id == n++) { + name = string("Coefficient ", i); + value = string("0x", strhex<2>(echo_filter_coefficient(i))); + return true; + } + } + + for(unsigned i = 0; i < 8; i++) { + if(id == n++) { + name = string("Voice ", i); + value = ""; + return true; + } + + if(id == n++) { name = "Volume - Left"; value = voice_volume_left(i); return true; } + if(id == n++) { name = "Volume - Right"; value = voice_volume_right(i); return true; } + if(id == n++) { name = "Pitch Height"; value = string("0x", strhex<4>(voice_pitch_height(i))); return true; } + if(id == n++) { name = "Source Number"; value = voice_source_number(i); return true; } + if(id == n++) { name = "ADSR1"; value = voice_adsr1(i); return true; } + if(id == n++) { name = "ADSR2"; value = voice_adsr2(i); return true; } + if(id == n++) { name = "GAIN"; value = voice_gain(i); return true; } + if(id == n++) { name = "ENVX"; value = voice_envx(i); return true; } + if(id == n++) { name = "OUTX"; value = voice_outx(i); return true; } + } + + return false; +} + +#endif diff --git a/asnes/dsp/debugger/dsp-debugger.hpp b/asnes/dsp/debugger/dsp-debugger.hpp new file mode 100755 index 00000000..698724b5 --- /dev/null +++ b/asnes/dsp/debugger/dsp-debugger.hpp @@ -0,0 +1,32 @@ +struct DSPDebugger : ChipDebugger { + bool property(unsigned id, string &name, string &value); + + virtual unsigned main_volume_left() { return 0; } + virtual unsigned main_volume_right() { return 0; } + virtual unsigned echo_volume_left() { return 0; } + virtual unsigned echo_volume_right() { return 0; } + virtual unsigned key_on() { return 0; } + virtual unsigned key_off() { return 0; } + virtual bool flag_reset() { return 0; } + virtual bool flag_mute() { return 0; } + virtual bool flag_echo_disable() { return 0; } + virtual unsigned flag_noise_clock() { return 0; } + virtual unsigned source_end_block() { return 0; } + virtual unsigned echo_feedback() { return 0; } + virtual unsigned pitch_modulation_enable() { return 0; } + virtual unsigned noise_enable() { return 0; } + virtual unsigned echo_enable() { return 0; } + virtual unsigned source_directory() { return 0; } + virtual unsigned echo_start_address() { return 0; } + virtual unsigned echo_directory() { return 0; } + virtual unsigned echo_filter_coefficient(unsigned) { return 0; } + virtual unsigned voice_volume_left(unsigned) { return 0; } + virtual unsigned voice_volume_right(unsigned) { return 0; } + virtual unsigned voice_pitch_height(unsigned) { return 0; } + virtual unsigned voice_source_number(unsigned) { return 0; } + virtual unsigned voice_adsr1(unsigned) { return 0; } + virtual unsigned voice_adsr2(unsigned) { return 0; } + virtual unsigned voice_gain(unsigned) { return 0; } + virtual unsigned voice_envx(unsigned) { return 0; } + virtual unsigned voice_outx(unsigned) { return 0; } +}; diff --git a/asnes/dsp/dsp.cpp b/asnes/dsp/dsp.cpp new file mode 100755 index 00000000..52edd1cf --- /dev/null +++ b/asnes/dsp/dsp.cpp @@ -0,0 +1,304 @@ +#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(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(smp.thread); +} + +void DSP::Enter() { dsp.enter(); } + +void DSP::enter() { + while(true) { + if(scheduler.sync == Scheduler::SynchronizeMode::All) { + scheduler.exit(Scheduler::ExitReason::SynchronizeEvent); + } + + voice_5(voice[0]); + voice_2(voice[1]); + tick(); + + voice_6(voice[0]); + voice_3(voice[1]); + tick(); + + voice_7(voice[0]); + voice_4(voice[1]); + voice_1(voice[3]); + tick(); + + voice_8(voice[0]); + voice_5(voice[1]); + voice_2(voice[2]); + tick(); + + voice_9(voice[0]); + voice_6(voice[1]); + voice_3(voice[2]); + tick(); + + voice_7(voice[1]); + voice_4(voice[2]); + voice_1(voice[4]); + tick(); + + voice_8(voice[1]); + voice_5(voice[2]); + voice_2(voice[3]); + tick(); + + voice_9(voice[1]); + voice_6(voice[2]); + voice_3(voice[3]); + tick(); + + voice_7(voice[2]); + voice_4(voice[3]); + voice_1(voice[5]); + tick(); + + voice_8(voice[2]); + voice_5(voice[3]); + voice_2(voice[4]); + tick(); + + voice_9(voice[2]); + voice_6(voice[3]); + voice_3(voice[4]); + tick(); + + voice_7(voice[3]); + voice_4(voice[4]); + voice_1(voice[6]); + tick(); + + voice_8(voice[3]); + voice_5(voice[4]); + voice_2(voice[5]); + tick(); + + voice_9(voice[3]); + voice_6(voice[4]); + voice_3(voice[5]); + tick(); + + voice_7(voice[4]); + voice_4(voice[5]); + voice_1(voice[7]); + tick(); + + voice_8(voice[4]); + voice_5(voice[5]); + voice_2(voice[6]); + tick(); + + voice_9(voice[4]); + voice_6(voice[5]); + voice_3(voice[6]); + tick(); + + voice_1(voice[0]); + voice_7(voice[5]); + voice_4(voice[6]); + tick(); + + voice_8(voice[5]); + voice_5(voice[6]); + voice_2(voice[7]); + tick(); + + voice_9(voice[5]); + voice_6(voice[6]); + voice_3(voice[7]); + tick(); + + voice_1(voice[1]); + voice_7(voice[6]); + voice_4(voice[7]); + tick(); + + voice_8(voice[6]); + voice_5(voice[7]); + voice_2(voice[0]); + tick(); + + voice_3a(voice[0]); + voice_9(voice[6]); + voice_6(voice[7]); + echo_22(); + tick(); + + voice_7(voice[7]); + echo_23(); + tick(); + + voice_8(voice[7]); + echo_24(); + tick(); + + voice_3b(voice[0]); + voice_9(voice[7]); + echo_25(); + tick(); + + echo_26(); + tick(); + + misc_27(); + echo_27(); + tick(); + + misc_28(); + echo_28(); + tick(); + + misc_29(); + echo_29(); + tick(); + + misc_30(); + voice_3c(voice[0]); + echo_30(); + tick(); + + voice_4(voice[0]); + voice_1(voice[2]); + tick(); + } +} + +void DSP::tick() { + step(3 * 8); + synchronize_smp(); +} + +/* register interface for S-SMP $00f2,$00f3 */ + +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; + } + + reset(); +} + +void DSP::reset() { + create(Enter, system.apu_frequency()); + + REG(flg) = 0xe0; + + state.noise = 0x4000; + state.echo_hist_pos = 0; + state.every_other_sample = 1; + state.echo_offset = 0; + state.counter = 0; +} + +DSP::DSP() { + static_assert(sizeof(int) >= 32 / 8, "int >= 32-bits"); + static_assert((int8)0x80 == -0x80, "8-bit sign extension"); + static_assert((int16)0x8000 == -0x8000, "16-bit sign extension"); + static_assert((uint16)0xffff0000 == 0, "16-bit unsigned clip"); + static_assert((-1 >> 1) == -1, "arithmetic shift right"); + + //-0x8000 <= n <= +0x7fff + assert(sclamp<16>(+0x8000) == +0x7fff); + assert(sclamp<16>(-0x8001) == -0x8000); +} + +DSP::~DSP() { +} + +} diff --git a/asnes/dsp/dsp.hpp b/asnes/dsp/dsp.hpp new file mode 100755 index 00000000..eb7885b8 --- /dev/null +++ b/asnes/dsp/dsp.hpp @@ -0,0 +1,179 @@ +class DSP : public Processor { +public: + //synchronization + alwaysinline void step(unsigned clocks); + alwaysinline void synchronize_smp(); + + static void Enter(); + void enter(); + void tick(); + + uint8 read(uint8 addr); + void write(uint8 addr, uint8 data); + + void power(); + void reset(); + + void serialize(serializer&); + DSP(); + ~DSP(); + +protected: + //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(); + + friend class DSPDebugger; +}; + +#if defined(DEBUGGER) + #include "debugger/debugger.hpp" + extern DSPDebugger dsp; +#else + extern DSP dsp; +#endif diff --git a/asnes/dsp/echo.cpp b/asnes/dsp/echo.cpp new file mode 100755 index 00000000..c0cf5f37 --- /dev/null +++ b/asnes/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 = memory::apuram[(uint16)(addr + 0)]; + uint8 hi = memory::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]; + memory::apuram[(uint16)(addr + 0)] = s; + memory::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/asnes/dsp/envelope.cpp b/asnes/dsp/envelope.cpp new file mode 100755 index 00000000..9ae0d3e0 --- /dev/null +++ b/asnes/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/asnes/dsp/gaussian.cpp b/asnes/dsp/gaussian.cpp new file mode 100755 index 00000000..80aed8ad --- /dev/null +++ b/asnes/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/asnes/dsp/misc.cpp b/asnes/dsp/misc.cpp new file mode 100755 index 00000000..244fc51f --- /dev/null +++ b/asnes/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/asnes/dsp/serialization.cpp b/asnes/dsp/serialization.cpp new file mode 100755 index 00000000..0595c11e --- /dev/null +++ b/asnes/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/asnes/dsp/voice.cpp b/asnes/dsp/voice.cpp new file mode 100755 index 00000000..2e882f21 --- /dev/null +++ b/asnes/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 = memory::apuram[(uint16)(addr + 0)]; + uint8 hi = memory::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 = memory::apuram[(uint16)(v.brr_addr + v.brr_offset)]; + state.t_brr_header = memory::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/asnes/input/input.cpp b/asnes/input/input.cpp new file mode 100755 index 00000000..dbd3ad71 --- /dev/null +++ b/asnes/input/input.cpp @@ -0,0 +1,344 @@ +#ifdef SYSTEM_CPP + +Input input; + +uint8 Input::port_read(bool portnumber) { + if(cartridge.has_serial() && portnumber == 1) { + return serial.latch(); + } + + port_t &p = port[portnumber]; + + switch(p.device) { + case Device::Joypad: { + if(cpu.joylatch() == 0) { + if(p.counter0 >= 16) return 1; + return system.interface->input_poll(portnumber, p.device, 0, p.counter0++); + } else { + return system.interface->input_poll(portnumber, p.device, 0, 0); + } + } //case Device::Joypad + + case Device::Multitap: { + if(cpu.joylatch()) return 2; //when latch is high -- data2 = 1, data1 = 0 + + unsigned deviceidx, deviceindex0, deviceindex1; + uint8 mask = (portnumber == 0 ? 0x40 : 0x80); + + if(cpu.pio() & mask) { + deviceidx = p.counter0; + if(deviceidx >= 16) return 3; + p.counter0++; + + deviceindex0 = 0; //controller 1 + deviceindex1 = 1; //controller 2 + } else { + deviceidx = p.counter1; + if(deviceidx >= 16) return 3; + p.counter1++; + + deviceindex0 = 2; //controller 3 + deviceindex1 = 3; //controller 4 + } + + return (system.interface->input_poll(portnumber, p.device, deviceindex0, deviceidx) << 0) + | (system.interface->input_poll(portnumber, p.device, deviceindex1, deviceidx) << 1); + } //case Device::Multitap + + case Device::Mouse: { + if(p.counter0 >= 32) return 1; + + int position_x = system.interface->input_poll(portnumber, p.device, 0, (unsigned)MouseID::X); //-n = left, 0 = center, +n = right + int position_y = system.interface->input_poll(portnumber, p.device, 0, (unsigned)MouseID::Y); //-n = up, 0 = center, +n = right + + 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_x) + + position_x = min(127, position_x); //range = 0 - 127 + position_y = min(127, position_y); //range = 0 - 127 + + switch(p.counter0++) { 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 system.interface->input_poll(portnumber, p.device, 0, (unsigned)MouseID::Right); + case 9: return system.interface->input_poll(portnumber, p.device, 0, (unsigned)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) & 1; + 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) & 1; + 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; + } + } //case Device::Mouse + + case Device::SuperScope: { + if(portnumber == 0) break; //Super Scope in port 1 not supported ... + if(p.counter0 >= 8) return 1; + + if(p.counter0 == 0) { + //turbo is a switch; toggle is edge sensitive + bool turbo = system.interface->input_poll(portnumber, p.device, 0, (unsigned)SuperScopeID::Turbo); + if(turbo && !p.superscope.turbolock) { + p.superscope.turbo = !p.superscope.turbo; //toggle state + p.superscope.turbolock = true; + } else if(!turbo) { + p.superscope.turbolock = false; + } + + //trigger is a button + //if turbo is active, trigger is level sensitive; otherwise it is edge sensitive + p.superscope.trigger = false; + bool trigger = system.interface->input_poll(portnumber, p.device, 0, (unsigned)SuperScopeID::Trigger); + if(trigger && (p.superscope.turbo || !p.superscope.triggerlock)) { + p.superscope.trigger = true; + p.superscope.triggerlock = true; + } else if(!trigger) { + p.superscope.triggerlock = false; + } + + //cursor is a button; it is always level sensitive + p.superscope.cursor = system.interface->input_poll(portnumber, p.device, 0, (unsigned)SuperScopeID::Cursor); + + //pause is a button; it is always edge sensitive + p.superscope.pause = false; + bool pause = system.interface->input_poll(portnumber, p.device, 0, (unsigned)SuperScopeID::Pause); + if(pause && !p.superscope.pauselock) { + p.superscope.pause = true; + p.superscope.pauselock = true; + } else if(!pause) { + p.superscope.pauselock = false; + } + + p.superscope.offscreen = + p.superscope.x < 0 || p.superscope.x >= 256 + || p.superscope.y < 0 || p.superscope.y >= (ppu.overscan() ? 240 : 225); + } + + switch(p.counter0++) { + case 0: return p.superscope.trigger; + case 1: return p.superscope.cursor; + case 2: return p.superscope.turbo; + case 3: return p.superscope.pause; + case 4: return 0; + case 5: return 0; + case 6: return p.superscope.offscreen; + case 7: return 0; //noise (1 = yes) + } + } //case Device::SuperScope + + case Device::Justifier: + case Device::Justifiers: { + if(portnumber == 0) break; //Justifier in port 1 not supported ... + if(p.counter0 >= 32) return 1; + + if(p.counter0 == 0) { + p.justifier.trigger1 = system.interface->input_poll(portnumber, p.device, 0, (unsigned)JustifierID::Trigger); + p.justifier.start1 = system.interface->input_poll(portnumber, p.device, 0, (unsigned)JustifierID::Start); + + if(p.device == Device::Justifiers) { + p.justifier.trigger2 = system.interface->input_poll(portnumber, p.device, 1, (unsigned)JustifierID::Trigger); + p.justifier.start2 = system.interface->input_poll(portnumber, p.device, 1, (unsigned)JustifierID::Start); + } else { + p.justifier.x2 = -1; + p.justifier.y2 = -1; + + p.justifier.trigger2 = false; + p.justifier.start2 = false; + } + } + + switch(p.counter0++) { + 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 p.justifier.trigger1; + case 25: return p.justifier.trigger2; + case 26: return p.justifier.start1; + case 27: return p.justifier.start2; + case 28: return p.justifier.active; + + case 29: return 0; + case 30: return 0; + case 31: return 0; + } + } //case Device::Justifier(s) + } //switch(p.device) + + //no device connected + return 0; +} + +//scan all input; update cursor positions if needed +void Input::update() { + system.interface->input_poll(); + port_t &p = port[1]; + + switch(p.device) { + case Device::SuperScope: { + int x = system.interface->input_poll(1, p.device, 0, (unsigned)SuperScopeID::X); + int y = system.interface->input_poll(1, p.device, 0, (unsigned)SuperScopeID::Y); + x += p.superscope.x; + y += p.superscope.y; + p.superscope.x = max(-16, min(256 + 16, x)); + p.superscope.y = max(-16, min(240 + 16, y)); + + latchx = p.superscope.x; + latchy = p.superscope.y; + } break; + + case Device::Justifier: + case Device::Justifiers: { + int x1 = system.interface->input_poll(1, p.device, 0, (unsigned)JustifierID::X); + int y1 = system.interface->input_poll(1, p.device, 0, (unsigned)JustifierID::Y); + x1 += p.justifier.x1; + y1 += p.justifier.y1; + p.justifier.x1 = max(-16, min(256 + 16, x1)); + p.justifier.y1 = max(-16, min(240 + 16, y1)); + + int x2 = system.interface->input_poll(1, p.device, 1, (unsigned)JustifierID::X); + int y2 = system.interface->input_poll(1, p.device, 1, (unsigned)JustifierID::Y); + x2 += p.justifier.x2; + y2 += p.justifier.y2; + p.justifier.x2 = max(-16, min(256 + 16, x2)); + p.justifier.y2 = max(-16, min(240 + 16, y2)); + + if(p.justifier.active == 0) { + latchx = p.justifier.x1; + latchy = p.justifier.y1; + } else { + latchx = (p.device == Device::Justifiers ? p.justifier.x2 : -1); + latchy = (p.device == Device::Justifiers ? p.justifier.y2 : -1); + } + } break; + } + + if(latchy < 0 || latchy >= (ppu.overscan() ? 240 : 225) || latchx < 0 || latchx >= 256) { + //cursor is offscreen, set to invalid position so counters are not latched + latchx = ~0; + latchy = ~0; + } else { + //cursor is onscreen + latchx += 40; //offset trigger position to simulate hardware latching delay + latchx <<= 2; //dot -> clock conversion + latchx += 2; //align trigger on half-dot ala interrupts (speed optimization for sCPU::add_clocks) + } +} + +void Input::port_set_device(bool portnumber, Device device) { + port_t &p = port[portnumber]; + + p.device = device; + p.counter0 = 0; + p.counter1 = 0; + + //set iobit to true if device is capable of latching PPU counters + iobit = port[1].device == Device::SuperScope + || port[1].device == Device::Justifier + || port[1].device == Device::Justifiers; + latchx = -1; + latchy = -1; + + if(device == Device::SuperScope) { + p.superscope.x = 256 / 2; + p.superscope.y = 240 / 2; + + p.superscope.trigger = false; + p.superscope.cursor = false; + p.superscope.turbo = false; + p.superscope.pause = false; + p.superscope.offscreen = false; + + p.superscope.turbolock = false; + p.superscope.triggerlock = false; + p.superscope.pauselock = false; + } else if(device == Device::Justifier) { + p.justifier.active = 0; + p.justifier.x1 = 256 / 2; + p.justifier.y1 = 240 / 2; + p.justifier.x2 = -1; + p.justifier.y2 = -1; + + p.justifier.trigger1 = false; + p.justifier.trigger2 = false; + p.justifier.start1 = false; + p.justifier.start2 = false; + } else if(device == Device::Justifiers) { + p.justifier.active = 0; + p.justifier.x1 = 256 / 2 - 16; + p.justifier.y1 = 240 / 2; + p.justifier.x2 = 256 / 2 + 16; + p.justifier.y2 = 240 / 2; + + p.justifier.trigger1 = false; + p.justifier.trigger2 = false; + p.justifier.start1 = false; + p.justifier.start2 = false; + } +} + +void Input::poll() { + port[0].counter0 = 0; + port[0].counter1 = 0; + port[1].counter0 = 0; + port[1].counter1 = 0; + + port[1].justifier.active = !port[1].justifier.active; +} + +void Input::init() { +} + +#endif diff --git a/asnes/input/input.hpp b/asnes/input/input.hpp new file mode 100755 index 00000000..e24ac2ed --- /dev/null +++ b/asnes/input/input.hpp @@ -0,0 +1,87 @@ +class Input { +public: + enum class Device : unsigned { + None, + Joypad, + Multitap, + Mouse, + SuperScope, + Justifier, + Justifiers, + }; + + enum class JoypadID : unsigned { + B = 0, Y = 1, Select = 2, Start = 3, + Up = 4, Down = 5, Left = 6, Right = 7, + A = 8, X = 9, L = 10, R = 11, + }; + + enum class MouseID : unsigned { + X = 0, Y = 1, Left = 2, Right = 3, + }; + + enum class SuperScopeID : unsigned { + X = 0, Y = 1, Trigger = 2, Cursor = 3, Turbo = 4, Pause = 5, + }; + + enum class JustifierID : unsigned { + X = 0, Y = 1, Trigger = 2, Start = 3, + }; + + uint8 port_read(bool port); + void port_set_device(bool port, Device device); + void init(); + void poll(); + void update(); + + //light guns (Super Scope, Justifier(s)) strobe IOBit whenever the CRT + //beam cannon is detected. this needs to be tested at the cycle level + //(hence inlining here for speed) to avoid 'dead space' during DRAM refresh. + //iobit is updated during port_set_device(), + //latchx, latchy are updated during update() (once per frame) + alwaysinline void tick() { + //only test if Super Scope or Justifier is connected + if(iobit && cpu.vcounter() == latchy && cpu.hcounter() == latchx) { + ppu.latch_counters(); + } + } + +private: + bool iobit; + int16_t latchx, latchy; + + struct port_t { + Device device; + unsigned counter0; //read counters + unsigned counter1; + + struct superscope_t { + int x, y; + + bool trigger; + bool cursor; + bool turbo; + bool pause; + bool offscreen; + + bool turbolock; + bool triggerlock; + bool pauselock; + } superscope; + + struct justifier_t { + bool active; + + int x1, x2; + int y1, y2; + + bool trigger1, trigger2; + bool start1, start2; + } justifier; + } port[2]; + + friend class System; + friend class Video; +}; + +extern Input input; diff --git a/asnes/interface/interface.hpp b/asnes/interface/interface.hpp new file mode 100755 index 00000000..e9ec2635 --- /dev/null +++ b/asnes/interface/interface.hpp @@ -0,0 +1,7 @@ +class Interface { +public: + virtual void video_refresh(const uint16_t *data, unsigned width, unsigned height) {} + virtual void audio_sample(uint16_t l_sample, uint16_t r_sample) {} + virtual void input_poll() {} + virtual int16_t input_poll(bool port, Input::Device device, unsigned index, unsigned id) { return 0; } +}; diff --git a/asnes/libsnes/libsnes.cpp b/asnes/libsnes/libsnes.cpp new file mode 100755 index 00000000..6dec61b8 --- /dev/null +++ b/asnes/libsnes/libsnes.cpp @@ -0,0 +1,264 @@ +#include "libsnes.hpp" +#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; + + void video_refresh(const uint16_t *data, unsigned width, unsigned height) { + if(pvideo_refresh) return pvideo_refresh(data, width, height); + } + + void audio_sample(uint16_t left, uint16_t right) { + if(paudio_sample) return paudio_sample(left, right); + } + + void input_poll() { + if(pinput_poll) return pinput_poll(); + } + + int16_t input_poll(bool port, SNES::Input::Device device, unsigned index, unsigned id) { + if(pinput_state) return pinput_state(port, (unsigned)device, index, id); + return 0; + } + + Interface() : pvideo_refresh(0), paudio_sample(0), pinput_poll(0), pinput_state(0) { + } +}; + +static Interface interface; + +unsigned snes_library_revision_major(void) { + return 1; +} + +unsigned snes_library_revision_minor(void) { + return 0; +} + +void snes_set_video_refresh(snes_video_refresh_t video_refresh) { + interface.pvideo_refresh = video_refresh; +} + +void snes_set_audio_sample(snes_audio_sample_t audio_sample) { + interface.paudio_sample = audio_sample; +} + +void snes_set_input_poll(snes_input_poll_t input_poll) { + interface.pinput_poll = input_poll; +} + +void snes_set_input_state(snes_input_state_t input_state) { + interface.pinput_state = input_state; +} + +void snes_set_controller_port_device(bool port, unsigned device) { + SNES::input.port_set_device(port, (SNES::Input::Device)device); +} + +void snes_init(void) { + SNES::system.init(&interface); + SNES::input.port_set_device(0, SNES::Input::Device::Joypad); + SNES::input.port_set_device(1, SNES::Input::Device::Joypad); +} + +void snes_term(void) { + SNES::system.term(); +} + +void snes_power(void) { + SNES::system.power(); +} + +void snes_reset(void) { + SNES::system.reset(); +} + +void snes_run(void) { + SNES::system.run(); +} + +unsigned snes_serialize_size(void) { + return SNES::system.serialize_size(); +} + +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; +} + +bool snes_unserialize(const uint8_t *data, unsigned size) { + serializer s(data, size); + return SNES::system.unserialize(s); +} + +void snes_cheat_reset(void) { + SNES::cheat.reset(); + SNES::cheat.synchronize(); +} + +void snes_cheat_set(unsigned index, bool enabled, const char *code) { + SNES::cheat[index] = code; + SNES::cheat[index].enabled = enabled; + SNES::cheat.synchronize(); +} + +bool snes_load_cartridge_normal( + const char *rom_xml, const uint8_t *rom_data, unsigned rom_size +) { + snes_cheat_reset(); + if(rom_data) SNES::memory::cartrom.copy(rom_data, rom_size); + string xmlrom = (rom_xml && *rom_xml) ? string(rom_xml) : snes_information(rom_data, rom_size).xml_memory_map; + SNES::cartridge.load(SNES::Cartridge::Mode::Normal, { xmlrom }); + SNES::system.power(); + return true; +} + +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::memory::cartrom.copy(rom_data, rom_size); + string xmlrom = (rom_xml && *rom_xml) ? string(rom_xml) : snes_information(rom_data, rom_size).xml_memory_map; + if(bsx_data) SNES::memory::bsxflash.copy(bsx_data, bsx_size); + string xmlbsx = (bsx_xml && *bsx_xml) ? string(bsx_xml) : snes_information(bsx_data, bsx_size).xml_memory_map; + SNES::cartridge.load(SNES::Cartridge::Mode::BsxSlotted, { xmlrom, xmlbsx }); + SNES::system.power(); + return true; +} + +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::memory::cartrom.copy(rom_data, rom_size); + string xmlrom = (rom_xml && *rom_xml) ? string(rom_xml) : snes_information(rom_data, rom_size).xml_memory_map; + if(bsx_data) SNES::memory::bsxflash.copy(bsx_data, bsx_size); + string xmlbsx = (bsx_xml && *bsx_xml) ? string(bsx_xml) : snes_information(bsx_data, bsx_size).xml_memory_map; + SNES::cartridge.load(SNES::Cartridge::Mode::Bsx, { xmlrom, xmlbsx }); + SNES::system.power(); + return true; +} + +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::memory::cartrom.copy(rom_data, rom_size); + string xmlrom = (rom_xml && *rom_xml) ? string(rom_xml) : snes_information(rom_data, rom_size).xml_memory_map; + if(sta_data) SNES::memory::stArom.copy(sta_data, sta_size); + string xmlsta = (sta_xml && *sta_xml) ? string(sta_xml) : snes_information(sta_data, sta_size).xml_memory_map; + if(stb_data) SNES::memory::stBrom.copy(stb_data, stb_size); + string xmlstb = (stb_xml && *stb_xml) ? string(stb_xml) : snes_information(stb_data, stb_size).xml_memory_map; + SNES::cartridge.load(SNES::Cartridge::Mode::SufamiTurbo, { xmlrom, xmlsta, xmlstb }); + SNES::system.power(); + return true; +} + +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::memory::cartrom.copy(rom_data, rom_size); + string xmlrom = (rom_xml && *rom_xml) ? string(rom_xml) : snes_information(rom_data, rom_size).xml_memory_map; + if(dmg_data) SNES::memory::gbrom.copy(dmg_data, dmg_size); + string xmldmg = (dmg_xml && *dmg_xml) ? string(dmg_xml) : snes_information(dmg_data, dmg_size).xml_memory_map; + SNES::cartridge.load(SNES::Cartridge::Mode::SuperGameBoy, { xmlrom, xmldmg }); + SNES::system.power(); + return true; +} + +void snes_unload_cartridge(void) { + SNES::cartridge.unload(); +} + +bool snes_get_region(void) { + return SNES::system.region() == SNES::System::Region::NTSC ? 0 : 1; +} + +uint8_t* snes_get_memory_data(unsigned id) { + if(SNES::cartridge.loaded() == false) return 0; + + switch(id) { + case SNES_MEMORY_CARTRIDGE_RAM: + return SNES::memory::cartram.data(); + case SNES_MEMORY_CARTRIDGE_RTC: + return SNES::memory::cartrtc.data(); + case SNES_MEMORY_BSX_RAM: + if(SNES::cartridge.mode() != SNES::Cartridge::Mode::Bsx) break; + return SNES::memory::bsxram.data(); + case SNES_MEMORY_BSX_PRAM: + if(SNES::cartridge.mode() != SNES::Cartridge::Mode::Bsx) break; + return SNES::memory::bsxpram.data(); + case SNES_MEMORY_SUFAMI_TURBO_A_RAM: + if(SNES::cartridge.mode() != SNES::Cartridge::Mode::SufamiTurbo) break; + return SNES::memory::stAram.data(); + case SNES_MEMORY_SUFAMI_TURBO_B_RAM: + if(SNES::cartridge.mode() != SNES::Cartridge::Mode::SufamiTurbo) break; + return SNES::memory::stBram.data(); + case SNES_MEMORY_GAME_BOY_RAM: + if(SNES::cartridge.mode() != SNES::Cartridge::Mode::SuperGameBoy) break; + SNES::supergameboy.save(); + return SNES::memory::gbram.data(); + case SNES_MEMORY_GAME_BOY_RTC: + if(SNES::cartridge.mode() != SNES::Cartridge::Mode::SuperGameBoy) break; + SNES::supergameboy.save(); + return SNES::memory::gbrtc.data(); + } + + return 0; +} + +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::memory::cartram.size(); + break; + case SNES_MEMORY_CARTRIDGE_RTC: + size = SNES::memory::cartrtc.size(); + break; + case SNES_MEMORY_BSX_RAM: + if(SNES::cartridge.mode() != SNES::Cartridge::Mode::Bsx) break; + size = SNES::memory::bsxram.size(); + break; + case SNES_MEMORY_BSX_PRAM: + if(SNES::cartridge.mode() != SNES::Cartridge::Mode::Bsx) break; + size = SNES::memory::bsxpram.size(); + break; + case SNES_MEMORY_SUFAMI_TURBO_A_RAM: + if(SNES::cartridge.mode() != SNES::Cartridge::Mode::SufamiTurbo) break; + size = SNES::memory::stAram.size(); + break; + case SNES_MEMORY_SUFAMI_TURBO_B_RAM: + if(SNES::cartridge.mode() != SNES::Cartridge::Mode::SufamiTurbo) break; + size = SNES::memory::stBram.size(); + break; + case SNES_MEMORY_GAME_BOY_RAM: + if(SNES::cartridge.mode() != SNES::Cartridge::Mode::SuperGameBoy) break; + size = SNES::memory::gbram.size(); + break; + case SNES_MEMORY_GAME_BOY_RTC: + if(SNES::cartridge.mode() != SNES::Cartridge::Mode::SuperGameBoy) break; + size = SNES::memory::gbrtc.size(); + break; + } + + if(size == -1U) size = 0; + return size; +} diff --git a/asnes/libsnes/libsnes.hpp b/asnes/libsnes/libsnes.hpp new file mode 100755 index 00000000..7e151738 --- /dev/null +++ b/asnes/libsnes/libsnes.hpp @@ -0,0 +1,123 @@ +#ifndef LIBSNES_HPP +#define LIBSNES_HPP + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define SNES_PORT_1 0 +#define SNES_PORT_2 1 + +#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_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 + +#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 + +typedef void (*snes_video_refresh_t)(const uint16_t *data, unsigned width, unsigned height); +typedef void (*snes_audio_sample_t)(uint16_t left, uint16_t right); +typedef void (*snes_input_poll_t)(void); +typedef int16_t (*snes_input_state_t)(bool port, unsigned device, unsigned index, unsigned id); + +unsigned snes_library_revision_major(void); +unsigned snes_library_revision_minor(void); + +void snes_set_video_refresh(snes_video_refresh_t); +void snes_set_audio_sample(snes_audio_sample_t); +void snes_set_input_poll(snes_input_poll_t); +void snes_set_input_state(snes_input_state_t); + +void snes_set_controller_port_device(bool port, unsigned device); + +void snes_init(void); +void snes_term(void); +void snes_power(void); +void snes_reset(void); +void snes_run(void); + +unsigned snes_serialize_size(void); +bool snes_serialize(uint8_t *data, unsigned size); +bool snes_unserialize(const uint8_t *data, unsigned size); + +void snes_cheat_reset(void); +void snes_cheat_set(unsigned index, bool enabled, const char *code); + +bool snes_load_cartridge_normal( + const char *rom_xml, const uint8_t *rom_data, unsigned rom_size +); + +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 +); + +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 +); + +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 +); + +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 +); + +void snes_unload_cartridge(void); + +bool snes_get_region(void); +uint8_t* snes_get_memory_data(unsigned id); +unsigned snes_get_memory_size(unsigned id); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/asnes/memory/memory-inline.hpp b/asnes/memory/memory-inline.hpp new file mode 100755 index 00000000..d49f8d48 --- /dev/null +++ b/asnes/memory/memory-inline.hpp @@ -0,0 +1,69 @@ +//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_ = -1U; + write_protect_ = false; +} + +void MappedRAM::map(uint8 *source, unsigned length) { + reset(); + data_ = source; + size_ = data_ && length > 0 ? length : -1U; +} + +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_(-1U), write_protect_(false) {} + +//Bus + +uint8 Bus::read(unsigned addr) { + #if defined(CHEAT_SYSTEM) + if(cheat.active() && cheat.exists(addr)) { + uint8 r; + if(cheat.read(addr, r)) return r; + } + #endif + + Page &p = page[addr >> 8]; + return p.access->read(p.offset + addr); +} + +void Bus::write(unsigned addr, uint8 data) { + Page &p = page[addr >> 8]; + return p.access->write(p.offset + addr, data); +} diff --git a/asnes/memory/memory.cpp b/asnes/memory/memory.cpp new file mode 100755 index 00000000..affd4f2e --- /dev/null +++ b/asnes/memory/memory.cpp @@ -0,0 +1,158 @@ +#include + +#define MEMORY_CPP +namespace SNES { + +Bus bus; + +#include "serialization.cpp" + +namespace memory { + MMIOAccess mmio; + StaticRAM wram(128 * 1024); + StaticRAM apuram(64 * 1024); + StaticRAM vram(64 * 1024); + StaticRAM oam(544); + StaticRAM cgram(512); + + UnmappedMemory memory_unmapped; + UnmappedMMIO mmio_unmapped; +}; + +unsigned UnmappedMemory::size() const { return 16 * 1024 * 1024; } +uint8 UnmappedMemory::read(unsigned) { return cpu.regs.mdr; } +void UnmappedMemory::write(unsigned, uint8) {} + +uint8 UnmappedMMIO::mmio_read(unsigned) { return cpu.regs.mdr; } +void UnmappedMMIO::mmio_write(unsigned, uint8) {} + +void MMIOAccess::map(unsigned addr, MMIO &access) { + //MMIO: $[00-3f]:[2000-5fff] + mmio[(addr - 0x2000) & 0x3fff] = &access; +} + +uint8 MMIOAccess::read(unsigned addr) { + return mmio[(addr - 0x2000) & 0x3fff]->mmio_read(addr); +} + +void MMIOAccess::write(unsigned addr, uint8 data) { + mmio[(addr - 0x2000) & 0x3fff]->mmio_write(addr, data); +} + +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(unsigned addr, Memory &access, unsigned offset) { + page[addr >> 8].access = &access; + page[addr >> 8].offset = offset - addr; +} + +void Bus::map( + MapMode mode, + uint8 bank_lo, uint8 bank_hi, + uint16 addr_lo, uint16 addr_hi, + Memory &access, unsigned offset, unsigned size +) { + assert(bank_lo <= bank_hi); + assert(addr_lo <= addr_hi); + if(access.size() == -1U) return; + + uint8 page_lo = addr_lo >> 8; + uint8 page_hi = addr_hi >> 8; + unsigned index = 0; + + switch(mode) { + case MapMode::Direct: { + for(unsigned bank = bank_lo; bank <= bank_hi; bank++) { + for(unsigned page = page_lo; page <= page_hi; page++) { + map((bank << 16) + (page << 8), access, (bank << 16) + (page << 8)); + } + } + } break; + + case MapMode::Linear: { + for(unsigned bank = bank_lo; bank <= bank_hi; bank++) { + for(unsigned page = page_lo; page <= page_hi; page++) { + map((bank << 16) + (page << 8), access, mirror(offset + index, access.size())); + index += 256; + if(size) index %= size; + } + } + } break; + + case MapMode::Shadow: { + for(unsigned bank = bank_lo; bank <= bank_hi; bank++) { + index += page_lo * 256; + if(size) index %= size; + + for(unsigned page = page_lo; page <= page_hi; page++) { + map((bank << 16) + (page << 8), access, mirror(offset + index, access.size())); + index += 256; + if(size) index %= size; + } + + index += (255 - page_hi) * 256; + if(size) index %= size; + } + } break; + } +} + +bool Bus::load_cart() { + if(cartridge.loaded() == true) return false; + + map_reset(); + map_xml(); + map_system(); + return true; +} + +void Bus::unload_cart() { +} + +void Bus::map_reset() { + map(MapMode::Direct, 0x00, 0xff, 0x0000, 0xffff, memory::memory_unmapped); + map(MapMode::Direct, 0x00, 0x3f, 0x2000, 0x5fff, memory::mmio); + map(MapMode::Direct, 0x80, 0xbf, 0x2000, 0x5fff, memory::mmio); + for(unsigned i = 0x2000; i <= 0x5fff; i++) memory::mmio.map(i, memory::mmio_unmapped); +} + +void Bus::map_xml() { + foreach(m, cartridge.mapping) { + if(m.memory) { + map(m.mode, m.banklo, m.bankhi, m.addrlo, m.addrhi, *m.memory, m.offset, m.size); + } else if(m.mmio) { + for(unsigned i = m.addrlo; i <= m.addrhi; i++) memory::mmio.map(i, *m.mmio); + } + } +} + +void Bus::map_system() { + map(MapMode::Linear, 0x00, 0x3f, 0x0000, 0x1fff, memory::wram, 0x000000, 0x002000); + map(MapMode::Linear, 0x80, 0xbf, 0x0000, 0x1fff, memory::wram, 0x000000, 0x002000); + map(MapMode::Linear, 0x7e, 0x7f, 0x0000, 0xffff, memory::wram); +} + +void Bus::power() { + foreach(n, memory::wram) n = config.cpu.wram_init_value; +} + +void Bus::reset() { +} + +} diff --git a/asnes/memory/memory.hpp b/asnes/memory/memory.hpp new file mode 100755 index 00000000..1492d7d8 --- /dev/null +++ b/asnes/memory/memory.hpp @@ -0,0 +1,111 @@ +struct Memory { + virtual inline unsigned size() const; + virtual uint8 read(unsigned addr) = 0; + virtual void write(unsigned addr, uint8 data) = 0; +}; + +struct MMIO { + virtual uint8 mmio_read(unsigned addr) = 0; + virtual void mmio_write(unsigned addr, uint8 data) = 0; +}; + +struct UnmappedMemory : Memory { + unsigned size() const; + uint8 read(unsigned); + void write(unsigned, uint8); +}; + +struct UnmappedMMIO : MMIO { + uint8 mmio_read(unsigned); + void mmio_write(unsigned, uint8); +}; + +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 MMIOAccess : Memory { + void map(unsigned addr, MMIO &access); + uint8 read(unsigned addr); + void write(unsigned addr, uint8 data); + + MMIO *mmio[0x4000]; +}; + +struct Bus { + unsigned mirror(unsigned addr, unsigned size); + void map(unsigned addr, Memory &access, unsigned offset); + enum class MapMode : unsigned { Direct, Linear, Shadow }; + void map(MapMode mode, + uint8 bank_lo, uint8 bank_hi, + uint16 addr_lo, uint16 addr_hi, + Memory &access, unsigned offset = 0, unsigned size = 0); + + alwaysinline uint8 read(unsigned addr); + alwaysinline void write(unsigned addr, uint8 data); + + bool load_cart(); + void unload_cart(); + + void power(); + void reset(); + + struct Page { + Memory *access; + unsigned offset; + } page[65536]; + + void serialize(serializer&); + +private: + void map_reset(); + void map_xml(); + void map_system(); +}; + +namespace memory { + extern MMIOAccess mmio; //S-CPU, S-PPU + extern StaticRAM wram; //S-CPU + extern StaticRAM apuram; //S-SMP, S-DSP + extern StaticRAM vram; //S-PPU + extern StaticRAM oam; //S-PPU + extern StaticRAM cgram; //S-PPU + + extern UnmappedMemory memory_unmapped; + extern UnmappedMMIO mmio_unmapped; +}; + +extern Bus bus; diff --git a/asnes/memory/serialization.cpp b/asnes/memory/serialization.cpp new file mode 100755 index 00000000..334a380a --- /dev/null +++ b/asnes/memory/serialization.cpp @@ -0,0 +1,11 @@ +#ifdef MEMORY_CPP + +void Bus::serialize(serializer &s) { + s.array(memory::wram.data(), memory::wram.size()); + s.array(memory::apuram.data(), memory::apuram.size()); + s.array(memory::vram.data(), memory::vram.size()); + s.array(memory::oam.data(), memory::oam.size()); + s.array(memory::cgram.data(), memory::cgram.size()); +} + +#endif diff --git a/asnes/ppu/background/background.cpp b/asnes/ppu/background/background.cpp new file mode 100755 index 00000000..7010068d --- /dev/null +++ b/asnes/ppu/background/background.cpp @@ -0,0 +1,249 @@ +#ifdef PPU_CPP + +#include "mode7.cpp" + +void PPU::Background::scanline() { + if(self.vcounter() == 1) { + t.mosaic_y = 1; + t.mosaic_countdown = 0; + } else { + if(!regs.mosaic || !t.mosaic_countdown) t.mosaic_y = self.vcounter(); + if(!t.mosaic_countdown) t.mosaic_countdown = regs.mosaic + 1; + t.mosaic_countdown--; + } + + t.x = 0; +} + +void PPU::Background::run() { + bool hires = (self.regs.bgmode == 5 || self.regs.bgmode == 6); + + if((self.hcounter() & 2) == 0) { + output.main.priority = 0; + output.sub.priority = 0; + } else if(hires == false) { + return; + } + + if(regs.mode == Mode::Inactive) return; + if(regs.main_enabled == false && regs.sub_enabled == false) return; + + unsigned x = t.x++; + unsigned y = t.mosaic_y; + if(regs.mode == Mode::Mode7) return run_mode7(x, y); + + 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 = (!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 hscroll = regs.hoffset; + unsigned vscroll = regs.voffset; + if(hires) { + hscroll <<= 1; + if(self.regs.interlace) y = (y << 1) + self.field(); + } + + unsigned hoffset = hscroll + mosaic_table[regs.mosaic][x]; + unsigned voffset = vscroll + y; + + if(self.regs.bgmode == 2 || self.regs.bgmode == 4 || self.regs.bgmode == 6) { + uint16 opt_x = (x + (hscroll & 7)); + + if(opt_x >= 8) { + unsigned hval = self.bg3.get_tile((opt_x - 8) + (self.bg3.regs.hoffset & ~7), self.bg3.regs.voffset + 0); + unsigned vval = self.bg3.get_tile((opt_x - 8) + (self.bg3.regs.hoffset & ~7), self.bg3.regs.voffset + 8); + unsigned opt_valid_bit = (id == ID::BG1 ? 0x2000 : 0x4000); + + 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; + } + } + } + + hoffset &= mask_x; + voffset &= mask_y; + + unsigned tile_number = get_tile(hoffset, voffset); + bool mirror_y = tile_number & 0x8000; + bool mirror_x = tile_number & 0x4000; + unsigned priority = (tile_number & 0x2000 ? regs.priority1 : regs.priority0); + unsigned palette_number = (tile_number >> 10) & 7; + unsigned palette_index = palette_offset + (palette_number << palette_size); + + if(tile_width == 4 && (bool)(hoffset & 8) != mirror_x) tile_number += 1; + if(tile_height == 4 && (bool)(voffset & 8) != mirror_y) tile_number += 16; + tile_number &= 0x03ff; + tile_number += tiledata_index; + tile_number &= tile_mask; + + if(mirror_x) hoffset ^= 7; + if(mirror_y) voffset ^= 7; + + uint8 color = get_color(hoffset, voffset, tile_number); + if(color == 0) return; + + color += palette_index; + + if(hires == false) { + if(regs.main_enabled) { + output.main.priority = priority; + output.main.palette = color; + output.main.tile = tile_number; + } + + if(regs.sub_enabled) { + output.sub.priority = priority; + output.sub.palette = color; + output.sub.tile = tile_number; + } + } else { + if(x & 1) { + if(regs.main_enabled) { + output.main.priority = priority; + output.main.palette = color; + output.main.tile = tile_number; + } + } else { + if(regs.sub_enabled) { + output.sub.priority = priority; + output.sub.palette = color; + output.sub.tile = tile_number; + } + } + } +} + +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 pos = ((y & 0x1f) << 5) + (x & 0x1f); + if(x & 0x20) pos += screen_x; + if(y & 0x20) pos += screen_y; + + uint16 addr = regs.screen_addr + (pos << 1); + return memory::vram[addr + 0] + (memory::vram[addr + 1] << 8); +} + +unsigned PPU::Background::get_color(unsigned x, unsigned y, uint16 offset) { + unsigned mask = 0x80 >> (x & 7); + + switch(regs.mode) { + case Background::Mode::BPP2: { + offset = (offset * 16) + ((y & 7) * 2); + + unsigned d0 = memory::vram[offset + 0]; + unsigned d1 = memory::vram[offset + 1]; + + return (((bool)(d0 & mask)) << 0) + + (((bool)(d1 & mask)) << 1); + } + + case Background::Mode::BPP4: { + offset = (offset * 32) + ((y & 7) * 2); + + unsigned d0 = memory::vram[offset + 0]; + unsigned d1 = memory::vram[offset + 1]; + unsigned d2 = memory::vram[offset + 16]; + unsigned d3 = memory::vram[offset + 17]; + + return (((bool)(d0 & mask)) << 0) + + (((bool)(d1 & mask)) << 1) + + (((bool)(d2 & mask)) << 2) + + (((bool)(d3 & mask)) << 3); + } + + case Background::Mode::BPP8: { + offset = (offset * 64) + ((y & 7) * 2); + + unsigned d0 = memory::vram[offset + 0]; + unsigned d1 = memory::vram[offset + 1]; + unsigned d2 = memory::vram[offset + 16]; + unsigned d3 = memory::vram[offset + 17]; + unsigned d4 = memory::vram[offset + 32]; + unsigned d5 = memory::vram[offset + 33]; + unsigned d6 = memory::vram[offset + 48]; + unsigned d7 = memory::vram[offset + 49]; + + return (((bool)(d0 & mask)) << 0) + + (((bool)(d1 & mask)) << 1) + + (((bool)(d2 & mask)) << 2) + + (((bool)(d3 & mask)) << 3) + + (((bool)(d4 & mask)) << 4) + + (((bool)(d5 & mask)) << 5) + + (((bool)(d6 & mask)) << 6) + + (((bool)(d7 & mask)) << 7); + } + }; +} + +void PPU::Background::reset() { + t.x = 0; + t.mosaic_y = 0; + t.mosaic_countdown = 0; + regs.tiledata_addr = 0; + regs.screen_addr = 0; + regs.screen_size = 0; + regs.mosaic = 0; + regs.tile_size = 0; + regs.mode = 0; + regs.priority0 = 0; + regs.priority1 = 0; + regs.main_enabled = 0; + regs.sub_enabled = 0; + regs.hoffset = 0; + regs.voffset = 0; + output.main.palette = 0; + output.main.priority = 0; + output.sub.palette = 0; + output.sub.priority = 0; +} + +PPU::Background::Background(PPU &self, unsigned id) : self(self), id(id) { + for(unsigned m = 0; m < 16; m++) { + for(unsigned x = 0; x < 4096; x++) { + mosaic_table[m][x] = (x / (m + 1)) * (m + 1); + } + } +} + +uint16 PPU::Background::mosaic_table[16][4096]; + +#endif diff --git a/asnes/ppu/background/background.hpp b/asnes/ppu/background/background.hpp new file mode 100755 index 00000000..0849022e --- /dev/null +++ b/asnes/ppu/background/background.hpp @@ -0,0 +1,58 @@ +class Background { +public: + PPU &self; + 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 { + unsigned x; + unsigned mosaic_y; + unsigned mosaic_countdown; + } t; + + struct { + unsigned tiledata_addr; + unsigned screen_addr; + unsigned screen_size; + unsigned mosaic; + bool tile_size; + + unsigned mode; + unsigned priority0; + unsigned priority1; + + bool main_enabled; + bool sub_enabled; + + unsigned hoffset; + unsigned voffset; + } regs; + + struct { + struct { + unsigned priority; //0 = none (transparent) + unsigned palette; + unsigned tile; + } main, sub; + } output; + + void scanline(); + void run(); + unsigned get_tile(unsigned x, unsigned y); + unsigned get_color(unsigned x, unsigned y, uint16 offset); + void reset(); + + void serialize(serializer&); + Background(PPU &self, unsigned id); + +private: + static uint16 mosaic_table[16][4096]; + + //mode7.cpp + signed clip(signed n); + void run_mode7(unsigned x, unsigned y); +}; diff --git a/asnes/ppu/background/mode7.cpp b/asnes/ppu/background/mode7.cpp new file mode 100755 index 00000000..c370fd67 --- /dev/null +++ b/asnes/ppu/background/mode7.cpp @@ -0,0 +1,100 @@ +#ifdef PPU_CPP + +signed PPU::Background::clip(signed n) { + //13-bit sign extend: --s---nnnnnnnnnn -> ssssssnnnnnnnnnn + return n & 0x2000 ? (n | ~1023) : (n & 1023); +} + +void PPU::Background::run_mode7(unsigned x, unsigned y) { + 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>(self.regs.mode7_hoffset); + signed voffset = sclip<13>(self.regs.mode7_voffset); + + if(self.regs.mode7_hflip) x = 255 - x; + if(self.regs.mode7_vflip) y = 255 - y; + + unsigned mosaic_x; + unsigned mosaic_y; + if(id == ID::BG1) { + mosaic_x = mosaic_table[self.bg1.regs.mosaic][x]; + mosaic_y = mosaic_table[self.bg1.regs.mosaic][y]; + } else if(id == ID::BG2) { + mosaic_x = mosaic_table[self.bg2.regs.mosaic][x]; + mosaic_y = mosaic_table[self.bg1.regs.mosaic][y]; //BG2 vertical mosaic uses BG1 mosaic size + } + + signed psx = ((a * clip(hoffset - cx)) & ~63) + ((b * clip(voffset - cy)) & ~63) + ((b * mosaic_y) & ~63) + (cx << 8); + signed psy = ((c * clip(hoffset - cx)) & ~63) + ((d * clip(voffset - cy)) & ~63) + ((d * mosaic_y) & ~63) + (cy << 8); + + signed px = psx + (a * mosaic_x); + signed py = psy + (c * mosaic_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 = memory::vram[((py >> 3) * 128 + (px >> 3)) << 1]; + palette = memory::vram[(((tile << 6) + ((py & 7) << 3) + (px & 7)) << 1) + 1]; + } break; + + //palette color 0 outside of screen area + case 2: { + if(px < 0 || px > 1023 || py < 0 || py > 1023) { + palette = 0; + } else { + px &= 1023; + py &= 1023; + tile = memory::vram[((py >> 3) * 128 + (px >> 3)) << 1]; + palette = memory::vram[(((tile << 6) + ((py & 7) << 3) + (px & 7)) << 1) + 1]; + } + } break; + + //character 0 repetition outside of screen area + case 3: { + if(px < 0 || px > 1023 || py < 0 || py > 1023) { + tile = 0; + } else { + px &= 1023; + py &= 1023; + tile = memory::vram[((py >> 3) * 128 + (px >> 3)) << 1]; + } + palette = memory::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_enabled) { + output.main.palette = palette; + output.main.priority = priority; + } + + if(regs.sub_enabled) { + output.sub.palette = palette; + output.sub.priority = priority; + } +} + +#endif diff --git a/asnes/ppu/counter/counter-inline.hpp b/asnes/ppu/counter/counter-inline.hpp new file mode 100755 index 00000000..263d10a4 --- /dev/null +++ b/asnes/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() == System::Region::NTSC && status.interlace == false && status.vcounter == 262) + || (system.region() == System::Region::NTSC && status.interlace == true && status.vcounter == 263) + || (system.region() == System::Region::NTSC && status.interlace == true && status.vcounter == 262 && status.field == 1) + || (system.region() == System::Region::PAL && status.interlace == false && status.vcounter == 312) + || (system.region() == System::Region::PAL && status.interlace == true && status.vcounter == 313) + || (system.region() == 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() == 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() == 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/asnes/ppu/counter/counter.hpp b/asnes/ppu/counter/counter.hpp new file mode 100755 index 00000000..f244156a --- /dev/null +++ b/asnes/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/asnes/ppu/debugger/debugger.cpp b/asnes/ppu/debugger/debugger.cpp new file mode 100755 index 00000000..1191514f --- /dev/null +++ b/asnes/ppu/debugger/debugger.cpp @@ -0,0 +1,3 @@ +#ifdef PPU_CPP + +#endif diff --git a/asnes/ppu/debugger/debugger.hpp b/asnes/ppu/debugger/debugger.hpp new file mode 100755 index 00000000..56d9fd59 --- /dev/null +++ b/asnes/ppu/debugger/debugger.hpp @@ -0,0 +1,8 @@ +class sPPUDebugger : public sPPU, public PPUDebugger { +public: + bool bg1_enabled[2]; + bool bg2_enabled[2]; + bool bg3_enabled[2]; + bool bg4_enabled[2]; + bool oam_enabled[4]; +}; diff --git a/asnes/ppu/debugger/ppu-debugger.cpp b/asnes/ppu/debugger/ppu-debugger.cpp new file mode 100755 index 00000000..501874f1 --- /dev/null +++ b/asnes/ppu/debugger/ppu-debugger.cpp @@ -0,0 +1,305 @@ +#ifdef PPU_CPP + +bool PPUDebugger::property(unsigned id, string &name, string &value) { + unsigned n = 0; + + //internal + if(id == n++) { name = "S-PPU1 MDR"; value = string("0x", strhex<2>(ppu1_mdr())); return true; } + if(id == n++) { name = "S-PPU2 MDR"; value = string("0x", strhex<2>(ppu2_mdr())); return true; } + + //$2100 + if(id == n++) { name = "$2100"; value = ""; return true; } + if(id == n++) { name = "Display Disable"; value = display_disable(); return true; } + if(id == n++) { name = "Display Brightness"; value = display_brightness(); return true; } + + //$2101 + if(id == n++) { name = "$2101"; value = ""; return true; } + if(id == n++) { name = "OAM Base Size"; value = oam_base_size(); return true; } + if(id == n++) { name = "OAM Name Select"; value = oam_name_select(); return true; } + if(id == n++) { name = "OAM Name Base Address"; value = string("0x", strhex<4>(oam_name_base_address())); return true; } + + //$2102-$2103 + if(id == n++) { name = "$2102-$2103"; value = ""; return true; } + if(id == n++) { name = "OAM Base Address"; value = string("0x", strhex<4>(oam_base_address())); return true; } + if(id == n++) { name = "OAM Priority"; value = oam_priority(); return true; } + + //$2105 + if(id == n++) { name = "$2105"; value = ""; return true; } + if(id == n++) { name = "BG1 Tile Size"; value = bg1_tile_size() ? "16x16" : "8x8"; return true; } + if(id == n++) { name = "BG2 Tile Size"; value = bg2_tile_size() ? "16x16" : "8x8"; return true; } + if(id == n++) { name = "BG3 Tile Size"; value = bg3_tile_size() ? "16x16" : "8x8"; return true; } + if(id == n++) { name = "BG4 Tile Size"; value = bg4_tile_size() ? "16x16" : "8x8"; return true; } + if(id == n++) { name = "BG3 Priority"; value = bg3_priority(); return true; } + if(id == n++) { name = "BG Mode"; value = bg_mode(); return true; } + + //$2106 + if(id == n++) { name = "$2106"; value = ""; return true; } + if(id == n++) { name = "Mosaic Size"; value = mosaic_size(); return true; } + if(id == n++) { name = "BG1 Mosaic Enable"; value = bg1_mosaic_enable(); return true; } + if(id == n++) { name = "BG2 Mosaic Enable"; value = bg2_mosaic_enable(); return true; } + if(id == n++) { name = "BG3 Mosaic Enable"; value = bg3_mosaic_enable(); return true; } + if(id == n++) { name = "BG4 Mosaic Enable"; value = bg4_mosaic_enable(); return true; } + + static char screen_size[4][8] = { "32x32", "32x64", "64x32", "64x64" }; + + //$2107 + if(id == n++) { name = "$2107"; value = ""; return true; } + if(id == n++) { name = "BG1 Screen Address"; value = string("0x", strhex<4>(bg1_screen_address())); return true; } + if(id == n++) { name = "BG1 Screen Size"; value = screen_size[bg1_screen_size()]; return true; } + + //$2108 + if(id == n++) { name = "$2108"; value = ""; return true; } + if(id == n++) { name = "BG2 Screen Address"; value = string("0x", strhex<4>(bg2_screen_address())); return true; } + if(id == n++) { name = "BG2 Screen Size"; value = screen_size[bg2_screen_size()]; return true; } + + //$2109 + if(id == n++) { name = "$2109"; value = ""; return true; } + if(id == n++) { name = "BG3 Screen Address"; value = string("0x", strhex<4>(bg3_screen_address())); return true; } + if(id == n++) { name = "BG3 Screen Size"; value = screen_size[bg3_screen_size()]; return true; } + + //$210a + if(id == n++) { name = "$210a"; value = ""; return true; } + if(id == n++) { name = "BG4 Screen Address"; value = string("0x", strhex<4>(bg4_screen_address())); return true; } + if(id == n++) { name = "BG4 Screen Size"; value = screen_size[bg4_screen_size()]; return true; } + + //$210b + if(id == n++) { name = "$210b"; value = ""; return true; } + if(id == n++) { name = "BG1 Name Base Address"; value = string("0x", strhex<4>(bg1_name_base_address())); return true; } + if(id == n++) { name = "BG2 Name Base Address"; value = string("0x", strhex<4>(bg2_name_base_address())); return true; } + + //$210c + if(id == n++) { name = "$210c"; value = ""; return true; } + if(id == n++) { name = "BG3 Name Base Address"; value = string("0x", strhex<4>(bg3_name_base_address())); return true; } + if(id == n++) { name = "BG4 Name Base Address"; value = string("0x", strhex<4>(bg4_name_base_address())); return true; } + + //$210d + if(id == n++) { name = "$210d"; value = ""; return true; } + if(id == n++) { name = "Mode 7 Scroll H-offset"; value = mode7_hoffset(); return true; } + if(id == n++) { name = "BG1 Scroll H-offset"; value = bg1_hoffset(); return true; } + + //$210e + if(id == n++) { name = "$210e"; value = ""; return true; } + if(id == n++) { name = "Mode 7 Scroll V-offset"; value = mode7_voffset(); return true; } + if(id == n++) { name = "BG1 Scroll V-offset"; value = bg1_voffset(); return true; } + + //$210f + if(id == n++) { name = "$210f"; value = ""; return true; } + if(id == n++) { name = "BG2 Scroll H-offset"; value = bg2_hoffset(); return true; } + + //$2110 + if(id == n++) { name = "$2110"; value = ""; return true; } + if(id == n++) { name = "BG2 Scroll V-offset"; value = bg2_voffset(); return true; } + + //$2111 + if(id == n++) { name = "$2111"; value = ""; return true; } + if(id == n++) { name = "BG3 Scroll H-offset"; value = bg3_hoffset(); return true; } + + //$2112 + if(id == n++) { name = "$2112"; value = ""; return true; } + if(id == n++) { name = "BG3 Scroll V-offset"; value = bg3_voffset(); return true; } + + //$2113 + if(id == n++) { name = "$2113"; value = ""; return true; } + if(id == n++) { name = "BG4 Scroll H-offset"; value = bg4_hoffset(); return true; } + + //$2114 + if(id == n++) { name = "$2114"; value = ""; return true; } + if(id == n++) { name = "BG4 Scroll V-offset"; value = bg4_voffset(); return true; } + + //$2115 + if(id == n++) { name = "$2115"; value = ""; return true; } + if(id == n++) { name = "VRAM Increment Mode"; value = (unsigned)vram_increment_mode(); return true; } + if(id == n++) { name = "VRAM Increment Formation"; value = vram_increment_formation(); return true; } + if(id == n++) { name = "VRAM Increment Size"; value = vram_increment_size(); return true; } + + //$2116-$2117 + if(id == n++) { name = "$2116-$2117"; value = ""; return true; } + if(id == n++) { name = "VRAM Address"; value = string("0x", strhex<4>(vram_address())); return true; } + + //$211a + if(id == n++) { name = "$211a"; value = ""; return true; } + if(id == n++) { name = "Mode 7 Repeat"; value = mode7_repeat(); return true; } + if(id == n++) { name = "Mode 7 V-flip"; value = mode7_vflip(); return true; } + if(id == n++) { name = "Mode 7 H-flip"; value = mode7_hflip(); return true; } + + //$211b + if(id == n++) { name = "$211b"; value = ""; return true; } + if(id == n++) { name = "Mode 7 A"; value = mode7_a(); return true; } + + //$211c + if(id == n++) { name = "$211c"; value = ""; return true; } + if(id == n++) { name = "Mode 7 B"; value = mode7_b(); return true; } + + //$211d + if(id == n++) { name = "$211d"; value = ""; return true; } + if(id == n++) { name = "Mode 7 C"; value = mode7_c(); return true; } + + //$211e + if(id == n++) { name = "$211e"; value = ""; return true; } + if(id == n++) { name = "Mode 7 D"; value = mode7_d(); return true; } + + //$211f + if(id == n++) { name = "$211f"; value = ""; return true; } + if(id == n++) { name = "Mode 7 X"; value = mode7_x(); return true; } + + //$2120 + if(id == n++) { name = "$2120"; value = ""; return true; } + if(id == n++) { name = "Mode 7 Y"; value = mode7_y(); return true; } + + //$2121 + if(id == n++) { name = "$2121"; value = ""; return true; } + if(id == n++) { name = "CGRAM Address"; value = string("0x", strhex<4>(cgram_address())); return true; } + + //$2123 + if(id == n++) { name = "$2123"; value = ""; return true; } + if(id == n++) { name = "BG1 Window 1 Enable"; value = bg1_window1_enable(); return true; } + if(id == n++) { name = "BG1 Window 1 Invert"; value = bg1_window1_invert(); return true; } + if(id == n++) { name = "BG1 Window 2 Enable"; value = bg1_window2_enable(); return true; } + if(id == n++) { name = "BG1 Window 2 Invert"; value = bg1_window2_invert(); return true; } + if(id == n++) { name = "BG2 Window 1 Enable"; value = bg2_window1_enable(); return true; } + if(id == n++) { name = "BG2 Window 1 Invert"; value = bg2_window1_invert(); return true; } + if(id == n++) { name = "BG2 Window 2 Enable"; value = bg2_window2_enable(); return true; } + if(id == n++) { name = "BG2 Window 2 Invert"; value = bg2_window2_invert(); return true; } + + //$2124 + if(id == n++) { name = "$2124"; value = ""; return true; } + if(id == n++) { name = "BG3 Window 1 Enable"; value = bg3_window1_enable(); return true; } + if(id == n++) { name = "BG3 Window 1 Invert"; value = bg3_window1_invert(); return true; } + if(id == n++) { name = "BG3 Window 2 Enable"; value = bg3_window2_enable(); return true; } + if(id == n++) { name = "BG3 Window 2 Invert"; value = bg3_window2_invert(); return true; } + if(id == n++) { name = "BG4 Window 1 Enable"; value = bg4_window1_enable(); return true; } + if(id == n++) { name = "BG4 Window 1 Invert"; value = bg4_window1_invert(); return true; } + if(id == n++) { name = "BG4 Window 2 Enable"; value = bg4_window2_enable(); return true; } + if(id == n++) { name = "BG4 Window 2 Invert"; value = bg4_window2_invert(); return true; } + + //$2125 + if(id == n++) { name = "$2125"; value = ""; return true; } + if(id == n++) { name = "OAM Window 1 Enable"; value = oam_window1_enable(); return true; } + if(id == n++) { name = "OAM Window 1 Invert"; value = oam_window1_invert(); return true; } + if(id == n++) { name = "OAM Window 2 Enable"; value = oam_window2_enable(); return true; } + if(id == n++) { name = "OAM Window 2 Invert"; value = oam_window2_invert(); return true; } + if(id == n++) { name = "Color Window 1 Enable"; value = color_window1_enable(); return true; } + if(id == n++) { name = "Color Window 1 Invert"; value = color_window1_invert(); return true; } + if(id == n++) { name = "Color Window 2 Enable"; value = color_window2_enable(); return true; } + if(id == n++) { name = "Color Window 2 Invert"; value = color_window2_invert(); return true; } + + //$2126 + if(id == n++) { name = "$2126"; value = ""; return true; } + if(id == n++) { name = "Window 1 Left"; value = window1_left(); return true; } + + //$2127 + if(id == n++) { name = "$2127"; value = ""; return true; } + if(id == n++) { name = "Window 1 Right"; value = window1_right(); return true; } + + //$2128 + if(id == n++) { name = "$2128"; value = ""; return true; } + if(id == n++) { name = "Window 2 Left"; value = window2_left(); return true; } + + //$2129 + if(id == n++) { name = "$2129"; value = ""; return true; } + if(id == n++) { name = "Window 2 Right"; value = window2_right(); return true; } + + static char window_mask_mode[4][8] = { "OR", "AND", "XOR", "XNOR" }; + + //$212a + if(id == n++) { name = "$212a"; value = ""; return true; } + if(id == n++) { name = "BG1 Window Mask"; value = window_mask_mode[bg1_window_mask()]; return true; } + if(id == n++) { name = "BG2 Window Mask"; value = window_mask_mode[bg2_window_mask()]; return true; } + if(id == n++) { name = "BG3 Window Mask"; value = window_mask_mode[bg3_window_mask()]; return true; } + if(id == n++) { name = "BG4 Window Mask"; value = window_mask_mode[bg4_window_mask()]; return true; } + + //$212b + if(id == n++) { name = "$212b"; value = ""; return true; } + if(id == n++) { name = "OAM Window Mask"; value = window_mask_mode[oam_window_mask()]; return true; } + if(id == n++) { name = "Color Window Mask"; value = window_mask_mode[color_window_mask()]; return true; } + + //$212c + if(id == n++) { name = "$212c"; value = ""; return true; } + if(id == n++) { name = "BG1 Mainscreen Enable"; value = bg1_mainscreen_enable(); return true; } + if(id == n++) { name = "BG2 Mainscreen Enable"; value = bg2_mainscreen_enable(); return true; } + if(id == n++) { name = "BG3 Mainscreen Enable"; value = bg3_mainscreen_enable(); return true; } + if(id == n++) { name = "BG4 Mainscreen Enable"; value = bg4_mainscreen_enable(); return true; } + if(id == n++) { name = "OAM Mainscreen Enable"; value = oam_mainscreen_enable(); return true; } + + //$212d + if(id == n++) { name = "$212d"; value = ""; return true; } + if(id == n++) { name = "BG1 Subscreen Enable"; value = bg1_subscreen_enable(); return true; } + if(id == n++) { name = "BG2 Subscreen Enable"; value = bg2_subscreen_enable(); return true; } + if(id == n++) { name = "BG3 Subscreen Enable"; value = bg3_subscreen_enable(); return true; } + if(id == n++) { name = "BG4 Subscreen Enable"; value = bg4_subscreen_enable(); return true; } + if(id == n++) { name = "OAM Subscreen Enable"; value = oam_subscreen_enable(); return true; } + + //$212e + if(id == n++) { name = "$212e"; value = ""; return true; } + if(id == n++) { name = "BG1 Mainscreen Window Enable"; value = bg1_mainscreen_window_enable(); return true; } + if(id == n++) { name = "BG2 Mainscreen Window Enable"; value = bg2_mainscreen_window_enable(); return true; } + if(id == n++) { name = "BG3 Mainscreen Window Enable"; value = bg3_mainscreen_window_enable(); return true; } + if(id == n++) { name = "BG4 Mainscreen Window Enable"; value = bg4_mainscreen_window_enable(); return true; } + if(id == n++) { name = "OAM Mainscreen Window Enable"; value = oam_mainscreen_window_enable(); return true; } + + //$212f + if(id == n++) { name = "$212f"; value = ""; return true; } + if(id == n++) { name = "BG1 Subscreen Window Enable"; value = bg1_subscreen_window_enable(); return true; } + if(id == n++) { name = "BG2 Subscreen Window Enable"; value = bg2_subscreen_window_enable(); return true; } + if(id == n++) { name = "BG3 Subscreen Window Enable"; value = bg3_subscreen_window_enable(); return true; } + if(id == n++) { name = "BG4 Subscreen Window Enable"; value = bg4_subscreen_window_enable(); return true; } + if(id == n++) { name = "OAM Subscreen Window Enable"; value = oam_subscreen_window_enable(); return true; } + + static char color_window_mask_mode[4][32] = { "Always", "Never", "Inside Window Only", "Outside Window Only" }; + + //$2130 + if(id == n++) { name = "$2130"; value = ""; return true; } + if(id == n++) { name = "Color Mainscreen Window Mask"; value = color_window_mask_mode[color_mainscreen_window_mask()]; return true; } + if(id == n++) { name = "Color Subscreen Window Mask"; value = color_window_mask_mode[color_subscreen_window_mask()]; return true; } + if(id == n++) { name = "Color Add/Subtract Mode"; value = !color_add_subtract_mode() ? "Fixed Color" : "Subscreen"; return true; } + if(id == n++) { name = "Direct Color"; value = direct_color(); return true; } + + //$2131 + if(id == n++) { name = "$2131"; value = ""; return true; } + if(id == n++) { name = "Color Mode"; value = !color_mode() ? "Add" : "Subtract"; return true; } + if(id == n++) { name = "Color Halve"; value = color_halve(); return true; } + if(id == n++) { name = "BG1 Color Enable"; value = bg1_color_enable(); return true; } + if(id == n++) { name = "BG2 Color Enable"; value = bg2_color_enable(); return true; } + if(id == n++) { name = "BG3 Color Enable"; value = bg3_color_enable(); return true; } + if(id == n++) { name = "BG4 Color Enable"; value = bg4_color_enable(); return true; } + if(id == n++) { name = "OAM Color Enable"; value = oam_color_enable(); return true; } + if(id == n++) { name = "Back Color Enable"; value = back_color_enable(); return true; } + + //$2132 + if(id == n++) { name = "$2132"; value = ""; return true; } + if(id == n++) { name = "Color Constant - Blue"; value = color_constant_blue(); return true; } + if(id == n++) { name = "Color Constant - Green"; value = color_constant_green(); return true; } + if(id == n++) { name = "Color Constant - Red"; value = color_constant_red(); return true; } + + //$2133 + if(id == n++) { name = "$2133"; value = ""; return true; } + if(id == n++) { name = "Mode 7 EXTBG"; value = mode7_extbg(); return true; } + if(id == n++) { name = "Pseudo Hires"; value = pseudo_hires(); return true; } + if(id == n++) { name = "Overscan"; value = overscan_enable(); return true; } + if(id == n++) { name = "OAM Interlace"; value = oam_interlace(); return true; } + if(id == n++) { name = "Interlace"; value = interlace_enable(); return true; } + + //$213c + if(id == n++) { name = "$213c"; value = ""; return true; } + if(id == n++) { name = "H-counter"; value = hcounter(); return true; } + + //$213d + if(id == n++) { name = "$213d"; value = ""; return true; } + if(id == n++) { name = "V-counter"; value = vcounter(); return true; } + + //$213e + if(id == n++) { name = "$213e"; value = ""; return true; } + if(id == n++) { name = "Range Over"; value = range_over(); return true; } + if(id == n++) { name = "Time Over"; value = time_over(); return true; } + if(id == n++) { name = "S-PPU1 Version"; value = ppu1_version(); return true; } + + //$213f + if(id == n++) { name = "$213f"; value = ""; return true; } + if(id == n++) { name = "Field"; value = field(); return true; } + if(id == n++) { name = "Region"; value = !region() ? "NTSC" : "PAL"; return true; } + if(id == n++) { name = "S-PPU2 Version"; value = ppu2_version(); return true; } + + return false; +} + +#endif diff --git a/asnes/ppu/debugger/ppu-debugger.hpp b/asnes/ppu/debugger/ppu-debugger.hpp new file mode 100755 index 00000000..a45bed7c --- /dev/null +++ b/asnes/ppu/debugger/ppu-debugger.hpp @@ -0,0 +1,243 @@ +struct PPUDebugger : ChipDebugger { + bool property(unsigned id, string &name, string &value); + + //internal + virtual unsigned ppu1_mdr() { return 0; } + virtual unsigned ppu2_mdr() { return 0; } + + //$2100 + virtual bool display_disable() { return 0; } + virtual unsigned display_brightness() { return 0; } + + //$2101 + virtual unsigned oam_base_size() { return 0; } + virtual unsigned oam_name_select() { return 0; } + virtual unsigned oam_name_base_address() { return 0; } + + //$2102-$2103 + virtual unsigned oam_base_address() { return 0; } + virtual bool oam_priority() { return 0; } + + //$2105 + virtual bool bg1_tile_size() { return 0; } + virtual bool bg2_tile_size() { return 0; } + virtual bool bg3_tile_size() { return 0; } + virtual bool bg4_tile_size() { return 0; } + virtual bool bg3_priority() { return 0; } + virtual unsigned bg_mode() { return 0; } + + //$2106 + virtual unsigned mosaic_size() { return 0; } + virtual bool bg1_mosaic_enable() { return 0; } + virtual bool bg2_mosaic_enable() { return 0; } + virtual bool bg3_mosaic_enable() { return 0; } + virtual bool bg4_mosaic_enable() { return 0; } + + //$2107 + virtual unsigned bg1_screen_address() { return 0; } + virtual unsigned bg1_screen_size() { return 0; } + + //$2108 + virtual unsigned bg2_screen_address() { return 0; } + virtual unsigned bg2_screen_size() { return 0; } + + //$2109 + virtual unsigned bg3_screen_address() { return 0; } + virtual unsigned bg3_screen_size() { return 0; } + + //$210a + virtual unsigned bg4_screen_address() { return 0; } + virtual unsigned bg4_screen_size() { return 0; } + + //$210b + virtual unsigned bg1_name_base_address() { return 0; } + virtual unsigned bg2_name_base_address() { return 0; } + + //$210c + virtual unsigned bg3_name_base_address() { return 0; } + virtual unsigned bg4_name_base_address() { return 0; } + + //$210d + virtual unsigned mode7_hoffset() { return 0; } + virtual unsigned bg1_hoffset() { return 0; } + + //$210e + virtual unsigned mode7_voffset() { return 0; } + virtual unsigned bg1_voffset() { return 0; } + + //$210f + virtual unsigned bg2_hoffset() { return 0; } + + //$2110 + virtual unsigned bg2_voffset() { return 0; } + + //$2111 + virtual unsigned bg3_hoffset() { return 0; } + + //$2112 + virtual unsigned bg3_voffset() { return 0; } + + //$2113 + virtual unsigned bg4_hoffset() { return 0; } + + //$2114 + virtual unsigned bg4_voffset() { return 0; } + + //$2115 + virtual bool vram_increment_mode() { return 0; } + virtual unsigned vram_increment_formation() { return 0; } + virtual unsigned vram_increment_size() { return 0; } + + //$2116-$2117 + virtual unsigned vram_address() { return 0; } + + //$211a + virtual unsigned mode7_repeat() { return 0; } + virtual bool mode7_vflip() { return 0; } + virtual bool mode7_hflip() { return 0; } + + //$211b + virtual unsigned mode7_a() { return 0; } + + //$211c + virtual unsigned mode7_b() { return 0; } + + //$211d + virtual unsigned mode7_c() { return 0; } + + //$211e + virtual unsigned mode7_d() { return 0; } + + //$211f + virtual unsigned mode7_x() { return 0; } + + //$2120 + virtual unsigned mode7_y() { return 0; } + + //$2121 + virtual unsigned cgram_address() { return 0; } + + //$2123 + virtual bool bg1_window1_enable() { return 0; } + virtual bool bg1_window1_invert() { return 0; } + virtual bool bg1_window2_enable() { return 0; } + virtual bool bg1_window2_invert() { return 0; } + virtual bool bg2_window1_enable() { return 0; } + virtual bool bg2_window1_invert() { return 0; } + virtual bool bg2_window2_enable() { return 0; } + virtual bool bg2_window2_invert() { return 0; } + + //$2124 + virtual bool bg3_window1_enable() { return 0; } + virtual bool bg3_window1_invert() { return 0; } + virtual bool bg3_window2_enable() { return 0; } + virtual bool bg3_window2_invert() { return 0; } + virtual bool bg4_window1_enable() { return 0; } + virtual bool bg4_window1_invert() { return 0; } + virtual bool bg4_window2_enable() { return 0; } + virtual bool bg4_window2_invert() { return 0; } + + //$2125 + virtual bool oam_window1_enable() { return 0; } + virtual bool oam_window1_invert() { return 0; } + virtual bool oam_window2_enable() { return 0; } + virtual bool oam_window2_invert() { return 0; } + virtual bool color_window1_enable() { return 0; } + virtual bool color_window1_invert() { return 0; } + virtual bool color_window2_enable() { return 0; } + virtual bool color_window2_invert() { return 0; } + + //$2126 + virtual unsigned window1_left() { return 0; } + + //$2127 + virtual unsigned window1_right() { return 0; } + + //$2128 + virtual unsigned window2_left() { return 0; } + + //$2129 + virtual unsigned window2_right() { return 0; } + + //$212a + virtual unsigned bg1_window_mask() { return 0; } + virtual unsigned bg2_window_mask() { return 0; } + virtual unsigned bg3_window_mask() { return 0; } + virtual unsigned bg4_window_mask() { return 0; } + + //$212b + virtual unsigned oam_window_mask() { return 0; } + virtual unsigned color_window_mask() { return 0; } + + //$212c + virtual bool bg1_mainscreen_enable() { return 0; } + virtual bool bg2_mainscreen_enable() { return 0; } + virtual bool bg3_mainscreen_enable() { return 0; } + virtual bool bg4_mainscreen_enable() { return 0; } + virtual bool oam_mainscreen_enable() { return 0; } + + //$212d + virtual bool bg1_subscreen_enable() { return 0; } + virtual bool bg2_subscreen_enable() { return 0; } + virtual bool bg3_subscreen_enable() { return 0; } + virtual bool bg4_subscreen_enable() { return 0; } + virtual bool oam_subscreen_enable() { return 0; } + + //$212e + virtual bool bg1_mainscreen_window_enable() { return 0; } + virtual bool bg2_mainscreen_window_enable() { return 0; } + virtual bool bg3_mainscreen_window_enable() { return 0; } + virtual bool bg4_mainscreen_window_enable() { return 0; } + virtual bool oam_mainscreen_window_enable() { return 0; } + + //$212f + virtual bool bg1_subscreen_window_enable() { return 0; } + virtual bool bg2_subscreen_window_enable() { return 0; } + virtual bool bg3_subscreen_window_enable() { return 0; } + virtual bool bg4_subscreen_window_enable() { return 0; } + virtual bool oam_subscreen_window_enable() { return 0; } + + //$2130 + virtual unsigned color_mainscreen_window_mask() { return 0; } + virtual unsigned color_subscreen_window_mask() { return 0; } + virtual bool color_add_subtract_mode() { return 0; } + virtual bool direct_color() { return 0; } + + //$2131 + virtual bool color_mode() { return 0; } + virtual bool color_halve() { return 0; } + virtual bool bg1_color_enable() { return 0; } + virtual bool bg2_color_enable() { return 0; } + virtual bool bg3_color_enable() { return 0; } + virtual bool bg4_color_enable() { return 0; } + virtual bool oam_color_enable() { return 0; } + virtual bool back_color_enable() { return 0; } + + //$2132 + virtual unsigned color_constant_blue() { return 0; } + virtual unsigned color_constant_green() { return 0; } + virtual unsigned color_constant_red() { return 0; } + + //$2133 + virtual bool mode7_extbg() { return 0; } + virtual bool pseudo_hires() { return 0; } + virtual bool overscan_enable() { return 0; } + virtual bool oam_interlace() { return 0; } + virtual bool interlace_enable() { return 0; } + + //$213c + virtual unsigned hcounter() { return 0; } + + //$213d + virtual unsigned vcounter() { return 0; } + + //$213e + virtual bool range_over() { return 0; } + virtual bool time_over() { return 0; } + virtual unsigned ppu1_version() { return 0; } + + //$213f + virtual bool field() { return 0; } + virtual bool region() { return 0; } + virtual unsigned ppu2_version() { return 0; } +}; diff --git a/asnes/ppu/mmio/mmio.cpp b/asnes/ppu/mmio/mmio.cpp new file mode 100755 index 00000000..959a6d71 --- /dev/null +++ b/asnes/ppu/mmio/mmio.cpp @@ -0,0 +1,871 @@ +#ifdef PPU_CPP + +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_disabled || vcounter() >= (!regs.overscan ? 225 : 240)) { + return memory::vram[addr]; + } + return 0x00; +} + +void PPU::vram_write(unsigned addr, uint8 data) { + if(regs.display_disabled || vcounter() >= (!regs.overscan ? 225 : 240)) { + memory::vram[addr] = data; + } +} + +uint8 PPU::oam_read(unsigned addr) { + if(!regs.display_disabled && vcounter() < (!regs.overscan ? 225 : 240)) addr = regs.ioamaddr; + if(addr & 0x0200) addr &= 0x021f; + return memory::oam[addr]; +} + +void PPU::oam_write(unsigned addr, uint8 data) { + if(!regs.display_disabled && vcounter() < (!regs.overscan ? 225 : 240)) addr = regs.ioamaddr; + if(addr & 0x0200) addr &= 0x021f; + memory::oam[addr] = data; + oam.update(addr, data); +} + +uint8 PPU::cgram_read(unsigned addr) { + return memory::cgram[addr]; +} + +void PPU::cgram_write(unsigned addr, uint8 data) { + memory::cgram[addr] = data; +} + +bool PPU::interlace() const { + return display.interlace; +} + +bool PPU::overscan() const { + return display.overscan; +} + +bool PPU::hires() const { + return true; +} + +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; + oam.regs.priority0 = 3; oam.regs.priority1 = 6; oam.regs.priority2 = 9; oam.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; + oam.regs.priority0 = 2; oam.regs.priority1 = 3; oam.regs.priority2 = 6; oam.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; + oam.regs.priority0 = 2; oam.regs.priority1 = 4; oam.regs.priority2 = 7; oam.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; + oam.regs.priority0 = 2; oam.regs.priority1 = 4; oam.regs.priority2 = 6; oam.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; + oam.regs.priority0 = 2; oam.regs.priority1 = 4; oam.regs.priority2 = 6; oam.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; + oam.regs.priority0 = 2; oam.regs.priority1 = 4; oam.regs.priority2 = 6; oam.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; + oam.regs.priority0 = 2; oam.regs.priority1 = 4; oam.regs.priority2 = 6; oam.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; + oam.regs.priority0 = 1; oam.regs.priority1 = 3; oam.regs.priority2 = 4; oam.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; + oam.regs.priority0 = 1; oam.regs.priority1 = 3; oam.regs.priority2 = 4; oam.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; + oam.regs.priority0 = 2; oam.regs.priority1 = 4; oam.regs.priority2 = 6; oam.regs.priority3 = 7; + } + } break; + } +} + +//INIDISP +void PPU::mmio_w2100(uint8 data) { + if(regs.display_disabled && vcounter() == (!regs.overscan ? 225 : 240)) oam.address_reset(); + regs.display_disabled = data & 0x80; + regs.display_brightness = data & 0x0f; +} + +//OBSEL +void PPU::mmio_w2101(uint8 data) { + oam.regs.base_size = (data >> 5) & 7; + oam.regs.nameselect = (data >> 3) & 3; + oam.regs.tiledata_addr = (data & 3) << 14; +} + +//OAMADDL +void PPU::mmio_w2102(uint8 data) { + regs.oam_baseaddr &= 0x0100; + regs.oam_baseaddr |= (data << 0); + oam.address_reset(); +} + +//OAMADDH +void PPU::mmio_w2103(uint8 data) { + regs.oam_priority = data & 0x80; + regs.oam_baseaddr &= 0x00ff; + regs.oam_baseaddr |= (data & 1) << 8; + oam.address_reset(); +} + +//OAMDATA +void PPU::mmio_w2104(uint8 data) { + if(regs.oam_addr & 0x0200) { + oam_write(regs.oam_addr, data); + } else if((regs.oam_addr & 1) == 0) { + regs.oam_latchdata = data; + } else { + 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; + oam.regs.first_sprite = (regs.oam_priority == false ? 0 : (regs.oam_addr >> 2) & 127); +} + +//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) { + 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; +} + +//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) { + oam.regs.main_enabled = data & 0x10; + bg4.regs.main_enabled = data & 0x08; + bg3.regs.main_enabled = data & 0x04; + bg2.regs.main_enabled = data & 0x02; + bg1.regs.main_enabled = data & 0x01; +} + +//TS +void PPU::mmio_w212d(uint8 data) { + oam.regs.sub_enabled = data & 0x10; + bg4.regs.sub_enabled = data & 0x08; + bg3.regs.sub_enabled = data & 0x04; + bg2.regs.sub_enabled = data & 0x02; + bg1.regs.sub_enabled = 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; + oam.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() { + regs.ppu1_mdr = oam_read(regs.oam_addr); + regs.oam_addr = (regs.oam_addr + 1) & 0x03ff; + oam.regs.first_sprite = (regs.oam_priority == false ? 0 : (regs.oam_addr >> 2) & 127); + 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() { + if((regs.cgram_addr & 1) == 0) { + regs.ppu2_mdr = cgram_read(regs.cgram_addr) & 0xff; + } else { + regs.ppu2_mdr &= 0x80; + regs.ppu2_mdr |= cgram_read(regs.cgram_addr) & 0x7f; + } + regs.cgram_addr = (regs.cgram_addr + 1) & 0x01ff; + return regs.ppu2_mdr; +} + +//OPHCT +uint8 PPU::mmio_r213c() { + if(regs.latch_hcounter == 0) { + 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 == 0) { + 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() { + regs.ppu1_mdr &= 0x10; + regs.ppu1_mdr |= oam.regs.time_over << 7; + regs.ppu1_mdr |= oam.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() == System::Region::NTSC ? 0 : 1) << 4; + regs.ppu2_mdr |= ppu2_version & 0x0f; + return regs.ppu2_mdr; +} + +void PPU::mmio_reset() { + regs.ppu1_mdr = 0xff; + regs.ppu2_mdr = 0xff; + + regs.vram_readbuffer = 0x0000; + regs.oam_latchdata = 0x00; + regs.cgram_latchdata = 0x00; + regs.bgofs_latchdata = 0x00; + regs.mode7_latchdata = 0x00; + regs.counters_latched = false; + regs.latch_hcounter = 0; + regs.latch_vcounter = 0; + + regs.ioamaddr = 0; + regs.icgramaddr = 0; + + //$2100 INIDISP + regs.display_disabled = true; + regs.display_brightness = 0; + + //$2102 OAMADDL + //$2103 OAMADDH + regs.oam_baseaddr = 0x0000; + regs.oam_addr = 0x0000; + regs.oam_priority = false; + + //$2105 BGMODE + regs.bg3_priority = false; + regs.bgmode = 0; + + //$210d BG1HOFS + regs.mode7_hoffset = 0x0000; + + //$210e BG1VOFS + regs.mode7_voffset = 0x0000; + + //$2115 VMAIN + regs.vram_incmode = 1; + regs.vram_mapping = 0; + regs.vram_incsize = 1; + + //$2116 VMADDL + //$2117 VMADDH + regs.vram_addr = 0x0000; + + //$211a M7SEL + regs.mode7_repeat = 0; + regs.mode7_vflip = false; + regs.mode7_hflip = false; + + //$211b M7A + regs.m7a = 0x0000; + + //$211c M7B + regs.m7b = 0x0000; + + //$211d M7C + regs.m7c = 0x0000; + + //$211e M7D + regs.m7d = 0x0000; + + //$211f M7X + regs.m7x = 0x0000; + + //$2120 M7Y + regs.m7y = 0x0000; + + //$2121 CGADD + regs.cgram_addr = 0x0000; + + //$2133 SETINI + regs.mode7_extbg = false; + regs.pseudo_hires = 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/asnes/ppu/mmio/mmio.hpp b/asnes/ppu/mmio/mmio.hpp new file mode 100755 index 00000000..bb8e3a43 --- /dev/null +++ b/asnes/ppu/mmio/mmio.hpp @@ -0,0 +1,170 @@ +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; + + uint16 ioamaddr; + uint16 icgramaddr; + + //$2100 INIDISP + bool display_disabled; + unsigned display_brightness; + + //$2102 OAMADDL + //$2103 OAMADDH + uint16 oam_baseaddr; + uint16 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; + uint8 vram_mapping; + uint8 vram_incsize; + + //$2116 VMADDL + //$2117 VMADDH + uint16 vram_addr; + + //$211a M7SEL + uint8 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 + uint16 cgram_addr; + + //$2133 SETINI + bool mode7_extbg; + bool pseudo_hires; + bool overscan; + bool interlace; + + //$213c OPHCT + uint16 hcounter; + + //$213d OPVCT + uint16 vcounter; +} regs; + +void latch_counters(); +uint16 get_vram_address(); + +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); + +bool interlace() const; +bool overscan() const; +bool hires() const; + +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(); +uint8 mmio_read(unsigned addr); +void mmio_write(unsigned addr, uint8 data); diff --git a/asnes/ppu/ppu.cpp b/asnes/ppu/ppu.cpp new file mode 100755 index 00000000..edfdebd0 --- /dev/null +++ b/asnes/ppu/ppu.cpp @@ -0,0 +1,155 @@ +#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(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(cpu.thread); +} + +void PPU::Enter() { ppu.enter(); } + +void PPU::enter() { + while(true) { + if(scheduler.sync == Scheduler::SynchronizeMode::All) { + scheduler.exit(Scheduler::ExitReason::SynchronizeEvent); + } + + scanline(); + add_clocks(88); + + if(vcounter() <= (!regs.overscan ? 224 : 239)) { + for(unsigned n = 0; n < 256; n++) { + bg1.run(); + bg2.run(); + bg3.run(); + bg4.run(); + add_clocks(2); + + bg1.run(); + bg2.run(); + bg3.run(); + bg4.run(); + oam.run(); + window.run(); + screen.run(); + add_clocks(2); + } + + add_clocks(22); + oam.tilefetch(); + } else { + add_clocks(1024 + 22 + 136); + } + + add_clocks(lineclocks() - 88 - 1024 - 22 - 136); + } +} + +void PPU::add_clocks(unsigned clocks) { + clocks >>= 1; + while(clocks--) { + tick(2); + step(2); + synchronize_cpu(); + } +} + +void PPU::power() { + ppu1_version = config.ppu1.version; + ppu2_version = config.ppu2.version; + + memset(memory::vram.data(), 0x00, memory::vram.size()); + memset(memory::oam.data(), 0x00, memory::oam.size()); + memset(memory::cgram.data(), 0x00, memory::cgram.size()); + + reset(); +} + +void PPU::reset() { + create(Enter, system.cpu_frequency()); + PPUCounter::reset(); + memset(surface, 0, 512 * 512 * sizeof(uint16)); + + mmio_reset(); + bg1.reset(); + bg2.reset(); + bg3.reset(); + bg4.reset(); + oam.reset(); + window.reset(); + screen.reset(); + + frame(); +} + +void PPU::scanline() { + if(vcounter() == 0) frame(); + bg1.scanline(); + bg2.scanline(); + bg3.scanline(); + bg4.scanline(); + oam.scanline(); + window.scanline(); + screen.scanline(); +} + +void PPU::frame() { + system.frame(); + oam.frame(); + + display.interlace = regs.interlace; + display.overscan = regs.overscan; + + //frame counter + static signed framecount = 0; + static time_t prev, curr; + framecount++; + + time(&curr); + if(curr != prev) { + status.frames_updated = true; + status.frames_executed = framecount; + framecount = 0; + prev = curr; + } +} + +PPU::PPU() : +bg1(*this, Background::ID::BG1), +bg2(*this, Background::ID::BG2), +bg3(*this, Background::ID::BG3), +bg4(*this, Background::ID::BG4), +oam(*this), +window(*this), +screen(*this) { + surface = new uint16[512 * 512]; + output = surface + 16 * 512; + + status.frames_updated = false; + status.frames_executed = 0; +} + +PPU::~PPU() { + delete[] surface; +} + +} diff --git a/asnes/ppu/ppu.hpp b/asnes/ppu/ppu.hpp new file mode 100755 index 00000000..c174446f --- /dev/null +++ b/asnes/ppu/ppu.hpp @@ -0,0 +1,59 @@ +#include "counter/counter.hpp" + +class PPU : public Processor, public PPUCounter, public MMIO { +public: + #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 oam; + Window window; + Screen screen; + + uint16 *surface; + uint16 *output; + + struct { + bool frames_updated; + unsigned frames_executed; + } status; + + uint8 ppu1_version; + uint8 ppu2_version; + + struct { + bool interlace; + bool overscan; + } display; + + //synchronization + alwaysinline void step(unsigned clocks); + alwaysinline void synchronize_cpu(); + + static void Enter(); + void enter(); + void add_clocks(unsigned); + + void power(); + void reset(); + + void scanline(); + void frame(); + + void serialize(serializer&); + PPU(); + ~PPU(); +}; + +#if defined(DEBUGGER) + #include "debugger/debugger.hpp" + extern PPUDebugger ppu; +#else + extern PPU ppu; +#endif diff --git a/asnes/ppu/screen/screen.cpp b/asnes/ppu/screen/screen.cpp new file mode 100755 index 00000000..0e8fb921 --- /dev/null +++ b/asnes/ppu/screen/screen.cpp @@ -0,0 +1,219 @@ +#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() { + uint16 color; + if(self.regs.pseudo_hires == false && self.regs.bgmode != 5 && self.regs.bgmode != 6) { + color = get_pixel(false); + *output++ = color; + *output++ = color; + } else { + color = get_pixel(true); + *output++ = color; + color = get_pixel(false); + *output++ = color; + } +} + +uint16 PPU::Screen::get_pixel(bool swap) { + 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.oam.output.main.priority > priority_main) { + priority_main = self.oam.output.main.priority; + color_main = get_color(self.oam.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.oam.output.sub.priority > priority_sub) { + priority_sub = self.oam.output.sub.priority; + color_sub = get_color(self.oam.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.oam.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 + //======== + + output = light_table[self.regs.display_brightness][output]; + if(self.regs.display_disabled) output = 0x0000; + return 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; + return memory::cgram[palette + 0] + (memory::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 = 0; + regs.direct_color = 0; + regs.color_mode = 0; + regs.color_halve = 0; + regs.bg1_color_enable = 0; + regs.bg2_color_enable = 0; + regs.bg3_color_enable = 0; + regs.bg4_color_enable = 0; + regs.oam_color_enable = 0; + regs.back_color_enable = 0; + regs.color_r = 0; + regs.color_g = 0; + regs.color_b = 0; +} + +PPU::Screen::Screen(PPU &self) : self(self) { + for(unsigned l = 0; l < 16; l++) { + for(unsigned r = 0; r < 32; r++) { + for(unsigned g = 0; g < 32; g++) { + for(unsigned b = 0; b < 32; b++) { + double luma = (double)l / 15.0; + unsigned ar = (luma * r + 0.5); + unsigned ag = (luma * g + 0.5); + unsigned ab = (luma * b + 0.5); + light_table[l][(r << 10) + (g << 5) + b] = (ab << 10) + (ag << 5) + ar; + } + } + } + } +} + +#endif diff --git a/asnes/ppu/screen/screen.hpp b/asnes/ppu/screen/screen.hpp new file mode 100755 index 00000000..3472dec0 --- /dev/null +++ b/asnes/ppu/screen/screen.hpp @@ -0,0 +1,37 @@ +class Screen { +public: + PPU &self; + uint16 *output; + + struct { + 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; + + uint8 color_b; + uint8 color_g; + uint8 color_r; + } regs; + + void scanline(); + void run(); + void reset(); + + void serialize(serializer&); + Screen(PPU &self); + +private: + uint16 light_table[16][32768]; + uint16 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); +}; diff --git a/asnes/ppu/serialization.cpp b/asnes/ppu/serialization.cpp new file mode 100755 index 00000000..aa12be90 --- /dev/null +++ b/asnes/ppu/serialization.cpp @@ -0,0 +1,264 @@ +#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.integer(status.frames_updated); + s.integer(status.frames_executed); + + 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.ioamaddr); + s.integer(regs.icgramaddr); + + s.integer(regs.display_disabled); + 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); + oam.serialize(s); + window.serialize(s); + screen.serialize(s); +} + +void PPU::Background::serialize(serializer &s) { + s.integer(id); + + s.integer(t.x); + s.integer(t.mosaic_y); + s.integer(t.mosaic_countdown); + + 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_enabled); + s.integer(regs.sub_enabled); + + s.integer(regs.hoffset); + s.integer(regs.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); +} + +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_enabled); + s.integer(regs.sub_enabled); + 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(t.x); + + 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); + +} + +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/asnes/ppu/sprite/list.cpp b/asnes/ppu/sprite/list.cpp new file mode 100755 index 00000000..ea0db2e0 --- /dev/null +++ b/asnes/ppu/sprite/list.cpp @@ -0,0 +1,54 @@ +#ifdef PPU_CPP + +void PPU::Sprite::update(unsigned addr, uint8 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; + } +} + +unsigned PPU::Sprite::SpriteItem::width() const { + if(size == 0) { + static unsigned width[] = { 8, 8, 8, 16, 16, 32, 16, 16 }; + return width[ppu.oam.regs.base_size]; + } else { + static unsigned width[] = { 16, 32, 64, 32, 64, 64, 32, 32 }; + return width[ppu.oam.regs.base_size]; + } +} + +unsigned PPU::Sprite::SpriteItem::height() const { + if(size == 0) { + if(ppu.oam.regs.interlace && ppu.oam.regs.base_size >= 6) return 16; + static unsigned height[] = { 8, 8, 8, 16, 16, 32, 32, 32 }; + return height[ppu.oam.regs.base_size]; + } else { + static unsigned height[] = { 16, 32, 64, 32, 64, 64, 64, 32 }; + return height[ppu.oam.regs.base_size]; + } +} + +#endif diff --git a/asnes/ppu/sprite/sprite.cpp b/asnes/ppu/sprite/sprite.cpp new file mode 100755 index 00000000..8501eee0 --- /dev/null +++ b/asnes/ppu/sprite/sprite.cpp @@ -0,0 +1,218 @@ +#ifdef PPU_CPP + +#include "list.cpp" + +void PPU::Sprite::address_reset() { + self.regs.oam_addr = self.regs.oam_baseaddr << 1; + 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; + auto oam_item = t.item[t.active]; + auto oam_tile = t.tile[t.active]; + + if(t.y == (!self.regs.overscan ? 225 : 240) && self.regs.display_disabled == 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.ioamaddr = 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; + + auto 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++) { + auto 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_enabled) { + output.main.palette = tile.palette + color; + output.main.priority = priority_table[tile.priority]; + } + + if(regs.sub_enabled) { + output.sub.palette = tile.palette + color; + output.sub.priority = priority_table[tile.priority]; + } + } + } +} + +void PPU::Sprite::tilefetch() { + auto oam_item = t.item[t.active]; + auto oam_tile = t.tile[t.active]; + + for(signed i = 31; i >= 0; i--) { + if(oam_item[i] == 0xff) continue; + auto 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 = memory::vram[addr + 0]; + oam_tile[n].d1 = memory::vram[addr + 1]; + self.add_clocks(2); + + oam_tile[n].d2 = memory::vram[addr + 16]; + oam_tile[n].d3 = memory::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; + } + + 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_enabled = 0; + regs.sub_enabled = 0; + regs.interlace = 0; + + regs.base_size = 0; + regs.nameselect = 0; + regs.tiledata_addr = 0; + regs.first_sprite = 0; + + regs.priority0 = 0; + regs.priority1 = 0; + regs.priority2 = 0; + regs.priority3 = 0; + + regs.time_over = 0; + regs.range_over = 0; + + 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/asnes/ppu/sprite/sprite.hpp b/asnes/ppu/sprite/sprite.hpp new file mode 100755 index 00000000..d861e724 --- /dev/null +++ b/asnes/ppu/sprite/sprite.hpp @@ -0,0 +1,81 @@ +class Sprite { +public: + PPU &self; + + 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 { + bool main_enabled; + bool sub_enabled; + bool interlace; + + uint8 base_size; + uint8 nameselect; + uint16 tiledata_addr; + uint8 first_sprite; + + unsigned priority0; + unsigned priority1; + unsigned priority2; + unsigned priority3; + + bool time_over; + bool range_over; + } regs; + + struct { + struct { + unsigned priority; //0 = none (transparent) + unsigned palette; + } main, sub; + } output; + + //list.cpp + void update(unsigned addr, uint8 data); + + //sprite.cpp + void address_reset(); + void frame(); + void scanline(); + void run(); + void tilefetch(); + void reset(); + + void serialize(serializer&); + Sprite(PPU &self); + +private: + bool on_scanline(SpriteItem&); +}; diff --git a/asnes/ppu/window/window.cpp b/asnes/ppu/window/window.cpp new file mode 100755 index 00000000..34966e3e --- /dev/null +++ b/asnes/ppu/window/window.cpp @@ -0,0 +1,167 @@ +#ifdef PPU_CPP + +void PPU::Window::scanline() { + t.x = 0; +} + +void PPU::Window::run() { + bool main, sub; + + 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.oam.output.main.priority = 0; + if(sub) self.oam.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; + + t.x++; +} + +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 +) { + unsigned x = t.x; + bool output; + + if(one_enable == false && two_enable == false) { + output = false; + } else if(one_enable == true && two_enable == false) { + output = (x >= regs.one_left && x <= regs.one_right) ^ one_invert; + } else if(one_enable == false && two_enable == true) { + output = (x >= regs.two_left && x <= regs.two_right) ^ two_invert; + } else { + bool one = (x >= regs.one_left && x <= regs.one_right) ^ one_invert; + bool two = (x >= regs.two_left && x <= regs.two_right) ^ two_invert; + 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() { + t.x = 0; + regs.bg1_one_enable = false; + regs.bg1_one_invert = false; + regs.bg1_two_enable = false; + regs.bg1_two_invert = false; + regs.bg2_one_enable = false; + regs.bg2_one_invert = false; + regs.bg2_two_enable = false; + regs.bg2_two_invert = false; + regs.bg3_one_enable = false; + regs.bg3_one_invert = false; + regs.bg3_two_enable = false; + regs.bg3_two_invert = false; + regs.bg4_one_enable = false; + regs.bg4_one_invert = false; + regs.bg4_two_enable = false; + regs.bg4_two_invert = false; + regs.oam_one_enable = false; + regs.oam_one_invert = false; + regs.oam_two_enable = false; + regs.oam_two_invert = false; + regs.col_one_enable = false; + regs.col_one_invert = false; + regs.col_two_enable = false; + regs.col_two_invert = false; + regs.one_left = 0; + regs.one_right = 0; + regs.two_left = 0; + regs.two_right = 0; + regs.bg1_mask = 0; + regs.bg2_mask = 0; + regs.bg3_mask = 0; + regs.bg4_mask = 0; + regs.oam_mask = 0; + regs.col_mask = 0; + regs.bg1_main_enable = 0; + regs.bg1_sub_enable = 0; + regs.bg2_main_enable = 0; + regs.bg2_sub_enable = 0; + regs.bg3_main_enable = 0; + regs.bg3_sub_enable = 0; + regs.bg4_main_enable = 0; + regs.bg4_sub_enable = 0; + regs.oam_main_enable = 0; + regs.oam_sub_enable = 0; + regs.col_main_mask = 0; + regs.col_sub_mask = 0; + output.main.color_enable = 0; + output.sub.color_enable = 0; +} + +PPU::Window::Window(PPU &self) : self(self) { +} + +#endif diff --git a/asnes/ppu/window/window.hpp b/asnes/ppu/window/window.hpp new file mode 100755 index 00000000..8092da21 --- /dev/null +++ b/asnes/ppu/window/window.hpp @@ -0,0 +1,87 @@ +class Window { +public: + PPU &self; + + struct { + unsigned x; + } t; + + 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; + + uint8 bg1_mask; + uint8 bg2_mask; + uint8 bg3_mask; + uint8 bg4_mask; + uint8 oam_mask; + uint8 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; + + uint8 col_main_mask; + uint8 col_sub_mask; + } regs; + + struct { + struct { + bool color_enable; + } main, sub; + } output; + + void scanline(); + void run(); + void reset(); + + void serialize(serializer&); + Window(PPU &self); + +private: + 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 + ); +}; diff --git a/asnes/scheduler/scheduler.cpp b/asnes/scheduler/scheduler.cpp new file mode 100755 index 00000000..e7549501 --- /dev/null +++ b/asnes/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 reason) { + exit_reason = reason; + thread = co_active(); + co_switch(host_thread); +} + +void Scheduler::init() { + host_thread = co_active(); + thread = cpu.thread; + sync = SynchronizeMode::None; +} + +Scheduler::Scheduler() { + host_thread = 0; + thread = 0; + exit_reason = ExitReason::UnknownEvent; +} + +#endif diff --git a/asnes/scheduler/scheduler.hpp b/asnes/scheduler/scheduler.hpp new file mode 100755 index 00000000..335d8e9c --- /dev/null +++ b/asnes/scheduler/scheduler.hpp @@ -0,0 +1,16 @@ +struct Scheduler : property { + enum class SynchronizeMode : unsigned { None, CPU, All } sync; + enum class ExitReason : unsigned { UnknownEvent, FrameEvent, SynchronizeEvent, DebuggerEvent }; + readonly 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); + + void init(); + Scheduler(); +}; + +extern Scheduler scheduler; diff --git a/asnes/smp/core/algorithms.cpp b/asnes/smp/core/algorithms.cpp new file mode 100755 index 00000000..30a21076 --- /dev/null +++ b/asnes/smp/core/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/asnes/smp/core/core.cpp b/asnes/smp/core/core.cpp new file mode 100755 index 00000000..88243dad --- /dev/null +++ b/asnes/smp/core/core.cpp @@ -0,0 +1,31 @@ +#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 + +SMPcore::SMPcore() { + initialize_opcode_table(); +} + +} diff --git a/asnes/smp/core/core.hpp b/asnes/smp/core/core.hpp new file mode 100755 index 00000000..85e8c74a --- /dev/null +++ b/asnes/smp/core/core.hpp @@ -0,0 +1,118 @@ +class SMPcore { +public: + #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; + + 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/asnes/smp/core/disassembler/disassembler.cpp b/asnes/smp/core/disassembler/disassembler.cpp new file mode 100755 index 00000000..ee5663a3 --- /dev/null +++ b/asnes/smp/core/disassembler/disassembler.cpp @@ -0,0 +1,315 @@ +#ifdef SMPCORE_CPP + +uint8 SMPcore::disassemble_read(uint16 addr) { + if(addr >= 0xffc0) { + #if defined(DEBUGGER) + if(smp.iplrom_enable()) return smp.iplrom[addr & 0x3f]; + #else + //unable to determine if IPLROM is enabled, assume that it is + return smp.iplrom[addr & 0x3f]; + #endif + } + return memory::apuram[addr]; +} + +uint16 SMPcore::relb(int8 offset, int op_len) { + uint16 pc = regs.pc + op_len; + return pc + offset; +} + +void SMPcore::disassemble_opcode(char *output, uint16 addr) { + char *s, t[512]; + uint8 op, op0, op1; + uint16 opw, opdp0, opdp1; + s = output; + + sprintf(s, "..%.4x ", addr); + + op = disassemble_read(addr + 0); + op0 = disassemble_read(addr + 1); + op1 = disassemble_read(addr + 2); + opw = (op0) | (op1 << 8); + opdp0 = ((unsigned)regs.p.p << 8) + op0; + opdp1 = ((unsigned)regs.p.p << 8) + op1; + + strcpy(t, " "); + + switch(op) { + case 0x00: sprintf(t, "nop"); break; + case 0x01: sprintf(t, "tcall 0"); break; + case 0x02: sprintf(t, "set0 $%.3x", opdp0); break; + case 0x03: sprintf(t, "bbs0 $%.3x,$%.4x", opdp0, relb(op1, 3)); break; + case 0x04: sprintf(t, "or a,$%.3x", opdp0); break; + case 0x05: sprintf(t, "or a,$%.4x", opw); break; + case 0x06: sprintf(t, "or a,(x)"); break; + case 0x07: sprintf(t, "or a,($%.3x+x)", opdp0); break; + case 0x08: sprintf(t, "or a,#$%.2x", op0); break; + case 0x09: sprintf(t, "or $%.3x,$%.3x", opdp1, opdp0); break; + case 0x0a: sprintf(t, "or1 c,$%.4x:%d", opw & 0x1fff, opw >> 13); break; + case 0x0b: sprintf(t, "asl $%.3x", opdp0); break; + case 0x0c: sprintf(t, "asl $%.4x", opw); break; + case 0x0d: sprintf(t, "push p"); break; + case 0x0e: sprintf(t, "tset $%.4x,a", opw); break; + case 0x0f: sprintf(t, "brk"); break; + case 0x10: sprintf(t, "bpl $%.4x", relb(op0, 2)); break; + case 0x11: sprintf(t, "tcall 1"); break; + case 0x12: sprintf(t, "clr0 $%.3x", opdp0); break; + case 0x13: sprintf(t, "bbc0 $%.3x,$%.4x", opdp0, relb(op1, 3)); break; + case 0x14: sprintf(t, "or a,$%.3x+x", opdp0); break; + case 0x15: sprintf(t, "or a,$%.4x+x", opw); break; + case 0x16: sprintf(t, "or a,$%.4x+y", opw); break; + case 0x17: sprintf(t, "or a,($%.3x)+y", opdp0); break; + case 0x18: sprintf(t, "or $%.3x,#$%.2x", opdp1, op0); break; + case 0x19: sprintf(t, "or (x),(y)"); break; + case 0x1a: sprintf(t, "decw $%.3x", opdp0); break; + case 0x1b: sprintf(t, "asl $%.3x+x", opdp0); break; + case 0x1c: sprintf(t, "asl a"); break; + case 0x1d: sprintf(t, "dec x"); break; + case 0x1e: sprintf(t, "cmp x,$%.4x", opw); break; + case 0x1f: sprintf(t, "jmp ($%.4x+x)", opw); break; + case 0x20: sprintf(t, "clrp"); break; + case 0x21: sprintf(t, "tcall 2"); break; + case 0x22: sprintf(t, "set1 $%.3x", opdp0); break; + case 0x23: sprintf(t, "bbs1 $%.3x,$%.4x", opdp0, relb(op1, 3)); break; + case 0x24: sprintf(t, "and a,$%.3x", opdp0); break; + case 0x25: sprintf(t, "and a,$%.4x", opw); break; + case 0x26: sprintf(t, "and a,(x)"); break; + case 0x27: sprintf(t, "and a,($%.3x+x)", opdp0); break; + case 0x28: sprintf(t, "and a,#$%.2x", op0); break; + case 0x29: sprintf(t, "and $%.3x,$%.3x", opdp1, opdp0); break; + case 0x2a: sprintf(t, "or1 c,!$%.4x:%d", opw & 0x1fff, opw >> 13); break; + case 0x2b: sprintf(t, "rol $%.3x", opdp0); break; + case 0x2c: sprintf(t, "rol $%.4x", opw); break; + case 0x2d: sprintf(t, "push a"); break; + case 0x2e: sprintf(t, "cbne $%.3x,$%.4x", opdp0, relb(op1, 3)); break; + case 0x2f: sprintf(t, "bra $%.4x", relb(op0, 2)); break; + case 0x30: sprintf(t, "bmi $%.4x", relb(op0, 2)); break; + case 0x31: sprintf(t, "tcall 3"); break; + case 0x32: sprintf(t, "clr1 $%.3x", opdp0); break; + case 0x33: sprintf(t, "bbc1 $%.3x,$%.4x", opdp0, relb(op1, 3)); break; + case 0x34: sprintf(t, "and a,$%.3x+x", opdp0); break; + case 0x35: sprintf(t, "and a,$%.4x+x", opw); break; + case 0x36: sprintf(t, "and a,$%.4x+y", opw); break; + case 0x37: sprintf(t, "and a,($%.3x)+y", opdp0); break; + case 0x38: sprintf(t, "and $%.3x,#$%.2x", opdp1, op0); break; + case 0x39: sprintf(t, "and (x),(y)"); break; + case 0x3a: sprintf(t, "incw $%.3x", opdp0); break; + case 0x3b: sprintf(t, "rol $%.3x+x", opdp0); break; + case 0x3c: sprintf(t, "rol a"); break; + case 0x3d: sprintf(t, "inc x"); break; + case 0x3e: sprintf(t, "cmp x,$%.3x", opdp0); break; + case 0x3f: sprintf(t, "call $%.4x", opw); break; + case 0x40: sprintf(t, "setp"); break; + case 0x41: sprintf(t, "tcall 4"); break; + case 0x42: sprintf(t, "set2 $%.3x", opdp0); break; + case 0x43: sprintf(t, "bbs2 $%.3x,$%.4x", opdp0, relb(op1, 3)); break; + case 0x44: sprintf(t, "eor a,$%.3x", opdp0); break; + case 0x45: sprintf(t, "eor a,$%.4x", opw); break; + case 0x46: sprintf(t, "eor a,(x)"); break; + case 0x47: sprintf(t, "eor a,($%.3x+x)", opdp0); break; + case 0x48: sprintf(t, "eor a,#$%.2x", op0); break; + case 0x49: sprintf(t, "eor $%.3x,$%.3x", opdp1, opdp0); break; + case 0x4a: sprintf(t, "and1 c,$%.4x:%d", opw & 0x1fff, opw >> 13); break; + case 0x4b: sprintf(t, "lsr $%.3x", opdp0); break; + case 0x4c: sprintf(t, "lsr $%.4x", opw); break; + case 0x4d: sprintf(t, "push x"); break; + case 0x4e: sprintf(t, "tclr $%.4x,a", opw); break; + case 0x4f: sprintf(t, "pcall $ff%.2x", op0); break; + case 0x50: sprintf(t, "bvc $%.4x", relb(op0, 2)); break; + case 0x51: sprintf(t, "tcall 5"); break; + case 0x52: sprintf(t, "clr2 $%.3x", opdp0); break; + case 0x53: sprintf(t, "bbc2 $%.3x,$%.4x", opdp0, relb(op1, 3)); break; + case 0x54: sprintf(t, "eor a,$%.3x+x", opdp0); break; + case 0x55: sprintf(t, "eor a,$%.4x+x", opw); break; + case 0x56: sprintf(t, "eor a,$%.4x+y", opw); break; + case 0x57: sprintf(t, "eor a,($%.3x)+y", opdp0); break; + case 0x58: sprintf(t, "eor $%.3x,#$%.2x", opdp1, op0); break; + case 0x59: sprintf(t, "eor (x),(y)"); break; + case 0x5a: sprintf(t, "cmpw ya,$%.3x", opdp0); break; + case 0x5b: sprintf(t, "lsr $%.3x+x", opdp0); break; + case 0x5c: sprintf(t, "lsr a"); break; + case 0x5d: sprintf(t, "mov x,a"); break; + case 0x5e: sprintf(t, "cmp y,$%.4x", opw); break; + case 0x5f: sprintf(t, "jmp $%.4x", opw); break; + case 0x60: sprintf(t, "clrc"); break; + case 0x61: sprintf(t, "tcall 6"); break; + case 0x62: sprintf(t, "set3 $%.3x", opdp0); break; + case 0x63: sprintf(t, "bbs3 $%.3x,$%.4x", opdp0, relb(op1, 3)); break; + case 0x64: sprintf(t, "cmp a,$%.3x", opdp0); break; + case 0x65: sprintf(t, "cmp a,$%.4x", opw); break; + case 0x66: sprintf(t, "cmp a,(x)"); break; + case 0x67: sprintf(t, "cmp a,($%.3x+x)", opdp0); break; + case 0x68: sprintf(t, "cmp a,#$%.2x", op0); break; + case 0x69: sprintf(t, "cmp $%.3x,$%.3x", opdp1, opdp0); break; + case 0x6a: sprintf(t, "and1 c,!$%.4x:%d", opw & 0x1fff, opw >> 13); break; + case 0x6b: sprintf(t, "ror $%.3x", opdp0); break; + case 0x6c: sprintf(t, "ror $%.4x", opw); break; + case 0x6d: sprintf(t, "push y"); break; + case 0x6e: sprintf(t, "dbnz $%.3x,$%.4x", opdp0, relb(op1, 3)); break; + case 0x6f: sprintf(t, "ret"); break; + case 0x70: sprintf(t, "bvs $%.4x", relb(op0, 2)); break; + case 0x71: sprintf(t, "tcall 7"); break; + case 0x72: sprintf(t, "clr3 $%.3x", opdp0); break; + case 0x73: sprintf(t, "bbc3 $%.3x,$%.4x", opdp0, relb(op1, 3)); break; + case 0x74: sprintf(t, "cmp a,$%.3x+x", opdp0); break; + case 0x75: sprintf(t, "cmp a,$%.4x+x", opw); break; + case 0x76: sprintf(t, "cmp a,$%.4x+y", opw); break; + case 0x77: sprintf(t, "cmp a,($%.3x)+y", opdp0); break; + case 0x78: sprintf(t, "cmp $%.3x,#$%.2x", opdp1, op0); break; + case 0x79: sprintf(t, "cmp (x),(y)"); break; + case 0x7a: sprintf(t, "addw ya,$%.3x", opdp0); break; + case 0x7b: sprintf(t, "ror $%.3x+x", opdp0); break; + case 0x7c: sprintf(t, "ror a"); break; + case 0x7d: sprintf(t, "mov a,x"); break; + case 0x7e: sprintf(t, "cmp y,$%.3x", opdp0); break; + case 0x7f: sprintf(t, "reti"); break; + case 0x80: sprintf(t, "setc"); break; + case 0x81: sprintf(t, "tcall 8"); break; + case 0x82: sprintf(t, "set4 $%.3x", opdp0); break; + case 0x83: sprintf(t, "bbs4 $%.3x,$%.4x", opdp0, relb(op1, 3)); break; + case 0x84: sprintf(t, "adc a,$%.3x", opdp0); break; + case 0x85: sprintf(t, "adc a,$%.4x", opw); break; + case 0x86: sprintf(t, "adc a,(x)"); break; + case 0x87: sprintf(t, "adc a,($%.3x+x)", opdp0); break; + case 0x88: sprintf(t, "adc a,#$%.2x", op0); break; + case 0x89: sprintf(t, "adc $%.3x,$%.3x", opdp1, opdp0); break; + case 0x8a: sprintf(t, "eor1 c,$%.4x:%d", opw & 0x1fff, opw >> 13); break; + case 0x8b: sprintf(t, "dec $%.3x", opdp0); break; + case 0x8c: sprintf(t, "dec $%.4x", opw); break; + case 0x8d: sprintf(t, "mov y,#$%.2x", op0); break; + case 0x8e: sprintf(t, "pop p"); break; + case 0x8f: sprintf(t, "mov $%.3x,#$%.2x", opdp1, op0); break; + case 0x90: sprintf(t, "bcc $%.4x", relb(op0, 2)); break; + case 0x91: sprintf(t, "tcall 9"); break; + case 0x92: sprintf(t, "clr4 $%.3x", opdp0); break; + case 0x93: sprintf(t, "bbc4 $%.3x,$%.4x", opdp0, relb(op1, 3)); break; + case 0x94: sprintf(t, "adc a,$%.3x+x", opdp0); break; + case 0x95: sprintf(t, "adc a,$%.4x+x", opw); break; + case 0x96: sprintf(t, "adc a,$%.4x+y", opw); break; + case 0x97: sprintf(t, "adc a,($%.3x)+y", opdp0); break; + case 0x98: sprintf(t, "adc $%.3x,#$%.2x", opdp1, op0); break; + case 0x99: sprintf(t, "adc (x),(y)"); break; + case 0x9a: sprintf(t, "subw ya,$%.3x", opdp0); break; + case 0x9b: sprintf(t, "dec $%.3x+x", opdp0); break; + case 0x9c: sprintf(t, "dec a"); break; + case 0x9d: sprintf(t, "mov x,sp"); break; + case 0x9e: sprintf(t, "div ya,x"); break; + case 0x9f: sprintf(t, "xcn a"); break; + case 0xa0: sprintf(t, "ei"); break; + case 0xa1: sprintf(t, "tcall 10"); break; + case 0xa2: sprintf(t, "set5 $%.3x", opdp0); break; + case 0xa3: sprintf(t, "bbs5 $%.3x,$%.4x", opdp0, relb(op1, 3)); break; + case 0xa4: sprintf(t, "sbc a,$%.3x", opdp0); break; + case 0xa5: sprintf(t, "sbc a,$%.4x", opw); break; + case 0xa6: sprintf(t, "sbc a,(x)"); break; + case 0xa7: sprintf(t, "sbc a,($%.3x+x)", opdp0); break; + case 0xa8: sprintf(t, "sbc a,#$%.2x", op0); break; + case 0xa9: sprintf(t, "sbc $%.3x,$%.3x", opdp1, opdp0); break; + case 0xaa: sprintf(t, "mov1 c,$%.4x:%d", opw & 0x1fff, opw >> 13); break; + case 0xab: sprintf(t, "inc $%.3x", opdp0); break; + case 0xac: sprintf(t, "inc $%.4x", opw); break; + case 0xad: sprintf(t, "cmp y,#$%.2x", op0); break; + case 0xae: sprintf(t, "pop a"); break; + case 0xaf: sprintf(t, "mov (x)+,a"); break; + case 0xb0: sprintf(t, "bcs $%.4x", relb(op0, 2)); break; + case 0xb1: sprintf(t, "tcall 11"); break; + case 0xb2: sprintf(t, "clr5 $%.3x", opdp0); break; + case 0xb3: sprintf(t, "bbc5 $%.3x,$%.4x", opdp0, relb(op1, 3)); break; + case 0xb4: sprintf(t, "sbc a,$%.3x+x", opdp0); break; + case 0xb5: sprintf(t, "sbc a,$%.4x+x", opw); break; + case 0xb6: sprintf(t, "sbc a,$%.4x+y", opw); break; + case 0xb7: sprintf(t, "sbc a,($%.3x)+y", opdp0); break; + case 0xb8: sprintf(t, "sbc $%.3x,#$%.2x", opdp1, op0); break; + case 0xb9: sprintf(t, "sbc (x),(y)"); break; + case 0xba: sprintf(t, "movw ya,$%.3x", opdp0); break; + case 0xbb: sprintf(t, "inc $%.3x+x", opdp0); break; + case 0xbc: sprintf(t, "inc a"); break; + case 0xbd: sprintf(t, "mov sp,x"); break; + case 0xbe: sprintf(t, "das a"); break; + case 0xbf: sprintf(t, "mov a,(x)+"); break; + case 0xc0: sprintf(t, "di"); break; + case 0xc1: sprintf(t, "tcall 12"); break; + case 0xc2: sprintf(t, "set6 $%.3x", opdp0); break; + case 0xc3: sprintf(t, "bbs6 $%.3x,$%.4x", opdp0, relb(op1, 3)); break; + case 0xc4: sprintf(t, "mov $%.3x,a", opdp0); break; + case 0xc5: sprintf(t, "mov $%.4x,a", opw); break; + case 0xc6: sprintf(t, "mov (x),a"); break; + case 0xc7: sprintf(t, "mov ($%.3x+x),a", opdp0); break; + case 0xc8: sprintf(t, "cmp x,#$%.2x", op0); break; + case 0xc9: sprintf(t, "mov $%.4x,x", opw); break; + case 0xca: sprintf(t, "mov1 $%.4x:%d,c", opw & 0x1fff, opw >> 13); break; + case 0xcb: sprintf(t, "mov $%.3x,y", opdp0); break; + case 0xcc: sprintf(t, "mov $%.4x,y", opw); break; + case 0xcd: sprintf(t, "mov x,#$%.2x", op0); break; + case 0xce: sprintf(t, "pop x"); break; + case 0xcf: sprintf(t, "mul ya"); break; + case 0xd0: sprintf(t, "bne $%.4x", relb(op0, 2)); break; + case 0xd1: sprintf(t, "tcall 13"); break; + case 0xd2: sprintf(t, "clr6 $%.3x", opdp0); break; + case 0xd3: sprintf(t, "bbc6 $%.3x,$%.4x", opdp0, relb(op1, 3)); break; + case 0xd4: sprintf(t, "mov $%.3x+x,a", opdp0); break; + case 0xd5: sprintf(t, "mov $%.4x+x,a", opw); break; + case 0xd6: sprintf(t, "mov $%.4x+y,a", opw); break; + case 0xd7: sprintf(t, "mov ($%.3x)+y,a", opdp0); break; + case 0xd8: sprintf(t, "mov $%.3x,x", opdp0); break; + case 0xd9: sprintf(t, "mov $%.3x+y,x", opdp0); break; + case 0xda: sprintf(t, "movw $%.3x,ya", opdp0); break; + case 0xdb: sprintf(t, "mov $%.3x+x,y", opdp0); break; + case 0xdc: sprintf(t, "dec y"); break; + case 0xdd: sprintf(t, "mov a,y"); break; + case 0xde: sprintf(t, "cbne $%.3x+x,$%.4x", opdp0, relb(op1, 3)); break; + case 0xdf: sprintf(t, "daa a"); break; + case 0xe0: sprintf(t, "clrv"); break; + case 0xe1: sprintf(t, "tcall 14"); break; + case 0xe2: sprintf(t, "set7 $%.3x", opdp0); break; + case 0xe3: sprintf(t, "bbs7 $%.3x,$%.4x", opdp0, relb(op1, 3)); break; + case 0xe4: sprintf(t, "mov a,$%.3x", opdp0); break; + case 0xe5: sprintf(t, "mov a,$%.4x", opw); break; + case 0xe6: sprintf(t, "mov a,(x)"); break; + case 0xe7: sprintf(t, "mov a,($%.3x+x)", opdp0); break; + case 0xe8: sprintf(t, "mov a,#$%.2x", op0); break; + case 0xe9: sprintf(t, "mov x,$%.4x", opw); break; + case 0xea: sprintf(t, "not1 c,$%.4x:%d", opw & 0x1fff, opw >> 13); break; + case 0xeb: sprintf(t, "mov y,$%.3x", opdp0); break; + case 0xec: sprintf(t, "mov y,$%.4x", opw); break; + case 0xed: sprintf(t, "notc"); break; + case 0xee: sprintf(t, "pop y"); break; + case 0xef: sprintf(t, "sleep"); break; + case 0xf0: sprintf(t, "beq $%.4x", relb(op0, 2)); break; + case 0xf1: sprintf(t, "tcall 15"); break; + case 0xf2: sprintf(t, "clr7 $%.3x", opdp0); break; + case 0xf3: sprintf(t, "bbc7 $%.3x,$%.4x", opdp0, relb(op1, 3)); break; + case 0xf4: sprintf(t, "mov a,$%.3x+x", opdp0); break; + case 0xf5: sprintf(t, "mov a,$%.4x+x", opw); break; + case 0xf6: sprintf(t, "mov a,$%.4x+y", opw); break; + case 0xf7: sprintf(t, "mov a,($%.3x)+y", opdp0); break; + case 0xf8: sprintf(t, "mov x,$%.3x", opdp0); break; + case 0xf9: sprintf(t, "mov x,$%.3x+y", opdp0); break; + case 0xfa: sprintf(t, "mov $%.3x,$%.3x", opdp1, opdp0); break; + case 0xfb: sprintf(t, "mov y,$%.3x+x", opdp0); break; + case 0xfc: sprintf(t, "inc y"); break; + case 0xfd: sprintf(t, "mov y,a"); break; + case 0xfe: sprintf(t, "dbnz y,$%.4x", relb(op0, 2)); break; + case 0xff: sprintf(t, "stop"); break; + } + + t[strlen(t)] = ' '; + strcat(s, t); + + sprintf(t, "A:%.2x X:%.2x Y:%.2x SP:01%.2x YA:%.4x ", + regs.a, regs.x, regs.y, regs.sp, (uint16)regs.ya); + strcat(s, t); + + sprintf(t, "%c%c%c%c%c%c%c%c", + regs.p.n ? 'N' : 'n', + regs.p.v ? 'V' : 'v', + regs.p.p ? 'P' : 'p', + regs.p.b ? 'B' : 'b', + regs.p.h ? 'H' : 'h', + regs.p.i ? 'I' : 'i', + regs.p.z ? 'Z' : 'z', + regs.p.c ? 'C' : 'c'); + strcat(s, t); +} + +#endif diff --git a/asnes/smp/core/disassembler/disassembler.hpp b/asnes/smp/core/disassembler/disassembler.hpp new file mode 100755 index 00000000..a6e78e09 --- /dev/null +++ b/asnes/smp/core/disassembler/disassembler.hpp @@ -0,0 +1,3 @@ +void disassemble_opcode(char *output, uint16 addr); +inline uint8 disassemble_read(uint16 addr); +inline uint16 relb(int8 offset, int op_len); diff --git a/asnes/smp/core/memory.hpp b/asnes/smp/core/memory.hpp new file mode 100755 index 00000000..9af32956 --- /dev/null +++ b/asnes/smp/core/memory.hpp @@ -0,0 +1,27 @@ +alwaysinline uint8_t op_readpc() { + return op_read(regs.pc++); +} + +alwaysinline uint8_t op_readstack() { + return op_read(0x0100 | ++regs.sp); +} + +alwaysinline void op_writestack(uint8_t data) { + op_write(0x0100 | regs.sp--, data); +} + +alwaysinline uint8_t op_readaddr(uint16_t addr) { + return op_read(addr); +} + +alwaysinline void op_writeaddr(uint16_t addr, uint8_t data) { + op_write(addr, 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/asnes/smp/core/opcode_misc.cpp b/asnes/smp/core/opcode_misc.cpp new file mode 100755 index 00000000..0f4d4ce3 --- /dev/null +++ b/asnes/smp/core/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_writestack(regs.r[n]); +} + +void SMPcore::op_push_p() { + op_io(); + op_io(); + op_writestack(regs.p); +} + +template void SMPcore::op_pop_reg() { + op_io(); + op_io(); + regs.r[n] = op_readstack(); +} + +void SMPcore::op_pop_p() { + op_io(); + op_io(); + regs.p = op_readstack(); +} + +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/asnes/smp/core/opcode_mov.cpp b/asnes/smp/core/opcode_mov.cpp new file mode 100755 index 00000000..e21593c8 --- /dev/null +++ b/asnes/smp/core/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_readaddr(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_readaddr(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_readaddr(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_readaddr(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_readaddr(dp); + op_writeaddr(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_readaddr(dp); + op_writeaddr(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_readaddr(dp); + op_writeaddr(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_readaddr(dp); + op_writeaddr(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_readaddr(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_readaddr(dp); + (regs.p.c) ? rd |= (1 << bit) : rd &= ~(1 << bit); + op_io(); + op_writeaddr(dp, rd); +} + +#endif diff --git a/asnes/smp/core/opcode_pc.cpp b/asnes/smp/core/opcode_pc.cpp new file mode 100755 index 00000000..b69d8416 --- /dev/null +++ b/asnes/smp/core/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_readaddr(dp + 0) << 0; + rd |= op_readaddr(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_writestack(regs.pc >> 8); + op_writestack(regs.pc >> 0); + regs.pc = rd; +} + +void SMPcore::op_pcall() { + rd = op_readpc(); + op_io(); + op_io(); + op_writestack(regs.pc >> 8); + op_writestack(regs.pc >> 0); + regs.pc = 0xff00 | rd; +} + +template void SMPcore::op_tcall() { + dp = 0xffde - (n << 1); + rd = op_readaddr(dp + 0) << 0; + rd |= op_readaddr(dp + 1) << 8; + op_io(); + op_io(); + op_io(); + op_writestack(regs.pc >> 8); + op_writestack(regs.pc >> 0); + regs.pc = rd; +} + +void SMPcore::op_brk() { + rd = op_readaddr(0xffde) << 0; + rd |= op_readaddr(0xffdf) << 8; + op_io(); + op_io(); + op_writestack(regs.pc >> 8); + op_writestack(regs.pc >> 0); + op_writestack(regs.p); + regs.pc = rd; + regs.p.b = 1; + regs.p.i = 0; +} + +void SMPcore::op_ret() { + rd = op_readstack() << 0; + rd |= op_readstack() << 8; + op_io(); + op_io(); + regs.pc = rd; +} + +void SMPcore::op_reti() { + regs.p = op_readstack(); + rd = op_readstack() << 0; + rd |= op_readstack() << 8; + op_io(); + op_io(); + regs.pc = rd; +} + +#endif diff --git a/asnes/smp/core/opcode_read.cpp b/asnes/smp/core/opcode_read.cpp new file mode 100755 index 00000000..2058cff2 --- /dev/null +++ b/asnes/smp/core/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_readaddr(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_readaddr(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_readaddr(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_readaddr(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_readaddr(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_readaddr(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_readaddr(dp); + rd ^= 1 << bit; + op_writeaddr(dp, rd); +} + +template void SMPcore::op_or1_bit() { + dp = op_readpc() << 0; + dp |= op_readpc() << 8; + bit = dp >> 13; + dp &= 0x1fff; + rd = op_readaddr(dp); + op_io(); + regs.p.c = regs.p.c | ((bool)(rd & (1 << bit)) ^ op); +} + +#endif diff --git a/asnes/smp/core/opcode_rmw.cpp b/asnes/smp/core/opcode_rmw.cpp new file mode 100755 index 00000000..103bd2db --- /dev/null +++ b/asnes/smp/core/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_readaddr(dp); + rd = (this->*op)(rd); + op_writeaddr(dp, rd); +} + +template +void SMPcore::op_adjust_addr_a() { + dp = op_readpc() << 0; + 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, (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/asnes/smp/core/registers.hpp b/asnes/smp/core/registers.hpp new file mode 100755 index 00000000..baed447b --- /dev/null +++ b/asnes/smp/core/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; + regya_t ya; + flag_t p; + regs_t() : a(r[0]), x(r[1]), y(r[2]), sp(r[3]), ya(r[2], r[0]) {} +}; diff --git a/asnes/smp/core/serialization.cpp b/asnes/smp/core/serialization.cpp new file mode 100755 index 00000000..883d013c --- /dev/null +++ b/asnes/smp/core/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/asnes/smp/core/table.cpp b/asnes/smp/core/table.cpp new file mode 100755 index 00000000..dc0f633a --- /dev/null +++ b/asnes/smp/core/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/asnes/smp/debugger/debugger.cpp b/asnes/smp/debugger/debugger.cpp new file mode 100755 index 00000000..8ca6695a --- /dev/null +++ b/asnes/smp/debugger/debugger.cpp @@ -0,0 +1,61 @@ +#ifdef SMP_CPP + +void sSMPDebugger::op_step() { + bool break_event = false; + + usage[regs.pc] |= UsageExec; + opcode_pc = regs.pc; + + if(debugger.step_smp) { + debugger.break_event = Debugger::BreakEvent::SMPStep; + scheduler.exit(Scheduler::ExitReason::DebuggerEvent); + } else { + debugger.breakpoint_test(Debugger::Breakpoint::Source::APURAM, Debugger::Breakpoint::Mode::Exec, regs.pc, 0x00); + } + + if(step_event) step_event(); + sSMP::op_step(); + synchronize_cpu(); +} + +uint8 sSMPDebugger::op_read(uint16 addr) { + uint8 data = sSMP::op_read(addr); + usage[addr] |= UsageRead; + debugger.breakpoint_test(Debugger::Breakpoint::Source::APURAM, Debugger::Breakpoint::Mode::Read, addr, data); + return data; +} + +void sSMPDebugger::op_write(uint16 addr, uint8 data) { + sSMP::op_write(addr, data); + usage[addr] |= UsageWrite; + usage[addr] &= ~UsageExec; + debugger.breakpoint_test(Debugger::Breakpoint::Source::APURAM, Debugger::Breakpoint::Mode::Write, addr, data); +} + +sSMPDebugger::sSMPDebugger() { + usage = new uint8[1 << 16](); + opcode_pc = 0xffc0; +} + +sSMPDebugger::~sSMPDebugger() { + delete[] usage; +} + +//=========== +//SMPDebugger +//=========== + +//$00f0 +unsigned sSMPDebugger::clock_speed() { return status.clock_speed; } +bool sSMPDebugger::timers_enable() { return status.timers_enabled; } +bool sSMPDebugger::ram_disable() { return status.ram_disabled; } +bool sSMPDebugger::ram_writable() { return status.ram_writable; } +bool sSMPDebugger::timers_disable() { return status.timers_disabled; } + +//$00f1 +bool sSMPDebugger::iplrom_enable() { return status.iplrom_enabled; } + +//$00f2 +unsigned sSMPDebugger::dsp_address() { return status.dsp_addr; } + +#endif diff --git a/asnes/smp/debugger/debugger.hpp b/asnes/smp/debugger/debugger.hpp new file mode 100755 index 00000000..ff03220c --- /dev/null +++ b/asnes/smp/debugger/debugger.hpp @@ -0,0 +1,36 @@ +class sSMPDebugger : public sSMP, public SMPDebugger { +public: + function step_event; + + enum Usage { + UsageRead = 0x80, + UsageWrite = 0x40, + UsageExec = 0x20, + }; + uint8 *usage; + uint16 opcode_pc; + + void op_step(); + uint8 op_read(uint16 addr); + void op_write(uint16 addr, uint8 data); + + sSMPDebugger(); + ~sSMPDebugger(); + + //=========== + //SMPDebugger + //=========== + + //$00f0 + unsigned clock_speed(); + bool timers_enable(); + bool ram_disable(); + bool ram_writable(); + bool timers_disable(); + + //$00f1 + bool iplrom_enable(); + + //$00f2 + unsigned dsp_address(); +}; diff --git a/asnes/smp/debugger/smp-debugger.cpp b/asnes/smp/debugger/smp-debugger.cpp new file mode 100755 index 00000000..31335a86 --- /dev/null +++ b/asnes/smp/debugger/smp-debugger.cpp @@ -0,0 +1,25 @@ +#ifdef SMP_CPP + +bool SMPDebugger::property(unsigned id, string &name, string &value) { + unsigned n = 0; + + //$00f0 + if(id == n++) { name = "$00f0"; value = ""; return true; } + if(id == n++) { name = "Clock Speed"; value = clock_speed(); return true; } + if(id == n++) { name = "Timers Enable"; value = timers_enable(); return true; } + if(id == n++) { name = "RAM Disable"; value = ram_disable(); return true; } + if(id == n++) { name = "RAM Writable"; value = ram_writable(); return true; } + if(id == n++) { name = "Timers Disable"; value = timers_disable(); return true; } + + //$00f1 + if(id == n++) { name = "$00f1"; value = ""; return true; } + if(id == n++) { name = "IPLROM Enable"; value = iplrom_enable(); return true; } + + //$00f2 + if(id == n++) { name = "$00f2"; value = ""; return true; } + if(id == n++) { name = "DSP Address"; value = string("0x", strhex<2>(dsp_address())); return true; } + + return false; +} + +#endif diff --git a/asnes/smp/debugger/smp-debugger.hpp b/asnes/smp/debugger/smp-debugger.hpp new file mode 100755 index 00000000..15e5b7b0 --- /dev/null +++ b/asnes/smp/debugger/smp-debugger.hpp @@ -0,0 +1,16 @@ +struct SMPDebugger : ChipDebugger { + bool property(unsigned id, string &name, string &value); + + //$00f0 + virtual unsigned clock_speed() { return 0; } + virtual bool timers_enable() { return 0; } + virtual bool ram_disable() { return 0; } + virtual bool ram_writable() { return 0; } + virtual bool timers_disable() { return 0; } + + //$00f1 + virtual bool iplrom_enable() { return 0; } + + //$00f2 + virtual unsigned dsp_address() { return 0; } +}; diff --git a/asnes/smp/iplrom.cpp b/asnes/smp/iplrom.cpp new file mode 100755 index 00000000..a2ade89d --- /dev/null +++ b/asnes/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/asnes/smp/memory/memory.cpp b/asnes/smp/memory/memory.cpp new file mode 100755 index 00000000..2f5081ce --- /dev/null +++ b/asnes/smp/memory/memory.cpp @@ -0,0 +1,213 @@ +#ifdef SMP_CPP + +alwaysinline uint8 SMP::ram_read(uint16 addr) { + if(addr >= 0xffc0 && status.iplrom_enabled) return iplrom[addr & 0x3f]; + if(status.ram_disabled) return 0x5a; //0xff on mini-SNES + return memory::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_disabled) memory::apuram[addr] = data; +} + +uint8 SMP::port_read(uint8 port) { + return memory::apuram[0xf4 + (port & 3)]; +} + +void SMP::port_write(uint8 port, uint8 data) { + memory::apuram[0xf4 + (port & 3)] = data; +} + +alwaysinline uint8 SMP::op_busread(uint16 addr) { + uint8 r; + if((addr & 0xfff0) == 0x00f0) { //00f0-00ff + switch(addr) { + case 0xf0: { //TEST -- write-only register + r = 0x00; + } break; + + case 0xf1: { //CONTROL -- write-only register + r = 0x00; + } break; + + case 0xf2: { //DSPADDR + r = status.dsp_addr; + } break; + + case 0xf3: { //DSPDATA + //0x80-0xff are read-only mirrors of 0x00-0x7f + r = dsp.read(status.dsp_addr & 0x7f); + } break; + + case 0xf4: //CPUIO0 + case 0xf5: //CPUIO1 + case 0xf6: //CPUIO2 + case 0xf7: { //CPUIO3 + synchronize_cpu(); + r = cpu.port_read(addr & 3); + } break; + + case 0xf8: { //RAM0 + r = status.smp_f8; + } break; + + case 0xf9: { //RAM1 + r = status.smp_f9; + } break; + + case 0xfa: //T0TARGET + case 0xfb: //T1TARGET + case 0xfc: { //T2TARGET -- write-only registers + r = 0x00; + } break; + + case 0xfd: { //T0OUT -- 4-bit counter value + r = t0.stage3_ticks & 15; + t0.stage3_ticks = 0; + } break; + + case 0xfe: { //T1OUT -- 4-bit counter value + r = t1.stage3_ticks & 15; + t1.stage3_ticks = 0; + } break; + + case 0xff: { //T2OUT -- 4-bit counter value + r = t2.stage3_ticks & 15; + t2.stage3_ticks = 0; + } break; + } + } else { + r = ram_read(addr); + } + + return r; +} + +alwaysinline void SMP::op_buswrite(uint16 addr, uint8 data) { + if((addr & 0xfff0) == 0x00f0) { //$00f0-00ff + 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_enabled = data & 0x08; + status.ram_disabled = data & 0x04; + status.ram_writable = data & 0x02; + status.timers_disabled = data & 0x01; + + status.timer_step = (1 << status.clock_speed) + (2 << status.timer_speed); + + t0.sync_stage1(); + t1.sync_stage1(); + t2.sync_stage1(); + } break; + + case 0xf1: { //CONTROL + status.iplrom_enabled = 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(t2.enabled == false && (data & 0x04)) { + t2.stage2_ticks = 0; + t2.stage3_ticks = 0; + } + t2.enabled = data & 0x04; + + if(t1.enabled == false && (data & 0x02)) { + t1.stage2_ticks = 0; + t1.stage3_ticks = 0; + } + t1.enabled = data & 0x02; + + if(t0.enabled == false && (data & 0x01)) { + t0.stage2_ticks = 0; + t0.stage3_ticks = 0; + } + t0.enabled = data & 0x01; + } break; + + case 0xf2: { //DSPADDR + status.dsp_addr = data; + } break; + + case 0xf3: { //DSPDATA + //0x80-0xff is a read-only mirror of 0x00-0x7f + if(!(status.dsp_addr & 0x80)) { + 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 & 3, data); + } break; + + case 0xf8: { //RAM0 + status.smp_f8 = data; + } break; + + case 0xf9: { //RAM1 + status.smp_f9 = data; + } break; + + case 0xfa: { //T0TARGET + t0.target = data; + } break; + + case 0xfb: { //T1TARGET + t1.target = data; + } break; + + case 0xfc: { //T2TARGET + t2.target = data; + } break; + + case 0xfd: //T0OUT + case 0xfe: //T1OUT + case 0xff: { //T2OUT -- read-only registers + } break; + } + } + + //all writes, even to MMIO registers, appear on bus + ram_write(addr, data); +} + +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/asnes/smp/memory/memory.hpp b/asnes/smp/memory/memory.hpp new file mode 100755 index 00000000..ae16cee0 --- /dev/null +++ b/asnes/smp/memory/memory.hpp @@ -0,0 +1,12 @@ +uint8 ram_read(uint16 addr); +void ram_write(uint16 addr, uint8 data); + +uint8 port_read(uint8 port); +void port_write(uint8 port, 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/asnes/smp/serialization.cpp b/asnes/smp/serialization.cpp new file mode 100755 index 00000000..7a6e6ac1 --- /dev/null +++ b/asnes/smp/serialization.cpp @@ -0,0 +1,52 @@ +#ifdef SMP_CPP + +void SMP::serialize(serializer &s) { + Processor::serialize(s); + SMPcore::core_serialize(s); + + s.integer(status.opcode); + + 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_enabled); + s.integer(status.ram_disabled); + s.integer(status.ram_writable); + s.integer(status.timers_disabled); + + s.integer(status.iplrom_enabled); + + s.integer(status.dsp_addr); + + s.integer(status.smp_f8); + s.integer(status.smp_f9); + + s.integer(t0.stage0_ticks); + s.integer(t0.stage1_ticks); + s.integer(t0.stage2_ticks); + s.integer(t0.stage3_ticks); + s.integer(t0.current_line); + s.integer(t0.enabled); + s.integer(t0.target); + + s.integer(t1.stage0_ticks); + s.integer(t1.stage1_ticks); + s.integer(t1.stage2_ticks); + s.integer(t1.stage3_ticks); + s.integer(t1.current_line); + s.integer(t1.enabled); + s.integer(t1.target); + + s.integer(t2.stage0_ticks); + s.integer(t2.stage1_ticks); + s.integer(t2.stage2_ticks); + s.integer(t2.stage3_ticks); + s.integer(t2.current_line); + s.integer(t2.enabled); + s.integer(t2.target); +} + +#endif diff --git a/asnes/smp/smp.cpp b/asnes/smp/smp.cpp new file mode 100755 index 00000000..5027dea0 --- /dev/null +++ b/asnes/smp/smp.cpp @@ -0,0 +1,123 @@ +#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(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(cpu.thread); +} + +void SMP::synchronize_dsp() { + if(dsp.clock < 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(dsp.thread); +} + +void SMP::Enter() { smp.enter(); } + +void SMP::enter() { + while(true) { + if(scheduler.sync == Scheduler::SynchronizeMode::All) { + scheduler.exit(Scheduler::ExitReason::SynchronizeEvent); + } + + op_step(); + } +} + +void SMP::op_step() { + (this->*opcode_table[op_readpc()])(); +} + +void SMP::power() { + //targets not initialized/changed upon reset + t0.target = 0; + t1.target = 0; + t2.target = 0; + + reset(); +} + +void SMP::reset() { + create(Enter, system.apu_frequency()); + + regs.pc = 0xffc0; + regs.a = 0x00; + regs.x = 0x00; + regs.y = 0x00; + regs.sp = 0xef; + regs.p = 0x02; + + for(unsigned i = 0; i < memory::apuram.size(); i++) { + memory::apuram.write(i, 0x00); + } + + status.clock_counter = 0; + status.dsp_counter = 0; + status.timer_step = 3; + + //$00f0 + status.clock_speed = 0; + status.timer_speed = 0; + status.timers_enabled = true; + status.ram_disabled = false; + status.ram_writable = true; + status.timers_disabled = false; + + //$00f1 + status.iplrom_enabled = true; + + //$00f2 + status.dsp_addr = 0x00; + + //$00f8,$00f9 + status.smp_f8 = 0x00; + status.smp_f9 = 0x00; + + t0.stage0_ticks = 0; + t1.stage0_ticks = 0; + t2.stage0_ticks = 0; + + t0.stage1_ticks = 0; + t1.stage1_ticks = 0; + t2.stage1_ticks = 0; + + t0.stage2_ticks = 0; + t1.stage2_ticks = 0; + t2.stage2_ticks = 0; + + t0.stage3_ticks = 0; + t1.stage3_ticks = 0; + t2.stage3_ticks = 0; + + t0.current_line = 0; + t1.current_line = 0; + t2.current_line = 0; + + t0.enabled = false; + t1.enabled = false; + t2.enabled = false; +} + +SMP::SMP() { +} + +SMP::~SMP() { +} + +} diff --git a/asnes/smp/smp.hpp b/asnes/smp/smp.hpp new file mode 100755 index 00000000..034d9819 --- /dev/null +++ b/asnes/smp/smp.hpp @@ -0,0 +1,59 @@ +class SMP : public Processor, public SMPcore { +public: + static const uint8 iplrom[64]; + + //synchronization + alwaysinline void step(unsigned clocks); + alwaysinline void synchronize_cpu(); + alwaysinline void synchronize_dsp(); + + static void Enter(); + void enter(); + debugvirtual void op_step(); + + #include "memory/memory.hpp" + #include "timing/timing.hpp" + + struct { + uint8 opcode; + + //timing + unsigned clock_counter; + unsigned dsp_counter; + unsigned timer_step; + + //$00f0 + uint8 clock_speed; + uint8 timer_speed; + bool timers_enabled; + bool ram_disabled; + bool ram_writable; + bool timers_disabled; + + //$00f1 + bool iplrom_enabled; + + //$00f2 + uint8 dsp_addr; + + //$00f8,$00f9 + uint8 smp_f8, smp_f9; + } status; + + //ssmp.cpp + void power(); + void reset(); + + void serialize(serializer&); + SMP(); + ~SMP(); + + friend class SMPDebugger; +}; + +#if defined(DEBUGGER) + #include "debugger/debugger.hpp" + extern SMPDebugger smp; +#else + extern SMP smp; +#endif diff --git a/asnes/smp/timing/timing.cpp b/asnes/smp/timing/timing.cpp new file mode 100755 index 00000000..24cff287 --- /dev/null +++ b/asnes/smp/timing/timing.cpp @@ -0,0 +1,60 @@ +#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() { + t0.tick(); + t1.tick(); + t2.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::sSMPTimer::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; + sync_stage1(); +} + +template +void SMP::sSMPTimer::sync_stage1() { + bool new_line = stage1_ticks; + if(smp.status.timers_enabled == false) new_line = false; + if(smp.status.timers_disabled == 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(enabled == false) return; + stage2_ticks++; + if(stage2_ticks != target) return; + + //stage 3 increment + stage2_ticks = 0; + stage3_ticks++; + stage3_ticks &= 15; +} + +#endif diff --git a/asnes/smp/timing/timing.hpp b/asnes/smp/timing/timing.hpp new file mode 100755 index 00000000..e76119a5 --- /dev/null +++ b/asnes/smp/timing/timing.hpp @@ -0,0 +1,21 @@ +template +class sSMPTimer { +public: + uint8 stage0_ticks; + uint8 stage1_ticks; + uint8 stage2_ticks; + uint8 stage3_ticks; + bool current_line; + bool enabled; + uint8 target; + + void tick(); + void sync_stage1(); +}; + +sSMPTimer<192> t0; +sSMPTimer<192> t1; +sSMPTimer< 24> t2; + +alwaysinline void add_clocks(unsigned clocks); +alwaysinline void cycle_edge(); diff --git a/asnes/snes.hpp b/asnes/snes.hpp new file mode 100755 index 00000000..98cbcf43 --- /dev/null +++ b/asnes/snes.hpp @@ -0,0 +1,94 @@ +namespace SNES { + namespace Info { + static const char Name[] = "asnes"; + static const char Version[] = "000"; + static const unsigned SerializerVersion = 12; + } +} + +//#define DEBUGGER + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +using namespace nall; + +#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 uint8_t uint8; + typedef uint16_t uint16; + typedef uint32_t uint32; + typedef uint64_t uint64; + + 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_); + frequency = frequency_; + clock = 0; + } + + inline void serialize(serializer &s) { + s.integer(frequency); + s.integer(clock); + } + + inline Processor() : thread(0) {} + }; + + struct ChipDebugger { + virtual bool property(unsigned id, string &name, string &value) = 0; + }; + + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + + #include + #include + #include +} + +namespace nall { + template<> struct has_size { enum { value = true }; }; + template<> struct has_size { enum { value = true }; }; +} + +#undef debugvirtual diff --git a/asnes/system/serialization.cpp b/asnes/system/serialization.cpp new file mode 100755 index 00000000..da72b4bb --- /dev/null +++ b/asnes/system/serialization.cpp @@ -0,0 +1,88 @@ +#ifdef SYSTEM_CPP + +serializer System::serialize() { + serializer s(serialize_size); + + unsigned signature = 0x31545342, version = Info::SerializerVersion, crc32 = cartridge.crc32(); + 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 != cartridge.crc32()) return false; + + reset(); + serialize_all(s); + return true; +} + +//======== +//internal +//======== + +void System::serialize(serializer &s) { + s.integer((unsigned&)region); + s.integer((unsigned&)expansion); +} + +void System::serialize_all(serializer &s) { + bus.serialize(s); + cartridge.serialize(s); + system.serialize(s); + cpu.serialize(s); + smp.serialize(s); + ppu.serialize(s); + dsp.serialize(s); + + if(cartridge.mode() == Cartridge::Mode::SuperGameBoy) supergameboy.serialize(s); + if(cartridge.has_superfx()) superfx.serialize(s); + if(cartridge.has_sa1()) sa1.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_cx4()) cx4.serialize(s); + if(cartridge.has_dsp1()) dsp1.serialize(s); + if(cartridge.has_dsp2()) dsp2.serialize(s); + if(cartridge.has_obc1()) obc1.serialize(s); + if(cartridge.has_st0010()) st0010.serialize(s); + if(cartridge.has_msu1()) msu1.serialize(s); + if(cartridge.has_serial()) serial.serialize(s); +} + +//called once upon cartridge load event: 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 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/asnes/system/system.cpp b/asnes/system/system.cpp new file mode 100755 index 00000000..a2c8efc8 --- /dev/null +++ b/asnes/system/system.cpp @@ -0,0 +1,236 @@ +#include + +#define SYSTEM_CPP +namespace SNES { + +System system; + +#include +#include +#include + +#include ; + 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/bsnes/smp/smp-debugger.cpp b/bsnes/smp/smp-debugger.cpp new file mode 100755 index 00000000..31335a86 --- /dev/null +++ b/bsnes/smp/smp-debugger.cpp @@ -0,0 +1,25 @@ +#ifdef SMP_CPP + +bool SMPDebugger::property(unsigned id, string &name, string &value) { + unsigned n = 0; + + //$00f0 + if(id == n++) { name = "$00f0"; value = ""; return true; } + if(id == n++) { name = "Clock Speed"; value = clock_speed(); return true; } + if(id == n++) { name = "Timers Enable"; value = timers_enable(); return true; } + if(id == n++) { name = "RAM Disable"; value = ram_disable(); return true; } + if(id == n++) { name = "RAM Writable"; value = ram_writable(); return true; } + if(id == n++) { name = "Timers Disable"; value = timers_disable(); return true; } + + //$00f1 + if(id == n++) { name = "$00f1"; value = ""; return true; } + if(id == n++) { name = "IPLROM Enable"; value = iplrom_enable(); return true; } + + //$00f2 + if(id == n++) { name = "$00f2"; value = ""; return true; } + if(id == n++) { name = "DSP Address"; value = string("0x", strhex<2>(dsp_address())); return true; } + + return false; +} + +#endif diff --git a/bsnes/smp/smp-debugger.hpp b/bsnes/smp/smp-debugger.hpp new file mode 100755 index 00000000..15e5b7b0 --- /dev/null +++ b/bsnes/smp/smp-debugger.hpp @@ -0,0 +1,16 @@ +struct SMPDebugger : ChipDebugger { + bool property(unsigned id, string &name, string &value); + + //$00f0 + virtual unsigned clock_speed() { return 0; } + virtual bool timers_enable() { return 0; } + virtual bool ram_disable() { return 0; } + virtual bool ram_writable() { return 0; } + virtual bool timers_disable() { return 0; } + + //$00f1 + virtual bool iplrom_enable() { return 0; } + + //$00f2 + virtual unsigned dsp_address() { return 0; } +}; diff --git a/bsnes/smp/smp.cpp b/bsnes/smp/smp.cpp new file mode 100755 index 00000000..a03b593e --- /dev/null +++ b/bsnes/smp/smp.cpp @@ -0,0 +1,65 @@ +#include + +#define SMP_CPP +namespace SNES { + +#if defined(DEBUGGER) + #include "smp-debugger.cpp" +#endif + +void SMP::Enter() { smp.enter(); } + +void SMP::power() { + create(SMP::Enter, system.apu_frequency()); +} + +void SMP::reset() { + create(SMP::Enter, system.apu_frequency()); +} + +void SMP::serialize(serializer &s) { + Processor::serialize(s); +} + +//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) +}; + +} diff --git a/bsnes/smp/smp.hpp b/bsnes/smp/smp.hpp new file mode 100755 index 00000000..2e154d69 --- /dev/null +++ b/bsnes/smp/smp.hpp @@ -0,0 +1,30 @@ +#if defined(DEBUGGER) + #include "smp-debugger.hpp" +#endif + +class SMP : public Processor { +public: + //synchronization + alwaysinline void step(unsigned clocks); + alwaysinline void synchronize_cpu(); + alwaysinline void synchronize_dsp(); + + static void Enter(); + virtual void enter() = 0; + + static const uint8 iplrom[64]; + + virtual uint8 ram_read(uint16 addr) = 0; + virtual void ram_write(uint16 addr, uint8 value) = 0; + + //$f4-$f7 + virtual uint8 port_read(uint8 port) = 0; + virtual void port_write(uint8 port, uint8 value) = 0; + + virtual void power(); + virtual void reset(); + + virtual void serialize(serializer&); + SMP() {} + virtual ~SMP() {} +}; diff --git a/bsnes/smp/ssmp/debugger/debugger.cpp b/bsnes/smp/ssmp/debugger/debugger.cpp new file mode 100755 index 00000000..2a72255d --- /dev/null +++ b/bsnes/smp/ssmp/debugger/debugger.cpp @@ -0,0 +1,61 @@ +#ifdef SSMP_CPP + +void sSMPDebugger::op_step() { + bool break_event = false; + + usage[regs.pc] |= UsageExec; + opcode_pc = regs.pc; + + if(debugger.step_smp) { + debugger.break_event = Debugger::BreakEvent::SMPStep; + scheduler.exit(Scheduler::ExitReason::DebuggerEvent); + } else { + debugger.breakpoint_test(Debugger::Breakpoint::Source::APURAM, Debugger::Breakpoint::Mode::Exec, regs.pc, 0x00); + } + + if(step_event) step_event(); + sSMP::op_step(); + synchronize_cpu(); +} + +uint8 sSMPDebugger::op_read(uint16 addr) { + uint8 data = sSMP::op_read(addr); + usage[addr] |= UsageRead; + debugger.breakpoint_test(Debugger::Breakpoint::Source::APURAM, Debugger::Breakpoint::Mode::Read, addr, data); + return data; +} + +void sSMPDebugger::op_write(uint16 addr, uint8 data) { + sSMP::op_write(addr, data); + usage[addr] |= UsageWrite; + usage[addr] &= ~UsageExec; + debugger.breakpoint_test(Debugger::Breakpoint::Source::APURAM, Debugger::Breakpoint::Mode::Write, addr, data); +} + +sSMPDebugger::sSMPDebugger() { + usage = new uint8[1 << 16](); + opcode_pc = 0xffc0; +} + +sSMPDebugger::~sSMPDebugger() { + delete[] usage; +} + +//=========== +//SMPDebugger +//=========== + +//$00f0 +unsigned sSMPDebugger::clock_speed() { return status.clock_speed; } +bool sSMPDebugger::timers_enable() { return status.timers_enabled; } +bool sSMPDebugger::ram_disable() { return status.ram_disabled; } +bool sSMPDebugger::ram_writable() { return status.ram_writable; } +bool sSMPDebugger::timers_disable() { return status.timers_disabled; } + +//$00f1 +bool sSMPDebugger::iplrom_enable() { return status.iplrom_enabled; } + +//$00f2 +unsigned sSMPDebugger::dsp_address() { return status.dsp_addr; } + +#endif diff --git a/bsnes/smp/ssmp/debugger/debugger.hpp b/bsnes/smp/ssmp/debugger/debugger.hpp new file mode 100755 index 00000000..ff03220c --- /dev/null +++ b/bsnes/smp/ssmp/debugger/debugger.hpp @@ -0,0 +1,36 @@ +class sSMPDebugger : public sSMP, public SMPDebugger { +public: + function step_event; + + enum Usage { + UsageRead = 0x80, + UsageWrite = 0x40, + UsageExec = 0x20, + }; + uint8 *usage; + uint16 opcode_pc; + + void op_step(); + uint8 op_read(uint16 addr); + void op_write(uint16 addr, uint8 data); + + sSMPDebugger(); + ~sSMPDebugger(); + + //=========== + //SMPDebugger + //=========== + + //$00f0 + unsigned clock_speed(); + bool timers_enable(); + bool ram_disable(); + bool ram_writable(); + bool timers_disable(); + + //$00f1 + bool iplrom_enable(); + + //$00f2 + unsigned dsp_address(); +}; diff --git a/bsnes/smp/ssmp/memory/memory.cpp b/bsnes/smp/ssmp/memory/memory.cpp new file mode 100755 index 00000000..21bba01d --- /dev/null +++ b/bsnes/smp/ssmp/memory/memory.cpp @@ -0,0 +1,213 @@ +#ifdef SSMP_CPP + +alwaysinline uint8 sSMP::ram_read(uint16 addr) { + if(addr >= 0xffc0 && status.iplrom_enabled) return iplrom[addr & 0x3f]; + if(status.ram_disabled) return 0x5a; //0xff on mini-SNES + return memory::apuram[addr]; +} + +alwaysinline void sSMP::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_disabled) memory::apuram[addr] = data; +} + +alwaysinline uint8 sSMP::port_read(uint8 port) { + return memory::apuram[0xf4 + (port & 3)]; +} + +alwaysinline void sSMP::port_write(uint8 port, uint8 data) { + memory::apuram[0xf4 + (port & 3)] = data; +} + +alwaysinline uint8 sSMP::op_busread(uint16 addr) { + uint8 r; + if((addr & 0xfff0) == 0x00f0) { //00f0-00ff + switch(addr) { + case 0xf0: { //TEST -- write-only register + r = 0x00; + } break; + + case 0xf1: { //CONTROL -- write-only register + r = 0x00; + } break; + + case 0xf2: { //DSPADDR + r = status.dsp_addr; + } break; + + case 0xf3: { //DSPDATA + //0x80-0xff are read-only mirrors of 0x00-0x7f + r = dsp.read(status.dsp_addr & 0x7f); + } break; + + case 0xf4: //CPUIO0 + case 0xf5: //CPUIO1 + case 0xf6: //CPUIO2 + case 0xf7: { //CPUIO3 + synchronize_cpu(); + r = cpu.port_read(addr & 3); + } break; + + case 0xf8: { //RAM0 + r = status.smp_f8; + } break; + + case 0xf9: { //RAM1 + r = status.smp_f9; + } break; + + case 0xfa: //T0TARGET + case 0xfb: //T1TARGET + case 0xfc: { //T2TARGET -- write-only registers + r = 0x00; + } break; + + case 0xfd: { //T0OUT -- 4-bit counter value + r = t0.stage3_ticks & 15; + t0.stage3_ticks = 0; + } break; + + case 0xfe: { //T1OUT -- 4-bit counter value + r = t1.stage3_ticks & 15; + t1.stage3_ticks = 0; + } break; + + case 0xff: { //T2OUT -- 4-bit counter value + r = t2.stage3_ticks & 15; + t2.stage3_ticks = 0; + } break; + } + } else { + r = ram_read(addr); + } + + return r; +} + +alwaysinline void sSMP::op_buswrite(uint16 addr, uint8 data) { + if((addr & 0xfff0) == 0x00f0) { //$00f0-00ff + 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_enabled = data & 0x08; + status.ram_disabled = data & 0x04; + status.ram_writable = data & 0x02; + status.timers_disabled = data & 0x01; + + status.timer_step = (1 << status.clock_speed) + (2 << status.timer_speed); + + t0.sync_stage1(); + t1.sync_stage1(); + t2.sync_stage1(); + } break; + + case 0xf1: { //CONTROL + status.iplrom_enabled = 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(t2.enabled == false && (data & 0x04)) { + t2.stage2_ticks = 0; + t2.stage3_ticks = 0; + } + t2.enabled = data & 0x04; + + if(t1.enabled == false && (data & 0x02)) { + t1.stage2_ticks = 0; + t1.stage3_ticks = 0; + } + t1.enabled = data & 0x02; + + if(t0.enabled == false && (data & 0x01)) { + t0.stage2_ticks = 0; + t0.stage3_ticks = 0; + } + t0.enabled = data & 0x01; + } break; + + case 0xf2: { //DSPADDR + status.dsp_addr = data; + } break; + + case 0xf3: { //DSPDATA + //0x80-0xff is a read-only mirror of 0x00-0x7f + if(!(status.dsp_addr & 0x80)) { + 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 & 3, data); + } break; + + case 0xf8: { //RAM0 + status.smp_f8 = data; + } break; + + case 0xf9: { //RAM1 + status.smp_f9 = data; + } break; + + case 0xfa: { //T0TARGET + t0.target = data; + } break; + + case 0xfb: { //T1TARGET + t1.target = data; + } break; + + case 0xfc: { //T2TARGET + t2.target = data; + } break; + + case 0xfd: //T0OUT + case 0xfe: //T1OUT + case 0xff: { //T2OUT -- read-only registers + } break; + } + } + + //all writes, even to MMIO registers, appear on bus + ram_write(addr, data); +} + +void sSMP::op_io() { + add_clocks(24); + cycle_edge(); +} + +uint8 sSMP::op_read(uint16 addr) { + add_clocks(12); + uint8 r = op_busread(addr); + add_clocks(12); + cycle_edge(); + return r; +} + +void sSMP::op_write(uint16 addr, uint8 data) { + add_clocks(24); + op_buswrite(addr, data); + cycle_edge(); +} + +#endif diff --git a/bsnes/smp/ssmp/memory/memory.hpp b/bsnes/smp/ssmp/memory/memory.hpp new file mode 100755 index 00000000..ae16cee0 --- /dev/null +++ b/bsnes/smp/ssmp/memory/memory.hpp @@ -0,0 +1,12 @@ +uint8 ram_read(uint16 addr); +void ram_write(uint16 addr, uint8 data); + +uint8 port_read(uint8 port); +void port_write(uint8 port, 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/bsnes/smp/ssmp/serialization.cpp b/bsnes/smp/ssmp/serialization.cpp new file mode 100755 index 00000000..204b23bf --- /dev/null +++ b/bsnes/smp/ssmp/serialization.cpp @@ -0,0 +1,52 @@ +#ifdef SSMP_CPP + +void sSMP::serialize(serializer &s) { + SMP::serialize(s); + SMPcore::core_serialize(s); + + s.integer(status.opcode); + + 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_enabled); + s.integer(status.ram_disabled); + s.integer(status.ram_writable); + s.integer(status.timers_disabled); + + s.integer(status.iplrom_enabled); + + s.integer(status.dsp_addr); + + s.integer(status.smp_f8); + s.integer(status.smp_f9); + + s.integer(t0.stage0_ticks); + s.integer(t0.stage1_ticks); + s.integer(t0.stage2_ticks); + s.integer(t0.stage3_ticks); + s.integer(t0.current_line); + s.integer(t0.enabled); + s.integer(t0.target); + + s.integer(t1.stage0_ticks); + s.integer(t1.stage1_ticks); + s.integer(t1.stage2_ticks); + s.integer(t1.stage3_ticks); + s.integer(t1.current_line); + s.integer(t1.enabled); + s.integer(t1.target); + + s.integer(t2.stage0_ticks); + s.integer(t2.stage1_ticks); + s.integer(t2.stage2_ticks); + s.integer(t2.stage3_ticks); + s.integer(t2.current_line); + s.integer(t2.enabled); + s.integer(t2.target); +} + +#endif diff --git a/bsnes/smp/ssmp/ssmp.cpp b/bsnes/smp/ssmp/ssmp.cpp new file mode 100755 index 00000000..6e26a216 --- /dev/null +++ b/bsnes/smp/ssmp/ssmp.cpp @@ -0,0 +1,109 @@ +#include + +#define SSMP_CPP +namespace SNES { + +#if defined(DEBUGGER) + #include "debugger/debugger.cpp" + sSMPDebugger smp; +#else + sSMP smp; +#endif + +#include "serialization.cpp" +#include "memory/memory.cpp" +#include "timing/timing.cpp" + +void sSMP::enter() { + while(true) { + if(scheduler.sync == Scheduler::SynchronizeMode::All) { + scheduler.exit(Scheduler::ExitReason::SynchronizeEvent); + } + + op_step(); + } +} + +void sSMP::op_step() { + (this->*opcode_table[op_readpc()])(); +} + +void sSMP::power() { + SMP::power(); + + //targets not initialized/changed upon reset + t0.target = 0; + t1.target = 0; + t2.target = 0; + + reset(); +} + +void sSMP::reset() { + SMP::reset(); + + regs.pc = 0xffc0; + regs.a = 0x00; + regs.x = 0x00; + regs.y = 0x00; + regs.sp = 0xef; + regs.p = 0x02; + + for(unsigned i = 0; i < memory::apuram.size(); i++) { + memory::apuram.write(i, 0x00); + } + + status.clock_counter = 0; + status.dsp_counter = 0; + status.timer_step = 3; + + //$00f0 + status.clock_speed = 0; + status.timer_speed = 0; + status.timers_enabled = true; + status.ram_disabled = false; + status.ram_writable = true; + status.timers_disabled = false; + + //$00f1 + status.iplrom_enabled = true; + + //$00f2 + status.dsp_addr = 0x00; + + //$00f8,$00f9 + status.smp_f8 = 0x00; + status.smp_f9 = 0x00; + + t0.stage0_ticks = 0; + t1.stage0_ticks = 0; + t2.stage0_ticks = 0; + + t0.stage1_ticks = 0; + t1.stage1_ticks = 0; + t2.stage1_ticks = 0; + + t0.stage2_ticks = 0; + t1.stage2_ticks = 0; + t2.stage2_ticks = 0; + + t0.stage3_ticks = 0; + t1.stage3_ticks = 0; + t2.stage3_ticks = 0; + + t0.current_line = 0; + t1.current_line = 0; + t2.current_line = 0; + + t0.enabled = false; + t1.enabled = false; + t2.enabled = false; +} + +sSMP::sSMP() { +} + +sSMP::~sSMP() { +} + +} diff --git a/bsnes/smp/ssmp/ssmp.hpp b/bsnes/smp/ssmp/ssmp.hpp new file mode 100755 index 00000000..d764a64e --- /dev/null +++ b/bsnes/smp/ssmp/ssmp.hpp @@ -0,0 +1,51 @@ +class sSMP : public SMP, public SMPcore { +public: + void enter(); + debugvirtual void op_step(); + + #include "memory/memory.hpp" + #include "timing/timing.hpp" + + struct { + uint8 opcode; + + //timing + unsigned clock_counter; + unsigned dsp_counter; + unsigned timer_step; + + //$00f0 + uint8 clock_speed; + uint8 timer_speed; + bool timers_enabled; + bool ram_disabled; + bool ram_writable; + bool timers_disabled; + + //$00f1 + bool iplrom_enabled; + + //$00f2 + uint8 dsp_addr; + + //$00f8,$00f9 + uint8 smp_f8, smp_f9; + } status; + + //ssmp.cpp + void power(); + void reset(); + + void serialize(serializer&); + sSMP(); + ~sSMP(); + + friend class sSMPDebug; +}; + +#if defined(DEBUGGER) + #include "debugger/debugger.hpp" + extern sSMPDebugger smp; +#else + extern sSMP smp; +#endif diff --git a/bsnes/smp/ssmp/timing/timing.cpp b/bsnes/smp/ssmp/timing/timing.cpp new file mode 100755 index 00000000..0fdf7eea --- /dev/null +++ b/bsnes/smp/ssmp/timing/timing.cpp @@ -0,0 +1,64 @@ +#ifdef SSMP_CPP + +void sSMP::add_clocks(unsigned clocks) { + step(clocks); + #if !defined(DSP_STATE_MACHINE) + synchronize_dsp(); + #else + while(dsp.clock < 0) dsp.enter(); + #endif + + //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 sSMP::cycle_edge() { + t0.tick(); + t1.tick(); + t2.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 sSMP::sSMPTimer::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; + sync_stage1(); +} + +template +void sSMP::sSMPTimer::sync_stage1() { + bool new_line = stage1_ticks; + if(smp.status.timers_enabled == false) new_line = false; + if(smp.status.timers_disabled == 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(enabled == false) return; + stage2_ticks++; + if(stage2_ticks != target) return; + + //stage 3 increment + stage2_ticks = 0; + stage3_ticks++; + stage3_ticks &= 15; +} + +#endif diff --git a/bsnes/smp/ssmp/timing/timing.hpp b/bsnes/smp/ssmp/timing/timing.hpp new file mode 100755 index 00000000..e76119a5 --- /dev/null +++ b/bsnes/smp/ssmp/timing/timing.hpp @@ -0,0 +1,21 @@ +template +class sSMPTimer { +public: + uint8 stage0_ticks; + uint8 stage1_ticks; + uint8 stage2_ticks; + uint8 stage3_ticks; + bool current_line; + bool enabled; + uint8 target; + + void tick(); + void sync_stage1(); +}; + +sSMPTimer<192> t0; +sSMPTimer<192> t1; +sSMPTimer< 24> t2; + +alwaysinline void add_clocks(unsigned clocks); +alwaysinline void cycle_edge(); diff --git a/bsnes/smp/synchronization.hpp b/bsnes/smp/synchronization.hpp new file mode 100755 index 00000000..2710aba5 --- /dev/null +++ b/bsnes/smp/synchronization.hpp @@ -0,0 +1,12 @@ +void SMP::step(unsigned clocks) { + clock += clocks * (uint64)cpu.frequency; + dsp.clock -= clocks; +} + +void SMP::synchronize_cpu() { + if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(cpu.thread); +} + +void SMP::synchronize_dsp() { + if(dsp.clock < 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(dsp.thread); +} diff --git a/bsnes/snes.hpp b/bsnes/snes.hpp new file mode 100755 index 00000000..4a7d70fc --- /dev/null +++ b/bsnes/snes.hpp @@ -0,0 +1,137 @@ +namespace SNES { + namespace Info { + static const char Name[] = "bsnes"; + static const char Version[] = "067.06"; + static const unsigned SerializerVersion = 12; + } +} + +#define CORE_SMEMORY +#define CORE_SCPU +#define CORE_SSMP +#define CORE_SDSP +#define CORE_BPPU + +//S-DSP can be encapsulated into a state machine using #define magic +//this avoids ~2.048m co_switch() calls per second (~5% speedup) +#define DSP_STATE_MACHINE + +//game genie + pro action replay code support (~2% speed hit) +#define CHEAT_SYSTEM + +//enable debugging extensions (~15% speed hit) +//#define DEBUGGER + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +using namespace nall; + +#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 uint8_t uint8; + typedef uint16_t uint16; + typedef uint32_t uint32; + typedef uint64_t uint64; + + 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_); + frequency = frequency_; + clock = 0; + } + + inline void serialize(serializer &s) { + s.integer(frequency); + s.integer(clock); + } + + inline Processor() : thread(0) {} + }; + + struct ChipDebugger { + virtual bool property(unsigned id, string &name, string &value) = 0; + }; + + #include + #if defined(CORE_SMEMORY) + #include + #endif + + #include + #if defined(CORE_SPPU) + #include + #elif defined(CORE_BPPU) + #include + #endif + + #include + #include + #if defined(CORE_SCPU) + #include + #endif + + #include + #include + #if defined(CORE_SSMP) + #include + #endif + + #include + #if defined(CORE_SDSP) + #include + #elif defined(CORE_ADSP) + #include + #endif + + #include + #include + #include + #include + + #include + #include + #include + #include + + #include + #include + #include +} + +namespace nall { + template<> struct has_size { enum { value = true }; }; + template<> struct has_size { enum { value = true }; }; +} + +#undef debugvirtual diff --git a/bsnes/system/serialization.cpp b/bsnes/system/serialization.cpp new file mode 100755 index 00000000..da72b4bb --- /dev/null +++ b/bsnes/system/serialization.cpp @@ -0,0 +1,88 @@ +#ifdef SYSTEM_CPP + +serializer System::serialize() { + serializer s(serialize_size); + + unsigned signature = 0x31545342, version = Info::SerializerVersion, crc32 = cartridge.crc32(); + 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 != cartridge.crc32()) return false; + + reset(); + serialize_all(s); + return true; +} + +//======== +//internal +//======== + +void System::serialize(serializer &s) { + s.integer((unsigned&)region); + s.integer((unsigned&)expansion); +} + +void System::serialize_all(serializer &s) { + bus.serialize(s); + cartridge.serialize(s); + system.serialize(s); + cpu.serialize(s); + smp.serialize(s); + ppu.serialize(s); + dsp.serialize(s); + + if(cartridge.mode() == Cartridge::Mode::SuperGameBoy) supergameboy.serialize(s); + if(cartridge.has_superfx()) superfx.serialize(s); + if(cartridge.has_sa1()) sa1.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_cx4()) cx4.serialize(s); + if(cartridge.has_dsp1()) dsp1.serialize(s); + if(cartridge.has_dsp2()) dsp2.serialize(s); + if(cartridge.has_obc1()) obc1.serialize(s); + if(cartridge.has_st0010()) st0010.serialize(s); + if(cartridge.has_msu1()) msu1.serialize(s); + if(cartridge.has_serial()) serial.serialize(s); +} + +//called once upon cartridge load event: 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 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/bsnes/system/system.cpp b/bsnes/system/system.cpp new file mode 100755 index 00000000..a2c8efc8 --- /dev/null +++ b/bsnes/system/system.cpp @@ -0,0 +1,236 @@ +#include + +#define SYSTEM_CPP +namespace SNES { + +System system; + +#include +#include +#include + +#include